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 - exactly like C writeEXIHeader() stream.WriteNBitUnsignedInteger(8, 0x80); stream.WriteNBitUnsignedInteger(8, 0x98); // Encode document content - exactly like C encode_iso1ExiDocument // V2G_Message choice = 76 (7-bit) stream.WriteNBitUnsignedInteger(7, 76); // Encode V2G_Message content - matches C encode_iso1AnonType_V2G_Message EncodeV2GMessageContent(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); } } /// /// Encode V2G_Message content - exact port of C encode_iso1AnonType_V2G_Message /// private static void EncodeV2GMessageContent(BitOutputStreamExact stream, V2GMessageExact message) { // Grammar state for V2G_Message: Header is mandatory stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(Header) EncodeMessageHeader(stream, message.SessionID); // Grammar state: Body is mandatory stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(Body) EncodeBodyType(stream, message.Body); // END_ELEMENT for V2G_Message stream.WriteNBitUnsignedInteger(1, 0); } /// /// Encode MessageHeader - exact port of C encode_iso1MessageHeaderType /// private static void EncodeMessageHeader(BitOutputStreamExact stream, string sessionId) { // Grammar state for MessageHeaderType: SessionID is mandatory stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(SessionID) // SessionID encoding - binary hex format exactly like C // Convert hex string to bytes first byte[] sessionBytes = ConvertHexStringToBytes(sessionId); // Encode as binary string (characters[BINARY_HEX]) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS choice stream.WriteUnsignedInteger((uint)sessionBytes.Length); // Length encoding // Write actual bytes WriteBytes(stream, sessionBytes); // End SessionID element stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // Grammar allows optional Notification and Signature, but we don't use them // End Header with 2-bit choice for end stream.WriteNBitUnsignedInteger(2, 2); // END_ELEMENT choice (skipping optional elements) } /// /// Encode Body content - exact port of C encode_iso1BodyType /// private static void EncodeBodyType(BitOutputStreamExact stream, BodyType body) { // Grammar state for Body: 6-bit choice for message type if (body.CurrentDemandReq_isUsed) { // Choice 13 for CurrentDemandReq - exactly like C version stream.WriteNBitUnsignedInteger(6, 13); EncodeCurrentDemandReqType(stream, body.CurrentDemandReq); } else if (body.CurrentDemandRes_isUsed) { // Choice 14 for CurrentDemandRes - exactly like C version stream.WriteNBitUnsignedInteger(6, 14); EncodeCurrentDemandResType(stream, body.CurrentDemandRes); } else { throw new Exception("Unsupported message type for encoding. Currently supported: CurrentDemandReq, CurrentDemandRes"); } // End Body element - grammar state 3 stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } 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 ? 1 : 0); // 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 ? 1 : 0); // 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 ? 1 : 0); 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 ? 1 : 0); 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 ? 1 : 0); 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 ? 1 : 0); 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 ? 1 : 0); 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 ? 1 : 0); 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 ? 1 : 0); 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 ? 1 : 0); // 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, (int)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, 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, (int)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; } /// /// Encode CurrentDemandRes - exact port of C encode_iso1CurrentDemandResType /// Grammar states 317-329 from C implementation /// private static void EncodeCurrentDemandResType(BitOutputStreamExact stream, CurrentDemandResType res) { // Grammar state 317: ResponseCode (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(ResponseCode) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(5, (int)res.ResponseCode); // 5-bit ResponseCode stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // Grammar state 318: DC_EVSEStatus (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(DC_EVSEStatus) EncodeDC_EVSEStatusType(stream, res.DC_EVSEStatus); // Grammar state 319: EVSEPresentVoltage (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVSEPresentVoltage) EncodePhysicalValueType(stream, res.EVSEPresentVoltage); // Grammar state 320: EVSEPresentCurrent (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVSEPresentCurrent) EncodePhysicalValueType(stream, res.EVSEPresentCurrent); // Grammar state 321: EVSECurrentLimitAchieved (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVSECurrentLimitAchieved) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BOOLEAN] stream.WriteNBitUnsignedInteger(1, res.EVSECurrentLimitAchieved ? 1 : 0); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // Grammar state 322: EVSEVoltageLimitAchieved (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVSEVoltageLimitAchieved) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BOOLEAN] stream.WriteNBitUnsignedInteger(1, res.EVSEVoltageLimitAchieved ? 1 : 0); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // Grammar state 323: EVSEPowerLimitAchieved (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVSEPowerLimitAchieved) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BOOLEAN] stream.WriteNBitUnsignedInteger(1, res.EVSEPowerLimitAchieved ? 1 : 0); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // Grammar state 324+: Handle optional elements and mandatory EVSEID EncodeCurrentDemandResOptionalElements(stream, res); // End CurrentDemandRes stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } /// /// Encode optional elements and mandatory EVSEID for CurrentDemandRes /// Based on C grammar states 324-329 /// private static void EncodeCurrentDemandResOptionalElements(BitOutputStreamExact stream, CurrentDemandResType res) { // Handle optional limits first, then mandatory EVSEID bool hasOptionalLimits = res.EVSEMaximumVoltageLimit_isUsed || res.EVSEMaximumCurrentLimit_isUsed || res.EVSEMaximumPowerLimit_isUsed; if (hasOptionalLimits) { // Encode optional limits if (res.EVSEMaximumVoltageLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 0); // Choice 0: EVSEMaximumVoltageLimit EncodePhysicalValueType(stream, res.EVSEMaximumVoltageLimit); } if (res.EVSEMaximumCurrentLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 1); // Choice 1: EVSEMaximumCurrentLimit EncodePhysicalValueType(stream, res.EVSEMaximumCurrentLimit); } if (res.EVSEMaximumPowerLimit_isUsed) { stream.WriteNBitUnsignedInteger(3, 2); // Choice 2: EVSEMaximumPowerLimit EncodePhysicalValueType(stream, res.EVSEMaximumPowerLimit); } } // EVSEID is always present (choice 3) stream.WriteNBitUnsignedInteger(3, 3); // Choice 3: EVSEID EncodeString(stream, res.EVSEID); // SAScheduleTupleID (8-bit, value-1) stream.WriteNBitUnsignedInteger(8, (int)(res.SAScheduleTupleID - 1)); // Handle final optional elements (MeterInfo, ReceiptRequired) if (res.MeterInfo_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); // Choice 0: MeterInfo EncodeMeterInfo(stream, res.MeterInfo); } if (res.ReceiptRequired_isUsed) { stream.WriteNBitUnsignedInteger(2, 1); // Choice 1: ReceiptRequired stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[BOOLEAN] stream.WriteNBitUnsignedInteger(1, res.ReceiptRequired ? 1 : 0); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } else { stream.WriteNBitUnsignedInteger(2, 2); // Choice 2: END_ELEMENT (skip optional elements) } } /// /// Encode DC_EVSEStatus - exact implementation matching C version /// private static void EncodeDC_EVSEStatusType(BitOutputStreamExact stream, DC_EVSEStatusType status) { // NotificationMaxDelay (16-bit unsigned) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(NotificationMaxDelay) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[UNSIGNED_INTEGER] stream.WriteNBitUnsignedInteger(16, status.NotificationMaxDelay); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // EVSENotification (2-bit enumeration) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVSENotification) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(2, (int)status.EVSENotification); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // Optional EVSEIsolationStatus if (status.EVSEIsolationStatus_isUsed) { stream.WriteNBitUnsignedInteger(2, 0); // Choice 0: EVSEIsolationStatus stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(3, (int)status.EVSEIsolationStatus); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // EVSEStatusCode after optional element stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(EVSEStatusCode) stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(4, (int)status.EVSEStatusCode); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } else { stream.WriteNBitUnsignedInteger(2, 1); // Choice 1: Skip to EVSEStatusCode stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[ENUMERATION] stream.WriteNBitUnsignedInteger(4, (int)status.EVSEStatusCode); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } // End DC_EVSEStatus stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } /// /// Encode string with length encoding - exact match to C encode_iso1String /// private static void EncodeString(BitOutputStreamExact stream, string str) { if (string.IsNullOrEmpty(str)) { // Empty string - just encode length 0 stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS choice stream.WriteUnsignedInteger(0); // Length 0 return; } // Convert string to UTF-8 bytes byte[] stringBytes = Encoding.UTF8.GetBytes(str); // Encode as string characters stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS choice stream.WriteUnsignedInteger((uint)stringBytes.Length); // String length // Write string bytes WriteBytes(stream, stringBytes); } /// /// Encode MeterInfo - exact match to C encode_iso1MeterInfoType /// private static void EncodeMeterInfo(BitOutputStreamExact stream, MeterInfoType meterInfo) { // MeterID (mandatory) stream.WriteNBitUnsignedInteger(1, 0); // START_ELEMENT(MeterID) EncodeString(stream, meterInfo.MeterID); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT // MeterReading (optional) if (meterInfo.MeterReading != 0) { stream.WriteNBitUnsignedInteger(4, 0); // Choice 0: MeterReading stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[UNSIGNED_INTEGER] stream.WriteUnsignedInteger((uint)meterInfo.MeterReading); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } // SigMeterReading (optional) if (meterInfo.SigMeterReading != 0) { stream.WriteNBitUnsignedInteger(4, 1); // Choice 1: SigMeterReading EncodeInteger8(stream, meterInfo.SigMeterReading); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } // MeterStatus (optional) if (!string.IsNullOrEmpty(meterInfo.MeterStatus)) { stream.WriteNBitUnsignedInteger(4, 2); // Choice 2: MeterStatus EncodeString(stream, meterInfo.MeterStatus); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } // TMeter (optional) if (meterInfo.TMeter != 0) { stream.WriteNBitUnsignedInteger(4, 3); // Choice 3: TMeter stream.WriteNBitUnsignedInteger(1, 0); // CHARACTERS[INTEGER] EncodeInteger64(stream, meterInfo.TMeter); stream.WriteNBitUnsignedInteger(1, 0); // END_ELEMENT } // End MeterInfo stream.WriteNBitUnsignedInteger(4, 4); // Choice 4: END_ELEMENT } /// /// Encode 64-bit signed integer /// private static void EncodeInteger64(BitOutputStreamExact stream, long 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 } } } }