- Implement complete CurrentDemandReq/CurrentDemandRes parsing (24 total fields) - Add enhanced_exi_viewer.c with detailed message analysis - Support -decode option for clean XML output (file-ready format) - Enable ISO1, ISO2, DIN codec support in build configuration - Fix C99 compatibility issues in makefiles (change -ansi to -std=c99) - Create test utilities for hex string to EXI conversion - Generate test files: test3.exi (CurrentDemandRes), test4.exi (CurrentDemandReq) Features: * Dual output modes: detailed analysis (default) vs XML (-decode) * Complete V2G message type detection and parsing * Session ID display in hex and ASCII formats * Voltage/current/power readings with proper units * All optional fields and status flags supported 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
627 lines
35 KiB
C
627 lines
35 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/* EXI codec headers */
|
|
#include "iso1EXIDatatypes.h"
|
|
#include "iso1EXIDatatypesDecoder.h"
|
|
#include "iso2EXIDatatypes.h"
|
|
#include "iso2EXIDatatypesDecoder.h"
|
|
#include "dinEXIDatatypes.h"
|
|
#include "dinEXIDatatypesDecoder.h"
|
|
#include "ByteStream.h"
|
|
|
|
#define BUFFER_SIZE 4096
|
|
|
|
void print_session_id(uint8_t *bytes, size_t len) {
|
|
printf("SessionID: ");
|
|
for(size_t i = 0; i < len; i++) {
|
|
printf("%02X", bytes[i]);
|
|
}
|
|
printf(" (");
|
|
for(size_t i = 0; i < len; i++) {
|
|
if (bytes[i] >= 32 && bytes[i] <= 126) {
|
|
printf("%c", bytes[i]);
|
|
} else {
|
|
printf(".");
|
|
}
|
|
}
|
|
printf(")\n");
|
|
}
|
|
|
|
void print_xml_header() {
|
|
printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
|
printf("<V2G_Message xmlns=\"urn:iso:15118:2:2013:MsgDef\"\n");
|
|
printf(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n");
|
|
}
|
|
|
|
void print_xml_footer() {
|
|
printf("</V2G_Message>\n");
|
|
}
|
|
|
|
void print_session_id_xml(uint8_t *bytes, size_t len) {
|
|
printf(" <SessionID>");
|
|
for(size_t i = 0; i < len; i++) {
|
|
printf("%02X", bytes[i]);
|
|
}
|
|
printf("</SessionID>\n");
|
|
}
|
|
|
|
void print_iso1_xml(struct iso1EXIDocument *doc) {
|
|
if (!doc->V2G_Message_isUsed) return;
|
|
|
|
print_xml_header();
|
|
|
|
// Header
|
|
printf(" <Header>\n");
|
|
print_session_id_xml(doc->V2G_Message.Header.SessionID.bytes,
|
|
doc->V2G_Message.Header.SessionID.bytesLen);
|
|
|
|
if (doc->V2G_Message.Header.Notification_isUsed) {
|
|
printf(" <Notification>\n");
|
|
printf(" <FaultCode>%d</FaultCode>\n", doc->V2G_Message.Header.Notification.FaultCode);
|
|
printf(" <FaultMsg>%s</FaultMsg>\n", doc->V2G_Message.Header.Notification.FaultMsg.characters);
|
|
printf(" </Notification>\n");
|
|
}
|
|
printf(" </Header>\n");
|
|
|
|
// Body
|
|
printf(" <Body>\n");
|
|
|
|
if (doc->V2G_Message.Body.CurrentDemandRes_isUsed) {
|
|
printf(" <CurrentDemandRes>\n");
|
|
printf(" <ResponseCode>%d</ResponseCode>\n", doc->V2G_Message.Body.CurrentDemandRes.ResponseCode);
|
|
printf(" <DC_EVSEStatus>\n");
|
|
printf(" <EVSENotification>%d</EVSENotification>\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSENotification);
|
|
printf(" <NotificationMaxDelay>%d</NotificationMaxDelay>\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.NotificationMaxDelay);
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus_isUsed) {
|
|
printf(" <EVSEIsolationStatus>%d</EVSEIsolationStatus>\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus);
|
|
}
|
|
printf(" <EVSEStatusCode>%d</EVSEStatusCode>\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEStatusCode);
|
|
printf(" </DC_EVSEStatus>\n");
|
|
printf(" <EVSEPresentVoltage>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Value);
|
|
printf(" </EVSEPresentVoltage>\n");
|
|
printf(" <EVSEPresentCurrent>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Value);
|
|
printf(" </EVSEPresentCurrent>\n");
|
|
printf(" <EVSECurrentLimitAchieved>%s</EVSECurrentLimitAchieved>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved ? "true" : "false");
|
|
printf(" <EVSEVoltageLimitAchieved>%s</EVSEVoltageLimitAchieved>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved ? "true" : "false");
|
|
printf(" <EVSEPowerLimitAchieved>%s</EVSEPowerLimitAchieved>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved ? "true" : "false");
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit_isUsed) {
|
|
printf(" <EVSEMaximumVoltageLimit>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit.Value);
|
|
printf(" </EVSEMaximumVoltageLimit>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit_isUsed) {
|
|
printf(" <EVSEMaximumCurrentLimit>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit.Value);
|
|
printf(" </EVSEMaximumCurrentLimit>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit_isUsed) {
|
|
printf(" <EVSEMaximumPowerLimit>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit.Value);
|
|
printf(" </EVSEMaximumPowerLimit>\n");
|
|
}
|
|
printf(" <EVSEID>%.*s</EVSEID>\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.charactersLen,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters);
|
|
printf(" <SAScheduleTupleID>%d</SAScheduleTupleID>\n", doc->V2G_Message.Body.CurrentDemandRes.SAScheduleTupleID);
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo_isUsed) {
|
|
printf(" <MeterInfo>\n");
|
|
printf(" <MeterID>%.*s</MeterID>\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterID.charactersLen,
|
|
doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterID.characters);
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterReading_isUsed) {
|
|
printf(" <MeterReading>%llu</MeterReading>\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterReading);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.SigMeterReading_isUsed) {
|
|
printf(" <SigMeterReading>");
|
|
for(int i = 0; i < doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.SigMeterReading.bytesLen; i++) {
|
|
printf("%02X", doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.SigMeterReading.bytes[i]);
|
|
}
|
|
printf("</SigMeterReading>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterStatus_isUsed) {
|
|
printf(" <MeterStatus>%d</MeterStatus>\n", doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterStatus);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.TMeter_isUsed) {
|
|
printf(" <TMeter>%lld</TMeter>\n", doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.TMeter);
|
|
}
|
|
printf(" </MeterInfo>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.ReceiptRequired_isUsed) {
|
|
printf(" <ReceiptRequired>%s</ReceiptRequired>\n", doc->V2G_Message.Body.CurrentDemandRes.ReceiptRequired ? "true" : "false");
|
|
}
|
|
printf(" </CurrentDemandRes>\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.CurrentDemandReq_isUsed) {
|
|
printf(" <CurrentDemandReq>\n");
|
|
printf(" <DC_EVStatus>\n");
|
|
printf(" <EVReady>%s</EVReady>\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false");
|
|
printf(" <EVErrorCode>%d</EVErrorCode>\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
|
|
printf(" <EVRESSSOC>%d</EVRESSSOC>\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC);
|
|
printf(" </DC_EVStatus>\n");
|
|
printf(" <EVTargetCurrent>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
|
|
printf(" </EVTargetCurrent>\n");
|
|
printf(" <EVTargetVoltage>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
|
|
printf(" </EVTargetVoltage>\n");
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) {
|
|
printf(" <EVMaximumVoltageLimit>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Value);
|
|
printf(" </EVMaximumVoltageLimit>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed) {
|
|
printf(" <EVMaximumCurrentLimit>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Value);
|
|
printf(" </EVMaximumCurrentLimit>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed) {
|
|
printf(" <EVMaximumPowerLimit>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Value);
|
|
printf(" </EVMaximumPowerLimit>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed) {
|
|
printf(" <BulkChargingComplete>%s</BulkChargingComplete>\n", doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete ? "true" : "false");
|
|
}
|
|
printf(" <ChargingComplete>%s</ChargingComplete>\n", doc->V2G_Message.Body.CurrentDemandReq.ChargingComplete ? "true" : "false");
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed) {
|
|
printf(" <RemainingTimeToFullSoC>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Value);
|
|
printf(" </RemainingTimeToFullSoC>\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed) {
|
|
printf(" <RemainingTimeToBulkSoC>\n");
|
|
printf(" <Multiplier>%d</Multiplier>\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Multiplier);
|
|
printf(" <Unit>%d</Unit>\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit);
|
|
printf(" <Value>%d</Value>\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Value);
|
|
printf(" </RemainingTimeToBulkSoC>\n");
|
|
}
|
|
printf(" </CurrentDemandReq>\n");
|
|
}
|
|
else {
|
|
printf(" <!-- Other message types not implemented in XML mode -->\n");
|
|
}
|
|
|
|
printf(" </Body>\n");
|
|
print_xml_footer();
|
|
}
|
|
|
|
void print_iso1_message(struct iso1EXIDocument *doc) {
|
|
printf("=== ISO1 V2G Message ===\n");
|
|
|
|
if (doc->V2G_Message_isUsed) {
|
|
// Print Header
|
|
printf("Header:\n");
|
|
print_session_id(doc->V2G_Message.Header.SessionID.bytes,
|
|
doc->V2G_Message.Header.SessionID.bytesLen);
|
|
|
|
if (doc->V2G_Message.Header.Notification_isUsed) {
|
|
printf(" Notification: FaultCode=%d, FaultMsg=%s\n",
|
|
doc->V2G_Message.Header.Notification.FaultCode,
|
|
doc->V2G_Message.Header.Notification.FaultMsg.characters);
|
|
}
|
|
|
|
printf("\nBody:\n");
|
|
|
|
// Check all possible message types
|
|
if (doc->V2G_Message.Body.SessionSetupReq_isUsed) {
|
|
printf(" Message Type: SessionSetupReq\n");
|
|
printf(" EVCCID: ");
|
|
for(size_t i = 0; i < doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen; i++) {
|
|
printf("%02X", doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i]);
|
|
}
|
|
printf(" (");
|
|
for(size_t i = 0; i < doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen; i++) {
|
|
if (doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i] >= 32 &&
|
|
doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i] <= 126) {
|
|
printf("%c", doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i]);
|
|
} else {
|
|
printf(".");
|
|
}
|
|
}
|
|
printf(")\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.SessionSetupRes_isUsed) {
|
|
printf(" Message Type: SessionSetupRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.SessionSetupRes.ResponseCode);
|
|
printf(" EVSEID: %s\n", doc->V2G_Message.Body.SessionSetupRes.EVSEID.characters);
|
|
if (doc->V2G_Message.Body.SessionSetupRes.EVSETimeStamp_isUsed) {
|
|
printf(" EVSETimeStamp: %ld\n", doc->V2G_Message.Body.SessionSetupRes.EVSETimeStamp);
|
|
}
|
|
}
|
|
else if (doc->V2G_Message.Body.ServiceDiscoveryReq_isUsed) {
|
|
printf(" Message Type: ServiceDiscoveryReq\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.ServiceDiscoveryRes_isUsed) {
|
|
printf(" Message Type: ServiceDiscoveryRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.ServiceDiscoveryRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.ServiceDetailReq_isUsed) {
|
|
printf(" Message Type: ServiceDetailReq\n");
|
|
printf(" ServiceID: %d\n", doc->V2G_Message.Body.ServiceDetailReq.ServiceID);
|
|
}
|
|
else if (doc->V2G_Message.Body.ServiceDetailRes_isUsed) {
|
|
printf(" Message Type: ServiceDetailRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.ServiceDetailRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.PaymentServiceSelectionReq_isUsed) {
|
|
printf(" Message Type: PaymentServiceSelectionReq\n");
|
|
printf(" SelectedPaymentOption: %d\n", doc->V2G_Message.Body.PaymentServiceSelectionReq.SelectedPaymentOption);
|
|
}
|
|
else if (doc->V2G_Message.Body.PaymentServiceSelectionRes_isUsed) {
|
|
printf(" Message Type: PaymentServiceSelectionRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.PaymentServiceSelectionRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.AuthorizationReq_isUsed) {
|
|
printf(" Message Type: AuthorizationReq\n");
|
|
if (doc->V2G_Message.Body.AuthorizationReq.Id_isUsed) {
|
|
printf(" ID: %s\n", doc->V2G_Message.Body.AuthorizationReq.Id.characters);
|
|
}
|
|
if (doc->V2G_Message.Body.AuthorizationReq.GenChallenge_isUsed) {
|
|
printf(" GenChallenge: ");
|
|
for(size_t i = 0; i < doc->V2G_Message.Body.AuthorizationReq.GenChallenge.bytesLen; i++) {
|
|
printf("%02X", doc->V2G_Message.Body.AuthorizationReq.GenChallenge.bytes[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
}
|
|
else if (doc->V2G_Message.Body.AuthorizationRes_isUsed) {
|
|
printf(" Message Type: AuthorizationRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.AuthorizationRes.ResponseCode);
|
|
printf(" EVSEProcessing: %d\n", doc->V2G_Message.Body.AuthorizationRes.EVSEProcessing);
|
|
}
|
|
else if (doc->V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed) {
|
|
printf(" Message Type: ChargeParameterDiscoveryReq\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.ChargeParameterDiscoveryRes_isUsed) {
|
|
printf(" Message Type: ChargeParameterDiscoveryRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.ChargeParameterDiscoveryRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.CableCheckReq_isUsed) {
|
|
printf(" Message Type: CableCheckReq\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.CableCheckRes_isUsed) {
|
|
printf(" Message Type: CableCheckRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.CableCheckRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.PreChargeReq_isUsed) {
|
|
printf(" Message Type: PreChargeReq\n");
|
|
printf(" EVTargetVoltage: %d\n", doc->V2G_Message.Body.PreChargeReq.EVTargetVoltage.Value);
|
|
printf(" EVTargetCurrent: %d\n", doc->V2G_Message.Body.PreChargeReq.EVTargetCurrent.Value);
|
|
}
|
|
else if (doc->V2G_Message.Body.PreChargeRes_isUsed) {
|
|
printf(" Message Type: PreChargeRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.PreChargeRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.PowerDeliveryReq_isUsed) {
|
|
printf(" Message Type: PowerDeliveryReq\n");
|
|
printf(" ChargeProgress: %d\n", doc->V2G_Message.Body.PowerDeliveryReq.ChargeProgress);
|
|
printf(" SAScheduleTupleID: %d\n", doc->V2G_Message.Body.PowerDeliveryReq.SAScheduleTupleID);
|
|
}
|
|
else if (doc->V2G_Message.Body.PowerDeliveryRes_isUsed) {
|
|
printf(" Message Type: PowerDeliveryRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.PowerDeliveryRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.ChargingStatusReq_isUsed) {
|
|
printf(" Message Type: ChargingStatusReq\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.ChargingStatusRes_isUsed) {
|
|
printf(" Message Type: ChargingStatusRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.ChargingStatusRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.MeteringReceiptReq_isUsed) {
|
|
printf(" Message Type: MeteringReceiptReq\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.MeteringReceiptRes_isUsed) {
|
|
printf(" Message Type: MeteringReceiptRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.MeteringReceiptRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.SessionStopReq_isUsed) {
|
|
printf(" Message Type: SessionStopReq\n");
|
|
printf(" ChargingSession: %d\n", doc->V2G_Message.Body.SessionStopReq.ChargingSession);
|
|
}
|
|
else if (doc->V2G_Message.Body.SessionStopRes_isUsed) {
|
|
printf(" Message Type: SessionStopRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.SessionStopRes.ResponseCode);
|
|
}
|
|
else if (doc->V2G_Message.Body.CurrentDemandReq_isUsed) {
|
|
printf(" Message Type: CurrentDemandReq\n");
|
|
printf(" DC_EVStatus:\n");
|
|
printf(" EVReady: %s\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false");
|
|
printf(" EVErrorCode: %d\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
|
|
printf(" EVRESSSOC: %d%%\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC);
|
|
printf(" EVTargetCurrent: %d A (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit);
|
|
printf(" EVTargetVoltage: %d V (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) {
|
|
printf(" EVMaximumVoltageLimit: %d V (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Value,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed) {
|
|
printf(" EVMaximumCurrentLimit: %d A (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Value,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed) {
|
|
printf(" EVMaximumPowerLimit: %d W (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Value,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed) {
|
|
printf(" BulkChargingComplete: %s\n", doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete ? "true" : "false");
|
|
}
|
|
printf(" ChargingComplete: %s\n", doc->V2G_Message.Body.CurrentDemandReq.ChargingComplete ? "true" : "false");
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed) {
|
|
printf(" RemainingTimeToFullSoC: %d s (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Value,
|
|
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed) {
|
|
printf(" RemainingTimeToBulkSoC: %d s (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Value,
|
|
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit);
|
|
}
|
|
}
|
|
else if (doc->V2G_Message.Body.CurrentDemandRes_isUsed) {
|
|
printf(" Message Type: CurrentDemandRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.CurrentDemandRes.ResponseCode);
|
|
printf(" DC_EVSEStatus:\n");
|
|
printf(" EVSENotification: %d\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSENotification);
|
|
printf(" NotificationMaxDelay: %d\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.NotificationMaxDelay);
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus_isUsed) {
|
|
printf(" EVSEIsolationStatus: %d\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus);
|
|
}
|
|
printf(" EVSEStatusCode: %d\n", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEStatusCode);
|
|
printf(" EVSEPresentVoltage: %d V (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Value,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Unit);
|
|
printf(" EVSEPresentCurrent: %d A (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Value,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Unit);
|
|
printf(" EVSECurrentLimitAchieved: %s\n", doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved ? "true" : "false");
|
|
printf(" EVSEVoltageLimitAchieved: %s\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved ? "true" : "false");
|
|
printf(" EVSEPowerLimitAchieved: %s\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved ? "true" : "false");
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit_isUsed) {
|
|
printf(" EVSEMaximumVoltageLimit: %d V (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit.Value,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumVoltageLimit.Unit);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit_isUsed) {
|
|
printf(" EVSEMaximumCurrentLimit: %d A (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit.Value,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumCurrentLimit.Unit);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit_isUsed) {
|
|
printf(" EVSEMaximumPowerLimit: %d W (Multiplier: %d, Unit: %d)\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit.Value,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit.Multiplier,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEMaximumPowerLimit.Unit);
|
|
}
|
|
printf(" EVSEID: %.*s\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.charactersLen,
|
|
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters);
|
|
printf(" SAScheduleTupleID: %d\n", doc->V2G_Message.Body.CurrentDemandRes.SAScheduleTupleID);
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo_isUsed) {
|
|
printf(" MeterInfo:\n");
|
|
printf(" MeterID: %.*s\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterID.charactersLen,
|
|
doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterID.characters);
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterReading_isUsed) {
|
|
printf(" MeterReading: %llu Wh\n",
|
|
doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterReading);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.SigMeterReading_isUsed) {
|
|
printf(" SigMeterReading: ");
|
|
for(int i = 0; i < doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.SigMeterReading.bytesLen; i++) {
|
|
printf("%02X", doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.SigMeterReading.bytes[i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterStatus_isUsed) {
|
|
printf(" MeterStatus: %d\n", doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.MeterStatus);
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.TMeter_isUsed) {
|
|
printf(" TMeter: %lld\n", doc->V2G_Message.Body.CurrentDemandRes.MeterInfo.TMeter);
|
|
}
|
|
}
|
|
if (doc->V2G_Message.Body.CurrentDemandRes.ReceiptRequired_isUsed) {
|
|
printf(" ReceiptRequired: %s\n", doc->V2G_Message.Body.CurrentDemandRes.ReceiptRequired ? "true" : "false");
|
|
}
|
|
}
|
|
else if (doc->V2G_Message.Body.WeldingDetectionReq_isUsed) {
|
|
printf(" Message Type: WeldingDetectionReq\n");
|
|
}
|
|
else if (doc->V2G_Message.Body.WeldingDetectionRes_isUsed) {
|
|
printf(" Message Type: WeldingDetectionRes\n");
|
|
printf(" ResponseCode: %d\n", doc->V2G_Message.Body.WeldingDetectionRes.ResponseCode);
|
|
}
|
|
else {
|
|
printf(" Message Type: Unknown or unhandled message type\n");
|
|
printf(" Debug: Checking which flags are set...\n");
|
|
// Debug output to see which flags might be set
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
void print_iso2_message(struct iso2EXIDocument *doc) {
|
|
printf("=== ISO2 V2G Message ===\n");
|
|
|
|
if (doc->V2G_Message_isUsed) {
|
|
printf("Header:\n");
|
|
print_session_id(doc->V2G_Message.Header.SessionID.bytes,
|
|
doc->V2G_Message.Header.SessionID.bytesLen);
|
|
|
|
printf("\nBody:\n");
|
|
if (doc->V2G_Message.Body.SessionSetupReq_isUsed) {
|
|
printf(" Message Type: SessionSetupReq\n");
|
|
printf(" EVCCID: ");
|
|
for(size_t i = 0; i < doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen; i++) {
|
|
printf("%02X", doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i]);
|
|
}
|
|
printf(" (");
|
|
for(size_t i = 0; i < doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen; i++) {
|
|
if (doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i] >= 32 &&
|
|
doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i] <= 126) {
|
|
printf("%c", doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i]);
|
|
} else {
|
|
printf(".");
|
|
}
|
|
}
|
|
printf(")\n");
|
|
}
|
|
// Add more ISO2 message types as needed
|
|
else {
|
|
printf(" Message Type: Other ISO2 message (not fully implemented)\n");
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[]) {
|
|
int xml_mode = 0;
|
|
char *filename = NULL;
|
|
|
|
if (argc == 2) {
|
|
filename = argv[1];
|
|
} else if (argc == 3 && strcmp(argv[1], "-decode") == 0) {
|
|
xml_mode = 1;
|
|
filename = argv[2];
|
|
} else {
|
|
printf("Usage: %s [-decode] input.exi\n", argv[0]);
|
|
printf("Enhanced EXI viewer with detailed message parsing\n");
|
|
printf(" -decode Output in XML format\n");
|
|
printf(" (default) Output detailed analysis\n");
|
|
return -1;
|
|
}
|
|
|
|
uint8_t buffer[BUFFER_SIZE];
|
|
bitstream_t stream;
|
|
size_t pos = 0;
|
|
int errn = 0;
|
|
|
|
struct iso1EXIDocument iso1Doc;
|
|
struct iso2EXIDocument iso2Doc;
|
|
struct dinEXIDocument dinDoc;
|
|
|
|
// Initialize documents
|
|
init_iso1EXIDocument(&iso1Doc);
|
|
init_iso2EXIDocument(&iso2Doc);
|
|
init_dinEXIDocument(&dinDoc);
|
|
|
|
// Read file
|
|
errn = readBytesFromFile(filename, buffer, BUFFER_SIZE, &pos);
|
|
if (errn != 0) {
|
|
printf("Error reading file: %s\n", filename);
|
|
return -1;
|
|
}
|
|
|
|
if (!xml_mode) {
|
|
printf("File: %s (%zu bytes)\n", filename, pos);
|
|
printf("Raw hex data: ");
|
|
for(size_t i = 0; i < (pos > 32 ? 32 : pos); i++) {
|
|
printf("%02X ", buffer[i]);
|
|
}
|
|
if (pos > 32) printf("...");
|
|
printf("\n\n");
|
|
}
|
|
|
|
// Setup stream
|
|
pos = 0; // reset position for decoding
|
|
stream.size = BUFFER_SIZE;
|
|
stream.data = buffer;
|
|
stream.pos = &pos;
|
|
stream.buffer = 0;
|
|
stream.capacity = 0;
|
|
|
|
// Try ISO1 first
|
|
pos = 0;
|
|
if (!xml_mode) printf("Trying ISO1 decoder...\n");
|
|
errn = decode_iso1ExiDocument(&stream, &iso1Doc);
|
|
if (errn == 0) {
|
|
if (!xml_mode) printf("✓ Successfully decoded as ISO1\n\n");
|
|
if (xml_mode) {
|
|
print_iso1_xml(&iso1Doc);
|
|
} else {
|
|
print_iso1_message(&iso1Doc);
|
|
}
|
|
return 0;
|
|
} else {
|
|
if (!xml_mode) printf("✗ ISO1 decode failed (error: %d)\n", errn);
|
|
}
|
|
|
|
// Try ISO2
|
|
pos = 0;
|
|
if (!xml_mode) printf("Trying ISO2 decoder...\n");
|
|
errn = decode_iso2ExiDocument(&stream, &iso2Doc);
|
|
if (errn == 0) {
|
|
if (!xml_mode) printf("✓ Successfully decoded as ISO2\n\n");
|
|
print_iso2_message(&iso2Doc);
|
|
return 0;
|
|
} else {
|
|
if (!xml_mode) printf("✗ ISO2 decode failed (error: %d)\n", errn);
|
|
}
|
|
|
|
// Try DIN
|
|
pos = 0;
|
|
if (!xml_mode) printf("Trying DIN decoder...\n");
|
|
errn = decode_dinExiDocument(&stream, &dinDoc);
|
|
if (errn == 0) {
|
|
if (!xml_mode) {
|
|
printf("✓ Successfully decoded as DIN\n\n");
|
|
printf("=== DIN V2G Message ===\n");
|
|
// Add DIN message printing as needed
|
|
}
|
|
return 0;
|
|
} else {
|
|
if (!xml_mode) printf("✗ DIN decode failed (error: %d)\n", errn);
|
|
}
|
|
|
|
if (!xml_mode) {
|
|
printf("\n❌ Could not decode EXI file with any supported codec\n");
|
|
printf("Supported formats: ISO1, ISO2, DIN\n");
|
|
}
|
|
|
|
return -1;
|
|
} |