 c6dc6735fa
			
		
	
	c6dc6735fa
	
	
	
		
			
			- Reorganize project structure: Port/ → DotNet/, VC/, C++/ - Add comprehensive cross-platform build automation - Windows: build_all.bat, build.bat files for all components - Linux/macOS: build_all.sh, build.sh files for all components - Update all build scripts with correct folder paths - Create test automation scripts (test_all.bat/sh) - Update documentation to reflect new structure - Maintain 100% roundtrip accuracy for test5.exi (pure EXI) - Support both Windows MSBuild and Linux GCC compilation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			1500 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1500 lines
		
	
	
		
			71 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <ctype.h>
 | |
| #ifdef _WIN32
 | |
|     #include <io.h>
 | |
|     #include <direct.h>
 | |
|     #include <sys/stat.h>
 | |
|     #define F_OK 0
 | |
|     #define access _access
 | |
|     #define fstat _fstat
 | |
|     #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
 | |
| #else
 | |
|     #include <unistd.h>
 | |
|     #include <sys/stat.h>
 | |
| #endif
 | |
| 
 | |
| /* 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"
 | |
| 
 | |
| /** EXI Debug mode */
 | |
| int EXI_DEBUG_MODE = 0;
 | |
| 
 | |
| #define DEBUG_PRINTF(x) do { if (EXI_DEBUG_MODE) printf x; } while (0)
 | |
| 
 | |
| #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>")) {
 | |
|         // Body 구조체 초기화 (모든 메시지 타입 플래그를 0으로 설정)
 | |
|         init_iso1BodyType(&doc->V2G_Message.Body);
 | |
|         
 | |
|         // 오직 CurrentDemandReq만 활성화
 | |
|         doc->V2G_Message.Body.CurrentDemandReq_isUsed = 1;
 | |
|         
 | |
|         // Initialize CurrentDemandReq structure completely
 | |
|         init_iso1CurrentDemandReqType(&doc->V2G_Message.Body.CurrentDemandReq);
 | |
|         
 | |
|         // Set all optional fields to NOT USED by default
 | |
|         doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed = 0;
 | |
|         doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed = 0; 
 | |
|         doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed = 0;
 | |
|         doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed = 0;
 | |
|         doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed = 0;
 | |
|         doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed = 0;
 | |
|         
 | |
|         // 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>")) {
 | |
|         // Body 구조체 초기화 (모든 메시지 타입 플래그를 0으로 설정)
 | |
|         init_iso1BodyType(&doc->V2G_Message.Body);
 | |
|         
 | |
|         // 오직 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
 | |
| }
 | |
| 
 | |
| // 구조체 전체 내용을 텍스트 파일로 덤프하는 함수
 | |
