diff --git a/csharp/dotnet/EXI/EXIEncoderExact.cs b/csharp/dotnet/EXI/EXIEncoderExact.cs
index b5b077c..a535135 100644
--- a/csharp/dotnet/EXI/EXIEncoderExact.cs
+++ b/csharp/dotnet/EXI/EXIEncoderExact.cs
@@ -92,9 +92,15 @@ namespace V2GDecoderNet.EXI
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");
+ throw new Exception("Unsupported message type for encoding. Currently supported: CurrentDemandReq, CurrentDemandRes");
}
// End Body element - grammar state 3
@@ -399,23 +405,241 @@ namespace V2GDecoderNet.EXI
return bytes;
}
- public static byte[] EncodeCurrentDemandRes(CurrentDemandResType response)
+ ///
+ /// Encode CurrentDemandRes - exact port of C encode_iso1CurrentDemandResType
+ /// Grammar states 317-329 from C implementation
+ ///
+ private static void EncodeCurrentDemandResType(BitOutputStreamExact stream, CurrentDemandResType res)
{
- try
+ // 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)
{
- 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.ToArray();
+ // 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);
+ }
}
- catch (Exception ex)
+
+ // 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)
{
- throw new Exception($"Failed to encode CurrentDemandRes: {ex.Message}", ex);
+ 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
}
}
}
diff --git a/csharp/dotnet/V2G/V2GMessageProcessor.cs b/csharp/dotnet/V2G/V2GMessageProcessor.cs
index d45e5af..407e713 100644
--- a/csharp/dotnet/V2G/V2GMessageProcessor.cs
+++ b/csharp/dotnet/V2G/V2GMessageProcessor.cs
@@ -446,7 +446,16 @@ namespace V2GDecoderNet
}
else
{
- throw new Exception("Unsupported message type for encoding - only CurrentDemandReq supported");
+ var currentDemandRes = bodyElement.Element(ns3 + "CurrentDemandRes");
+ if (currentDemandRes != null)
+ {
+ v2gMessage.Body.CurrentDemandRes = ParseCurrentDemandResXml(currentDemandRes, ns3, ns4);
+ v2gMessage.Body.CurrentDemandRes_isUsed = true;
+ }
+ else
+ {
+ throw new Exception("Unsupported message type for encoding - supported: CurrentDemandReq, CurrentDemandRes");
+ }
}
// Encode to EXI
@@ -546,6 +555,130 @@ namespace V2GDecoderNet
return req;
}
+
+ private static CurrentDemandResType ParseCurrentDemandResXml(XElement resElement, XNamespace ns3, XNamespace ns4)
+ {
+ var res = new CurrentDemandResType();
+
+ // Parse ResponseCode
+ var responseCode = resElement.Element(ns3 + "ResponseCode");
+ if (responseCode != null)
+ {
+ res.ResponseCode = (ResponseCodeType)Enum.Parse(typeof(ResponseCodeType), responseCode.Value);
+ }
+
+ // Parse DC_EVSEStatus
+ var dcEvseStatus = resElement.Element(ns3 + "DC_EVSEStatus");
+ if (dcEvseStatus != null)
+ {
+ res.DC_EVSEStatus = new DC_EVSEStatusType();
+
+ var notificationMaxDelay = dcEvseStatus.Element(ns4 + "NotificationMaxDelay");
+ if (notificationMaxDelay != null)
+ res.DC_EVSEStatus.NotificationMaxDelay = ushort.Parse(notificationMaxDelay.Value);
+
+ var evseNotification = dcEvseStatus.Element(ns4 + "EVSENotification");
+ if (evseNotification != null)
+ res.DC_EVSEStatus.EVSENotification = (EVSENotificationType)int.Parse(evseNotification.Value);
+
+ var evseIsolationStatus = dcEvseStatus.Element(ns4 + "EVSEIsolationStatus");
+ if (evseIsolationStatus != null)
+ {
+ res.DC_EVSEStatus.EVSEIsolationStatus = (IsolationLevelType)int.Parse(evseIsolationStatus.Value);
+ res.DC_EVSEStatus.EVSEIsolationStatus_isUsed = true;
+ }
+
+ var evseStatusCode = dcEvseStatus.Element(ns4 + "EVSEStatusCode");
+ if (evseStatusCode != null)
+ res.DC_EVSEStatus.EVSEStatusCode = (DC_EVSEStatusCodeType)int.Parse(evseStatusCode.Value);
+ }
+
+ // Parse EVSEPresentVoltage
+ var evsePresentVoltage = resElement.Element(ns3 + "EVSEPresentVoltage");
+ if (evsePresentVoltage != null)
+ {
+ res.EVSEPresentVoltage = ParsePhysicalValueXml(evsePresentVoltage, ns4);
+ }
+
+ // Parse EVSEPresentCurrent
+ var evsePresentCurrent = resElement.Element(ns3 + "EVSEPresentCurrent");
+ if (evsePresentCurrent != null)
+ {
+ res.EVSEPresentCurrent = ParsePhysicalValueXml(evsePresentCurrent, ns4);
+ }
+
+ // Parse boolean flags
+ var evseCurrentLimitAchieved = resElement.Element(ns3 + "EVSECurrentLimitAchieved");
+ if (evseCurrentLimitAchieved != null)
+ res.EVSECurrentLimitAchieved = bool.Parse(evseCurrentLimitAchieved.Value);
+
+ var evseVoltageLimitAchieved = resElement.Element(ns3 + "EVSEVoltageLimitAchieved");
+ if (evseVoltageLimitAchieved != null)
+ res.EVSEVoltageLimitAchieved = bool.Parse(evseVoltageLimitAchieved.Value);
+
+ var evsePowerLimitAchieved = resElement.Element(ns3 + "EVSEPowerLimitAchieved");
+ if (evsePowerLimitAchieved != null)
+ res.EVSEPowerLimitAchieved = bool.Parse(evsePowerLimitAchieved.Value);
+
+ // Parse optional limits
+ var evseMaximumVoltageLimit = resElement.Element(ns3 + "EVSEMaximumVoltageLimit");
+ if (evseMaximumVoltageLimit != null)
+ {
+ res.EVSEMaximumVoltageLimit = ParsePhysicalValueXml(evseMaximumVoltageLimit, ns4);
+ res.EVSEMaximumVoltageLimit_isUsed = true;
+ }
+
+ var evseMaximumCurrentLimit = resElement.Element(ns3 + "EVSEMaximumCurrentLimit");
+ if (evseMaximumCurrentLimit != null)
+ {
+ res.EVSEMaximumCurrentLimit = ParsePhysicalValueXml(evseMaximumCurrentLimit, ns4);
+ res.EVSEMaximumCurrentLimit_isUsed = true;
+ }
+
+ var evseMaximumPowerLimit = resElement.Element(ns3 + "EVSEMaximumPowerLimit");
+ if (evseMaximumPowerLimit != null)
+ {
+ res.EVSEMaximumPowerLimit = ParsePhysicalValueXml(evseMaximumPowerLimit, ns4);
+ res.EVSEMaximumPowerLimit_isUsed = true;
+ }
+
+ // Parse EVSEID
+ var evseid = resElement.Element(ns3 + "EVSEID");
+ if (evseid != null)
+ res.EVSEID = evseid.Value;
+
+ // Parse SAScheduleTupleID
+ var saScheduleTupleId = resElement.Element(ns3 + "SAScheduleTupleID");
+ if (saScheduleTupleId != null)
+ res.SAScheduleTupleID = byte.Parse(saScheduleTupleId.Value);
+
+ // Parse MeterInfo (optional)
+ var meterInfo = resElement.Element(ns3 + "MeterInfo");
+ if (meterInfo != null)
+ {
+ res.MeterInfo = new MeterInfoType();
+
+ var meterID = meterInfo.Element(ns4 + "MeterID");
+ if (meterID != null)
+ res.MeterInfo.MeterID = meterID.Value;
+
+ var meterReading = meterInfo.Element(ns4 + "MeterReading");
+ if (meterReading != null)
+ res.MeterInfo.MeterReading = ulong.Parse(meterReading.Value);
+
+ res.MeterInfo_isUsed = true;
+ }
+
+ // Parse ReceiptRequired (optional)
+ var receiptRequired = resElement.Element(ns3 + "ReceiptRequired");
+ if (receiptRequired != null)
+ {
+ res.ReceiptRequired = bool.Parse(receiptRequired.Value);
+ res.ReceiptRequired_isUsed = true;
+ }
+
+ return res;
+ }
private static PhysicalValueType ParsePhysicalValueXml(XElement element, XNamespace ns4)
{