#include #include #include #include #include #include /* EXI codec headers */ #include "iso1EXIDatatypes.h" #include "iso1EXIDatatypesDecoder.h" #include "iso1EXIDatatypesEncoder.h" #include "iso2EXIDatatypes.h" #include "iso2EXIDatatypesDecoder.h" #include "iso2EXIDatatypesEncoder.h" #include "dinEXIDatatypes.h" #include "dinEXIDatatypesDecoder.h" #include "dinEXIDatatypesEncoder.h" #include "ByteStream.h" #define BUFFER_SIZE 4096 // Network protocol patterns and definitions #define ETH_TYPE_IPV6 0x86DD // Ethernet Type: IPv6 #define IPV6_NEXT_HEADER_TCP 0x06 // IPv6 Next Header: TCP #define TCP_V2G_PORT 15118 // V2G communication port // V2G Transfer Protocol patterns and definitions #define V2G_PROTOCOL_VERSION 0x01 // Protocol Version #define V2G_INV_PROTOCOL_VERSION 0xFE // Inverse Protocol Version #define V2G_PAYLOAD_ISO_DIN_SAP 0x8001 // ISO 15118-2/DIN/SAP payload type #define V2G_PAYLOAD_ISO2 0x8002 // ISO 15118-20 payload type #define EXI_START_PATTERN 0x8098 // EXI document start pattern // Function declarations for Phase 2 Session & Service Management helper functions void print_session_setup_req_details(struct iso1SessionSetupReqType *sessionSetupReq); void print_session_setup_res_details(struct iso1SessionSetupResType *sessionSetupRes); void print_service_discovery_req_details(struct iso1ServiceDiscoveryReqType *serviceDiscoveryReq); void print_service_discovery_res_details(struct iso1ServiceDiscoveryResType *serviceDiscoveryRes); void print_authorization_req_details(struct iso1AuthorizationReqType *authorizationReq); void print_authorization_res_details(struct iso1AuthorizationResType *authorizationRes); void print_charge_param_discovery_req_details(struct iso1ChargeParameterDiscoveryReqType *chargeParamReq); void print_charge_param_discovery_res_details(struct iso1ChargeParameterDiscoveryResType *chargeParamRes); // Phase 3: Extended Features - Function declarations void print_payment_service_selection_req_details(struct iso1PaymentServiceSelectionReqType *paymentServiceSelectionReq); void print_payment_service_selection_res_details(struct iso1PaymentServiceSelectionResType *paymentServiceSelectionRes); void print_charging_status_req_details(struct iso1ChargingStatusReqType *chargingStatusReq); void print_charging_status_res_details(struct iso1ChargingStatusResType *chargingStatusRes); void print_session_stop_req_details(struct iso1SessionStopReqType *sessionStopReq); void print_session_stop_res_details(struct iso1SessionStopResType *sessionStopRes); // 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); printf("✓ Structure dump saved to %s\n", filename); } // 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.SessionSetupReq_isUsed) { printf("🔐 SessionSetupReq: Charging session initialization\n"); print_session_setup_req_details(&doc->V2G_Message.Body.SessionSetupReq); } else if (doc->V2G_Message.Body.SessionSetupRes_isUsed) { printf("✅ SessionSetupRes: Session setup response\n"); print_session_setup_res_details(&doc->V2G_Message.Body.SessionSetupRes); } else if (doc->V2G_Message.Body.ServiceDiscoveryReq_isUsed) { printf("🔍 ServiceDiscoveryReq: Available charging services discovery\n"); print_service_discovery_req_details(&doc->V2G_Message.Body.ServiceDiscoveryReq); } else if (doc->V2G_Message.Body.ServiceDiscoveryRes_isUsed) { printf("✅ ServiceDiscoveryRes: Service discovery response\n"); print_service_discovery_res_details(&doc->V2G_Message.Body.ServiceDiscoveryRes); } else if (doc->V2G_Message.Body.AuthorizationReq_isUsed) { printf("🔑 AuthorizationReq: Charging authorization verification\n"); print_authorization_req_details(&doc->V2G_Message.Body.AuthorizationReq); } else if (doc->V2G_Message.Body.AuthorizationRes_isUsed) { printf("✅ AuthorizationRes: Authorization response\n"); print_authorization_res_details(&doc->V2G_Message.Body.AuthorizationRes); } else if (doc->V2G_Message.Body.ChargeParameterDiscoveryReq_isUsed) { printf("⚙️ ChargeParameterDiscoveryReq: Charging parameter exchange\n"); print_charge_param_discovery_req_details(&doc->V2G_Message.Body.ChargeParameterDiscoveryReq); } else if (doc->V2G_Message.Body.ChargeParameterDiscoveryRes_isUsed) { printf("✅ ChargeParameterDiscoveryRes: Charge parameter response\n"); print_charge_param_discovery_res_details(&doc->V2G_Message.Body.ChargeParameterDiscoveryRes); } // Phase 3: Extended Features - Message detection else if (doc->V2G_Message.Body.PaymentServiceSelectionReq_isUsed) { printf("💳 PaymentServiceSelectionReq: Payment method selection request\n"); print_payment_service_selection_req_details(&doc->V2G_Message.Body.PaymentServiceSelectionReq); } else if (doc->V2G_Message.Body.PaymentServiceSelectionRes_isUsed) { printf("💳 PaymentServiceSelectionRes: Payment method selection response\n"); print_payment_service_selection_res_details(&doc->V2G_Message.Body.PaymentServiceSelectionRes); } else if (doc->V2G_Message.Body.ChargingStatusReq_isUsed) { printf("🔋 ChargingStatusReq: AC charging status request\n"); print_charging_status_req_details(&doc->V2G_Message.Body.ChargingStatusReq); } else if (doc->V2G_Message.Body.ChargingStatusRes_isUsed) { printf("🔋 ChargingStatusRes: AC charging status response\n"); print_charging_status_res_details(&doc->V2G_Message.Body.ChargingStatusRes); } else if (doc->V2G_Message.Body.SessionStopReq_isUsed) { printf("🛑 SessionStopReq: Session termination request\n"); print_session_stop_req_details(&doc->V2G_Message.Body.SessionStopReq); } else if (doc->V2G_Message.Body.SessionStopRes_isUsed) { printf("🛑 SessionStopRes: Session termination response\n"); print_session_stop_res_details(&doc->V2G_Message.Body.SessionStopRes); } 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"); } // Helper function to parse and print session setup request details void print_session_setup_req_details(struct iso1SessionSetupReqType *sessionSetupReq) { printf(" 🆔 EVCCID: "); for(int i = 0; i < sessionSetupReq->EVCCID.bytesLen; i++) { printf("%02X", sessionSetupReq->EVCCID.bytes[i]); } printf("\n"); } // Helper function to parse and print session setup response details void print_session_setup_res_details(struct iso1SessionSetupResType *sessionSetupRes) { printf(" 📋 Response Code: %d\n", sessionSetupRes->ResponseCode); printf(" 🆔 EVSEID: %.*s\n", sessionSetupRes->EVSEID.charactersLen, sessionSetupRes->EVSEID.characters); if(sessionSetupRes->EVSETimeStamp_isUsed) { printf(" ⏰ EVSETimeStamp: %ld\n", sessionSetupRes->EVSETimeStamp); } } // Helper function to parse and print service discovery request details void print_service_discovery_req_details(struct iso1ServiceDiscoveryReqType *serviceDiscoveryReq) { if(serviceDiscoveryReq->ServiceScope_isUsed) { printf(" 🔍 ServiceScope: %.*s\n", serviceDiscoveryReq->ServiceScope.charactersLen, serviceDiscoveryReq->ServiceScope.characters); } if(serviceDiscoveryReq->ServiceCategory_isUsed) { printf(" 📂 ServiceCategory: %d\n", serviceDiscoveryReq->ServiceCategory); } } // Helper function to parse and print service discovery response details void print_service_discovery_res_details(struct iso1ServiceDiscoveryResType *serviceDiscoveryRes) { printf(" 📋 Response Code: %d\n", serviceDiscoveryRes->ResponseCode); printf(" ⚡ PaymentOptionList:\n"); for(int i = 0; i < serviceDiscoveryRes->PaymentOptionList.PaymentOption.arrayLen; i++) { printf(" Payment Option %d: %d\n", i+1, serviceDiscoveryRes->PaymentOptionList.PaymentOption.array[i]); } printf(" 🔧 ChargeService:\n"); printf(" ServiceID: %d\n", serviceDiscoveryRes->ChargeService.ServiceID); printf(" ServiceName: %.*s\n", serviceDiscoveryRes->ChargeService.ServiceName_isUsed ? serviceDiscoveryRes->ChargeService.ServiceName.charactersLen : 0, serviceDiscoveryRes->ChargeService.ServiceName_isUsed ? serviceDiscoveryRes->ChargeService.ServiceName.characters : ""); printf(" ServiceCategory: %d\n", serviceDiscoveryRes->ChargeService.ServiceCategory); printf(" FreeService: %s\n", serviceDiscoveryRes->ChargeService.FreeService ? "true" : "false"); if(serviceDiscoveryRes->ServiceList_isUsed && serviceDiscoveryRes->ServiceList.Service.arrayLen > 0) { printf(" 📋 ServiceList:\n"); for(int i = 0; i < serviceDiscoveryRes->ServiceList.Service.arrayLen; i++) { printf(" Service %d - ID: %d, Category: %d\n", i+1, serviceDiscoveryRes->ServiceList.Service.array[i].ServiceID, serviceDiscoveryRes->ServiceList.Service.array[i].ServiceCategory); } } } // Helper function to parse and print authorization request details void print_authorization_req_details(struct iso1AuthorizationReqType *authorizationReq) { if(authorizationReq->Id_isUsed) { printf(" 🆔 ID: %.*s\n", authorizationReq->Id.charactersLen, authorizationReq->Id.characters); } if(authorizationReq->GenChallenge_isUsed) { printf(" 🎲 GenChallenge: "); for(int i = 0; i < authorizationReq->GenChallenge.bytesLen; i++) { printf("%02X", authorizationReq->GenChallenge.bytes[i]); } printf("\n"); } } // Helper function to parse and print authorization response details void print_authorization_res_details(struct iso1AuthorizationResType *authorizationRes) { printf(" 📋 Response Code: %d\n", authorizationRes->ResponseCode); printf(" ⚡ EVSEProcessing: %d\n", authorizationRes->EVSEProcessing); } // Helper function to parse and print charge parameter discovery request details void print_charge_param_discovery_req_details(struct iso1ChargeParameterDiscoveryReqType *chargeParamReq) { if(chargeParamReq->MaxEntriesSAScheduleTuple_isUsed) { printf(" 📊 MaxEntriesSAScheduleTuple: %d\n", chargeParamReq->MaxEntriesSAScheduleTuple); } printf(" 🔌 RequestedEnergyTransferMode: %d\n", chargeParamReq->RequestedEnergyTransferMode); if(chargeParamReq->DC_EVChargeParameter_isUsed) { printf(" ⚡ DC_EVChargeParameter:\n"); printf(" 📋 DC_EVStatus:\n"); printf(" 🔋 EVReady: %s\n", chargeParamReq->DC_EVChargeParameter.DC_EVStatus.EVReady ? "true" : "false"); printf(" ⚡ EVErrorCode: %d\n", chargeParamReq->DC_EVChargeParameter.DC_EVStatus.EVErrorCode); printf(" 🔌 EVRESSSOC: %d%%\n", chargeParamReq->DC_EVChargeParameter.DC_EVStatus.EVRESSSOC); printf(" 🔋 EVMaximumCurrentLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumCurrentLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumCurrentLimit.Unit); printf(" 💯 Value: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumCurrentLimit.Value); printf(" ⚡ EVMaximumVoltageLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumVoltageLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumVoltageLimit.Unit); printf(" 💯 Value: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumVoltageLimit.Value); if(chargeParamReq->DC_EVChargeParameter.EVMaximumPowerLimit_isUsed) { printf(" 🔌 EVMaximumPowerLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumPowerLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumPowerLimit.Unit); printf(" 💯 Value: %d\n", chargeParamReq->DC_EVChargeParameter.EVMaximumPowerLimit.Value); } if(chargeParamReq->DC_EVChargeParameter.EVEnergyCapacity_isUsed) { printf(" 🔋 EVEnergyCapacity:\n"); printf(" 📊 Multiplier: %d\n", chargeParamReq->DC_EVChargeParameter.EVEnergyCapacity.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamReq->DC_EVChargeParameter.EVEnergyCapacity.Unit); printf(" 💯 Value: %d\n", chargeParamReq->DC_EVChargeParameter.EVEnergyCapacity.Value); } if(chargeParamReq->DC_EVChargeParameter.EVEnergyRequest_isUsed) { printf(" ⚡ EVEnergyRequest:\n"); printf(" 📊 Multiplier: %d\n", chargeParamReq->DC_EVChargeParameter.EVEnergyRequest.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamReq->DC_EVChargeParameter.EVEnergyRequest.Unit); printf(" 💯 Value: %d\n", chargeParamReq->DC_EVChargeParameter.EVEnergyRequest.Value); } if(chargeParamReq->DC_EVChargeParameter.FullSOC_isUsed) { printf(" 🔋 FullSOC: %d%%\n", chargeParamReq->DC_EVChargeParameter.FullSOC); } if(chargeParamReq->DC_EVChargeParameter.BulkSOC_isUsed) { printf(" 📊 BulkSOC: %d%%\n", chargeParamReq->DC_EVChargeParameter.BulkSOC); } } } // Helper function to parse and print charge parameter discovery response details void print_charge_param_discovery_res_details(struct iso1ChargeParameterDiscoveryResType *chargeParamRes) { printf(" 📋 Response Code: %d\n", chargeParamRes->ResponseCode); printf(" ⚡ EVSEProcessing: %d\n", chargeParamRes->EVSEProcessing); if(chargeParamRes->SAScheduleList_isUsed) { printf(" 📅 SASchedules:\n"); printf(" Schedule Entries: %d\n", chargeParamRes->SAScheduleList.SAScheduleTuple.arrayLen); for(int i = 0; i < chargeParamRes->SAScheduleList.SAScheduleTuple.arrayLen; i++) { printf(" Schedule %d:\n", i+1); printf(" SAScheduleTupleID: %d\n", chargeParamRes->SAScheduleList.SAScheduleTuple.array[i].SAScheduleTupleID); if(chargeParamRes->SAScheduleList.SAScheduleTuple.array[i].PMaxSchedule.PMaxScheduleEntry.arrayLen > 0) { printf(" PMaxScheduleEntries: %d\n", chargeParamRes->SAScheduleList.SAScheduleTuple.array[i].PMaxSchedule.PMaxScheduleEntry.arrayLen); } } } if(chargeParamRes->DC_EVSEChargeParameter_isUsed) { printf(" 🔌 DC_EVSEChargeParameter:\n"); printf(" 📊 DC_EVSEStatus:\n"); printf(" 🔌 EVSEIsolationStatus: %d\n", chargeParamRes->DC_EVSEChargeParameter.DC_EVSEStatus.EVSEIsolationStatus); printf(" ⚡ EVSEStatusCode: %d\n", chargeParamRes->DC_EVSEChargeParameter.DC_EVSEStatus.EVSEStatusCode); printf(" ⚡ EVSEMaximumCurrentLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumCurrentLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumCurrentLimit.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumCurrentLimit.Value); printf(" 🔋 EVSEMaximumVoltageLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumVoltageLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumVoltageLimit.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumVoltageLimit.Value); printf(" 🔌 EVSEMaximumPowerLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumPowerLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumPowerLimit.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMaximumPowerLimit.Value); printf(" ⚡ EVSEMinimumCurrentLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMinimumCurrentLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMinimumCurrentLimit.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMinimumCurrentLimit.Value); printf(" 🔋 EVSEMinimumVoltageLimit:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMinimumVoltageLimit.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMinimumVoltageLimit.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEMinimumVoltageLimit.Value); if(chargeParamRes->DC_EVSEChargeParameter.EVSECurrentRegulationTolerance_isUsed) { printf(" 📊 EVSECurrentRegulationTolerance:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSECurrentRegulationTolerance.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSECurrentRegulationTolerance.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSECurrentRegulationTolerance.Value); } printf(" ⚡ EVSEPeakCurrentRipple:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEPeakCurrentRipple.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEPeakCurrentRipple.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEPeakCurrentRipple.Value); if(chargeParamRes->DC_EVSEChargeParameter.EVSEEnergyToBeDelivered_isUsed) { printf(" 🔋 EVSEEnergyToBeDelivered:\n"); printf(" 📊 Multiplier: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEEnergyToBeDelivered.Multiplier); printf(" 🔢 Unit: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEEnergyToBeDelivered.Unit); printf(" 💯 Value: %d\n", chargeParamRes->DC_EVSEChargeParameter.EVSEEnergyToBeDelivered.Value); } } } int main(int argc, char *argv[]) { int xml_mode = 0; int encode_mode = 0; char *filename = NULL; if (argc == 2) { filename = argv[1]; } else if (argc == 3 && strcmp(argv[1], "-decode") == 0) { xml_mode = 1; filename = argv[2]; } else if (argc == 3 && strcmp(argv[1], "-encode") == 0) { encode_mode = 1; filename = argv[2]; } else { printf("Usage: %s [-decode|-encode] input_file\\n", argv[0]); printf("Enhanced EXI viewer with XML conversion capabilities\\n"); printf(" -decode Convert EXI to Wireshark-style XML format\\n"); printf(" -encode Convert XML to EXI format\\n"); printf(" (default) Analyze EXI with detailed output\\n"); return -1; } uint8_t buffer[BUFFER_SIZE]; bitstream_t stream; size_t pos = 0; int errn = 0; struct iso1EXIDocument iso1Doc; struct iso2EXIDocument iso2Doc; struct dinEXIDocument dinDoc; // Initialize documents init_iso1EXIDocument(&iso1Doc); init_iso2EXIDocument(&iso2Doc); init_dinEXIDocument(&dinDoc); // Handle encode mode (XML to EXI) if (encode_mode) { // Read XML file FILE* xml_file = fopen(filename, "r"); if (!xml_file) { printf("Error opening XML file: %s\\n", filename); return -1; } // Read entire XML content fseek(xml_file, 0, SEEK_END); long xml_size = ftell(xml_file); fseek(xml_file, 0, SEEK_SET); char* xml_content = malloc(xml_size + 1); if (!xml_content) { printf("Error allocating memory for XML content\\n"); fclose(xml_file); return -1; } fread(xml_content, 1, xml_size, xml_file); xml_content[xml_size] = '\0'; fclose(xml_file); // Parse XML to ISO1 document structure if (parse_xml_to_iso1(xml_content, &iso1Doc) != 0) { printf("Error parsing XML file - no supported message type found\\n"); free(xml_content); return -1; } // 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; // 구조체 덤프 (디버그용, 필요시 활성화) // dump_iso1_document_to_file(&iso1Doc, "struct_xml.txt"); 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 file for decode/analysis mode errn = readEXIFile(filename, buffer, BUFFER_SIZE, &pos); if (errn != 0) { printf("Error reading file: %s\\n", filename); return -1; } if (!xml_mode) { printf("File: %s (%zu bytes)\\n", filename, pos); printf("Raw hex data: "); for(size_t i = 0; i < (pos > 32 ? 32 : pos); i++) { printf("%02X ", buffer[i]); } if (pos > 32) printf("..."); printf("\\n\\n"); // Analyze data structure and extract EXI body analyze_data_structure(buffer, pos); } // Extract EXI body from V2G Transfer Protocol data uint8_t exi_buffer[BUFFER_SIZE]; size_t exi_size = extract_exi_body(buffer, pos, exi_buffer, BUFFER_SIZE); if (exi_size != pos) { if (!xml_mode) { printf("EXI body extracted: %zu bytes (was %zu bytes)\\n", exi_size, pos); printf("EXI hex data: "); for(size_t i = 0; i < (exi_size > 32 ? 32 : exi_size); i++) { printf("%02X ", exi_buffer[i]); } if (exi_size > 32) printf("..."); printf("\\n\\n"); } // Use extracted EXI data memcpy(buffer, exi_buffer, exi_size); pos = exi_size; } // Setup stream pos = 0; // reset position for decoding stream.size = BUFFER_SIZE; stream.data = buffer; stream.pos = &pos; stream.buffer = 0; stream.capacity = 0; // Try ISO1 first pos = 0; if (!xml_mode) printf("Trying ISO1 decoder...\\n"); errn = decode_iso1ExiDocument(&stream, &iso1Doc); if (errn == 0) { if (!xml_mode) printf("✓ Successfully decoded as ISO1\\n\\n"); if (xml_mode) { print_iso1_xml_wireshark(&iso1Doc); } else { print_iso1_message(&iso1Doc); // 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; } // Phase 3: Extended Features - Helper function implementations void print_payment_service_selection_req_details(struct iso1PaymentServiceSelectionReqType *paymentServiceSelectionReq) { printf(" 📄 Payment Service Selection Request Details:\\n"); printf(" 💳 Purpose: Select payment method for charging session\\n"); printf(" 🆔 Selected Payment Option: "); switch(paymentServiceSelectionReq->SelectedPaymentOption) { case iso1paymentOptionType_Contract: printf("Contract-based payment\\n"); break; case iso1paymentOptionType_ExternalPayment: printf("External payment method\\n"); break; default: printf("Unknown (%d)\\n", paymentServiceSelectionReq->SelectedPaymentOption); } printf(" 📋 Selected Service List:\\n"); if (paymentServiceSelectionReq->SelectedServiceList.SelectedService.arrayLen > 0) { for (int i = 0; i < paymentServiceSelectionReq->SelectedServiceList.SelectedService.arrayLen; i++) { printf(" %d. Service ID: %d, Parameter Set ID: %d\\n", i + 1, paymentServiceSelectionReq->SelectedServiceList.SelectedService.array[i].ServiceID, paymentServiceSelectionReq->SelectedServiceList.SelectedService.array[i].ParameterSetID_isUsed ? paymentServiceSelectionReq->SelectedServiceList.SelectedService.array[i].ParameterSetID : -1); } } else { printf(" No services selected\\n"); } printf(" ✅ Payment service selection requested\\n"); } void print_payment_service_selection_res_details(struct iso1PaymentServiceSelectionResType *paymentServiceSelectionRes) { printf(" 📄 Payment Service Selection Response Details:\\n"); printf(" 💳 Purpose: Confirm payment method selection\\n"); printf(" 📊 Response Code: "); switch(paymentServiceSelectionRes->ResponseCode) { case iso1responseCodeType_OK: printf("✅ OK - Payment selection accepted\\n"); break; case iso1responseCodeType_OK_NewSessionEstablished: printf("✅ OK - New session established\\n"); break; case iso1responseCodeType_FAILED: printf("❌ FAILED\\n"); break; default: printf("⚠️ Unknown (%d)\\n", paymentServiceSelectionRes->ResponseCode); } printf(" 🔄 Payment method selection completed\\n"); } void print_charging_status_req_details(struct iso1ChargingStatusReqType *chargingStatusReq) { printf(" 📄 Charging Status Request Details:\\n"); printf(" 🔋 Purpose: Request AC charging status information\\n"); printf(" 🔍 Requesting current charging status from EVSE\\n"); printf(" 📊 Status inquiry for AC charging session\\n"); } void print_charging_status_res_details(struct iso1ChargingStatusResType *chargingStatusRes) { printf(" 📄 Charging Status Response Details:\\n"); printf(" 🔋 Purpose: Provide AC charging status information\\n"); printf(" 📊 Response Code: "); switch(chargingStatusRes->ResponseCode) { case iso1responseCodeType_OK: printf("✅ OK\\n"); break; case iso1responseCodeType_OK_NewSessionEstablished: printf("✅ OK - New session established\\n"); break; case iso1responseCodeType_FAILED: printf("❌ FAILED\\n"); break; default: printf("⚠️ Unknown (%d)\\n", chargingStatusRes->ResponseCode); } printf(" 🆔 EVSE ID: "); if (chargingStatusRes->EVSEID.charactersLen > 0) { printf("%.*s\\n", (int)chargingStatusRes->EVSEID.charactersLen, chargingStatusRes->EVSEID.characters); } else { printf("Not provided\\n"); } printf(" 🔌 SA Schedule Tuple ID: %d\\n", chargingStatusRes->SAScheduleTupleID); if (chargingStatusRes->EVSEMaxCurrent_isUsed) { printf(" ⚡ EVSE Max Current: %d %s (10^%d)\\n", chargingStatusRes->EVSEMaxCurrent.Value, chargingStatusRes->EVSEMaxCurrent.Unit == 5 ? "A" : "Unknown Unit", chargingStatusRes->EVSEMaxCurrent.Multiplier); } if (chargingStatusRes->MeterInfo_isUsed) { printf(" 📊 Meter Information:\\n"); printf(" 🆔 Meter ID: "); if (chargingStatusRes->MeterInfo.MeterID.charactersLen > 0) { printf("%.*s\\n", (int)chargingStatusRes->MeterInfo.MeterID.charactersLen, chargingStatusRes->MeterInfo.MeterID.characters); } else { printf("Not provided\\n"); } if (chargingStatusRes->MeterInfo.MeterReading_isUsed) { printf(" 📏 Meter Reading: %llu Wh\\n", (unsigned long long)chargingStatusRes->MeterInfo.MeterReading); } if (chargingStatusRes->MeterInfo.SigMeterReading_isUsed) { printf(" 🔏 Signed Meter Reading: %.*s\\n", (int)chargingStatusRes->MeterInfo.SigMeterReading.bytesLen, (char*)chargingStatusRes->MeterInfo.SigMeterReading.bytes); } if (chargingStatusRes->MeterInfo.MeterStatus_isUsed) { printf(" 📊 Meter Status: %d\\n", chargingStatusRes->MeterInfo.MeterStatus); } } if (chargingStatusRes->ReceiptRequired_isUsed) { printf(" 🧾 Receipt Required: %s\\n", chargingStatusRes->ReceiptRequired ? "Yes" : "No"); } printf(" ✅ AC charging status provided\\n"); } void print_session_stop_req_details(struct iso1SessionStopReqType *sessionStopReq) { printf(" 📄 Session Stop Request Details:\\n"); printf(" 🛑 Purpose: Request charging session termination\\n"); printf(" 🔄 Charging Session: "); switch(sessionStopReq->ChargingSession) { case iso1chargingSessionType_Terminate: printf("✅ Terminate - Session ending completely\\n"); break; case iso1chargingSessionType_Pause: printf("⏸️ Pause - Session paused temporarily\\n"); break; default: printf("⚠️ Unknown (%d)\\n", sessionStopReq->ChargingSession); } printf(" 🛑 Requesting session termination\\n"); } void print_session_stop_res_details(struct iso1SessionStopResType *sessionStopRes) { printf(" 📄 Session Stop Response Details:\\n"); printf(" 🛑 Purpose: Confirm charging session termination\\n"); printf(" 📊 Response Code: "); switch(sessionStopRes->ResponseCode) { case iso1responseCodeType_OK: printf("✅ OK - Session terminated successfully\\n"); break; case iso1responseCodeType_OK_NewSessionEstablished: printf("✅ OK - New session established\\n"); break; case iso1responseCodeType_FAILED: printf("❌ FAILED - Session termination failed\\n"); break; default: printf("⚠️ Unknown (%d)\\n", sessionStopRes->ResponseCode); } printf(" 🔚 Charging session terminated\\n"); }