diff --git a/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj b/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj
index 7e803d2..67fa239 100644
--- a/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj
+++ b/Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj
@@ -108,6 +108,7 @@
+
UCMainAutoRun.xaml
diff --git a/Machines/ILSOS_CCW_H52A/Sequences/ProcessManager.cs b/Machines/ILSOS_CCW_H52A/Sequences/ProcessManager.cs
index 57ba05c..9b71740 100644
--- a/Machines/ILSOS_CCW_H52A/Sequences/ProcessManager.cs
+++ b/Machines/ILSOS_CCW_H52A/Sequences/ProcessManager.cs
@@ -7,14 +7,30 @@ using ILSOS_CCW_H52A.Device;
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows;
+using WeldingCommon;
using static ILSOS_CCW_H52A.Device.PLCControllerCCWH52A;
namespace ILSOS_CCW_H52A.Sequences
{
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");
@@ -24,26 +40,20 @@ namespace ILSOS_CCW_H52A.Sequences
public double DegreePerJig { get => RotaryMaxAngle / RotaryJigCount; }
public double HalfDegreePerJig { get => DegreePerJig / 2; }
-
- private readonly IAuroraEnvironment _env;
- private readonly PLCControllerCCWH52A _plc;
- private readonly Scanner _scanner;
-
- public ProcessManager()
+ public void Init(IAuroraEnvironment env)
{
+ _env = env;
_plc = _env.DeviceManager[DeviceIds.PLCDevice] as PLCControllerCCWH52A;
- _scanner = _env.DeviceManager[DeviceIds.Scanner] as Scanner;
+ _scanner = _env.DeviceManager[DeviceIds.Scanner] as ScannerCCWH52A;
}
-
public IEnumerable CheckRotaryState()
{
List rotaryStates = new List();
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 (rotaryProcessState == false)
+ // _plc.ToggleBit(eBitInput.PLC_USE_WELD_JIG_1 + i, eBitOutput.PC_WELD_COMPLETE_1 + i);
}
if (usedAnalyzerLog) {
@@ -57,8 +67,6 @@ namespace ILSOS_CCW_H52A.Sequences
public bool GetRotaryCanExist(int jigNo)
{
- //var RotaryStates = CheckRotaryState();
-
if (jigNo == 0)
jigNo = 1;
diff --git a/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs
new file mode 100644
index 0000000..352aaaa
--- /dev/null
+++ b/Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs
@@ -0,0 +1,1217 @@
+using Aurora;
+using Aurora.Api;
+using Aurora.Api.Manager;
+using Aurora.View;
+using AuroraCellTracker;
+using AuroraIO;
+using AuroraLaser;
+using AuroraLaserScanner;
+using AuroraLBAD.Service;
+using CoreUnit.Common;
+using CoreUnit.Control.UserControl.Canvas;
+using CoreUnit.Scanner.UserControl.ScannerCanvas;
+using DB;
+using Hermes;
+using ILSOS_CCW_H52A.Device;
+using ILSOS_CCW_H52A.Models;
+using ILSOS_CCW_H52A.RMS;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using WeldingCommon;
+using WeldingCommon.Alarm;
+using WeldingCommon.Recipes;
+using WeldingCommon.Sequences;
+using WeldingCommon.Settings;
+using WeldingCommon.Utils;
+using static AuroraLBAD.Service.StreamLightLBAD;
+using static ILSOS_CCW_H52A.Device.IOController_CCW_H52A;
+using static ILSOS_CCW_H52A.Device.PLCControllerCCWH52A;
+using static ILSOS_CCW_H52A.Device.ScannerListCCWH52A;
+
+namespace ILSOS_CCW_H52A.Sequences
+{
+ public class SeqAutoRunRMOTF
+ {
+ static readonly Logger Log = new Logger("SeqAutoRunRMOTF");
+
+ const string SEQ_ENTER = "SEQ_ENTER"; // inputParam 으로 받자.
+ const string SEQ_CHECK_INIT_CONDITION = "Check Init Condition"; // Check Init Condition
+
+ const string SEQ_FORK_ENCODER_MONITOR = "Fork Encoder Monitor";
+ const string SEQ_ENCODER_MONITOR = "Check Encoder Monitor";
+ const string SEQ_JOIN_ENCODER_MONITOR = "Join Encoder Monitor";
+
+ // TODO: [장비구현필요] 실제 공정 추가
+ const string SEQ_CHECK_LOAD_COMPLETE = "Check Load Complete";
+
+ const string SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ = "Set Sequence Param (UploadWeldingList Seq)";
+ const string SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ = "Process (UploadWeldingList Seq)";
+ const string SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ = "Get Sequence Result (UploadWeldingList Seq)";
+
+ public const string SEQ_CHECK_SCANNER_LIST_COMPLETE = "Check Scanner List Complete";
+ const string SEQ_READY_SCANNER_LIST = "Ready ScannerList";
+
+ const string SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ = "Set Sequence Param (ReceiveCellInfo Seq)";
+ const string SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ = "Process (ReceiveCellInfo Seq)";
+ const string SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ = "Get Sequence Result (ReceiveCellInfo Seq)";
+
+ const string SEQ_EXECUTE_WELDING = "Execute Welding";
+ const string SEQ_REQUEST_UNLOAD_CELL = "Request Unload Cell";
+ //const string SEQ_WAIT_LBAD_JUDGE = "Wait LBAD Judge";
+
+ const string SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ = "Set Sequence Param (ReportCellResult Seq)";
+ const string SEQ_DO_ID_REPORT_CELL_RESULT_SEQ = "Process (ReportCellResult Seq)";
+ const string SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ = "Get Sequence Result (ReportCellResult Seq)";
+
+ //const string SEQ_REPORT_CELLINFO_TO_PLC = "ReportCellInfoToPLC";
+ const string SEQ_CHECK_CYCLE_STOP = "Check Cycle Stop";
+ const string SEQ_CHECK_CAN_WELDED = "Check Can Welded";
+
+ const string SEQ_SAVE_RESULT_DATA = "Save Result Data";
+
+ const string SEQ_WRITE_COMPLETION_PLC_BIT = "Write Completion PLC Bit";
+ const string SEQ_INIT_CONDITION_ERROR = "Init Condition Error";
+ const string SEQ_COMPLETE = "SEQ_COMPLETE"; // 완료
+
+ static class ContextKey
+ {
+ public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqAutoRunCCWH52A";
+ public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqAutoRunCCWH52A";
+ public const string KEY_SEQUENCE_ERROR_CODE = "KEY_SEQUENCE_ERROR_CODE_SeqAutoRunCCWH52A";
+ }
+
+ const int PLC_TIMEOUT = 60; // sec, Timeout은 시퀀스 정지가 아니라 타임아웃 시간마다 로그만 남기도록 한다
+
+ private Scanner _Scanner;
+ private LaserDevice _Laser;
+
+ private PLCControllerCCWH52A _PLC;
+ private IODevice _IO;
+ private StreamLightLBAD _StreamLightLBAD;
+ private static CellTracker _CellTracker;
+
+ private SystemParameter _SystemParameter;
+ private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl; //스캐너 동작관련 객체 선언
+
+ private string _LotId = "N/A";
+ private string _CellId = "N/A";
+ private int _ModelNo;
+
+ private IRecipeManager _RecipeManager;
+
+ private eListNo _WeldingScannerListNo;
+
+ private List _SelectedDrawObjectList;
+
+ private SeqAutoRunCCWH52A_InitParam _InitParam;
+
+ private uint _TodayProduct;
+ private uint _TotalProduct;
+
+ private double _LaserPowerByLBAD;
+ private int _CompletedJigNo;
+ private static bool isWeled;
+
+ private bool _ModulationMode;
+
+ private DateTime _AutoRunStartTime;
+ private DateTime _WeldingStartTime;
+ private TimeSpan _WeldingSpanTime;
+ private DateTime _WeldEndTime;
+ private TimeSpan _InterfaceSpanTime;
+ private DateTime _InterfaceStartTime;
+
+ private double _CellMeasureCenterOffsetX;
+ private double _CellMeasureCenterOffsetY;
+
+ // MOTF
+ private TimeSpan _WeldInterval;
+
+ private int _LastScannerParamNo = -1; //마지막 작업한 스캔 변수값들이 현재 작업하려는 스캔 변수값과 같은지 판단함(같으면 설정작업을 skip함)
+ private int _EncoderOverflowCount = 0;
+
+ private double _RotaryEncoderOffset = 0;
+ private int _WeldingJigNo = -1;
+ private int _WeldingCount = 0;
+ private int _RotaryJigCount = 16;
+
+ private double _WeldingStartAngle;
+
+ private bool isTheFirstListWrite;
+ private bool isTheSecondListWrite;
+ private bool isTheFirstListExecute;
+ private int _RecipeNo;
+
+ private bool UsePlcShotThemAll { get; set; } = false;
+ private bool UsePlcPositionCompensation { get; set; } = false;
+ private string RecipeFileName { get; set; } = "";
+ private enum InitErrorList
+ {
+ SUCCESS = 0,
+ LBAD_CLIENT_ACTIVATE_FAILED,
+ LBAD_SUB_ACTIVATE_FAILED,
+ LASER_INIT_ERROR,
+ PLC_AUTORUN_BIT_OFF,
+ MOTF_OFF,
+ PLC_ALARM,
+ }
+ public SequenceBuilder Builder()
+ {
+ var sb = new SequenceBuilder(1000, true);
+
+ sb.AddAction(SEQ_ENTER, Sequence_Enter);
+ sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기
+
+ sb.AddFork(SEQ_FORK_ENCODER_MONITOR);
+ sb.AddAction(SEQ_ENCODER_MONITOR, EncoderMonitor);
+ sb.AddJoin(SEQ_JOIN_ENCODER_MONITOR);
+
+ sb.AddAction(SEQ_CHECK_LOAD_COMPLETE, CheckLoadComplete);
+
+ sb.AddAction(SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ, SetReceiveCellInfo_SeqParams); // Cell 정보를 받아와 입력한다.
+ sb.AddSubsequence(SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ, SequenceManager.GetSeqBuilder_SeqReceiveCellInfo()); // cell 정보를 받아오는 Sub Seq 실행
+ sb.AddAction(SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ, GetReceiveCellInfo_SeqResult); // Cell 정보를 가져온다
+
+ sb.AddAction(SEQ_CHECK_SCANNER_LIST_COMPLETE, CheckScannerListComplete);
+ sb.AddDecision(SEQ_READY_SCANNER_LIST, ReadyScannerList);
+
+ sb.AddAction(SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ, SetUploadWeldingList_SeqParams);
+ sb.AddSubsequence(SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ, SequenceManager.GetSeqBuilder_SeqUploadWeldingList());
+ sb.AddAction(SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ, GetUploadWeldingList_SeqResult);
+
+ sb.AddAction(SEQ_EXECUTE_WELDING, ExecuteWelding);
+ sb.AddAction(SEQ_REQUEST_UNLOAD_CELL, RequestUnloadCell);
+
+ sb.AddAction(SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ, SetReportCellResult_SeqParams);
+ sb.AddSubsequence(SEQ_DO_ID_REPORT_CELL_RESULT_SEQ, SequenceManager.GetSeqBuilder_SeqReportCellResult());
+ sb.AddAction(SEQ_GET_RESULT_REPORT_CELL_RESULT_SEQ, GetReportCellResult_SeqResult);
+
+ sb.AddDecision(SEQ_CHECK_CYCLE_STOP, CheckCycleStop);
+ sb.AddDecision(SEQ_CHECK_CAN_WELDED, CheckCanWelded);
+
+ sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함.
+ sb.AddAction(SEQ_WRITE_COMPLETION_PLC_BIT, WriteCompletionPLCBit); // 정상적으로 종료 되면, complete bit write
+ sb.AddAction(SEQ_INIT_CONDITION_ERROR, InitConditionError);
+ sb.AddAction(SEQ_COMPLETE, Complete);
+
+ sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_CHECK_LOAD_COMPLETE]
+ > sb[SEQ_SET_PARAMS_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_DO_ID_RECEIVE_CELL_INFO_SEQ] > sb[SEQ_GET_PARAMS_RECEIVE_CELL_INFO_SEQ]
+ > sb[SEQ_CHECK_SCANNER_LIST_COMPLETE] > sb[SEQ_READY_SCANNER_LIST]
+ > 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_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]);
+
+ sb.Flows(sb[SEQ_CHECK_CAN_WELDED] > sb[SEQ_CHECK_CYCLE_STOP]); // 용접을 하지 않은 Cell은 상위보고 하지 않는다.
+ sb.Flows(sb[SEQ_CHECK_CYCLE_STOP] > sb[SEQ_CHECK_LOAD_COMPLETE]);
+
+ sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_INIT_CONDITION_ERROR] > sb.Final);
+ sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_ENCODER_MONITOR] > sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]);
+
+ #region
+ //sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_ENCODER_MONITOR]);
+ ////sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_CHECK_LOAD_COMPLETE]);
+ //sb.Flows(sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]);
+ ///
+ //// 1. 시퀀스 시작 ~ 포크
+ //sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_FORK_ENCODER_MONITOR]);
+
+ //// 2. Fork 이후 메인 루트
+ //sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_CHECK_LOAD_COMPLETE]);
+ //sb.Flows(sb[SEQ_CHECK_LOAD_COMPLETE] > sb[SEQ_CHECK_SCANNER_LIST_COMPLETE]);
+ //sb.Flows(sb[SEQ_CHECK_SCANNER_LIST_COMPLETE] > sb[SEQ_READY_SCANNER_LIST]);
+ //sb.Flows(sb[SEQ_READY_SCANNER_LIST] > sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ]);
+ //sb.Flows(sb[SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ]);
+ //sb.Flows(sb[SEQ_DO_ID_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ]);
+ //sb.Flows(sb[SEQ_GET_RESULT_UPLOAD_WELDING_LIST_SEQ] > sb[SEQ_EXECUTE_WELDING]);
+ //sb.Flows(sb[SEQ_EXECUTE_WELDING] > sb[SEQ_REQUEST_UNLOAD_CELL]);
+ //sb.Flows(sb[SEQ_REQUEST_UNLOAD_CELL] > sb[SEQ_CHECK_CYCLE_STOP]);
+ //sb.Flows(sb[SEQ_CHECK_CYCLE_STOP] > sb[SEQ_SAVE_RESULT_DATA]);
+ //sb.Flows(sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_WRITE_COMPLETION_PLC_BIT]);
+ //sb.Flows(sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_JOIN_ENCODER_MONITOR]);
+
+ //// 3. Fork 이후 병렬 브랜치
+ //sb.Flows(sb[SEQ_FORK_ENCODER_MONITOR] > sb[SEQ_ENCODER_MONITOR]);
+ //sb.Flows(sb[SEQ_ENCODER_MONITOR] > sb[SEQ_JOIN_ENCODER_MONITOR]);
+
+ //// 4. Join 이후 Complete
+ //sb.Flows(sb[SEQ_JOIN_ENCODER_MONITOR] > sb[SEQ_COMPLETE]);
+
+ //// 5. 에러 분기/종료
+ //sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_INIT_CONDITION_ERROR] > sb.Final);
+
+ //// 6. 루프, 분기(선택적)
+ //sb.Flows(sb[SEQ_READY_SCANNER_LIST] > sb[SEQ_EXECUTE_WELDING]);
+ //sb.Flows(sb[SEQ_CHECK_CYCLE_STOP] > sb[SEQ_CHECK_LOAD_COMPLETE]);
+ #endregion
+ sb.OnStopped(OnStopped);
+ sb.OnForceError(OnForceError);
+
+ sb.Draw("SeqAutoRunRMOTF.pdf");
+
+ return sb;
+ }
+
+ private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param)
+ {
+ Log.I("SeqAutoRunRMOTF Start");
+ Status.SetWorkingPosition(eWorkingPosition.CHECKING_CONDITION);
+
+ _InitParam = param.Param as SeqAutoRunCCWH52A_InitParam;
+ _RecipeManager = Application.Current.AuroraEnv().RecipeManager;
+ _SystemParameter = context.GetSetting(CommonSettingIds.SystemParameter);
+
+ InitDev(context);
+ if (!CheckDeviceReady(context)) {
+ //Check Device
+ Log.E("The devices are not active.");
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_DeviceNotReady);
+ }
+ if (InitSequenceVariable(context) == false) {
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_InitVariableError);
+ }
+ Thread.Sleep(1); // 부하 방지
+ return SequenceAction.Ok();
+ }
+
+ private bool InitSequenceVariable(SequenceContext context)
+ {
+ _AutoRunStartTime = DateTime.Now;
+ _TodayProduct = _SystemParameter.TodayProductCount;
+ _TotalProduct = _SystemParameter.TotalProductCount;
+
+ var ActivateRecipeName = _RecipeManager.ActivatedRecipe();
+ Log.I($"Current Activate Recipe, ID : {ActivateRecipeName.Id}, Name : {ActivateRecipeName.Name}");
+
+ var mainPage = WeldingRecipeManager.GetWeldingPage();
+ _ScannerCanvasUserCtrl = mainPage.ScannerCanvasCtrlAutoPage;
+
+ _WeldingScannerListNo = eListNo.LIST1;
+
+ _CellMeasureCenterOffsetX = 0.0;
+ _CellMeasureCenterOffsetY = 0.0;
+
+ var currentModelName = context.Recipes.ActivatedModelName();
+ var modelItem = context.GetModel(currentModelName, CommonModelIds.EquipmentSpecificModel);
+ _ModelNo = modelItem.Model_No;
+
+ return true;
+ }
+
+ private void InitDev(SequenceContext context)
+ {
+ //Device Init
+ _Scanner = context.GetLaserScanner();
+ _Laser = context.GetLaser();
+ if (_InitParam.UseStream) {
+ _StreamLightLBAD = new StreamLightLBAD();
+ _StreamLightLBAD._Client = context.GetStreamLightClient();
+ _StreamLightLBAD._Sub = context.GetStreamLightSub();
+ }
+ _CellTracker = context.GetCellTracker();
+ _PLC = context.Devices[DeviceIds.PLCDevice] as PLCControllerCCWH52A;
+ _IO = context.GetIOUnit();
+ }
+
+ private bool CheckDeviceReady(SequenceContext context)
+ {
+ List deviceListToCheck = new List() { CommonDeviceIds.Scanner, CommonDeviceIds.Laser, CommonDeviceIds.PLCDevice, CommonDeviceIds.IODevice };
+
+ if (_InitParam.UseStream) {
+ deviceListToCheck.Add(CommonServiceIds.StreamLightLBADClient);
+ deviceListToCheck.Add(CommonServiceIds.StreamLightLBADSub);
+ }
+
+ bool isAllDeviceActivated = true;
+ foreach (string item in deviceListToCheck) {
+ if (!context.Devices[item].IsActivated) {
+ isAllDeviceActivated = isAllDeviceActivated && false;
+ }
+ }
+
+ return isAllDeviceActivated;
+ }
+
+ private bool CheckCondition(SequenceContext context)
+ {
+ int InitializeCode = InitializeDeviceStatus(context);
+ if (InitializeCode == (int)InitErrorList.SUCCESS)
+ return true;
+ else
+ context.PutValue(ContextKey.KEY_SEQUENCE_ERROR_CODE, InitializeCode);
+ return false;
+ }
+
+ private int InitializeDeviceStatus(SequenceContext context)
+ {
+ // PLC Interface AutoStatus Start
+ // Won : PC의 AutoStatus 가 True 되는 순간 Rotary가 돌기 시작한다.
+ // Rotary 돌기 시작 한 후 Encoder 값을 읽으면 안 되므로 정지상태에서 읽은 후 AutoStatus true 하도록
+ // InitAutoRunBit 시점 변경
+ //InitAutoRunBit();
+ // LBAD Initialize
+ // LBAD 는 server 이기 때문에, 만약 끊어졌으면 다시 접속이 필요함
+ if (_SystemParameter.BypassModeLBAD == false) {
+ if (!_StreamLightLBAD._Client.IsActivated) {
+ int ret = _StreamLightLBAD._Client.Activate();
+ Thread.Sleep(500);
+ if (ret != E1.OK) {
+ Log.E($"StreamLightClient activate failed");
+ return (int)InitErrorList.LBAD_CLIENT_ACTIVATE_FAILED;
+ }
+ }
+
+ if (_StreamLightLBAD._Sub.IsActivated == false) {
+ int ret = _StreamLightLBAD._Sub.Activate();
+ Thread.Sleep(500);
+ if (ret != E1.OK) {
+ Log.E($"StreamLightSub activate failed");
+ return (int)InitErrorList.LBAD_SUB_ACTIVATE_FAILED;
+ }
+ }
+ _StreamLightLBAD.Initialize();
+ if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_On) == false) {
+ Log.I(string.Format($"[LBAD Interface]CheckReadyStatus null"));
+ //return false;
+ }
+ }
+
+ // Laser Initialize
+ _Laser.PilotLaserOff();
+ _Laser.LaserOn();
+ Thread.Sleep(500);
+ if (!_Laser.GetLaserReady()) {
+ Log.E("ERROR Initialize Laser");
+ return (int)InitErrorList.LASER_INIT_ERROR;
+ }
+ // Scanner Initialize
+ if (_Scanner.GetModulationMode()) {
+ _ModulationMode = true;
+ } else {
+ _ModulationMode = false;
+ }
+
+ //// PLC Initialize AutoRun Bit 최소화 작업 진행
+ if (_PLC.ReadBit((int)eBitInput.PLC_AUTO_STATUS).data == false) // AutoRun Bit가 OFF인 경우
+ {
+ if (_PLC.ReadBit((int)eBitInput.PLC_AUTO_REQ).data == false) // AutoRun Request 상태 체크
+ {
+ Log.E(string.Format("ERROR Initialize PLC, PLC Alarm ON")); // AutoRun Request와 AutoRun Bit 모두 OFF인 경우, PLC 측에서 Alarm 발생함.
+ return (int)InitErrorList.PLC_ALARM;
+ } else {
+ Log.E(string.Format("ERROR Initialize PLC, Bit Off PLC_AUTO_STATUS")); // AutoRun Request가 ON인데 AutoRun Bit 모두 OFF인 경우, PLC에서 AutoRun Bit를 켜지 않음.
+ return (int)InitErrorList.PLC_AUTORUN_BIT_OFF;
+ }
+ }
+
+ // Marking On The Flying Off
+ if (_Scanner.Config.UseMarkingOnTheFly == false) {
+ Log.E("ERROR Config Setting : Scanner Marking on the fly (OFF)");
+ return (int)InitErrorList.MOTF_OFF;
+ }
+
+ // MOTF Set Scanner List
+ _Scanner.SelectCorrectionTable(2);
+ Log.I(string.Format("Normal, Select Laser Correction File"));
+
+ // Aurora 내부에서 찾아야할 듯 함
+ //UsePlcShotThemAll = Dao.SystemRecipe.PlcShotThemAll;
+ //UsePlcPositionCompensation = Dao.SystemRecipe.PlcPositionCompensationUse;
+ string[] strArr = Dao.SystemRecipe.WorkRecipeFile.Split('\\');
+ RecipeFileName = strArr[strArr.Length - 1];
+
+ _LastScannerParamNo = -1;
+
+ // 트럼프 TruDisk를 쓰기 위해서는 SetFirstPulseKiller를 사용해야 함.
+
+ // Rotary는 시작 트리거 미사용
+ _Scanner.SetControlMode(0);
+ UtilUnit.logUnit.SetLog(LogUnit.eLogType.DEBUG, MethodBase.GetCurrentMethod().Name, "[AutoRun] 외부 시작 트리거 DISABLE");
+
+ #region MOTF Absolute Encoder 기준으로 초기화
+ // Set Rotate Center는 Ctrl명령, 처음 한번만 실행
+ _Scanner.SetRotateCenter(_Scanner.Config.MarkingOnTheFlyRotateCenterX, _Scanner.Config.MarkingOnTheFlyRotateCenterY);
+
+ _Scanner.InitFly2D(); // X, Y 엔코더 리셋
+ _EncoderOverflowCount = 0;
+
+ // 현재 정지상태의 Rotary 엔코더 값을 읽어와서 저장
+ _RotaryEncoderOffset = GetRotaryCurrentPosition(); // m_abwRotaryEncoderOffset의 범위는 0 이상 360 미만
+
+ InitRotaryOnTheFlyWeldingCount(_RotaryEncoderOffset, _RotaryJigCount, out _WeldingCount, out _WeldingJigNo);
+
+ Log.I(string.Format($"ScannerUnit.InitRotaryOnTheFlyWeldingCount : m_abwRotaryEncoderOffset({_RotaryEncoderOffset}), weldingCount({_WeldingCount}), weldingJigNo({_WeldingJigNo}), rotaryJigCount({_RotaryJigCount})"));
+
+ // 용접 완료 대기 스캐너 List 리셋
+ ResetInputScannerList();
+ WaitingScannerList.Reset();
+ CompleteScannerList.Reset();
+
+ isTheFirstListWrite = false; // 작업 시작 시 처음 2개의 List는 입력하고 시작한다
+ isTheSecondListWrite = false;
+ isTheFirstListExecute = true; // 제일 첫 List는 ExecuteList 필요. 이후는 AutoChange
+
+ _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, true);
+ Log.I(string.Format($"[PLCInterface]SET PC_AUTO_STATUS True"));
+ #endregion
+
+ return (int)InitErrorList.SUCCESS;
+ }
+
+
+ private double GetRotaryCurrentPosition()
+ {
+ try {
+ //double finalValue = Convert.ToDouble(_plc.GetLongAIValue("PLC_CURRENT_POS_1") / 100000f);
+ double finalValue = Convert.ToDouble(_PLC.ReadDWord((int)eWordInput.PLC_CURRENT_POS_1).data / 100000f);
+
+ finalValue += _Scanner.Config.RotaryEncoderOffset;
+
+ if (360.0 <= finalValue) {
+ while (360.0 <= finalValue) {
+ finalValue -= 360.0;
+ }
+ } else if (finalValue < 0) {
+ while (finalValue < 0) {
+ finalValue += 360.0;
+ }
+ }
+
+ return finalValue;
+ } catch {
+ return -9999.9999;
+ }
+ }
+
+ ///
+ /// 현재 Rotary의 회전 각도에서, 현재 용접 카운트와 지그 번호를 계산. 추가로 Overflow 카운트도 초기화
+ ///
+ /// Rotary 회전 Offset. 운전 시작 시 Rotary가 지그 1번 정중심에서부터 시작했을 경우가 0. 범위는 0이상
+ /// Rotary 내 지그 갯수임. 장비 및 T/T에 따라서, 360도 당 지그의 개수는 다를 수 있으므로.
+ /// Rotary Welding Count. 30도 구간 하나가 지나갈 때마다 +1
+ /// 현재 '용접 중' 구간에 들어가 있는 지그 번호
+ public void InitRotaryOnTheFlyWeldingCount(double rotaryAngleOffset, int rotaryJigCount, out int weldingCount, out int weldingJigNo)
+ {
+ // 현재 로터리 엔코더 값을 기준으로 현재 카운트, 지그 번호를 확인
+ double anglePerOneJig = 360 / (double)rotaryJigCount; //double로 바꿔서 계산해야 22가 아닌 22.5로 anglePerOneJig가 계산됨 25.08.02
+ weldingCount = (int)((rotaryAngleOffset + anglePerOneJig / 2) / anglePerOneJig); // _WeldingCount = 0, 1, 2, ...
+
+ weldingJigNo = (int)(weldingCount % rotaryJigCount) + 1; // _WeldingJigNo = 1, 2, ... , 12
+ Log.I($"Angle Per One Jig : {anglePerOneJig}");
+ }
+
+ private string CheckInitCondition(SequenceContext context, string id, ICollection branches, SequenceActionResult param)
+ {
+ if (!CheckCondition(context)) {
+ //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition);
+ Log.E("Condition is not in state to start sequence.");
+ context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, false);
+ return SEQ_INIT_CONDITION_ERROR;
+ }
+
+ Status.EquipmentMode = eEquipmentMode.AUTO;
+
+ var startTime = DateTime.Now;
+ context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime);
+ Thread.Sleep(1); // 부하 방지
+ Log.D("ReceiveCellInfo Sequence CheckInitCondition");
+ return SEQ_FORK_ENCODER_MONITOR;
+ //return SEQ_CHECK_LOAD_COMPLETE;
+ }
+
+ #region TODO: [장비구현필요]
+ // TODO: [장비구현필요] 공정 Node 구현
+
+ // 지그 상태 확인
+ private SequenceActionResult CheckLoadComplete(SequenceContext context, string id, SequenceActionResult param)
+ {
+ Status.SetWorkingPosition(eWorkingPosition.LOADING_CELL);
+
+ // MOTF WaitScannerLoadListEnable
+ // List가 실행 중이라면(ex : 엔코더 대기 상태 등), 반환값은 0, LoadList 할 수 없는 상태(List 실행 중이거나, 엔코더 대기 상태)
+ // LoadList가 0보다 크면 다음 시퀀스 진행해야 함
+ WaitContinue:
+ var ret = context.RetryUntilSuccess(() => _Scanner.LoadList(_WeldingScannerListNo) > 0, int.MaxValue, 10, true);
+ if (ret == E2.TIMEOUT) {
+ Log.E("TIMEOUT_Scanner_LoadList : 600시간 Run");
+ goto WaitContinue;
+ }
+
+ Log.I($"LoadList function success : List {_WeldingScannerListNo}");
+ Thread.Sleep(1); // 부하 방지
+ return SequenceAction.Ok();
+ }
+
+ private SequenceActionResult CheckScannerListComplete(SequenceContext context, string id, SequenceActionResult param)
+ {
+ if (isTheFirstListWrite) // 작업 시작 시, 첫번째 리스트는 바로 쓰고, 바로 시작한다(ExecuteList). 다만 List 명령에 의해 Encoder가 들어올 때까지 대기.
+ {
+ isTheFirstListWrite = false;
+ Log.I("Write the 1st scanner list");
+
+ } else {
+ if (isTheSecondListWrite) // 두번째 리스트는 바로 쓰지만, 첫번째 리스트 실행이 완료되면 자동으로 이어서 실행된다(AutoChange)
+ {
+ isTheSecondListWrite = false;
+ Log.I("Write the 2nd scanner list");
+
+ } else {
+ isWeled = false;
+ // 첫번째나 두번째가 아니라면, 실제 리스트 실행이 끝난 것이기 때문에 완료 처리가 필요
+ CompleteScannerList = InputScannerList[(int)_WeldingScannerListNo - 1];
+
+ if (!CheckCurrentEncoderAngle(CompleteScannerList.StartAngle)) {
+ // 에러 상황(현재 Rotary Encoder 값이 정상적인 스캐너 리스트 각도 범위를 벗어남)
+ Log.I("ERR_SCANNER_LIST_PROCESSING_TIMEOVER");
+
+ return SequenceAction.Error();
+
+ // WON : 2칸 전에 용접을 진행하지 않아 현재 Angle이 CompleteScannerList.StartAngle + 45 범위를 벗어난 경우라면 에러 상황이 아니므로 에러로 처리 할 필요가 없다
+ // 위에 CheckCurrentEncoderAngle if문 조건 대신 아래 내용으로 테스트 해본 뒤에 적용 필요
+ //if (!CheckCurrentEncoderAngle(CompleteScannerList.StartAngle)) {
+ // // 에러 상황(현재 Rotary Encoder 값이 정상적인 스캐너 리스트 각도 범위를 벗어남)
+ // if (CompleteScannerList.CanExist) {
+ // Log.I("ERR_SCANNER_LIST_PROCESSING_TIMEOVER");
+ // return SequenceAction.Error();
+ // }
+ // return SequenceAction.Ok();
+ } else {
+ if (CompleteScannerList.CanExist) {
+ isWeled = true;
+ #region 용접 시간, 엔코더 측정/기록
+ //_WeldInterval = DateTime.Now - _WeldEndTime;
+ //_WeldEndTime = DateTime.Now;
+ //double elapsedTime = _Scanner.GetTime();
+ //_WeldingSpanTime = TimeSpan.FromSeconds(elapsedTime); // Encoder 대기 넘어간 후의, 실제 스캐너 리스트 실행 시간
+ //_WeldingStartTime = _WeldEndTime.Subtract(_WeldingSpanTime);
+
+ double elaspedTime = _Scanner.GetTime();
+
+ if (CompleteScannerList.WeldComplete) {
+ _WeldInterval = DateTime.Now - _WeldEndTime;
+ _WeldEndTime = DateTime.Now;
+ _WeldingStartTime = CompleteScannerList.WeldStartTime;
+ _WeldingSpanTime = TimeSpan.FromSeconds(elaspedTime);
+ _LotId = CompleteScannerList.LotId;
+ _CellId = CompleteScannerList.CellId;
+
+ Log.I(string.Format("JigNo : {0}, Weld start : {1}, Weld End : {2}, WeldTime : {3}, interval : {4:F3}",
+ CompleteScannerList.JigNo, _WeldingStartTime.ToString("HH:mm:ss.fff"), _WeldEndTime.ToString("HH:mm:ss.fff"), _WeldingSpanTime.TotalMilliseconds, _WeldInterval.TotalSeconds));
+
+ if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + CompleteScannerList.JigNo - 1).data) {
+ _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + CompleteScannerList.JigNo - 1, true);
+ Log.I(string.Format($"PC_WELD_COMPLETE_{CompleteScannerList.JigNo} -> true)"));
+ }
+ UpdateProductCount(context);
+ UpdateProductInfoForView();
+ SaveCellAPD();
+ }
+
+ //if (_SystemParameter.BypassModeLBAD == false) {
+ // if (_StreamLightLBAD.SendWeldingStop() == false) {
+ // Log.I(string.Format($"[LBAD Interface]SendWeldingStop null"));
+ // //return null;
+ // }
+ //}
+
+ UtilUnit.logUnit.SetLog(LogUnit.eLogType.PRODUCT, MethodBase.GetCurrentMethod().Name, "UPDATE_WELDING_TIME;" + _WeldingSpanTime.TotalMilliseconds);
+ UtilUnit.logUnit.SetLog(LogUnit.eLogType.PRODUCT, MethodBase.GetCurrentMethod().Name, "UPDATE_PRODUCT_COUNT;" + _WeldEndTime.ToString("yyyy-MM-dd HH:mm:ss"));
+
+ double encoder0_0, encoder1_0, encoder0_1, encoder1_1;
+ _Scanner.ReadEncoder(out encoder0_0, out encoder1_0, out encoder0_1, out encoder1_1, true);
+
+ double startEncoderCount = encoder0_0;
+ double endEncoderCount = encoder0_1;
+ double angularVelocity = (endEncoderCount - startEncoderCount) / elaspedTime; // 단위는 [degree/sec]
+
+ Log.I(string.Format("Encoder start : {0:F4}, end : {1:F4}, vel : {2:F2}", startEncoderCount, endEncoderCount, angularVelocity));
+ #endregion
+ #region 스캐너 리스트 Record 파일 기록
+ if (_Scanner.Config.UseRecordSignal) {
+ String RecordFileName = _RecipeManager.ActivatedRecipe().Name + "_Jig#" + CompleteScannerList.JigNo.ToString();
+ string DataFilePath = ProjectUtil.GetDataLogSetting().GetFilePath_ScannerRecordLog(RecordFileName, true);
+ _Scanner.SaveRecord(DataFilePath);
+ }
+ #endregion
+ }
+ }
+ }
+ }
+ return SequenceAction.Ok();
+ }
+
+ public bool CheckCurrentEncoderAngle(double startAngle)
+ {
+ double encoderX, currentAngle;
+
+ _Scanner.GetEncoder(out encoderX, out currentAngle, true);
+
+ if (0 < startAngle && currentAngle < 0) {
+ // Encoder 반전되는 순간이며, 이 때만 인터락 무시
+ } else if (startAngle <= currentAngle) {
+ if (currentAngle < startAngle + 45)// 다음 리스트 구간이 끝나기 전까지는 완료가 되어야 한다
+ {
+ Log.I(string.Format("Current encoder check OK, startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
+ return true;
+ } else Log.W(string.Format("Current encoder check NG(Too late), startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
+ } else Log.W(string.Format("Current encoder check NG(Too early), startAngle : {0}, currentAngle : {1})", startAngle, currentAngle));
+
+ return false;
+ }
+
+ private string ReadyScannerList(SequenceContext context, string id, ICollection branches, SequenceActionResult param)
+ {
+ Status.SetWorkingPosition(eWorkingPosition.UPLOADING_WELD_LIST);
+
+ //_SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[0].Objects;
+
+ #region List 입력 시점의 엔코더 카운트 로그 기록
+ double encoderX, encoderY;
+
+ _Scanner.GetEncoder(out encoderX, out encoderY, true);
+
+ UtilUnit.logUnit.SetLog(LogUnit.eLogType.SYSTEM, MethodBase.GetCurrentMethod().Name, string.Format("Encoder X, Y, Offset, Y-X+Offset : {0}, {1}, {2}, {3}",
+ encoderX.ToString("F5"), encoderY.ToString("F5"), _RotaryEncoderOffset.ToString("F5"), (encoderY - encoderX + _RotaryEncoderOffset).ToString("F5")));
+ #endregion
+
+ // 스캐너 위치 초기화
+ _Scanner.Fly_Return(_Scanner.Config.MarkingOntheFlyParkingPositionX, _Scanner.Config.MarkingOntheFlyParkingPositionY);
+
+ #region List 입력(엔코더 조건 만족 대기)
+ _WeldingCount++;
+
+ double compensateEncoderReset = 0.015; // 엔코더 리셋 보상 각도 설정(70도/sec 기준 약 0.015도)
+
+ WaitForEncoderNextRotarySection(_WeldingCount, _RotaryJigCount, _RotaryEncoderOffset, compensateEncoderReset, out _WeldingStartAngle);
+
+ Log.I(string.Format("ScannerUnit.WaitForEncoderNextRotarySection : m_weldingCount({0}),m_abwRotaryEncoderOffset({1:F3}), compensateEncoderReset({2:F3}), overflowCount({3}), weldingStartAngle({4:F3})",
+ _WeldingCount, _RotaryEncoderOffset, compensateEncoderReset, _EncoderOverflowCount, _WeldingStartAngle));
+ #endregion
+
+ // 3개임으로 3으로 작성
+ #region List 입력(용접 좌표 입력)
+ if (_WeldingJigNo == _RotaryJigCount)
+ _WeldingJigNo = 1;
+ else
+ _WeldingJigNo++;
+
+ bool canExist; // 지그 용접 수행 여부 확인
+
+ if (UsePlcShotThemAll) {
+ canExist = true;
+ Log.I("Settings - PLC : Shot Them All Mode");
+ } else
+ canExist = GetRotaryCanExist(_WeldingJigNo);
+
+ Log.I("Can Exist : " + canExist.ToString() + ", JigNo " + _WeldingJigNo + " ,CountNo " + _WeldingCount + ", StartAngle(Y) : " + _WeldingStartAngle);
+
+ if (canExist) {
+ #region 용접할 Objects 선택
+ if (_WeldingJigNo - 1 < _ScannerCanvasUserCtrl.Model.Layers.Length) {
+ _SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[_WeldingJigNo - 1].Objects;
+ Log.I(string.Format("Page 불러오기 : [{0}])", _WeldingJigNo - 1));
+ } else {
+ _SelectedDrawObjectList = _ScannerCanvasUserCtrl.Model.Layers[0].Objects;
+ Log.I(string.Format("Page 불러오기 : [0])"));
+ }
+ #endregion
+ // 입력한 Scanner List를 완료대기열에 추가
+ // Won : GetProcessingWeldingData().WorkLotId 는 CellTrakcer로 부터 받은 상태의 LotId
+ // GetProcessingWeldingData().LotId 는 AutorunView에 표기할 용접이 완료 된 LotID
+ string LotId = Status.GetProcessingWeldingData().WorkLotId; // CellTracker로 부터 WorkLotID, WorkCellID 받는 구문 추가 후 해당 데이터로 입력 필요
+ string CellId = Status.GetProcessingWeldingData().WorkCellId;
+ SetInputScannerList(_WeldingScannerListNo, DateTime.Now, _WeldingCount, _WeldingStartAngle, _WeldingJigNo, _RotaryJigCount, canExist, LotId, CellId);
+ _RecipeNo = _RecipeManager.ActivatedRecipe().Id;
+
+ SetWeldingData();
+ return SEQ_SET_PARAMS_UPLOAD_WELDING_LIST_SEQ;
+ } else {
+ return SEQ_EXECUTE_WELDING;
+ }
+ #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)
+ {
+ // 한 바퀴당 지그 숫자에 따른 변경
+ double encoderAllowedMinDegree = Int32.MinValue / _Scanner.Config.MarkingOnTheFlyEncoderResolutionRotate * 360.0;
+ double encoderAllowedMaxDegree = Int32.MaxValue / _Scanner.Config.MarkingOnTheFlyEncoderResolutionRotate * 360.0;
+
+ double anglePerOneJig = (double)(360 / (double)rotaryJigCount);
+
+ // 아래는 지그 당 각도가 30도라는 가정하의 수식임.
+ // 1.1. 시작 각도 = (현재 지그 번호 * 30도) -15도 - 초기화 엔코더 값 - 엔코더 리셋 보상값
+ // rotaryAngleOffset = x가 0도 이상 15도 미만일 때, weldingCount = 1부터 시작, sectionStartAngle = 15 - x에서 30씩 증가
+ // rotaryAngleOffset = x가 15도 이상 45도 미만일 때, weldingCount = 2부터 시작, sectionStartAngle = 30 - x에서 30씩 증가
+ // rotaryAngleOffset = x가 45도 이상 75도 미만일 때, weldingCount = 3부터 시작, sectionStartAngle = 30 - x에서 30씩 증가
+ // rotaryAngleOffset = x가 315도 이상 345도 미만일 때, weldingCount = 12부터 시작, sectionStartAngle = 30 - x에서 30씩 증가
+ // rotaryAngleOffset = x가 345도 이상 360도 미만일 때, weldingCount = 13부터 시작, sectionStartAngle = 30 - x에서 30씩 증가
+ sectionStartAngle = weldingCount * anglePerOneJig - (anglePerOneJig / 2) - rotaryAngleOffset - encoderResetOffset - _EncoderOverflowCount * (encoderAllowedMaxDegree - encoderAllowedMinDegree);
+
+ // 1.2. 계산된 시작각도가 이미 지나버렸으면 알람 발생 필요
+
+ // 1.3. Overflow가 나는 숫자라면, 보상할 것.
+ if (sectionStartAngle > encoderAllowedMaxDegree) {
+ sectionStartAngle -= encoderAllowedMaxDegree - encoderAllowedMinDegree;
+ _EncoderOverflowCount++;
+ }
+
+ double sectionEndAngle = (encoderAllowedMaxDegree < sectionStartAngle + anglePerOneJig) ? encoderAllowedMaxDegree : sectionStartAngle + anglePerOneJig;
+
+ _Scanner.WaitForEncoderRotateRange(encoderAllowedMinDegree, encoderAllowedMaxDegree, sectionStartAngle, sectionEndAngle);
+ }
+
+ public bool GetRotaryCanExist(int jigNo)
+ {
+ #region 지그 16개 요청기록 로그 남기기, 빠져나간 캔에 대한 Complete Bit 초기화
+ string result = "";
+
+ for (int i = 1; i <= _RotaryJigCount; i++) {
+ if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data)
+ result += "1, ";
+ else {
+ result += "0, ";
+
+ string outId = string.Format("PC_WELD_COMPLETE_{0}", i);
+
+ if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data) {
+ _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false);
+ Log.I(string.Format($"PC_WELD_COMPLETE_{1 + i} -> FALSE)"));
+ }
+ }
+ }
+ //UtilUnit.logUnit.SetLog(LogUnit.eLogType.DEBUG, MethodBase.GetCurrentMethod().Name, string.Format("PLC -> PC 용접 Cmd : ({0})", result));
+ #endregion
+
+ if (jigNo <= _RotaryJigCount)
+ //return m_rscPlc.GetDIStatus(string.Format("PLC_USE_WELD_JIG_{0}", jigNo));
+ return _PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + (jigNo - 1)).data;
+ else
+ return false;
+ }
+
+ private SequenceActionResult SetUploadWeldingList_SeqParams(SequenceContext context, string id, SequenceActionResult param)
+ {
+ //현재 시퀀스에서 LoadList를 해주기 때문에 Sub 시퀀스에 IncludeListStartEnd를 false로 넘겨줘야 함
+ SeqUploadWeldingList_InitParam seqParams = new SeqUploadWeldingList_InitParam() {
+ NeedSetLaserTiming = _ModulationMode,
+ IncludeListStartEnd = false,
+ CellMeasureCenterOffsetX = _CellMeasureCenterOffsetX,
+ CellMeasureCenterOffsetY = _CellMeasureCenterOffsetY,
+ SelectedDrawObjectList = _SelectedDrawObjectList,
+ WeldingScannerListNo = _WeldingScannerListNo,
+ Rotation_Degree = 11.25,
+ };
+
+ return SequenceAction.Ok(seqParams);
+ }
+
+ private SequenceActionResult GetUploadWeldingList_SeqResult(SequenceContext context, string id, SequenceActionResult param)
+ {
+ var resultOutParam = param.Param as SeqUploadWeldingList_OutParam;
+ if (resultOutParam.Success == false) {
+ //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_ResultFailed);
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_ResultFailed);
+ //return null;
+ }
+
+ return SequenceAction.Ok();
+ }
+
+ private SequenceActionResult SetReceiveCellInfo_SeqParams(SequenceContext context, string id, SequenceActionResult param)
+ {
+ SeqReceiveCellInfo_InitParam seqParams = new SeqReceiveCellInfo_InitParam() {
+ };
+
+ return SequenceAction.Ok(seqParams);
+ }
+
+ private SequenceActionResult GetReceiveCellInfo_SeqResult(SequenceContext context, string id, SequenceActionResult param)
+ {
+ var resultOutParam = param.Param as SeqReceiveCellInfo_OutParam;
+ if (resultOutParam.Success == false) {
+ return null;
+ }
+ return SequenceAction.Ok();
+ }
+
+ private SequenceActionResult ExecuteWelding(SequenceContext context, string id, SequenceActionResult param)
+ {
+ Status.SetWorkingPosition(eWorkingPosition.EXECUTING_WELD);
+
+ // MOTF Welding
+ // List 닫기
+ _Scanner.SetEndList();
+
+ #region List 실행(처음엔 ExecuteList, 다음부터 AutoChangeList, 이후 대상 리스트 교체 (1 <-> 2))
+ if (isTheFirstListExecute) {
+ isTheFirstListExecute = false;
+
+ _Scanner.ExecuteList(_WeldingScannerListNo);
+ Log.I(string.Format($"START Welding : ExecuteList_{_WeldingScannerListNo}"));
+ } else {
+ _Scanner.AutoChange();
+ Log.I(string.Format($"Next upload list : uploadList_{_WeldingScannerListNo}"));
+ }
+
+ _WeldingScannerListNo = (_WeldingScannerListNo != eListNo.LIST1) ? eListNo.LIST1 : eListNo.LIST2;
+ #endregion
+
+ return SequenceAction.Ok();
+ }
+
+ private SequenceActionResult RequestUnloadCell(SequenceContext context, string id, SequenceActionResult param)
+ {
+ Status.SetWorkingPosition(eWorkingPosition.UNLOADING_CELL);
+
+ return SequenceAction.Ok();
+ }
+ private SequenceActionResult EncoderMonitor(SequenceContext context, string id, SequenceActionResult param)
+ {
+ int EncoderLogCount = 0;
+ double send_time_offset = 0.1;
+ string LotID = "", CellID = "";
+
+ while (!context.Stopped) {
+ double encoderX, currentAngle;
+ _Scanner.GetEncoder(out encoderX, out currentAngle, true);
+ double CurrentPosPLC = GetRotaryCurrentPosition();
+ bool WeldingInPosition = encoderX > _Scanner.Config.MarkingRangeMinRotate + 11.25 - send_time_offset - 0.1 && encoderX < _Scanner.Config.MarkingRangeMinRotate + 11.25 - send_time_offset + 0.1;
+
+ if (WeldingInPosition) {
+ if (InputScannerList[(int)_WeldingScannerListNo - 1].CanExist) {
+ Log.I($"Fork Before Min Rotate Encoder X : ({encoderX}), Encoder Y : ({currentAngle}), PLC Rotary : ({CurrentPosPLC}), List {_WeldingScannerListNo}, JigNo : ({InputScannerList[(int)_WeldingScannerListNo - 1].JigNo})");
+ if (!_SystemParameter.BypassModeLBAD) {
+ if (InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo == false) {
+ LotID = InputScannerList[(int)_WeldingScannerListNo - 1].LotId;
+ CellID = InputScannerList[(int)_WeldingScannerListNo - 1].CellId;
+ if (currentAngle > InputScannerList[(int)_WeldingScannerListNo - 1].StartAngle) {
+ InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo = _StreamLightLBAD.SendCellInfo(LotID, CellID, _RecipeNo, _ModelNo, InputScannerList[(int)_WeldingScannerListNo - 1].JigNo);
+
+ if (InputScannerList[(int)_WeldingScannerListNo - 1].CheckSendCellinfo == false)
+ Log.I($"[LBAD Interface]SendCellInfo null, LotID [{LotID}], CellID [{CellID}], JigNo [{InputScannerList[(int)_WeldingScannerListNo - 1].JigNo}]");
+ else
+ Log.I($"[StreamLight]SendCellInfo LotID [{LotID}], CellID [{CellID}], JigNo [{InputScannerList[(int)_WeldingScannerListNo - 1].JigNo}]");
+ }
+ }
+ }
+ }
+ }
+
+ if (_IO._iOUnit.ReadDiBit(Convert.ToInt32(eADIAddress.LaserOn))) {
+ if (InputScannerList[(int)_WeldingScannerListNo - 1].WeldStartTime == DateTime.MinValue && InputScannerList[(int)_WeldingScannerListNo - 1].CanExist) {
+ InputScannerList[(int)_WeldingScannerListNo - 1].WeldStartTime = DateTime.Now;
+ InputScannerList[(int)_WeldingScannerListNo - 1].WeldComplete = true;
+ EncoderLogCount = 0;
+ Log.I($"Laser On, Welding Complete. JigNo : ({InputScannerList[(int)_WeldingScannerListNo - 1].JigNo})");
+ }
+
+ if (EncoderLogCount == 0) {
+ Log.I($"Fork Laser On Encoder X : ({encoderX}), Encoder Y : ({currentAngle}), PLC Rotary : ({CurrentPosPLC}), List {_WeldingScannerListNo}, JigNo : ({InputScannerList[(int)_WeldingScannerListNo - 1].JigNo})");
+ }
+ EncoderLogCount = (EncoderLogCount + 1) % 7;
+ }
+
+ for (int i = 0; i < _RotaryJigCount; i++) {
+ if (_PLC.ReadBit((int)eBitInput.PLC_USE_WELD_JIG_1 + i).data == false) {
+ _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false);
+ }
+ }
+
+ _PLC.WriteDWord((int)eWordOutput.PC_CURRENT_POS, (int)((currentAngle + _RotaryEncoderOffset) % 360 * 100_000f));
+ Thread.Sleep(1); // 부하 방지
+ }
+ return SequenceAction.Ok();
+ }
+
+ private void UpdateProductCount(SequenceContext context)
+ {
+ if (_SystemParameter.LastResetTime < DateTime.Now.Date) {
+ _SystemParameter.LastResetTime = DateTime.Now.Date;
+ Log.I($"Today Count Reset. {_SystemParameter.TodayProductCount} -> 0");
+ _SystemParameter.TodayProductCount = 0;
+ }
+
+ _TodayProduct = _SystemParameter.TodayProductCount;
+ _TotalProduct = _SystemParameter.TotalProductCount;
+ if (_TodayProduct < uint.MaxValue) {
+ _TodayProduct++;
+ }
+ if (_TotalProduct < uint.MaxValue) {
+ _TotalProduct++;
+ }
+ _SystemParameter.TodayProductCount = _TodayProduct;
+ _SystemParameter.TotalProductCount = _TotalProduct;
+ }
+
+ private void UpdateProductInfoForView()
+ {
+ Status.GetProcessingWeldingData().TotalProduct = _TotalProduct;
+ Status.GetProcessingWeldingData().TodayProduct = _TodayProduct;
+
+ Status.GetProcessingWeldingData().AutoRunStartTime = _AutoRunStartTime;
+
+ Status.GetProcessingWeldingData().WeldingStartTime = _WeldingStartTime;
+ Status.GetProcessingWeldingData().WeldingEndTime = _WeldEndTime;
+ Status.GetProcessingWeldingData().WeldingSpanTimeMilliseconds = _WeldingSpanTime.TotalMilliseconds.ToString();
+
+ // Status.GetProcessingWeldingData().LotId 는 Autorun View에 표기할 용접이 완료 된 LotId 이므로 WorkLotID 와의 구분 확인
+ Status.GetProcessingWeldingData().LotId = _LotId;
+ Status.GetProcessingWeldingData().CellId = _CellId;
+
+ Status.GetProcessingWeldingData().RecipeNo = _RecipeNo;
+ Status.GetProcessingWeldingData().LaserPowerByLBAD = _LaserPowerByLBAD.ToString();
+
+ Status.GetProcessingWeldingData().WeldingJigNo = _CompletedJigNo.ToString();
+
+ }
+ private SequenceActionResult SetReportCellResult_SeqParams(SequenceContext context, string id, SequenceActionResult param)
+ {
+ Status.SetWorkingPosition(eWorkingPosition.REPORTING_RESULT);
+
+ SeqReportCellResult_InitParam seqParams = new SeqReportCellResult_InitParam() {
+ EDC_Interface = new EDC(),
+ };
+
+ return SequenceAction.Ok(seqParams);
+ }
+
+ private SequenceActionResult GetReportCellResult_SeqResult(SequenceContext context, string id, SequenceActionResult param)
+ {
+ var resultOutParam = param.Param as SeqReportCellResult_OutParam;
+ if (resultOutParam.Success == false) {
+ return null;
+ }
+
+ return SequenceAction.Ok();
+ }
+
+ private static string CheckCycleStop(SequenceContext context, string id, ICollection branches, SequenceActionResult param)
+ {
+ Status.SetWorkingPosition(eWorkingPosition.CHECKING_CYCLE);
+
+ // TODO: 확인 필요, CybleStop 에 대한 내용, cycle stop 여부 가져오기
+ //bCycleStop = GetCycleStopRequest();
+
+ // TODO: 한번만 돌도록 설정함, PLC 로 받으면 좋음.
+ //Jin : cycleStop 명령을 용접 프로그램에서 할 경우가 현재는 없음..
+ // CheckCycleStop 노드와 Complete 노드 사이 다른 노드가 필요하다면 해당 노드를 CheckCycleStop 노드 위로 올리자
+ bool cycleStop = false;
+ if (cycleStop) {
+ return SEQ_SAVE_RESULT_DATA;
+ } else {
+ return SEQ_CHECK_LOAD_COMPLETE;
+ }
+ }
+ // END ALL
+ #endregion
+ private static string CheckCanWelded(SequenceContext context, string id, ICollection branches, SequenceActionResult param)
+ {
+ if (isWeled)
+ return SEQ_SET_PARAMS_REPORT_CELL_RESULT_SEQ;
+ else
+ 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)
+ {
+
+ context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true);
+ return SequenceAction.Ok();
+ }
+
+ private SequenceActionResult WriteCompletionPLCBit(SequenceContext context, string id, SequenceActionResult param)
+ {
+ // 완료 비트 write 하는 Node 추가 필요. format 유지, ReceiveCellInfo 없음
+ Log.D("AutoRun Sequence WriteCompletionPLCBit");
+
+ return SequenceAction.Ok();
+ }
+
+ private SequenceActionResult InitConditionError(SequenceContext context, string id, SequenceActionResult param)
+ {
+ context.TryGetValue(ContextKey.KEY_SEQUENCE_ERROR_CODE, out int InitializeCode);
+ switch (InitializeCode) {
+ case (int)InitErrorList.PLC_AUTORUN_BIT_OFF:
+ Log.D("AutoRun Sequence InitConditionError : PLC_AUTORUN_OFF");
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition_PLC_AUTORUN_OFF);
+ case (int)InitErrorList.LBAD_SUB_ACTIVATE_FAILED:
+ Log.D("AutoRun Sequence InitConditionError : LBAD_SUB_ERR");
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition_LBAD_SUB_ERR);
+ case (int)InitErrorList.LBAD_CLIENT_ACTIVATE_FAILED:
+ Log.D("AutoRun Sequence InitConditionError : LBAD_CLIENT_ERR");
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition_LBAD_CLIENT_ERR);
+ case (int)InitErrorList.LASER_INIT_ERROR:
+ Log.D("AutoRun Sequence InitConditionError : LASER_ERR");
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition_LASER_ERR);
+ case (int)InitErrorList.MOTF_OFF:
+ Log.D("AutoRun Sequence InitConditionError : MOTF_OFF");
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition_MOTF_OFF);
+ case (int)InitErrorList.PLC_ALARM:
+ Log.D("AutoRun Sequence InitConditionError : PLC_ALARM");
+ return null;
+ default:
+ Log.D("AutoRun Sequence InitConditionError");
+ return SequenceAction.ForceStopByAlarm(AlarmCode.ALARM_SEQUENCE_AutoRun_CheckInitCondition);
+ }
+ }
+
+ private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param)
+ {
+ RestoreDeviceStatus();
+
+ context.Settings.Save(CommonSettingIds.SystemParameter, _SystemParameter, "SeqAutoRunCCWH52A");
+
+ context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult);
+ SeqAutoRunCCWH52A_OutParam result = new SeqAutoRunCCWH52A_OutParam() { Success = seqResult };
+
+ if (seqResult == false) {
+ //AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_AutoRun_ResultFailed);
+ }
+ Log.D("AutoRun Sequence Complete");
+ Status.SetWorkingPosition(eWorkingPosition.DONE);
+
+ return SequenceAction.Ok(result);
+ }
+
+ private bool RestoreDeviceStatus()
+ {
+ //PLC All BIT OFF
+ for (int i = 0; i < _RotaryJigCount; i++) {
+ _PLC.WriteBit((int)eBitOutput.PC_WELD_COMPLETE_1 + i, false);
+ }
+ _PLC.WriteBit((int)eBitOutput.PC_AUTO_STATUS, false);
+ //_PLC.WriteBit((int)eBitOutput.PC_RECIPE_PARAM_UPLOAD_COMPLETE, false);
+ Log.I(string.Format("[PLCInterface]PC All Bit Off"));
+
+ //LBAD Ready Off
+ //_LBAD.SetReadybyAuto(false);
+ if (_SystemParameter.BypassModeLBAD == false) {
+ if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_Off) == false) {
+ Log.I(string.Format($"[LBAD Interface]CheckReadyStatus null"));
+ }
+ }
+
+ //Laser Off
+ _Laser.LaserOff();
+
+ //List 실행 종료
+ _Scanner.StopExecution();
+
+ //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨
+ _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND);
+ _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND);
+
+ //Scanner 원점 복귀
+ _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND);
+
+ //Vision Correctiong 파일 변경
+ _Scanner.SelectCorrectionTable(1);
+
+ //Recipe를 기존에 Manual에서 작업하던 화면으로 rollback
+ WeldingRecipeManager.RollbackRecipe();
+
+ return true;
+ }
+
+ private void OnForceError(SequenceContext context, string arg2)
+ {
+ context.Settings.Save(CommonSettingIds.SystemParameter, _SystemParameter, "SeqAutoRunCCWH52A");
+
+ OnSeqActionStop();
+
+ Log.I("SequenceAction Ok : OnForceError");
+ }
+
+ private void OnStopped(SequenceContext context)
+ {
+ context.Settings.Save(CommonSettingIds.SystemParameter, _SystemParameter, "SeqAutoRunCCWH52A");
+
+ OnSeqActionStop();
+
+ Log.I("SequenceAction Ok : OnStopped");
+ }
+
+ private void OnSeqActionStop()
+ {
+ Status.EquipmentMode = eEquipmentMode.MANUAL;
+
+ //PLC All BIT OFF
+ for (int i = 0; i < _RotaryJigCount; i++)
+ _PLC.WriteBit(eBitOutput.PC_WELD_COMPLETE_1 + i, false);
+ _PLC.WriteBit(eBitOutput.PC_AUTO_STATUS, false);
+ Log.I("[PLCInterface]PC All Bit Off");
+
+ //LBAD Ready Off
+ //_LBAD.SetReadybyAuto(false);
+ if (_SystemParameter.BypassModeLBAD == false) {
+ if (_StreamLightLBAD.SendReadyStatus(eRequestData.Ready_Off) == false) {
+ Log.I(string.Format($"[LBAD Interface]CheckReadyStatus null"));
+ }
+ }
+
+ //Laser Off
+ _Laser.LaserOff();
+
+ //List 실행 종료
+ _Scanner.StopExecution();
+
+ //Anlog 출력 0으로 변경, efunctiontype을 command로 변경해주면 list가 아니라 직접 변경됨
+ _Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.COMMAND);
+ _Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.COMMAND);
+
+ //Scanner 원점 복귀
+ _Scanner.JumpAbs(0, 0, eFunctionType.COMMAND);
+
+ //Vision Correctiong 파일 변경
+ _Scanner.SelectCorrectionTable(1);
+
+ //Recipe를 기존에 Manual에서 작업하던 화면으로 rollback
+ WeldingRecipeManager.RollbackRecipe();
+ }
+ }
+}
diff --git a/WeldingComponents/WeldingCommon/Utils/ChangeChecker.cs b/WeldingComponents/WeldingCommon/Utils/ChangeChecker.cs
new file mode 100644
index 0000000..0a4cf6a
--- /dev/null
+++ b/WeldingComponents/WeldingCommon/Utils/ChangeChecker.cs
@@ -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 mapping = new Dictionary();
+ 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;
+ }
+ }
+}
diff --git a/WeldingComponents/WeldingCommon/WeldingCommon.csproj b/WeldingComponents/WeldingCommon/WeldingCommon.csproj
index bee41e5..f5b2a0b 100644
--- a/WeldingComponents/WeldingCommon/WeldingCommon.csproj
+++ b/WeldingComponents/WeldingCommon/WeldingCommon.csproj
@@ -248,6 +248,7 @@
+