Compare commits

...

7 Commits

Author SHA1 Message Date
gram
d4af6cfc14 .. 2025-09-10 01:56:17 +09:00
gram
3c43fa1318 Clean up encoding output to show only hex string
- Remove verbose "Encoded EXI data (X bytes):" message
- Terminal output now shows only clean hex string
- File redirection maintains binary output functionality
- Streamlined user experience for hex data copying

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 01:52:46 +09:00
gram
2c82751a1a Add intelligent output format selection for EXI encoding
- Terminal output: Display hex string for easy copying/viewing
- File redirection: Write binary data for proper file creation
- Auto-detect output destination using fstat() and isatty()
- Clean terminal display without debug messages
- Support both viewing (hex) and file creation (binary) workflows

Usage examples:
- ./enhanced_exi_viewer -encode file.xml          # Shows hex string
- ./enhanced_exi_viewer -encode file.xml > out.exi  # Creates binary file

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 01:51:51 +09:00
gram
22aab10585 Fix CurrentDemandRes encoding with proper namespace parsing and isUsed flags
- Add CurrentDemandRes parsing support to parse_xml_to_iso1()
- Fix namespace-aware XML parsing using find_tag_content_ns()
- Set EVSEIsolationStatus_isUsed flag for proper EXI encoding
- Complete round-trip XML↔EXI conversion with 100% accuracy
- Support both CurrentDemandReq and CurrentDemandRes message types

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 01:48:20 +09:00
gram
760eb49afa feat: Complete XML encoding support with namespace-aware parser
Major improvements to XML parsing for perfect round-trip EXI encoding:

- **Namespace-aware XML parsing**: Handle ns1:, ns2:, ns3:, ns4: prefixed tags
- **Enhanced find_tag_content()**: Auto-detect namespaced and regular tags
- **Improved find_tag_in_section()**: Process PhysicalValue tags with namespaces
- **SessionID namespace support**: Parse both <SessionID> and <ns2:SessionID>
- **Perfect round-trip encoding**: XML → EXI → XML with 100% binary accuracy

Test results:
 test3.exi: 43 bytes - perfect decode/encode
 test4.xml: Perfect XML→EXI→XML round-trip
 test5.exi: 43 bytes - identical binary reconstruction
 All Unit values preserved as numbers (3=A, 4=V, 5=W, 2=s)
 EVErrorCode preserved as numbers (0 instead of NO_ERROR)

The enhanced_exi_viewer now supports complete bidirectional
EXI ↔ XML conversion with namespace-aware parsing.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 01:23:23 +09:00
gram
a93ad2b8e5 feat: Output Unit and EVErrorCode as numeric values instead of strings
- Remove get_unit_string() and get_error_string() helper functions
- Output Unit values as numbers (3=A, 4=V, 5=W, 2=s) instead of letters
- Output EVErrorCode as numbers (0) instead of strings (NO_ERROR)
- Maintain compatibility with Wireshark XML format structure
- Add build.bat for easy compilation
- Improve SessionID parsing for namespaced XML tags

This ensures decoded EXI outputs preserve original numeric values
rather than converting them to human-readable strings.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-10 01:11:13 +09:00
gram
4d9b03e3c5 .. 2025-09-10 01:02:29 +09:00
18 changed files with 507 additions and 419 deletions

20
build.bat Normal file
View File

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

View File

@@ -1,76 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int hex_char_to_int(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
return -1;
}
int main() {
const char* hex_string = "8098021050908C0C0C0E0C50E00000002040C40C203030C01400003103D00C0618370105088270095A5A30303030300008";
size_t hex_len = strlen(hex_string);
if (hex_len % 2 != 0) {
printf("Error: Hex string length must be even\n");
return -1;
}
size_t binary_len = hex_len / 2;
unsigned char* binary_data = malloc(binary_len);
if (!binary_data) {
printf("Memory allocation failed\n");
return -1;
}
// Convert hex string to binary
for (size_t i = 0; i < binary_len; i++) {
int high = hex_char_to_int(hex_string[i * 2]);
int low = hex_char_to_int(hex_string[i * 2 + 1]);
if (high == -1 || low == -1) {
printf("Invalid hex character at position %zu\n", i * 2);
free(binary_data);
return -1;
}
binary_data[i] = (high << 4) | low;
}
// Write to file
FILE* file = fopen("test3.exi", "wb");
if (!file) {
printf("Cannot create output file\n");
free(binary_data);
return -1;
}
size_t written = fwrite(binary_data, 1, binary_len, file);
fclose(file);
free(binary_data);
if (written != binary_len) {
printf("Write error\n");
return -1;
}
printf("Successfully created test3.exi with %zu bytes\n", binary_len);
// Show first few bytes for verification
printf("First 16 bytes: ");
FILE* verify = fopen("test3.exi", "rb");
if (verify) {
for (int i = 0; i < 16 && i < binary_len; i++) {
int c = fgetc(verify);
printf("%02X ", c);
}
fclose(verify);
}
printf("\n");
return 0;
}

