diff --git a/Port/dotnet/V2G/V2GMessageProcessor.cs b/Port/dotnet/V2G/V2GMessageProcessor.cs index 68414d8..ac6b225 100644 --- a/Port/dotnet/V2G/V2GMessageProcessor.cs +++ b/Port/dotnet/V2G/V2GMessageProcessor.cs @@ -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})" + }; + } } } \ No newline at end of file