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 ErrorMessages = new Dictionary() { { 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 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 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 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 CheckValuesResponse(string response) { try { List list = new List(); 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; } } }