feat: Complete Phase 2 .NET implementation - Session & Service Management

- Add comprehensive XML parsing analysis methods for all 8 Phase 2 messages
- ParseSessionSetupReqAnalysis/ParseSessionSetupResAnalysis: Session initialization
- ParseServiceDiscoveryReqAnalysis/ParseServiceDiscoveryResAnalysis: Service discovery
- ParseAuthorizationReqAnalysis/ParseAuthorizationResAnalysis: Authentication
- ParseChargeParameterDiscoveryReqAnalysis/ParseChargeParameterDiscoveryResAnalysis: Parameter exchange
- Add GetUnitString helper for physical value unit conversion
- Include emoji indicators for better readability and debugging
- All Phase 2 messages now fully implemented across C, VC2022, and .NET platforms

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-12 07:31:47 +09:00
parent f6250d762a
commit 14dfe964b4

View File

@@ -106,6 +106,71 @@ namespace V2GDecoderNet
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));
}
// Add other message types as needed
}
@@ -766,5 +831,495 @@ namespace V2GDecoderNet
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 GetUnitString(string? unitValue)
{
return unitValue switch
{
"5" => "A", // Ampere
"29" => "V", // Volt
"38" => "W", // Watt
"33" => "Wh", // Watt-hour
"159" => "s", // Second
_ => $"Unit({unitValue})"
};
}
}
}