feat: Complete Phase 3 Extended Features in .NET - Payment, Charging Status, Session Stop
- Add XML parsing detection for all 6 Phase 3 message types: - PaymentServiceSelectionReq/Res: Payment method selection - ChargingStatusReq/Res: AC charging status monitoring - SessionStopReq/Res: Session termination control - Implement comprehensive Parse*Analysis methods with emoji indicators: - ParsePaymentServiceSelectionReqAnalysis/ResAnalysis: Payment option parsing - ParseChargingStatusReqAnalysis/ResAnalysis: AC status with meter info - ParseSessionStopReqAnalysis/ResAnalysis: Session termination handling - Add detailed XML element parsing for: - Payment options (Contract vs External) - Service list enumeration and parameter sets - EVSE charging status and meter readings - Session control with ChargingSession enum - Maintain .NET coding style with LINQ and modern C# patterns - All 18 V2G message types now fully implemented across C, VC2022, and .NET - Complete multi-platform V2G decoder achievement! 🎉 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -170,6 +170,55 @@ namespace V2GDecoderNet
|
||||
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
|
||||
}
|
||||
@@ -1309,6 +1358,212 @@ namespace V2GDecoderNet
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user