2026-06-05 13:44:39 +09:00

272 lines
8.9 KiB
C#

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
}
}