feat: Complete cross-platform build system and folder reorganization
- Reorganize project structure: Port/ → DotNet/, VC/, C++/ - Add comprehensive cross-platform build automation - Windows: build_all.bat, build.bat files for all components - Linux/macOS: build_all.sh, build.sh files for all components - Update all build scripts with correct folder paths - Create test automation scripts (test_all.bat/sh) - Update documentation to reflect new structure - Maintain 100% roundtrip accuracy for test5.exi (pure EXI) - Support both Windows MSBuild and Linux GCC compilation 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
209
DotNet/V2G/V2GProtocol.cs
Normal file
209
DotNet/V2G/V2GProtocol.cs
Normal file
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2024 C# Port
|
||||
* Original Copyright (C) 2007-2018 Siemens AG
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published
|
||||
* by the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using V2GDecoderNet.EXI;
|
||||
|
||||
namespace V2GDecoderNet.V2G
|
||||
{
|
||||
/// <summary>
|
||||
/// V2G Transfer Protocol constants and definitions
|
||||
/// </summary>
|
||||
public static class V2GProtocol
|
||||
{
|
||||
// Network protocol patterns
|
||||
public const ushort ETH_TYPE_IPV6 = 0x86DD;
|
||||
public const byte IPV6_NEXT_HEADER_TCP = 0x06;
|
||||
public const ushort TCP_V2G_PORT = 15118;
|
||||
|
||||
// V2G Transfer Protocol patterns
|
||||
public const byte V2G_PROTOCOL_VERSION = 0x01;
|
||||
public const byte V2G_INV_PROTOCOL_VERSION = 0xFE;
|
||||
public const ushort V2G_PAYLOAD_ISO_DIN_SAP = 0x8001;
|
||||
public const ushort V2G_PAYLOAD_ISO2 = 0x8002;
|
||||
public const ushort EXI_START_PATTERN = 0x8098;
|
||||
|
||||
/// <summary>
|
||||
/// Get payload type name for display
|
||||
/// </summary>
|
||||
/// <param name="payloadType">Payload type value</param>
|
||||
/// <returns>Human-readable payload type name</returns>
|
||||
public static string GetPayloadTypeName(ushort payloadType)
|
||||
{
|
||||
return payloadType switch
|
||||
{
|
||||
V2G_PAYLOAD_ISO_DIN_SAP => "ISO 15118-2/DIN/SAP",
|
||||
V2G_PAYLOAD_ISO2 => "ISO 15118-20",
|
||||
_ => "Unknown"
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract EXI body from V2G Transfer Protocol data
|
||||
/// </summary>
|
||||
/// <param name="inputData">Input data containing V2GTP header and EXI body</param>
|
||||
/// <returns>Extracted EXI body data</returns>
|
||||
public static byte[] ExtractEXIBody(byte[] inputData)
|
||||
{
|
||||
if (inputData == null || inputData.Length < 8)
|
||||
{
|
||||
// Too small for V2GTP header, assume it's pure EXI
|
||||
return inputData ?? Array.Empty<byte>();
|
||||
}
|
||||
|
||||
// First, look for V2G Transfer Protocol header anywhere in the data
|
||||
// Pattern: 0x01 0xFE 0x80 0x01 (V2GTP header for ISO/DIN/SAP)
|
||||
for (int i = 0; i <= inputData.Length - 8; i++)
|
||||
{
|
||||
if (inputData[i] == V2G_PROTOCOL_VERSION && inputData[i + 1] == V2G_INV_PROTOCOL_VERSION)
|
||||
{
|
||||
ushort payloadType = (ushort)((inputData[i + 2] << 8) | inputData[i + 3]);
|
||||
|
||||
if (payloadType == V2G_PAYLOAD_ISO_DIN_SAP || payloadType == V2G_PAYLOAD_ISO2)
|
||||
{
|
||||
// Valid V2GTP header found: skip 8-byte header to get EXI body
|
||||
int exiStart = i + 8;
|
||||
var exiBody = new byte[inputData.Length - exiStart];
|
||||
Array.Copy(inputData, exiStart, exiBody, 0, exiBody.Length);
|
||||
return exiBody;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no V2GTP header found, look for EXI start pattern anywhere in the data
|
||||
for (int i = 0; i <= inputData.Length - 2; i++)
|
||||
{
|
||||
ushort pattern = (ushort)((inputData[i] << 8) | inputData[i + 1]);
|
||||
if (pattern == EXI_START_PATTERN)
|
||||
{
|
||||
// Found EXI start pattern
|
||||
var exiBody = new byte[inputData.Length - i];
|
||||
Array.Copy(inputData, i, exiBody, 0, exiBody.Length);
|
||||
return exiBody;
|
||||
}
|
||||
}
|
||||
|
||||
// No pattern found, assume it's pure EXI
|
||||
return inputData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analyze complete packet structure
|
||||
/// </summary>
|
||||
/// <param name="data">Packet data</param>
|
||||
/// <returns>Analysis result</returns>
|
||||
public static PacketAnalysis AnalyzeDataStructure(byte[] data)
|
||||
{
|
||||
var analysis = new PacketAnalysis
|
||||
{
|
||||
TotalSize = data?.Length ?? 0,
|
||||
HasEthernetHeader = false,
|
||||
HasIPv6Header = false,
|
||||
HasTCPHeader = false,
|
||||
HasV2GTPHeader = false,
|
||||
V2GTPPayloadType = 0,
|
||||
EXIBodyOffset = 0,
|
||||
EXIBodyLength = 0
|
||||
};
|
||||
|
||||
if (data == null || data.Length == 0)
|
||||
return analysis;
|
||||
|
||||
int offset = 0;
|
||||
|
||||
// Check for Ethernet header (at least 14 bytes)
|
||||
if (data.Length >= 14)
|
||||
{
|
||||
ushort etherType = (ushort)((data[12] << 8) | data[13]);
|
||||
if (etherType == ETH_TYPE_IPV6)
|
||||
{
|
||||
analysis.HasEthernetHeader = true;
|
||||
offset = 14;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for IPv6 header (40 bytes)
|
||||
if (analysis.HasEthernetHeader && data.Length >= offset + 40)
|
||||
{
|
||||
byte version = (byte)((data[offset] >> 4) & 0x0F);
|
||||
if (version == 6)
|
||||
{
|
||||
analysis.HasIPv6Header = true;
|
||||
byte nextHeader = data[offset + 6];
|
||||
if (nextHeader == IPV6_NEXT_HEADER_TCP)
|
||||
{
|
||||
offset += 40;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for TCP header (at least 20 bytes)
|
||||
if (analysis.HasIPv6Header && data.Length >= offset + 20)
|
||||
{
|
||||
ushort destPort = (ushort)((data[offset + 2] << 8) | data[offset + 3]);
|
||||
if (destPort == TCP_V2G_PORT)
|
||||
{
|
||||
analysis.HasTCPHeader = true;
|
||||
byte headerLength = (byte)((data[offset + 12] >> 4) * 4);
|
||||
offset += headerLength;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for V2GTP header
|
||||
if (data.Length >= offset + 8)
|
||||
{
|
||||
if (data[offset] == V2G_PROTOCOL_VERSION && data[offset + 1] == V2G_INV_PROTOCOL_VERSION)
|
||||
{
|
||||
analysis.HasV2GTPHeader = true;
|
||||
analysis.V2GTPPayloadType = (ushort)((data[offset + 2] << 8) | data[offset + 3]);
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Remaining data is EXI body
|
||||
analysis.EXIBodyOffset = offset;
|
||||
analysis.EXIBodyLength = Math.Max(0, data.Length - offset);
|
||||
|
||||
return analysis;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Packet analysis result
|
||||
/// </summary>
|
||||
public class PacketAnalysis
|
||||
{
|
||||
public int TotalSize { get; set; }
|
||||
public bool HasEthernetHeader { get; set; }
|
||||
public bool HasIPv6Header { get; set; }
|
||||
public bool HasTCPHeader { get; set; }
|
||||
public bool HasV2GTPHeader { get; set; }
|
||||
public ushort V2GTPPayloadType { get; set; }
|
||||
public int EXIBodyOffset { get; set; }
|
||||
public int EXIBodyLength { get; set; }
|
||||
|
||||
public string GetPayloadTypeName()
|
||||
{
|
||||
return V2GProtocol.GetPayloadTypeName(V2GTPPayloadType);
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
var parts = new List<string>();
|
||||
if (HasEthernetHeader) parts.Add("Ethernet");
|
||||
if (HasIPv6Header) parts.Add("IPv6");
|
||||
if (HasTCPHeader) parts.Add("TCP");
|
||||
if (HasV2GTPHeader) parts.Add($"V2GTP ({GetPayloadTypeName()})");
|
||||
|
||||
var structure = parts.Count > 0 ? string.Join(" → ", parts) : "Raw data";
|
||||
return $"{structure} | EXI: {EXIBodyLength} bytes @ offset {EXIBodyOffset}";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user