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

315 lines
11 KiB
C#

using Aurora.Api;
using Aurora.Api.Devices;
using Aurora.Util.HislogPlus;
using Hermes;
using PropertyTools.DataAnnotations;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Input;
using static AuroraHeightSensor.HeightSensor;
namespace AuroraHeightSensor.Keyence
{
public enum IXHResponseType
{
Running = 0,
NormalComplete = 1,
AbnormalComplete = 2,
}
public enum IXHCommandType
{
ZeroOffsetRequest = 1,
ZeroOffsetClearRequest = 2,
ResetRequest = 3,
ZeroOffsetResult = 54,
ResetResult = 55,
ProgramSwitchRequest = 163, // Write [0~31]
ProgramSwitchResult = 164, // Read [ResponseType]
// 설정 읽기 시나리오
ReadSettingRequest = 165, // R/W [0~32]
ReadSettingResult = 166, // Read [ResponseType]
ReadSettingValue = 167, // Read [-999.99 ~ 999.99]
// 설정 쓰기 시나리오
WriteSettingValue = 168, // Write [-999.99 ~ 999.99]
WriteSettingRequest = 169, // Write [0~32]
WriteSettingResult = 170, // Read [ResponseType]
// 측정 시나리오
ReadMeasurementRequest = 171, // Write [0~18]
ReadMeasurementResult = 172, // Read [ResponseType]
ReadMeasurementValue = 173 // Read [-999.99 ~ 999.99]
}
public class HeightSensorIXH : SocketConn, IHeightSensor
{
private readonly Dictionary<int, string> ErrorMessages = new Dictionary<int, string>()
{
{ 9, "입력된 데이터가 유효 범위 밖입니다." },
{ 12, "동작 명령이 실행 불가능한 상태입니다." },
{ 14, "이 주소는 쓰기 금지 또는 쓰기할 수 없는 상태입니다." },
{ 16, "이 데이터 번호는 읽기 금지 또는 읽기가 불가능한 상태입니다." },
{ 20, "데이터 번호가 유효 범위 밖입니다." },
{ 22, "앰프 번호가 유효 범위 밖입니다." },
{ 31, "지정된 앰프 번호, 데이터 번호에 대한 읽기/쓰기에 센서가 대응하지 않습니다. 현재의 모드로는 쓰기할 수 없습니다." },
{ 254, "시스템 에러 상태입니다. 기동 시간을 대기하거나 재기동이 필요할 수 있습니다." },
{ 255, "커맨드 포맷이 바르지 않습니다." }
};
private const string ReadCmd = "SR";
private const string WriteCmd = "SW";
private const string MeasureAllCmd = "M0\r\n";
private string _ampNo = "01"; // [DL 유닛 경우, 00] // [기본 유닛 경우, 01] // [증설 유닛 경우, 04]
private IXHCommandType _sendCommand;
private object _lock = new object();
public HeightSensorIXH(string deviceId, bool isSimul = false, SocketProtocol type = SocketProtocol.TCP, Action<bool> commErr = null) : base(deviceId, isSimul, type, commErr)
{
}
public int Activate(IConnConfig Config)
{
return Connect(Config as SocketConnConfig);
}
public bool CheckConnected()
{
return IsConnected();
}
public int Deactivate()
{
return Close();
}
public int Measure(eChannel channel, out double value)
{
value = double.NaN;
byte[] receive = new byte[64];
lock (_lock) {
var command = MakeCommand(_ampNo, IXHCommandType.ReadMeasurementRequest, $"+00000000{(int)channel}");
if (SendAndRecieve(command, out receive, 2000) != E1.OK) {
Log.I($"[Fail] : Measure Request >> {command}");
return E1.NETWORK;
}
Task.Delay(100);
if (!ParseResponse(Encoding.ASCII.GetString(receive), IXHCommandType.ReadMeasurementRequest))
return E2.UNKNOWN;
command = MakeCommand(_ampNo, IXHCommandType.ReadMeasurementResult);
if (SendAndRecieve(command, out receive, 1000) != E1.OK) {
Log.I($"[Fail] : Measure Result >> {command}");
return E1.NETWORK;
}
Task.Delay(100);
if (!ParseResponse(Encoding.ASCII.GetString(receive), IXHCommandType.ReadMeasurementResult))
return E2.UNKNOWN;
command = MakeCommand(_ampNo, IXHCommandType.ReadMeasurementValue);
if (SendAndRecieve(command, out receive, 1000) != E1.OK) {
Log.I($"[Fail] : Measure Value >> {command}");
return E1.NETWORK;
}
Task.Delay(100);
if (!ParseResponse(Encoding.ASCII.GetString(receive), IXHCommandType.ReadMeasurementResult, out value))
return E2.UNKNOWN;
}
return E1.OK;
}
public int MeasureAll(out List<double> SensorValue)
{
SensorValue = null;
byte[] receive = new byte[64];
lock (_lock) {
if (SendAndRecieve(MeasureAllCmd, out receive) != E1.OK) {
Log.I($"[Fail] : {MeasureAllCmd}");
return E1.OK;
}
if (!ParseResponse(Encoding.ASCII.GetString(receive), out SensorValue))
return E1.OK;
}
return E1.OK;
}
public int SetLed(bool isOn)
{
return 0;
}
public int SetZero(eChannel channel, bool isSet = true)
{
byte[] receive = new byte[64];
lock(_lock) {
var Set = isSet ? "1" : "0";
var command = MakeCommand(_ampNo, IXHCommandType.ZeroOffsetRequest, $"+00000000{Set}");
if(SendAndRecieve(command, out receive, 1000) != E1.OK) {
Log.I($"[Fail] : Zero Request >> {command}");
return E1.NETWORK;
}
Task.Delay(100);
if (!ParseResponse(Encoding.ASCII.GetString(receive), IXHCommandType.ZeroOffsetRequest))
return E2.UNKNOWN;
command = MakeCommand(_ampNo, IXHCommandType.ZeroOffsetResult);
if(SendAndRecieve(command, out receive, 1000) != E1.OK) {
Log.I($"[Fail] : Measure Request >> {command}");
return E1.NETWORK;
}
Task.Delay(100);
if (!ParseResponse(Encoding.ASCII.GetString(receive), IXHCommandType.ZeroOffsetResult))
return E2.UNKNOWN;
}
return E1.OK;
}
public int SetZeroShift(eChannel channel)
{
return E1.OK;
}
public int SetZeroShiftReset(eChannel channel)
{
return E1.OK;
}
public int StartStopAutoTransmit(bool isOn, ICommand callback)
{
return 0;
}
public int Reset(eChannel channel)
{
return E1.OK;
}
public bool ParseResponse(string response, IXHCommandType cmd)
{
if(string.IsNullOrEmpty(response))
return false;
// Command 확인
if (_sendCommand != cmd)
return false;
// 정상 종료가 확인
if (CheckResponse(response) != IXHResponseType.NormalComplete)
return false;
// 애러 확인 (ER,**,ddd\r\n)
if (CheckErrorResponse(response))
return false;
return true;
}
public bool ParseResponse(string response, IXHCommandType cmd, out double value)
{
value = double.NaN;
if (ParseResponse(response, cmd))
return false;
value = CheckValueResponse(response);
return true;
}
public bool ParseResponse(string response, out List<double> values)
{
values = null;
// 정상 종료가 확인
if (CheckResponse(response) != IXHResponseType.NormalComplete)
return false;
// 애러 확인
if (CheckErrorResponse(response))
return false;
values = CheckValuesResponse(response);
return true;
}
private IXHResponseType CheckResponse(string response)
{
if (response.Contains("+000000000"))
return IXHResponseType.Running;
if (response.Contains("+000000002"))
return IXHResponseType.AbnormalComplete;
return IXHResponseType.NormalComplete;
}
private bool CheckErrorResponse(string response)
{
try {
var errorMatch = Regex.Match(response, @"ER,[A-Z]+,?(\d{3})");
if (errorMatch.Success) {
int errorCode = int.Parse(errorMatch.Groups[1].Value);
if (ErrorMessages.TryGetValue(errorCode, out string message)) {
Log.I($"[에러 {errorCode}] {message}");
}
return true;
}
} catch (Exception ex) {
Log.I($"Exception : {ex.Message}");
}
return false;
}
private double CheckValueResponse(string response)
{
try {
var match = Regex.Match(response, @"[\+\-]\d{9}");
if (match.Success) {
var value = Convert.ToDouble(match.Value) / 1000;
Log.I($"Height Measure : {value}");
return value;
}
} catch (Exception ex) {
Log.I($"Exception : {ex.Message}");
}
return double.NaN;
}
private List<double> CheckValuesResponse(string response)
{
try {
List<double> list = new List<double>();
string[] parts = response.Split(',');
for (int i = 1; i < parts.Length; i++) // 0번째는 "M0"
{
string value = parts[i].Replace("\r", "").Replace("\n", "").Trim();
list.Add(Convert.ToDouble(value) / 1000);
}
return list;
} catch (Exception ex) {
Log.I($"Exception : {ex.Message}");
}
return null;
}
public string MakeCommand(string ampNo, IXHCommandType command, string sendValue = null)
{
string ret;
if(sendValue != null)
ret = ($"{WriteCmd},{ampNo},{(int)command:000},{sendValue}");
else
ret = ($"{ReadCmd},{ampNo},{(int)command:000}");
ret += "\r\n";
_sendCommand = command;
return ret;
}
}
}