#include #include #include #include #ifdef _WIN32 #include #include #include #define F_OK 0 #define access _access #define fstat _fstat #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #else #include #include #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), "", 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), "", 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), "", 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), "", 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, ""); if (ns_start) { ns_start += strlen(""); char* ns_end = strstr(ns_start, ""); 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, "") || strstr(xml_content, "")) { // 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, ""); if (!current_section) current_section = strstr(xml_content, ""); if (current_section) { char* current_end = strstr(current_section, ""); if (!current_end) current_end = strstr(current_section, ""); 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, ""); if (!voltage_section) voltage_section = strstr(xml_content, ""); if (voltage_section) { char* voltage_end = strstr(voltage_section, ""); if (!voltage_end) voltage_end = strstr(voltage_section, ""); 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, "") || strstr(xml_content, "")) { doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed = 1; char* max_volt_section = strstr(xml_content, ""); if (!max_volt_section) max_volt_section = strstr(xml_content, ""); char* max_volt_end = strstr(max_volt_section, ""); if (!max_volt_end) max_volt_end = strstr(max_volt_section, ""); 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, "") || strstr(xml_content, "")) { doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed = 1; char* max_curr_section = strstr(xml_content, ""); if (!max_curr_section) max_curr_section = strstr(xml_content, ""); char* max_curr_end = strstr(max_curr_section, ""); if (!max_curr_end) max_curr_end = strstr(max_curr_section, ""); 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, "") || strstr(xml_content, "")) { doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed = 1; char* max_power_section = strstr(xml_content, ""); if (!max_power_section) max_power_section = strstr(xml_content, ""); char* max_power_end = strstr(max_power_section, ""); if (!max_power_end) max_power_end = strstr(max_power_section, ""); 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, "") || strstr(xml_content, "")) { doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed = 1; char* time_section = strstr(xml_content, ""); if (!time_section) time_section = strstr(xml_content, ""); char* time_end = strstr(time_section, ""); if (!time_end) time_end = strstr(time_section, ""); if (time_section && time_end) { parse_physical_value_from_section(time_section, time_end, &doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC); } } if (strstr(xml_content, "") || strstr(xml_content, "")) { doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed = 1; char* bulk_time_section = strstr(xml_content, ""); if (!bulk_time_section) bulk_time_section = strstr(xml_content, ""); char* bulk_time_end = strstr(bulk_time_section, ""); if (!bulk_time_end) bulk_time_end = strstr(bulk_time_section, ""); 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, "") || strstr(xml_content, "")) { // 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, ""); if (!voltage_section) voltage_section = strstr(xml_content, ""); if (voltage_section) { char* voltage_end = strstr(voltage_section, ""); if (!voltage_end) voltage_end = strstr(voltage_section, ""); 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, ""); if (!current_section) current_section = strstr(xml_content, ""); if (current_section) { char* current_end = strstr(current_section, ""); if (!current_end) current_end = strstr(current_section, ""); 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); DEBUG_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("\n"); printf("\n"); } void print_xml_footer_wireshark() { printf(""); } void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) { print_xml_header_wireshark(); printf(""); for(int i = 0; i < doc->V2G_Message.Header.SessionID.bytesLen; i++) { printf("%02X", doc->V2G_Message.Header.SessionID.bytes[i]); } printf(""); printf(""); if (doc->V2G_Message.Body.CurrentDemandRes_isUsed) { printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.ResponseCode); printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEStatusCode); printf(""); printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Value); printf(""); printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Value); printf(""); printf("%s", doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved ? "true" : "false"); printf("%s", doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved ? "true" : "false"); printf("%s", doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved ? "true" : "false"); printf("%.*s", doc->V2G_Message.Body.CurrentDemandRes.EVSEID.charactersLen, doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters); printf("%d", doc->V2G_Message.Body.CurrentDemandRes.SAScheduleTupleID); printf(""); } else if (doc->V2G_Message.Body.CurrentDemandReq_isUsed) { printf(""); printf(""); printf("%s", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false"); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC); printf(""); printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value); printf(""); if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) { printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Value); printf(""); } if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed) { printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Value); printf(""); } if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed) { printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Value); printf(""); } if (doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed) { printf("%s", doc->V2G_Message.Body.CurrentDemandReq.BulkChargingComplete ? "true" : "false"); } printf("%s", doc->V2G_Message.Body.CurrentDemandReq.ChargingComplete ? "true" : "false"); if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed) { printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Value); printf(""); } if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed) { printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Value); printf(""); } // EVTargetVoltage must come last according to EXI grammar printf(""); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit); printf("%d", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value); printf(""); printf(""); } printf(""); 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[]) { DEBUG_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: V2GDecoder [-debug] [-decode|-encode] input_file\\n"); printf(" V2GDecoder [-debug] -encode (read XML from stdin)\\n"); printf(" V2GDecoder [-debug] -decode (read hex string from stdin)\\n"); 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 | V2GDecoder -decode)\\n"); printf(" -encode Convert XML to EXI format\\n"); printf(" -encode Read XML from stdin (type file.xml | V2GDecoder -encode)\\n"); printf(" (default) Analyze EXI with detailed output\\n"); printf("\\nContact: tindevil82@gmail.com\\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) { DEBUG_PRINTF(("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 DEBUG_PRINTF(("DEBUG: About to parse XML content\n")); int parse_result = parse_xml_to_iso1(xml_content, &iso1Doc); DEBUG_PRINTF(("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) { DEBUG_PRINTF(("EVReady: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false")); DEBUG_PRINTF(("EVErrorCode: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode)); DEBUG_PRINTF(("EVRESSSOC: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC)); DEBUG_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)); DEBUG_PRINTF(("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; // 구조체 덤프 (디버그용, 필요시 활성화) DEBUG_PRINTF(("DEBUG: About to dump structure to temp/struct_xml.txt\n")); if (EXI_DEBUG_MODE) { dump_iso1_document_to_file(&iso1Doc, "temp/struct_xml.txt"); } DEBUG_PRINTF(("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 DEBUG_PRINTF(("\\n=== Original EXI Structure Debug ===\\n")); DEBUG_PRINTF(("V2G_Message_isUsed: %s\\n", iso1Doc.V2G_Message_isUsed ? "true" : "false")); DEBUG_PRINTF(("SessionID length: %d\\n", iso1Doc.V2G_Message.Header.SessionID.bytesLen)); DEBUG_PRINTF(("CurrentDemandReq_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq_isUsed ? "true" : "false")); if (iso1Doc.V2G_Message.Body.CurrentDemandReq_isUsed) { DEBUG_PRINTF(("EVReady: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false")); DEBUG_PRINTF(("EVErrorCode: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode)); DEBUG_PRINTF(("EVRESSSOC: %d\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC)); DEBUG_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)); DEBUG_PRINTF(("EVMaximumVoltageLimit_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed ? "true" : "false")); DEBUG_PRINTF(("EVMaximumCurrentLimit_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed ? "true" : "false")); DEBUG_PRINTF(("EVMaximumPowerLimit_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed ? "true" : "false")); DEBUG_PRINTF(("BulkChargingComplete_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.BulkChargingComplete_isUsed ? "true" : "false")); DEBUG_PRINTF(("RemainingTimeToFullSoC_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed ? "true" : "false")); DEBUG_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; }