Binary file not shown.

View File

@@ -1,24 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
const char* hex_string = "8098021050908C0C0C0E0C50D10032018600A01881AE0601860C806140C801030800006100001881980600";
FILE* file = fopen("test4.exi", "wb");
if (!file) {
printf("Error creating test4.exi\n");
return -1;
}
size_t len = strlen(hex_string);
for (size_t i = 0; i < len; i += 2) {
unsigned int byte;
sscanf(&hex_string[i], "%2x", &byte);
fputc(byte, file);
}
fclose(file);
printf("test4.exi created successfully (%zu bytes)\n", len/2);
return 0;
}

Binary file not shown.

View File

@@ -1,66 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* EXI codec headers */
#include "iso1EXIDatatypes.h"
#include "iso1EXIDatatypesEncoder.h"
#include "ByteStream.h"
#define BUFFER_SIZE 1024
int main() {
struct iso1EXIDocument exiDoc;
bitstream_t stream;
uint8_t buffer[BUFFER_SIZE];
size_t pos = 0;
int errn = 0;
/* Initialize EXI document */
init_iso1EXIDocument(&exiDoc);
/* Create a simple V2G message */
exiDoc.V2G_Message_isUsed = 1;
init_iso1MessageHeaderType(&exiDoc.V2G_Message.Header);
/* Set session ID to all zeros */
memset(exiDoc.V2G_Message.Header.SessionID.bytes, 0, 8);
exiDoc.V2G_Message.Header.SessionID.bytesLen = 8;
/* Create session setup request */
exiDoc.V2G_Message.Body.SessionSetupReq_isUsed = 1;
init_iso1SessionSetupReqType(&exiDoc.V2G_Message.Body.SessionSetupReq);
/* Set EVCC ID */
strcpy((char*)exiDoc.V2G_Message.Body.SessionSetupReq.EVCCID.bytes, "01");
exiDoc.V2G_Message.Body.SessionSetupReq.EVCCID.bytesLen = 2;
/* Setup output stream */
stream.size = BUFFER_SIZE;
stream.data = buffer;
stream.pos = &pos;
stream.buffer = 0;
stream.capacity = 8;
/* Encode to EXI */
printf("Encoding V2G message to EXI...\n");
errn = encode_iso1ExiDocument(&stream, &exiDoc);
if (errn != 0) {
printf("Encoding failed with error: %d\n", errn);
return -1;
}
/* Write to file */
FILE *fp = fopen("test_message.exi", "wb");
if (fp == NULL) {
printf("Cannot create output file\n");
return -1;
}
fwrite(buffer, 1, pos, fp);
fclose(fp);
printf("Created test_message.exi with %zu bytes\n", pos);
return 0;
}

Binary file not shown.

View File

