using Aurora; using Aurora.Api; using Hermes; using MathNet.Numerics.Distributions; using System; using System.Collections.Generic; using System.Threading; using System.Windows; namespace AuroraLaser.Coherent { // Compact 시리즈가 아닌, Coherent 사 제품을 공통으로 묶을 수 있을까? public class LaserCompact : Device { protected readonly Logger Log; private ILaserCompact _laser; // 공통 인터페이스 public LaserCompactMiniConfig _rawConfig; protected bool _isSimulBySetting; protected const Int16 FAIL = -1; public int Index { get; set; } //----- For Simul double _powerPerc; Random _randomInt; Normal _randomValue; const double magForGaussian = 5.0; //=========================== #region 생성자 public LaserCompact(string deviceId, bool isSimulBySetting = false, int laserIndex = 0) { DeviceId = deviceId; CommType = CommunicationType.Serial; Type = DeviceType.Laser; _isSimulBySetting = isSimulBySetting; Index = laserIndex; Log = new Logger("Compact_" + deviceId); } #endregion #region 활성화/비활성화 protected override int DoActivate(IDeviceConfig config) { _rawConfig = config as LaserCompactMiniConfig; IsSimul = _rawConfig.IsSimul || _isSimulBySetting; _laser = new CompactMini(DeviceId, IsSimul); var result = _laser.Connect(_rawConfig); if (IsSimul == false) { if (result == E1.OK) { // 통신 연결 성공 if (_laser.NeedConfigSave) { // 시리얼넘버의 최초 write 발생 시, config 저장 필요 Application.Current.AuroraEnv().DeviceManager.SaveDeviceConfig(DeviceId); // Config 저장 // SN 가 변경되었다는 이벤트 publish EventPublisher.Publish(new EventMessage("LaserCompactMini", LaserEvent.EVENT_ID_SerialNumberUpdate)); // reset _laser.NeedConfigSave = false; Log.I($"Serial number ({_rawConfig.SerialNumber}) is saved"); } Log.I($"Activated Laser : {DeviceId}"); } else { Log.E($"Activate Laser : {DeviceId} Fail"); } } else { // 시뮬 _randomInt = new Random(); _randomValue = new Normal(0.0, 0.1); } return result; } protected override int DoDeactivate() { Log.I($"Deactivate Laser : {DeviceId}"); if (IsSimul == false) return _laser.Disconnect(); else return E1.OK; } #endregion #region 통신 상태 public int Reconnect() { if (Monitor.TryEnter(_alarmLockReconnectObj) && (IsCommError || !_laser.IsConnected())) { if (_laser.Reconnect() == E1.OK) { ClearAlarm(CreateEigenAlarmCode(99)); ClearAlarm(CreateEigenAlarmCode(98)); return E1.OK; } return E1.NETWORK; } return E1.NETWORK; } public bool IsConnected() { if (!IsActivated) { return false; } IsCommError = !_laser.IsConnected(); return IsCommError == false; } // 알람용인데, 흠 전체적인 그림을 봐야겠는데.... public bool IsNetworkError() { if (_laser == null) { Log.E($"{DeviceId} is null"); return true; } else { IsCommError = _laser.IsNetworkError || !_laser.IsConnected(); if (IsCommError) { if (!_laser.IsConnected()) { AddAlarm(CreateEigenAlarmCode(98)); // Eigen 단어가 어색한데....장치니까 Device 는 어떨까? } else { AddAlarm(CreateEigenAlarmCode(99)); } } return IsCommError; } } #endregion // 이 구조체들이 공용화가 어디까지 될까?? Compact 시리즈 모델이 추가되면 수정여부 재검토 public STRUCT_STATUS_REGISTER GetLaserStatusInfo() { return _laser.GetLaserStatusInfo(); } public STRUCT_LASER_MODE_REGISTER GetLaserModeInfo() { return _laser.GetLaserModeInfo(); } public STRUCT_ERROR_REGISTER GetLaserErrMsgInfo() { return _laser.GetLaserErrMsgInfo(); } #region 레이저 관련, 통신 R/W API 포함 public bool IsLaserError() { bool result = _laser.IsLaserError; if (result) { var statusInfo = GetLaserStatusInfo(); if (statusInfo.bErrorStatus) { AddAlarm(CreateEigenAlarmCode(10)); } if (statusInfo.bFatalError) { AddAlarm(CreateEigenAlarmCode(11)); } } return result; } public void ErrorReset() { _laser.ErrorReset(); ClearAlarm(new int[2] { CreateEigenAlarmCode(10), CreateEigenAlarmCode(11) }); } public int SetOutputPower(double powerPerc) { _powerPerc = powerPerc; if (IsSimul == false) { return _laser.SetOutputPower(powerPerc) ? E1.OK : FAIL; } return E1.OK; } #region Power calibration public int LoadLaserCalibrationData() { // Aurora Config Json reload Application.Current.AuroraEnv().DeviceManager.LoadDeviceConfig(DeviceId); _laser.LoadLaserCalibrationData(); return E1.OK; } public List<(double perc, double watt)> GetPowerCalData() { return _laser?.GetPowerCalData(); } public double Watt2Power(double watt) { return _laser.Watt2Power(watt); } public double Watt2Power(double watt, eModelType calibType) { return _laser.Watt2Power(watt, calibType); } // 오직 시뮬에서만 필요 public double Power2Watt(double dPowerPerc) { if (IsSimul) { return _laser.Power2Watt(dPowerPerc); } else { Log.W("Only for Sim mode"); return 0.0; } } public void SetLaserPowerCalibMethod(string powerCalibMethod) { _laser?.SetLaserPowerCalibMethod(powerCalibMethod); } public string GetLaserPowerCalibMethod() { return _laser?.GetLaserPowerCalibMethod(); } #endregion public void Update() { if (!IsActivated) return; if (IsSimul == false) { _laser.Update(); } else { // 시뮬 // 1. 레이저 Internal Set Power 업데이트 int iPow = (int)(Math.Round(_powerPerc, 1) * 10); // [0, 1000] var l_compactMiniRegister = (_laser as CompactMini).m_Register; l_compactMiniRegister.m_stStatus_Regster.PowerValue = iPow + (int)(Math.Round(_randomValue.Sample() * magForGaussian, 1) * 10.0); // 가우시안 노이즈 추가 // 2. 온도 시뮬레이션 l_compactMiniRegister.m_stStatus_Regster.Temp = _randomInt.Next(0, 51); // [0, 50] } } #endregion #region Alarm private const int _alarmPrefix = 32; // TYPE 값 기준으로 하는게 어떨까? private object _alarmObject = new object(); private readonly object _alarmLockReconnectObj = new object(); public override void ClearAlarms() { if (IsActivated) { lock (_alarmObject) { var list = GetAlarmList().ToArray(); foreach (var alarmCode in list) { switch (alarmCode % 100) { case 10: case 11: ErrorReset(); break; case 98: case 99: Reconnect(); break; default: ClearAlarm(alarmCode); break; } } } } else { base.ClearAlarms(); } } private int CreateEigenAlarmCode(int alarmCode) { return _alarmPrefix * 1000 + Index * 100 + alarmCode; } #endregion } }