feat: Add enhanced EXI viewer with XML decode capability
- 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>
This commit is contained in:
197
exi_viewer.c
Normal file
197
exi_viewer.c
Normal file
@@ -0,0 +1,197 @@
|
||||
#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("\n");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// Print Body
|
||||
printf("Body:\n");
|
||||
|
||||
if (doc->V2G_Message.Body.SessionSetupReq_isUsed) {
|
||||
printf(" SessionSetupReq:\n");
|
||||
printf(" EVCCID: ");
|
||||
for(size_t i = 0; i < doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen; i++) {
|
||||
printf("%c", doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (doc->V2G_Message.Body.SessionSetupRes_isUsed) {
|
||||
printf(" 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);
|
||||
}
|
||||
}
|
||||
|
||||
if (doc->V2G_Message.Body.ServiceDiscoveryReq_isUsed) {
|
||||
printf(" ServiceDiscoveryReq:\n");
|
||||
printf(" Service discovery request detected\n");
|
||||
}
|
||||
|
||||
if (doc->V2G_Message.Body.AuthorizationReq_isUsed) {
|
||||
printf(" 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");
|
||||
}
|
||||
}
|
||||
|
||||
if (doc->V2G_Message.Body.PowerDeliveryReq_isUsed) {
|
||||
printf(" PowerDeliveryReq:\n");
|
||||
printf(" ChargeProgress: %d\n", doc->V2G_Message.Body.PowerDeliveryReq.ChargeProgress);
|
||||
printf(" SAScheduleTupleID: %d\n", doc->V2G_Message.Body.PowerDeliveryReq.SAScheduleTupleID);
|
||||
}
|
||||
|
||||
if (doc->V2G_Message.Body.SessionStopReq_isUsed) {
|
||||
printf(" SessionStopReq:\n");
|
||||
printf(" ChargingSession: %d\n", doc->V2G_Message.Body.SessionStopReq.ChargingSession);
|
||||
}
|
||||
|
||||
// Add more message types as needed
|
||||
}
|
||||
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("Body:\n");
|
||||
if (doc->V2G_Message.Body.SessionSetupReq_isUsed) {
|
||||
printf(" SessionSetupReq:\n");
|
||||
printf(" EVCCID: ");
|
||||
for(size_t i = 0; i < doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen; i++) {
|
||||
printf("%c", doc->V2G_Message.Body.SessionSetupReq.EVCCID.bytes[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
// Add more ISO2-specific message types as needed
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
printf("Usage: %s input.exi\n", argv[0]);
|
||||
printf("This tool decodes and displays the content of EXI files\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;
|
||||
|
||||
// Read file
|
||||
errn = readBytesFromFile(argv[1], buffer, BUFFER_SIZE, &pos);
|
||||
if (errn != 0) {
|
||||
printf("Error reading file: %s\n", argv[1]);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("File: %s (%zu bytes)\n", argv[1], 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;
|
||||
printf("Trying ISO1 decoder...\n");
|
||||
errn = decode_iso1ExiDocument(&stream, &iso1Doc);
|
||||
if (errn == 0) {
|
||||
printf("✓ Successfully decoded as ISO1\n");
|
||||
print_iso1_message(&iso1Doc);
|
||||
return 0;
|
||||
} else {
|
||||
printf("✗ ISO1 decode failed (error: %d)\n", errn);
|
||||
}
|
||||
|
||||
// Try ISO2
|
||||
pos = 0;
|
||||
printf("Trying ISO2 decoder...\n");
|
||||
errn = decode_iso2ExiDocument(&stream, &iso2Doc);
|
||||
if (errn == 0) {
|
||||
printf("✓ Successfully decoded as ISO2\n");
|
||||
print_iso2_message(&iso2Doc);
|
||||
return 0;
|
||||
} else {
|
||||
printf("✗ ISO2 decode failed (error: %d)\n", errn);
|
||||
}
|
||||
|
||||
// Try DIN
|
||||
pos = 0;
|
||||
printf("Trying DIN decoder...\n");
|
||||
errn = decode_dinExiDocument(&stream, &dinDoc);
|
||||
if (errn == 0) {
|
||||
printf("✓ Successfully decoded as DIN\n");
|
||||
printf("=== DIN V2G Message ===\n");
|
||||
// Add DIN message printing as needed
|
||||
return 0;
|
||||
} else {
|
||||
printf("✗ DIN decode failed (error: %d)\n", errn);
|
||||
}
|
||||
|
||||
printf("\n❌ Could not decode EXI file with any supported codec\n");
|
||||
printf("Supported formats: ISO1, ISO2, DIN\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
Reference in New Issue
Block a user