@@ -2,6 +2,8 @@
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/stat.h>
/* EXI codec headers */
#include "iso1EXIDatatypes.h"
@@ -17,6 +19,205 @@
#define BUFFER_SIZE 4096
// Network protocol patterns and definitions
#define ETH_TYPE_IPV6 0x86DD // Ethernet Type: IPv6
#define IPV6_NEXT_HEADER_TCP 0x06 // IPv6 Next Header: TCP
#define TCP_V2G_PORT 15118 // V2G communication port
// V2G Transfer Protocol patterns and definitions
#define V2G_PROTOCOL_VERSION 0x01 // Protocol Version
#define V2G_INV_PROTOCOL_VERSION 0xFE // Inverse Protocol Version
#define V2G_PAYLOAD_ISO_DIN_SAP 0x8001 // ISO 15118-2/DIN/SAP payload type
#define V2G_PAYLOAD_ISO2 0x8002 // ISO 15118-20 payload type
#define EXI_START_PATTERN 0x8098 // EXI document start pattern
// Function to detect and extract EXI body from V2G Transfer Protocol data
size_t extract_exi_body(uint8_t* input_data, size_t input_size, uint8_t* output_data, size_t output_size) {
if (input_size < 8) {
// Too small for V2G TP header, assume it's pure EXI
if (input_size <= output_size) {
memcpy(output_data, input_data, input_size);
return input_size;
}
return 0;
}
// Check for V2G Transfer Protocol header
if (input_data[0] == V2G_PROTOCOL_VERSION && input_data[1] == V2G_INV_PROTOCOL_VERSION) {
uint16_t payload_type = (input_data[2] << 8) | input_data[3];
if (payload_type == V2G_PAYLOAD_ISO_DIN_SAP || payload_type == V2G_PAYLOAD_ISO2) {
// Valid V2G TP header detected: skip 8-byte header
size_t exi_size = input_size - 8;
if (exi_size <= output_size) {
memcpy(output_data, input_data + 8, exi_size);
return exi_size;
}
return 0;
}
}
// Look for EXI start pattern anywhere in the data
for (size_t i = 0; i <= input_size - 2; i++) {
uint16_t pattern = (input_data[i] << 8) | input_data[i + 1];
if (pattern == EXI_START_PATTERN) {
// Found EXI start pattern
size_t exi_size = input_size - i;
if (exi_size <= output_size) {
memcpy(output_data, input_data + i, exi_size);
return exi_size;
}
return 0;
}
}
// No pattern found, assume it's pure EXI
if (input_size <= output_size) {
memcpy(output_data, input_data, input_size);
return input_size;
}
return 0;
}
// Function to get payload type name
const char* get_payload_type_name(uint16_t payload_type) {
switch(payload_type) {
case V2G_PAYLOAD_ISO_DIN_SAP: return "ISO 15118-2/DIN/SAP";
case V2G_PAYLOAD_ISO2: return "ISO 15118-20";
default: return "Unknown";
}
}
// Function to analyze complete packet structure
void analyze_data_structure(uint8_t* data, size_t size) {
printf("=== Data Structure Analysis ===\n");
printf("Total size: %zu bytes\n", size);
size_t offset = 0;
// Check for Ethernet header (at least 14 bytes)
if (size >= 14) {
uint16_t eth_type = (data[12] << 8) | data[13];
if (eth_type == ETH_TYPE_IPV6) {
printf("Layer 2: Ethernet Frame\n");
printf(" Destination MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
data[0], data[1], data[2], data[3], data[4], data[5]);
printf(" Source MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
data[6], data[7], data[8], data[9], data[10], data[11]);
printf(" EtherType: 0x%04x (IPv6)\n", eth_type);
offset = 14;
// Check for IPv6 header (40 bytes)
if (size >= offset + 40) {
uint8_t version = (data[offset] >> 4) & 0x0F;
uint16_t payload_length = (data[offset + 4] << 8) | data[offset + 5];
uint8_t next_header = data[offset + 6];
uint8_t hop_limit = data[offset + 7];
if (version == 6) {
printf("Layer 3: IPv6 Header\n");
printf(" Version: %d\n", version);
printf(" Payload Length: %u\n", payload_length);
printf(" Next Header: %u", next_header);
if (next_header == IPV6_NEXT_HEADER_TCP) {
printf(" (TCP)\n");
} else {
printf("\n");
}
printf(" Hop Limit: %u\n", hop_limit);
// Show IPv6 addresses
printf(" Source Address: ");
for (int i = 0; i < 16; i += 2) {
printf("%02x%02x", data[offset + 8 + i], data[offset + 8 + i + 1]);
if (i < 14) printf(":");
}
printf("\n Destination Address: ");
for (int i = 0; i < 16; i += 2) {
printf("%02x%02x", data[offset + 24 + i], data[offset + 24 + i + 1]);
if (i < 14) printf(":");
}
printf("\n");
offset += 40;
// Check for TCP header (at least 20 bytes)
if (next_header == IPV6_NEXT_HEADER_TCP && size >= offset + 20) {
uint16_t src_port = (data[offset] << 8) | data[offset + 1];
uint16_t dst_port = (data[offset + 2] << 8) | data[offset + 3];
uint32_t seq_num = (data[offset + 4] << 24) | (data[offset + 5] << 16) |
(data[offset + 6] << 8) | data[offset + 7];
uint8_t tcp_header_len = (data[offset + 12] >> 4) * 4;
printf("Layer 4: TCP Header\n");
printf(" Source Port: %u\n", src_port);
printf(" Destination Port: %u", dst_port);
if (dst_port == TCP_V2G_PORT || src_port == TCP_V2G_PORT) {
printf(" (V2G Communication)\n");
} else {
printf("\n");
}
printf(" Sequence Number: %u\n", seq_num);
printf(" TCP Header Length: %u bytes\n", tcp_header_len);
offset += tcp_header_len;
}
}
}
}
}
// Look for V2G Transfer Protocol starting from current offset
if (size >= offset + 8 &&
data[offset] == V2G_PROTOCOL_VERSION &&
data[offset + 1] == V2G_INV_PROTOCOL_VERSION) {
printf("Layer 7: V2G Transfer Protocol\n");
printf(" Protocol Version: 0x%02x\n", data[offset]);
printf(" Inverse Protocol Version: 0x%02x\n", data[offset + 1]);
uint16_t payload_type = (data[offset + 2] << 8) | data[offset + 3];
printf(" Payload Type: 0x%04x (%s)\n", payload_type, get_payload_type_name(payload_type));
uint32_t payload_length = (data[offset + 4] << 24) | (data[offset + 5] << 16) |
(data[offset + 6] << 8) | data[offset + 7];
printf(" Payload Length: %u\n", payload_length);
printf("EXI body starts at offset: %zu\n", offset + 8);
// Verify payload length
if (size >= offset + 8 && (size - offset - 8) == payload_length) {
printf("✓ Payload length matches actual data (%zu bytes)\n", size - offset - 8);
} else if (size >= offset + 8) {
printf("⚠ Payload length mismatch: expected %u, got %zu bytes\n",
payload_length, size - offset - 8);
}
offset += 8;
}
else if (offset == 0) {
// No network headers detected, check if it's direct V2G TP or EXI
if (size >= 4) {
uint32_t header = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3];
printf("First 4 bytes: 0x%08X\n", header);
printf("Protocol: Unknown or Direct EXI\n");
}
}
// Look for EXI pattern
for (size_t i = 0; i <= size - 2; i++) {
uint16_t pattern = (data[i] << 8) | data[i + 1];
if (pattern == EXI_START_PATTERN) {
printf("EXI start pattern (0x8098) found at offset: %zu\n", i);
if (i >= offset) {
printf("EXI payload size: %zu bytes\n", size - i);
}
break;
}
}
printf("\n");
}
// Helper function to convert char* string to exi_string_character_t* array
static int writeStringToEXIString(char* string, exi_string_character_t* exiString) {
int pos = 0;
@@ -37,25 +238,60 @@ char* trim_whitespace(char* str) {
return str;
}
// Helper function to find XML tag content within a bounded section
// 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 start_tag[256], end_tag[256];
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);
snprintf(end_tag, sizeof(end_tag), "</%s>", tag);
// Search for tag within the bounded section
char* tag_start = strstr(section_start, start_tag);
if (!tag_start || tag_start >= section_end) {
if (tag_start && tag_start < section_end) {
content_start = tag_start + strlen(start_tag);
}
}
if (!content_start || content_start >= section_end) {
return NULL;
}
char* content_start = tag_start + strlen(start_tag);
if (content_start >= section_end) {
return NULL;
// Look for end tag (try both patterns)
char end_tag_pattern[256];
snprintf(end_tag_pattern, sizeof(end_tag_pattern), "</%s>", tag);
tag_end = strstr(content_start, end_tag_pattern);
if (!tag_end || tag_end > section_end) {
// Try namespace end pattern
snprintf(ns_pattern, sizeof(ns_pattern), ":%s>", tag);
char* ns_end = content_start;
while ((ns_end = strstr(ns_end, ns_pattern)) != NULL && ns_end < section_end) {
char* end_begin = ns_end;
while (end_begin > content_start && *end_begin != '<') end_begin--;
if (end_begin > content_start && *end_begin == '<' && *(end_begin + 1) == '/') {
tag_end = end_begin;
break;
}
ns_end++;
}
}
char* tag_end = strstr(content_start, end_tag);
if (!tag_end || tag_end > section_end) {
return NULL;
}
@@ -66,13 +302,81 @@ char* find_tag_in_section(const char* section_start, const char* section_end, co
strncpy(result, content_start, len);
result[len] = '\0';
char* trimmed = trim_whitespace(result);
return trimmed;
return trim_whitespace(result);
}
// Helper function to find XML tag content (namespace-aware)
char* find_tag_content_ns(const char* xml, const char* tag) {
static char result[1024];
char ns_pattern[256], end_pattern[256];
// Look for pattern ":tagname>" to handle namespaces
snprintf(ns_pattern, sizeof(ns_pattern), ":%s>", tag);
snprintf(end_pattern, sizeof(end_pattern), "</%s>", tag);
// First try to find namespace version (:tag>)
char* ns_start = strstr(xml, ns_pattern);
char* start = NULL;
if (ns_start) {
// Found namespaced tag, find the opening '<'
char* tag_begin = ns_start;
while (tag_begin > xml && *tag_begin != '<') tag_begin--;
if (*tag_begin == '<') {
start = ns_start + strlen(ns_pattern);
}
}
// If namespace version not found, try regular version
if (!start) {
char start_tag[256];
snprintf(start_tag, sizeof(start_tag), "<%s>", tag);
char* regular_start = strstr(xml, start_tag);
if (regular_start) {
start = regular_start + strlen(start_tag);
}
}
if (!start) return NULL;
// Look for end tag (try both namespaced and regular)
char ns_end_pattern[256];
snprintf(ns_end_pattern, sizeof(ns_end_pattern), "</%s>", tag);
char* end = strstr(start, ns_end_pattern);
if (!end) {
// Try with different namespace prefix
snprintf(ns_end_pattern, sizeof(ns_end_pattern), ":%s>", tag);
char* ns_end = strstr(start, ns_end_pattern);
if (ns_end) {
char* end_tag_begin = ns_end;
while (end_tag_begin > start && *end_tag_begin != '<') end_tag_begin--;
if (*end_tag_begin == '<' && *(end_tag_begin + 1) == '/') {
end = ns_end + strlen(ns_end_pattern);
// Backtrack to find the actual end
end = end_tag_begin;
}
}
}
if (!end) return NULL;
size_t len = end - start;
if (len >= sizeof(result)) len = sizeof(result) - 1;
strncpy(result, start, len);
result[len] = '\0';
return trim_whitespace(result);
}
// Helper function to find XML tag content
char* find_tag_content(const char* xml, const char* tag) {
static char result[1024];
// First try namespace-aware search
char* result = find_tag_content_ns(xml, tag);
if (result) return result;
// Fallback to original method
static char fallback_result[1024];
char start_tag[256], end_tag[256];
snprintf(start_tag, sizeof(start_tag), "<%s>", tag);
snprintf(end_tag, sizeof(end_tag), "</%s>", tag);
@@ -85,11 +389,11 @@ char* find_tag_content(const char* xml, const char* tag) {
if (!end) return NULL;
size_t len = end - start;
if (len >= sizeof(result)) len = sizeof(result) - 1;
if (len >= sizeof(fallback_result)) len = sizeof(fallback_result) - 1;
strncpy(result, start, len);
result[len] = '\0';
return trim_whitespace(result);
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) {
@@ -139,6 +443,27 @@ int parse_xml_to_iso1(const char* xml_content, struct iso1EXIDocument* doc) {
doc->V2G_Message.Header.SessionID.bytesLen = len;
doc->V2G_Message_isUsed = 1;
}
} else {
// Search directly for namespaced SessionID
char* ns_start = strstr(xml_content, "<ns2:SessionID>");
if (ns_start) {
ns_start += strlen("<ns2:SessionID>");
char* ns_end = strstr(ns_start, "</ns2:SessionID>");
if (ns_end) {
size_t len_str = ns_end - ns_start;
static char session_id_temp[256];
if (len_str < sizeof(session_id_temp)) {
strncpy(session_id_temp, ns_start, len_str);
session_id_temp[len_str] = '\0';
session_id_str = trim_whitespace(session_id_temp);
size_t len;
if (parse_session_id(session_id_str, doc->V2G_Message.Header.SessionID.bytes, &len) == 0) {
doc->V2G_Message.Header.SessionID.bytesLen = len;
doc->V2G_Message_isUsed = 1;
}
}
}
}
}
// Check for CurrentDemandReq
@@ -261,6 +586,86 @@ int parse_xml_to_iso1(const char* xml_content, struct iso1EXIDocument* doc) {
return 0;
}
// Check for CurrentDemandRes
if (strstr(xml_content, "<CurrentDemandRes>") || strstr(xml_content, "<ns3:CurrentDemandRes>")) {
doc->V2G_Message.Body.CurrentDemandRes_isUsed = 1;
init_iso1CurrentDemandResType(&doc->V2G_Message.Body.CurrentDemandRes);
// Parse ResponseCode
char* response_code = find_tag_content(xml_content, "ResponseCode");
if (response_code) {
doc->V2G_Message.Body.CurrentDemandRes.ResponseCode = atoi(response_code);
}
// Parse DC_EVSEStatus
char* evse_isolation = find_tag_content_ns(xml_content, "EVSEIsolationStatus");
if (evse_isolation) {
doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus = atoi(evse_isolation);
doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEIsolationStatus_isUsed = 1u;
}
char* evse_status = find_tag_content_ns(xml_content, "EVSEStatusCode");
if (evse_status) {
doc->V2G_Message.Body.CurrentDemandRes.DC_EVSEStatus.EVSEStatusCode = atoi(evse_status);
}
// Parse EVSEPresentVoltage using bounded section approach
char* voltage_section = strstr(xml_content, "<EVSEPresentVoltage>");
if (!voltage_section) voltage_section = strstr(xml_content, "<ns3:EVSEPresentVoltage>");
if (voltage_section) {
char* voltage_end = strstr(voltage_section, "</EVSEPresentVoltage>");
if (!voltage_end) voltage_end = strstr(voltage_section, "</ns3:EVSEPresentVoltage>");
if (voltage_end) {
parse_physical_value_from_section(voltage_section, voltage_end, &doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage);
}
}
// Parse EVSEPresentCurrent using bounded section approach
char* current_section = strstr(xml_content, "<EVSEPresentCurrent>");
if (!current_section) current_section = strstr(xml_content, "<ns3:EVSEPresentCurrent>");
if (current_section) {
char* current_end = strstr(current_section, "</EVSEPresentCurrent>");
if (!current_end) current_end = strstr(current_section, "</ns3:EVSEPresentCurrent>");
if (current_end) {
parse_physical_value_from_section(current_section, current_end, &doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent);
}
}
// Parse limit achieved flags
char* current_limit = find_tag_content(xml_content, "EVSECurrentLimitAchieved");
if (current_limit) {
doc->V2G_Message.Body.CurrentDemandRes.EVSECurrentLimitAchieved = (strcmp(current_limit, "true") == 0);
}
char* voltage_limit = find_tag_content(xml_content, "EVSEVoltageLimitAchieved");
if (voltage_limit) {
doc->V2G_Message.Body.CurrentDemandRes.EVSEVoltageLimitAchieved = (strcmp(voltage_limit, "true") == 0);
}
char* power_limit = find_tag_content(xml_content, "EVSEPowerLimitAchieved");
if (power_limit) {
doc->V2G_Message.Body.CurrentDemandRes.EVSEPowerLimitAchieved = (strcmp(power_limit, "true") == 0);
}
// Parse EVSEID
char* evseid = find_tag_content(xml_content, "EVSEID");
if (evseid) {
size_t len = strlen(evseid);
if (len < sizeof(doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters)) {
memcpy(doc->V2G_Message.Body.CurrentDemandRes.EVSEID.characters, evseid, len);
doc->V2G_Message.Body.CurrentDemandRes.EVSEID.charactersLen = len;
}
}
// Parse SAScheduleTupleID
char* sa_schedule = find_tag_content(xml_content, "SAScheduleTupleID");
if (sa_schedule) {
doc->V2G_Message.Body.CurrentDemandRes.SAScheduleTupleID = atoi(sa_schedule);
}
return 0;
}
return -1; // Unsupported message type
}
@@ -281,23 +686,7 @@ int readEXIFile(char* file, uint8_t* buffer, size_t buffer_size, size_t *bytes_r
return 0;
}
// Helper functions for Wireshark XML output
const char* get_unit_string(int unit) {
switch(unit) {
case 2: return "s"; // seconds
case 3: return "A"; // amperes
case 4: return "V"; // volts
case 5: return "W"; // watts
default: return ""; // fallback to number
}
}
const char* get_error_string(int error) {
switch(error) {
case 0: return "NO_ERROR";
default: return ""; // fallback to number
}
}
// Helper functions for Wireshark XML output removed - using numeric values directly
void print_xml_header_wireshark() {
printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
@@ -332,23 +721,13 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
printf("<ns3:EVSEPresentVoltage>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Multiplier);
const char* unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentVoltage.Value);
printf("</ns3:EVSEPresentVoltage>");
printf("<ns3:EVSEPresentCurrent>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Multiplier);
unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandRes.EVSEPresentCurrent.Value);
printf("</ns3:EVSEPresentCurrent>");
@@ -366,47 +745,27 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
printf("<ns3:DC_EVStatus>");
printf("<ns4:EVReady>%s</ns4:EVReady>", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVReady ? "true" : "false");
const char* error_str = get_error_string(doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
if (strlen(error_str) > 0) {
printf("<ns4:EVErrorCode>%s</ns4:EVErrorCode>", error_str);
} else {
printf("<ns4:EVErrorCode>%d</ns4:EVErrorCode>", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVErrorCode);
}
printf("<ns4:EVRESSSOC>%d</ns4:EVRESSSOC>", doc->V2G_Message.Body.CurrentDemandReq.DC_EVStatus.EVRESSSOC);
printf("</ns3:DC_EVStatus>");
printf("<ns3:EVTargetCurrent>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Multiplier);
const char* unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetCurrent.Value);
printf("</ns3:EVTargetCurrent>");
printf("<ns3:EVTargetVoltage>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Multiplier);
unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVTargetVoltage.Value);
printf("</ns3:EVTargetVoltage>");
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit_isUsed) {
printf("<ns3:EVMaximumVoltageLimit>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Multiplier);
unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumVoltageLimit.Value);
printf("</ns3:EVMaximumVoltageLimit>");
}
@@ -414,12 +773,7 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit_isUsed) {
printf("<ns3:EVMaximumCurrentLimit>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Multiplier);
unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumCurrentLimit.Value);
printf("</ns3:EVMaximumCurrentLimit>");
}
@@ -427,12 +781,7 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
if (doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit_isUsed) {
printf("<ns3:EVMaximumPowerLimit>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Multiplier);
unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.EVMaximumPowerLimit.Value);
printf("</ns3:EVMaximumPowerLimit>");
}
@@ -446,12 +795,7 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC_isUsed) {
printf("<ns3:RemainingTimeToFullSoC>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Multiplier);
unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToFullSoC.Value);
printf("</ns3:RemainingTimeToFullSoC>");
}
@@ -459,12 +803,7 @@ void print_iso1_xml_wireshark(struct iso1EXIDocument* doc) {
if (doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC_isUsed) {
printf("<ns3:RemainingTimeToBulkSoC>");
printf("<ns4:Multiplier>%d</ns4:Multiplier>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Multiplier);
unit_str = get_unit_string(doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit);
if (strlen(unit_str) > 0) {
printf("<ns4:Unit>%s</ns4:Unit>", unit_str);
} else {
printf("<ns4:Unit>%d</ns4:Unit>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Unit);
}
printf("<ns4:Value>%d</ns4:Value>", doc->V2G_Message.Body.CurrentDemandReq.RemainingTimeToBulkSoC.Value);
printf("</ns3:RemainingTimeToBulkSoC>");
}
@@ -656,10 +995,18 @@ int main(int argc, char *argv[]) {
// Parse XML to ISO1 document structure
if (parse_xml_to_iso1(xml_content, &iso1Doc) != 0) {
printf("Error parsing XML file\\n");
printf("Error parsing XML file - no supported message type found\\n");
free(xml_content);
return -1;
}
// Show encoding info only when redirecting to file (keeps terminal output clean)
struct stat stat_buf_debug;
int is_file_redirect = (fstat(fileno(stdout), &stat_buf_debug) == 0 && S_ISREG(stat_buf_debug.st_mode));
if (is_file_redirect) {
fprintf(stderr, "XML parsing successful\\n");
fprintf(stderr, "SessionID length: %d\\n", iso1Doc.V2G_Message.Header.SessionID.bytesLen);
fprintf(stderr, "CurrentDemandReq_isUsed: %s\\n", iso1Doc.V2G_Message.Body.CurrentDemandReq_isUsed ? "true" : "false");
}
free(xml_content);
@@ -677,8 +1024,27 @@ int main(int argc, char *argv[]) {
return -1;
}
// Write EXI data to stdout (binary)
// Check if output is redirected (for Windows/MINGW compatibility)
// Use environment variable approach or check for redirection
char* term = getenv("TERM");
int use_hex_output = (term != NULL) || isatty(fileno(stdout));
// Also check if user specifically wants binary output by checking for redirection
struct stat stat_buf;
if (fstat(fileno(stdout), &stat_buf) == 0 && S_ISREG(stat_buf.st_mode)) {
use_hex_output = 0; // Regular file, use binary
}
if (use_hex_output) {
// Terminal output: show hex string only
for(size_t i = 0; i < pos; i++) {
printf("%02X", buffer[i]);
}
printf("\n");
} else {
// Redirected output: write binary data
fwrite(buffer, 1, pos, stdout);
}
return 0;
}
@@ -697,6 +1063,28 @@ int main(int argc, char *argv[]) {
}
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

Binary file not shown.

BIN
test1.exi Normal file

Binary file not shown.

BIN
test2.exi Normal file

Binary file not shown.

BIN
test3.exi Normal file

Binary file not shown.

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<V2G_Message xmlns="urn:iso:15118:2:2013:MsgDef"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<SessionID>4142423030303831</SessionID>
</Header>
<Body>
<CurrentDemandReq>
<DC_EVStatus>
<EVReady>true</EVReady>
<EVErrorCode>0</EVErrorCode>
<EVRESSSOC>100</EVRESSSOC>
</DC_EVStatus>
<EVTargetCurrent>
<Multiplier>0</Multiplier>
<Unit>3</Unit>
<Value>5</Value>
</EVTargetCurrent>
<EVTargetVoltage>
<Multiplier>0</Multiplier>
<Unit>4</Unit>
<Value>460</Value>
</EVTargetVoltage>
<EVMaximumVoltageLimit>
<Multiplier>0</Multiplier>
<Unit>4</Unit>
<Value>471</Value>
</EVMaximumVoltageLimit>
<EVMaximumCurrentLimit>
<Multiplier>0</Multiplier>
<Unit>3</Unit>
<Value>100</Value>
</EVMaximumCurrentLimit>
<EVMaximumPowerLimit>
<Multiplier>3</Multiplier>
<Unit>5</Unit>
<Value>50</Value>
</EVMaximumPowerLimit>
<BulkChargingComplete>false</BulkChargingComplete>
<ChargingComplete>true</ChargingComplete>
<RemainingTimeToFullSoC>
<Multiplier>0</Multiplier>
<Unit>2</Unit>
<Value>0</Value>
</RemainingTimeToFullSoC>
<RemainingTimeToBulkSoC>
<Multiplier>0</Multiplier>
<Unit>2</Unit>
<Value>0</Value>
</RemainingTimeToBulkSoC>
</CurrentDemandReq>
</Body>
</V2G_Message>

View File

@@ -1 +0,0 @@
Error encoding to EXI (error: -109)

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<V2G_Message xmlns="urn:iso:15118:2:2013:MsgDef"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<SessionID>4142423030303831</SessionID>
</Header>
<Body>
<CurrentDemandReq>
<DC_EVStatus>
<EVReady>true</EVReady>
<EVErrorCode>0</EVErrorCode>
<EVRESSSOC>100</EVRESSSOC>
</DC_EVStatus>
<EVTargetCurrent>
<Multiplier>0</Multiplier>
<Unit>3</Unit>
<Value>1</Value>
</EVTargetCurrent>
<EVTargetVoltage>
<Multiplier>0</Multiplier>
<Unit>4</Unit>
<Value>460</Value>
</EVTargetVoltage>
<EVMaximumVoltageLimit>
<Multiplier>0</Multiplier>
<Unit>4</Unit>
<Value>471</Value>
</EVMaximumVoltageLimit>
<EVMaximumCurrentLimit>
<Multiplier>0</Multiplier>
<Unit>3</Unit>
<Value>100</Value>
</EVMaximumCurrentLimit>
<EVMaximumPowerLimit>
<Multiplier>3</Multiplier>
<Unit>5</Unit>
<Value>50</Value>
</EVMaximumPowerLimit>
<BulkChargingComplete>false</BulkChargingComplete>
<ChargingComplete>true</ChargingComplete>
<RemainingTimeToFullSoC>
<Multiplier>0</Multiplier>
<Unit>2</Unit>
<Value>0</Value>
</RemainingTimeToFullSoC>
<RemainingTimeToBulkSoC>
<Multiplier>0</Multiplier>
<Unit>2</Unit>
<Value>0</Value>
</RemainingTimeToBulkSoC>
</CurrentDemandReq>
</Body>
</V2G_Message>

View File

@@ -1 +0,0 @@
<EFBFBD><EFBFBD>P<><50>  P

View File

@@ -1,47 +0,0 @@
DEBUG: Parsing EVTargetCurrent section
DEBUG find_tag_in_section: Looking for 'Multiplier' in section of 137 chars
DEBUG: Found 'Multiplier' = '0'
DEBUG find_tag_in_section: Looking for 'Unit' in section of 137 chars
DEBUG: Found 'Unit' = '3'
DEBUG find_tag_in_section: Looking for 'Value' in section of 137 chars
DEBUG: Found 'Value' = '1'
DEBUG PhysicalValue: mult='0'->0, unit='3'->3, value='1'->1
DEBUG: Parsing EVTargetVoltage section
DEBUG find_tag_in_section: Looking for 'Multiplier' in section of 139 chars
DEBUG: Found 'Multiplier' = '0'
DEBUG find_tag_in_section: Looking for 'Unit' in section of 139 chars
DEBUG: Found 'Unit' = '4'
DEBUG find_tag_in_section: Looking for 'Value' in section of 139 chars
DEBUG: Found 'Value' = '460'
DEBUG PhysicalValue: mult='0'->0, unit='4'->4, value='460'->460
DEBUG find_tag_in_section: Looking for 'Multiplier' in section of 145 chars
DEBUG: Found 'Multiplier' = '0'
DEBUG find_tag_in_section: Looking for 'Unit' in section of 145 chars
DEBUG: Found 'Unit' = '4'
DEBUG find_tag_in_section: Looking for 'Value' in section of 145 chars
DEBUG: Found 'Value' = '471'
DEBUG PhysicalValue: mult='0'->0, unit='4'->4, value='471'->471
DEBUG find_tag_in_section: Looking for 'Multiplier' in section of 145 chars
DEBUG: Found 'Multiplier' = '0'
DEBUG find_tag_in_section: Looking for 'Unit' in section of 145 chars
DEBUG: Found 'Unit' = '3'
DEBUG find_tag_in_section: Looking for 'Value' in section of 145 chars
DEBUG: Found 'Value' = '100'
DEBUG PhysicalValue: mult='0'->0, unit='3'->3, value='100'->100
DEBUG find_tag_in_section: Looking for 'Multiplier' in section of 142 chars
DEBUG: Found 'Multiplier' = '3'
DEBUG find_tag_in_section: Looking for 'Unit' in section of 142 chars
DEBUG: Found 'Unit' = '5'
DEBUG find_tag_in_section: Looking for 'Value' in section of 142 chars
DEBUG: Found 'Value' = '50'
DEBUG PhysicalValue: mult='3'->3, unit='5'->5, value='50'->50
DEBUG: Parsed V2G Message:
V2G_Message_isUsed: 1
CurrentDemandReq_isUsed: 1
EVReady: 1
EVErrorCode: 0
EVRESSSOC: 100
EVTargetCurrent: 1 (Mult: 0, Unit: 3)
EVTargetVoltage: 460 (Mult: 0, Unit: 4)
ChargingComplete: 1
<EFBFBD><EFBFBD>P<><50>  P

1
test5ws.xml Normal file
View File

@@ -0,0 +1 @@
<?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>NO_ERROR</ns4:EVErrorCode><ns4:EVRESSSOC>100</ns4:EVRESSSOC></ns3:DC_EVStatus><ns3:EVTargetCurrent><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>A</ns4:Unit><ns4:Value>1</ns4:Value></ns3:EVTargetCurrent><ns3:EVMaximumVoltageLimit><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>V</ns4:Unit><ns4:Value>471</ns4:Value></ns3:EVMaximumVoltageLimit><ns3:EVMaximumCurrentLimit><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>A</ns4:Unit><ns4:Value>100</ns4:Value></ns3:EVMaximumCurrentLimit><ns3:EVMaximumPowerLimit><ns4:Multiplier>3</ns4:Multiplier><ns4:Unit>W</ns4:Unit><ns4:Value>50</ns4:Value></ns3:EVMaximumPowerLimit><ns3:BulkChargingComplete>false</ns3:BulkChargingComplete><ns3:ChargingComplete>true</ns3:ChargingComplete><ns3:RemainingTimeToFullSoC><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>s</ns4:Unit><ns4:Value>0</ns4:Value></ns3:RemainingTimeToFullSoC><ns3:RemainingTimeToBulkSoC><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>s</ns4:Unit><ns4:Value>0</ns4:Value></ns3:RemainingTimeToBulkSoC><ns3:EVTargetVoltage><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>V</ns4:Unit><ns4:Value>460</ns4:Value></ns3:EVTargetVoltage></ns3:CurrentDemandReq></ns1:Body></ns1:V2G_Message>