Implements C# console application for decoding ISO 15118-2, DIN SPEC 70121, and SAP protocol messages from binary data. Features include: - V2G Transfer Protocol header parsing - EXI, DIN, and SAP message type support - Network packet analysis (Ethernet/IPv6/TCP) - Hex dump utilities - Raw data parsing from hex files - Sample raw data for testing Successfully tested with sample V2G EXI message data. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
194 lines
9.0 KiB
C#
194 lines
9.0 KiB
C#
using System;
|
|
using System.IO;
|
|
|
|
namespace V2GProtocol
|
|
{
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Console.WriteLine("=== V2G Transfer Protocol Decoder/Encoder ===");
|
|
Console.WriteLine("ISO 15118-2 / DIN SPEC 70121 / SAP Protocol");
|
|
Console.WriteLine();
|
|
|
|
try
|
|
{
|
|
// 기본 데이터 파일 경로
|
|
string dataFilePath = @"data\632 raw data.txt";
|
|
|
|
if (args.Length > 0)
|
|
{
|
|
dataFilePath = args[0];
|
|
}
|
|
|
|
if (!File.Exists(dataFilePath))
|
|
{
|
|
Console.WriteLine($"Error: Data file not found: {dataFilePath}");
|
|
Console.WriteLine("Usage: V2GDecoder.exe [data_file_path]");
|
|
return;
|
|
}
|
|
|
|
Console.WriteLine($"Loading data from: {dataFilePath}");
|
|
|
|
// 헥스 파일에서 바이너리 데이터 파싱
|
|
byte[] rawData = V2GDecoder.ParseHexFile(dataFilePath);
|
|
|
|
Console.WriteLine($"Parsed {rawData.Length} bytes from hex file");
|
|
Console.WriteLine();
|
|
|
|
// 전체 데이터 헥스 덤프
|
|
Console.WriteLine("=== Raw Data Hex Dump ===");
|
|
Console.WriteLine(V2GDecoder.BytesToHex(rawData));
|
|
Console.WriteLine();
|
|
|
|
// V2G 메시지 디코딩 시도
|
|
Console.WriteLine("=== V2G Message Analysis ===");
|
|
|
|
// 전체 데이터에서 V2G 메시지 찾기
|
|
AnalyzeV2GMessages(rawData);
|
|
|
|
// 네트워크 패킷 분석 (이더넷/IPv6/TCP 헤더 포함인 경우)
|
|
AnalyzeNetworkPacket(rawData);
|
|
|
|
Console.WriteLine("\nPress any key to exit...");
|
|
Console.ReadKey();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error: {ex.Message}");
|
|
Console.WriteLine(ex.StackTrace);
|
|
}
|
|
}
|
|
|
|
static void AnalyzeV2GMessages(byte[] data)
|
|
{
|
|
// V2G Transfer Protocol 시그니처 찾기
|
|
for (int i = 0; i < data.Length - 8; i++)
|
|
{
|
|
if (data[i] == 0x01 && data[i + 1] == 0xFE) // V2G TP Header
|
|
{
|
|
Console.WriteLine($"Potential V2G message found at offset 0x{i:X4}");
|
|
|
|
try
|
|
{
|
|
byte[] messageData = new byte[data.Length - i];
|
|
Array.Copy(data, i, messageData, 0, messageData.Length);
|
|
|
|
var message = V2GDecoder.DecodeMessage(messageData);
|
|
|
|
Console.WriteLine($" Version: 0x{message.Version:X2}");
|
|
Console.WriteLine($" Inverse Version: 0x{message.InverseVersion:X2}");
|
|
Console.WriteLine($" Payload Type: 0x{(ushort)message.PayloadType:X4} ({message.PayloadType})");
|
|
Console.WriteLine($" Payload Length: {message.PayloadLength} bytes");
|
|
Console.WriteLine($" Valid: {message.IsValid}");
|
|
Console.WriteLine();
|
|
|
|
if (message.IsValid && message.Payload != null)
|
|
{
|
|
Console.WriteLine("=== Decoded Message Content ===");
|
|
Console.WriteLine(message.DecodedContent);
|
|
Console.WriteLine();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($" Error decoding message: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void AnalyzeNetworkPacket(byte[] data)
|
|
{
|
|
Console.WriteLine("=== Network Packet Analysis ===");
|
|
|
|
if (data.Length < 54) // Minimum Ethernet + IPv6 + TCP header size
|
|
{
|
|
Console.WriteLine("Data too short for network packet analysis");
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
// 이더넷 헤더 분석 (14 bytes)
|
|
Console.WriteLine("Ethernet Header:");
|
|
Console.WriteLine($" Destination MAC: {string.Join(":", data[0..6].Select(b => b.ToString("X2")))}");
|
|
Console.WriteLine($" Source MAC: {string.Join(":", data[6..12].Select(b => b.ToString("X2")))}");
|
|
Console.WriteLine($" EtherType: 0x{(data[12] << 8 | data[13]):X4}");
|
|
|
|
ushort etherType = (ushort)(data[12] << 8 | data[13]);
|
|
|
|
if (etherType == 0x86DD) // IPv6
|
|
{
|
|
Console.WriteLine("\nIPv6 Header:");
|
|
int ipv6Offset = 14;
|
|
byte versionTrafficClass = data[ipv6Offset];
|
|
Console.WriteLine($" Version: {(versionTrafficClass >> 4) & 0xF}");
|
|
Console.WriteLine($" Traffic Class: 0x{((versionTrafficClass & 0xF) << 4 | (data[ipv6Offset + 1] >> 4)):X2}");
|
|
|
|
ushort payloadLength = (ushort)(data[ipv6Offset + 4] << 8 | data[ipv6Offset + 5]);
|
|
byte nextHeader = data[ipv6Offset + 6];
|
|
byte hopLimit = data[ipv6Offset + 7];
|
|
|
|
Console.WriteLine($" Payload Length: {payloadLength}");
|
|
Console.WriteLine($" Next Header: 0x{nextHeader:X2} ({(nextHeader == 6 ? "TCP" : "Other")})");
|
|
Console.WriteLine($" Hop Limit: {hopLimit}");
|
|
|
|
// IPv6 주소는 16바이트씩
|
|
var srcAddr = data[(ipv6Offset + 8)..(ipv6Offset + 24)];
|
|
var dstAddr = data[(ipv6Offset + 24)..(ipv6Offset + 40)];
|
|
|
|
Console.WriteLine($" Source Address: {string.Join(":", Enumerable.Range(0, 8).Select(i => $"{srcAddr[i*2]:X2}{srcAddr[i*2+1]:X2}"))}");
|
|
Console.WriteLine($" Destination Address: {string.Join(":", Enumerable.Range(0, 8).Select(i => $"{dstAddr[i*2]:X2}{dstAddr[i*2+1]:X2}"))}");
|
|
|
|
if (nextHeader == 6) // TCP
|
|
{
|
|
int tcpOffset = ipv6Offset + 40;
|
|
if (data.Length > tcpOffset + 20)
|
|
{
|
|
Console.WriteLine("\nTCP Header:");
|
|
ushort srcPort = (ushort)(data[tcpOffset] << 8 | data[tcpOffset + 1]);
|
|
ushort dstPort = (ushort)(data[tcpOffset + 2] << 8 | data[tcpOffset + 3]);
|
|
|
|
Console.WriteLine($" Source Port: {srcPort}");
|
|
Console.WriteLine($" Destination Port: {dstPort}");
|
|
|
|
// V2G는 일반적으로 포트 15118을 사용
|
|
if (srcPort == 15118 || dstPort == 15118)
|
|
{
|
|
Console.WriteLine(" -> V2G Communication detected (port 15118)!");
|
|
}
|
|
|
|
// TCP 데이터 시작 위치 계산
|
|
byte dataOffset = (byte)((data[tcpOffset + 12] >> 4) * 4);
|
|
int tcpDataOffset = tcpOffset + dataOffset;
|
|
|
|
if (tcpDataOffset < data.Length)
|
|
{
|
|
Console.WriteLine($"\nTCP Payload (starting at offset 0x{tcpDataOffset:X4}):");
|
|
byte[] tcpPayload = data[tcpDataOffset..];
|
|
|
|
// TCP 페이로드에서 V2G 메시지 찾기
|
|
if (tcpPayload.Length > 8 && tcpPayload[0] == 0x01 && tcpPayload[1] == 0xFE)
|
|
{
|
|
Console.WriteLine("V2G Message found in TCP payload!");
|
|
var v2gMessage = V2GDecoder.DecodeMessage(tcpPayload);
|
|
Console.WriteLine(v2gMessage.DecodedContent);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("TCP Payload hex dump:");
|
|
Console.WriteLine(V2GDecoder.BytesToHex(tcpPayload));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Console.WriteLine($"Error analyzing network packet: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
} |