fix: Resolve EXI encoding issues and enhance V2GDecoder functionality

- Fix critical EXI encoding error -109 (EXI_ERROR_UNKOWN_EVENT) by adding proper structure initialization
  * Added init_iso1BodyType() call before setting message type flags in XML parsing
  * Prevents garbage values in unused message type flags that caused wrong grammar paths

- Rename enhanced_exi_viewer to V2GDecoder for consistency
  * Updated build.bat to build V2GDecoder instead of enhanced_exi_viewer
  * Maintains all existing functionality with improved reliability

- Add comprehensive structure debugging capabilities
  * Created structure dump functions to output complete document state
  * Added struct_exi.txt and struct_xml.txt for comparing parsed vs original structures

- Enhance output formatting for encoding operations
  * Clean hex-only output for encoding mode (similar to XML decode mode)
  * Removed debug clutter for production use while preserving debugging code

- Add test infrastructure with minimal_test.xml for focused testing
  * Validates CurrentDemandReq message encoding with minimal required fields

- Improve encoder debugging with position tracking in iso1EXIDatatypesEncoder.c
  * Added debug points to track exact failure locations during encoding

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
ChiKyun Kim
2025-09-10 13:01:53 +09:00
parent fe368f2d23
commit 35af323ff0
8 changed files with 441 additions and 87 deletions

View File

