namespace RobotNet10.CANOpen.Models; /// /// Emergency (EMCY) Error Codes theo CANopen standard /// public enum EmergencyErrorCode : ushort { // 0x00xx: Error Reset / No Error ErrorReset = 0x0000, // 0x10xx: Generic Error GenericError = 0x1000, // 0x20xx: Current CurrentGeneric = 0x2000, CurrentInputSide = 0x2100, CurrentInsideDevice = 0x2200, CurrentOutputSide = 0x2300, // 0x21xx: Current, device input side OverCurrent = 0x2110, // 0x22xx: Current inside the device ShortCircuit = 0x2220, // 0x23xx: Current, device output side LoadDump = 0x2330, // 0x30xx: Voltage VoltageGeneric = 0x3000, MainsVoltage = 0x3100, VoltageInsideDevice = 0x3200, OutputVoltage = 0x3300, // 0x31xx: Mains Voltage UnderVoltage = 0x3110, OverVoltage = 0x3120, // 0x40xx: Temperature TemperatureGeneric = 0x4000, AmbientTemperature = 0x4100, DeviceTemperature = 0x4200, // 0x41xx: Ambient Temperature TooHigh = 0x4110, TooLow = 0x4120, // 0x50xx: Device Hardware DeviceHardware = 0x5000, // 0x60xx: Device Software DeviceSoftware = 0x6000, InternalSoftware = 0x6100, UserSoftware = 0x6200, DataSet = 0x6300, // 0x70xx: Additional Modules AdditionalModules = 0x7000, // 0x80xx: Monitoring Monitoring = 0x8000, Communication = 0x8100, ProtocolError = 0x8200, // 0x81xx: Communication CanOverrun = 0x8110, ErrorPassive = 0x8120, HeartbeatError = 0x8130, BusOffRecovered = 0x8140, // 0x82xx: Protocol Error PdoNotProcessed = 0x8210, PdoLengthExceeded = 0x8220, DamMpdo = 0x8230, SyncDataLength = 0x8240, // 0x90xx: External Error ExternalError = 0x9000, // 0xF0xx: Additional Functions AdditionalFunctions = 0xF000, // 0xFFxx: Device specific DeviceSpecific = 0xFF00 } /// /// Emergency Message theo CANopen /// public readonly struct EmergencyMessage(byte nodeId, EmergencyErrorCode errorCode, byte errorRegister, byte[] manufacturerError, DateTime timestamp) { /// /// Node ID của device gửi emergency /// public byte NodeId { get; init; } = nodeId; /// /// Emergency Error Code (16-bit) /// public EmergencyErrorCode ErrorCode { get; init; } = errorCode; /// /// Error Register (8-bit) /// public byte ErrorRegister { get; init; } = errorRegister; /// /// Manufacturer Specific Error Code (40-bit / 5 bytes) /// public byte[] ManufacturerError { get; init; } = manufacturerError; /// /// Timestamp khi nhận emergency /// public DateTime Timestamp { get; init; } = timestamp; /// /// Parse Emergency message từ CAN frame /// public static EmergencyMessage FromCanFrame(uint canId, byte[] data, DateTime timestamp) { if (data.Length < 8) throw new ArgumentException($"Emergency message must be 8 bytes: {BitConverter.ToString(data)}"); byte nodeId = (byte)(canId & 0x7F); ushort errorCode = (ushort)(data[0] | (data[1] << 8)); byte errorRegister = data[2]; byte[] manufacturerError = new byte[5]; Array.Copy(data, 3, manufacturerError, 0, 5); return new EmergencyMessage(nodeId, (EmergencyErrorCode)errorCode, errorRegister, manufacturerError, timestamp); } /// /// Convert sang CAN frame data /// public byte[] ToBytes() { byte[] data = new byte[8]; data[0] = (byte)((ushort)ErrorCode & 0xFF); data[1] = (byte)(((ushort)ErrorCode >> 8) & 0xFF); data[2] = ErrorRegister; if (ManufacturerError != null && ManufacturerError.Length >= 5) { Array.Copy(ManufacturerError, 0, data, 3, 5); } return data; } // Error Register bit definitions public bool GenericErrorBit => (ErrorRegister & 0x01) != 0; public bool CurrentErrorBit => (ErrorRegister & 0x02) != 0; public bool VoltageErrorBit => (ErrorRegister & 0x04) != 0; public bool TemperatureErrorBit => (ErrorRegister & 0x08) != 0; public bool CommunicationErrorBit => (ErrorRegister & 0x10) != 0; public bool DeviceProfileErrorBit => (ErrorRegister & 0x20) != 0; public bool ManufacturerErrorBit => (ErrorRegister & 0x80) != 0; public override string ToString() { return $"EMCY[Node {NodeId}]: Code=0x{(ushort)ErrorCode:X4}, Register=0x{ErrorRegister:X2}"; } }