| void dump_iso1_document_to_file(const struct iso1EXIDocument* doc, const char* filename) {
 | |
|     FILE* fp = fopen(filename, "w");
 | |
|     if (!fp) {
 | |
|         printf("Error: Cannot create file %s\n", filename);
 | |
|         return;
 | |
|     }
 | |
|     
 | |
|     fprintf(fp, "=== ISO1 EXI Document Structure Dump ===\n\n");
 | |
|     
 | |
|     // Document level
 | |
|     fprintf(fp, "V2G_Message_isUsed: %u\n", doc->V2G_Message_isUsed);
 | |
|     
 | |
|     if (doc->V2G_Message_isUsed) {
 | |
|         // Header
 | |
|         fprintf(fp, "\n--- Header ---\n");
 | |
|         fprintf(fp, "SessionID.bytesLen: %u\n", doc->V2G_Message.Header.SessionID.bytesLen);
 | |
|         fprintf(fp, "SessionID.bytes: ");
 | |
|         for (int i = 0; i < doc->V2G_Message.Header.SessionID.bytesLen; i++) {
 | |
|             fprintf(fp, "%02X", doc->V2G_Message.Header.SessionID.bytes[i]);
 | |
|         }
 | |
|         fprintf(fp, "\n");
 | |
|         fprintf(fp, "Notification_isUsed: %u\n", doc->V2G_Message.Header.Notification_isUsed);
 | |
|         fprintf(fp, "Signature_isUsed: %u\n", doc->V2G_Message.Header.Signature_isUsed);
 | |
|         
 | |
|         // Body - All message type flags
 | |
|         fprintf(fp, "\n--- Body Message Type Flags ---\n");
 | |
|         fprintf(fp, "AuthorizationReq_isUsed: %u\n", doc->V2G_Message.Body.AuthorizationReq_isUsed);
 | |
|         fprintf(fp, "AuthorizationRes_isUsed: %u\n", doc->V2G_Message.Body.AuthorizationRes_isUsed);
 | |
|         fprintf(fp, "BodyElement_isUsed: %u\n", doc->V2G_Message.Body.BodyElement_isUsed);
 | |
|         fprintf(fp, "CableCheckReq_isUsed: %u\n", doc->V2G_Message.Body.CableCheckReq_isUsed);
 | |
|         fprintf(fp, "CableCheckRes_isUsed: %u\n", doc->V2G_Message.Body.CableCheckRes_isUsed);
 | |
|         fprintf(fp, "CertificateInstallationReq_isUsed: %u\n", doc->V2G_Message.Body.CertificateInstallationReq_isUsed);
 | |
|         fprintf(fp, "CertificateInstallationRes_isUsed: %u\n", doc->V2G_Message.Body.CertificateInstallationRes_isUsed);
 | |
|         fprintf(fp, "CertificateUpdateReq_isUsed: %u\n", doc->V2G_Message.Body.CertificateUpdateReq_isUsed);
 | |
|         fprintf(fp, "CertificateUpdateRes_isUsed: %u\n", doc->V2G_Message.Body.CertificateUpdateRes_isUsed);
 | |
|         fprintf(fp, "ChargeParameterDiscoveryReq_isUsed: %u\n", doc->V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed);
 | |
|         fprintf(fp, "ChargeParameterDiscoveryRes_isUsed: %u\n", doc->V2G_Message.Body.ChargeParameterDiscoveryRes_isUsed);
 | |
|         fprintf(fp, "ChargingStatusReq_isUsed: %u\n", doc->V2G_Message.Body.ChargingStatusReq_isUsed);
 | |
|         fprintf(fp, "ChargingStatusRes_isUsed: %u\n", doc->V2G_Message.Body.ChargingStatusRes_isUsed);
 | |
|         fprintf(fp, "CurrentDemandReq_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandReq_isUsed);
 | |
|         fprintf(fp, "CurrentDemandRes_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandRes_isUsed);
 | |
|         fprintf(fp, "MeteringReceiptReq_isUsed: %u\n", doc->V2G_Message.Body.MeteringReceiptReq_isUsed);
 | |
|         fprintf(fp, "MeteringReceiptRes_isUsed: %u\n", doc->V2G_Message.Body.MeteringReceiptRes_isUsed);
 | |
|         fprintf(fp, "PaymentDetailsReq_isUsed: %u\n", doc->V2G_Message.Body.PaymentDetailsReq_isUsed);
 | |
|         fprintf(fp, "PaymentDetailsRes_isUsed: %u\n", doc->V2G_Message.Body.PaymentDetailsRes_isUsed);
 | |
|         fprintf(fp, "PaymentServiceSelectionReq_isUsed: %u\n", doc->V2G_Message.Body.PaymentServiceSelectionReq_isUsed);
 | |
|         fprintf(fp, "PaymentServiceSelectionRes_isUsed: %u\n", doc->V2G_Message.Body.PaymentServiceSelectionRes_isUsed);
 | |
|         fprintf(fp, "PowerDeliveryReq_isUsed: %u\n", doc->V2G_Message.Body.PowerDeliveryReq_isUsed);
 | |
|         fprintf(fp, "PowerDeliveryRes_isUsed: %u\n", doc->V2G_Message.Body.PowerDeliveryRes_isUsed);
 | |
|         fprintf(fp, "PreChargeReq_isUsed: %u\n", doc->V2G_Message.Body.PreChargeReq_isUsed);
 | |
|         fprintf(fp, "PreChargeRes_isUsed: %u\n", doc->V2G_Message.Body.PreChargeRes_isUsed);
 | |
|         fprintf(fp, "ServiceDetailReq_isUsed: %u\n", doc->V2G_Message.Body.ServiceDetailReq_isUsed);
 | |
|         fprintf(fp, "ServiceDetailRes_isUsed: %u\n", doc->V2G_Message.Body.ServiceDetailRes_isUsed);
 | |
|         fprintf(fp, "ServiceDiscoveryReq_isUsed: %u\n", doc->V2G_Message.Body.ServiceDiscoveryReq_isUsed);
 | |
|         fprintf(fp, "ServiceDiscoveryRes_isUsed: %u\n", doc->V2G_Message.Body.ServiceDiscoveryRes_isUsed);
 | |
|         fprintf(fp, "SessionSetupReq_isUsed: %u\n", doc->V2G_Message.Body.SessionSetupReq_isUsed);
 | |
|         fprintf(fp, "SessionSetupRes_isUsed: %u\n", doc->V2G_Message.Body.SessionSetupRes_isUsed);
 | |
|         fprintf(fp, "SessionStopReq_isUsed: %u\n", doc->V2G_Message.Body.SessionStopReq_isUsed);
 | |
|         fprintf(fp, "SessionStopRes_isUsed: %u\n", doc->V2G_Message.Body.SessionStopRes_isUsed);
 | |
|         fprintf(fp, "WeldingDetectionReq_isUsed: %u\n", doc->V2G_Message.Body.WeldingDetectionReq_isUsed);
 | |
|         fprintf(fp, "WeldingDetectionRes_isUsed: %u\n", doc->V2G_Message.Body.WeldingDetectionRes_isUsed);
 | |
|         
 | |
|         // CurrentDemandReq 상세 정보
 | |
|         if (doc->V2G_Message.Body.CurrentDemandReq_isUsed) {
 | |
|             fprintf(fp, "\n--- CurrentDemandReq Details ---\n");
 | |
|             
 | |
|             // DC_EVStatus
 | |
|             fprintf(fp, "DC_EVStatus.EVReady: %u\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady);
 | |
|             fprintf(fp, "DC_EVStatus.EVErrorCode: %d\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
 | |
|             fprintf(fp, "DC_EVStatus.EVRESSSOC: %d\n", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC);
 | |
|             
 | |
|             // EVTargetCurrent
 | |
|             fprintf(fp, "EVTargetCurrent.Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier);
 | |
|             fprintf(fp, "EVTargetCurrent.Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit);
 | |
|             fprintf(fp, "EVTargetCurrent.Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
 | |
|             
 | |
|             // 선택적 필드들
 | |
|             fprintf(fp, "EVMaximumVoltageLimit_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed);
 | |
|             if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) {
 | |
|                 fprintf(fp, "EVMaximumVoltageLimit.Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier);
 | |
|                 fprintf(fp, "EVMaximumVoltageLimit.Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit);
 | |
|                 fprintf(fp, "EVMaximumVoltageLimit.Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Value);
 | |
|             }
 | |
|             
 | |
|             fprintf(fp, "EVMaximumCurrentLimit_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed);
 | |
|             if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed) {
 | |
|                 fprintf(fp, "EVMaximumCurrentLimit.Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Multiplier);
 | |
|                 fprintf(fp, "EVMaximumCurrentLimit.Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit);
 | |
|                 fprintf(fp, "EVMaximumCurrentLimit.Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Value);
 | |
|             }
 | |
|             
 | |
|             fprintf(fp, "EVMaximumPowerLimit_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed);
 | |
|             if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed) {
 | |
|                 fprintf(fp, "EVMaximumPowerLimit.Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Multiplier);
 | |
|                 fprintf(fp, "EVMaximumPowerLimit.Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit);
 | |
|                 fprintf(fp, "EVMaximumPowerLimit.Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Value);
 | |
|             }
 | |
|             
 | |
|             fprintf(fp, "BulkChargingComplete_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed);
 | |
|             if (doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed) {
 | |
|                 fprintf(fp, "BulkChargingComplete: %u\n", doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete);
 | |
|             }
 | |
|             
 | |
|             // ChargingComplete (필수 필드)
 | |
|             fprintf(fp, "ChargingComplete: %u\n", doc->V2G_Message.Body.CurrentDemandReq.ChargingComplete);
 | |
|             
 | |
|             fprintf(fp, "RemainingTimeToFullSoC_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed);
 | |
|             if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed) {
 | |
|                 fprintf(fp, "RemainingTimeToFullSoC.Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Multiplier);
 | |
|                 fprintf(fp, "RemainingTimeToFullSoC.Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit);
 | |
|                 fprintf(fp, "RemainingTimeToFullSoC.Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Value);
 | |
|             }
 | |
|             
 | |
|             fprintf(fp, "RemainingTimeToBulkSoC_isUsed: %u\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed);
 | |
|             if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed) {
 | |
|                 fprintf(fp, "RemainingTimeToBulkSoC.Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Multiplier);
 | |
|                 fprintf(fp, "RemainingTimeToBulkSoC.Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit);
 | |
|                 fprintf(fp, "RemainingTimeToBulkSoC.Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Value);
 | |
|             }
 | |
|             
 | |
|             // EVTargetVoltage (필수 필드)
 | |
|             fprintf(fp, "EVTargetVoltage.Multiplier: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier);
 | |
|             fprintf(fp, "EVTargetVoltage.Unit: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
 | |
|             fprintf(fp, "EVTargetVoltage.Value: %d\n", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     fclose(fp);
 | |
|     printf("✓ Structure dump saved to %s\n", filename);
 | |
| }
 | |
| 
 | |
| // Helper function to convert hex string to binary
 | |
| int hexStringToBinary(const char* hex_string, uint8_t* buffer, size_t buffer_size, size_t* bytes_written) {
 | |
|     size_t hex_len = strlen(hex_string);
 | |
|     
 | |
|     // Remove any whitespace and newlines
 | |
|     char* cleaned_hex = malloc(hex_len + 1);
 | |
|     if (!cleaned_hex) {
 | |
|         return -1;
 | |
|     }
 | |
|     
 | |
|     size_t clean_pos = 0;
 | |
|     for (size_t i = 0; i < hex_len; i++) {
 | |
|         char c = hex_string[i];
 | |
|         if (isxdigit(c)) {
 | |
|             cleaned_hex[clean_pos++] = toupper(c);
 | |
|         }
 | |
|     }
 | |
|     cleaned_hex[clean_pos] = '\0';
 | |
|     
 | |
|     // Check if hex string length is even
 | |
|     if (clean_pos % 2 != 0) {
 | |
|         free(cleaned_hex);
 | |
|         return -1; // Invalid hex string length
 | |
|     }
 | |
|     
 | |
|     size_t binary_len = clean_pos / 2;
 | |
|     if (binary_len > buffer_size) {
 | |
|         free(cleaned_hex);
 | |
|         return -2; // Buffer too small
 | |
|     }
 | |
|     
 | |
|     // Convert hex pairs to bytes
 | |
|     for (size_t i = 0; i < binary_len; i++) {
 | |
|         char hex_byte[3] = {cleaned_hex[i*2], cleaned_hex[i*2+1], '\0'};
 | |
|         unsigned int byte_val;
 | |
|         if (sscanf(hex_byte, "%X", &byte_val) != 1) {
 | |
|             free(cleaned_hex);
 | |
|             return -3; // Invalid hex character
 | |
|         }
 | |
|         buffer[i] = (uint8_t)byte_val;
 | |
|     }
 | |
|     
 | |
|     *bytes_written = binary_len;
 | |
|     free(cleaned_hex);
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| // 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>");
 | |
|         
 | |
|         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>");
 | |
|         }
 | |
|         
 | |
|         // EVTargetVoltage must come last according to EXI grammar
 | |
|         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>");
 | |
|         
 | |
|         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[]) {
 | |
|     printf("DEBUG: argc=%d\n", argc);
 | |
|     int xml_mode = 0;
 | |
|     int encode_mode = 0;
 | |
|     char *filename = NULL;
 | |
|     int arg_index = 1;
 | |
|     
 | |
|     // Check for debug option first
 | |
|     if (argc >= 2 && strcmp(argv[1], "-debug") == 0) {
 | |
|         EXI_DEBUG_MODE = 1;
 | |
|         arg_index = 2;
 | |
|         argc--; // Shift arguments
 | |
|         printf("Debug mode enabled: detailed bit-level encoding/decoding output\n");
 | |
|     }
 | |
|     
 | |
|     if (argc == 2) {
 | |
|         if (strcmp(argv[arg_index], "-encode") == 0) {
 | |
|             // Special case: -encode without filename reads from stdin
 | |
|             encode_mode = 1;
 | |
|             filename = "-";  // Use "-" to indicate stdin
 | |
|         } else if (strcmp(argv[arg_index], "-decode") == 0) {
 | |
|             // Special case: -decode without filename reads from stdin
 | |
|             xml_mode = 1;
 | |
|             filename = "-";  // Use "-" to indicate stdin
 | |
|         } else {
 | |
|             filename = argv[arg_index];
 | |
|         }
 | |
|     } else if (argc == 3 && strcmp(argv[arg_index], "-decode") == 0) {
 | |
|         xml_mode = 1;
 | |
|         filename = argv[arg_index + 1];
 | |
|     } else if (argc == 3 && strcmp(argv[arg_index], "-encode") == 0) {
 | |
|         encode_mode = 1;
 | |
|         filename = argv[arg_index + 1];
 | |
|     } else {
 | |
|         printf("Usage: %s [-debug] [-decode|-encode] input_file\\n", argv[0]);
 | |
|         printf("       %s [-debug] -encode          (read XML from stdin)\\n", argv[0]);
 | |
|         printf("       %s [-debug] -decode          (read hex string from stdin)\\n", argv[0]);
 | |
|         printf("Enhanced EXI viewer with XML conversion capabilities\\n");
 | |
|         printf("  -debug       Enable detailed bit-level encoding/decoding output\\n");
 | |
|         printf("  -decode      Convert EXI to Wireshark-style XML format\\n");
 | |
|         printf("  -decode      Read hex string from stdin (echo hex | %s -decode)\\n", argv[0]);
 | |
|         printf("  -encode      Convert XML to EXI format\\n");
 | |
|         printf("  -encode      Read XML from stdin (type file.xml | %s -encode)\\n", argv[0]);
 | |
|         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) {
 | |
|         fprintf(stderr, "DEBUG: Entering encode mode\n");
 | |
|         FILE* xml_file;
 | |
|         char* xml_content;
 | |
|         long xml_size;
 | |
|         
 | |
|         // Check if reading from stdin or file
 | |
|         if (strcmp(filename, "-") == 0) {
 | |
|             // Read from stdin
 | |
|             xml_file = stdin;
 | |
|             
 | |
|             // Read all content from stdin
 | |
|             size_t capacity = 4096;
 | |
|             xml_content = malloc(capacity);
 | |
|             if (!xml_content) {
 | |
|                 printf("Error allocating memory for XML content\\n");
 | |
|                 return -1;
 | |
|             }
 | |
|             
 | |
|             xml_size = 0;
 | |
|             int ch;
 | |
|             while ((ch = fgetc(stdin)) != EOF) {
 | |
|                 if (xml_size >= capacity - 1) {
 | |
|                     capacity *= 2;
 | |
|                     xml_content = realloc(xml_content, capacity);
 | |
|                     if (!xml_content) {
 | |
|                         printf("Error reallocating memory for XML content\\n");
 | |
|                         return -1;
 | |
|                     }
 | |
|                 }
 | |
|                 xml_content[xml_size++] = ch;
 | |
|             }
 | |
|             xml_content[xml_size] = '\0';
 | |
|         } else {
 | |
|             // Read XML 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);
 | |
|             xml_size = ftell(xml_file);
 | |
|             fseek(xml_file, 0, SEEK_SET);
 | |
|             
 | |
|             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
 | |
|         fprintf(stderr, "DEBUG: About to parse XML content\n");
 | |
|         int parse_result = parse_xml_to_iso1(xml_content, &iso1Doc);
 | |
|         fprintf(stderr, "DEBUG: XML parse result: %d\n", parse_result);
 | |
|         if (parse_result != 0) {
 | |
|             printf("Error parsing XML file - no supported message type found\\n");
 | |
|             free(xml_content);
 | |
|             return -1;
 | |
|         }
 | |
|         
 | |
|         // XML parsing debug info to stderr (optional)
 | |
|         // fprintf(stderr, "=== XML Parsing Debug ===\\n");
 | |
|         // fprintf(stderr, "V2G_Message_isUsed: %s\\n", iso1Doc.V2G_Message_isUsed ? "true" : "false");
 | |
|         // 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");
 | |
|         
 | |
|         // Debug output disabled for clean hex-only output
 | |
|         
 | |
|         if (iso1Doc.V2G_Message.Body.CurrentDemandReq_isUsed) {
 | |
|             fprintf(stderr, "EVReady: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false");
 | |
|             fprintf(stderr, "EVErrorCode: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
 | |
|             fprintf(stderr, "EVRESSSOC: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC);
 | |
|             fprintf(stderr, "EVTargetCurrent: M=%d, U=%d, V=%d\\n", 
 | |
|                    iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier,
 | |
|                    iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit,
 | |
|                    iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
 | |
|             fprintf(stderr, "EVTargetVoltage: M=%d, U=%d, V=%d\\n", 
 | |
|                    iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier,
 | |
|                    iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit,
 | |
|                    iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
 | |
|         }
 | |
|         
 | |
|         
 | |
|         free(xml_content);
 | |
|         
 | |
|         // Encode to EXI
 | |
|         pos = 0;
 | |
|         stream.size = BUFFER_SIZE;
 | |
|         stream.data = buffer;
 | |
|         stream.pos = &pos;
 | |
|         stream.buffer = 0;
 | |
|         stream.capacity = 0;
 | |
|         
 | |
|         // 구조체 덤프 (디버그용, 필요시 활성화)
 | |
|         fprintf(stderr, "DEBUG: About to dump structure to temp/struct_xml.txt\n");
 | |
|         dump_iso1_document_to_file(&iso1Doc, "temp/struct_xml.txt");
 | |
|         fprintf(stderr, "DEBUG: Structure dump completed\n");
 | |
|         
 | |
|         errn = encode_iso1ExiDocument(&stream, &iso1Doc);
 | |
|         
 | |
|         if (errn != 0) {
 | |
|             fprintf(stderr, "Error encoding to EXI (error: %d)\\n", errn);
 | |
|             return -1;
 | |
|         }
 | |
|         
 | |
|         // Check if output is redirected
 | |
|         struct stat stat_buf;
 | |
|         int is_redirected = (fstat(fileno(stdout), &stat_buf) == 0 && S_ISREG(stat_buf.st_mode));
 | |
|         
 | |
|         if (is_redirected) {
 | |
|             // Redirected output: write binary data
 | |
|             fwrite(buffer, 1, pos, stdout);
 | |
|         } else {
 | |
|             // Terminal output: show hex string only (like XML decode mode)
 | |
|             for(size_t i = 0; i < pos; i++) {
 | |
|                 printf("%02X", buffer[i]);
 | |
|             }
 | |
|             printf("\n");
 | |
|         }
 | |
|         return 0;
 | |
|     }
 | |
|     
 | |
|     // Read EXI data for decode/analysis mode
 | |
|     if (strcmp(filename, "-") == 0 && xml_mode) {
 | |
|         // Read hex string from stdin for decode mode
 | |
|         char hex_input[BUFFER_SIZE * 2 + 1];  // Hex string can be up to 2x binary size
 | |
|         size_t hex_len = 0;
 | |
|         
 | |
|         // Read hex string from stdin
 | |
|         int ch;
 | |
|         while ((ch = fgetc(stdin)) != EOF && hex_len < sizeof(hex_input) - 1) {
 | |
|             hex_input[hex_len++] = ch;
 | |
|         }
 | |
|         hex_input[hex_len] = '\0';
 | |
|         
 | |
|         // Convert hex string to binary
 | |
|         errn = hexStringToBinary(hex_input, buffer, BUFFER_SIZE, &pos);
 | |
|         if (errn != 0) {
 | |
|             printf("Error converting hex string to binary (error: %d)\\n", errn);
 | |
|             printf("Make sure input is a valid hex string like: 8098021050908C0C0C0E0C50E001993206002040C40C203030C014000603DA98B3E60C0008\\n");
 | |
|             return -1;
 | |
|         }
 | |
|         
 | |
|         if (!xml_mode) {
 | |
|             printf("Hex string from stdin (%zu bytes converted to %zu binary bytes)\\n", hex_len, pos);
 | |
|         }
 | |
|     } else {
 | |
|         // Read from file
 | |
|         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);
 | |
|             
 | |
|             // Compare with expected structure
 | |
|             printf("\\n=== Original EXI Structure Debug ===\\n");
 | |
|             printf("V2G_Message_isUsed: %s\\n", iso1Doc.V2G_Message_isUsed ? "true" : "false");
 | |
|             printf("SessionID length: %d\\n", iso1Doc.V2G_Message.Header.SessionID.bytesLen);
 | |
|             printf("CurrentDemandReq_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq_isUsed ? "true" : "false");
 | |
|             
 | |
|             if (iso1Doc.V2G_Message.Body.CurrentDemandReq_isUsed) {
 | |
|                 printf("EVReady: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false");
 | |
|                 printf("EVErrorCode: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
 | |
|                 printf("EVRESSSOC: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC);
 | |
|                 printf("EVTargetCurrent: M=%d, U=%d, V=%d\\n", 
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier,
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit,
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
 | |
|                 printf("EVMaximumVoltageLimit_isUsed: %s\\n", 
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed ? "true" : "false");
 | |
|                 printf("EVMaximumCurrentLimit_isUsed: %s\\n", 
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed ? "true" : "false");
 | |
|                 printf("EVMaximumPowerLimit_isUsed: %s\\n", 
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed ? "true" : "false");
 | |
|                 printf("BulkChargingComplete_isUsed: %s\\n", 
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed ? "true" : "false");
 | |
|                 printf("RemainingTimeToFullSoC_isUsed: %s\\n", 
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed ? "true" : "false");
 | |
|                 printf("RemainingTimeToBulkSoC_isUsed: %s\\n", 
 | |
|                        iso1Doc.V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed ? "true" : "false");
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         // 원본 EXI 디코딩 후 구조체 덤프 (분석 모드일 때만)
 | |
|         if (!xml_mode) {
 | |
|             dump_iso1_document_to_file(&iso1Doc, "struct_exi.txt");
 | |
|         }
 | |
|         
 | |
|         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;
 | |
| } |