Compare commits

...

10 Commits

Author SHA1 Message Date
632e9b0a5e Merge branch 'ProcessManual_Ref' 2026-02-25 15:57:27 +09:00
CCW
1bca7d8aef Camera View Window Move Command 활성화 2026-02-25 15:39:40 +09:00
5523b1f79c 잔건 관련 업데이트
1. 중복 보고
 - 보고가 완료된 사항을 체크하는 플래그를 통해 방지
2. 최초 셀 데이터 누락
 - Cell Tracker에서 마지막 데이터를 누적하고 있음
   정지 시점에서 이미 CellTracker는 데이터를 가지고 있어야 함
2026-02-23 19:10:15 +09:00
25d96fcae4 Calibration 메뉴얼 버튼 추가 2026-02-22 14:35:58 +09:00
4e99433782 refcatoring 관련 프로그램 추가 2026-02-21 15:47:11 +09:00
c6e9783950 APD 추가
- HeadNo(JigNo)

PLC API 추가
2026-02-21 14:41:47 +09:00
5b4a856a2e AutoRun Simulation 추가
- AutoRun은 수정 안함(들여쓰기 변경)
- Timer 기반 시뮬레이션
2026-02-21 02:34:10 +09:00
e946197f8b PLC Api 추가 2026-02-13 20:36:46 +09:00
a95534302f Cell APD Log 적용 최종 버전 2026-02-06 14:35:53 +09:00
caf35988a7 APD 저장 기능 추가 + 상위보고 데이터 생성 시 Line 외 Arc, Group도 생성 기능 추가 2026-02-02 10:01:41 +09:00
29 changed files with 2385 additions and 390 deletions

View File

