using System; using System.Text; using V2GDecoderNet.V2G; namespace V2GDecoderNet.EXI { /// /// EXI Encoder with exact C compatibility /// Matches iso1EXIDatatypesEncoder.c structure /// public static class EXIEncoderExact { public static byte[] EncodeV2GMessage(V2GMessageExact message) { try { var stream = new BitOutputStreamExact(); // Write EXI header (0x80 0x98) stream.WriteNBitUnsignedInteger(16, 0x8098); // Write V2G message structure EncodeV2GMessageStructure(stream, message); return stream.ToArray(); } catch (Exception ex) { Console.Error.WriteLine($"EXI encoding error: {ex.Message}"); throw new Exception($"Failed to encode V2G message: {ex.Message}", ex); } } private static void EncodeV2GMessageStructure(BitOutputStreamExact stream, V2GMessageExact message) { // Grammar state 0: Start with V2G_Message stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT({urn:iso:15118:2:2013:MsgDef}V2G_Message) // Grammar state 1: Header stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT({urn:iso:15118:2:2013:MsgHeader}Header) EncodeMessageHeader(stream, message.SessionID); // Grammar state 2: Body stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT({urn:iso:15118:2:2013:MsgDef}Body) EncodeBody(stream, message.Body); // End V2G_Message stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } private static void EncodeMessageHeader(BitOutputStreamExact stream, string sessionId) { // Grammar state for MessageHeaderType // START_ELEMENT({urn:iso:15118:2:2013:MsgHeader}SessionID) stream.WriteNBitUnsignedInteger(1, 0); // SessionID as hex binary stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BINARY_HEX] // Convert hex string to bytes byte[] sessionBytes = ConvertHexStringToBytes(sessionId); stream.WriteUnsignedInteger(sessionBytes.Length); WriteBytes(stream, sessionBytes); // End SessionID element stream.WriteNBitUnsignedInteger(1, 0); // EE // End Header (no Notification or Signature) stream.WriteNBitUnsignedInteger(1, 0); // EE } private static void EncodeBody(BitOutputStreamExact stream, BodyType body) { // Body type choice - CurrentDemandReq = 13 (6-bit) if (body.CurrentDemandReq_isUsed) { stream.WriteNBitUnsignedInteger(6, 13); // CurrentDemandReq choice EncodeCurrentDemandReqType(stream, body.CurrentDemandReq); } // Add other message types as needed else { throw new Exception("Unsupported message type for encoding"); } // End Body stream.WriteNBitUnsignedInteger(1, 0); // EE } private static void EncodeCurrentDemandReqType(BitOutputStreamExact stream, CurrentDemandReqType req) { // Grammar state 273: DC_EVStatus (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(DC_EVStatus) EncodeDC_EVStatusType(stream, req.DC_EVStatus); // Grammar state 274: EVTargetCurrent (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVTargetCurrent) EncodePhysicalValueType(stream, req.EVTargetCurrent); // Grammar state 275: Optional elements (3-bit choice) EncodeOptionalElements275(stream, req); } private static void EncodeOptionalElements275(BitOutputStreamExact stream, CurrentDemandReqType req) { // Grammar state 275 - handle optional elements in sequence if (req.EVMaximumVoltageLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 0); // EVMaximumVoltageLimit choice EncodePhysicalValueType(stream, req.EVMaximumVoltageLimit); EncodeOptionalElements276(stream, req); // Continue to state 276 } else if (req.EVMaximumCurrentLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 1); // EVMaximumCurrentLimit choice EncodePhysicalValueType(stream, req.EVMaximumCurrentLimit); EncodeOptionalElements277(stream, req); // Continue to state 277 } else if (req.EVMaximumPowerLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 2); // EVMaximumPowerLimit choice EncodePhysicalValueType(stream, req.EVMaximumPowerLimit); EncodeOptionalElements278(stream, req); // Continue to state 278 } else if (req.BulkChargingComplete_isUsed) { stream.WriteNBitUnsignedInteger(3, 3); // BulkChargingComplete choice stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.BulkChargingComplete ? 1u : 0u); // boolean value stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements279(stream, req); // Continue to state 279 } else { // ChargingComplete (mandatory) stream.WriteNBitUnsignedInteger(3, 4); // ChargingComplete choice stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.ChargingComplete ? 1u : 0u); // boolean value stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements280(stream, req); // Continue to state 280 } } private static void EncodeOptionalElements276(BitOutputStreamExact stream, CurrentDemandReqType req) { // After EVMaximumVoltageLimit - states similar to 275 but different grammar if (req.EVMaximumCurrentLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 0); // EVMaximumCurrentLimit EncodePhysicalValueType(stream, req.EVMaximumCurrentLimit); EncodeOptionalElements277(stream, req); } else if (req.EVMaximumPowerLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 1); // EVMaximumPowerLimit EncodePhysicalValueType(stream, req.EVMaximumPowerLimit); EncodeOptionalElements278(stream, req); } else if (req.BulkChargingComplete_isUsed) { stream.WriteNBitUnsignedInteger(3, 2); // BulkChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.BulkChargingComplete ? 1u : 0u); stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements279(stream, req); } else { stream.WriteNBitUnsignedInteger(3, 3); // ChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.ChargingComplete ? 1u : 0u); stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements280(stream, req); } } private static void EncodeOptionalElements277(BitOutputStreamExact stream, CurrentDemandReqType req) { // After EVMaximumCurrentLimit if (req.EVMaximumPowerLimit_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); // EVMaximumPowerLimit EncodePhysicalValueType(stream, req.EVMaximumPowerLimit); EncodeOptionalElements278(stream, req); } else if (req.BulkChargingComplete_isUsed) { stream.WriteNBitUnsignedInteger(2, 1); // BulkChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.BulkChargingComplete ? 1u : 0u); stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements279(stream, req); } else { stream.WriteNBitUnsignedInteger(2, 2); // ChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.ChargingComplete ? 1u : 0u); stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements280(stream, req); } } private static void EncodeOptionalElements278(BitOutputStreamExact stream, CurrentDemandReqType req) { // After EVMaximumPowerLimit if (req.BulkChargingComplete_isUsed) { stream.WriteNBitUnsignedInteger(1, 0); // BulkChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.BulkChargingComplete ? 1u : 0u); stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements279(stream, req); } else { stream.WriteNBitUnsignedInteger(1, 1); // ChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.ChargingComplete ? 1u : 0u); stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements280(stream, req); } } private static void EncodeOptionalElements279(BitOutputStreamExact stream, CurrentDemandReqType req) { // After BulkChargingComplete - must have ChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // ChargingComplete stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, req.ChargingComplete ? 1u : 0u); stream.WriteNBitUnsignedInteger(1, 0); // EE EncodeOptionalElements280(stream, req); } private static void EncodeOptionalElements280(BitOutputStreamExact stream, CurrentDemandReqType req) { // Grammar state 280: 2-bit choice for remaining optional elements if (req.RemainingTimeToFullSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); // RemainingTimeToFullSoC EncodePhysicalValueType(stream, req.RemainingTimeToFullSoC); EncodeOptionalElements281(stream, req); } else if (req.RemainingTimeToBulkSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 1); // RemainingTimeToBulkSoC EncodePhysicalValueType(stream, req.RemainingTimeToBulkSoC); EncodeOptionalElements282(stream, req); } else { stream.WriteNBitUnsignedInteger(2, 2); // EVTargetVoltage (mandatory) EncodePhysicalValueType(stream, req.EVTargetVoltage); // End CurrentDemandReq stream.WriteNBitUnsignedInteger(1, 0); // EE } } private static void EncodeOptionalElements281(BitOutputStreamExact stream, CurrentDemandReqType req) { // Grammar state 281: 2-bit choice after RemainingTimeToFullSoC if (req.RemainingTimeToBulkSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); // RemainingTimeToBulkSoC EncodePhysicalValueType(stream, req.RemainingTimeToBulkSoC); EncodeOptionalElements282(stream, req); } else { stream.WriteNBitUnsignedInteger(2, 1); // EVTargetVoltage (mandatory) EncodePhysicalValueType(stream, req.EVTargetVoltage); // End CurrentDemandReq stream.WriteNBitUnsignedInteger(1, 0); // EE } } private static void EncodeOptionalElements282(BitOutputStreamExact stream, CurrentDemandReqType req) { // Grammar state 282: Must encode EVTargetVoltage stream.WriteNBitUnsignedInteger(1, 0); // EVTargetVoltage EncodePhysicalValueType(stream, req.EVTargetVoltage); // End CurrentDemandReq stream.WriteNBitUnsignedInteger(1, 0); // EE } private static void EncodeDC_EVStatusType(BitOutputStreamExact stream, DC_EVStatusType status) { // Grammar state for DC_EVStatusType // EVReady (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVReady) stream.WriteNBitUnsignedInteger(1, 0); // boolean start stream.WriteNBitUnsignedInteger(1, status.EVReady ? 1u : 0u); // boolean value stream.WriteNBitUnsignedInteger(1, 0); // EE // EVErrorCode (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVErrorCode) stream.WriteNBitUnsignedInteger(1, 0); // enum start stream.WriteNBitUnsignedInteger(4, (uint)status.EVErrorCode); // 4-bit enum value stream.WriteNBitUnsignedInteger(1, 0); // EE // EVRESSSOC (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVRESSSOC) stream.WriteNBitUnsignedInteger(1, 0); // integer start stream.WriteNBitUnsignedInteger(7, (uint)status.EVRESSSOC); // 7-bit value (0-100) stream.WriteNBitUnsignedInteger(1, 0); // EE // End DC_EVStatus stream.WriteNBitUnsignedInteger(1, 0); // EE } private static void EncodePhysicalValueType(BitOutputStreamExact stream, PhysicalValueType value) { // Grammar state for PhysicalValueType // Multiplier (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(Multiplier) stream.WriteNBitUnsignedInteger(1, 0); // integer start EncodeInteger8(stream, value.Multiplier); stream.WriteNBitUnsignedInteger(1, 0); // EE // Unit (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(Unit) stream.WriteNBitUnsignedInteger(1, 0); // enum start stream.WriteNBitUnsignedInteger(3, (uint)value.Unit); // 3-bit enum stream.WriteNBitUnsignedInteger(1, 0); // EE // Value (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(Value) stream.WriteNBitUnsignedInteger(1, 0); // integer start EncodeInteger16(stream, value.Value); stream.WriteNBitUnsignedInteger(1, 0); // EE // End PhysicalValue stream.WriteNBitUnsignedInteger(1, 0); // EE } private static void EncodeInteger8(BitOutputStreamExact stream, sbyte value) { if (value >= 0) { stream.WriteNBitUnsignedInteger(1, 0); // positive sign bit stream.WriteUnsignedInteger((uint)value); } else { stream.WriteNBitUnsignedInteger(1, 1); // negative sign bit stream.WriteUnsignedInteger((uint)(-(value + 1))); // magnitude } } private static void EncodeInteger16(BitOutputStreamExact stream, short value) { if (value >= 0) { stream.WriteNBitUnsignedInteger(1, 0); // positive sign bit stream.WriteUnsignedInteger((uint)value); } else { stream.WriteNBitUnsignedInteger(1, 1); // negative sign bit stream.WriteUnsignedInteger((uint)(-(value + 1))); // magnitude } } private static void WriteBytes(BitOutputStreamExact stream, byte[] bytes) { foreach (byte b in bytes) { stream.WriteNBitUnsignedInteger(8, b); } } private static byte[] ConvertHexStringToBytes(string hex) { if (hex.Length % 2 != 0) throw new ArgumentException("Hex string must have even length"); byte[] bytes = new byte[hex.Length / 2]; for (int i = 0; i < hex.Length; i += 2) { bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); } return bytes; } public static byte[] EncodeCurrentDemandRes(CurrentDemandResType response) { try { var stream = new BitOutputStreamExact(); // Write EXI header stream.WriteNBitUnsignedInteger(16, 0x8098); // Simple CurrentDemandRes encoding for testing // This is a placeholder - real implementation would need full grammar return stream.GetBytes(); } catch (Exception ex) { throw new Exception($"Failed to encode CurrentDemandRes: {ex.Message}", ex); } } } }