From 4e994337829ee5046a9129f2cff21116bd242127 Mon Sep 17 00:00:00 2001 From: choieigun Date: Sat, 21 Feb 2026 15:47:11 +0900 Subject: [PATCH] =?UTF-8?q?refcatoring=20=EA=B4=80=EB=A0=A8=20=ED=94=84?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EB=9E=A8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Machines/ILSOS_CCW_H52A/ILSOS_CCW_H52A.csproj | 1 + .../Sequences/ProcessManager.cs | 34 +- .../Sequences/SeqAutoRunRMOTF.cs | 1217 +++++++++++++++++ .../WeldingCommon/Utils/ChangeChecker.cs | 45 + .../WeldingCommon/WeldingCommon.csproj | 1 + 5 files changed, 1285 insertions(+), 13 deletions(-) create mode 100644 Machines/ILSOS_CCW_H52A/Sequences/SeqAutoRunRMOTF.cs create mode 100644 WeldingComponents/WeldingCommon/Utils/ChangeChecker.cs 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 @@ +