@ -26,7 +26,7 @@ namespace AuroraCellTracker
public class CellTracker : Device
{
Logger _Logger = new Logger("CellTracker");
ICellTracker _CellTracker;
public ICellTracker _CellTracker;
public CellTrackerConfig _CellTrackerConfig { get; set; }
private ICellTracker[] _MultiCellTrackerObj = new ICellTracker[4];

View File

@ -149,6 +149,8 @@ namespace AuroraCellTracker.DaekhonCoporation
{
string lotId = "", cellId = "";
bool gotNew = false;
if (selectedMode == eMode.Queue) {
if (null != dllSocketCtrl) {
string str = dllSocketCtrl.DllSocket_GetReceiveMessageInQueue(true, true);
@ -158,6 +160,7 @@ namespace AuroraCellTracker.DaekhonCoporation
CalcCellInfo(str, out lotId, out cellId);
SetCellInfo(lotId, cellId);
gotNew = true;
} else
CellInfoStrings.ReceiveStrInQueue = "Receive Length is 0";
} else
@ -166,27 +169,33 @@ namespace AuroraCellTracker.DaekhonCoporation
_Logger.D(string.Format($"{CellInfoStrings.ReceiveStrInQueue}"));
} else if (selectedMode == eMode.Callback) {
// Callback 방식의 경우, 자체 ConcurrentQueue에서 Deque해서 가져와야 함
CellInfoStr info;
CellInfoStr info;
if (CellInfoQueue.TryDequeue(out info)) {
SetCellInfo(info.LotID, info.CellID);
gotNew = true;
}
}
// Queue 방식인데 Cell Tracker에서 메세지가 안 왔거나, Callback 방식인데 Cell Tracker API 함수가 Callback 안 된 경우(ex : ECS 오프라인 모드),
// Queue 방식인데 Cell Tracker에서 메세지가 안 왔거나,
// Callback 방식인데 실제 수신이 없거나(또는 같은 값이 연속으로 들어오는 경우),
// Lot ID/Cell ID를 ES 지정사양에 맞추어 생성
if (OldLotId == CellInfoStrings.LotID && OldCellId == CellInfoStrings.CellID) {
if (!gotNew || (OldLotId == CellInfoStrings.LotID && OldCellId == CellInfoStrings.CellID)) {
CalcCellInfo("", out lotId, out cellId);
AddCellInfo(lotId, cellId);
CellInfoStr info;
if (CellInfoQueue.TryDequeue(out info))
{
if (CellInfoQueue.TryDequeue(out info)) {
SetCellInfo(info.LotID, info.CellID);
} else {
// 새로운 값이 안들어와도 일단 보여줌
SetCellInfo(lotId, cellId);
}
}
OldLotId = CellInfoStrings.LotID;
OldCellId = CellInfoStrings.CellID;
_Logger.I($"Received CellInfo >> count({CellInfoQueue.Count})");
}
public void SendData(string str, bool isSendToUpperSystem = false)

View File

@ -505,7 +505,7 @@ namespace AuroraIO.AJINEXTEK
int err = (int)CAXD.AxdiReadInport(index, ref data);
if (err != (int)AXT_FUNC_RESULT.AXT_RT_SUCCESS)
{
throw new Exception(string.Format("ERR_IO_FAIL_TO_GET_BIT;ERROR CODE:{0}", err));
return false;
}
return Convert.ToBoolean(data);
}

View File

@ -143,6 +143,10 @@
<Project>{E7E68E54-43D2-4413-9510-43D25F13CD80}</Project>
<Name>AuroraLaser</Name>
</ProjectReference>
<ProjectReference Include="..\AuroraPLC\AuroraPLC.csproj">
<Project>{1A47B1F3-5BDE-41DF-8F4D-25298AD4F6DE}</Project>
<Name>AuroraPLC</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -22,7 +22,7 @@ namespace AuroraLaserScanner
public ScannerConfig Config { get; set; }
//public ScannerObj _scannerObj { get; set; }
private ScannerObj _scannerObj { get; set; }
protected ScannerObj _scannerObj { get; set; }
public Scanner(string deviceId)
{
@ -37,9 +37,9 @@ namespace AuroraLaserScanner
Config = config as ScannerConfig;
if (IsSimul) {
_scannerObj = new ScannerSimul();
return E1.OK;
//return E1.OK;
}
if (Config.Type == eRTCType.RTC5) {
else if (Config.Type == eRTCType.RTC5) {
_scannerObj = new ScannerRTC5();
} else if (Config.Type == eRTCType.RTC6) {
_scannerObj = new ScannerRTC6();

View File

@ -91,7 +91,7 @@ namespace AuroraLaserScanner
RealImage = 0,
VirtualImage = 1
}
public enum eListNo { LIST1 = 1, LIST2 = 2 };
public enum eListNo { NONE = 0, LIST1 = 1, LIST2 = 2 };
public enum eAutoControlParameter
{

View File

@ -1,10 +1,14 @@
using Aurora.Api;
using Hermes;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static AuroraPLC.PLCBase;
namespace AuroraLaserScanner
{
internal class ScannerSimul : ScannerObj
public class ScannerSimul : ScannerObj
{
static new readonly Logger Log = new Logger("Scanner");
@ -14,7 +18,154 @@ namespace AuroraLaserScanner
//set_trigger 관련 추가, get_waveform 에서 얻어올 포인터 추가, 최대 크기로 설정 (2^20 = 1,048,576)
[MarshalAs(UnmanagedType.LPArray, SizeConst = 1048576)] int[] m_getWaveformPtr = new int[1048576];
private bool _ModulationMode;
private ScannerConfig _config;
// -----------------------------
// MOTF Simulation state
// -----------------------------
public Func<int, ePLCDevice, bool, (int err, bool data)> PLCReadBit;
public Func<int, ePLCDevice, bool, (int err, short data)> PLCReadWord;
public Func<int, ePLCDevice, bool, (int err, int data)> PLCReadDWord;
public Func<double> PLCGetAbsDeg;
private const int PLC_CURRENT_POS_1_ADDR = 4;
public Stopwatch sw = Stopwatch.StartNew();
// encoder가 반환할 값
public volatile int _simEncXCount = 0; //랩핑 각도(0~360
public volatile int _simEncYCount = 0; //누적 각도
public volatile bool LastWaitMissedRange = false;
private const int RotaryJigCount = 16;
private const double JigPerMsec = 300;
private const double DegreePerRev = 360.0;
private const double OneCycleMsec = JigPerMsec * RotaryJigCount;
// 누적각을 scanner 내부에서 유지
public double _angleAbsDeg = 0.0;
private bool _listClosed = false;
private bool _list1Busy = false;
private bool _list2Busy = false;
private eListNo _currentExecutingList = eListNo.NONE;
private bool _autoChangeRequested = false;
private long _listStartMs = 0; // ← 이게 executionStartTime 역할
private const int MinListTimeMs = 30; // 너무 빠른 완료 방지
private const int MaxListTimeMs = 800; // 너무 느린 완료 방지
private int SimulatedListTimeMs = 250; // 리스트 실행 시간
private const double RecommendWindowDeg = 45.0;
private const double RecommendUseRatio = 0.35; // 45도 중 실제 마킹이 차지하는 비율(가볍게)
public void EquipmentSimulator()
{
PullEncoderFromPlcOnce();
UpdateListExecution();
}
private int DegToCount(double deg)
{
double c = deg * _config.MarkingOnTheFlyEncoderResolutionRotate / DegreePerRev;
return (int)System.Math.Round(c);
}
private int DegToCountAbs(double absDeg)
{
double c = absDeg * _config.MarkingOnTheFlyEncoderResolutionRotate / DegreePerRev;
long lc = (long)System.Math.Round(c);
return unchecked((int)lc);
}
private void UpdateListExecution()
{
if (_currentExecutingList == eListNo.NONE)
return;
long now = sw.ElapsedMilliseconds;
long elapsed = now - _listStartMs;
int doneTime = GetEffectiveListTimeMs();
// 최소 현실 가드: elapsed가 음수인 경우 방지
if (elapsed < 0) {
_listStartMs = now;
return;
}
if (elapsed >= doneTime) {
// 현재 리스트 실행 완료
if (_currentExecutingList == eListNo.LIST1)
_list1Busy = false;
else
_list2Busy = false;
if (_autoChangeRequested) {
// 반대 리스트 자동 실행
_currentExecutingList =
(_currentExecutingList == eListNo.LIST1) ? eListNo.LIST2 : eListNo.LIST1;
if (_currentExecutingList == eListNo.LIST1) {
_list1Busy = true;
_list2Busy = false;
} else {
_list2Busy = true;
_list1Busy = false;
}
_listStartMs = now;
_autoChangeRequested = false;
} else {
_currentExecutingList = eListNo.NONE;
}
}
}
private int GetEffectiveListTimeMs()
{
int t = SimulatedListTimeMs;
// 4800msec / 360 * 45 * ratio
int recommendedListRunTime = (int)Math.Round((OneCycleMsec / DegreePerRev) * RecommendWindowDeg * RecommendUseRatio);
// recommended가 너무 작거나 0이 되는 경우 방지
if (recommendedListRunTime < MinListTimeMs)
recommendedListRunTime = MinListTimeMs;
if (t < MinListTimeMs)
t = MinListTimeMs;
if (t > MaxListTimeMs)
t = MaxListTimeMs;
if (recommendedListRunTime > MaxListTimeMs)
recommendedListRunTime = MaxListTimeMs;
return t;
}
public void TrySyncAbsAngleToPlcNow()
{
_angleAbsDeg = PLCGetAbsDeg();
// mod 동기화도 같이
int modRaw = PLCReadDWord(PLC_CURRENT_POS_1_ADDR, ePLCDevice.W, false).data;
double modDeg = (modRaw / 100_000.0) % DegreePerRev;
if (modDeg < 0)
modDeg += DegreePerRev;
LastWaitMissedRange = false;
}
public void ResetSim()
{
_angleAbsDeg = 0.0;
_simEncXCount = 0;
_simEncYCount = 0;
StopExecution(); // 리스트 상태 리셋
LastWaitMissedRange = false;
}
public override uint Open()
{
IsOpenUnit = true;
@ -33,6 +184,7 @@ namespace AuroraLaserScanner
CommandPosZ = 0;
_ModulationMode = config.Modulation;
_config = config;
return E1.OK;
}
@ -107,12 +259,17 @@ namespace AuroraLaserScanner
}
public override void GetEncoder(out double encoderX, out double encoderY, bool rotateMode = false)
{
// simulation 할때 값 반영 검토 필요
encoderX = 0;
encoderY = 0;
int valueX = _simEncXCount;
int valueY = _simEncYCount;
// 너가 쓰는 변환식 그대로
encoderX = (valueX * DegreePerRev) / _config.MarkingOnTheFlyEncoderResolutionRotate;
encoderY = _angleAbsDeg;
if (encoderY > -0.5 && encoderY < 0)
encoderY = 0; // 시뮬 안정화
return;
}
public override uint GetMarkingInfo()
@ -187,9 +344,62 @@ namespace AuroraLaserScanner
public override void WaitForEncoderRotateRange(double minX, double maxX, double minY, double maxY)
{
// simulation 할때 값 반영 검토 필요
}
LastWaitMissedRange = false;
long t0 = sw.ElapsedMilliseconds;
while (true) {
// 블로킹 대기 중에도 PLC에서 최신값을 직접 당겨온다
PullEncoderFromPlcOnce();
// GetEncoder는 _simEncYCount를 deg로 바꿔줄 것
GetEncoder(out _, out double curY, true);
// 정상 진입
if (curY >= minY && curY <= maxY)
return;
// 최소 현실 가드: 구간을 지나쳤으면 TIMEOUT까지 끌지 말고 즉시 종료
// (실장에서도 "놓치면" 의미 없고 다음 로직이 꼬임)
if (curY > maxY) {
LastWaitMissedRange = true;
Log.W($"[SIM] WaitForEncoderRotateRange MISSED. curY={curY:F3}, range=[{minY:F3}..{maxY:F3}]");
return;
}
// 최후 안전장치
if (sw.ElapsedMilliseconds - t0 > 5000) {
LastWaitMissedRange = true;
Log.W($"[SIM] WaitForEncoderRotateRange TIMEOUT. curY={curY:F3}, range=[{minY:F3}..{maxY:F3}]");
return;
}
Thread.Sleep(1);
}
}
private void PullEncoderFromPlcOnce()
{
// 방어: delegate 미설정이면 아무 것도 못함
if (PLCReadDWord == null || _config == null || _config.MarkingOnTheFlyEncoderResolutionRotate == 0)
return;
// 1) 랩핑각 (0~360)
int modRaw = PLCReadDWord(PLC_CURRENT_POS_1_ADDR, ePLCDevice.W, true).data;
double angleModDeg = (modRaw / 100_000.0) % 360.0;
if (angleModDeg < 0)
angleModDeg += 360.0;
// 2) 누적각 (계속 증가)
//int absRaw = PLCReadDWord(PLC_CURRENT_POS_ABS_ADDR, ePLCDevice.W, true).data;
//double angleAbsDeg = absRaw / 100_000.0;
if (PLCGetAbsDeg != null) {
_angleAbsDeg = PLCGetAbsDeg();
}
// 3) count로 변환 (n_get_encoder가 반환한다고 가정하는 값)
_simEncXCount = DegToCount(angleModDeg);
_simEncYCount = DegToCountAbs(_angleAbsDeg);
}
public override void SetFlyX(double encoderResolution)
{
// simulation 할때 값 반영 검토 필요
@ -283,16 +493,34 @@ namespace AuroraLaserScanner
}
public override uint LoadList(eListNo no)
{
// simulation 할때 값 반영 검토 필요
return 0;
bool busy = (no == eListNo.LIST1) ? _list1Busy : _list2Busy;
if (busy)
return 0; // 실행 중 → Load 불가
return (uint)no; // Load 가능
}
public override void SetEndList()
{
// simulation 할때 값 반영 검토 필요
_listClosed = true;
}
public override void ExecuteList(eListNo no)
{
// simulation 할때 값 반영 검토 필요
if (!_listClosed)
Log.W("ExecuteList called without SetEndList");
_listClosed = false; // 실행 시작하면서 다시 열림 상태로
_currentExecutingList = no;
if (no == eListNo.LIST1) {
_list1Busy = true;
_list2Busy = false;
} else {
_list2Busy = true;
_list1Busy = false;
}
_listStartMs = sw.ElapsedMilliseconds;
}
public override void StartLoop()
{
@ -304,16 +532,28 @@ namespace AuroraLaserScanner
}
public override void AutoChange()
{
// simulation 할때 값 반영 검토 필요
if (_currentExecutingList == eListNo.NONE)
return;
_autoChangeRequested = true;
}
public override bool GetBusyStatus()
{
// simulation 할때 값 반영 검토 필요
return false;
return _list1Busy || _list2Busy || _currentExecutingList != eListNo.NONE;
}
public override void StopExecution()
{
// simulation 할때 값 반영 검토 필요
_list1Busy = false;
_list2Busy = false;
_currentExecutingList = eListNo.NONE;
_autoChangeRequested = false;
_listStartMs = 0;
_listClosed = false;
_angleAbsDeg = 0.0;
_simEncXCount = 0;
_simEncYCount = 0;
}
public override uint GetRTCVersion()
{

View File

@ -205,8 +205,8 @@ namespace ILSOS_ACW_H52A.Sequences
> sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ]
> sb[SEQ_EXECUTE_WELDING] > sb[SEQ_REQUEST_UNLOAD_CELL] > sb[SEQ_CHECK_CAN_WELDED]
> sb[SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ] > sb[SEQ_DO_ID_REPORT_CELL_RESULT_SEQ] > sb[SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ]
> sb[SEQ_CHECK_CYCLE_STOP]
> sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]);
> sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_CHECK_CYCLE_STOP]
> sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]);
sb.Flows(sb[SEQ_READY_SCANNER_LIST] > sb[SEQ_EXECUTE_WELDING]);
@ -269,8 +269,6 @@ namespace ILSOS_ACW_H52A.Sequences
_RecipeManager = Application.Current.AuroraEnv().RecipeManager;
_SystemParameter = context.GetSetting<SystemParameter>(CommonSettingIds.SystemParameter);
InitDev(context);
if (!CheckDeviceReady(context))
{ //Check Device
@ -495,8 +493,8 @@ namespace ILSOS_ACW_H52A.Sequences
WaitingScannerList.Reset();
CompleteScannerList.Reset();
isTheFirstListWrite = true; // 작업 시작 시 처음 2개의 List는 입력하고 시작한다
isTheSecondListWrite = true;
isTheFirstListWrite = false; // 작업 시작 시 처음 2개의 List는 입력하고 시작한다
isTheSecondListWrite = false;
isTheFirstListExecute = true; // 제일 첫 List는 ExecuteList 필요. 이후는 AutoChange
InitAutoRunBit(); // GetRotaryCurrentPosition() 호출 이후 AutoStatus True 변경
@ -570,6 +568,8 @@ namespace ILSOS_ACW_H52A.Sequences
return SEQ_INIT_CONDITION_ERROR;
}
Status.EquipmentMode = eEquipmentMode.AUTO;
var startTime = DateTime.Now;
context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime);
Thread.Sleep(1); // 부하 방지
@ -684,6 +684,7 @@ namespace ILSOS_ACW_H52A.Sequences
}
UpdateProductCount(context);
UpdateProductInfoForView();
SaveCellAPD();
}
//if (_SystemParameter.BypassModeLBAD == false) {
@ -838,33 +839,40 @@ namespace ILSOS_ACW_H52A.Sequences
SetInputScannerList(_WeldingScannerListNo, DateTime.Now, _WeldingCount, _WeldingStartAngle, _WeldingJigNo, _RotaryJigCount, canExist, LotId, CellId);
_RecipeNo = _RecipeManager.ActivatedRecipe().Id;
var item = _SelectedDrawObjectList[0];
if (item is Line)
{
Line lineItem = (Line)item;
ScannerParam laserParam = WeldingRecipeManager.GetScannerParam(lineItem.ScannerParamNo);
// EDC 보내는 용도
Status.GetProcessingWeldingData().LaserPowerCore = laserParam.LaserPower1.ToString();
Status.GetProcessingWeldingData().MarkSpeed = laserParam.MarkSpeed.ToString();
Status.GetProcessingWeldingData().FocalLength = (_PLC.ReadDWord((int)eWordInput.PLC_WELDING_FOCAL_LENGTH).data / 1000f).ToString();
Status.GetProcessingWeldingData().WeldingJigNo = _WeldingJigNo.ToString();
}
SetWeldingData();
}
else
{
return SEQ_EXECUTE_WELDING;
}
//var item = _SelectedDrawObjectList[0];
// 여기서 어디로 가는지 설정 필요함
return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ;
#endregion
}
private ScannerParam GetSelectedDrawObjectData(IDrawObject DrawObj)
{
if (DrawObj is Line lineItem)
return WeldingRecipeManager.GetScannerParam(lineItem.ScannerParamNo);
else if (DrawObj is Arc arcItem)
return WeldingRecipeManager.GetScannerParam(arcItem.ScannerParamNo);
else if (DrawObj is Group groupItem)
return WeldingRecipeManager.GetScannerParam(groupItem.ScannerParamNo);
return null;
}
private void SetWeldingData()
{
ScannerParam laserParam = null;
laserParam = GetSelectedDrawObjectData(_SelectedDrawObjectList[0]);
if (laserParam != null)
{
// EDC 보내는 용도
Status.GetProcessingWeldingData().LaserPowerCore = laserParam.LaserPower1.ToString();
Status.GetProcessingWeldingData().MarkSpeed = laserParam.MarkSpeed.ToString();
Status.GetProcessingWeldingData().FocalLength = (_PLC.ReadDWord((int)eWordInput.PLC_WELDING_FOCAL_LENGTH).data / 1000f).ToString();
Status.GetProcessingWeldingData().WeldingJigNo = _WeldingJigNo.ToString();
}
}
public void WaitForEncoderNextRotarySection(int weldingCount, int rotaryJigCount, double rotaryAngleOffset, double encoderResetOffset, out double sectionStartAngle)
@ -1179,13 +1187,19 @@ namespace ILSOS_ACW_H52A.Sequences
return SEQ_CHECK_CYCLE_STOP;
}
private void SaveCellAPD()
{
if (Status.GetProcessingWeldingData().APD.Count > 0)
{
string directoryPath = "D:\\ILSOSConfig\\APD_History";
string fileName = DateTime.Now.ToString("HH") + "_CellAPD";
string fullPath = Path.Combine(directoryPath, Status.GetProcessingWeldingData().APD.First().Date.ToString("yyyy-MM-dd"), fileName);
Status.GetProcessingWeldingData().APD.CreateFiles(fullPath);
}
}
private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param)
{
string directoryPath = "D:\\ILSOSConfig\\APD_History";
string fileName = Status.GetProcessingWeldingData().APD.First().Date.ToString("HHmmss") + "_" + Status.GetProcessingWeldingData().APD.First().CellID;
string fullPath = Path.Combine(directoryPath, Status.GetProcessingWeldingData().APD.First().Date.ToString("yyyy-MM-dd"), fileName);
Status.GetProcessingWeldingData().APD.CreateFiles(fullPath);
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true);
return SequenceAction.Ok();
}
@ -1309,6 +1323,8 @@ namespace ILSOS_ACW_H52A.Sequences
private void OnSeqActionStop()
{
Status.EquipmentMode = eEquipmentMode.MANUAL;
//PLC All BIT OFF
for (int i = 0; i < _RotaryJigCount; i++)
{

View File

@ -1,11 +1,15 @@
using Aurora;
using Aurora.Api;
using AuroraPLC;
using Hermes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WeldingCommon.Devices;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar;
namespace ILSOS_CCW_H52A.Device
{
@ -13,7 +17,183 @@ namespace ILSOS_CCW_H52A.Device
{
public PLCControllerCCWH52A(string deviceId, ePLCNetType plcNetType, PLCMMapFileType fileType, IAuroraEnvironment aurora) : base(deviceId, plcNetType, fileType, aurora)
{
}
protected override int DoActivate(IDeviceConfig config)
{
var ret = base.DoActivate(config);
if (ret == E1.OK && IsSimul) {
//WriteBit((int)eBitInput.PLC_AUTO_STATUS, true, ePLCDevice.B, false);
//WriteBit((int)eBitInput.PLC_AUTO_REQ, true, ePLCDevice.B, false);
SetMonitoringService(EquipmentSimulator, 1);
}
return ret;
}
protected override int DoDeactivate()
{
StopMonitoringService();
var res = base.DoDeactivate();
if (res != E1.OK) {
Log.E("Deactivate is failed");
return res;
}
return res;
}
// -----------------------------
// Simulation state
// -----------------------------
private const int PLC_CURRENT_POS_1_ADDR = 4; // 기존 (mod 0~360)
private readonly Random _rand = new Random();
private readonly Stopwatch _sw = Stopwatch.StartNew();
private long _lastMs = -1;
private const int RotaryJigCount = 16;
private const double JigPerMsec = 300;
private const double DegreePerRev = 360.0;
private const double OneCycleMsec = JigPerMsec * RotaryJigCount;
private const double DegreePerMsec = DegreePerRev / OneCycleMsec;
private const double DegreePerJig = DegreePerRev / RotaryJigCount; // 22.5
private const double HalfDegreePerJig = DegreePerJig / 2.0; // 11.25
// PLC_CURRENT_POS_1 = degree * 100000
private const double PlcAngleScale = 100_000.0;
// 시퀀스가 "다음 지그"를 미리 읽으므로 안전하게 2칸 선행(권장)
private const int LeadSectors = 2;
// Motor position
private readonly object _absLock = new object();
public double GetSimAbsDeg()
{
long value = System.Threading.Interlocked.Read(ref _angleAbsDeg);
return value / 100000.0;
}
private long _angleAbsDeg = 0; // 누적 각도(계속 증가)
private double _angleModDeg = 0.0; // 0~360 랩핑 각도
// PLC USE_WELD_JIG bits
private readonly bool[] _canExist = new bool[RotaryJigCount];
private readonly bool[] _decided = new bool[RotaryJigCount]; // 이번 회전에서 해당 섹터가 결정됐는지
private int _prevSector = -1;
private static void FillBool(bool[] arr, bool value)
{
if (arr == null)
return;
for (int i = 0; i < arr.Length; i++)
arr[i] = value;
}
private static int Mod(int x, int m)
{
int r = x % m;
return (r < 0) ? (r + m) : r;
}
private int GetSectorFromAngle(double angleDeg)
{
// 시퀀스의 (angle + half)/per와 동일한 기준으로 섹터 결정
return (int)((angleDeg + HalfDegreePerJig) / DegreePerJig) % RotaryJigCount;
}
private void OnSectorChanged(int prev, int curr)
{
// 첫 진입
if (prev < 0) {
// 처음 시작 시에는 전부 OFF 상태에서 시작
FillBool(_canExist, false);
FillBool(_decided, false);
} else {
// 랩(15 -> 0) 감지: 새 바퀴 시작으로 보고 "지나간 영역 OFF"를 강하게 반영
if (prev == RotaryJigCount - 1 && curr == 0) {
FillBool(_canExist, false);
FillBool(_decided, false);
} else {
// 지나간 섹터(prev)는 OFF 처리
_canExist[prev] = false;
_decided[prev] = false; // 다음 회전에서 다시 결정되도록
}
}
// 시퀀스는 "다음 지그"를 읽으므로 curr+1부터 선행 결정
for (int k = 1; k <= LeadSectors; k++) {
int s = Mod(curr + k, RotaryJigCount);
if (!_decided[s]) {
// 7/8 확률 ON (0~7 중 0만 OFF로 만들려면 >0)
bool isCanExist = true;// (_rand.Next(0, 8) > 0);
_canExist[s] = isCanExist;
_decided[s] = true;
}
}
// 현재 섹터(curr)도 켜고 싶다면 여기서 결정 가능
}
private void EquipmentSimulator()
{
if (!ReadBit((int)eBitOutput.PC_AUTO_STATUS, ePLCDevice.B, false).data)
return;
long now = _sw.ElapsedMilliseconds;
if (_lastMs < 0)
_lastMs = now;
long dtMs = now - _lastMs;
if (dtMs < 0)
dtMs = 0;
if (dtMs > 100)
dtMs = 100;
_lastMs = now;
// abs 누적각 (시뮬 전용)
long deltaScaled = (long)Math.Round(DegreePerMsec * dtMs * 100000.0);
System.Threading.Interlocked.Add(ref _angleAbsDeg, deltaScaled);
double absDeg = GetSimAbsDeg();
_angleModDeg = absDeg % DegreePerRev;
if (_angleModDeg < 0)
_angleModDeg += DegreePerRev;
// mod 각도
int plcAngleMod = (int)Math.Round(_angleModDeg * PlcAngleScale);
WriteDWord(PLC_CURRENT_POS_1_ADDR, plcAngleMod, ePLCDevice.W, false);
// 섹터 변경/비트 처리 로직은 기존 그대로
int sectorNow = GetSectorFromAngle(_angleModDeg);
if (sectorNow != _prevSector) {
OnSectorChanged(_prevSector, sectorNow);
_prevSector = sectorNow;
}
WriteBitBlock((int)eBitInput.PLC_USE_WELD_JIG_1, RotaryJigCount, _canExist, ePLCDevice.B, false);
}
public void ResetSimulator()
{
_lastMs = -1;
System.Threading.Interlocked.Exchange(ref _angleAbsDeg, 0);
_angleModDeg = 0.0;
FillBool(_canExist, false);
FillBool(_decided, false);
_prevSector = -1;
// 각도 초기화 반영
WriteDWord(PLC_CURRENT_POS_1_ADDR, 0, ePLCDevice.W, false);
// 지그 비트 초기화 반영
WriteBitBlock((int)eBitInput.PLC_USE_WELD_JIG_1, RotaryJigCount, _canExist, ePLCDevice.B, false);
}
}
}

View File

@ -1,4 +1,6 @@
using Hermes;
using Aurora.Util.HislogPlus;
using AuroraPLC;
using Hermes;
using System;
using System.Collections.Generic;
using System.Linq;
@ -10,6 +12,60 @@ namespace ILSOS_CCW_H52A.Device
{
partial class PLCControllerCCWH52A
{
public (int err, bool data) ReadBit(eBitInput inputAddr, ePLCDevice device = ePLCDevice.B, bool isInput = true) => base.ReadBit((int)inputAddr, device, isInput);
public int WriteBit(eBitOutput outputAddr, bool onOff, ePLCDevice device = ePLCDevice.B, bool isOutput = true) => base.WriteBit((int)outputAddr, onOff, device, isOutput);
public (int err, short data) ReadWord(eWordInput inputAddr, ePLCDevice device = ePLCDevice.W, bool isInput = true) => base.ReadWord((int)inputAddr, device, isInput);
public int WriteWord(eWordOutput outputAddr, short shData, ePLCDevice device = ePLCDevice.W, bool isOutput = true) => base.WriteWord((int)outputAddr, shData, device, isOutput);
public (int err, int data) ReadDWord(eWordInput inputAddr, ePLCDevice device = ePLCDevice.W, bool isInput = true) => base.ReadDWord((int)inputAddr, device, isInput);
public int WriteDWord(eWordOutput outputAddr, int iVal, ePLCDevice device = ePLCDevice.W, bool isOutput = true) => base.WriteDWord((int)outputAddr, iVal, device, isOutput);
public (int err, short[] datas) ReadWords(eWordInput startAddress, eWordInput endAddress, ePLCDevice device = ePLCDevice.W, bool isInput = true) => base.ReadWordBlock((int)startAddress, endAddress - startAddress + 1, device, isInput);
public int WriteWords(eWordOutput startAddress, eWordOutput endAddress, short[] data, ePLCDevice device = ePLCDevice.W, bool isOutput = true) => base.WriteWordBlock((int)startAddress, endAddress - startAddress, data, device, isOutput);
public string ReadASCII(eWordInput start, eWordInput end)
{
StringBuilder sb = new StringBuilder();
var words = ReadWords(start, end);
foreach (var word in words.datas)
sb.Append(Conv_ReadData(PLCBase.eDataType.Type_Ascii, word, 0, false, false));
return sb.ToString();
}
public int WriteWordToBit((eWordOutput addrOffset, int bitNo) address, bool on)
{
var value = base.ReadWord((int)address.addrOffset, ePLCDevice.W, false);
short current = value.data;
if (on) {
current |= (short)(1 << address.bitNo);
} else {
current &= (short)~(1 << address.bitNo);
}
return base.WriteWord((int)address.addrOffset, current, ePLCDevice.W);
}
public bool ReadWordToBit((eWordInput addrOffset, int bitNo) address)
{
short current = ReadWord(address.addrOffset).data;
return (current & (1 << address.bitNo)) != 0;
}
public IEnumerable<bool> ReadWordToBits(eWordInput address, bool isLowerFirst = true)
{
var ret = new List<bool>();
int word = ReadWord(address).data;
if (isLowerFirst) {
for (int idx = 0; idx < 15; idx++)
ret.Add((word & (1 << idx)) != 0);
} else {
for (int idx = 15; idx > 0; idx--)
ret.Add((word & (1 << idx)) != 0);
}
return ret;
}
public override void MappingPLCAddr()
{
PLC_AUTO_REQAddr = (int)eBitInput.PLC_AUTO_REQ;
@ -92,5 +148,17 @@ namespace ILSOS_CCW_H52A.Device
else
return 1000;
}
public int ToggleBit(eBitInput readBit, eBitOutput writeBit)
{
int ret = 0;
if (ReadBit(readBit).data)
ret = WriteBit(writeBit, false);
else
ret = WriteBit(writeBit, true);
Log.I($"{Enum.GetName(typeof(eBitOutput), writeBit)} -> {!ReadBit(readBit).data}");
return ret;
}
}
}

View File

@ -0,0 +1,66 @@
using Aurora;
using Aurora.Api;
using AuroraLaserScanner;
using Hermes;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using WeldingCommon;
using static ILSOS_CCW_H52A.Device.PLCControllerCCWH52A;
namespace ILSOS_CCW_H52A.Device
{
public class ScannerCCWH52A : Scanner
{
// -----------------------------
// Simulation state
// -----------------------------
private PLCControllerCCWH52A _plc;
public ScannerCCWH52A(string deviceId) : base(deviceId)
{
}
protected override int DoActivate(IDeviceConfig config)
{
var ret = base.DoActivate(config);
if (ret == E1.OK && IsSimul) {
if (_plc == null) {
_plc = Application.Current.AuroraEnv().DeviceManager[CommonDeviceIds.PLCDevice] as PLCControllerCCWH52A;
return E2.ABORTED;
}
// 값의 동기화가 필요해서 추가 함
if (!_plc.IsConnected && !_plc.IsSimul)
return E2.ABORTED;
var core = _scannerObj as ScannerSimul;
core.PLCReadBit = _plc.ReadBit;
core.PLCReadWord = _plc.ReadWord;
core.PLCReadDWord = _plc.ReadDWord;
core.PLCGetAbsDeg = _plc.GetSimAbsDeg;
//_plc.ResetSimulator();
core.ResetSim();
core.TrySyncAbsAngleToPlcNow();
SetMonitoringService((_scannerObj as ScannerSimul).EquipmentSimulator, 1);
}
return ret;
}
protected override int DoDeactivate()
{
var ret = base.DoDeactivate();
if (ret == E1.OK && IsSimul){
var core = _scannerObj as ScannerSimul;
StopMonitoringService();
}
return ret;
}
}
}

View File

@ -66,6 +66,7 @@ namespace ILSOS_CCW_H52A.Device
public bool CheckSendCellinfo { get; set; }
public bool CheckSendPLCLaserPower { get; set; }
public bool ReportComplete { get; set; }
public void SetReportComplete(bool comp) => ReportComplete = comp;
public override string ToString() => string.Format("{0},WriteTime({1}),WeldingCount({2}),StartAngle({3}),JigNo({4}),CanExist({5}),LotId({6}),CellId({7})",
ListNo, WriteTime.ToString("HHmmss.fff"), WeldingCount, StartAngle, JigNo, CanExist, LotId, CellId);

View File

@ -11,6 +11,7 @@ using AuroraCellTracker.Views;
using AuroraIO;
using AuroraIO.View;
using AuroraLaser;
using AuroraLaserScanner;
using AuroraLBAD;
using AuroraLBAD.Services;
using AuroraOCT;
@ -140,6 +141,7 @@ namespace ILSOS_CCW_H52A
public override void RegisterDevices(IDeviceRegistry registry)
{
registry.Register(CommonDeviceIds.Scanner, () => new ScannerCCWH52A(CommonDeviceIds.Scanner), new ScannerConfig());
base.RegisterDevices(registry);
var isSimul = ApplicationExt.IsSimulation;

View File

@ -98,6 +98,7 @@
<Compile Include="Device\PLC\PLCContoller.Config.cs" />
<Compile Include="Device\PLC\PLCController.Address.cs" />
<Compile Include="Device\PLC\PLCController.Logic.cs" />
<Compile Include="Device\Scanner\ScannerCCWH52A.cs" />
<Compile Include="Device\Scanner\ScannerList.cs" />
<Compile Include="EquipDef.cs" />
<Compile Include="EquipmentSetup.cs" />
@ -105,7 +106,10 @@
<Compile Include="Recipes\RecipeEventHelper.cs" />
<Compile Include="RMS\EDC.cs" />
<Compile Include="Sequences\Monitoring\AlarmMonitoring.cs" />
<Compile Include="Sequences\ProcessObjects\ProcessManager.cs" />
<Compile Include="Sequences\ProcessObjects\ReceivedCellInfo.cs" />
<Compile Include="Sequences\SeqAutoRunCCWH52A.cs" />
<Compile Include="Sequences\SeqAutoRunRMOTF.cs" />
<Compile Include="Sequences\SequenceContextEx.cs" />
<Compile Include="Views\Main\UCMainAutoRun.xaml.cs">
<DependentUpon>UCMainAutoRun.xaml</DependentUpon>

View File

@ -0,0 +1,126 @@
using Aurora;
using Aurora.Api;
using Aurora.Util.HislogPlus;
using AuroraLaserScanner;
using AuroraPLC;
using ILSOS_CCW_H52A.Device;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using WeldingCommon;
using WeldingCommon.Data;
using static ILSOS_CCW_H52A.Device.PLCControllerCCWH52A;
namespace ILSOS_CCW_H52A.Sequences.ProcessObjects
{
public class ProcessManager
{
private static ProcessManager _Instance;
public static ProcessManager Instance {
get {
if (_Instance is null) {
_Instance = new ProcessManager();
}
return _Instance;
}
}
private IAuroraEnvironment _env;
private PLCControllerCCWH52A _plc;
private ScannerCCWH52A _scanner;
private bool usedAnalyzerLog = false;
private readonly Logger _log = new Logger("ProcessManager");
public const double compensateEncoderReset = 0.015; // 엔코더 리셋 보상 각도 설정(70도/sec 기준 약 0.015도)
public const int RotaryJigCount = 16;
public const int RotaryMaxAngle = 360;
public double DegreePerJig { get => RotaryMaxAngle / RotaryJigCount; }
public double HalfDegreePerJig { get => DegreePerJig / 2; }
//public ConcurrentQueue<ReceivedCellInfo>
public void Init(IAuroraEnvironment env)
{
_env = env;
_plc = _env.DeviceManager[DeviceIds.PLCDevice] as PLCControllerCCWH52A;
_scanner = _env.DeviceManager[DeviceIds.Scanner] as ScannerCCWH52A;
}
public IEnumerable<bool> CheckRotaryState()
{
List<bool> rotaryStates = new List<bool>();
for (int i = 0; i < RotaryJigCount; i++) {
var rotaryProcessState = _plc.ReadBit(eBitInput.PLC_USE_WELD_JIG_1 + i).data;
rotaryStates.Add(rotaryProcessState);
//if (rotaryProcessState == false)
// _plc.ToggleBit(eBitInput.PLC_USE_WELD_JIG_1 + i, eBitOutput.PC_WELD_COMPLETE_1 + i);
}
if (usedAnalyzerLog) {
StringBuilder sb = new StringBuilder();
rotaryStates.ForEach(x => sb.Append($"{x} "));
_log.D($"Check Rotary State : {sb.ToString()}");
}
return rotaryStates;
}
public bool GetRotaryCanExist(int jigNo)
{
if (jigNo == 0)
jigNo = 1;
if (jigNo <= RotaryJigCount)
return _plc.ReadBit(eBitInput.PLC_USE_WELD_JIG_1 + (jigNo - 1)).data;
else
return false;
}
/// <summary>
/// Rotary의 현재 위치정보를 0~360로 전달함
/// </summary>
public double GetRotaryCurrentPosition(bool isView360 = true)
{
try {
double finalValue = Convert.ToDouble(_plc.ReadDWord(eWordInput.PLC_CURRENT_POS_1).data / 100_000f);
finalValue += _scanner.Config.RotaryEncoderOffset;
if (isView360) {
if (RotaryMaxAngle <= finalValue) {
while (RotaryMaxAngle <= finalValue) {
finalValue -= RotaryMaxAngle;
}
}
else if (finalValue < 0) {
while (finalValue < 0) {
finalValue += RotaryMaxAngle;
}
}
}
return finalValue;
}
catch {
return -9999.9999;
}
}
/// <summary>
/// 중심 기준 가장 가까운 지그 번호(내림 연산)
/// <param name="HalfDegreePerJig">내림 카운팅을 중심 기준 값</param>
/// </summary>
/// <returns></returns>
public int getCurrentJigNo() => (int)((GetRotaryCurrentPosition(isView360:true) + HalfDegreePerJig) / DegreePerJig) + 1;
/// <summary>
/// 360도 이상으로 준다고하면 Welding Count를 가져올 수 있음 2word = 2147483.647 / 3600 = 596 >> 596*16 = 9_536까지 카운팅 가능함
/// <param name="HalfDegreePerJig">내림 카운팅을 중심 기준 값</param>
/// </summary>
/// <returns></returns>
public int getWeldingCount() => (int)((GetRotaryCurrentPosition(isView360:false) + HalfDegreePerJig) / DegreePerJig);
}
}

View File

@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ILSOS_CCW_H52A.Sequences.ProcessObjects
{
public class ReceivedCellInfo
{
public ReceivedCellInfo() {
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -236,7 +236,7 @@ namespace WeldingCommon
//registry.Register(CommonDeviceIds.Laser, () => new Laser(CommonDeviceIds.Laser, isSimul), new LaserConfig(),
// device => new UCLaserDevice() { DataContext = new UCLaserDeviceViewModel(AuroraEnv, CommonDeviceIds.Laser) });
registry.Register(CommonDeviceIds.Scanner, () => new Scanner(CommonDeviceIds.Scanner), new ScannerConfig());
//registry.Register(CommonDeviceIds.Scanner, () => new Scanner(CommonDeviceIds.Scanner), new ScannerConfig());
//registry.Register(CommonDeviceIds.SmcChiller, () => new SmcChiller(CommonDeviceIds.SmcChiller, 0, isSimul), new SmcChiller.Config(),
// device => new UCSMCChillerDevice() { DataContext = new UCSMCChillerDeviceViewModel(AuroraEnv, CommonDeviceIds.SmcChiller) });
registry.Register(CommonDeviceIds.SmcChiller, () => new SmcChiller(CommonDeviceIds.SmcChiller), new SmcChillerConfig(),

View File

@ -28,6 +28,8 @@ namespace WeldingCommon.Data
table.Columns.Add("Time", typeof(string));
table.Columns.Add(nameof(ActualProcessUnit.LotID), typeof(string));
table.Columns.Add(nameof(ActualProcessUnit.CellID), typeof(string));
table.Columns.Add(nameof(ActualProcessUnit.HeadNo), typeof(string));
table.Columns.Add(nameof(ActualProcessUnit.WeldType), typeof(string));
table.Columns.Add(nameof(ActualProcessUnit.EncoderCheck), typeof(bool));
table.Columns.Add(nameof(ActualProcessUnit.StartPointX), typeof(double));
table.Columns.Add(nameof(ActualProcessUnit.StartPointY), typeof(double));
@ -60,10 +62,10 @@ namespace WeldingCommon.Data
protected override void InsertItem(int index, ActualProcessUnit item)
{
base.InsertItem(index, item);
table.Rows.Add(item.Date.ToString("yyyy-MM-dd"), item.Date.ToString("HH:mm:ss.fff"), item.LotID, item.CellID, item.EncoderCheck,
item.StartPointX, item.StartPointY, item.EndPointX, item.EndPointY,
item.WeldCondition, item.LaserOnDelay, item.LaserOffDelay, item.JumpDelay, item.MarkDelay,
item.PolygonDelay, item.Power, item.JumpSpeed, item.MarkSpeed, item.JumpX,
table.Rows.Add(item.Date.ToString("yyyy-MM-dd"), item.Date.ToString("HH:mm:ss.fff"), item.LotID, item.CellID, item.HeadNo, item.WeldType, item.EncoderCheck,
item.StartPointX, item.StartPointY, item.EndPointX, item.EndPointY,
item.WeldCondition, item.LaserOnDelay, item.LaserOffDelay, item.JumpDelay, item.MarkDelay,
item.PolygonDelay, item.Power, item.JumpSpeed, item.MarkSpeed, item.JumpX,
item.JumpY);
}
@ -78,8 +80,14 @@ namespace WeldingCommon.Data
{
public DateTime Date { get => _Date; set => SetProperty(ref _Date, value); }
private DateTime _Date;
public string LotID { get => Status.GetProcessingWeldingData().LotId; }
public string CellID { get => Status.GetProcessingWeldingData().CellId; }
public string LotID { get => _LodID; set => SetProperty(ref _LodID, value); }
private string _LodID;
public string CellID { get => _CellID; set => SetProperty(ref _CellID, value); }
private string _CellID;
public string HeadNo { get => _HeadNo; set => SetProperty(ref _HeadNo, value); }
private string _HeadNo;
public string WeldType { get => _WeldType; set => SetProperty(ref _WeldType, value); }
private string _WeldType;
public bool EncoderCheck { get => _EncoderCheck; set => SetProperty(ref _EncoderCheck, value); }
private bool _EncoderCheck;
public double StartPointX { get => _StartPointX; set => SetProperty(ref _StartPointX, value); }

View File

@ -20,7 +20,7 @@ namespace WeldingCommon.Devices
public abstract partial class PLCController : PLCBase
{
// Log 사용 방법
static Logger Log = new Logger("PLCController");
protected static Logger Log = new Logger("PLCController");
//static Logger EventLog = new Logger(eMsg_TYPE.PLC.ToString(), "Event");

View File

@ -108,11 +108,11 @@
public const string Laser = "Laser";
public const string LaserIO = "LaserIO";
public const string Scanner = "Scanner";
public const string SmcChiller = "SmcChiller";
public const string PowerMeter = "PowerMeter";
public const string Light = "Light"; // Camera Light Controller
public const string PLCDevice = "PLC";
public const string Scanner = "Scanner";
public const string IODevice = "IO_Board";
public const string CellTracker = "CellTracker";

View File

@ -345,27 +345,22 @@ namespace WeldingCommon.Sequences
_FindPatternCount = 0;
return SEQ_DECISION_CHECK_OFFSET;
}
} else {
if (_InitParam.Is1StepCalibration)
{
_Is1stepCount++;
Log.I(string.Format($"Is1stepCount : {_Is1stepCount} "));
return SEQ_FIND_PATTERN;
}
else
{
if(_NotFoundCount > 5)
{
Log.I(string.Format($"Pattern Not Found. NotFoundCount OverFlow. Next Sequnece : SetVisionPositionFailed."));
}
if (_InitParam.Is1StepCalibration) {
_Is1stepCount++;
Log.I(string.Format($"Is1stepCount : {_Is1stepCount} "));
return SEQ_FIND_PATTERN;
}
if (_NotFoundCount > 5) {
Log.I(string.Format($"Pattern Not Found. NotFoundCount OverFlow. Next Sequnece : SetVisionPositionFailed."));
return SEQ_SET_VISION_POSITION_FAILED;
}
else
{
_NotFoundCount++;
Log.I(string.Format($"NotFoundCount : {_NotFoundCount} "));
return SEQ_FIND_PATTERN;
}
}
else {
_NotFoundCount++;
Log.I(string.Format($"NotFoundCount : {_NotFoundCount} "));
return SEQ_FIND_PATTERN;
}
}
@ -412,12 +407,15 @@ namespace WeldingCommon.Sequences
{
Log.I(string.Format($"Find Pattern Result Offset:[{_PatternOffsetX},{_PatternOffsetY}] Score:[{_Score}]"));
// Pattern Find OK
if (Math.Max(Math.Abs(_PatternOffsetX), Math.Abs(_PatternOffsetY)) < _InitParam.CheckOffset) {
Log.I(string.Format($"Check Offset Result True Criteria:[{_InitParam.CheckOffset}] Max Offset:[{Math.Max(Math.Abs(_PatternOffsetX), Math.Abs(_PatternOffsetY))}"));
Log.FuncI(string.Format($"Check Offset Result True Criteria:[{_InitParam.CheckOffset}] Max Offset:[{Math.Max(Math.Abs(_PatternOffsetX), Math.Abs(_PatternOffsetY))}"));
return SEQ_SET_VISION_POSITION;
} else if (_FindPatternCount < 10) {
}
// Pattern Find nOK >> 설정 범위 넘어감
else if (_FindPatternCount < 10) {
_CheckOffsetCount++;
Log.I(string.Format($"Check Offset Result True Criteria:[{_InitParam.CheckOffset}] Max Offset:[{Math.Max(Math.Abs(_PatternOffsetX), Math.Abs(_PatternOffsetY))}"));
Log.FuncI(string.Format($"Find Pattern Count[{_FindPatternCount}]"));
return SEQ_FIND_PATTERN;
} else {
Log.E(String.Format("ERROR_CHECK_OFFSET_COUNT_OVER"));

View File

@ -409,6 +409,7 @@ namespace WeldingCommon.Sequences {
//CSV Laser Log 추가
laserLog.Date = DateTime.Now;
laserLog.WeldType = "Line";
laserLog.WeldCondition = param.No + 1;
laserLog.LaserOnDelay = param.LaserOnDelay;
laserLog.LaserOffDelay = param.LaserOffDelay;
@ -700,7 +701,7 @@ namespace WeldingCommon.Sequences {
Log.I(string.Format($"[Line Weld] Mark(Ramping) ({obj.PF2.X},{obj.PF2.Y})"));
} else {
// Ramping 이 없는 경우
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.PF2.X + xOffset, obj.PF2.Y + yOffset);
@ -723,6 +724,9 @@ namespace WeldingCommon.Sequences {
laserLog.StartPointY = obj.PF1.Y;
laserLog.EndPointX = obj.PF2.X;
laserLog.EndPointY = obj.PF2.Y;
laserLog.LotID = Status.GetProcessingWeldingData().WorkLotId;
laserLog.CellID = Status.GetProcessingWeldingData().WorkCellId;
laserLog.HeadNo = Status.GetProcessingWeldingData().WeldingJigNo;
Status.GetProcessingWeldingData().APD.Add(laserLog);
}
@ -1408,12 +1412,26 @@ namespace WeldingCommon.Sequences {
// 1.스캐너 가공조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
var laserLog = new ActualProcessUnit();
float percentLaser1 = 0;
float percentLaser2 = 0;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[Arc Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
//CSV Laser Log 추가
laserLog.Date = DateTime.Now;
laserLog.WeldType = "Arc";
laserLog.WeldCondition = param.No + 1;
laserLog.LaserOnDelay = param.LaserOnDelay;
laserLog.LaserOffDelay = param.LaserOffDelay;
laserLog.JumpDelay = param.JumpDelay;
laserLog.MarkDelay = param.MarkDelay;
laserLog.PolygonDelay = param.PolygonDelay;
laserLog.Power = param.LaserPower1;
laserLog.JumpSpeed = param.JumpSpeed;
laserLog.MarkSpeed = param.MarkSpeed;
// 2. 스캐너 가공 작업
// 2.1. Wobbel 관련 설정
// Snowman 사용 모드가 아닐 경우 Wobbel 모드 실행
@ -1590,7 +1608,7 @@ namespace WeldingCommon.Sequences {
// 2.2.3.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 2.2.3.2. Constant Range 구간 위치 변경
for (int iter = risingEdgeCount+firstEdgeCount; iter < totalCount - fallingEdgeCount; iter++) {
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, theta);
@ -1637,6 +1655,16 @@ namespace WeldingCommon.Sequences {
_LastX = obj.PF2.X + xOffset;
_LastY = obj.PF2.Y + yOffset;
//CSV Laser Log 추가
laserLog.StartPointX = obj.PF1.X;
laserLog.StartPointY = obj.PF1.Y;
laserLog.EndPointX = obj.PF2.X;
laserLog.EndPointY = obj.PF2.Y;
laserLog.LotID = Status.GetProcessingWeldingData().WorkLotId;
laserLog.CellID = Status.GetProcessingWeldingData().WorkCellId;
Status.GetProcessingWeldingData().APD.Add(laserLog);
if (obj.MadebyRCC) {
Log.I(string.Format($"[RCC SubPath Arc Weld] Arc center ({obj.CenterPointF.X + xOffset},{obj.CenterPointF.Y + yOffset}), Sweep angle : ({obj.SweepAngle})"));
} else {
@ -1653,12 +1681,26 @@ namespace WeldingCommon.Sequences {
// 1.스캐너 가공조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
var laserLog = new ActualProcessUnit();
float percentLaser1 = 0;
float percentLaser2 = 0;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[Arc3P Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
//CSV Laser Log 추가
laserLog.Date = DateTime.Now;
laserLog.WeldType = "Arc3";
laserLog.WeldCondition = param.No + 1;
laserLog.LaserOnDelay = param.LaserOnDelay;
laserLog.LaserOffDelay = param.LaserOffDelay;
laserLog.JumpDelay = param.JumpDelay;
laserLog.MarkDelay = param.MarkDelay;
laserLog.PolygonDelay = param.PolygonDelay;
laserLog.Power = param.LaserPower1;
laserLog.JumpSpeed = param.JumpSpeed;
laserLog.MarkSpeed = param.MarkSpeed;
// 2. 스캐너 가공 작업
// 2.1. Wobbel 관련 설정
// Snowman 사용 모드가 아닐 경우 Wobbel 모드 실행
@ -1883,6 +1925,16 @@ namespace WeldingCommon.Sequences {
_LastX = obj.PF2.X + xOffset;
_LastY = obj.PF2.Y + yOffset;
//CSV Laser Log 추가
laserLog.StartPointX = obj.PF1.X;
laserLog.StartPointY = obj.PF1.Y;
laserLog.EndPointX = obj.PF2.X;
laserLog.EndPointY = obj.PF2.Y;
laserLog.LotID = Status.GetProcessingWeldingData().WorkLotId;
laserLog.CellID = Status.GetProcessingWeldingData().WorkCellId;
Status.GetProcessingWeldingData().APD.Add(laserLog);
if (obj.MadebyRCC) {
Log.I(string.Format($"[RCC SubPath Arc3P Weld] Arc center ({obj.CenterPointF.X + xOffset},{obj.CenterPointF.Y + yOffset}), Sweep angle : ({obj.SweepAngle})"));
} else {

View File

@ -52,12 +52,14 @@ namespace WeldingCommon.Settings
var forRecipes = VisionRecipeManager.GetFovRecipes(forInfos);
if (forRecipes == null)
return;
foreach (var forRecipe in forRecipes) {
// PixelSize
forRecipe.CalFovRecipe.PixelSize.X = SysParam.CameraPixelSize;
forRecipe.CalFovRecipe.PixelSize.Y = SysParam.CameraPixelSize;
}
RecipeFileManager.SaveVisionRecipe();
#warning 여기서 이거 왜 새로 저장하는건지 확인 필요함
//foreach (var forRecipe in forRecipes) {
// // PixelSize
// forRecipe.CalFovRecipe.PixelSize.X = SysParam.CameraPixelSize;
// forRecipe.CalFovRecipe.PixelSize.Y = SysParam.CameraPixelSize;
//}
//RecipeFileManager.SaveVisionRecipe();
}
}
}

View File

@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WeldingCommon.Utils
{
public class ChangeChecker
{
private static Dictionary<string, bool> mapping = new Dictionary<string, bool>();
public static bool IsChangedBool(string Resource, bool value, bool defaultVal = false)
{
if (mapping.ContainsKey(Resource) == false) {
mapping[Resource] = defaultVal;
return false;
}
if (mapping[Resource] == value) {
return false;
}
mapping[Resource] = value;
return true;
}
public static bool IsChangedBoolMonitoring(string Resource, bool value, bool MonitoringValue, bool defaultVal = false)
{
if (mapping.ContainsKey(Resource) == false) {
mapping[Resource] = defaultVal;
return false;
}
if (mapping[Resource] == value) {
return false;
}
mapping[Resource] = value;
if(mapping[Resource] == MonitoringValue)
return true;
return false;
}
}
}

View File

@ -61,6 +61,15 @@
<DataGridTextColumn Header="Master Y" Binding="{Binding MasterY}"/>
<DataGridTextColumn Header="Vision X" Binding="{Binding VisionX}"/>
<DataGridTextColumn Header="Vision Y" Binding="{Binding VisionY}"/>
<DataGridTemplateColumn Header="Get Center Count">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Click"
Command="{Binding DataContext.CMD_GetCenterCnt, RelativeSource={RelativeSource AncestorType=DataGrid}}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</GroupBox>

View File

@ -44,6 +44,7 @@ namespace WeldingCommon.Views
public ICommand CMD_StopScannerCalibration { get; }
public ICommand CMD_FindPattern { get; }
public ICommand CMD_CalculateDistance { get; }
public ICommand CMD_GetCenterCnt { get; }
//public ICommand CMD_ChangedCaltype { get; }
//public ICommand CMD_SeqStop { get; }
@ -182,6 +183,7 @@ namespace WeldingCommon.Views
CMD_StopScannerCalibration = new DelegateCommand(PerformCmdStopScannerCalibration);
CMD_FindPattern = new DelegateCommand(PerformCmdFindPattern);
CMD_CalculateDistance = new DelegateCommand(PerformCmdCalculateDistance);
CMD_GetCenterCnt = new DelegateCommand(PerformCmdGetCenterCount);
_MonitorTimer = new DispatcherTimer();
_MonitorTimer.Tick += OnMonitoring;
@ -191,6 +193,7 @@ namespace WeldingCommon.Views
_Aurora.EventObservable.AsObservable(CommonSequenceIds.SeqScannerCalibration)
.ObserveOnDispatcher().Subscribe(x => OnUpdateCorrectionTable(x));
}
private void InitVariable()
{
GridRowCount = 13;
@ -618,6 +621,19 @@ namespace WeldingCommon.Views
Log.W($"{e.Message}");
}
}
private void PerformCmdGetCenterCount(object obj)
{
_Scanner.GetXYPos(out double currentX, out double currentY);
// Vision Scanner Calibration인 경우
int visionBitX = (int)(currentX * _Scanner.Config.ScaleXYFactor);
int visionBitY = (int)(currentY * _Scanner.Config.ScaleXYFactor);
VisionCorrectionList[SelectedRowIndex].VisionX = visionBitX;
VisionCorrectionList[SelectedRowIndex].VisionY = visionBitY;
}
}
public class CorrectionUnit : NotifyPropertyChangedBase
{
@ -647,5 +663,4 @@ namespace WeldingCommon.Views
private double _Distance;
public double Distance { get => _Distance; set => SetProperty(ref _Distance, value); }
}
}

View File

@ -248,6 +248,7 @@
<Compile Include="Settings\SystemParameter.cs" />
<Compile Include="Settings\SystemParameterManager.cs" />
<Compile Include="Status.cs" />
<Compile Include="Utils\ChangeChecker.cs" />
<Compile Include="Utils\CSV_Manager.cs" />
<Compile Include="Utils\ExcelManagementUnit.cs" />
<Compile Include="Utils\ImageUtil.cs" />