/* * Copyright (C) 2007-2024 C# Port * Original Copyright (C) 2007-2018 Siemens AG * * Exact EXI Encoder implementation - byte-compatible with OpenV2G VC2022 C implementation * Matches iso1EXIDatatypesEncoder.c exactly with all grammar states and bit patterns */ using System; using System.Text; using V2GDecoderNet.EXI; namespace V2GDecoderNet.V2G { /// /// Exact EXI Encoder implementation matching VC2022 C code exactly /// Matches iso1EXIDatatypesEncoder.c with all grammar states 256-330 /// public class EXIEncoderExact { /// /// Encode V2G message to EXI - exact implementation matching VC2022 /// Entry point: encode_iso1ExiDocument() /// public static byte[] EncodeV2GMessage(V2GMessageExact message) { if (message == null) throw new ArgumentNullException(nameof(message)); var stream = new BitOutputStreamExact(); try { // Step 1: Write EXI header - exact match to VC2022 writeEXIHeader() WriteEXIHeader(stream); // Step 2: Encode V2G_Message choice 76 in 7-bit encoding // matches: if(exiDoc->V2G_Message_isUsed == 1u) encodeNBitUnsignedInteger(stream, 7, 76); stream.WriteNBitUnsignedInteger(7, 76); // Step 3: Encode V2G_Message structure - Grammar states 256β†’257β†’3 EncodeAnonType_V2G_Message(stream, message); // Step 4: Flush remaining bits - exact match to VC2022 encodeFinish() stream.Flush(); return stream.ToArray(); } catch (Exception ex) { throw new EXIExceptionExact(EXIErrorCodesExact.EXI_ERROR_NOT_IMPLEMENTED_YET, "V2G message encoding failed", ex); } } /// /// Encode Iso1EXIDocument to EXI - exact implementation matching VC2022 encode_iso1ExiDocument() /// Provides complete debugging comparison with VC2022 structure dump /// public static byte[] EncodeIso1Document(Iso1EXIDocument doc) { if (doc == null) throw new ArgumentNullException(nameof(doc)); // Convert to V2GMessageExact and use existing encoder if (!doc.V2G_Message_isUsed || doc.V2G_Message == null) { throw new ArgumentException("V2G_Message not set in Iso1EXIDocument"); } return EncodeV2GMessage(doc.V2G_Message); } /// /// Print detailed Iso1EXIDocument structure for debugging comparison with VC2022 /// Matches the output format from VC2022 dump_iso1_document_to_file() /// public static void PrintIso1DocumentDebug(Iso1EXIDocument doc) { var debug = new StringBuilder(); debug.AppendLine("=== Iso1EXIDocument Structure Debug ==="); // Document level flags debug.AppendLine($"V2G_Message_isUsed: {doc.V2G_Message_isUsed}"); debug.AppendLine($"CurrentDemandReq_isUsed: {doc.CurrentDemandReq_isUsed}"); debug.AppendLine($"CurrentDemandRes_isUsed: {doc.CurrentDemandRes_isUsed}"); if (doc.V2G_Message_isUsed && doc.V2G_Message != null) { debug.AppendLine(); debug.AppendLine("--- V2G_Message ---"); debug.AppendLine($"SessionID: {doc.V2G_Message.SessionID ?? "null"}"); if (doc.V2G_Message.Body != null) { debug.AppendLine(); debug.AppendLine("--- Body ---"); debug.AppendLine($"CurrentDemandReq_isUsed: {doc.V2G_Message.Body.CurrentDemandReq_isUsed}"); debug.AppendLine($"CurrentDemandRes_isUsed: {doc.V2G_Message.Body.CurrentDemandRes_isUsed}"); if (doc.V2G_Message.Body.CurrentDemandReq_isUsed && doc.V2G_Message.Body.CurrentDemandReq != null) { var req = doc.V2G_Message.Body.CurrentDemandReq; debug.AppendLine(); debug.AppendLine("--- CurrentDemandReq ---"); // DC_EVStatus if (req.DC_EVStatus != null) { debug.AppendLine($"DC_EVStatus.EVReady: {req.DC_EVStatus.EVReady}"); debug.AppendLine($"DC_EVStatus.EVErrorCode: {req.DC_EVStatus.EVErrorCode}"); debug.AppendLine($"DC_EVStatus.EVRESSSOC: {req.DC_EVStatus.EVRESSSOC}"); } // Physical values if (req.EVTargetCurrent != null) { debug.AppendLine($"EVTargetCurrent: M={req.EVTargetCurrent.Multiplier}, U={(int)req.EVTargetCurrent.Unit}, V={req.EVTargetCurrent.Value}"); } if (req.EVTargetVoltage != null) { debug.AppendLine($"EVTargetVoltage: M={req.EVTargetVoltage.Multiplier}, U={(int)req.EVTargetVoltage.Unit}, V={req.EVTargetVoltage.Value}"); } // Optional fields debug.AppendLine($"EVMaximumVoltageLimit_isUsed: {req.EVMaximumVoltageLimit_isUsed}"); if (req.EVMaximumVoltageLimit_isUsed && req.EVMaximumVoltageLimit != null) { debug.AppendLine($"EVMaximumVoltageLimit: M={req.EVMaximumVoltageLimit.Multiplier}, U={(int)req.EVMaximumVoltageLimit.Unit}, V={req.EVMaximumVoltageLimit.Value}"); } debug.AppendLine($"EVMaximumCurrentLimit_isUsed: {req.EVMaximumCurrentLimit_isUsed}"); if (req.EVMaximumCurrentLimit_isUsed && req.EVMaximumCurrentLimit != null) { debug.AppendLine($"EVMaximumCurrentLimit: M={req.EVMaximumCurrentLimit.Multiplier}, U={(int)req.EVMaximumCurrentLimit.Unit}, V={req.EVMaximumCurrentLimit.Value}"); } debug.AppendLine($"EVMaximumPowerLimit_isUsed: {req.EVMaximumPowerLimit_isUsed}"); if (req.EVMaximumPowerLimit_isUsed && req.EVMaximumPowerLimit != null) { debug.AppendLine($"EVMaximumPowerLimit: M={req.EVMaximumPowerLimit.Multiplier}, U={(int)req.EVMaximumPowerLimit.Unit}, V={req.EVMaximumPowerLimit.Value}"); } debug.AppendLine($"BulkChargingComplete_isUsed: {req.BulkChargingComplete_isUsed}"); if (req.BulkChargingComplete_isUsed) { debug.AppendLine($"BulkChargingComplete: {req.BulkChargingComplete}"); } debug.AppendLine($"ChargingComplete: {req.ChargingComplete}"); debug.AppendLine($"RemainingTimeToFullSoC_isUsed: {req.RemainingTimeToFullSoC_isUsed}"); if (req.RemainingTimeToFullSoC_isUsed && req.RemainingTimeToFullSoC != null) { debug.AppendLine($"RemainingTimeToFullSoC: M={req.RemainingTimeToFullSoC.Multiplier}, U={(int)req.RemainingTimeToFullSoC.Unit}, V={req.RemainingTimeToFullSoC.Value}"); } debug.AppendLine($"RemainingTimeToBulkSoC_isUsed: {req.RemainingTimeToBulkSoC_isUsed}"); if (req.RemainingTimeToBulkSoC_isUsed && req.RemainingTimeToBulkSoC != null) { debug.AppendLine($"RemainingTimeToBulkSoC: M={req.RemainingTimeToBulkSoC.Multiplier}, U={(int)req.RemainingTimeToBulkSoC.Unit}, V={req.RemainingTimeToBulkSoC.Value}"); } } } } debug.AppendLine("=== End Iso1EXIDocument Structure ==="); Console.Error.WriteLine(debug.ToString()); } /// /// Create Iso1EXIDocument from V2GMessageExact for structure comparison /// Enables exact debugging comparison between VC2022 and dotnet /// public static Iso1EXIDocument CreateIso1DocumentFromV2GMessage(V2GMessageExact message) { var doc = new Iso1EXIDocument(); doc.Initialize(); // VC2022 equivalent: init_iso1EXIDocument() doc.V2G_Message_isUsed = true; doc.V2G_Message = message; // Set document-level flags based on message content if (message.Body?.CurrentDemandReq_isUsed == true) { doc.CurrentDemandReq_isUsed = true; } if (message.Body?.CurrentDemandRes_isUsed == true) { doc.CurrentDemandRes_isUsed = true; } return doc; } /// /// Write EXI header - exact match to VC2022 writeEXIHeader() /// Initializes stream and writes 0x80 (10000000) - 8 bits /// private static void WriteEXIHeader(BitOutputStreamExact stream) { // VC2022: int writeEXIHeader(bitstream_t* stream) { // stream->buffer = 0; // stream->capacity = 8; // return writeBits(stream, 8, 128); // } // CRITICAL: Initialize stream state exactly like VC2022 - ONLY at the beginning stream.ResetBuffer(); stream.WriteBits(8, 128); // 0x80 // Console.Error.WriteLine($"πŸ” [WriteEXIHeader] Written 0x80, position: {stream.Position}, buffer: {stream.BufferState}, capacity: {stream.CapacityState}"); } /// /// Encode V2G_Message structure - exact match to VC2022 encode_iso1AnonType_V2G_Message() /// Grammar states: 256 (Header) β†’ 257 (Body) β†’ 3 (END_ELEMENT) /// private static void EncodeAnonType_V2G_Message(BitOutputStreamExact stream, V2GMessageExact message) { int grammarID = 256; bool done = false; // Console.Error.WriteLine($"πŸ” [V2G_Message] Starting grammar state machine, position: {stream.Position}"); while (!done) { switch (grammarID) { case 256: // Grammar 256: Header is mandatory // Console.Error.WriteLine($"πŸ” [Grammar 256] Encoding Header, position: {stream.Position}"); stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(Header) EncodeMessageHeaderType(stream, message); grammarID = 257; break; case 257: // Grammar 257: Body is mandatory // Console.Error.WriteLine($"πŸ” [Grammar 257] Encoding Body, position: {stream.Position}"); stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(Body) EncodeBodyType(stream, message.Body); grammarID = 3; break; case 3: // Grammar 3: END_ELEMENT // Console.Error.WriteLine($"πŸ” [Grammar 3] END_ELEMENT, position: {stream.Position}"); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT done = true; break; default: throw new EXIExceptionExact(EXIErrorCodesExact.EXI_ERROR_UNKNOWN_EVENT, $"Unknown V2G_Message grammar state: {grammarID}"); } } // Console.Error.WriteLine($"πŸ” [V2G_Message] Grammar state machine completed, position: {stream.Position}"); } /// /// Encode MessageHeader - exact match to VC2022 encode_iso1MessageHeaderType() /// Grammar states 0β†’1 with SessionID BINARY_HEX encoding /// private static void EncodeMessageHeaderType(BitOutputStreamExact stream, V2GMessageExact message) { // Console.Error.WriteLine($"πŸ” [MessageHeader] Starting encoding, position: {stream.Position}"); // Grammar state 0: SessionID is mandatory stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(SessionID) // SessionID BINARY_HEX encoding - exact match to VC2022 stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BINARY_HEX] // Convert SessionID hex string to bytes - exact match to VC2022 structure byte[] sessionIdBytes = ConvertHexStringToBytes(message.SessionID ?? "4142423030303831"); // Write length using VC2022 encodeUnsignedInteger16 - CRITICAL FIX! stream.WriteUnsignedInteger16((ushort)sessionIdBytes.Length); // Console.Error.WriteLine($"πŸ” [SessionID] Length: {sessionIdBytes.Length}, position: {stream.Position}"); // Write bytes (VC2022 uses encodeBytes) foreach (byte b in sessionIdBytes) { stream.WriteBits(8, b); } // Console.Error.WriteLine($"πŸ” [SessionID] Bytes written, position: {stream.Position}"); stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Grammar state 1: Skip optional Notification, Signature β†’ END_ELEMENT stream.WriteNBitUnsignedInteger(2, 2); // END_ELEMENT choice (choice 2 in 2-bit) // Console.Error.WriteLine($"πŸ” [MessageHeader] Encoding completed, position: {stream.Position}"); } /// /// Encode Body - exact match to VC2022 encode_iso1BodyType() /// Grammar state 220: 6-bit choice for message type /// private static void EncodeBodyType(BitOutputStreamExact stream, BodyType body) { // Console.Error.WriteLine($"πŸ” [Body] Starting encoding, position: {stream.Position}"); // Grammar state 220: Message type selection (6-bit choice) if (body.CurrentDemandReq_isUsed) { // Console.Error.WriteLine($"πŸ” [Body] Encoding CurrentDemandReq (choice 13)"); stream.WriteNBitUnsignedInteger(6, 13); // CurrentDemandReq = choice 13 EncodeCurrentDemandReqType(stream, body.CurrentDemandReq); } else if (body.CurrentDemandRes_isUsed) { // Console.Error.WriteLine($"πŸ” [Body] Encoding CurrentDemandRes (choice 14)"); stream.WriteNBitUnsignedInteger(6, 14); // CurrentDemandRes = choice 14 EncodeCurrentDemandResType(stream, body.CurrentDemandRes); } else { throw new EXIExceptionExact(EXIErrorCodesExact.EXI_ERROR_NOT_IMPLEMENTED_YET, "No supported message type found in Body"); } // Grammar state 3: END_ELEMENT stream.WriteNBitUnsignedInteger(1, 0); // Console.Error.WriteLine($"πŸ” [Body] Encoding completed, position: {stream.Position}"); } /// /// Encode CurrentDemandReq - exact match to VC2022 encode_iso1CurrentDemandReqType() /// Grammar states 273-283 with precise choice bit patterns /// private static void EncodeCurrentDemandReqType(BitOutputStreamExact stream, CurrentDemandReqType req) { int grammarID = 273; bool done = false; // Console.Error.WriteLine($"πŸ” [CurrentDemandReq] Starting grammar state machine, position: {stream.Position}"); while (!done) { // Console.Error.WriteLine($"πŸ” [DEBUG CurrentDemandReq] Grammar case: {grammarID}, stream pos: {stream.Position}"); switch (grammarID) { case 273: // DC_EVStatus is mandatory stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(DC_EVStatus) EncodeDC_EVStatusType(stream, req.DC_EVStatus); grammarID = 274; break; case 274: // EVTargetCurrent is mandatory stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVTargetCurrent) EncodePhysicalValueType(stream, req.EVTargetCurrent); grammarID = 275; break; case 275: // 3-bit choice for optional elements (5 choices) Console.Error.WriteLine($"πŸ” Grammar 275: EVMaxVoltageLimit_isUsed={req.EVMaximumVoltageLimit_isUsed}"); Console.Error.WriteLine($"πŸ” Grammar 275: EVMaxCurrentLimit_isUsed={req.EVMaximumCurrentLimit_isUsed}"); Console.Error.WriteLine($"πŸ” Grammar 275: EVMaxPowerLimit_isUsed={req.EVMaximumPowerLimit_isUsed}"); Console.Error.WriteLine($"πŸ” Grammar 275: BulkChargingComplete_isUsed={req.BulkChargingComplete_isUsed}"); if (req.EVMaximumVoltageLimit_isUsed) { Console.Error.WriteLine($"πŸ” Grammar 275: choice 0 (EVMaximumVoltageLimit), 3-bit=0"); stream.WriteNBitUnsignedInteger(3, 0); EncodePhysicalValueType(stream, req.EVMaximumVoltageLimit); grammarID = 276; } else if (req.EVMaximumCurrentLimit_isUsed) { // Console.Error.WriteLine($"πŸ” Grammar 275: choice 1 (EVMaximumCurrentLimit), 3-bit=1"); stream.WriteNBitUnsignedInteger(3, 1); EncodePhysicalValueType(stream, req.EVMaximumCurrentLimit); grammarID = 277; } else if (req.EVMaximumPowerLimit_isUsed) { // Console.Error.WriteLine($"πŸ” Grammar 275: choice 2 (EVMaximumPowerLimit), 3-bit=2"); stream.WriteNBitUnsignedInteger(3, 2); EncodePhysicalValueType(stream, req.EVMaximumPowerLimit); grammarID = 278; } else if (req.BulkChargingComplete_isUsed) { // Console.Error.WriteLine($"πŸ” Grammar 275: choice 3 (BulkChargingComplete), 3-bit=3"); stream.WriteNBitUnsignedInteger(3, 3); EncodeBooleanElement(stream, req.BulkChargingComplete); grammarID = 279; } else // ChargingComplete is mandatory default (if( 1 == 1 )) { Console.Error.WriteLine($"πŸ” Grammar 275: choice 4 (ChargingComplete), 3-bit=4"); stream.WriteNBitUnsignedInteger(3, 4); EncodeBooleanElement(stream, req.ChargingComplete); grammarID = 280; } break; case 276: // After EVMaximumVoltageLimit - 3-bit choice (4 choices) Console.Error.WriteLine($"πŸ” Grammar 276: EVMaxCurrentLimit_isUsed={req.EVMaximumCurrentLimit_isUsed}"); Console.Error.WriteLine($"πŸ” Grammar 276: EVMaxPowerLimit_isUsed={req.EVMaximumPowerLimit_isUsed}"); Console.Error.WriteLine($"πŸ” Grammar 276: BulkChargingComplete_isUsed={req.BulkChargingComplete_isUsed}"); if (req.EVMaximumCurrentLimit_isUsed) { Console.Error.WriteLine($"πŸ” Grammar 276: choice 0 (EVMaximumCurrentLimit), 3-bit=0"); stream.WriteNBitUnsignedInteger(3, 0); EncodePhysicalValueType(stream, req.EVMaximumCurrentLimit); grammarID = 277; } else if (req.EVMaximumPowerLimit_isUsed) { // Console.Error.WriteLine($"πŸ” Grammar 276: choice 1 (EVMaximumPowerLimit), 3-bit=1"); stream.WriteNBitUnsignedInteger(3, 1); EncodePhysicalValueType(stream, req.EVMaximumPowerLimit); grammarID = 278; } else if (req.BulkChargingComplete_isUsed) { // Console.Error.WriteLine($"πŸ” Grammar 276: choice 2 (BulkChargingComplete), 3-bit=2"); stream.WriteNBitUnsignedInteger(3, 2); EncodeBooleanElement(stream, req.BulkChargingComplete); grammarID = 279; } else // ChargingComplete (if( 1 == 1 )) { // Console.Error.WriteLine($"πŸ” Grammar 276: choice 3 (ChargingComplete), 3-bit=3"); stream.WriteNBitUnsignedInteger(3, 3); EncodeBooleanElement(stream, req.ChargingComplete); grammarID = 280; } break; case 277: // After EVMaximumCurrentLimit - 2-bit choice (3 choices) Console.Error.WriteLine($"πŸ” Grammar 277: EVMaxPowerLimit_isUsed={req.EVMaximumPowerLimit_isUsed}"); Console.Error.WriteLine($"πŸ” Grammar 277: BulkChargingComplete_isUsed={req.BulkChargingComplete_isUsed}"); if (req.EVMaximumPowerLimit_isUsed) { Console.Error.WriteLine($"πŸ” Grammar 277: choice 0 (EVMaximumPowerLimit), 2-bit=0"); stream.WriteNBitUnsignedInteger(2, 0); EncodePhysicalValueType(stream, req.EVMaximumPowerLimit); grammarID = 278; } else if (req.BulkChargingComplete_isUsed) { // Console.Error.WriteLine($"πŸ” Grammar 277: choice 1 (BulkChargingComplete), 2-bit=1"); stream.WriteNBitUnsignedInteger(2, 1); EncodeBooleanElement(stream, req.BulkChargingComplete); grammarID = 279; } else // ChargingComplete (if( 1 == 1 )) { // Console.Error.WriteLine($"πŸ” Grammar 277: choice 2 (ChargingComplete), 2-bit=2"); stream.WriteNBitUnsignedInteger(2, 2); EncodeBooleanElement(stream, req.ChargingComplete); grammarID = 280; } break; case 278: // After EVMaximumPowerLimit - 2-bit choice (2 choices) Console.Error.WriteLine($"πŸ” Grammar 278: BulkChargingComplete_isUsed={req.BulkChargingComplete_isUsed}"); if (req.BulkChargingComplete_isUsed) { // Console.Error.WriteLine($"πŸ“ Grammar 278: choice 0 (BulkChargingComplete), 2-bit=0"); stream.WriteNBitUnsignedInteger(2, 0); EncodeBooleanElement(stream, req.BulkChargingComplete); grammarID = 279; } else // ChargingComplete (if( 1 == 1 )) { Console.Error.WriteLine($"πŸ“ Grammar 278: choice 1 (ChargingComplete), 2-bit=1"); stream.WriteNBitUnsignedInteger(2, 1); EncodeBooleanElement(stream, req.ChargingComplete); grammarID = 280; } break; case 279: // After BulkChargingComplete - skip to optional elements if (req.RemainingTimeToFullSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); EncodePhysicalValueType(stream, req.RemainingTimeToFullSoC); grammarID = 281; } else if (req.RemainingTimeToBulkSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 1); EncodePhysicalValueType(stream, req.RemainingTimeToBulkSoC); grammarID = 282; } else { stream.WriteNBitUnsignedInteger(2, 2); EncodePhysicalValueType(stream, req.EVTargetVoltage); // Mandatory grammarID = 3; // END } break; case 280: // After ChargingComplete - 2-bit choice if (req.RemainingTimeToFullSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); EncodePhysicalValueType(stream, req.RemainingTimeToFullSoC); grammarID = 281; } else if (req.RemainingTimeToBulkSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 1); EncodePhysicalValueType(stream, req.RemainingTimeToBulkSoC); grammarID = 282; } else { stream.WriteNBitUnsignedInteger(2, 2); EncodePhysicalValueType(stream, req.EVTargetVoltage); // Mandatory grammarID = 3; // END } break; case 281: // After RemainingTimeToFullSoC - 2-bit choice if (req.RemainingTimeToBulkSoC_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); EncodePhysicalValueType(stream, req.RemainingTimeToBulkSoC); grammarID = 282; } else { stream.WriteNBitUnsignedInteger(2, 1); EncodePhysicalValueType(stream, req.EVTargetVoltage); // Mandatory grammarID = 3; // END } break; case 282: // After RemainingTimeToBulkSoC - 1-bit choice stream.WriteNBitUnsignedInteger(1, 0); EncodePhysicalValueType(stream, req.EVTargetVoltage); // Mandatory grammarID = 3; // END break; case 3: // END_ELEMENT stream.WriteNBitUnsignedInteger(1, 0); done = true; break; default: throw new EXIExceptionExact(EXIErrorCodesExact.EXI_ERROR_UNKNOWN_EVENT, $"Unknown CurrentDemandReq grammar state: {grammarID}"); } } // Console.Error.WriteLine($"πŸ” [CurrentDemandReq] Grammar state machine completed, final position: {stream.Position}"); } /// /// Encode CurrentDemandRes - simplified implementation /// private static void EncodeCurrentDemandResType(BitOutputStreamExact stream, CurrentDemandResType res) { // Console.Error.WriteLine($"πŸ” [CurrentDemandRes] Starting encoding, position: {stream.Position}"); // Grammar 317: ResponseCode (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(ResponseCode) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(5, (int)res.ResponseCode); // 5-bit enumeration stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Simple implementation - skip complex grammar for now stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // Console.Error.WriteLine($"πŸ” [CurrentDemandRes] Encoding completed, position: {stream.Position}"); } /// /// Encode DC_EVStatus - exact match to VC2022 encode_iso1DC_EVStatusType() /// Grammar states 314-316 /// private static void EncodeDC_EVStatusType(BitOutputStreamExact stream, DC_EVStatusType status) { // Console.Error.WriteLine($"πŸ” [DC_EVStatus] Starting encoding, position: {stream.Position}"); // Grammar 314: EVReady (mandatory boolean) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVReady) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BOOLEAN] stream.WriteBit(status.EVReady ? 1 : 0); // Boolean bit stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Grammar 315: EVErrorCode (mandatory enumeration) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVErrorCode) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(4, status.EVErrorCode); // 4-bit enumeration stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Grammar 316: EVRESSSOC (mandatory 7-bit unsigned integer) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVRESSSOC) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[NBIT_UNSIGNED_INTEGER] stream.WriteNBitUnsignedInteger(7, status.EVRESSSOC); // 7-bit unsigned (0-100) stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Grammar 3: END_ELEMENT stream.WriteNBitUnsignedInteger(1, 0); // Console.Error.WriteLine($"πŸ” [DC_EVStatus] Encoding completed, position: {stream.Position}"); } /// /// Encode PhysicalValue - exact match to VC2022 encode_iso1PhysicalValueType() /// Grammar states 117β†’118β†’119β†’3 with complete START_ELEMENTβ†’CHARACTERSβ†’EE pattern /// private static void EncodePhysicalValueType(BitOutputStreamExact stream, PhysicalValueType value) { int posBefore = stream.Position; // Console.Error.WriteLine($"πŸ”¬ [PhysicalValue] Starting: M={value.Multiplier}, U={(int)value.Unit}, V={value.Value}, pos_before={posBefore}"); // Grammar 117: START_ELEMENT(Multiplier) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[NBIT_UNSIGNED_INTEGER] stream.WriteNBitUnsignedInteger(3, (int)(value.Multiplier + 3)); // 3-bit unsigned + 3 offset stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Grammar 118: START_ELEMENT(Unit) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(3, (int)value.Unit); // 3-bit enumeration stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Grammar 119: START_ELEMENT(Value) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[INTEGER] stream.WriteInteger16((short)value.Value); // VC2022 encodeInteger16 stream.WriteNBitUnsignedInteger(1, 0); // valid EE // Grammar 3: END_ELEMENT stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT int posAfter = stream.Position; // Console.Error.WriteLine($"πŸ”¬ [PhysicalValue] Completed: M={value.Multiplier}, U={(int)value.Unit}, V={value.Value}, pos_after={posAfter}, used_bytes={posAfter - posBefore}"); } /// /// Encode boolean element - exact match to VC2022 boolean encoding pattern /// CHARACTERS[BOOLEAN] + value + valid EE /// private static void EncodeBooleanElement(BitOutputStreamExact stream, bool value) { stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BOOLEAN] stream.WriteBit(value ? 1 : 0); // Boolean bit stream.WriteNBitUnsignedInteger(1, 0); // valid EE } /// /// Convert hex string to byte array - exact match to VC2022 SessionID handling /// private static byte[] ConvertHexStringToBytes(string hexString) { if (string.IsNullOrEmpty(hexString)) return new byte[0]; // Remove any spaces or hyphens hexString = hexString.Replace(" ", "").Replace("-", ""); // Ensure even length if (hexString.Length % 2 != 0) hexString = "0" + hexString; byte[] bytes = new byte[hexString.Length / 2]; for (int i = 0; i < bytes.Length; i++) { bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } return bytes; } } }