Files
V2GDecoderC/enhanced_exi_viewer.c
gram 3c43fa1318 Clean up encoding output to show only hex string
- Remove verbose "Encoded EXI data (X bytes):" message
- Terminal output now shows only clean hex string
- File redirection maintains binary output functionality
- Streamlined user experience for hex data copying

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 01:52:46 +09:00

1151 lines
51 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/stat.h>
/* EXI codec headers */
#include "iso1EXIDatatypes.h"
#include "iso1EXIDatatypesDecoder.h"
#include "iso1EXIDatatypesEncoder.h"
#include "iso2EXIDatatypes.h"
#include "iso2EXIDatatypesDecoder.h"
#include "iso2EXIDatatypesEncoder.h"
#include "dinEXIDatatypes.h"
#include "dinEXIDatatypesDecoder.h"
#include "dinEXIDatatypesEncoder.h"
#include "ByteStream.h"
#define BUFFER_SIZE 4096
// Network protocol patterns and definitions
#define ETH_TYPE_IPV6 0x86DD // Ethernet Type: IPv6
#define IPV6_NEXT_HEADER_TCP 0x06 // IPv6 Next Header: TCP
#define TCP_V2G_PORT 15118 // V2G communication port
// V2G Transfer Protocol patterns and definitions
#define V2G_PROTOCOL_VERSION 0x01 // Protocol Version
#define V2G_INV_PROTOCOL_VERSION 0xFE // Inverse Protocol Version
#define V2G_PAYLOAD_ISO_DIN_SAP 0x8001 // ISO 15118-2/DIN/SAP payload type
#define V2G_PAYLOAD_ISO2 0x8002 // ISO 15118-20 payload type
#define EXI_START_PATTERN 0x8098 // EXI document start pattern
// Function to detect and extract EXI body from V2G Transfer Protocol data
size_t extract_exi_body(uint8_t* input_data, size_t input_size, uint8_t* output_data, size_t output_size) {
if (input_size < 8) {
// Too small for V2G TP header, assume it's pure EXI
if (input_size <= output_size) {
memcpy(output_data, input_data, input_size);
return input_size;
}
return 0;
}
// Check for V2G Transfer Protocol header
if (input_data[0] == V2G_PROTOCOL_VERSION && input_data[1] == V2G_INV_PROTOCOL_VERSION) {
uint16_t payload_type = (input_data[2] << 8) | input_data[3];
if (payload_type == V2G_PAYLOAD_ISO_DIN_SAP || payload_type == V2G_PAYLOAD_ISO2) {
// Valid V2G TP header detected: skip 8-byte header
size_t exi_size = input_size - 8;
if (exi_size <= output_size) {
memcpy(output_data, input_data + 8, exi_size);
return exi_size;
}
return 0;
}
}
// Look for EXI start pattern anywhere in the data
for (size_t i = 0; i <= input_size - 2; i++) {
uint16_t pattern = (input_data[i] << 8) | input_data[i + 1];
if (pattern == EXI_START_PATTERN) {
// Found EXI start pattern
size_t exi_size = input_size - i;
if (exi_size <= output_size) {
memcpy(output_data, input_data + i, exi_size);
return exi_size;
}
return 0;
}
}
// No pattern found, assume it's pure EXI
if (input_size <= output_size) {
memcpy(output_data, input_data, input_size);
return input_size;
}
return 0;
}
// Function to get payload type name
const char* get_payload_type_name(uint16_t payload_type) {
switch(payload_type) {
case V2G_PAYLOAD_ISO_DIN_SAP: return "ISO 15118-2/DIN/SAP";
case V2G_PAYLOAD_ISO2: return "ISO 15118-20";
default: return "Unknown";
}
}
// Function to analyze complete packet structure
void analyze_data_structure(uint8_t* data, size_t size) {
printf("=== Data Structure Analysis ===\n");
printf("Total size: %zu bytes\n", size);
size_t offset = 0;
// Check for Ethernet header (at least 14 bytes)
if (size >= 14) {
uint16_t eth_type = (data[12] << 8) | data[13];
if (eth_type == ETH_TYPE_IPV6) {
printf("Layer 2: Ethernet Frame\n");
printf(" Destination MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
data[0], data[1], data[2], data[3], data[4], data[5]);
printf(" Source MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
data[6], data[7], data[8], data[9], data[10], data[11]);
printf(" EtherType: 0x%04x (IPv6)\n", eth_type);
offset = 14;
// Check for IPv6 header (40 bytes)
if (size >= offset + 40) {
uint8_t version = (data[offset] >> 4) & 0x0F;
uint16_t payload_length = (data[offset + 4] << 8) | data[offset + 5];
uint8_t next_header = data[offset + 6];
uint8_t hop_limit = data[offset + 7];
if (version == 6) {
printf("Layer 3: IPv6 Header\n");
printf(" Version: %d\n", version);
printf(" Payload Length: %u\n", payload_length);
printf(" Next Header: %u", next_header);
if (next_header == IPV6_NEXT_HEADER_TCP) {
printf(" (TCP)\n");
} else {
printf("\n");
}
printf(" Hop Limit: %u\n", hop_limit);
// Show IPv6 addresses
printf(" Source Address: ");
for (int i = 0; i < 16; i += 2) {
printf("%02x%02x", data[offset + 8 + i], data[offset + 8 + i + 1]);
if (i < 14) printf(":");
}
printf("\n Destination Address: ");
for (int i = 0; i < 16; i += 2) {
printf("%02x%02x", data[offset + 24 + i], data[offset + 24 + i + 1]);
if (i < 14) printf(":");
}
printf("\n");
offset += 40;
// Check for TCP header (at least 20 bytes)
if (next_header == IPV6_NEXT_HEADER_TCP && size >= offset + 20) {
uint16_t src_port = (data[offset] << 8) | data[offset + 1];
uint16_t dst_port = (data[offset + 2] << 8) | data[offset + 3];
uint32_t seq_num = (data[offset + 4] << 24) | (data[offset + 5] << 16) |
(data[offset + 6] << 8) | data[offset + 7];
uint8_t tcp_header_len = (data[offset + 12] >> 4) * 4;
printf("Layer 4: TCP Header\n");
printf(" Source Port: %u\n", src_port);
printf(" Destination Port: %u", dst_port);
if (dst_port == TCP_V2G_PORT || src_port == TCP_V2G_PORT) {
printf(" (V2G Communication)\n");
} else {
printf("\n");
}
printf(" Sequence Number: %u\n", seq_num);
printf(" TCP Header Length: %u bytes\n", tcp_header_len);
offset += tcp_header_len;
}
}
}
}
}
// Look for V2G Transfer Protocol starting from current offset
if (size >= offset + 8 &&
data[offset] == V2G_PROTOCOL_VERSION &&
data[offset + 1] == V2G_INV_PROTOCOL_VERSION) {
printf("Layer 7: V2G Transfer Protocol\n");
printf(" Protocol Version: 0x%02x\n", data[offset]);
printf(" Inverse Protocol Version: 0x%02x\n", data[offset + 1]);
uint16_t payload_type = (data[offset + 2] << 8) | data[offset + 3];
printf(" Payload Type: 0x%04x (%s)\n", payload_type, get_payload_type_name(payload_type));
uint32_t payload_length = (data[offset + 4] << 24) | (data[offset + 5] << 16) |
(data[offset + 6] << 8) | data[offset + 7];
printf(" Payload Length: %u\n", payload_length);
printf("EXI body starts at offset: %zu\n", offset + 8);
// Verify payload length
if (size >= offset + 8 && (size - offset - 8) == payload_length) {
printf("✓ Payload length matches actual data (%zu bytes)\n", size - offset - 8);
} else if (size >= offset + 8) {
printf("⚠ Payload length mismatch: expected %u, got %zu bytes\n",
payload_length, size - offset - 8);
}
offset += 8;
}
else if (offset == 0) {
// No network headers detected, check if it's direct V2G TP or EXI
if (size >= 4) {
uint32_t header = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
printf("First 4 bytes: 0x%08X\n", header);
printf("Protocol: Unknown or Direct EXI\n");
}
}
// Look for EXI pattern
for (size_t i = 0; i <= size - 2; i++) {
uint16_t pattern = (data[i] << 8) | data[i + 1];
if (pattern == EXI_START_PATTERN) {
printf("EXI start pattern (0x8098) found at offset: %zu\n", i);
if (i >= offset) {
printf("EXI payload size: %zu bytes\n", size - i);
}
break;
}
}
printf("\n");
}
// Helper function to convert char* string to exi_string_character_t* array
static int writeStringToEXIString(char* string, exi_string_character_t* exiString) {
int pos = 0;
while(string[pos] != '\0') {
exiString[pos] = string[pos];
pos++;
}
return pos;
}
char* trim_whitespace(char* str) {
char* end;
while(isspace((unsigned char)*str)) str++;
if(*str == 0) return str;
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
end[1] = '\0';
return str;
}
// Helper function to find XML tag content within a bounded section (namespace-aware)
char* find_tag_in_section(const char* section_start, const char* section_end, const char* tag) {
static char result[1024];
char ns_pattern[256];
char* content_start = NULL;
char* tag_end = NULL;
// First try namespace pattern (:tag>)
snprintf(ns_pattern, sizeof(ns_pattern), ":%s>", tag);
char* ns_tag = section_start;
while ((ns_tag = strstr(ns_tag, ns_pattern)) != NULL && ns_tag < section_end) {
// Find the opening '<'
char* tag_begin = ns_tag;
while (tag_begin > section_start && *tag_begin != '<') tag_begin--;
if (*tag_begin == '<' && tag_begin >= section_start) {
content_start = ns_tag + strlen(ns_pattern);
break;
}
ns_tag++;
}
// If namespace version not found, try regular version
if (!content_start) {
char start_tag[256];
snprintf(start_tag, sizeof(start_tag), "<%s>", tag);
char* tag_start = strstr(section_start, start_tag);
if (tag_start && tag_start < section_end) {
content_start = tag_start + strlen(start_tag);
}
}
if (!content_start || content_start >= section_end) {
return NULL;
}
// Look for end tag (try both patterns)
char end_tag_pattern[256];
snprintf(end_tag_pattern, sizeof(end_tag_pattern), "</%s>", tag);
tag_end = strstr(content_start, end_tag_pattern);
if (!tag_end || tag_end > section_end) {
// Try namespace end pattern
snprintf(ns_pattern, sizeof(ns_pattern), ":%s>", tag);
char* ns_end = content_start;
while ((ns_end = strstr(ns_end, ns_pattern)) != NULL && ns_end < section_end) {
char* end_begin = ns_end;
while (end_begin > content_start && *end_begin != '<') end_begin--;
if (end_begin > content_start && *end_begin == '<' && *(end_begin + 1) == '/') {
tag_end = end_begin;
break;
}
ns_end++;
}
}
if (!tag_end || tag_end > section_end) {
return NULL;
}
size_t len = tag_end - content_start;
if (len >= sizeof(result)) len = sizeof(result) - 1;
strncpy(result, content_start, len);
result[len] = '\0';
return trim_whitespace(result);
}
// Helper function to find XML tag content (namespace-aware)
char* find_tag_content_ns(const char* xml, const char* tag) {
static char result[1024];
char ns_pattern[256], end_pattern[256];
// Look for pattern ":tagname>" to handle namespaces
snprintf(ns_pattern, sizeof(ns_pattern), ":%s>", tag);
snprintf(end_pattern, sizeof(end_pattern), "</%s>", tag);
// First try to find namespace version (:tag>)
char* ns_start = strstr(xml, ns_pattern);
char* start = NULL;
if (ns_start) {
// Found namespaced tag, find the opening '<'
char* tag_begin = ns_start;
while (tag_begin > xml && *tag_begin != '<') tag_begin--;
if (*tag_begin == '<') {
start = ns_start + strlen(ns_pattern);
}
}
// If namespace version not found, try regular version
if (!start) {
char start_tag[256];
snprintf(start_tag, sizeof(start_tag), "<%s>", tag);
char* regular_start = strstr(xml, start_tag);
if (regular_start) {
start = regular_start + strlen(start_tag);
}
}
if (!start) return NULL;
// Look for end tag (try both namespaced and regular)
char ns_end_pattern[256];
snprintf(ns_end_pattern, sizeof(ns_end_pattern), "</%s>", tag);
char* end = strstr(start, ns_end_pattern);
if (!end) {
// Try with different namespace prefix
snprintf(ns_end_pattern, sizeof(ns_end_pattern), ":%s>", tag);
char* ns_end = strstr(start, ns_end_pattern);
if (ns_end) {
char* end_tag_begin = ns_end;
while (end_tag_begin > start && *end_tag_begin != '<') end_tag_begin--;
if (*end_tag_begin == '<' && *(end_tag_begin + 1) == '/') {
end = ns_end + strlen(ns_end_pattern);
// Backtrack to find the actual end
end = end_tag_begin;
}
}
}
if (!end) return NULL;
size_t len = end - start;
if (len >= sizeof(result)) len = sizeof(result) - 1;
strncpy(result, start, len);
result[len] = '\0';
return trim_whitespace(result);
}
// Helper function to find XML tag content
char* find_tag_content(const char* xml, const char* tag) {
// First try namespace-aware search
char* result = find_tag_content_ns(xml, tag);
if (result) return result;
// Fallback to original method
static char fallback_result[1024];
char start_tag[256], end_tag[256];
snprintf(start_tag, sizeof(start_tag), "<%s>", tag);
snprintf(end_tag, sizeof(end_tag), "</%s>", tag);
char* start = strstr(xml, start_tag);
if (!start) return NULL;
start += strlen(start_tag);
char* end = strstr(start, end_tag);
if (!end) return NULL;
size_t len = end - start;
if (len >= sizeof(fallback_result)) len = sizeof(fallback_result) - 1;
strncpy(fallback_result, start, len);
fallback_result[len] = '\0';
return trim_whitespace(fallback_result);
}
int parse_session_id(const char* hex_str, uint8_t* bytes, size_t* len) {
size_t hex_len = strlen(hex_str);
if (hex_len % 2 != 0) return -1;
*len = hex_len / 2;
for (size_t i = 0; i < *len; i++) {
unsigned int byte;
if (sscanf(&hex_str[i*2], "%2x", &byte) != 1) return -1;
bytes[i] = (uint8_t)byte;
}
return 0;
}
// Parse PhysicalValue from section bounded XML
void parse_physical_value_from_section(const char* section_start, const char* section_end, struct iso1PhysicalValueType* pv) {
// Copy the found values to local variables to avoid static buffer overwriting
char mult_str[64] = {0};
char unit_str[64] = {0};
char value_str[64] = {0};
char* mult = find_tag_in_section(section_start, section_end, "Multiplier");
if (mult) strncpy(mult_str, mult, sizeof(mult_str)-1);
char* unit = find_tag_in_section(section_start, section_end, "Unit");
if (unit) strncpy(unit_str, unit, sizeof(unit_str)-1);
char* value = find_tag_in_section(section_start, section_end, "Value");
if (value) strncpy(value_str, value, sizeof(value_str)-1);
// Now parse the copied values
if (mult) pv->Multiplier = atoi(mult_str);
if (unit) pv->Unit = atoi(unit_str);
if (value) pv->Value = atoi(value_str);
}
// Parse XML to ISO1 document for encoding
int parse_xml_to_iso1(const char* xml_content, struct iso1EXIDocument* doc) {
init_iso1EXIDocument(doc);
// Find SessionID
char* session_id_str = find_tag_content(xml_content, "SessionID");
if (session_id_str) {
size_t len;
if (parse_session_id(session_id_str, doc->V2G_Message.Header.SessionID.bytes, &len) == 0) {
doc->V2G_Message.Header.SessionID.bytesLen = len;
doc->V2G_Message_isUsed = 1;
}
} else {
// Search directly for namespaced SessionID
char* ns_start = strstr(xml_content, "<ns2:SessionID>");
if (ns_start) {
ns_start += strlen("<ns2:SessionID>");
char* ns_end = strstr(ns_start, "</ns2:SessionID>");
if (ns_end) {
size_t len_str = ns_end - ns_start;
static char session_id_temp[256];
if (len_str < sizeof(session_id_temp)) {
strncpy(session_id_temp, ns_start, len_str);
session_id_temp[len_str] = '\0';
session_id_str = trim_whitespace(session_id_temp);
size_t len;
if (parse_session_id(session_id_str, doc->V2G_Message.Header.SessionID.bytes, &len) == 0) {
doc->V2G_Message.Header.SessionID.bytesLen = len;
doc->V2G_Message_isUsed = 1;
}
}
}
}
}
// Check for CurrentDemandReq
if (strstr(xml_content, "<CurrentDemandReq>") || strstr(xml_content, "<ns3:CurrentDemandReq>")) {
doc->V2G_Message.Body.CurrentDemandReq_isUsed = 1;
init_iso1CurrentDemandReqType(&doc->V2G_Message.Body.CurrentDemandReq);
// Parse DC_EVStatus
char* ev_ready = find_tag_content(xml_content, "EVReady");
if (ev_ready) {
doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady = (strcmp(ev_ready, "true") == 0);
}
char* ev_error = find_tag_content(xml_content, "EVErrorCode");
if (ev_error) {
if (strcmp(ev_error, "NO_ERROR") == 0) {
doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode = 0;
} else {
doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode = atoi(ev_error);
}
}
char* ev_soc = find_tag_content(xml_content, "EVRESSSOC");
if (ev_soc) {
doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC = atoi(ev_soc);
}
// Parse EVTargetCurrent using bounded section approach
char* current_section = strstr(xml_content, "<EVTargetCurrent>");
if (!current_section) current_section = strstr(xml_content, "<ns3:EVTargetCurrent>");
if (current_section) {
char* current_end = strstr(current_section, "</EVTargetCurrent>");
if (!current_end) current_end = strstr(current_section, "</ns3:EVTargetCurrent>");
if (current_end) {
parse_physical_value_from_section(current_section, current_end, &doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent);
}
}
// Parse EVTargetVoltage using bounded section approach
char* voltage_section = strstr(xml_content, "<EVTargetVoltage>");
if (!voltage_section) voltage_section = strstr(xml_content, "<ns3:EVTargetVoltage>");
if (voltage_section) {
char* voltage_end = strstr(voltage_section, "</EVTargetVoltage>");
if (!voltage_end) voltage_end = strstr(voltage_section, "</ns3:EVTargetVoltage>");
if (voltage_end) {
parse_physical_value_from_section(voltage_section, voltage_end, &doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage);
}
}
// Parse ChargingComplete
char* charging_complete = find_tag_content(xml_content, "ChargingComplete");
if (charging_complete) {
doc->V2G_Message.Body.CurrentDemandReq.ChargingComplete = (strcmp(charging_complete, "true") == 0);
}
// Parse optional fields if present
if (strstr(xml_content, "<EVMaximumVoltageLimit>") || strstr(xml_content, "<ns3:EVMaximumVoltageLimit>")) {
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed = 1;
char* max_volt_section = strstr(xml_content, "<EVMaximumVoltageLimit>");
if (!max_volt_section) max_volt_section = strstr(xml_content, "<ns3:EVMaximumVoltageLimit>");
char* max_volt_end = strstr(max_volt_section, "</EVMaximumVoltageLimit>");
if (!max_volt_end) max_volt_end = strstr(max_volt_section, "</ns3:EVMaximumVoltageLimit>");
if (max_volt_section && max_volt_end) {
parse_physical_value_from_section(max_volt_section, max_volt_end, &doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit);
}
}
if (strstr(xml_content, "<EVMaximumCurrentLimit>") || strstr(xml_content, "<ns3:EVMaximumCurrentLimit>")) {
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed = 1;
char* max_curr_section = strstr(xml_content, "<EVMaximumCurrentLimit>");
if (!max_curr_section) max_curr_section = strstr(xml_content, "<ns3:EVMaximumCurrentLimit>");
char* max_curr_end = strstr(max_curr_section, "</EVMaximumCurrentLimit>");
if (!max_curr_end) max_curr_end = strstr(max_curr_section, "</ns3:EVMaximumCurrentLimit>");
if (max_curr_section && max_curr_end) {
parse_physical_value_from_section(max_curr_section, max_curr_end, &doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit);
}
}
if (strstr(xml_content, "<EVMaximumPowerLimit>") || strstr(xml_content, "<ns3:EVMaximumPowerLimit>")) {
doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed = 1;
char* max_power_section = strstr(xml_content, "<EVMaximumPowerLimit>");
if (!max_power_section) max_power_section = strstr(xml_content, "<ns3:EVMaximumPowerLimit>");
char* max_power_end = strstr(max_power_section, "</EVMaximumPowerLimit>");
if (!max_power_end) max_power_end = strstr(max_power_section, "</ns3:EVMaximumPowerLimit>");
if (max_power_section && max_power_end) {
parse_physical_value_from_section(max_power_section, max_power_end, &doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit);
}
}
// Parse BulkChargingComplete
char* bulk_charging_complete = find_tag_content(xml_content, "BulkChargingComplete");
if (bulk_charging_complete) {
doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed = 1;
doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete = (strcmp(bulk_charging_complete, "true") == 0);
}
// Parse remaining time fields
if (strstr(xml_content, "<RemainingTimeToFullSoC>") || strstr(xml_content, "<ns3:RemainingTimeToFullSoC>")) {
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed = 1;
char* time_section = strstr(xml_content, "<RemainingTimeToFullSoC>");
if (!time_section) time_section = strstr(xml_content, "<ns3:RemainingTimeToFullSoC>");
char* time_end = strstr(time_section, "</RemainingTimeToFullSoC>");
if (!time_end) time_end = strstr(time_section, "</ns3:RemainingTimeToFullSoC>");
if (time_section && time_end) {
parse_physical_value_from_section(time_section, time_end, &doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC);
}
}
if (strstr(xml_content, "<RemainingTimeToBulkSoC>") || strstr(xml_content, "<ns3:RemainingTimeToBulkSoC>")) {
doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed = 1;
char* bulk_time_section = strstr(xml_content, "<RemainingTimeToBulkSoC>");
if (!bulk_time_section) bulk_time_section = strstr(xml_content, "<ns3:RemainingTimeToBulkSoC>");
char* bulk_time_end = strstr(bulk_time_section, "</RemainingTimeToBulkSoC>");
if (!bulk_time_end) bulk_time_end = strstr(bulk_time_section, "</ns3:RemainingTimeToBulkSoC>");
if (bulk_time_section && bulk_time_end) {
parse_physical_value_from_section(bulk_time_section, bulk_time_end, &doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC);
}
}
return 0;
}
// Check for CurrentDemandRes
if (strstr(xml_content, "<CurrentDemandRes>") || strstr(xml_content, "<ns3:CurrentDemandRes>")) {
doc->V2G_Message.Body.CurrentDemandRes_isUsed = 1;
init_iso1CurrentDemandResType(&doc->V2G_Message.Body.CurrentDemandRes);
// Parse ResponseCode
char* response_code = find_tag_content(xml_content, "ResponseCode");
if (response_code) {
doc->V2G_Message.Body.CurrentDemandRes.ResponseCode = atoi(response_code);
}
// Parse DC_EVSEStatus
char* evse_isolation = find_tag_content_ns(xml_content, "EVSEIsolationStatus");
if (evse_isolation) {
doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus = atoi(evse_isolation);
doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus_isUsed = 1u;
}
char* evse_status = find_tag_content_ns(xml_content, "EVSEStatusCode");
if (evse_status) {
doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEStatusCode = atoi(evse_status);
}
// Parse EVSEPresentVoltage using bounded section approach
char* voltage_section = strstr(xml_content, "<EVSEPresentVoltage>");
if (!voltage_section) voltage_section = strstr(xml_content, "<ns3:EVSEPresentVoltage>");
if (voltage_section) {
char* voltage_end = strstr(voltage_section, "</EVSEPresentVoltage>");
if (!voltage_end) voltage_end = strstr(voltage_section, "</ns3:EVSEPresentVoltage>");
if (voltage_end) {
parse_physical_value_from_section(voltage_section, voltage_end, &doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage);
}
}
// Parse EVSEPresentCurrent using bounded section approach
char* current_section = strstr(xml_content, "<EVSEPresentCurrent>");
if (!current_section) current_section = strstr(xml_content, "<ns3:EVSEPresentCurrent>");
if (current_section) {
char* current_end = strstr(current_section, "</EVSEPresentCurrent>");
if (!current_end) current_end = strstr(current_section, "</ns3:EVSEPresentCurrent>");
if (current_end) {
parse_physical_value_from_section(current_section, current_end, &doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent);
}
}
// Parse limit achieved flags
char* current_limit = find_tag_content(xml_content, "EVSECurrentLimitAchieved");
if (current_limit) {
doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved = (strcmp(current_limit, "true") == 0);
}
char* voltage_limit = find_tag_content(xml_content, "EVSEVoltageLimitAchieved");
if (voltage_limit) {
doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved = (strcmp(voltage_limit, "true") == 0);
}
char* power_limit = find_tag_content(xml_content, "EVSEPowerLimitAchieved");
if (power_limit) {
doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved = (strcmp(power_limit, "true") == 0);
}
// Parse EVSEID
char* evseid = find_tag_content(xml_content, "EVSEID");
if (evseid) {
size_t len = strlen(evseid);
if (len < sizeof(doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters)) {
memcpy(doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters, evseid, len);
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.charactersLen = len;
}
}
// Parse SAScheduleTupleID
char* sa_schedule = find_tag_content(xml_content, "SAScheduleTupleID");
if (sa_schedule) {
doc->V2G_Message.Body.CurrentDemandRes.SAScheduleTupleID = atoi(sa_schedule);
}
return 0;
}
return -1; // Unsupported message type
}
// Helper function to read EXI file
int readEXIFile(char* file, uint8_t* buffer, size_t buffer_size, size_t *bytes_read) {
FILE *fp = fopen(file, "rb");
if (fp == NULL) {
return -1;
}
*bytes_read = fread(buffer, 1, buffer_size, fp);
fclose(fp);
if (*bytes_read == 0) {
return -1;
}
return 0;
}
// Helper functions for Wireshark XML output removed - using numeric values directly
void print_xml_header_wireshark() {
printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
printf("<ns1:V2G_Message xmlns:ns1=\"urn:iso:15118:2:2013:MsgDef\"");
printf(" xmlns:ns2=\"urn:iso:15118:2:2013:MsgHeader\"");
printf(" xmlns:ns3=\"urn:iso:15118:2:2013:MsgBody\"");
printf(" xmlns:ns4=\"urn:iso:15118:2:2013:MsgDataTypes\">\n");
}
void print_xml_footer_wireshark() {
printf("</ns1:V2G_Message>");
}
void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
print_xml_header_wireshark();
printf("<ns1:Header><ns2:SessionID>");
for(int i = 0; i < doc->V2G_Message.Header.SessionID.bytesLen; i++) {
printf("%02X", doc->V2G_Message.Header.SessionID.bytes[i]);
}
printf("</ns2:SessionID></ns1:Header>");
printf("<ns1:Body>");
if (doc->V2G_Message.Body.CurrentDemandRes_isUsed) {
printf("<ns3:CurrentDemandRes>");
printf("<ns3:ResponseCode>%d</ns3:ResponseCode>", doc->V2G_Message.Body.CurrentDemandRes.ResponseCode);
printf("<ns3:DC_EVSEStatus>");
printf("<ns4:EVSEIsolationStatus>%d</ns4:EVSEIsolationStatus>", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus);
printf("<ns4:EVSEStatusCode>%d</ns4:EVSEStatusCode>", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEStatusCode);
printf("</ns3:DC_EVSEStatus>");
printf("<ns3:EVSEPresentVoltage>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Value);
printf("</ns3:EVSEPresentVoltage>");
printf("<ns3:EVSEPresentCurrent>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Value);
printf("</ns3:EVSEPresentCurrent>");
printf("<ns3:EVSECurrentLimitAchieved>%s</ns3:EVSECurrentLimitAchieved>", doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved ? "true" : "false");
printf("<ns3:EVSEVoltageLimitAchieved>%s</ns3:EVSEVoltageLimitAchieved>", doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved ? "true" : "false");
printf("<ns3:EVSEPowerLimitAchieved>%s</ns3:EVSEPowerLimitAchieved>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved ? "true" : "false");
printf("<ns3:EVSEID>%.*s</ns3:EVSEID>",
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.charactersLen,
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters);
printf("<ns3:SAScheduleTupleID>%d</ns3:SAScheduleTupleID>", doc->V2G_Message.Body.CurrentDemandRes.SAScheduleTupleID);
printf("</ns3:CurrentDemandRes>");
}
else if (doc->V2G_Message.Body.CurrentDemandReq_isUsed) {
printf("<ns3:CurrentDemandReq>");
printf("<ns3:DC_EVStatus>");
printf("<ns4:EVReady>%s</ns4:EVReady>", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false");
printf("<ns4:EVErrorCode>%d</ns4:EVErrorCode>", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
printf("<ns4:EVRESSSOC>%d</ns4:EVRESSSOC>", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC);
printf("</ns3:DC_EVStatus>");
printf("<ns3:EVTargetCurrent>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
printf("</ns3:EVTargetCurrent>");
printf("<ns3:EVTargetVoltage>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
printf("</ns3:EVTargetVoltage>");
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) {
printf("<ns3:EVMaximumVoltageLimit>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Value);
printf("</ns3:EVMaximumVoltageLimit>");
}
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed) {
printf("<ns3:EVMaximumCurrentLimit>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Value);
printf("</ns3:EVMaximumCurrentLimit>");
}
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed) {
printf("<ns3:EVMaximumPowerLimit>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Value);
printf("</ns3:EVMaximumPowerLimit>");
}
if (doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed) {
printf("<ns3:BulkChargingComplete>%s</ns3:BulkChargingComplete>", doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete ? "true" : "false");
}
printf("<ns3:ChargingComplete>%s</ns3:ChargingComplete>", doc->V2G_Message.Body.CurrentDemandReq.ChargingComplete ? "true" : "false");
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed) {
printf("<ns3:RemainingTimeToFullSoC>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Value);
printf("</ns3:RemainingTimeToFullSoC>");
}
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed) {
printf("<ns3:RemainingTimeToBulkSoC>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Value);
printf("</ns3:RemainingTimeToBulkSoC>");
}
printf("</ns3:CurrentDemandReq>");
}
printf("</ns1:Body>");
print_xml_footer_wireshark();
}
void print_iso1_message(struct iso1EXIDocument* doc) {
printf("=== ISO 15118-2 V2G Message Analysis ===\n");
printf("Message Type: ISO1 (2013)\n");
printf("V2G_Message_isUsed: %s\n", doc->V2G_Message_isUsed ? "true" : "false");
if (doc->V2G_Message_isUsed) {
printf("\n--- Header ---\n");
printf("SessionID: ");
for(int i = 0; i < doc->V2G_Message.Header.SessionID.bytesLen; i++) {
printf("%02X", doc->V2G_Message.Header.SessionID.bytes[i]);
}
printf(" (");
for(int i = 0; i < doc->V2G_Message.Header.SessionID.bytesLen; i++) {
if (doc->V2G_Message.Header.SessionID.bytes[i] >= 32 && doc->V2G_Message.Header.SessionID.bytes[i] <= 126) {
printf("%c", doc->V2G_Message.Header.SessionID.bytes[i]);
} else {
printf(".");
}
}
printf(")\n");
printf("\n--- Body ---\n");
if (doc->V2G_Message.Body.CurrentDemandRes_isUsed) {
printf("Message Type: CurrentDemandRes\n");
printf("ResponseCode: %d\n", doc->V2G_Message.Body.CurrentDemandRes.ResponseCode);
printf("\nDC_EVSEStatus:\n");
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("\nEVSEPresentVoltage:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Value);
printf("\nEVSEPresentCurrent:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Value);
printf("\nLimit Status:\n");
printf(" CurrentLimitAchieved: %s\n", doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved ? "true" : "false");
printf(" VoltageLimitAchieved: %s\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved ? "true" : "false");
printf(" PowerLimitAchieved: %s\n", doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved ? "true" : "false");
printf("\nEVSEID: %.*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);
}
else if (doc->V2G_Message.Body.CurrentDemandReq_isUsed) {
printf("Message Type: CurrentDemandReq\n");
printf("\nDC_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("\nEVTargetCurrent:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
printf("\nEVTargetVoltage:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) {
printf("\nEVMaximumVoltageLimit:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Value);
}
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed) {
printf("\nEVMaximumCurrentLimit:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Value);
}
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed) {
printf("\nEVMaximumPowerLimit:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Value);
}
if (doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed) {
printf("\nBulkChargingComplete: %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("\nRemainingTimeToFullSoC:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Value);
}
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed) {
printf("\nRemainingTimeToBulkSoC:\n");
printf(" Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Multiplier);
printf(" Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit);
printf(" Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Value);
}
}
else {
printf("Message Type: Other message type (not fully supported)\n");
}
}
printf("\n");
}
int main(int argc, char *argv[]) {
int xml_mode = 0;
int encode_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 if (argc == 3 && strcmp(argv[1], "-encode") == 0) {
encode_mode = 1;
filename = argv[2];
} else {
printf("Usage: %s [-decode|-encode] input_file\\n", argv[0]);
printf("Enhanced EXI viewer with XML conversion capabilities\\n");
printf(" -decode Convert EXI to Wireshark-style XML format\\n");
printf(" -encode Convert XML to EXI format\\n");
printf(" (default) Analyze EXI with detailed output\\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);
// Handle encode mode (XML to EXI)
if (encode_mode) {
// Read XML file
FILE* xml_file = fopen(filename, "r");
if (!xml_file) {
printf("Error opening XML file: %s\\n", filename);
return -1;
}
// Read entire XML content
fseek(xml_file, 0, SEEK_END);
long xml_size = ftell(xml_file);
fseek(xml_file, 0, SEEK_SET);
char* xml_content = malloc(xml_size + 1);
if (!xml_content) {
printf("Error allocating memory for XML content\\n");
fclose(xml_file);
return -1;
}
fread(xml_content, 1, xml_size, xml_file);
xml_content[xml_size] = '\0';
fclose(xml_file);
// Parse XML to ISO1 document structure
if (parse_xml_to_iso1(xml_content, &iso1Doc) != 0) {
printf("Error parsing XML file - no supported message type found\\n");
free(xml_content);
return -1;
}
// Show encoding info only when redirecting to file (keeps terminal output clean)
struct stat stat_buf_debug;
int is_file_redirect = (fstat(fileno(stdout), &stat_buf_debug) == 0 && S_ISREG(stat_buf_debug.st_mode));
if (is_file_redirect) {
fprintf(stderr, "XML parsing successful\\n");
fprintf(stderr, "SessionID length: %d\\n", iso1Doc.V2G_Message.Header.SessionID.bytesLen);
fprintf(stderr, "CurrentDemandReq_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq_isUsed ? "true" : "false");
}
free(xml_content);
// Encode to EXI
pos = 0;
stream.size = BUFFER_SIZE;
stream.data = buffer;
stream.pos = &pos;
stream.buffer = 0;
stream.capacity = 0;
errn = encode_iso1ExiDocument(&stream, &iso1Doc);
if (errn != 0) {
printf("Error encoding to EXI (error: %d)\\n", errn);
return -1;
}
// Check if output is redirected (for Windows/MINGW compatibility)
// Use environment variable approach or check for redirection
char* term = getenv("TERM");
int use_hex_output = (term != NULL) || isatty(fileno(stdout));
// Also check if user specifically wants binary output by checking for redirection
struct stat stat_buf;
if (fstat(fileno(stdout), &stat_buf) == 0 && S_ISREG(stat_buf.st_mode)) {
use_hex_output = 0; // Regular file, use binary
}
if (use_hex_output) {
// Terminal output: show hex string only
for(size_t i = 0; i < pos; i++) {
printf("%02X", buffer[i]);
}
printf("\n");
} else {
// Redirected output: write binary data
fwrite(buffer, 1, pos, stdout);
}
return 0;
}
// Read EXI file for decode/analysis mode
errn = readEXIFile(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");
// Analyze data structure and extract EXI body
analyze_data_structure(buffer, pos);
}
// Extract EXI body from V2G Transfer Protocol data
uint8_t exi_buffer[BUFFER_SIZE];
size_t exi_size = extract_exi_body(buffer, pos, exi_buffer, BUFFER_SIZE);
if (exi_size != pos) {
if (!xml_mode) {
printf("EXI body extracted: %zu bytes (was %zu bytes)\\n", exi_size, pos);
printf("EXI hex data: ");
for(size_t i = 0; i < (exi_size > 32 ? 32 : exi_size); i++) {
printf("%02X ", exi_buffer[i]);
}
if (exi_size > 32) printf("...");
printf("\\n\\n");
}
// Use extracted EXI data
memcpy(buffer, exi_buffer, exi_size);
pos = exi_size;
}
// 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_wireshark(&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");
if (xml_mode) {
printf("ISO2 XML output not implemented for Wireshark format\\n");
} else {
printf("ISO2 analysis not fully implemented\\n");
}
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;
}