file 정리
This commit is contained in:
@@ -1,661 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2024 C# Port
|
||||
*
|
||||
* Exact EXI Codec Program - Byte-compatible with OpenV2G C implementation
|
||||
* Produces identical binary output to original C code
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using V2GDecoderNet.EXI;
|
||||
using V2GDecoderNet.V2G;
|
||||
|
||||
namespace V2GDecoderNet
|
||||
{
|
||||
class ProgramExact
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("=== V2GDecoderNet - Exact EXI Codec ===");
|
||||
Console.WriteLine("Byte-compatible C# port of OpenV2G EXI implementation");
|
||||
Console.WriteLine();
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
ShowUsage();
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string command = args[0].ToLower();
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "decode-exact":
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Error: Input file required for decode-exact command");
|
||||
ShowUsage();
|
||||
return;
|
||||
}
|
||||
DecodeFileExact(args[1], args.Length > 2 ? args[2] : null);
|
||||
break;
|
||||
|
||||
case "encode-exact":
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Error: Input file required for encode-exact command");
|
||||
ShowUsage();
|
||||
return;
|
||||
}
|
||||
EncodeFileExact(args[1], args.Length > 2 ? args[2] : null);
|
||||
break;
|
||||
|
||||
case "test-exact":
|
||||
RunExactRoundtripTest(args.Length > 1 ? args[1] : "../../test1.exi");
|
||||
break;
|
||||
|
||||
case "test-all-exact":
|
||||
TestAllFilesExact();
|
||||
break;
|
||||
|
||||
case "debug-bits":
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Error: debug-bits requires input file");
|
||||
ShowUsage();
|
||||
return;
|
||||
}
|
||||
DebugBitLevel(args[1]);
|
||||
break;
|
||||
|
||||
case "decode-req":
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("Error: decode-req requires input file");
|
||||
ShowUsage();
|
||||
return;
|
||||
}
|
||||
DecodeCurrentDemandReqDirect(args[1]);
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.WriteLine($"Error: Unknown command '{command}'");
|
||||
ShowUsage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: {ex.Message}");
|
||||
if (ex is EXIExceptionExact exiEx)
|
||||
{
|
||||
Console.WriteLine($"EXI Error Code: {exiEx.ErrorCode}");
|
||||
Console.WriteLine($"EXI Error: {EXIExceptionExact.GetErrorMessage(exiEx.ErrorCode)}");
|
||||
}
|
||||
#if DEBUG
|
||||
Console.WriteLine($"Stack Trace: {ex.StackTrace}");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowUsage()
|
||||
{
|
||||
Console.WriteLine("Usage:");
|
||||
Console.WriteLine(" V2GDecoderNet decode-exact <input.exi> [output.xml]");
|
||||
Console.WriteLine(" V2GDecoderNet encode-exact <test-params> [output.exi]");
|
||||
Console.WriteLine(" V2GDecoderNet test-exact [input.exi]");
|
||||
Console.WriteLine(" V2GDecoderNet test-all-exact");
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Examples:");
|
||||
Console.WriteLine(" V2GDecoderNet decode-exact test1.exi test1_exact.xml");
|
||||
Console.WriteLine(" V2GDecoderNet test-exact test1.exi");
|
||||
Console.WriteLine(" V2GDecoderNet test-all-exact");
|
||||
}
|
||||
|
||||
static void DecodeFileExact(string inputFile, string? outputFile = null)
|
||||
{
|
||||
Console.WriteLine($"Exact decoding: {inputFile}");
|
||||
|
||||
if (!File.Exists(inputFile))
|
||||
{
|
||||
throw new FileNotFoundException($"Input file not found: {inputFile}");
|
||||
}
|
||||
|
||||
// Read EXI data
|
||||
byte[] exiData = File.ReadAllBytes(inputFile);
|
||||
Console.WriteLine($"Read {exiData.Length} bytes from {inputFile}");
|
||||
|
||||
// Extract EXI body from V2GTP data if present
|
||||
byte[] exiBody = ExtractEXIBody(exiData);
|
||||
|
||||
if (exiBody.Length != exiData.Length)
|
||||
{
|
||||
Console.WriteLine($"Extracted EXI body: {exiBody.Length} bytes (V2GTP header removed)");
|
||||
}
|
||||
|
||||
// Decode using exact EXI decoder
|
||||
var v2gMessage = EXIDecoderExact.DecodeV2GMessage(exiBody);
|
||||
|
||||
// Debug: Print decoded message values
|
||||
Console.WriteLine("\n=== Decoded Message Debug Info ===");
|
||||
if (v2gMessage.Body.CurrentDemandReq_isUsed)
|
||||
{
|
||||
var req = v2gMessage.Body.CurrentDemandReq;
|
||||
Console.WriteLine($"CurrentDemandReq detected:");
|
||||
Console.WriteLine($" EVRESSSOC: {req.DC_EVStatus.EVRESSSOC}");
|
||||
Console.WriteLine($" EVReady: {req.DC_EVStatus.EVReady}");
|
||||
Console.WriteLine($" EVErrorCode: {req.DC_EVStatus.EVErrorCode}");
|
||||
Console.WriteLine($" EVTargetCurrent: Mult={req.EVTargetCurrent.Multiplier}, Unit={req.EVTargetCurrent.Unit}, Value={req.EVTargetCurrent.Value}");
|
||||
Console.WriteLine($" EVMaximumVoltageLimit_isUsed: {req.EVMaximumVoltageLimit_isUsed}");
|
||||
if (req.EVMaximumVoltageLimit_isUsed)
|
||||
Console.WriteLine($" EVMaximumVoltageLimit: Mult={req.EVMaximumVoltageLimit.Multiplier}, Unit={req.EVMaximumVoltageLimit.Unit}, Value={req.EVMaximumVoltageLimit.Value}");
|
||||
Console.WriteLine($" ChargingComplete: {req.ChargingComplete} (isUsed: {req.ChargingComplete_isUsed})");
|
||||
Console.WriteLine($" EVTargetVoltage: Mult={req.EVTargetVoltage.Multiplier}, Unit={req.EVTargetVoltage.Unit}, Value={req.EVTargetVoltage.Value}");
|
||||
}
|
||||
Console.WriteLine("=====================================\n");
|
||||
|
||||
// Convert to XML representation
|
||||
string xmlOutput = MessageToXml(v2gMessage);
|
||||
|
||||
// Determine output file name
|
||||
outputFile ??= Path.ChangeExtension(inputFile, "_exact.xml");
|
||||
|
||||
// Write XML output
|
||||
File.WriteAllText(outputFile, xmlOutput);
|
||||
Console.WriteLine($"XML written to: {outputFile}");
|
||||
Console.WriteLine($"XML size: {xmlOutput.Length} characters");
|
||||
}
|
||||
|
||||
static void EncodeFileExact(string testParams, string? outputFile = null)
|
||||
{
|
||||
Console.WriteLine($"Exact encoding with test parameters: {testParams}");
|
||||
|
||||
// Create test message based on parameters or use default
|
||||
var message = CreateTestMessage();
|
||||
|
||||
// Encode using exact EXI encoder (temporary - needs universal encoder)
|
||||
byte[] exiData = new byte[] { 0x80 }; // TODO: Implement universal encoder
|
||||
|
||||
// Determine output file name
|
||||
outputFile ??= "test_exact_output.exi";
|
||||
|
||||
// Write EXI output
|
||||
File.WriteAllBytes(outputFile, exiData);
|
||||
Console.WriteLine($"EXI written to: {outputFile}");
|
||||
Console.WriteLine($"EXI size: {exiData.Length} bytes");
|
||||
|
||||
// Show hex dump
|
||||
Console.WriteLine("Hex dump:");
|
||||
ShowHexDump(exiData, 0, Math.Min(64, exiData.Length));
|
||||
}
|
||||
|
||||
static void RunExactRoundtripTest(string inputFile)
|
||||
{
|
||||
Console.WriteLine($"Running exact roundtrip test on: {inputFile}");
|
||||
|
||||
if (!File.Exists(inputFile))
|
||||
{
|
||||
throw new FileNotFoundException($"Input file not found: {inputFile}");
|
||||
}
|
||||
|
||||
// Step 1: Read original EXI file
|
||||
byte[] originalExi = File.ReadAllBytes(inputFile);
|
||||
Console.WriteLine($"Original EXI size: {originalExi.Length} bytes");
|
||||
|
||||
// Step 2: Extract EXI body
|
||||
byte[] exiBody = ExtractEXIBody(originalExi);
|
||||
Console.WriteLine($"EXI body size: {exiBody.Length} bytes");
|
||||
|
||||
// Step 3: Decode EXI to message using exact decoder
|
||||
var v2gMessage = EXIDecoderExact.DecodeV2GMessage(exiBody);
|
||||
Console.WriteLine("Decoded EXI to message structure");
|
||||
|
||||
// Step 4: Encode message back to EXI using exact encoder (temporary - needs universal encoder)
|
||||
byte[] newExi = new byte[] { 0x80 }; // TODO: Implement universal encoder
|
||||
Console.WriteLine($"Encoded message to EXI: {newExi.Length} bytes");
|
||||
|
||||
// Step 5: Compare original vs new EXI
|
||||
bool identical = exiBody.SequenceEqual(newExi);
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("=== Exact Roundtrip Test Results ===");
|
||||
Console.WriteLine($"Original EXI body: {exiBody.Length} bytes");
|
||||
Console.WriteLine($"New EXI: {newExi.Length} bytes");
|
||||
Console.WriteLine($"Files identical: {(identical ? "YES ✓" : "NO ✗")}");
|
||||
|
||||
if (!identical)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Differences found:");
|
||||
ShowDifferences(exiBody, newExi);
|
||||
|
||||
// Save files for comparison
|
||||
string originalFile = Path.ChangeExtension(inputFile, "_original_body.exi");
|
||||
string newFile = Path.ChangeExtension(inputFile, "_new_exact.exi");
|
||||
File.WriteAllBytes(originalFile, exiBody);
|
||||
File.WriteAllBytes(newFile, newExi);
|
||||
Console.WriteLine($"Saved original body to: {originalFile}");
|
||||
Console.WriteLine($"Saved new EXI to: {newFile}");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine(identical ? "✓ Exact roundtrip test PASSED" : "✗ Exact roundtrip test FAILED");
|
||||
}
|
||||
|
||||
static void TestAllFilesExact()
|
||||
{
|
||||
Console.WriteLine("Testing all EXI files with exact codec:");
|
||||
|
||||
string[] testFiles = { "test1.exi", "test2.exi", "test3.exi", "test4.exi", "test5.exi" };
|
||||
int passCount = 0;
|
||||
|
||||
foreach (string testFile in testFiles)
|
||||
{
|
||||
string fullPath = Path.Combine("../../", testFile);
|
||||
if (File.Exists(fullPath))
|
||||
{
|
||||
Console.WriteLine($"\n--- Testing {testFile} ---");
|
||||
try
|
||||
{
|
||||
RunExactRoundtripTest(fullPath);
|
||||
passCount++;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"FAILED: {ex.Message}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"Skipping {testFile} - file not found");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"\n=== Summary: {passCount}/{testFiles.Length} tests passed ===");
|
||||
}
|
||||
|
||||
static CurrentDemandResType CreateTestMessage()
|
||||
{
|
||||
return new CurrentDemandResType
|
||||
{
|
||||
ResponseCode = ResponseCodeType.OK,
|
||||
DC_EVSEStatus = new DC_EVSEStatusType
|
||||
{
|
||||
NotificationMaxDelay = 0,
|
||||
EVSENotification = EVSENotificationType.None,
|
||||
EVSEIsolationStatus = IsolationLevelType.Valid,
|
||||
EVSEIsolationStatus_isUsed = true,
|
||||
EVSEStatusCode = DC_EVSEStatusCodeType.EVSE_Ready
|
||||
},
|
||||
EVSEPresentVoltage = new PhysicalValueType(0, UnitSymbolType.V, 450),
|
||||
EVSEPresentCurrent = new PhysicalValueType(0, UnitSymbolType.A, 5),
|
||||
EVSECurrentLimitAchieved = false,
|
||||
EVSEVoltageLimitAchieved = false,
|
||||
EVSEPowerLimitAchieved = false,
|
||||
EVSEID = "Z",
|
||||
SAScheduleTupleID = 1
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert V2G message to XML format matching C print_iso1_xml_wireshark() exactly
|
||||
/// </summary>
|
||||
static string MessageToXml(V2GMessageExact v2gMessage)
|
||||
{
|
||||
var xml = new System.Text.StringBuilder();
|
||||
|
||||
// XML Header with full namespace declarations (matching C print_xml_header_wireshark)
|
||||
xml.AppendLine(@"<?xml version=""1.0"" encoding=""UTF-8""?>");
|
||||
xml.Append(@"<ns1:V2G_Message xmlns:ns1=""urn:iso:15118:2:2013:MsgDef""");
|
||||
xml.Append(@" xmlns:ns2=""urn:iso:15118:2:2013:MsgHeader""");
|
||||
xml.Append(@" xmlns:ns3=""urn:iso:15118:2:2013:MsgBody""");
|
||||
xml.AppendLine(@" xmlns:ns4=""urn:iso:15118:2:2013:MsgDataTypes"">");
|
||||
|
||||
// Header with SessionID
|
||||
xml.Append("<ns1:Header><ns2:SessionID>");
|
||||
if (!string.IsNullOrEmpty(v2gMessage.SessionID))
|
||||
{
|
||||
xml.Append(v2gMessage.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Default SessionID like C decoder output
|
||||
xml.Append("4142423030303831");
|
||||
}
|
||||
xml.AppendLine("</ns2:SessionID></ns1:Header>");
|
||||
|
||||
// Body
|
||||
xml.Append("<ns1:Body>");
|
||||
|
||||
if (v2gMessage.Body.CurrentDemandReq_isUsed)
|
||||
{
|
||||
WriteCurrentDemandReqXml(xml, v2gMessage.Body.CurrentDemandReq);
|
||||
}
|
||||
else if (v2gMessage.Body.CurrentDemandRes_isUsed)
|
||||
{
|
||||
WriteCurrentDemandResXml(xml, v2gMessage.Body.CurrentDemandRes);
|
||||
}
|
||||
else
|
||||
{
|
||||
xml.Append("<ns3:Unknown>Message type not recognized</ns3:Unknown>");
|
||||
}
|
||||
|
||||
xml.Append("</ns1:Body>");
|
||||
xml.Append("</ns1:V2G_Message>");
|
||||
|
||||
return xml.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write CurrentDemandReq XML matching C source exactly
|
||||
/// </summary>
|
||||
static void WriteCurrentDemandReqXml(System.Text.StringBuilder xml, CurrentDemandReqType req)
|
||||
{
|
||||
xml.Append("<ns3:CurrentDemandReq>");
|
||||
|
||||
// DC_EVStatus
|
||||
xml.Append("<ns3:DC_EVStatus>");
|
||||
xml.Append($"<ns4:EVReady>{(req.DC_EVStatus.EVReady ? "true" : "false")}</ns4:EVReady>");
|
||||
xml.Append($"<ns4:EVErrorCode>{req.DC_EVStatus.EVErrorCode}</ns4:EVErrorCode>");
|
||||
xml.Append($"<ns4:EVRESSSOC>{req.DC_EVStatus.EVRESSSOC}</ns4:EVRESSSOC>");
|
||||
xml.Append("</ns3:DC_EVStatus>");
|
||||
|
||||
// EVTargetCurrent
|
||||
xml.Append("<ns3:EVTargetCurrent>");
|
||||
xml.Append($"<ns4:Multiplier>{req.EVTargetCurrent.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)req.EVTargetCurrent.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{req.EVTargetCurrent.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:EVTargetCurrent>");
|
||||
|
||||
// EVMaximumVoltageLimit (optional)
|
||||
if (req.EVMaximumVoltageLimit_isUsed && req.EVMaximumVoltageLimit != null)
|
||||
{
|
||||
xml.Append("<ns3:EVMaximumVoltageLimit>");
|
||||
xml.Append($"<ns4:Multiplier>{req.EVMaximumVoltageLimit.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)req.EVMaximumVoltageLimit.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{req.EVMaximumVoltageLimit.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:EVMaximumVoltageLimit>");
|
||||
}
|
||||
|
||||
// EVMaximumCurrentLimit (optional)
|
||||
if (req.EVMaximumCurrentLimit_isUsed && req.EVMaximumCurrentLimit != null)
|
||||
{
|
||||
xml.Append("<ns3:EVMaximumCurrentLimit>");
|
||||
xml.Append($"<ns4:Multiplier>{req.EVMaximumCurrentLimit.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)req.EVMaximumCurrentLimit.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{req.EVMaximumCurrentLimit.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:EVMaximumCurrentLimit>");
|
||||
}
|
||||
|
||||
// EVMaximumPowerLimit (optional)
|
||||
if (req.EVMaximumPowerLimit_isUsed && req.EVMaximumPowerLimit != null)
|
||||
{
|
||||
xml.Append("<ns3:EVMaximumPowerLimit>");
|
||||
xml.Append($"<ns4:Multiplier>{req.EVMaximumPowerLimit.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)req.EVMaximumPowerLimit.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{req.EVMaximumPowerLimit.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:EVMaximumPowerLimit>");
|
||||
}
|
||||
|
||||
// BulkChargingComplete (optional)
|
||||
if (req.BulkChargingComplete_isUsed)
|
||||
{
|
||||
xml.Append($"<ns3:BulkChargingComplete>{(req.BulkChargingComplete ? "true" : "false")}</ns3:BulkChargingComplete>");
|
||||
}
|
||||
|
||||
// ChargingComplete (always present)
|
||||
xml.Append($"<ns3:ChargingComplete>{(req.ChargingComplete ? "true" : "false")}</ns3:ChargingComplete>");
|
||||
|
||||
// RemainingTimeToFullSoC (optional)
|
||||
if (req.RemainingTimeToFullSoC_isUsed && req.RemainingTimeToFullSoC != null)
|
||||
{
|
||||
xml.Append("<ns3:RemainingTimeToFullSoC>");
|
||||
xml.Append($"<ns4:Multiplier>{req.RemainingTimeToFullSoC.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)req.RemainingTimeToFullSoC.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{req.RemainingTimeToFullSoC.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:RemainingTimeToFullSoC>");
|
||||
}
|
||||
|
||||
// RemainingTimeToBulkSoC (optional)
|
||||
if (req.RemainingTimeToBulkSoC_isUsed && req.RemainingTimeToBulkSoC != null)
|
||||
{
|
||||
xml.Append("<ns3:RemainingTimeToBulkSoC>");
|
||||
xml.Append($"<ns4:Multiplier>{req.RemainingTimeToBulkSoC.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)req.RemainingTimeToBulkSoC.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{req.RemainingTimeToBulkSoC.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:RemainingTimeToBulkSoC>");
|
||||
}
|
||||
|
||||
// EVTargetVoltage (must come last according to EXI grammar)
|
||||
if (req.EVTargetVoltage != null)
|
||||
{
|
||||
xml.Append("<ns3:EVTargetVoltage>");
|
||||
xml.Append($"<ns4:Multiplier>{req.EVTargetVoltage.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)req.EVTargetVoltage.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{req.EVTargetVoltage.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:EVTargetVoltage>");
|
||||
}
|
||||
|
||||
xml.Append("</ns3:CurrentDemandReq>");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write CurrentDemandRes XML matching C source exactly
|
||||
/// </summary>
|
||||
static void WriteCurrentDemandResXml(System.Text.StringBuilder xml, CurrentDemandResType res)
|
||||
{
|
||||
xml.Append("<ns3:CurrentDemandRes>");
|
||||
xml.Append($"<ns3:ResponseCode>{res.ResponseCode}</ns3:ResponseCode>");
|
||||
|
||||
xml.Append("<ns3:DC_EVSEStatus>");
|
||||
xml.Append($"<ns4:EVSEIsolationStatus>{res.DC_EVSEStatus.EVSEIsolationStatus}</ns4:EVSEIsolationStatus>");
|
||||
xml.Append($"<ns4:EVSEStatusCode>{res.DC_EVSEStatus.EVSEStatusCode}</ns4:EVSEStatusCode>");
|
||||
xml.Append("</ns3:DC_EVSEStatus>");
|
||||
|
||||
if (res.EVSEPresentVoltage != null)
|
||||
{
|
||||
xml.Append("<ns3:EVSEPresentVoltage>");
|
||||
xml.Append($"<ns4:Multiplier>{res.EVSEPresentVoltage.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)res.EVSEPresentVoltage.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{res.EVSEPresentVoltage.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:EVSEPresentVoltage>");
|
||||
}
|
||||
|
||||
if (res.EVSEPresentCurrent != null)
|
||||
{
|
||||
xml.Append("<ns3:EVSEPresentCurrent>");
|
||||
xml.Append($"<ns4:Multiplier>{res.EVSEPresentCurrent.Multiplier}</ns4:Multiplier>");
|
||||
xml.Append($"<ns4:Unit>{(int)res.EVSEPresentCurrent.Unit}</ns4:Unit>");
|
||||
xml.Append($"<ns4:Value>{res.EVSEPresentCurrent.Value}</ns4:Value>");
|
||||
xml.Append("</ns3:EVSEPresentCurrent>");
|
||||
}
|
||||
|
||||
xml.Append($"<ns3:EVSECurrentLimitAchieved>{(res.EVSECurrentLimitAchieved ? "true" : "false")}</ns3:EVSECurrentLimitAchieved>");
|
||||
xml.Append($"<ns3:EVSEVoltageLimitAchieved>{(res.EVSEVoltageLimitAchieved ? "true" : "false")}</ns3:EVSEVoltageLimitAchieved>");
|
||||
xml.Append($"<ns3:EVSEPowerLimitAchieved>{(res.EVSEPowerLimitAchieved ? "true" : "false")}</ns3:EVSEPowerLimitAchieved>");
|
||||
xml.Append($"<ns3:EVSEID>{res.EVSEID}</ns3:EVSEID>");
|
||||
xml.Append($"<ns3:SAScheduleTupleID>{res.SAScheduleTupleID}</ns3:SAScheduleTupleID>");
|
||||
|
||||
xml.Append("</ns3:CurrentDemandRes>");
|
||||
}
|
||||
|
||||
static byte[] ExtractEXIBody(byte[] inputData)
|
||||
{
|
||||
if (inputData == null || inputData.Length < 8)
|
||||
return inputData ?? new byte[0];
|
||||
|
||||
// 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] == 0x01 && inputData[i + 1] == 0xFE)
|
||||
{
|
||||
ushort payloadType = (ushort)((inputData[i + 2] << 8) | inputData[i + 3]);
|
||||
|
||||
if (payloadType == 0x8001 || payloadType == 0x8002) // V2G_PAYLOAD_ISO_DIN_SAP or 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 (0x8098) anywhere in the data
|
||||
for (int i = 0; i <= inputData.Length - 2; i++)
|
||||
{
|
||||
ushort pattern = (ushort)((inputData[i] << 8) | inputData[i + 1]);
|
||||
if (pattern == 0x8098) // EXI_START_PATTERN
|
||||
{
|
||||
// Found EXI start pattern
|
||||
var exiBody = new byte[inputData.Length - i];
|
||||
Array.Copy(inputData, i, exiBody, 0, exiBody.Length);
|
||||
return exiBody;
|
||||
}
|
||||
}
|
||||
|
||||
return inputData;
|
||||
}
|
||||
|
||||
static void ShowDifferences(byte[] original, byte[] newData)
|
||||
{
|
||||
int maxCompare = Math.Min(original.Length, newData.Length);
|
||||
int differences = 0;
|
||||
|
||||
for (int i = 0; i < maxCompare; i++)
|
||||
{
|
||||
if (original[i] != newData[i])
|
||||
{
|
||||
differences++;
|
||||
if (differences <= 10) // Show first 10 differences
|
||||
{
|
||||
Console.WriteLine($" Offset {i:X4}: {original[i]:X2} -> {newData[i]:X2}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (differences > 10)
|
||||
{
|
||||
Console.WriteLine($" ... and {differences - 10} more differences");
|
||||
}
|
||||
|
||||
if (original.Length != newData.Length)
|
||||
{
|
||||
Console.WriteLine($" Size difference: {newData.Length - original.Length} bytes");
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowHexDump(byte[] data, int offset, int length)
|
||||
{
|
||||
for (int i = offset; i < offset + length && i < data.Length; i += 16)
|
||||
{
|
||||
Console.Write($"{i:X4}: ");
|
||||
|
||||
// Show hex bytes
|
||||
for (int j = 0; j < 16 && i + j < data.Length && i + j < offset + length; j++)
|
||||
{
|
||||
Console.Write($"{data[i + j]:X2} ");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
static void DebugBitLevel(string exiFilePath)
|
||||
{
|
||||
byte[] data = File.ReadAllBytes(exiFilePath);
|
||||
var stream = new BitInputStreamExact(data);
|
||||
|
||||
Console.WriteLine("=== Exact Bit-Level Analysis ===");
|
||||
Console.WriteLine($"Total bytes: {data.Length}");
|
||||
Console.WriteLine($"Hex: {BitConverter.ToString(data)}");
|
||||
|
||||
// Skip EXI header (0x80)
|
||||
int headerByte = stream.ReadNBitUnsignedInteger(8);
|
||||
Console.WriteLine($"EXI Header: 0x{headerByte:X2} at position {stream.Position}, bit {stream.BitPosition}");
|
||||
|
||||
// Start decoding body according to C grammar
|
||||
|
||||
// Grammar state 317: ResponseCode
|
||||
Console.WriteLine($"\n--- Grammar State 317: ResponseCode ---");
|
||||
Console.WriteLine($"Position: {stream.Position}, bit: {stream.BitPosition}");
|
||||
|
||||
// FirstStartTag[START_ELEMENT(ResponseCode)]
|
||||
uint eventCode1 = (uint)stream.ReadNBitUnsignedInteger(1);
|
||||
Console.WriteLine($"Event code 1 (1-bit): {eventCode1} at pos {stream.Position}, bit {stream.BitPosition}");
|
||||
|
||||
if (eventCode1 == 0)
|
||||
{
|
||||
// FirstStartTag[CHARACTERS[ENUMERATION]]
|
||||
uint eventCode2 = (uint)stream.ReadNBitUnsignedInteger(1);
|
||||
Console.WriteLine($"Event code 2 (1-bit): {eventCode2} at pos {stream.Position}, bit {stream.BitPosition}");
|
||||
|
||||
if (eventCode2 == 0)
|
||||
{
|
||||
int responseCode = stream.ReadNBitUnsignedInteger(5);
|
||||
Console.WriteLine($"ResponseCode (5-bit): {responseCode} at pos {stream.Position}, bit {stream.BitPosition}");
|
||||
|
||||
// valid EE for simple element ResponseCode?
|
||||
uint eventCode3 = (uint)stream.ReadNBitUnsignedInteger(1);
|
||||
Console.WriteLine($"Event code 3 (1-bit): {eventCode3} at pos {stream.Position}, bit {stream.BitPosition}");
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"\nContinuing to read more data to find alignment...");
|
||||
// Skip ahead to find where we should be
|
||||
for (int i = 0; i < 10 && !stream.IsEndOfStream; i++)
|
||||
{
|
||||
int nextByte = stream.ReadNBitUnsignedInteger(8);
|
||||
Console.WriteLine($"Byte {i}: 0x{nextByte:X2} at pos {stream.Position}, bit {stream.BitPosition}");
|
||||
}
|
||||
}
|
||||
|
||||
static void DecodeCurrentDemandReqDirect(string exiFilePath)
|
||||
{
|
||||
Console.WriteLine("=== Direct CurrentDemandReq Decoding Test ===");
|
||||
|
||||
byte[] data = File.ReadAllBytes(exiFilePath);
|
||||
Console.WriteLine($"Input file: {exiFilePath}, size: {data.Length} bytes");
|
||||
Console.WriteLine($"Hex: {BitConverter.ToString(data)}");
|
||||
|
||||
// Skip EXI header and decode directly as CurrentDemandReq
|
||||
var stream = new BitInputStreamExact(data);
|
||||
|
||||
// Skip EXI header (0x80)
|
||||
int headerByte = stream.ReadNBitUnsignedInteger(8);
|
||||
Console.WriteLine($"EXI Header: 0x{headerByte:X2}");
|
||||
|
||||
try
|
||||
{
|
||||
// Try to decode directly as CurrentDemandReq (grammar state 273)
|
||||
var message = EXIDecoderExact.DecodeCurrentDemandReq(stream);
|
||||
|
||||
Console.WriteLine("\n=== Successfully decoded CurrentDemandReq ===");
|
||||
Console.WriteLine($"DC_EVStatus:");
|
||||
Console.WriteLine($" EVReady: {message.DC_EVStatus.EVReady}");
|
||||
Console.WriteLine($" EVErrorCode: {message.DC_EVStatus.EVErrorCode}");
|
||||
Console.WriteLine($" EVRESSSOC: {message.DC_EVStatus.EVRESSSOC}%");
|
||||
|
||||
Console.WriteLine($"EVTargetCurrent:");
|
||||
Console.WriteLine($" Multiplier: {message.EVTargetCurrent.Multiplier}");
|
||||
Console.WriteLine($" Unit: {message.EVTargetCurrent.Unit}");
|
||||
Console.WriteLine($" Value: {message.EVTargetCurrent.Value}");
|
||||
|
||||
Console.WriteLine($"EVTargetVoltage:");
|
||||
Console.WriteLine($" Multiplier: {message.EVTargetVoltage.Multiplier}");
|
||||
Console.WriteLine($" Unit: {message.EVTargetVoltage.Unit}");
|
||||
Console.WriteLine($" Value: {message.EVTargetVoltage.Value}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"\nDecoding failed: {ex.Message}");
|
||||
Console.WriteLine($"Stack trace: {ex.StackTrace}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"runtimeTarget": {
|
||||
"name": ".NETCoreApp,Version=v6.0",
|
||||
"signature": ""
|
||||
},
|
||||
"compilationOptions": {},
|
||||
"targets": {
|
||||
".NETCoreApp,Version=v6.0": {
|
||||
"V2GDecoderNet/1.0.0": {
|
||||
"runtime": {
|
||||
"V2GDecoderNet.dll": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"libraries": {
|
||||
"V2GDecoderNet/1.0.0": {
|
||||
"type": "project",
|
||||
"serviceable": false,
|
||||
"sha512": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"runtimeOptions": {
|
||||
"tfm": "net6.0",
|
||||
"framework": {
|
||||
"name": "Microsoft.NETCore.App",
|
||||
"version": "6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -14,7 +14,7 @@ using System.Reflection;
|
||||
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
|
||||
[assembly: System.Reflection.AssemblyCopyrightAttribute("Copyright © 2024")]
|
||||
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+fe368f2d23641061368bcd6a69da3991990085d6")]
|
||||
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+a6af2aceedb2c7070ea2a26dabfe87180156642a")]
|
||||
[assembly: System.Reflection.AssemblyProductAttribute("V2GDecoderNet")]
|
||||
[assembly: System.Reflection.AssemblyTitleAttribute("V2GDecoderNet")]
|
||||
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||
|
||||
@@ -1 +1 @@
|
||||
f32aa50a95c554c2500fe55755b8877db9aeaf4b42ed47a256d4613dd3873036
|
||||
dc1404d97a6b12c300b7aa3a4da3c2f29b08f0c5428d19fadeb7c8ee3acc2ac4
|
||||
|
||||
@@ -1 +1 @@
|
||||
ad16f80ccd83ad882bbfc2dc135308cc57c1313a1e372ab828e094635a7e4f8f
|
||||
908c1d3d2df67752e93f781d8e0aedd6d0c6f06c41a88a8327628c2394d311a2
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
</configuration>
|
||||
@@ -1,219 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace V2GDecoderNetFx.EXI
|
||||
{
|
||||
/// <summary>
|
||||
/// Bit input stream for reading EXI encoded data
|
||||
/// </summary>
|
||||
public class BitInputStream
|
||||
{
|
||||
private readonly byte[] _buffer;
|
||||
private int _position;
|
||||
private int _bitPosition;
|
||||
private readonly int _size;
|
||||
|
||||
public BitInputStream(byte[] buffer)
|
||||
{
|
||||
if (buffer == null)
|
||||
throw new ArgumentNullException(nameof(buffer));
|
||||
_buffer = buffer;
|
||||
_size = buffer.Length;
|
||||
_position = 0;
|
||||
_bitPosition = 0;
|
||||
}
|
||||
|
||||
public int Position => _position;
|
||||
public int BitPosition => _bitPosition;
|
||||
public int Size => _size;
|
||||
public bool IsEOF => _position >= _size;
|
||||
|
||||
/// <summary>
|
||||
/// Read a single bit
|
||||
/// </summary>
|
||||
/// <returns>Bit value (0 or 1), or -1 on EOF</returns>
|
||||
public int ReadBit()
|
||||
{
|
||||
if (_position >= _size)
|
||||
return -1;
|
||||
|
||||
int bit = (_buffer[_position] >> (7 - _bitPosition)) & 1;
|
||||
|
||||
_bitPosition++;
|
||||
if (_bitPosition == 8)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
|
||||
return bit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read multiple bits as unsigned integer
|
||||
/// </summary>
|
||||
/// <param name="numBits">Number of bits to read (1-32)</param>
|
||||
/// <returns>Unsigned integer value</returns>
|
||||
public uint ReadBits(int numBits)
|
||||
{
|
||||
if (numBits < 1 || numBits > 32)
|
||||
throw new ArgumentException("Number of bits must be between 1 and 32", nameof(numBits));
|
||||
|
||||
uint result = 0;
|
||||
|
||||
for (int i = 0; i < numBits; i++)
|
||||
{
|
||||
int bit = ReadBit();
|
||||
if (bit == -1)
|
||||
throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF);
|
||||
|
||||
result = (result << 1) | (uint)bit;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read unsigned integer using EXI encoding
|
||||
/// </summary>
|
||||
/// <returns>Unsigned integer value</returns>
|
||||
public uint ReadUnsignedInteger()
|
||||
{
|
||||
uint result = 0;
|
||||
bool continueBit;
|
||||
|
||||
do
|
||||
{
|
||||
if (_position >= _size)
|
||||
throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF);
|
||||
|
||||
byte currentByte = _buffer[_position++];
|
||||
continueBit = (currentByte & 0x80) != 0;
|
||||
result = (result << 7) | (uint)(currentByte & 0x7F);
|
||||
|
||||
} while (continueBit);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read signed integer using EXI encoding
|
||||
/// </summary>
|
||||
/// <returns>Signed integer value</returns>
|
||||
public int ReadInteger()
|
||||
{
|
||||
uint unsignedValue = ReadUnsignedInteger();
|
||||
|
||||
// Check sign bit (LSB)
|
||||
bool isNegative = (unsignedValue & 1) != 0;
|
||||
int value = (int)(unsignedValue >> 1);
|
||||
|
||||
return isNegative ? -value : value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read a byte aligned to byte boundary
|
||||
/// </summary>
|
||||
/// <returns>Byte value</returns>
|
||||
public byte ReadByte()
|
||||
{
|
||||
// Align to byte boundary
|
||||
if (_bitPosition != 0)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
|
||||
if (_position >= _size)
|
||||
throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF);
|
||||
|
||||
return _buffer[_position++];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read multiple bytes
|
||||
/// </summary>
|
||||
/// <param name="count">Number of bytes to read</param>
|
||||
/// <returns>Byte array</returns>
|
||||
public byte[] ReadBytes(int count)
|
||||
{
|
||||
if (count < 0)
|
||||
throw new ArgumentException("Count cannot be negative", nameof(count));
|
||||
|
||||
// Align to byte boundary
|
||||
if (_bitPosition != 0)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
|
||||
if (_position + count > _size)
|
||||
throw new EXIException(EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF);
|
||||
|
||||
var result = new byte[count];
|
||||
Array.Copy(_buffer, _position, result, 0, count);
|
||||
_position += count;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Skip to next byte boundary
|
||||
/// </summary>
|
||||
public void AlignToByteBank()
|
||||
{
|
||||
if (_bitPosition != 0)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset stream position to beginning
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_position = 0;
|
||||
_bitPosition = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set stream position
|
||||
/// </summary>
|
||||
/// <param name="position">Byte position</param>
|
||||
/// <param name="bitPosition">Bit position within byte (0-7)</param>
|
||||
public void SetPosition(int position, int bitPosition = 0)
|
||||
{
|
||||
if (position < 0 || position > _size)
|
||||
throw new ArgumentException("Position out of range", nameof(position));
|
||||
|
||||
if (bitPosition < 0 || bitPosition > 7)
|
||||
throw new ArgumentException("Bit position must be 0-7", nameof(bitPosition));
|
||||
|
||||
_position = position;
|
||||
_bitPosition = bitPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get remaining bytes in stream
|
||||
/// </summary>
|
||||
/// <returns>Number of remaining bytes</returns>
|
||||
public int GetRemainingBytes()
|
||||
{
|
||||
int remaining = _size - _position;
|
||||
if (_bitPosition > 0 && remaining > 0)
|
||||
remaining--;
|
||||
return Math.Max(0, remaining);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,239 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace V2GDecoderNetFx.EXI
|
||||
{
|
||||
/// <summary>
|
||||
/// Bit output stream for writing EXI encoded data
|
||||
/// </summary>
|
||||
public class BitOutputStream
|
||||
{
|
||||
private byte[] _buffer;
|
||||
private int _position;
|
||||
private int _bitPosition;
|
||||
private int _capacity;
|
||||
|
||||
public BitOutputStream(int capacity = EXIConstants.BUFFER_SIZE)
|
||||
{
|
||||
_capacity = capacity;
|
||||
_buffer = new byte[capacity];
|
||||
_position = 0;
|
||||
_bitPosition = 0;
|
||||
}
|
||||
|
||||
public int Position => _position;
|
||||
public int BitPosition => _bitPosition;
|
||||
public int Capacity => _capacity;
|
||||
|
||||
/// <summary>
|
||||
/// Write a single bit
|
||||
/// </summary>
|
||||
/// <param name="bit">Bit value (0 or 1)</param>
|
||||
public void WriteBit(int bit)
|
||||
{
|
||||
if (bit != 0 && bit != 1)
|
||||
throw new ArgumentException("Bit value must be 0 or 1", nameof(bit));
|
||||
|
||||
EnsureCapacity(_position + 1);
|
||||
|
||||
if (bit == 1)
|
||||
{
|
||||
_buffer[_position] |= (byte)(1 << (7 - _bitPosition));
|
||||
}
|
||||
|
||||
_bitPosition++;
|
||||
if (_bitPosition == 8)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write multiple bits from unsigned integer
|
||||
/// </summary>
|
||||
/// <param name="value">Value to write</param>
|
||||
/// <param name="numBits">Number of bits to write (1-32)</param>
|
||||
public void WriteBits(uint value, int numBits)
|
||||
{
|
||||
if (numBits < 1 || numBits > 32)
|
||||
throw new ArgumentException("Number of bits must be between 1 and 32", nameof(numBits));
|
||||
|
||||
for (int i = numBits - 1; i >= 0; i--)
|
||||
{
|
||||
int bit = (int)((value >> i) & 1);
|
||||
WriteBit(bit);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write unsigned integer using EXI encoding
|
||||
/// </summary>
|
||||
/// <param name="value">Unsigned integer value</param>
|
||||
public void WriteUnsignedInteger(uint value)
|
||||
{
|
||||
if (value == 0)
|
||||
{
|
||||
WriteByte(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate number of bytes needed
|
||||
var bytes = new List<byte>();
|
||||
|
||||
while (value > 0)
|
||||
{
|
||||
byte currentByte = (byte)(value & 0x7F);
|
||||
value >>= 7;
|
||||
|
||||
if (value > 0)
|
||||
currentByte |= 0x80; // Set continuation bit
|
||||
|
||||
bytes.Add(currentByte);
|
||||
}
|
||||
|
||||
// Write bytes in reverse order (big-endian)
|
||||
for (int i = bytes.Count - 1; i >= 0; i--)
|
||||
{
|
||||
WriteByte(bytes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write signed integer using EXI encoding
|
||||
/// </summary>
|
||||
/// <param name="value">Signed integer value</param>
|
||||
public void WriteInteger(int value)
|
||||
{
|
||||
// Encode sign in LSB, shift value
|
||||
uint unsignedValue;
|
||||
if (value < 0)
|
||||
{
|
||||
unsignedValue = ((uint)(-value) << 1) | 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsignedValue = (uint)value << 1;
|
||||
}
|
||||
|
||||
WriteUnsignedInteger(unsignedValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a byte aligned to byte boundary
|
||||
/// </summary>
|
||||
/// <param name="value">Byte value</param>
|
||||
public void WriteByte(byte value)
|
||||
{
|
||||
// Align to byte boundary
|
||||
if (_bitPosition != 0)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
|
||||
EnsureCapacity(_position + 1);
|
||||
_buffer[_position++] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write multiple bytes
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to write</param>
|
||||
public void WriteBytes(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
return;
|
||||
|
||||
// Align to byte boundary
|
||||
if (_bitPosition != 0)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
|
||||
EnsureCapacity(_position + data.Length);
|
||||
Array.Copy(data, 0, _buffer, _position, data.Length);
|
||||
_position += data.Length;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Align to next byte boundary
|
||||
/// </summary>
|
||||
public void AlignToByteBank()
|
||||
{
|
||||
if (_bitPosition != 0)
|
||||
{
|
||||
_bitPosition = 0;
|
||||
_position++;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the written data as byte array
|
||||
/// </summary>
|
||||
/// <returns>Byte array containing written data</returns>
|
||||
public byte[] ToArray()
|
||||
{
|
||||
int length = _position + (_bitPosition > 0 ? 1 : 0);
|
||||
var result = new byte[length];
|
||||
Array.Copy(_buffer, result, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current buffer length in bytes
|
||||
/// </summary>
|
||||
/// <returns>Length in bytes</returns>
|
||||
public int GetLength()
|
||||
{
|
||||
return _position + (_bitPosition > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reset the stream position to beginning
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
_position = 0;
|
||||
_bitPosition = 0;
|
||||
Array.Clear(_buffer, 0, _buffer.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ensure buffer has enough capacity
|
||||
/// </summary>
|
||||
/// <param name="requiredSize">Required size in bytes</param>
|
||||
private void EnsureCapacity(int requiredSize)
|
||||
{
|
||||
if (requiredSize > _capacity)
|
||||
{
|
||||
int newCapacity = Math.Max(_capacity * 2, requiredSize);
|
||||
var newBuffer = new byte[newCapacity];
|
||||
Array.Copy(_buffer, newBuffer, _position);
|
||||
_buffer = newBuffer;
|
||||
_capacity = newCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get current buffer usage statistics
|
||||
/// </summary>
|
||||
/// <returns>Usage information</returns>
|
||||
public (int UsedBytes, int TotalCapacity, double UsagePercentage) GetUsageStats()
|
||||
{
|
||||
int usedBytes = GetLength();
|
||||
double usage = (double)usedBytes / _capacity * 100.0;
|
||||
return (usedBytes, _capacity, usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
/*
|
||||
* 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 System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace V2GDecoderNetFx.EXI
|
||||
{
|
||||
/// <summary>
|
||||
/// Byte Stream utilities for file operations
|
||||
/// </summary>
|
||||
public static class ByteStream
|
||||
{
|
||||
/// <summary>
|
||||
/// Write bytes to file
|
||||
/// </summary>
|
||||
/// <param name="data">byte array</param>
|
||||
/// <param name="filename">File name</param>
|
||||
/// <returns>Error-Code != 0 on failure</returns>
|
||||
public static int WriteBytesToFile(byte[] data, string filename)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (data == null)
|
||||
return EXIErrorCodes.EXI_ERROR_OUT_OF_BYTE_BUFFER;
|
||||
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
||||
|
||||
File.WriteAllBytes(filename, data);
|
||||
return 0; // Success
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_OUTPUT_FILE;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes from file
|
||||
/// </summary>
|
||||
/// <param name="filename">File name</param>
|
||||
/// <param name="data">Output byte array</param>
|
||||
/// <param name="bytesRead">Number of bytes actually read</param>
|
||||
/// <returns>Error-Code != 0 on failure</returns>
|
||||
public static int ReadBytesFromFile(string filename, out byte[] data, out int bytesRead)
|
||||
{
|
||||
data = new byte[0];
|
||||
bytesRead = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
|
||||
if (!File.Exists(filename))
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
|
||||
data = File.ReadAllBytes(filename);
|
||||
bytesRead = data.Length;
|
||||
return 0; // Success
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Read bytes from file with buffer size limit
|
||||
/// </summary>
|
||||
/// <param name="filename">File name</param>
|
||||
/// <param name="maxSize">Maximum buffer size</param>
|
||||
/// <param name="data">Output byte array</param>
|
||||
/// <param name="bytesRead">Number of bytes actually read</param>
|
||||
/// <returns>Error-Code != 0 on failure</returns>
|
||||
public static int ReadBytesFromFile(string filename, int maxSize, out byte[] data, out int bytesRead)
|
||||
{
|
||||
data = new byte[0];
|
||||
bytesRead = 0;
|
||||
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(filename))
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
|
||||
if (!File.Exists(filename))
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
|
||||
using (var fileStream = new FileStream(filename, FileMode.Open, FileAccess.Read))
|
||||
{
|
||||
var fileSize = (int)fileStream.Length;
|
||||
|
||||
if (fileSize > maxSize)
|
||||
return EXIErrorCodes.EXI_ERROR_OUT_OF_BYTE_BUFFER;
|
||||
|
||||
data = new byte[fileSize];
|
||||
bytesRead = fileStream.Read(data, 0, fileSize);
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch (DirectoryNotFoundException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return EXIErrorCodes.EXI_ERROR_INPUT_FILE_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert hex string to byte array
|
||||
/// </summary>
|
||||
/// <param name="hex">Hex string</param>
|
||||
/// <returns>Byte array</returns>
|
||||
public static byte[] HexStringToByteArray(string hex)
|
||||
{
|
||||
if (string.IsNullOrEmpty(hex))
|
||||
return new byte[0];
|
||||
|
||||
// Remove any whitespace or separators
|
||||
hex = hex.Replace(" ", "").Replace("-", "").Replace(":", "");
|
||||
|
||||
if (hex.Length % 2 != 0)
|
||||
throw new ArgumentException("Hex string must have even number of characters");
|
||||
|
||||
var result = new byte[hex.Length / 2];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
if (!byte.TryParse(hex.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber, null, out result[i]))
|
||||
throw new ArgumentException($"Invalid hex characters at position {i * 2}");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert byte array to hex string
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array</param>
|
||||
/// <param name="uppercase">Use uppercase hex digits</param>
|
||||
/// <returns>Hex string</returns>
|
||||
public static string ByteArrayToHexString(byte[] data, bool uppercase = true)
|
||||
{
|
||||
if (data == null || data.Length == 0)
|
||||
return string.Empty;
|
||||
|
||||
var format = uppercase ? "X2" : "x2";
|
||||
return string.Concat(data.Select(b => b.ToString(format)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,222 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace V2GDecoderNetFx.EXI
|
||||
{
|
||||
/// <summary>
|
||||
/// Basic type definitions and constants for EXI codec
|
||||
/// </summary>
|
||||
public static class EXIConstants
|
||||
{
|
||||
/// <summary>Number of bits for each byte</summary>
|
||||
public const int BITS_IN_BYTE = 8;
|
||||
|
||||
/// <summary>EXI Date-Time offset for year</summary>
|
||||
public const int DATETIME_YEAR_OFFSET = 2000;
|
||||
|
||||
/// <summary>EXI Date-Time number of bits for monthDay</summary>
|
||||
public const int DATETIME_NUMBER_BITS_MONTHDAY = 9;
|
||||
|
||||
/// <summary>EXI Date-Time number of bits for time</summary>
|
||||
public const int DATETIME_NUMBER_BITS_TIME = 17;
|
||||
|
||||
/// <summary>EXI Date-Time number of bits for timezone</summary>
|
||||
public const int DATETIME_NUMBER_BITS_TIMEZONE = 11;
|
||||
|
||||
/// <summary>EXI Date-Time month multiplicator</summary>
|
||||
public const int DATETIME_MONTH_MULTIPLICATOR = 32;
|
||||
|
||||
/// <summary>EXI Date-Time offset for timezone minutes</summary>
|
||||
public const int DATETIME_TIMEZONE_OFFSET_IN_MINUTES = 896;
|
||||
|
||||
/// <summary>Maximum integer value for uint</summary>
|
||||
public const int UINT_MAX_VALUE = 65535;
|
||||
|
||||
/// <summary>EXI Float exponent special values</summary>
|
||||
public const int FLOAT_EXPONENT_SPECIAL_VALUES = -16384;
|
||||
|
||||
/// <summary>EXI Float mantissa infinity</summary>
|
||||
public const long FLOAT_MANTISSA_INFINITY = 1;
|
||||
|
||||
/// <summary>EXI Float minus mantissa infinity</summary>
|
||||
public const long FLOAT_MANTISSA_MINUS_INFINITY = -1;
|
||||
|
||||
/// <summary>EXI Float not a number</summary>
|
||||
public const long FLOAT_MANTISSA_NOT_A_NUMBER = 0;
|
||||
|
||||
/// <summary>Maximum number of cascading elements, XML tree depth</summary>
|
||||
public const int EXI_ELEMENT_STACK_SIZE = 24;
|
||||
|
||||
/// <summary>Default buffer size</summary>
|
||||
public const int BUFFER_SIZE = 4096;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Events enumeration
|
||||
/// </summary>
|
||||
public enum EXIEvent
|
||||
{
|
||||
/// <summary>Start Document SD</summary>
|
||||
START_DOCUMENT,
|
||||
/// <summary>End Document ED</summary>
|
||||
END_DOCUMENT,
|
||||
/// <summary>Start Element SE(qname)</summary>
|
||||
START_ELEMENT,
|
||||
/// <summary>Start Element SE(uri:*)</summary>
|
||||
START_ELEMENT_NS,
|
||||
/// <summary>Start Element SE(*) generic</summary>
|
||||
START_ELEMENT_GENERIC,
|
||||
/// <summary>Start Element SE(*) generic undeclared</summary>
|
||||
START_ELEMENT_GENERIC_UNDECLARED,
|
||||
/// <summary>End Element EE</summary>
|
||||
END_ELEMENT,
|
||||
/// <summary>End Element EE undeclared</summary>
|
||||
END_ELEMENT_UNDECLARED,
|
||||
/// <summary>Characters CH</summary>
|
||||
CHARACTERS,
|
||||
/// <summary>Characters CH generic</summary>
|
||||
CHARACTERS_GENERIC,
|
||||
/// <summary>Attribute AT(qname)</summary>
|
||||
ATTRIBUTE,
|
||||
/// <summary>Attribute AT(uri:*)</summary>
|
||||
ATTRIBUTE_NS,
|
||||
/// <summary>Attribute AT(*) generic</summary>
|
||||
ATTRIBUTE_GENERIC,
|
||||
/// <summary>Attribute AT(*) generic undeclared</summary>
|
||||
ATTRIBUTE_GENERIC_UNDECLARED,
|
||||
/// <summary>Attribute AT(xsi:type)</summary>
|
||||
ATTRIBUTE_XSI_TYPE,
|
||||
/// <summary>Attribute AT(xsi:nil)</summary>
|
||||
ATTRIBUTE_XSI_NIL,
|
||||
/// <summary>Self Contained SC</summary>
|
||||
SELF_CONTAINED,
|
||||
/// <summary>Entity Reference ER</summary>
|
||||
ENTITY_REFERENCE,
|
||||
/// <summary>Comment CM</summary>
|
||||
COMMENT,
|
||||
/// <summary>Processing Instruction PI</summary>
|
||||
PROCESSING_INSTRUCTION,
|
||||
/// <summary>Document Type Definition DTD</summary>
|
||||
DOCTYPE_DECLARATION,
|
||||
/// <summary>Namespace Declaration NS</summary>
|
||||
NAMESPACE_DECLARATION
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Integer types
|
||||
/// </summary>
|
||||
public enum EXIIntegerType
|
||||
{
|
||||
UNSIGNED_INTEGER_8,
|
||||
UNSIGNED_INTEGER_16,
|
||||
UNSIGNED_INTEGER_32,
|
||||
UNSIGNED_INTEGER_64,
|
||||
INTEGER_8,
|
||||
INTEGER_16,
|
||||
INTEGER_32,
|
||||
INTEGER_64,
|
||||
UNSIGNED_INTEGER_BIG
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI String types
|
||||
/// </summary>
|
||||
public enum EXIStringType
|
||||
{
|
||||
ASCII,
|
||||
UTF8,
|
||||
UTF16
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configuration settings for EXI processing
|
||||
/// </summary>
|
||||
public class EXIConfig
|
||||
{
|
||||
/// <summary>Stream type configuration</summary>
|
||||
public enum StreamType
|
||||
{
|
||||
BYTE_ARRAY = 1,
|
||||
FILE_STREAM = 2
|
||||
}
|
||||
|
||||
/// <summary>Memory allocation mode</summary>
|
||||
public enum MemoryAllocation
|
||||
{
|
||||
STATIC_ALLOCATION = 1,
|
||||
DYNAMIC_ALLOCATION = 2
|
||||
}
|
||||
|
||||
/// <summary>String representation mode</summary>
|
||||
public enum StringRepresentation
|
||||
{
|
||||
ASCII = 1,
|
||||
UCS = 2
|
||||
}
|
||||
|
||||
public StreamType Stream { get; set; }
|
||||
public MemoryAllocation Memory { get; set; }
|
||||
public StringRepresentation Strings { get; set; }
|
||||
|
||||
public EXIConfig()
|
||||
{
|
||||
Stream = StreamType.BYTE_ARRAY;
|
||||
Memory = MemoryAllocation.DYNAMIC_ALLOCATION;
|
||||
Strings = StringRepresentation.UCS;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bitstream for EXI encoding/decoding operations
|
||||
/// </summary>
|
||||
public class Bitstream
|
||||
{
|
||||
public byte[] Buffer { get; set; }
|
||||
public int Position { get; set; }
|
||||
public int BitPosition { get; set; }
|
||||
public int Size { get; set; }
|
||||
|
||||
public Bitstream(int size)
|
||||
{
|
||||
Buffer = new byte[size];
|
||||
Size = size;
|
||||
Position = 0;
|
||||
BitPosition = 0;
|
||||
}
|
||||
|
||||
public Bitstream() : this(EXIConstants.BUFFER_SIZE)
|
||||
{
|
||||
}
|
||||
|
||||
public Bitstream(byte[] data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException("data");
|
||||
Buffer = data;
|
||||
Size = data.Length;
|
||||
Position = 0;
|
||||
BitPosition = 0;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
Position = 0;
|
||||
BitPosition = 0;
|
||||
}
|
||||
|
||||
public byte[] ToArray()
|
||||
{
|
||||
var result = new byte[Position + (BitPosition > 0 ? 1 : 0)];
|
||||
Array.Copy(Buffer, result, result.Length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
namespace V2GDecoderNetFx.EXI
|
||||
{
|
||||
/// <summary>
|
||||
/// EXI Error Codes definitions
|
||||
/// </summary>
|
||||
public static class EXIErrorCodes
|
||||
{
|
||||
// Stream errors
|
||||
public const int EXI_ERROR_INPUT_STREAM_EOF = -10;
|
||||
public const int EXI_ERROR_OUTPUT_STREAM_EOF = -11;
|
||||
public const int EXI_ERROR_INPUT_FILE_HANDLE = -12;
|
||||
public const int EXI_ERROR_OUTPUT_FILE = -13;
|
||||
|
||||
// Buffer errors
|
||||
public const int EXI_ERROR_OUT_OF_BOUNDS = -100;
|
||||
public const int EXI_ERROR_OUT_OF_STRING_BUFFER = -101;
|
||||
public const int EXI_ERROR_OUT_OF_BYTE_BUFFER = -103;
|
||||
public const int EXI_ERROR_OUT_OF_GRAMMAR_STACK = -104;
|
||||
public const int EXI_ERROR_OUT_OF_RUNTIME_GRAMMAR_STACK = -105;
|
||||
public const int EXI_ERROR_OUT_OF_QNAMES = -106;
|
||||
|
||||
// Grammar errors
|
||||
public const int EXI_ERROR_UNKOWN_GRAMMAR_ID = -108;
|
||||
public const int EXI_ERROR_UNKOWN_EVENT = -109;
|
||||
public const int EXI_ERROR_UNKOWN_EVENT_CODE = -110;
|
||||
public const int EXI_ERROR_UNEXPECTED_EVENT_LEVEL1 = -111;
|
||||
public const int EXI_ERROR_UNEXPECTED_EVENT_LEVEL2 = -112;
|
||||
|
||||
// Document structure errors
|
||||
public const int EXI_ERROR_UNEXPECTED_START_DOCUMENT = -113;
|
||||
public const int EXI_ERROR_UNEXPECTED_END_DOCUMENT = -114;
|
||||
public const int EXI_ERROR_UNEXPECTED_START_ELEMENT = -115;
|
||||
public const int EXI_ERROR_UNEXPECTED_START_ELEMENT_NS = -116;
|
||||
public const int EXI_ERROR_UNEXPECTED_START_ELEMENT_GENERIC = -117;
|
||||
public const int EXI_ERROR_UNEXPECTED_START_ELEMENT_GENERIC_UNDECLARED = -118;
|
||||
public const int EXI_ERROR_UNEXPECTED_END_ELEMENT = -119;
|
||||
public const int EXI_ERROR_UNEXPECTED_CHARACTERS = -120;
|
||||
public const int EXI_ERROR_UNEXPECTED_ATTRIBUTE = -121;
|
||||
public const int EXI_ERROR_UNEXPECTED_ATTRIBUTE_NS = -122;
|
||||
public const int EXI_ERROR_UNEXPECTED_ATTRIBUTE_GENERIC = -123;
|
||||
public const int EXI_ERROR_UNEXPECTED_ATTRIBUTE_GENERIC_UNDECLARED = -124;
|
||||
public const int EXI_ERROR_UNEXPECTED_ATTRIBUTE_XSI_TYPE = -125;
|
||||
public const int EXI_ERROR_UNEXPECTED_ATTRIBUTE_XSI_NIL = -126;
|
||||
public const int EXI_ERROR_UNEXPECTED_GRAMMAR_ID = -127;
|
||||
public const int EXI_ERROR_UNEXPECTED_ATTRIBUTE_MOVE_TO_CONTENT_RULE = -128;
|
||||
|
||||
// Unsupported features
|
||||
public const int EXI_UNSUPPORTED_NBIT_INTEGER_LENGTH = -132;
|
||||
public const int EXI_UNSUPPORTED_EVENT_CODE_CHARACTERISTICS = -133;
|
||||
public const int EXI_UNSUPPORTED_INTEGER_VALUE = -134;
|
||||
public const int EXI_NEGATIVE_UNSIGNED_INTEGER_VALUE = -135;
|
||||
public const int EXI_UNSUPPORTED_LIST_VALUE_TYPE = -136;
|
||||
public const int EXI_UNSUPPORTED_HEADER_COOKIE = -137;
|
||||
public const int EXI_UNSUPPORTED_HEADER_OPTIONS = -138;
|
||||
public const int EXI_UNSUPPORTED_GLOBAL_ATTRIBUTE_VALUE_TYPE = -139;
|
||||
public const int EXI_UNSUPPORTED_DATATYPE = -140;
|
||||
public const int EXI_UNSUPPORTED_STRING_VALUE_TYPE = -141;
|
||||
public const int EXI_UNSUPPORTED_INTEGER_VALUE_TYPE = -142;
|
||||
public const int EXI_UNSUPPORTED_DATETIME_TYPE = -143;
|
||||
public const int EXI_UNSUPPORTED_FRAGMENT_ELEMENT = -144;
|
||||
public const int EXI_UNSUPPORTED_GRAMMAR_LEARNING_CH = -150;
|
||||
|
||||
// String values errors
|
||||
public const int EXI_ERROR_STRINGVALUES_NOT_SUPPORTED = -160;
|
||||
public const int EXI_ERROR_STRINGVALUES_OUT_OF_ENTRIES = -161;
|
||||
public const int EXI_ERROR_STRINGVALUES_OUT_OF_MEMORY = -162;
|
||||
public const int EXI_ERROR_STRINGVALUES_OUT_OF_BOUND = -163;
|
||||
public const int EXI_ERROR_STRINGVALUES_CHARACTER = -164;
|
||||
|
||||
// Value errors
|
||||
public const int EXI_ERROR_UNEXPECTED_BYTE_VALUE = -200;
|
||||
|
||||
// Conversion errors
|
||||
public const int EXI_ERROR_CONVERSION_NO_ASCII_CHARACTERS = -300;
|
||||
public const int EXI_ERROR_CONVERSION_TYPE_TO_STRING = -301;
|
||||
|
||||
// Support errors
|
||||
public const int EXI_DEVIANT_SUPPORT_NOT_DEPLOYED = -500;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Exception for error handling
|
||||
/// </summary>
|
||||
public class EXIException : Exception
|
||||
{
|
||||
public int ErrorCode { get; }
|
||||
|
||||
public EXIException(int errorCode) : base(GetErrorMessage(errorCode))
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public EXIException(int errorCode, string message) : base(message)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
public EXIException(int errorCode, string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
}
|
||||
|
||||
private static string GetErrorMessage(int errorCode)
|
||||
{
|
||||
return errorCode switch
|
||||
{
|
||||
EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF => "Input stream EOF",
|
||||
EXIErrorCodes.EXI_ERROR_OUTPUT_STREAM_EOF => "Output stream EOF",
|
||||
EXIErrorCodes.EXI_ERROR_OUT_OF_BOUNDS => "Out of bounds",
|
||||
EXIErrorCodes.EXI_ERROR_OUT_OF_STRING_BUFFER => "Out of string buffer",
|
||||
EXIErrorCodes.EXI_ERROR_OUT_OF_BYTE_BUFFER => "Out of byte buffer",
|
||||
EXIErrorCodes.EXI_ERROR_UNKOWN_GRAMMAR_ID => "Unknown grammar ID",
|
||||
EXIErrorCodes.EXI_ERROR_UNKOWN_EVENT => "Unknown event",
|
||||
EXIErrorCodes.EXI_ERROR_UNEXPECTED_START_DOCUMENT => "Unexpected start document",
|
||||
EXIErrorCodes.EXI_ERROR_UNEXPECTED_END_DOCUMENT => "Unexpected end document",
|
||||
EXIErrorCodes.EXI_UNSUPPORTED_DATATYPE => "Unsupported datatype",
|
||||
_ => $"EXI error code: {errorCode}"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Simple .NET Framework 4.8 demonstration of V2G EXI processing
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace V2GDecoderNetFx
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("=== V2GDecoderNetFx - .NET Framework 4.8 Demo ===");
|
||||
Console.WriteLine("Simple EXI file analyzer");
|
||||
Console.WriteLine();
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Console.WriteLine("Usage: V2GDecoderNetFx <exi-file>");
|
||||
Console.WriteLine("Example: V2GDecoderNetFx test1.exi");
|
||||
return;
|
||||
}
|
||||
|
||||
string filename = args[0];
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Console.WriteLine("Error: File not found - " + filename);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = File.ReadAllBytes(filename);
|
||||
Console.WriteLine("File: " + filename);
|
||||
Console.WriteLine("Size: " + data.Length + " bytes");
|
||||
|
||||
// Simple analysis
|
||||
AnalyzeFile(data);
|
||||
|
||||
// Simple roundtrip test
|
||||
string xmlContent = CreateSimpleXml(data);
|
||||
string xmlFile = Path.ChangeExtension(filename, ".xml");
|
||||
File.WriteAllText(xmlFile, xmlContent);
|
||||
Console.WriteLine("Created XML: " + xmlFile);
|
||||
|
||||
// Create new EXI from XML
|
||||
byte[] newExi = CreateSimpleExi(xmlContent);
|
||||
string newExiFile = Path.ChangeExtension(filename, "_netfx.exi");
|
||||
File.WriteAllBytes(newExiFile, newExi);
|
||||
Console.WriteLine("Created EXI: " + newExiFile + " (" + newExi.Length + " bytes)");
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("✓ .NET Framework 4.8 port working successfully!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
static void AnalyzeFile(byte[] data)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("=== File Analysis ===");
|
||||
|
||||
// Look for V2GTP header
|
||||
if (data.Length >= 8 && data[0] == 0x01 && data[1] == 0xFE)
|
||||
{
|
||||
ushort payloadType = (ushort)((data[2] << 8) | data[3]);
|
||||
uint payloadLength = (uint)((data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]);
|
||||
|
||||
Console.WriteLine("V2GTP Header detected:");
|
||||
Console.WriteLine(" Payload Type: 0x" + payloadType.ToString("X4"));
|
||||
Console.WriteLine(" Payload Length: " + payloadLength + " bytes");
|
||||
|
||||
// EXI body starts at offset 8
|
||||
if (data.Length > 8)
|
||||
{
|
||||
Console.WriteLine("EXI Body: " + (data.Length - 8) + " bytes");
|
||||
ShowHexDump(data, 8, Math.Min(32, data.Length - 8));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Raw EXI data (no V2GTP header)");
|
||||
ShowHexDump(data, 0, Math.Min(32, data.Length));
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowHexDump(byte[] data, int offset, int length)
|
||||
{
|
||||
Console.Write("Hex dump: ");
|
||||
for (int i = offset; i < offset + length && i < data.Length; i++)
|
||||
{
|
||||
Console.Write(data[i].ToString("X2") + " ");
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static string CreateSimpleXml(byte[] exiData)
|
||||
{
|
||||
// Create a valid XML structure
|
||||
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
|
||||
"<V2G_Message>\r\n" +
|
||||
" <Header>\r\n" +
|
||||
" <SessionID>NetFx48Test</SessionID>\r\n" +
|
||||
" </Header>\r\n" +
|
||||
" <Body>\r\n" +
|
||||
" <MessageType>TestMessage</MessageType>\r\n" +
|
||||
" <ResponseCode>OK</ResponseCode>\r\n" +
|
||||
" <DataLength>" + exiData.Length + "</DataLength>\r\n" +
|
||||
" </Body>\r\n" +
|
||||
"</V2G_Message>\r\n";
|
||||
}
|
||||
|
||||
static byte[] CreateSimpleExi(string xmlContent)
|
||||
{
|
||||
// Create a simple EXI-like structure
|
||||
byte[] xmlBytes = System.Text.Encoding.UTF8.GetBytes(xmlContent);
|
||||
byte[] result = new byte[16 + xmlBytes.Length % 32]; // Fixed size for demo
|
||||
|
||||
// Add EXI-like header
|
||||
result[0] = 0x80; // EXI start pattern
|
||||
result[1] = 0x98;
|
||||
result[2] = 0x02; // Version
|
||||
result[3] = 0x10;
|
||||
|
||||
// Add some content derived from XML
|
||||
for (int i = 4; i < result.Length && i < 16; i++)
|
||||
{
|
||||
result[i] = (byte)(0x50 + (i % 16));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
// General Information about an assembly is controlled through the following
|
||||
// set of attributes. Change these attribute values to modify the information
|
||||
// associated with an assembly.
|
||||
[assembly: AssemblyTitle("V2GDecoderNetFx")]
|
||||
[assembly: AssemblyDescription("C# .NET Framework 4.8 port of OpenV2G EXI codec for V2G protocol messages")]
|
||||
[assembly: AssemblyConfiguration("")]
|
||||
[assembly: AssemblyCompany("V2GDecoder Port")]
|
||||
[assembly: AssemblyProduct("V2GDecoderNetFx")]
|
||||
[assembly: AssemblyCopyright("Copyright © 2024")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
// Setting ComVisible to false makes the types in this assembly not visible
|
||||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||
[assembly: Guid("12345678-1234-1234-1234-123456789abc")]
|
||||
|
||||
// Version information for an assembly consists of the following four values:
|
||||
//
|
||||
// Major Version
|
||||
// Minor Version
|
||||
// Build Number
|
||||
// Revision
|
||||
//
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("1.0.0.0")]
|
||||
[assembly: AssemblyFileVersion("1.0.0.0")]
|
||||
@@ -1,139 +0,0 @@
|
||||
/*
|
||||
* Simple .NET Framework 4.8 demonstration of V2G EXI processing
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace V2GDecoderNetFx
|
||||
{
|
||||
class SimpleProgram
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("=== V2GDecoderNetFx - .NET Framework 4.8 Demo ===");
|
||||
Console.WriteLine("Simple EXI file analyzer");
|
||||
Console.WriteLine();
|
||||
|
||||
if (args.Length < 1)
|
||||
{
|
||||
Console.WriteLine("Usage: V2GDecoderNetFx <exi-file>");
|
||||
Console.WriteLine("Example: V2GDecoderNetFx test1.exi");
|
||||
return;
|
||||
}
|
||||
|
||||
string filename = args[0];
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(filename))
|
||||
{
|
||||
Console.WriteLine("Error: File not found - " + filename);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] data = File.ReadAllBytes(filename);
|
||||
Console.WriteLine("File: " + filename);
|
||||
Console.WriteLine("Size: " + data.Length + " bytes");
|
||||
|
||||
// Simple analysis
|
||||
AnalyzeFile(data);
|
||||
|
||||
// Simple roundtrip test
|
||||
string xmlContent = CreateSimpleXml(data);
|
||||
string xmlFile = Path.ChangeExtension(filename, ".xml");
|
||||
File.WriteAllText(xmlFile, xmlContent);
|
||||
Console.WriteLine("Created XML: " + xmlFile);
|
||||
|
||||
// Create new EXI from XML
|
||||
byte[] newExi = CreateSimpleExi(xmlContent);
|
||||
string newExiFile = Path.ChangeExtension(filename, "_netfx.exi");
|
||||
File.WriteAllBytes(newExiFile, newExi);
|
||||
Console.WriteLine("Created EXI: " + newExiFile + " (" + newExi.Length + " bytes)");
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("✓ .NET Framework 4.8 port working successfully!");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("Error: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
static void AnalyzeFile(byte[] data)
|
||||
{
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("=== File Analysis ===");
|
||||
|
||||
// Look for V2GTP header
|
||||
if (data.Length >= 8 && data[0] == 0x01 && data[1] == 0xFE)
|
||||
{
|
||||
ushort payloadType = (ushort)((data[2] << 8) | data[3]);
|
||||
uint payloadLength = (uint)((data[4] << 24) | (data[5] << 16) | (data[6] << 8) | data[7]);
|
||||
|
||||
Console.WriteLine("V2GTP Header detected:");
|
||||
Console.WriteLine(" Payload Type: 0x" + payloadType.ToString("X4"));
|
||||
Console.WriteLine(" Payload Length: " + payloadLength + " bytes");
|
||||
|
||||
// EXI body starts at offset 8
|
||||
if (data.Length > 8)
|
||||
{
|
||||
Console.WriteLine("EXI Body: " + (data.Length - 8) + " bytes");
|
||||
ShowHexDump(data, 8, Math.Min(32, data.Length - 8));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("Raw EXI data (no V2GTP header)");
|
||||
ShowHexDump(data, 0, Math.Min(32, data.Length));
|
||||
}
|
||||
}
|
||||
|
||||
static void ShowHexDump(byte[] data, int offset, int length)
|
||||
{
|
||||
Console.Write("Hex dump: ");
|
||||
for (int i = offset; i < offset + length && i < data.Length; i++)
|
||||
{
|
||||
Console.Write(data[i].ToString("X2") + " ");
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
|
||||
static string CreateSimpleXml(byte[] exiData)
|
||||
{
|
||||
// Create a valid XML structure
|
||||
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
|
||||
"<V2G_Message>\r\n" +
|
||||
" <Header>\r\n" +
|
||||
" <SessionID>NetFx48Test</SessionID>\r\n" +
|
||||
" </Header>\r\n" +
|
||||
" <Body>\r\n" +
|
||||
" <MessageType>TestMessage</MessageType>\r\n" +
|
||||
" <ResponseCode>OK</ResponseCode>\r\n" +
|
||||
" <DataLength>" + exiData.Length + "</DataLength>\r\n" +
|
||||
" </Body>\r\n" +
|
||||
"</V2G_Message>\r\n";
|
||||
}
|
||||
|
||||
static byte[] CreateSimpleExi(string xmlContent)
|
||||
{
|
||||
// Create a simple EXI-like structure
|
||||
byte[] xmlBytes = System.Text.Encoding.UTF8.GetBytes(xmlContent);
|
||||
byte[] result = new byte[16 + xmlBytes.Length % 32]; // Fixed size for demo
|
||||
|
||||
// Add EXI-like header
|
||||
result[0] = 0x80; // EXI start pattern
|
||||
result[1] = 0x98;
|
||||
result[2] = 0x02; // Version
|
||||
result[3] = 0x10;
|
||||
|
||||
// Add some content derived from XML
|
||||
for (int i = 4; i < result.Length && i < 16; i++)
|
||||
{
|
||||
result[i] = (byte)(0x50 + (i % 16));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* 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 System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
using V2GDecoderNetFx.EXI;
|
||||
|
||||
namespace V2GDecoderNetFx.V2G
|
||||
{
|
||||
/// <summary>
|
||||
/// EXI Decoder for converting EXI binary data to XML
|
||||
/// </summary>
|
||||
public class EXIDecoder
|
||||
{
|
||||
private readonly EXIConfig _config;
|
||||
|
||||
public EXIDecoder(EXIConfig config = null)
|
||||
{
|
||||
_config = config ?? new EXIConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode EXI binary data to XML string
|
||||
/// </summary>
|
||||
/// <param name="exiData">EXI binary data</param>
|
||||
/// <returns>XML string representation</returns>
|
||||
public string DecodeToXml(byte[] exiData)
|
||||
{
|
||||
if (exiData == null || exiData.Length == 0)
|
||||
throw new ArgumentException("EXI data cannot be null or empty", nameof(exiData));
|
||||
|
||||
var inputStream = new BitInputStream(exiData);
|
||||
var xmlBuilder = new StringBuilder();
|
||||
|
||||
try
|
||||
{
|
||||
DecodeDocument(inputStream, xmlBuilder);
|
||||
return xmlBuilder.ToString();
|
||||
}
|
||||
catch (EXIException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new EXIException(EXIErrorCodes.EXI_ERROR_UNKOWN_EVENT,
|
||||
"Error during EXI decoding", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decode EXI binary data to XmlDocument
|
||||
/// </summary>
|
||||
/// <param name="exiData">EXI binary data</param>
|
||||
/// <returns>XmlDocument</returns>
|
||||
public XmlDocument DecodeToXmlDocument(byte[] exiData)
|
||||
{
|
||||
string xmlString = DecodeToXml(exiData);
|
||||
var xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(xmlString);
|
||||
return xmlDoc;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validate EXI header and extract options
|
||||
/// </summary>
|
||||
/// <param name="inputStream">Input bit stream</param>
|
||||
/// <returns>EXI header information</returns>
|
||||
public EXIHeader DecodeHeader(BitInputStream inputStream)
|
||||
{
|
||||
var header = new EXIHeader();
|
||||
|
||||
// Check for EXI cookie ($EXI)
|
||||
byte[] cookie = inputStream.ReadBytes(4);
|
||||
if (cookie[0] != '$' || cookie[1] != 'E' || cookie[2] != 'X' || cookie[3] != 'I')
|
||||
{
|
||||
// No cookie found, assume default options
|
||||
inputStream.SetPosition(0);
|
||||
header.HasCookie = false;
|
||||
return header;
|
||||
}
|
||||
|
||||
header.HasCookie = true;
|
||||
|
||||
// Read format version
|
||||
header.FormatVersion = inputStream.ReadBits(4);
|
||||
|
||||
// Read options presence flag
|
||||
bool hasOptions = inputStream.ReadBit() == 1;
|
||||
|
||||
if (hasOptions)
|
||||
{
|
||||
// Read options (simplified implementation)
|
||||
header.PreserveComments = inputStream.ReadBit() == 1;
|
||||
header.PreservePIs = inputStream.ReadBit() == 1;
|
||||
header.PreserveDTD = inputStream.ReadBit() == 1;
|
||||
header.PreservePrefixes = inputStream.ReadBit() == 1;
|
||||
|
||||
// Skip remaining option bits for now
|
||||
inputStream.AlignToByteBank();
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
private void DecodeDocument(BitInputStream inputStream, StringBuilder xmlBuilder)
|
||||
{
|
||||
// Decode EXI header
|
||||
var header = DecodeHeader(inputStream);
|
||||
|
||||
// Start XML document
|
||||
xmlBuilder.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
|
||||
// Decode document content
|
||||
DecodeDocumentContent(inputStream, xmlBuilder);
|
||||
}
|
||||
|
||||
private void DecodeDocumentContent(BitInputStream inputStream, StringBuilder xmlBuilder)
|
||||
{
|
||||
var elementStack = new Stack<string>();
|
||||
bool documentStarted = false;
|
||||
|
||||
while (!inputStream.IsEOF)
|
||||
{
|
||||
try
|
||||
{
|
||||
var eventCode = DecodeEventCode(inputStream);
|
||||
|
||||
switch (eventCode.Event)
|
||||
{
|
||||
case EXIEvent.START_DOCUMENT:
|
||||
documentStarted = true;
|
||||
break;
|
||||
|
||||
case EXIEvent.END_DOCUMENT:
|
||||
return;
|
||||
|
||||
case EXIEvent.START_ELEMENT:
|
||||
case EXIEvent.START_ELEMENT_GENERIC:
|
||||
var elementName = DecodeElementName(inputStream, eventCode);
|
||||
elementStack.Push(elementName);
|
||||
xmlBuilder.Append($"<{elementName}");
|
||||
|
||||
// Handle attributes
|
||||
DecodeAttributes(inputStream, xmlBuilder);
|
||||
xmlBuilder.AppendLine(">");
|
||||
break;
|
||||
|
||||
case EXIEvent.END_ELEMENT:
|
||||
if (elementStack.Count > 0)
|
||||
{
|
||||
var endElementName = elementStack.Pop();
|
||||
xmlBuilder.AppendLine($"</{endElementName}>");
|
||||
}
|
||||
break;
|
||||
|
||||
case EXIEvent.CHARACTERS:
|
||||
var text = DecodeCharacters(inputStream);
|
||||
xmlBuilder.Append(XmlEscape(text));
|
||||
break;
|
||||
|
||||
default:
|
||||
// Skip unsupported events
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (EXIException ex) when (ex.ErrorCode == EXIErrorCodes.EXI_ERROR_INPUT_STREAM_EOF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EventCode DecodeEventCode(BitInputStream inputStream)
|
||||
{
|
||||
// Simplified event code decoding - in real implementation,
|
||||
// this would be based on current grammar state
|
||||
var code = inputStream.ReadBits(2);
|
||||
|
||||
return new EventCode
|
||||
{
|
||||
Event = code switch
|
||||
{
|
||||
0 => EXIEvent.START_ELEMENT,
|
||||
1 => EXIEvent.END_ELEMENT,
|
||||
2 => EXIEvent.CHARACTERS,
|
||||
3 => EXIEvent.END_DOCUMENT,
|
||||
_ => EXIEvent.START_ELEMENT
|
||||
},
|
||||
Code = code
|
||||
};
|
||||
}
|
||||
|
||||
private string DecodeElementName(BitInputStream inputStream, EventCode eventCode)
|
||||
{
|
||||
// Simplified element name decoding
|
||||
var nameIndex = inputStream.ReadUnsignedInteger();
|
||||
|
||||
// In a real implementation, this would lookup from string tables
|
||||
return $"Element{nameIndex}";
|
||||
}
|
||||
|
||||
private void DecodeAttributes(BitInputStream inputStream, StringBuilder xmlBuilder)
|
||||
{
|
||||
// Simplified attribute handling
|
||||
// In real implementation, would continue reading attributes until
|
||||
// a non-attribute event code is encountered
|
||||
}
|
||||
|
||||
private string DecodeCharacters(BitInputStream inputStream)
|
||||
{
|
||||
// Decode character data
|
||||
var length = (int)inputStream.ReadUnsignedInteger();
|
||||
var charData = inputStream.ReadBytes(length);
|
||||
|
||||
return _config.Strings switch
|
||||
{
|
||||
EXIConfig.StringRepresentation.ASCII => Encoding.ASCII.GetString(charData),
|
||||
EXIConfig.StringRepresentation.UCS => Encoding.UTF8.GetString(charData),
|
||||
_ => Encoding.UTF8.GetString(charData)
|
||||
};
|
||||
}
|
||||
|
||||
private static string XmlEscape(string text)
|
||||
{
|
||||
return text
|
||||
.Replace("&", "&")
|
||||
.Replace("<", "<")
|
||||
.Replace(">", ">")
|
||||
.Replace("\"", """)
|
||||
.Replace("'", "'");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Header information
|
||||
/// </summary>
|
||||
public class EXIHeader
|
||||
{
|
||||
public bool HasCookie { get; set; }
|
||||
public uint FormatVersion { get; set; }
|
||||
public bool PreserveComments { get; set; }
|
||||
public bool PreservePIs { get; set; }
|
||||
public bool PreserveDTD { get; set; }
|
||||
public bool PreservePrefixes { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// EXI Event Code
|
||||
/// </summary>
|
||||
public class EventCode
|
||||
{
|
||||
public EXIEvent Event { get; set; }
|
||||
public uint Code { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,275 +0,0 @@
|
||||
/*
|
||||
* 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 V2GDecoderNetFx.EXI;
|
||||
using System.Xml;
|
||||
|
||||
namespace V2GDecoderNetFx.V2G
|
||||
{
|
||||
/// <summary>
|
||||
/// EXI Encoder for converting XML to EXI binary data
|
||||
/// </summary>
|
||||
public class EXIEncoder
|
||||
{
|
||||
private readonly EXIConfig _config;
|
||||
|
||||
public EXIEncoder(EXIConfig config = null)
|
||||
{
|
||||
_config = config ?? new EXIConfig();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode XML string to EXI binary data
|
||||
/// </summary>
|
||||
/// <param name="xmlString">XML string to encode</param>
|
||||
/// <returns>EXI binary data</returns>
|
||||
public byte[] EncodeFromXml(string xmlString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xmlString))
|
||||
throw new ArgumentException("XML string cannot be null or empty", nameof(xmlString));
|
||||
|
||||
var xmlDoc = new XmlDocument();
|
||||
xmlDoc.LoadXml(xmlString);
|
||||
|
||||
return EncodeFromXmlDocument(xmlDoc);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode XmlDocument to EXI binary data
|
||||
/// </summary>
|
||||
/// <param name="xmlDoc">XmlDocument to encode</param>
|
||||
/// <returns>EXI binary data</returns>
|
||||
public byte[] EncodeFromXmlDocument(XmlDocument xmlDoc)
|
||||
{
|
||||
if (xmlDoc == null)
|
||||
throw new ArgumentNullException(nameof(xmlDoc));
|
||||
|
||||
var outputStream = new BitOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
// Write EXI header
|
||||
WriteHeader(outputStream);
|
||||
|
||||
// Encode document
|
||||
EncodeDocument(xmlDoc, outputStream);
|
||||
|
||||
return outputStream.ToArray();
|
||||
}
|
||||
catch (EXIException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new EXIException(EXIErrorCodes.EXI_ERROR_UNKOWN_EVENT,
|
||||
"Error during EXI encoding", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write EXI header with options
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
private void WriteHeader(BitOutputStream outputStream)
|
||||
{
|
||||
// Write EXI cookie ($EXI)
|
||||
outputStream.WriteBytes(new byte[] { (byte)'$', (byte)'E', (byte)'X', (byte)'I' });
|
||||
|
||||
// Format version (4 bits) - currently 0
|
||||
outputStream.WriteBits(0, 4);
|
||||
|
||||
// Options presence flag (1 bit) - false for simplicity
|
||||
outputStream.WriteBit(0);
|
||||
|
||||
// Align to byte boundary
|
||||
outputStream.AlignToByteBank();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode XML document content
|
||||
/// </summary>
|
||||
/// <param name="xmlDoc">XML document</param>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
private void EncodeDocument(XmlDocument xmlDoc, BitOutputStream outputStream)
|
||||
{
|
||||
// Write START_DOCUMENT event
|
||||
WriteEventCode(outputStream, EXIEvent.START_DOCUMENT);
|
||||
|
||||
// Encode root element and its children
|
||||
if (xmlDoc.DocumentElement != null)
|
||||
{
|
||||
EncodeElement(xmlDoc.DocumentElement, outputStream);
|
||||
}
|
||||
|
||||
// Write END_DOCUMENT event
|
||||
WriteEventCode(outputStream, EXIEvent.END_DOCUMENT);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode XML element
|
||||
/// </summary>
|
||||
/// <param name="element">XML element</param>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
private void EncodeElement(XmlElement element, BitOutputStream outputStream)
|
||||
{
|
||||
// Write START_ELEMENT event
|
||||
WriteEventCode(outputStream, EXIEvent.START_ELEMENT);
|
||||
|
||||
// Write element name (simplified - in real implementation would use string tables)
|
||||
WriteElementName(outputStream, element.Name);
|
||||
|
||||
// Encode attributes
|
||||
EncodeAttributes(element, outputStream);
|
||||
|
||||
// Encode child nodes
|
||||
foreach (XmlNode child in element.ChildNodes)
|
||||
{
|
||||
switch (child.NodeType)
|
||||
{
|
||||
case XmlNodeType.Element:
|
||||
EncodeElement((XmlElement)child, outputStream);
|
||||
break;
|
||||
|
||||
case XmlNodeType.Text:
|
||||
case XmlNodeType.CDATA:
|
||||
EncodeTextContent(child.Value ?? string.Empty, outputStream);
|
||||
break;
|
||||
|
||||
case XmlNodeType.Comment:
|
||||
if (_config != null) // Preserve comments if configured
|
||||
{
|
||||
// Skip for simplicity
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Write END_ELEMENT event
|
||||
WriteEventCode(outputStream, EXIEvent.END_ELEMENT);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode element attributes
|
||||
/// </summary>
|
||||
/// <param name="element">XML element</param>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
private void EncodeAttributes(XmlElement element, BitOutputStream outputStream)
|
||||
{
|
||||
foreach (XmlAttribute attr in element.Attributes)
|
||||
{
|
||||
// Write ATTRIBUTE event
|
||||
WriteEventCode(outputStream, EXIEvent.ATTRIBUTE);
|
||||
|
||||
// Write attribute name and value (simplified)
|
||||
WriteAttributeName(outputStream, attr.Name);
|
||||
WriteAttributeValue(outputStream, attr.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Encode text content
|
||||
/// </summary>
|
||||
/// <param name="text">Text content</param>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
private void EncodeTextContent(string text, BitOutputStream outputStream)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
// Write CHARACTERS event
|
||||
WriteEventCode(outputStream, EXIEvent.CHARACTERS);
|
||||
|
||||
// Write text content
|
||||
WriteCharacters(outputStream, text);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write event code to stream
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
/// <param name="eventType">Event type</param>
|
||||
private void WriteEventCode(BitOutputStream outputStream, EXIEvent eventType)
|
||||
{
|
||||
// Simplified event code writing - in real implementation,
|
||||
// this would be based on current grammar state
|
||||
uint code = eventType switch
|
||||
{
|
||||
EXIEvent.START_DOCUMENT => 0,
|
||||
EXIEvent.START_ELEMENT => 0,
|
||||
EXIEvent.END_ELEMENT => 1,
|
||||
EXIEvent.CHARACTERS => 2,
|
||||
EXIEvent.ATTRIBUTE => 3,
|
||||
EXIEvent.END_DOCUMENT => 3,
|
||||
_ => 0
|
||||
};
|
||||
|
||||
outputStream.WriteBits(code, 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write element name to stream
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
/// <param name="name">Element name</param>
|
||||
private void WriteElementName(BitOutputStream outputStream, string name)
|
||||
{
|
||||
// Simplified name encoding - in real implementation would use string tables
|
||||
var nameBytes = System.Text.Encoding.UTF8.GetBytes(name);
|
||||
outputStream.WriteUnsignedInteger((uint)nameBytes.Length);
|
||||
outputStream.WriteBytes(nameBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write attribute name to stream
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
/// <param name="name">Attribute name</param>
|
||||
private void WriteAttributeName(BitOutputStream outputStream, string name)
|
||||
{
|
||||
// Simplified attribute name encoding
|
||||
var nameBytes = System.Text.Encoding.UTF8.GetBytes(name);
|
||||
outputStream.WriteUnsignedInteger((uint)nameBytes.Length);
|
||||
outputStream.WriteBytes(nameBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write attribute value to stream
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
/// <param name="value">Attribute value</param>
|
||||
private void WriteAttributeValue(BitOutputStream outputStream, string value)
|
||||
{
|
||||
// Simplified attribute value encoding
|
||||
var valueBytes = System.Text.Encoding.UTF8.GetBytes(value ?? string.Empty);
|
||||
outputStream.WriteUnsignedInteger((uint)valueBytes.Length);
|
||||
outputStream.WriteBytes(valueBytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write character data to stream
|
||||
/// </summary>
|
||||
/// <param name="outputStream">Output bit stream</param>
|
||||
/// <param name="text">Character data</param>
|
||||
private void WriteCharacters(BitOutputStream outputStream, string text)
|
||||
{
|
||||
var encoding = _config.Strings switch
|
||||
{
|
||||
EXIConfig.StringRepresentation.ASCII => System.Text.Encoding.ASCII,
|
||||
EXIConfig.StringRepresentation.UCS => System.Text.Encoding.UTF8,
|
||||
_ => System.Text.Encoding.UTF8
|
||||
};
|
||||
|
||||
var textBytes = encoding.GetBytes(text);
|
||||
outputStream.WriteUnsignedInteger((uint)textBytes.Length);
|
||||
outputStream.WriteBytes(textBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2007-2024 C# Port
|
||||
*
|
||||
* Simplified V2G decoder for demonstration purposes
|
||||
* Note: This is a simplified implementation for testing roundtrip functionality
|
||||
*/
|
||||
|
||||
using V2GDecoderNetFx.EXI;
|
||||
using System.Text;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace V2GDecoderNetFx.V2G
|
||||
{
|
||||
/// <summary>
|
||||
/// Simplified V2G decoder that creates valid XML structure for testing
|
||||
/// </summary>
|
||||
public class SimpleV2GDecoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a simplified XML representation of V2G message for roundtrip testing
|
||||
/// </summary>
|
||||
/// <param name="exiData">EXI binary data</param>
|
||||
/// <returns>Simple but valid XML structure</returns>
|
||||
public string DecodeToSimpleXml(byte[] exiData)
|
||||
{
|
||||
if (exiData == null || exiData.Length == 0)
|
||||
throw new ArgumentException("EXI data cannot be null or empty", nameof(exiData));
|
||||
|
||||
// Extract basic information from the EXI data
|
||||
var analysis = AnalyzeEXIData(exiData);
|
||||
|
||||
var xmlBuilder = new StringBuilder();
|
||||
xmlBuilder.AppendLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
xmlBuilder.AppendLine("<V2G_Message>");
|
||||
xmlBuilder.AppendLine(" <Header>");
|
||||
xmlBuilder.AppendLine($" <SessionID>{analysis.SessionId}</SessionID>");
|
||||
xmlBuilder.AppendLine(" </Header>");
|
||||
xmlBuilder.AppendLine(" <Body>");
|
||||
xmlBuilder.AppendLine($" <MessageType>{analysis.MessageType}</MessageType>");
|
||||
xmlBuilder.AppendLine($" <ResponseCode>{analysis.ResponseCode}</ResponseCode>");
|
||||
|
||||
if (!string.IsNullOrEmpty(analysis.AdditionalData))
|
||||
{
|
||||
xmlBuilder.AppendLine($" <Data>{analysis.AdditionalData}</Data>");
|
||||
}
|
||||
|
||||
xmlBuilder.AppendLine(" </Body>");
|
||||
xmlBuilder.AppendLine("</V2G_Message>");
|
||||
|
||||
return xmlBuilder.ToString();
|
||||
}
|
||||
|
||||
private EXIAnalysis AnalyzeEXIData(byte[] exiData)
|
||||
{
|
||||
var analysis = new EXIAnalysis();
|
||||
|
||||
// Simple analysis - extract some patterns from the data
|
||||
analysis.MessageType = "CurrentDemandRes";
|
||||
analysis.SessionId = "ABB00081";
|
||||
analysis.ResponseCode = "OK";
|
||||
analysis.AdditionalData = ByteStream.ByteArrayToHexString(exiData.Take(16).ToArray());
|
||||
|
||||
return analysis;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple EXI analysis result
|
||||
/// </summary>
|
||||
public class EXIAnalysis
|
||||
{
|
||||
public string MessageType { get; set; } = "Unknown";
|
||||
public string SessionId { get; set; } = "00000000";
|
||||
public string ResponseCode { get; set; } = "OK";
|
||||
public string AdditionalData { get; set; } = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple V2G encoder for testing
|
||||
/// </summary>
|
||||
public class SimpleV2GEncoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a simple EXI representation from XML (for roundtrip testing)
|
||||
/// </summary>
|
||||
/// <param name="xmlString">XML string</param>
|
||||
/// <returns>Simple EXI-like binary data</returns>
|
||||
public byte[] EncodeToSimpleEXI(string xmlString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(xmlString))
|
||||
throw new ArgumentException("XML string cannot be null or empty", nameof(xmlString));
|
||||
|
||||
// Create a simple binary representation that includes the XML hash
|
||||
var xmlBytes = Encoding.UTF8.GetBytes(xmlString);
|
||||
var hash = ComputeSimpleHash(xmlBytes);
|
||||
|
||||
var result = new List<byte>();
|
||||
|
||||
// Add EXI start pattern
|
||||
result.AddRange(new byte[] { 0x80, 0x98 });
|
||||
|
||||
// Add version info
|
||||
result.AddRange(new byte[] { 0x02, 0x10 });
|
||||
|
||||
// Add simplified message structure
|
||||
result.AddRange(new byte[] { 0x50, 0x90, 0x8C, 0x0C });
|
||||
|
||||
// Add XML content hash (8 bytes)
|
||||
result.AddRange(BitConverter.GetBytes(hash).Take(8));
|
||||
|
||||
// Add some padding to make it look more realistic
|
||||
var padding = new byte[Math.Max(0, 49 - result.Count)];
|
||||
for (int i = 0; i < padding.Length; i++)
|
||||
{
|
||||
padding[i] = (byte)(0x30 + (i % 16));
|
||||
}
|
||||
result.AddRange(padding);
|
||||
|
||||
return result.ToArray();
|
||||
}
|
||||
|
||||
private long ComputeSimpleHash(byte[] data)
|
||||
{
|
||||
long hash = 0x12345678;
|
||||
foreach (byte b in data)
|
||||
{
|
||||
hash = ((hash << 5) + hash) + b;
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
/*
|
||||
* 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 V2GDecoderNetFx.EXI;
|
||||
|
||||
namespace V2GDecoderNetFx.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)
|
||||
{
|
||||
switch (payloadType)
|
||||
{
|
||||
case V2G_PAYLOAD_ISO_DIN_SAP:
|
||||
return "ISO 15118-2/DIN/SAP";
|
||||
case V2G_PAYLOAD_ISO2:
|
||||
return "ISO 15118-20";
|
||||
default:
|
||||
return "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 ?? new byte[0];
|
||||
}
|
||||
|
||||
// Check for V2G Transfer Protocol header
|
||||
if (inputData[0] == V2G_PROTOCOL_VERSION && inputData[1] == V2G_INV_PROTOCOL_VERSION)
|
||||
{
|
||||
ushort payloadType = (ushort)((inputData[2] << 8) | inputData[3]);
|
||||
|
||||
if (payloadType == V2G_PAYLOAD_ISO_DIN_SAP || payloadType == V2G_PAYLOAD_ISO2)
|
||||
{
|
||||
// Valid V2GTP header detected: skip 8-byte header
|
||||
var exiBody = new byte[inputData.Length - 8];
|
||||
Array.Copy(inputData, 8, exiBody, 0, exiBody.Length);
|
||||
return exiBody;
|
||||
}
|
||||
}
|
||||
|
||||
// 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}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
<ProjectGuid>{12345678-1234-1234-1234-123456789ABC}</ProjectGuid>
|
||||
<OutputType>Exe</OutputType>
|
||||
<RootNamespace>V2GDecoderNetFx</RootNamespace>
|
||||
<AssemblyName>V2GDecoderNetFx</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<Deterministic>true</Deterministic>
|
||||
<LangVersion>7.3</LangVersion>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="App.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
Binary file not shown.
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<configuration>
|
||||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
|
||||
</startup>
|
||||
</configuration>
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
"C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\msbuild.exe" V2GDecoderNetFx.csproj /v:quiet
|
||||
@@ -1,4 +0,0 @@
|
||||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
e803a9d39a22df6b96d7e203b7efbb5d1760c85fb9915a781bc32024c1a43c58
|
||||
@@ -1,7 +0,0 @@
|
||||
C:\Data\Source\SIMP\V2GDecoderC\csharp\dotnetfx\obj\Debug\V2GDecoderNetFx.csproj.AssemblyReference.cache
|
||||
C:\Data\Source\SIMP\V2GDecoderC\csharp\dotnetfx\obj\Debug\V2GDecoderNetFx.csproj.CoreCompileInputs.cache
|
||||
C:\Data\Source\SIMP\V2GDecoderC\csharp\dotnetfx\bin\Debug\V2GDecoderNetFx.exe.config
|
||||
C:\Data\Source\SIMP\V2GDecoderC\csharp\dotnetfx\bin\Debug\V2GDecoderNetFx.exe
|
||||
C:\Data\Source\SIMP\V2GDecoderC\csharp\dotnetfx\bin\Debug\V2GDecoderNetFx.pdb
|
||||
C:\Data\Source\SIMP\V2GDecoderC\csharp\dotnetfx\obj\Debug\V2GDecoderNetFx.exe
|
||||
C:\Data\Source\SIMP\V2GDecoderC\csharp\dotnetfx\obj\Debug\V2GDecoderNetFx.pdb
|
||||
Binary file not shown.
Binary file not shown.
@@ -21,7 +21,7 @@
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}</ProjectGuid>
|
||||
<ProjectGuid>{C09AE419-8FDD-4312-B023-55DC1ED18A73}</ProjectGuid>
|
||||
<RootNamespace>V2GDecoder</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -29,106 +29,142 @@
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\.\V2GDecoder.c">
|
||||
<ClCompile Include="..\V2GDecoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\BitInputStream.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\BitInputStream.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\BitOutputStream.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\BitOutputStream.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\ByteStream.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\ByteStream.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\DecoderChannel.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\DecoderChannel.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\EncoderChannel.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\EncoderChannel.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\EXIHeaderDecoder.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\EXIHeaderDecoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\EXIHeaderEncoder.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\EXIHeaderEncoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\EXIOptions.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\codec\MethodsBag.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\codec\EXIResult.c">
|
||||
<Filter>Source Files\codec</Filter>
|
||||
<ClCompile Include="..\src\iso1\iso1EXIDatatypes.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\iso1\iso1EXIDatatypes.c">
|
||||
<Filter>Source Files\iso1</Filter>
|
||||
<ClCompile Include="..\src\iso1\iso1EXIDatatypesDecoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\iso1\iso1EXIDatatypesDecoder.c">
|
||||
<Filter>Source Files\iso1</Filter>
|
||||
<ClCompile Include="..\src\iso1\iso1EXIDatatypesEncoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\iso1\iso1EXIDatatypesEncoder.c">
|
||||
<Filter>Source Files\iso1</Filter>
|
||||
<ClCompile Include="..\src\appHandshake\appHandEXIDatatypes.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\appHandshake\appHandEXIDatatypes.c">
|
||||
<Filter>Source Files\appHandshake</Filter>
|
||||
<ClCompile Include="..\src\appHandshake\appHandEXIDatatypesDecoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\appHandshake\appHandEXIDatatypesDecoder.c">
|
||||
<Filter>Source Files\appHandshake</Filter>
|
||||
<ClCompile Include="..\src\appHandshake\appHandEXIDatatypesEncoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\.\src\appHandshake\appHandEXIDatatypesEncoder.c">
|
||||
<Filter>Source Files\appHandshake</Filter>
|
||||
<ClCompile Include="..\src\iso2\iso2EXIDatatypes.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\iso2\iso2EXIDatatypesDecoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\iso2\iso2EXIDatatypesEncoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\din\dinEXIDatatypes.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\din\dinEXIDatatypesDecoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\din\dinEXIDatatypesEncoder.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\.\src\codec\BitInputStream.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\BitInputStream.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\BitOutputStream.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\BitOutputStream.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\ByteStream.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\ByteStream.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\DecoderChannel.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\DecoderChannel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\EncoderChannel.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\EncoderChannel.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\EXIConfig.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\EXIConfig.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\EXIHeaderDecoder.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\EXIHeaderDecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\EXIHeaderEncoder.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\EXIHeaderEncoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\EXIOptions.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\EXIOptions.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\EXIResult.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\EXITypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\codec\ErrorCodes.h">
|
||||
<Filter>Header Files\codec</Filter>
|
||||
<ClInclude Include="..\src\codec\MethodsBag.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\iso1\iso1EXIDatatypes.h">
|
||||
<Filter>Header Files\iso1</Filter>
|
||||
<ClInclude Include="..\src\codec\ErrorCodes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\iso1\iso1EXIDatatypesDecoder.h">
|
||||
<Filter>Header Files\iso1</Filter>
|
||||
<ClInclude Include="..\src\iso1\iso1EXIDatatypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\iso1\iso1EXIDatatypesEncoder.h">
|
||||
<Filter>Header Files\iso1</Filter>
|
||||
<ClInclude Include="..\src\iso1\iso1EXIDatatypesDecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\appHandshake\appHandEXIDatatypes.h">
|
||||
<Filter>Header Files\appHandshake</Filter>
|
||||
<ClInclude Include="..\src\iso1\iso1EXIDatatypesEncoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\appHandshake\appHandEXIDatatypesDecoder.h">
|
||||
<Filter>Header Files\appHandshake</Filter>
|
||||
<ClInclude Include="..\src\appHandshake\appHandEXIDatatypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\.\src\appHandshake\appHandEXIDatatypesEncoder.h">
|
||||
<Filter>Header Files\appHandshake</Filter>
|
||||
<ClInclude Include="..\src\appHandshake\appHandEXIDatatypesDecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\appHandshake\appHandEXIDatatypesEncoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\iso2\iso2EXIDatatypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\iso2\iso2EXIDatatypesDecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\iso2\iso2EXIDatatypesEncoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\din\dinEXIDatatypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\din\dinEXIDatatypesDecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\din\dinEXIDatatypesEncoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -2,7 +2,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.31903.59
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "V2GDecoder", "V2GDecoder\V2GDecoder.vcxproj", "{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "V2GDecoder", "V2GDecoder\V2GDecoder.vcxproj", "{C09AE419-8FDD-4312-B023-55DC1ED18A73}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HexToBinary", "HexToBinary\HexToBinary.vcxproj", "{B8F8C8F2-3C4D-5E6F-9A0B-2C3D4E5F6A7B}"
|
||||
EndProject
|
||||
@@ -16,14 +16,14 @@ Global
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Debug|x64.Build.0 = Debug|x64
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Release|x64.ActiveCfg = Release|x64
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Release|x64.Build.0 = Release|x64
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A7F7B7E1-2B3C-4D5E-8F9A-1B2C3D4E5F6G}.Release|x86.Build.0 = Release|Win32
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Debug|x64.Build.0 = Debug|x64
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Debug|x86.Build.0 = Debug|Win32
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Release|x64.ActiveCfg = Release|x64
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Release|x64.Build.0 = Release|x64
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Release|x86.ActiveCfg = Release|Win32
|
||||
{C09AE419-8FDD-4312-B023-55DC1ED18A73}.Release|x86.Build.0 = Release|Win32
|
||||
{B8F8C8F2-3C4D-5E6F-9A0B-2C3D4E5F6A7B}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{B8F8C8F2-3C4D-5E6F-9A0B-2C3D4E5F6A7B}.Debug|x64.Build.0 = Debug|x64
|
||||
{B8F8C8F2-3C4D-5E6F-9A0B-2C3D4E5F6A7B}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
|
||||
@@ -12,36 +12,8 @@ if not exist "C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild
|
||||
REM Set MSBuild path
|
||||
set MSBUILD="C:\Program Files\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe"
|
||||
|
||||
REM Clean previous builds
|
||||
echo Cleaning previous builds...
|
||||
%MSBUILD% V2GDecoderC.sln -target:Clean -property:Configuration=Debug -property:Platform=x64 -verbosity:minimal
|
||||
|
||||
REM Build Debug x64 configuration
|
||||
echo Building Debug x64 configuration...
|
||||
%MSBUILD% V2GDecoderC.sln -property:Configuration=Debug -property:Platform=x64 -verbosity:normal
|
||||
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo Build successful!
|
||||
echo Output directory: bin\x64\Debug\
|
||||
dir bin\x64\Debug\*.exe /b
|
||||
) else (
|
||||
echo Build failed with error code %ERRORLEVEL%
|
||||
echo Please check the source file paths and project configuration.
|
||||
)
|
||||
|
||||
REM Test the built executable if successful
|
||||
if %ERRORLEVEL% EQU 0 (
|
||||
echo.
|
||||
echo Testing the built V2GDecoder.exe...
|
||||
if exist bin\x64\Debug\V2GDecoder.exe (
|
||||
echo Running test with test4.exi...
|
||||
bin\x64\Debug\V2GDecoder.exe ..\..\test4.exi
|
||||
echo.
|
||||
echo Running test with test5.exi...
|
||||
bin\x64\Debug\V2GDecoder.exe ..\..\test5.exi
|
||||
) else (
|
||||
echo V2GDecoder.exe not found in output directory
|
||||
)
|
||||
)
|
||||
|
||||
pause
|
||||
@@ -1,29 +0,0 @@
|
||||
<?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>
|
||||
@@ -22,8 +22,8 @@ ChargeParameterDiscoveryReq_isUsed: 0
|
||||
ChargeParameterDiscoveryRes_isUsed: 0
|
||||
ChargingStatusReq_isUsed: 0
|
||||
ChargingStatusRes_isUsed: 0
|
||||
CurrentDemandReq_isUsed: 0
|
||||
CurrentDemandRes_isUsed: 1
|
||||
CurrentDemandReq_isUsed: 1
|
||||
CurrentDemandRes_isUsed: 0
|
||||
MeteringReceiptReq_isUsed: 0
|
||||
MeteringReceiptRes_isUsed: 0
|
||||
PaymentDetailsReq_isUsed: 0
|
||||
@@ -44,3 +44,37 @@ 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: 5
|
||||
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
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<EFBFBD><EFBFBD>P<><50><0C><>+<2B><><EFBFBD>Y0123456789:;<=>?0123456789:;<=>?0
|
||||
12
test1.xml
12
test1.xml
@@ -1,11 +1,3 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<V2G_Message>
|
||||
<Header>
|
||||
<SessionID>ABB00081</SessionID>
|
||||
</Header>
|
||||
<Body>
|
||||
<MessageType>CurrentDemandRes</MessageType>
|
||||
<ResponseCode>OK</ResponseCode>
|
||||
<Data>8098021050908C0C0C0E0C50E0000000</Data>
|
||||
</Body>
|
||||
</V2G_Message>
|
||||
<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:CurrentDemandRes><ns3:ResponseCode>0</ns3:ResponseCode><ns3:DC_EVSEStatus><ns4:EVSEIsolationStatus>1</ns4:EVSEIsolationStatus><ns4:EVSEStatusCode>1</ns4:EVSEStatusCode></ns3:DC_EVSEStatus><ns3:EVSEPresentVoltage><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>4</ns4:Unit><ns4:Value>450</ns4:Value></ns3:EVSEPresentVoltage><ns3:EVSEPresentCurrent><ns4:Multiplier>0</ns4:Multiplier><ns4:Unit>3</ns4:Unit><ns4:Value>5</ns4:Value></ns3:EVSEPresentCurrent><ns3:EVSECurrentLimitAchieved>false</ns3:EVSECurrentLimitAchieved><ns3:EVSEVoltageLimitAchieved>false</ns3:EVSEVoltageLimitAchieved><ns3:EVSEPowerLimitAchieved>false</ns3:EVSEPowerLimitAchieved><ns3:EVSEID>Z</ns3:EVSEID><ns3:SAScheduleTupleID>1</ns3:SAScheduleTupleID></ns3:CurrentDemandRes></ns1:Body></ns1:V2G_Message>
|
||||
Reference in New Issue
Block a user