@@ -468,9 +468,23 @@ int parse_xml_to_iso1(const char* xml_content, struct iso1EXIDocument* doc) {
// Check for CurrentDemandReq // Check for CurrentDemandReq
if (strstr(xml_content, "<CurrentDemandReq>") || strstr(xml_content, "<ns3:CurrentDemandReq>")) { if (strstr(xml_content, "<CurrentDemandReq>") || strstr(xml_content, "<ns3:CurrentDemandReq>")) {
// Body 구조체 초기화 (모든 메시지 타입 플래그를 0으로 설정)
init_iso1BodyType(&doc->V2G_Message.Body);
// 오직 CurrentDemandReq만 활성화
doc->V2G_Message.Body.CurrentDemandReq_isUsed = 1; doc->V2G_Message.Body.CurrentDemandReq_isUsed = 1;
// Initialize CurrentDemandReq structure completely
init_iso1CurrentDemandReqType(&doc->V2G_Message.Body.CurrentDemandReq); 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 // Parse DC_EVStatus
char* ev_ready = find_tag_content(xml_content, "EVReady"); char* ev_ready = find_tag_content(xml_content, "EVReady");
if (ev_ready) { if (ev_ready) {
@@ -588,6 +602,10 @@ int parse_xml_to_iso1(const char* xml_content, struct iso1EXIDocument* doc) {
// Check for CurrentDemandRes // Check for CurrentDemandRes
if (strstr(xml_content, "<CurrentDemandRes>") || strstr(xml_content, "<ns3:CurrentDemandRes>")) { if (strstr(xml_content, "<CurrentDemandRes>") || strstr(xml_content, "<ns3:CurrentDemandRes>")) {
// Body 구조체 초기화 (모든 메시지 타입 플래그를 0으로 설정)
init_iso1BodyType(&doc->V2G_Message.Body);
// 오직 CurrentDemandRes만 활성화
doc->V2G_Message.Body.CurrentDemandRes_isUsed = 1; doc->V2G_Message.Body.CurrentDemandRes_isUsed = 1;
init_iso1CurrentDemandResType(&doc->V2G_Message.Body.CurrentDemandRes); init_iso1CurrentDemandResType(&doc->V2G_Message.Body.CurrentDemandRes);
@@ -669,6 +687,138 @@ int parse_xml_to_iso1(const char* xml_content, struct iso1EXIDocument* doc) {
return -1; // Unsupported message type 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 // Helper function to read EXI file
int readEXIFile(char* file, uint8_t* buffer, size_t buffer_size, size_t *bytes_read) { int readEXIFile(char* file, uint8_t* buffer, size_t buffer_size, size_t *bytes_read) {
FILE *fp = fopen(file, "rb"); FILE *fp = fopen(file, "rb");
@@ -756,12 +906,6 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value); printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
printf("</ns3:EVTargetCurrent>"); printf("</ns3:EVTargetCurrent>");
printf("<ns3:EVTargetVoltage>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
printf("</ns3:EVTargetVoltage>");
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) { if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) {
printf("<ns3:EVMaximumVoltageLimit>"); printf("<ns3:EVMaximumVoltageLimit>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier); printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier);
@@ -808,6 +952,13 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
printf("</ns3:RemainingTimeToBulkSoC>"); printf("</ns3:RemainingTimeToBulkSoC>");
} }
// EVTargetVoltage must come last according to EXI grammar
printf("<ns3:EVTargetVoltage>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier);
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
printf("</ns3:EVTargetVoltage>");
printf("</ns3:CurrentDemandReq>"); printf("</ns3:CurrentDemandReq>");
} }
@@ -999,14 +1150,29 @@ int main(int argc, char *argv[]) {
free(xml_content); free(xml_content);
return -1; return -1;
} }
// Show encoding info only when redirecting to file (keeps terminal output clean)
struct stat stat_buf_debug; // XML parsing debug info to stderr (optional)
int is_file_redirect = (fstat(fileno(stdout), &stat_buf_debug) == 0 && S_ISREG(stat_buf_debug.st_mode)); // fprintf(stderr, "=== XML Parsing Debug ===\\n");
if (is_file_redirect) { // fprintf(stderr, "V2G_Message_isUsed: %s\\n", iso1Doc.V2G_Message_isUsed ? "true" : "false");
fprintf(stderr, "XML parsing successful\\n"); // fprintf(stderr, "SessionID length: %d\\n", iso1Doc.V2G_Message.Header.SessionID.bytesLen);
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");
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); free(xml_content);
@@ -1018,32 +1184,29 @@ int main(int argc, char *argv[]) {
stream.buffer = 0; stream.buffer = 0;
stream.capacity = 0; stream.capacity = 0;
// 구조체 덤프 (디버그용, 필요시 활성화)
// dump_iso1_document_to_file(&iso1Doc, "struct_xml.txt");
errn = encode_iso1ExiDocument(&stream, &iso1Doc); errn = encode_iso1ExiDocument(&stream, &iso1Doc);
if (errn != 0) { if (errn != 0) {
printf("Error encoding to EXI (error: %d)\\n", errn); fprintf(stderr, "Error encoding to EXI (error: %d)\\n", errn);
return -1; return -1;
} }
// Check if output is redirected (for Windows/MINGW compatibility) // Check if output is redirected
// Use environment variable approach or check for redirection
char* term = getenv("TERM");
int use_hex_output = (term != NULL) || isatty(fileno(stdout));
// Also check if user specifically wants binary output by checking for redirection
struct stat stat_buf; struct stat stat_buf;
if (fstat(fileno(stdout), &stat_buf) == 0 && S_ISREG(stat_buf.st_mode)) { int is_redirected = (fstat(fileno(stdout), &stat_buf) == 0 && S_ISREG(stat_buf.st_mode));
use_hex_output = 0; // Regular file, use binary
}
if (use_hex_output) { if (is_redirected) {
// Terminal output: show hex string only // 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++) { for(size_t i = 0; i < pos; i++) {
printf("%02X", buffer[i]); printf("%02X", buffer[i]);
} }
printf("\n"); printf("\n");
} else {
// Redirected output: write binary data
fwrite(buffer, 1, pos, stdout);
} }
return 0; return 0;
} }
@@ -1105,7 +1268,41 @@ int main(int argc, char *argv[]) {
print_iso1_xml_wireshark(&iso1Doc); print_iso1_xml_wireshark(&iso1Doc);
} else { } else {
print_iso1_message(&iso1Doc); 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; return 0;
} else { } else {
if (!xml_mode) printf("✗ ISO1 decode failed (error: %d)\\n", errn); if (!xml_mode) printf("✗ ISO1 decode failed (error: %d)\\n", errn);

BIN
V2GDecoder.exe Normal file

Binary file not shown.

View File

@@ -1,7 +1,7 @@
@echo off @echo off
echo Building enhanced_exi_viewer... echo Building V2GDecoder...
gcc -o enhanced_exi_viewer enhanced_exi_viewer.c ^ gcc -o V2GDecoder V2GDecoder.c ^
src/iso1/*.c ^ src/iso1/*.c ^
src/iso2/*.c ^ src/iso2/*.c ^
src/din/*.c ^ src/din/*.c ^
@@ -12,7 +12,7 @@ gcc -o enhanced_exi_viewer enhanced_exi_viewer.c ^
-I./src/din -I./src/din
if %ERRORLEVEL% EQU 0 ( if %ERRORLEVEL% EQU 0 (
echo Build successful! enhanced_exi_viewer.exe created. echo Build successful! V2GDecoder.exe created.
) else ( ) else (
echo Build failed with error code %ERRORLEVEL% echo Build failed with error code %ERRORLEVEL%
) )

Binary file not shown.

29
minimal_test.xml Normal file
View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<ns1:V2G_Message xmlns:ns1="urn:iso:15118:2:2013:MsgDef"
xmlns:ns2="urn:iso:15118:2:2013:MsgHeader"
xmlns:ns3="urn:iso:15118:2:2013:MsgBody"
xmlns:ns4="urn:iso:15118:2:2013:MsgDataTypes">
<ns1:Header>
<ns2:SessionID>4142423030303831</ns2:SessionID>
</ns1:Header>
<ns1:Body>
<ns3:CurrentDemandReq>
<ns3:DC_EVStatus>
<ns4:EVReady>true</ns4:EVReady>
<ns4:EVErrorCode>0</ns4:EVErrorCode>
<ns4:EVRESSSOC>100</ns4:EVRESSSOC>
</ns3:DC_EVStatus>
<ns3:EVTargetCurrent>
<ns4:Multiplier>0</ns4:Multiplier>
<ns4:Unit>3</ns4:Unit>
<ns4:Value>5</ns4:Value>
</ns3:EVTargetCurrent>
<ns3:ChargingComplete>true</ns3:ChargingComplete>
<ns3:EVTargetVoltage>
<ns4:Multiplier>0</ns4:Multiplier>
<ns4:Unit>4</ns4:Unit>
<ns4:Value>460</ns4:Value>
</ns3:EVTargetVoltage>
</ns3:CurrentDemandReq>
</ns1:Body>
</ns1:V2G_Message>

File diff suppressed because one or more lines are too long

46
struct_exi.txt Normal file
View File

@@ -0,0 +1,46 @@
=== ISO1 EXI Document Structure Dump ===
V2G_Message_isUsed: 1
--- Header ---
SessionID.bytesLen: 8
SessionID.bytes: 4142423030303831
Notification_isUsed: 0
Signature_isUsed: 0
--- Body Message Type Flags ---
AuthorizationReq_isUsed: 0
AuthorizationRes_isUsed: 0
BodyElement_isUsed: 0
CableCheckReq_isUsed: 0
CableCheckRes_isUsed: 0
CertificateInstallationReq_isUsed: 0
CertificateInstallationRes_isUsed: 0
CertificateUpdateReq_isUsed: 0
CertificateUpdateRes_isUsed: 0
ChargeParameterDiscoveryReq_isUsed: 0
ChargeParameterDiscoveryRes_isUsed: 0
ChargingStatusReq_isUsed: 0
ChargingStatusRes_isUsed: 0
CurrentDemandReq_isUsed: 0
CurrentDemandRes_isUsed: 1
MeteringReceiptReq_isUsed: 0
MeteringReceiptRes_isUsed: 0
PaymentDetailsReq_isUsed: 0
PaymentDetailsRes_isUsed: 0
PaymentServiceSelectionReq_isUsed: 0
PaymentServiceSelectionRes_isUsed: 0
PowerDeliveryReq_isUsed: 0
PowerDeliveryRes_isUsed: 0
PreChargeReq_isUsed: 0
PreChargeRes_isUsed: 0
ServiceDetailReq_isUsed: 0
ServiceDetailRes_isUsed: 0
ServiceDiscoveryReq_isUsed: 0
ServiceDiscoveryRes_isUsed: 0
SessionSetupReq_isUsed: 0
SessionSetupRes_isUsed: 0
SessionStopReq_isUsed: 0
SessionStopRes_isUsed: 0
WeldingDetectionReq_isUsed: 0
WeldingDetectionRes_isUsed: 0

80
struct_xml.txt Normal file
View File

@@ -0,0 +1,80 @@
=== ISO1 EXI Document Structure Dump ===
V2G_Message_isUsed: 1
--- Header ---
SessionID.bytesLen: 8
SessionID.bytes: 4142423030303831
Notification_isUsed: 0
Signature_isUsed: 0
--- Body Message Type Flags ---
AuthorizationReq_isUsed: 0
AuthorizationRes_isUsed: 0
BodyElement_isUsed: 0
CableCheckReq_isUsed: 0
CableCheckRes_isUsed: 0
CertificateInstallationReq_isUsed: 0
CertificateInstallationRes_isUsed: 0
CertificateUpdateReq_isUsed: 0
CertificateUpdateRes_isUsed: 0
ChargeParameterDiscoveryReq_isUsed: 0
ChargeParameterDiscoveryRes_isUsed: 0
ChargingStatusReq_isUsed: 0
ChargingStatusRes_isUsed: 0
CurrentDemandReq_isUsed: 1
CurrentDemandRes_isUsed: 0
MeteringReceiptReq_isUsed: 0
MeteringReceiptRes_isUsed: 0
PaymentDetailsReq_isUsed: 0
PaymentDetailsRes_isUsed: 0
PaymentServiceSelectionReq_isUsed: 0
PaymentServiceSelectionRes_isUsed: 0
PowerDeliveryReq_isUsed: 0
PowerDeliveryRes_isUsed: 0
PreChargeReq_isUsed: 0
PreChargeRes_isUsed: 0
ServiceDetailReq_isUsed: 0
ServiceDetailRes_isUsed: 0
ServiceDiscoveryReq_isUsed: 0
ServiceDiscoveryRes_isUsed: 0
SessionSetupReq_isUsed: 0
SessionSetupRes_isUsed: 0
SessionStopReq_isUsed: 0
SessionStopRes_isUsed: 0
WeldingDetectionReq_isUsed: 0
WeldingDetectionRes_isUsed: 0
--- CurrentDemandReq Details ---
DC_EVStatus.EVReady: 1
DC_EVStatus.EVErrorCode: 0
DC_EVStatus.EVRESSSOC: 100
EVTargetCurrent.Multiplier: 0
EVTargetCurrent.Unit: 3
EVTargetCurrent.Value: 1
EVMaximumVoltageLimit_isUsed: 1
EVMaximumVoltageLimit.Multiplier: 0
EVMaximumVoltageLimit.Unit: 4
EVMaximumVoltageLimit.Value: 471
EVMaximumCurrentLimit_isUsed: 1
EVMaximumCurrentLimit.Multiplier: 0
EVMaximumCurrentLimit.Unit: 3
EVMaximumCurrentLimit.Value: 100
EVMaximumPowerLimit_isUsed: 1
EVMaximumPowerLimit.Multiplier: 3
EVMaximumPowerLimit.Unit: 5
EVMaximumPowerLimit.Value: 50
BulkChargingComplete_isUsed: 1
BulkChargingComplete: 0
ChargingComplete: 1
RemainingTimeToFullSoC_isUsed: 1
RemainingTimeToFullSoC.Multiplier: 0
RemainingTimeToFullSoC.Unit: 2
RemainingTimeToFullSoC.Value: 0
RemainingTimeToBulkSoC_isUsed: 1
RemainingTimeToBulkSoC.Multiplier: 0
RemainingTimeToBulkSoC.Unit: 2
RemainingTimeToBulkSoC.Value: 0
EVTargetVoltage.Multiplier: 0
EVTargetVoltage.Unit: 4
EVTargetVoltage.Value: 460