using System; using System.Text; using System.Xml.Linq; using System.Globalization; using V2GDecoderNet.EXI; using V2GDecoderNet.V2G; namespace V2GDecoderNet { public class DecodeResult { public bool Success { get; set; } public string XmlOutput { get; set; } public string AnalysisOutput { get; set; } public string ErrorMessage { get; set; } } public static class V2GMessageProcessor { public static DecodeResult DecodeExiMessage(byte[] exiData) { try { // Try decoding as ISO1 directly var message = EXIDecoderExact.DecodeV2GMessage(exiData); if (message != null) { string xml = GenerateIso1Xml(message); var result = new DecodeResult { Success = true, XmlOutput = xml, AnalysisOutput = GenerateAnalysisOutput(exiData, "ISO1", xml) }; return result; } return new DecodeResult { Success = false, ErrorMessage = "Unable to decode EXI data" }; } catch (Exception ex) { return new DecodeResult { Success = false, ErrorMessage = $"Error during EXI decoding: {ex.Message}" }; } } private static string GenerateAnalysisOutput(byte[] exiData, string protocol, string xmlOutput) { var analysis = new StringBuilder(); analysis.AppendLine($"Trying {protocol} decoder..."); analysis.AppendLine($"Successfully decoded as {protocol}"); analysis.AppendLine(); analysis.AppendLine("=== ISO 15118-2 V2G Message Analysis ==="); analysis.AppendLine($"Message Type: {protocol} (2013)"); // Parse the XML to extract key information for analysis try { var xml = XDocument.Parse(xmlOutput); var ns1 = XNamespace.Get("urn:iso:15118:2:2013:MsgDef"); var ns2 = XNamespace.Get("urn:iso:15118:2:2013:MsgHeader"); var ns3 = XNamespace.Get("urn:iso:15118:2:2013:MsgBody"); var ns4 = XNamespace.Get("urn:iso:15118:2:2013:MsgDataTypes"); var message = xml.Root; var header = message.Element(ns1 + "Header"); var body = message.Element(ns1 + "Body"); analysis.AppendLine("V2G_Message_isUsed: true"); analysis.AppendLine(); analysis.AppendLine("--- Header ---"); if (header != null) { var sessionId = header.Element(ns2 + "SessionID")?.Value; if (!string.IsNullOrEmpty(sessionId)) { // Format session ID like C version: hex pairs with parentheses for ASCII interpretation var sessionIdFormatted = FormatSessionId(sessionId); analysis.AppendLine($"SessionID: {sessionIdFormatted}"); } } analysis.AppendLine(); analysis.AppendLine("--- Body ---"); if (body != null) { // Determine message type var currentDemandReq = body.Element(ns3 + "CurrentDemandReq"); if (currentDemandReq != null) { analysis.AppendLine("Message Type: CurrentDemandReq"); analysis.AppendLine(); // Parse CurrentDemandReq details analysis.Append(ParseCurrentDemandReqAnalysis(currentDemandReq, ns3, ns4)); } // Phase 2: Session & Service Management Messages var sessionSetupReq = body.Element(ns3 + "SessionSetupReq"); if (sessionSetupReq != null) { analysis.AppendLine("Message Type: SessionSetupReq"); analysis.AppendLine(); analysis.Append(ParseSessionSetupReqAnalysis(sessionSetupReq, ns3, ns4)); } var sessionSetupRes = body.Element(ns3 + "SessionSetupRes"); if (sessionSetupRes != null) { analysis.AppendLine("Message Type: SessionSetupRes"); analysis.AppendLine(); analysis.Append(ParseSessionSetupResAnalysis(sessionSetupRes, ns3, ns4)); } var serviceDiscoveryReq = body.Element(ns3 + "ServiceDiscoveryReq"); if (serviceDiscoveryReq != null) { analysis.AppendLine("Message Type: ServiceDiscoveryReq"); analysis.AppendLine(); analysis.Append(ParseServiceDiscoveryReqAnalysis(serviceDiscoveryReq, ns3, ns4)); } var serviceDiscoveryRes = body.Element(ns3 + "ServiceDiscoveryRes"); if (serviceDiscoveryRes != null) { analysis.AppendLine("Message Type: ServiceDiscoveryRes"); analysis.AppendLine(); analysis.Append(ParseServiceDiscoveryResAnalysis(serviceDiscoveryRes, ns3, ns4)); } var authorizationReq = body.Element(ns3 + "AuthorizationReq"); if (authorizationReq != null) { analysis.AppendLine("Message Type: AuthorizationReq"); analysis.AppendLine(); analysis.Append(ParseAuthorizationReqAnalysis(authorizationReq, ns3, ns4)); } var authorizationRes = body.Element(ns3 + "AuthorizationRes"); if (authorizationRes != null) { analysis.AppendLine("Message Type: AuthorizationRes"); analysis.AppendLine(); analysis.Append(ParseAuthorizationResAnalysis(authorizationRes, ns3, ns4)); } var chargeParameterDiscoveryReq = body.Element(ns3 + "ChargeParameterDiscoveryReq"); if (chargeParameterDiscoveryReq != null) { analysis.AppendLine("Message Type: ChargeParameterDiscoveryReq"); analysis.AppendLine(); analysis.Append(ParseChargeParameterDiscoveryReqAnalysis(chargeParameterDiscoveryReq, ns3, ns4)); } var chargeParameterDiscoveryRes = body.Element(ns3 + "ChargeParameterDiscoveryRes"); if (chargeParameterDiscoveryRes != null) { analysis.AppendLine("Message Type: ChargeParameterDiscoveryRes"); analysis.AppendLine(); analysis.Append(ParseChargeParameterDiscoveryResAnalysis(chargeParameterDiscoveryRes, ns3, ns4)); } // Phase 3: Extended Features - Message detection var paymentServiceSelectionReq = body.Element(ns3 + "PaymentServiceSelectionReq"); if (paymentServiceSelectionReq != null) { analysis.AppendLine("Message Type: PaymentServiceSelectionReq"); analysis.AppendLine(); analysis.Append(ParsePaymentServiceSelectionReqAnalysis(paymentServiceSelectionReq, ns3, ns4)); } var paymentServiceSelectionRes = body.Element(ns3 + "PaymentServiceSelectionRes"); if (paymentServiceSelectionRes != null) { analysis.AppendLine("Message Type: PaymentServiceSelectionRes"); analysis.AppendLine(); analysis.Append(ParsePaymentServiceSelectionResAnalysis(paymentServiceSelectionRes, ns3, ns4)); } var chargingStatusReq = body.Element(ns3 + "ChargingStatusReq"); if (chargingStatusReq != null) { analysis.AppendLine("Message Type: ChargingStatusReq"); analysis.AppendLine(); analysis.Append(ParseChargingStatusReqAnalysis(chargingStatusReq, ns3, ns4)); } var chargingStatusRes = body.Element(ns3 + "ChargingStatusRes"); if (chargingStatusRes != null) { analysis.AppendLine("Message Type: ChargingStatusRes"); analysis.AppendLine(); analysis.Append(ParseChargingStatusResAnalysis(chargingStatusRes, ns3, ns4)); } var sessionStopReq = body.Element(ns3 + "SessionStopReq"); if (sessionStopReq != null) { analysis.AppendLine("Message Type: SessionStopReq"); analysis.AppendLine(); analysis.Append(ParseSessionStopReqAnalysis(sessionStopReq, ns3, ns4)); } var sessionStopRes = body.Element(ns3 + "SessionStopRes"); if (sessionStopRes != null) { analysis.AppendLine("Message Type: SessionStopRes"); analysis.AppendLine(); analysis.Append(ParseSessionStopResAnalysis(sessionStopRes, ns3, ns4)); } // Add other message types as needed } // Add structure debug information analysis.AppendLine(); analysis.Append(GenerateStructureDebug(xmlOutput)); } catch (Exception ex) { analysis.AppendLine($"Error parsing XML for analysis: {ex.Message}"); } return analysis.ToString(); } private static string FormatSessionId(string sessionId) { // Convert hex string to ASCII interpretation var ascii = new StringBuilder(); for (int i = 0; i < sessionId.Length; i += 2) { if (i + 1 < sessionId.Length) { var hex = sessionId.Substring(i, 2); var value = Convert.ToInt32(hex, 16); if (value >= 32 && value <= 126) // Printable ASCII { ascii.Append((char)value); } else { ascii.Append('.'); } } } return $"{sessionId} ({ascii})"; } private static string ParseCurrentDemandReqAnalysis(XElement currentDemandReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); // DC_EVStatus var dcEvStatus = currentDemandReq.Element(ns3 + "DC_EVStatus"); if (dcEvStatus != null) { analysis.AppendLine("DC_EVStatus:"); var evReady = dcEvStatus.Element(ns4 + "EVReady")?.Value; var evErrorCode = dcEvStatus.Element(ns4 + "EVErrorCode")?.Value; var evRessSoc = dcEvStatus.Element(ns4 + "EVRESSSOC")?.Value; analysis.AppendLine($" EVReady: {evReady?.ToLower() ?? "false"}"); analysis.AppendLine($" EVErrorCode: {evErrorCode ?? "0"}"); analysis.AppendLine($" EVRESSSOC: {evRessSoc ?? "0"}%"); analysis.AppendLine(); } // Parse physical values analysis.Append(ParsePhysicalValue(currentDemandReq, ns3, ns4, "EVTargetCurrent")); analysis.Append(ParsePhysicalValue(currentDemandReq, ns3, ns4, "EVTargetVoltage")); analysis.Append(ParsePhysicalValue(currentDemandReq, ns3, ns4, "EVMaximumVoltageLimit")); analysis.Append(ParsePhysicalValue(currentDemandReq, ns3, ns4, "EVMaximumCurrentLimit")); analysis.Append(ParsePhysicalValue(currentDemandReq, ns3, ns4, "EVMaximumPowerLimit")); // Boolean values var bulkChargingComplete = currentDemandReq.Element(ns3 + "BulkChargingComplete")?.Value; var chargingComplete = currentDemandReq.Element(ns3 + "ChargingComplete")?.Value; analysis.AppendLine($"BulkChargingComplete: {bulkChargingComplete?.ToLower() ?? "false"}"); analysis.AppendLine($"ChargingComplete: {chargingComplete?.ToLower() ?? "false"}"); analysis.AppendLine(); // Time values analysis.Append(ParsePhysicalValue(currentDemandReq, ns3, ns4, "RemainingTimeToFullSoC")); analysis.Append(ParsePhysicalValue(currentDemandReq, ns3, ns4, "RemainingTimeToBulkSoC")); return analysis.ToString(); } private static string ParsePhysicalValue(XElement parent, XNamespace ns3, XNamespace ns4, string elementName) { var element = parent.Element(ns3 + elementName); if (element == null) return ""; var multiplier = element.Element(ns4 + "Multiplier")?.Value ?? "0"; var unit = element.Element(ns4 + "Unit")?.Value ?? "0"; var value = element.Element(ns4 + "Value")?.Value ?? "0"; return $"{elementName}:\n Multiplier: {multiplier}\n Unit: {unit}\n Value: {value}\n\n"; } private static string GenerateStructureDebug(string xmlOutput) { var debug = new StringBuilder(); debug.AppendLine("=== Original EXI Structure Debug ==="); try { var xml = XDocument.Parse(xmlOutput); var ns1 = XNamespace.Get("urn:iso:15118:2:2013:MsgDef"); var ns2 = XNamespace.Get("urn:iso:15118:2:2013:MsgHeader"); var ns3 = XNamespace.Get("urn:iso:15118:2:2013:MsgBody"); var ns4 = XNamespace.Get("urn:iso:15118:2:2013:MsgDataTypes"); var message = xml.Root; debug.AppendLine("V2G_Message_isUsed: true"); var header = message.Element(ns1 + "Header"); if (header != null) { var sessionId = header.Element(ns2 + "SessionID")?.Value; if (!string.IsNullOrEmpty(sessionId)) { debug.AppendLine($"SessionID length: {sessionId.Length / 2}"); } } var body = message.Element(ns1 + "Body"); var currentDemandReq = body?.Element(ns3 + "CurrentDemandReq"); if (currentDemandReq != null) { debug.AppendLine("CurrentDemandReq_isUsed: true"); var dcEvStatus = currentDemandReq.Element(ns3 + "DC_EVStatus"); if (dcEvStatus != null) { debug.AppendLine($"EVReady: {dcEvStatus.Element(ns4 + "EVReady")?.Value?.ToLower() ?? "false"}"); debug.AppendLine($"EVErrorCode: {dcEvStatus.Element(ns4 + "EVErrorCode")?.Value ?? "0"}"); debug.AppendLine($"EVRESSSOC: {dcEvStatus.Element(ns4 + "EVRESSSOC")?.Value ?? "0"}"); } var evTargetCurrent = currentDemandReq.Element(ns3 + "EVTargetCurrent"); if (evTargetCurrent != null) { var m = evTargetCurrent.Element(ns4 + "Multiplier")?.Value ?? "0"; var u = evTargetCurrent.Element(ns4 + "Unit")?.Value ?? "0"; var v = evTargetCurrent.Element(ns4 + "Value")?.Value ?? "0"; debug.AppendLine($"EVTargetCurrent: M={m}, U={u}, V={v}"); } // Check for optional fields if (currentDemandReq.Element(ns3 + "EVMaximumVoltageLimit") != null) debug.AppendLine("EVMaximumVoltageLimit_isUsed: true"); if (currentDemandReq.Element(ns3 + "EVMaximumCurrentLimit") != null) debug.AppendLine("EVMaximumCurrentLimit_isUsed: true"); if (currentDemandReq.Element(ns3 + "EVMaximumPowerLimit") != null) debug.AppendLine("EVMaximumPowerLimit_isUsed: true"); if (currentDemandReq.Element(ns3 + "BulkChargingComplete") != null) debug.AppendLine("BulkChargingComplete_isUsed: true"); if (currentDemandReq.Element(ns3 + "RemainingTimeToFullSoC") != null) debug.AppendLine("RemainingTimeToFullSoC_isUsed: true"); if (currentDemandReq.Element(ns3 + "RemainingTimeToBulkSoC") != null) debug.AppendLine("RemainingTimeToBulkSoC_isUsed: true"); } debug.AppendLine("Structure dump saved to struct_exi.txt"); } catch (Exception ex) { debug.AppendLine($"Error generating structure debug: {ex.Message}"); } return debug.ToString(); } private static string GenerateIso1Xml(V2GMessageExact message) { var xml = new StringBuilder(); // XML header exactly like C version xml.AppendLine(""); xml.Append(""); // Header if (!string.IsNullOrEmpty(message.SessionID)) { xml.AppendLine("" + message.SessionID + ""); } // Body xml.Append(""); if (message.Body != null && message.Body.CurrentDemandReq_isUsed && message.Body.CurrentDemandReq != null) { xml.Append(WriteCurrentDemandReqXml(message.Body.CurrentDemandReq)); } else if (message.Body != null && message.Body.CurrentDemandRes_isUsed && message.Body.CurrentDemandRes != null) { xml.Append(WriteCurrentDemandResXml(message.Body.CurrentDemandRes)); } xml.AppendLine(""); xml.AppendLine(""); return xml.ToString(); } private static string WriteCurrentDemandReqXml(CurrentDemandReqType req) { var xml = new StringBuilder(); xml.Append(""); // DC_EVStatus (mandatory) if (req.DC_EVStatus != null) { xml.Append(""); xml.Append($"{req.DC_EVStatus.EVReady.ToString().ToLower()}"); xml.Append($"{req.DC_EVStatus.EVErrorCode}"); xml.Append($"{req.DC_EVStatus.EVRESSSOC}"); xml.Append(""); } // EVTargetCurrent (mandatory) if (req.EVTargetCurrent != null) { xml.Append(""); xml.Append($"{req.EVTargetCurrent.Multiplier}"); xml.Append($"{(int)req.EVTargetCurrent.Unit}"); xml.Append($"{req.EVTargetCurrent.Value}"); xml.Append(""); } // EVMaximumVoltageLimit if (req.EVMaximumVoltageLimit_isUsed && req.EVMaximumVoltageLimit != null) { xml.Append(""); xml.Append($"{req.EVMaximumVoltageLimit.Multiplier}"); xml.Append($"{(int)req.EVMaximumVoltageLimit.Unit}"); xml.Append($"{req.EVMaximumVoltageLimit.Value}"); xml.Append(""); } // EVMaximumCurrentLimit if (req.EVMaximumCurrentLimit_isUsed && req.EVMaximumCurrentLimit != null) { xml.Append(""); xml.Append($"{req.EVMaximumCurrentLimit.Multiplier}"); xml.Append($"{(int)req.EVMaximumCurrentLimit.Unit}"); xml.Append($"{req.EVMaximumCurrentLimit.Value}"); xml.Append(""); } // EVMaximumPowerLimit if (req.EVMaximumPowerLimit_isUsed && req.EVMaximumPowerLimit != null) { xml.Append(""); xml.Append($"{req.EVMaximumPowerLimit.Multiplier}"); xml.Append($"{(int)req.EVMaximumPowerLimit.Unit}"); xml.Append($"{req.EVMaximumPowerLimit.Value}"); xml.Append(""); } // BulkChargingComplete if (req.BulkChargingComplete_isUsed) { xml.Append($"{req.BulkChargingComplete.ToString().ToLower()}"); } // ChargingComplete (mandatory in VC2022) xml.Append($"{req.ChargingComplete.ToString().ToLower()}"); // RemainingTimeToFullSoC if (req.RemainingTimeToFullSoC_isUsed && req.RemainingTimeToFullSoC != null) { xml.Append(""); xml.Append($"{req.RemainingTimeToFullSoC.Multiplier}"); xml.Append($"{(int)req.RemainingTimeToFullSoC.Unit}"); xml.Append($"{req.RemainingTimeToFullSoC.Value}"); xml.Append(""); } // RemainingTimeToBulkSoC if (req.RemainingTimeToBulkSoC_isUsed && req.RemainingTimeToBulkSoC != null) { xml.Append(""); xml.Append($"{req.RemainingTimeToBulkSoC.Multiplier}"); xml.Append($"{(int)req.RemainingTimeToBulkSoC.Unit}"); xml.Append($"{req.RemainingTimeToBulkSoC.Value}"); xml.Append(""); } // EVTargetVoltage (mandatory - appears at the end in C version) if (req.EVTargetVoltage != null) { xml.Append(""); xml.Append($"{req.EVTargetVoltage.Multiplier}"); xml.Append($"{(int)req.EVTargetVoltage.Unit}"); xml.Append($"{req.EVTargetVoltage.Value}"); xml.Append(""); } xml.Append(""); return xml.ToString(); } private static string WriteCurrentDemandResXml(CurrentDemandResType res) { var xml = new StringBuilder(); xml.Append(""); // ResponseCode (mandatory) xml.Append($"{(int)res.ResponseCode}"); // DC_EVSEStatus (mandatory) if (res.DC_EVSEStatus != null) { xml.Append(""); xml.Append($"{(int)res.DC_EVSEStatus.EVSEIsolationStatus}"); xml.Append($"{(int)res.DC_EVSEStatus.EVSEStatusCode}"); xml.Append(""); } // EVSEPresentVoltage (mandatory) if (res.EVSEPresentVoltage != null) { xml.Append(""); xml.Append($"{res.EVSEPresentVoltage.Multiplier}"); xml.Append($"{(int)res.EVSEPresentVoltage.Unit}"); xml.Append($"{res.EVSEPresentVoltage.Value}"); xml.Append(""); } // EVSEPresentCurrent (mandatory) if (res.EVSEPresentCurrent != null) { xml.Append(""); xml.Append($"{res.EVSEPresentCurrent.Multiplier}"); xml.Append($"{(int)res.EVSEPresentCurrent.Unit}"); xml.Append($"{res.EVSEPresentCurrent.Value}"); xml.Append(""); } // Limit flags (mandatory) xml.Append($"{res.EVSECurrentLimitAchieved.ToString().ToLower()}"); xml.Append($"{res.EVSEVoltageLimitAchieved.ToString().ToLower()}"); xml.Append($"{res.EVSEPowerLimitAchieved.ToString().ToLower()}"); // EVSEID (mandatory) xml.Append($"{res.EVSEID}"); // SAScheduleTupleID (mandatory) xml.Append($"{res.SAScheduleTupleID}"); xml.Append(""); return xml.ToString(); } public static byte[] EncodeXmlToExi(string xmlContent) { try { // Console.Error.WriteLine("πŸ” [EncodeXmlToExi] Starting XML to EXI encoding..."); // Parse XML to determine message type and encode accordingly var xml = XDocument.Parse(xmlContent); var ns1 = XNamespace.Get("urn:iso:15118:2:2013:MsgDef"); var ns2 = XNamespace.Get("urn:iso:15118:2:2013:MsgHeader"); var ns3 = XNamespace.Get("urn:iso:15118:2:2013:MsgBody"); var ns4 = XNamespace.Get("urn:iso:15118:2:2013:MsgDataTypes"); var messageElement = xml.Root; var headerElement = messageElement?.Element(ns1 + "Header"); var bodyElement = messageElement?.Element(ns1 + "Body"); if (bodyElement == null) throw new Exception("No Body element found in XML"); // Parse message structure var v2gMessage = new V2GMessageExact(); // Parse Header if (headerElement != null) { var sessionIdElement = headerElement.Element(ns2 + "SessionID"); if (sessionIdElement != null) { v2gMessage.SessionID = sessionIdElement.Value; // Console.Error.WriteLine($"πŸ” [Header] SessionID: {v2gMessage.SessionID}"); } } // Default SessionID if not provided (matching VC2022 test pattern) if (string.IsNullOrEmpty(v2gMessage.SessionID)) { v2gMessage.SessionID = "4142423030303831"; // "ABB00081" in hex // Console.Error.WriteLine($"πŸ” [Header] Using default SessionID: {v2gMessage.SessionID}"); } // Parse Body v2gMessage.Body = new BodyType(); var currentDemandReq = bodyElement.Element(ns3 + "CurrentDemandReq"); if (currentDemandReq != null) { // Console.Error.WriteLine("πŸ” [Body] Found CurrentDemandReq message"); v2gMessage.Body.CurrentDemandReq = ParseCurrentDemandReqXml(currentDemandReq, ns3, ns4); v2gMessage.Body.CurrentDemandReq_isUsed = true; } else { var currentDemandRes = bodyElement.Element(ns3 + "CurrentDemandRes"); if (currentDemandRes != null) { // Console.Error.WriteLine("πŸ” [Body] Found CurrentDemandRes message"); v2gMessage.Body.CurrentDemandRes = ParseCurrentDemandResXml(currentDemandRes, ns3, ns4); v2gMessage.Body.CurrentDemandRes_isUsed = true; } else { throw new Exception("Unsupported message type for encoding - supported: CurrentDemandReq, CurrentDemandRes"); } } // Create Iso1EXIDocument and encode to EXI using exact encoder var iso1Doc = EXIEncoderExact.CreateIso1DocumentFromV2GMessage(v2gMessage); return EXIEncoderExact.EncodeIso1Document(iso1Doc); } catch (Exception ex) { throw new Exception($"Failed to encode XML to EXI: {ex.Message}", ex); } } private static CurrentDemandReqType ParseCurrentDemandReqXml(XElement reqElement, XNamespace ns3, XNamespace ns4) { var req = new CurrentDemandReqType(); // Parse DC_EVStatus var dcEvStatus = reqElement.Element(ns3 + "DC_EVStatus"); if (dcEvStatus != null) { req.DC_EVStatus = new DC_EVStatusType(); var evReady = dcEvStatus.Element(ns4 + "EVReady"); if (evReady != null) req.DC_EVStatus.EVReady = bool.Parse(evReady.Value); var evErrorCode = dcEvStatus.Element(ns4 + "EVErrorCode"); if (evErrorCode != null) req.DC_EVStatus.EVErrorCode = int.Parse(evErrorCode.Value); var evRessSoc = dcEvStatus.Element(ns4 + "EVRESSSOC"); if (evRessSoc != null) req.DC_EVStatus.EVRESSSOC = byte.Parse(evRessSoc.Value); } // Parse EVTargetCurrent var evTargetCurrent = reqElement.Element(ns3 + "EVTargetCurrent"); if (evTargetCurrent != null) { req.EVTargetCurrent = ParsePhysicalValueXml(evTargetCurrent, ns4); } // Parse optional elements var evMaxVoltageLimit = reqElement.Element(ns3 + "EVMaximumVoltageLimit"); if (evMaxVoltageLimit != null) { req.EVMaximumVoltageLimit = ParsePhysicalValueXml(evMaxVoltageLimit, ns4); req.EVMaximumVoltageLimit_isUsed = true; } var evMaxCurrentLimit = reqElement.Element(ns3 + "EVMaximumCurrentLimit"); if (evMaxCurrentLimit != null) { req.EVMaximumCurrentLimit = ParsePhysicalValueXml(evMaxCurrentLimit, ns4); req.EVMaximumCurrentLimit_isUsed = true; } var evMaxPowerLimit = reqElement.Element(ns3 + "EVMaximumPowerLimit"); if (evMaxPowerLimit != null) { req.EVMaximumPowerLimit = ParsePhysicalValueXml(evMaxPowerLimit, ns4); req.EVMaximumPowerLimit_isUsed = true; } var bulkChargingComplete = reqElement.Element(ns3 + "BulkChargingComplete"); if (bulkChargingComplete != null) { req.BulkChargingComplete = bool.Parse(bulkChargingComplete.Value); // XMLμ—μ„œ μš”μ†Œκ°€ λͺ…μ‹œμ μœΌλ‘œ ν¬ν•¨λ˜λ©΄ κ°’κ³Ό 관계없이 _isUsed = true req.BulkChargingComplete_isUsed = true; // Element exists in XML } var chargingComplete = reqElement.Element(ns3 + "ChargingComplete"); if (chargingComplete != null) { req.ChargingComplete = bool.Parse(chargingComplete.Value); // ChargingComplete is mandatory in VC2022 (no _isUsed flag) } var remainingTimeToFullSoc = reqElement.Element(ns3 + "RemainingTimeToFullSoC"); if (remainingTimeToFullSoc != null) { req.RemainingTimeToFullSoC = ParsePhysicalValueXml(remainingTimeToFullSoc, ns4); req.RemainingTimeToFullSoC_isUsed = true; } var remainingTimeToBulkSoc = reqElement.Element(ns3 + "RemainingTimeToBulkSoC"); if (remainingTimeToBulkSoc != null) { req.RemainingTimeToBulkSoC = ParsePhysicalValueXml(remainingTimeToBulkSoc, ns4); req.RemainingTimeToBulkSoC_isUsed = true; } var evTargetVoltage = reqElement.Element(ns3 + "EVTargetVoltage"); if (evTargetVoltage != null) { req.EVTargetVoltage = ParsePhysicalValueXml(evTargetVoltage, ns4); // EVTargetVoltage is mandatory in VC2022 (no _isUsed flag) } 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) { var value = new PhysicalValueType(); var multiplier = element.Element(ns4 + "Multiplier"); if (multiplier != null) value.Multiplier = sbyte.Parse(multiplier.Value); var unit = element.Element(ns4 + "Unit"); if (unit != null) value.Unit = (UnitSymbolType)int.Parse(unit.Value); var val = element.Element(ns4 + "Value"); if (val != null) value.Value = short.Parse(val.Value); return value; } private static string ParseSessionSetupReqAnalysis(XElement sessionSetupReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ“‹ Session Setup Request Analysis:"); analysis.AppendLine(" Purpose: Initialize charging session with EVSE"); analysis.AppendLine(); var evccId = sessionSetupReq.Element(ns3 + "EVCCID"); if (evccId != null) { analysis.AppendLine($" πŸ”‘ EV Controller ID: {evccId.Value}"); analysis.AppendLine($" Length: {evccId.Value.Length} bytes"); } analysis.AppendLine(" πŸ“Š Session Initialization Process"); analysis.AppendLine(" βœ… Request sent to establish charging session"); return analysis.ToString(); } private static string ParseSessionSetupResAnalysis(XElement sessionSetupRes, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ“‹ Session Setup Response Analysis:"); analysis.AppendLine(" Purpose: Confirm session establishment"); analysis.AppendLine(); var responseCode = sessionSetupRes.Element(ns3 + "ResponseCode"); if (responseCode != null) { analysis.AppendLine($" πŸ“Š Response Code: {responseCode.Value}"); analysis.AppendLine($" Status: {(responseCode.Value == "OK" ? "βœ… Success" : "❌ Failed")}"); } var evseId = sessionSetupRes.Element(ns3 + "EVSEID"); if (evseId != null) { analysis.AppendLine($" 🏭 EVSE ID: {evseId.Value}"); } var evseTimeStamp = sessionSetupRes.Element(ns3 + "EVSETimeStamp"); if (evseTimeStamp != null) { analysis.AppendLine($" ⏰ EVSE Timestamp: {evseTimeStamp.Value}"); } analysis.AppendLine(" πŸ”„ Session established successfully"); return analysis.ToString(); } private static string ParseServiceDiscoveryReqAnalysis(XElement serviceDiscoveryReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ” Service Discovery Request Analysis:"); analysis.AppendLine(" Purpose: Discover available charging services"); analysis.AppendLine(); var serviceScope = serviceDiscoveryReq.Element(ns3 + "ServiceScope"); if (serviceScope != null) { analysis.AppendLine($" 🎯 Service Scope: {serviceScope.Value}"); } var serviceCategory = serviceDiscoveryReq.Element(ns3 + "ServiceCategory"); if (serviceCategory != null) { analysis.AppendLine($" πŸ“‚ Service Category: {serviceCategory.Value}"); } analysis.AppendLine(" πŸ” Requesting available services from EVSE"); analysis.AppendLine(" πŸ“‹ Preparing for service selection phase"); return analysis.ToString(); } private static string ParseServiceDiscoveryResAnalysis(XElement serviceDiscoveryRes, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ” Service Discovery Response Analysis:"); analysis.AppendLine(" Purpose: Provide available charging services"); analysis.AppendLine(); var responseCode = serviceDiscoveryRes.Element(ns3 + "ResponseCode"); if (responseCode != null) { analysis.AppendLine($" πŸ“Š Response Code: {responseCode.Value}"); analysis.AppendLine($" Status: {(responseCode.Value == "OK" ? "βœ… Success" : "❌ Failed")}"); } var paymentOptionList = serviceDiscoveryRes.Element(ns3 + "PaymentOptionList"); if (paymentOptionList != null) { analysis.AppendLine(" πŸ’³ Available Payment Options:"); var paymentOptions = paymentOptionList.Elements(ns3 + "PaymentOption"); foreach (var option in paymentOptions) { analysis.AppendLine($" β€’ {option.Value}"); } } var chargeService = serviceDiscoveryRes.Element(ns3 + "ChargeService"); if (chargeService != null) { analysis.AppendLine(" ⚑ Charging Service Available:"); var serviceId = chargeService.Element(ns3 + "ServiceID"); if (serviceId != null) { analysis.AppendLine($" πŸ†” Service ID: {serviceId.Value}"); } var serviceCategory = chargeService.Element(ns3 + "ServiceCategory"); if (serviceCategory != null) { analysis.AppendLine($" πŸ“‚ Category: {serviceCategory.Value}"); } var freeService = chargeService.Element(ns3 + "FreeService"); if (freeService != null) { analysis.AppendLine($" πŸ’° Free Service: {freeService.Value}"); } } var serviceList = serviceDiscoveryRes.Element(ns3 + "ServiceList"); if (serviceList != null) { analysis.AppendLine(" πŸ“‹ Additional Services:"); var services = serviceList.Elements(ns3 + "Service"); int serviceCount = 0; foreach (var service in services) { serviceCount++; var serviceId = service.Element(ns3 + "ServiceID"); var serviceName = service.Element(ns3 + "ServiceName"); analysis.AppendLine($" {serviceCount}. ID: {serviceId?.Value}, Name: {serviceName?.Value}"); } } return analysis.ToString(); } private static string ParseAuthorizationReqAnalysis(XElement authorizationReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ” Authorization Request Analysis:"); analysis.AppendLine(" Purpose: Authenticate and authorize charging"); analysis.AppendLine(); var idTokenInfo = authorizationReq.Element(ns3 + "Id"); if (idTokenInfo != null) { analysis.AppendLine($" 🎫 ID Token: {idTokenInfo.Value}"); } var genChallenge = authorizationReq.Element(ns3 + "GenChallenge"); if (genChallenge != null) { analysis.AppendLine($" πŸ”‘ Generated Challenge: {genChallenge.Value}"); analysis.AppendLine($" Length: {genChallenge.Value.Length} bytes"); } analysis.AppendLine(" πŸ”’ Requesting authorization for charging session"); analysis.AppendLine(" πŸ›‘οΈ Security challenge-response mechanism active"); return analysis.ToString(); } private static string ParseAuthorizationResAnalysis(XElement authorizationRes, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ” Authorization Response Analysis:"); analysis.AppendLine(" Purpose: Confirm authorization status"); analysis.AppendLine(); var responseCode = authorizationRes.Element(ns3 + "ResponseCode"); if (responseCode != null) { analysis.AppendLine($" πŸ“Š Response Code: {responseCode.Value}"); analysis.AppendLine($" Authorization: {(responseCode.Value == "OK" ? "βœ… Granted" : "❌ Denied")}"); } var evseProcesing = authorizationRes.Element(ns3 + "EVSEProcessing"); if (evseProcesing != null) { analysis.AppendLine($" βš™οΈ EVSE Processing: {evseProcesing.Value}"); string processingStatus = evseProcesing.Value switch { "Finished" => "βœ… Complete", "Ongoing" => "πŸ”„ In Progress", _ => "⚠️ Unknown" }; analysis.AppendLine($" Status: {processingStatus}"); } analysis.AppendLine(" πŸ”“ Authorization process completed"); return analysis.ToString(); } private static string ParseChargeParameterDiscoveryReqAnalysis(XElement chargeParamReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("⚑ Charge Parameter Discovery Request Analysis:"); analysis.AppendLine(" Purpose: Exchange charging parameters and capabilities"); analysis.AppendLine(); var maxEntriesSaScheduleTuple = chargeParamReq.Element(ns3 + "MaxEntriesSAScheduleTuple"); if (maxEntriesSaScheduleTuple != null) { analysis.AppendLine($" πŸ“Š Max SA Schedule Entries: {maxEntriesSaScheduleTuple.Value}"); } var requestedEnergyTransferMode = chargeParamReq.Element(ns3 + "RequestedEnergyTransferMode"); if (requestedEnergyTransferMode != null) { analysis.AppendLine($" πŸ”Œ Energy Transfer Mode: {requestedEnergyTransferMode.Value}"); } var dcEvChargeParameter = chargeParamReq.Element(ns3 + "DC_EVChargeParameter"); if (dcEvChargeParameter != null) { analysis.AppendLine(" πŸ”‹ DC EV Charge Parameters:"); var dcEvStatus = dcEvChargeParameter.Element(ns4 + "DC_EVStatus"); if (dcEvStatus != null) { var evReady = dcEvStatus.Element(ns4 + "EVReady"); var evErrorCode = dcEvStatus.Element(ns4 + "EVErrorCode"); var evRessSoc = dcEvStatus.Element(ns4 + "EVRESSSOC"); if (evReady != null) analysis.AppendLine($" πŸš— EV Ready: {evReady.Value}"); if (evErrorCode != null) analysis.AppendLine($" ❌ Error Code: {evErrorCode.Value}"); if (evRessSoc != null) analysis.AppendLine($" πŸ”‹ Battery SoC: {evRessSoc.Value}%"); } var evMaxCurrent = dcEvChargeParameter.Element(ns4 + "EVMaximumCurrentLimit"); if (evMaxCurrent != null) { var value = evMaxCurrent.Element(ns4 + "Value"); var unit = evMaxCurrent.Element(ns4 + "Unit"); var multiplier = evMaxCurrent.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" ⚑ Max Current: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evMaxVoltage = dcEvChargeParameter.Element(ns4 + "EVMaximumVoltageLimit"); if (evMaxVoltage != null) { var value = evMaxVoltage.Element(ns4 + "Value"); var unit = evMaxVoltage.Element(ns4 + "Unit"); var multiplier = evMaxVoltage.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" πŸ”Œ Max Voltage: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evMaxPower = dcEvChargeParameter.Element(ns4 + "EVMaximumPowerLimit"); if (evMaxPower != null) { var value = evMaxPower.Element(ns4 + "Value"); var unit = evMaxPower.Element(ns4 + "Unit"); var multiplier = evMaxPower.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" ⚑ Max Power: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evEnergyCapacity = dcEvChargeParameter.Element(ns4 + "EVEnergyCapacity"); if (evEnergyCapacity != null) { var value = evEnergyCapacity.Element(ns4 + "Value"); var unit = evEnergyCapacity.Element(ns4 + "Unit"); var multiplier = evEnergyCapacity.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" πŸ”‹ Energy Capacity: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evEnergyRequest = dcEvChargeParameter.Element(ns4 + "EVEnergyRequest"); if (evEnergyRequest != null) { var value = evEnergyRequest.Element(ns4 + "Value"); var unit = evEnergyRequest.Element(ns4 + "Unit"); var multiplier = evEnergyRequest.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" 🎯 Energy Request: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var fullSoc = dcEvChargeParameter.Element(ns4 + "FullSOC"); if (fullSoc != null) { analysis.AppendLine($" πŸ”‹ Full SoC: {fullSoc.Value}%"); } var bulkSoc = dcEvChargeParameter.Element(ns4 + "BulkSOC"); if (bulkSoc != null) { analysis.AppendLine($" πŸ”‹ Bulk SoC: {bulkSoc.Value}%"); } } analysis.AppendLine(" πŸ“‹ Requesting EVSE charging parameters"); return analysis.ToString(); } private static string ParseChargeParameterDiscoveryResAnalysis(XElement chargeParamRes, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("⚑ Charge Parameter Discovery Response Analysis:"); analysis.AppendLine(" Purpose: Provide EVSE charging capabilities and schedules"); analysis.AppendLine(); var responseCode = chargeParamRes.Element(ns3 + "ResponseCode"); if (responseCode != null) { analysis.AppendLine($" πŸ“Š Response Code: {responseCode.Value}"); analysis.AppendLine($" Status: {(responseCode.Value == "OK" ? "βœ… Success" : "❌ Failed")}"); } var evseProcesing = chargeParamRes.Element(ns3 + "EVSEProcessing"); if (evseProcesing != null) { analysis.AppendLine($" βš™οΈ EVSE Processing: {evseProcesing.Value}"); } var saScheduleList = chargeParamRes.Element(ns3 + "SAScheduleList"); if (saScheduleList != null) { analysis.AppendLine(" πŸ“… SA Schedule List:"); var schedules = saScheduleList.Elements(ns3 + "SAScheduleTuple"); int scheduleCount = 0; foreach (var schedule in schedules) { scheduleCount++; analysis.AppendLine($" πŸ“… Schedule {scheduleCount}:"); var saScheduleTupleId = schedule.Element(ns4 + "SAScheduleTupleID"); if (saScheduleTupleId != null) analysis.AppendLine($" πŸ†” ID: {saScheduleTupleId.Value}"); var pMaxSchedule = schedule.Element(ns4 + "PMaxSchedule"); if (pMaxSchedule != null) { analysis.AppendLine(" ⚑ Power Schedule:"); var pMaxScheduleEntries = pMaxSchedule.Elements(ns4 + "PMaxScheduleEntry"); foreach (var entry in pMaxScheduleEntries) { var relativeTimeInterval = entry.Element(ns4 + "RelativeTimeInterval"); var pMax = entry.Element(ns4 + "PMax"); if (relativeTimeInterval != null && pMax != null) { var start = relativeTimeInterval.Element(ns4 + "start"); var duration = relativeTimeInterval.Element(ns4 + "duration"); var pMaxValue = pMax.Element(ns4 + "Value"); analysis.AppendLine($" ⏰ Start: {start?.Value}s, Duration: {duration?.Value}s, Power: {pMaxValue?.Value}W"); } } } var salesTariff = schedule.Element(ns4 + "SalesTariff"); if (salesTariff != null) { analysis.AppendLine(" πŸ’° Sales Tariff Available"); var salesTariffId = salesTariff.Element(ns4 + "Id"); if (salesTariffId != null) analysis.AppendLine($" πŸ†” Tariff ID: {salesTariffId.Value}"); } } } var dcEvseChargeParameter = chargeParamRes.Element(ns3 + "DC_EVSEChargeParameter"); if (dcEvseChargeParameter != null) { analysis.AppendLine(" 🏭 DC EVSE Charge Parameters:"); var dcEvseStatus = dcEvseChargeParameter.Element(ns4 + "DC_EVSEStatus"); if (dcEvseStatus != null) { var notificationMaxDelay = dcEvseStatus.Element(ns4 + "NotificationMaxDelay"); var evseNotification = dcEvseStatus.Element(ns4 + "EVSENotification"); var evseIsolationStatus = dcEvseStatus.Element(ns4 + "EVSEIsolationStatus"); if (notificationMaxDelay != null) analysis.AppendLine($" ⏰ Max Notification Delay: {notificationMaxDelay.Value}s"); if (evseNotification != null) analysis.AppendLine($" πŸ“’ EVSE Notification: {evseNotification.Value}"); if (evseIsolationStatus != null) analysis.AppendLine($" πŸ”Œ Isolation Status: {evseIsolationStatus.Value}"); } var evseMaxCurrent = dcEvseChargeParameter.Element(ns4 + "EVSEMaximumCurrentLimit"); if (evseMaxCurrent != null) { var value = evseMaxCurrent.Element(ns4 + "Value"); var unit = evseMaxCurrent.Element(ns4 + "Unit"); var multiplier = evseMaxCurrent.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" ⚑ EVSE Max Current: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evseMaxVoltage = dcEvseChargeParameter.Element(ns4 + "EVSEMaximumVoltageLimit"); if (evseMaxVoltage != null) { var value = evseMaxVoltage.Element(ns4 + "Value"); var unit = evseMaxVoltage.Element(ns4 + "Unit"); var multiplier = evseMaxVoltage.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" πŸ”Œ EVSE Max Voltage: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evseMaxPower = dcEvseChargeParameter.Element(ns4 + "EVSEMaximumPowerLimit"); if (evseMaxPower != null) { var value = evseMaxPower.Element(ns4 + "Value"); var unit = evseMaxPower.Element(ns4 + "Unit"); var multiplier = evseMaxPower.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" ⚑ EVSE Max Power: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evseMinCurrent = dcEvseChargeParameter.Element(ns4 + "EVSEMinimumCurrentLimit"); if (evseMinCurrent != null) { var value = evseMinCurrent.Element(ns4 + "Value"); var unit = evseMinCurrent.Element(ns4 + "Unit"); var multiplier = evseMinCurrent.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" ⚑ EVSE Min Current: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evseMinVoltage = dcEvseChargeParameter.Element(ns4 + "EVSEMinimumVoltageLimit"); if (evseMinVoltage != null) { var value = evseMinVoltage.Element(ns4 + "Value"); var unit = evseMinVoltage.Element(ns4 + "Unit"); var multiplier = evseMinVoltage.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" πŸ”Œ EVSE Min Voltage: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evseCurrentRegulationTolerance = dcEvseChargeParameter.Element(ns4 + "EVSECurrentRegulationTolerance"); if (evseCurrentRegulationTolerance != null) { var value = evseCurrentRegulationTolerance.Element(ns4 + "Value"); var unit = evseCurrentRegulationTolerance.Element(ns4 + "Unit"); var multiplier = evseCurrentRegulationTolerance.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" πŸ“Š Current Regulation Tolerance: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evsePeakCurrentRipple = dcEvseChargeParameter.Element(ns4 + "EVSEPeakCurrentRipple"); if (evsePeakCurrentRipple != null) { var value = evsePeakCurrentRipple.Element(ns4 + "Value"); var unit = evsePeakCurrentRipple.Element(ns4 + "Unit"); var multiplier = evsePeakCurrentRipple.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" 🌊 Peak Current Ripple: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var evseEnergyToBeDelivered = dcEvseChargeParameter.Element(ns4 + "EVSEEnergyToBeDelivered"); if (evseEnergyToBeDelivered != null) { var value = evseEnergyToBeDelivered.Element(ns4 + "Value"); var unit = evseEnergyToBeDelivered.Element(ns4 + "Unit"); var multiplier = evseEnergyToBeDelivered.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" 🎯 Energy To Deliver: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } } return analysis.ToString(); } private static string ParsePaymentServiceSelectionReqAnalysis(XElement paymentServiceSelectionReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ’³ Payment Service Selection Request Analysis:"); analysis.AppendLine(" Purpose: Select payment method for charging session"); analysis.AppendLine(); var selectedPaymentOption = paymentServiceSelectionReq.Element(ns3 + "SelectedPaymentOption"); if (selectedPaymentOption != null) { analysis.AppendLine($" πŸ†” Selected Payment Option: {selectedPaymentOption.Value}"); string paymentType = selectedPaymentOption.Value switch { "Contract" => "πŸ”— Contract-based payment", "ExternalPayment" => "πŸ’° External payment method", _ => $"❓ Unknown ({selectedPaymentOption.Value})" }; analysis.AppendLine($" Type: {paymentType}"); } var selectedServiceList = paymentServiceSelectionReq.Element(ns3 + "SelectedServiceList"); if (selectedServiceList != null) { analysis.AppendLine(" πŸ“‹ Selected Service List:"); var selectedServices = selectedServiceList.Elements(ns3 + "SelectedService"); int serviceCount = 0; foreach (var service in selectedServices) { serviceCount++; var serviceId = service.Element(ns3 + "ServiceID"); var parameterSetId = service.Element(ns3 + "ParameterSetID"); analysis.AppendLine($" {serviceCount}. Service ID: {serviceId?.Value}"); if (parameterSetId != null) analysis.AppendLine($" Parameter Set ID: {parameterSetId.Value}"); } if (serviceCount == 0) analysis.AppendLine(" No services selected"); } analysis.AppendLine(" βœ… Payment service selection requested"); return analysis.ToString(); } private static string ParsePaymentServiceSelectionResAnalysis(XElement paymentServiceSelectionRes, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ’³ Payment Service Selection Response Analysis:"); analysis.AppendLine(" Purpose: Confirm payment method selection"); analysis.AppendLine(); var responseCode = paymentServiceSelectionRes.Element(ns3 + "ResponseCode"); if (responseCode != null) { analysis.AppendLine($" πŸ“Š Response Code: {responseCode.Value}"); string status = responseCode.Value switch { "OK" => "βœ… Payment selection accepted", "OK_NewSessionEstablished" => "βœ… New session established", "FAILED" => "❌ Payment selection failed", _ => $"⚠️ Unknown ({responseCode.Value})" }; analysis.AppendLine($" Status: {status}"); } analysis.AppendLine(" πŸ”„ Payment method selection completed"); return analysis.ToString(); } private static string ParseChargingStatusReqAnalysis(XElement chargingStatusReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ”‹ Charging Status Request Analysis:"); analysis.AppendLine(" Purpose: Request AC charging status information"); analysis.AppendLine(); analysis.AppendLine(" πŸ” Requesting current charging status from EVSE"); analysis.AppendLine(" πŸ“Š Status inquiry for AC charging session"); return analysis.ToString(); } private static string ParseChargingStatusResAnalysis(XElement chargingStatusRes, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ”‹ Charging Status Response Analysis:"); analysis.AppendLine(" Purpose: Provide AC charging status information"); analysis.AppendLine(); var responseCode = chargingStatusRes.Element(ns3 + "ResponseCode"); if (responseCode != null) { analysis.AppendLine($" πŸ“Š Response Code: {responseCode.Value}"); string status = responseCode.Value switch { "OK" => "βœ… Success", "OK_NewSessionEstablished" => "βœ… New session established", "FAILED" => "❌ Failed", _ => $"⚠️ Unknown ({responseCode.Value})" }; analysis.AppendLine($" Status: {status}"); } var evseId = chargingStatusRes.Element(ns3 + "EVSEID"); if (evseId != null) { analysis.AppendLine($" 🏭 EVSE ID: {evseId.Value}"); } var saScheduleTupleId = chargingStatusRes.Element(ns3 + "SAScheduleTupleID"); if (saScheduleTupleId != null) { analysis.AppendLine($" πŸ”Œ SA Schedule Tuple ID: {saScheduleTupleId.Value}"); } var evseMaxCurrent = chargingStatusRes.Element(ns3 + "EVSEMaxCurrent"); if (evseMaxCurrent != null) { var value = evseMaxCurrent.Element(ns4 + "Value"); var unit = evseMaxCurrent.Element(ns4 + "Unit"); var multiplier = evseMaxCurrent.Element(ns4 + "Multiplier"); if (value != null) analysis.AppendLine($" ⚑ EVSE Max Current: {value.Value} {GetUnitString(unit?.Value)} (10^{multiplier?.Value ?? "0"})"); } var meterInfo = chargingStatusRes.Element(ns3 + "MeterInfo"); if (meterInfo != null) { analysis.AppendLine(" πŸ“Š Meter Information:"); var meterId = meterInfo.Element(ns3 + "MeterID"); if (meterId != null) analysis.AppendLine($" πŸ†” Meter ID: {meterId.Value}"); var meterReading = meterInfo.Element(ns3 + "MeterReading"); if (meterReading != null) analysis.AppendLine($" πŸ“ Meter Reading: {meterReading.Value} Wh"); var sigMeterReading = meterInfo.Element(ns3 + "SigMeterReading"); if (sigMeterReading != null) analysis.AppendLine($" πŸ” Signed Meter Reading: [Length: {sigMeterReading.Value.Length}]"); var meterStatus = meterInfo.Element(ns3 + "MeterStatus"); if (meterStatus != null) analysis.AppendLine($" πŸ“Š Meter Status: {meterStatus.Value}"); } var receiptRequired = chargingStatusRes.Element(ns3 + "ReceiptRequired"); if (receiptRequired != null) { analysis.AppendLine($" 🧾 Receipt Required: {receiptRequired.Value}"); } analysis.AppendLine(" βœ… AC charging status provided"); return analysis.ToString(); } private static string ParseSessionStopReqAnalysis(XElement sessionStopReq, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ›‘ Session Stop Request Analysis:"); analysis.AppendLine(" Purpose: Request charging session termination"); analysis.AppendLine(); var chargingSession = sessionStopReq.Element(ns3 + "ChargingSession"); if (chargingSession != null) { analysis.AppendLine($" πŸ”„ Charging Session: {chargingSession.Value}"); string sessionType = chargingSession.Value switch { "Terminate" => "βœ… Terminate - Session ending completely", "Pause" => "⏸️ Pause - Session paused temporarily", _ => $"⚠️ Unknown ({chargingSession.Value})" }; analysis.AppendLine($" Type: {sessionType}"); } analysis.AppendLine(" πŸ›‘ Requesting session termination"); return analysis.ToString(); } private static string ParseSessionStopResAnalysis(XElement sessionStopRes, XNamespace ns3, XNamespace ns4) { var analysis = new StringBuilder(); analysis.AppendLine("πŸ›‘ Session Stop Response Analysis:"); analysis.AppendLine(" Purpose: Confirm charging session termination"); analysis.AppendLine(); var responseCode = sessionStopRes.Element(ns3 + "ResponseCode"); if (responseCode != null) { analysis.AppendLine($" πŸ“Š Response Code: {responseCode.Value}"); string status = responseCode.Value switch { "OK" => "βœ… Session terminated successfully", "OK_NewSessionEstablished" => "βœ… New session established", "FAILED" => "❌ Session termination failed", _ => $"⚠️ Unknown ({responseCode.Value})" }; analysis.AppendLine($" Status: {status}"); } analysis.AppendLine(" πŸ”š Charging session terminated"); return analysis.ToString(); } private static string GetUnitString(string? unitValue) { return unitValue switch { "5" => "A", // Ampere "29" => "V", // Volt "38" => "W", // Watt "33" => "Wh", // Watt-hour "159" => "s", // Second _ => $"Unit({unitValue})" }; } } }