Port to .NET Framework 4.8 with C# 9.0 compatibility

- Change target framework from net6.0 to net48 with LangVersion 9.0
- Remove nullable reference types for .NET Framework compatibility
- Replace Convert.FromHexString/ToHexString with custom implementations
- Fix switch pattern matching to use traditional case statements
- Replace range operators with LINQ Take/Skip and Array.Copy
- Replace Math.Log2 with Math.Log(p)/Math.Log(2) formula
- Fix Contains method calls to use ToLower() instead of StringComparison

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-07 20:02:22 +09:00
parent 4f2f6a99d7
commit e09c63080e
3 changed files with 58 additions and 24 deletions

View File

@@ -7,6 +7,23 @@ namespace V2GProtocol
{ {
class Program class Program
{ {
static byte[] FromHexString(string hexString)
{
if (hexString.Length % 2 != 0)
throw new ArgumentException("Invalid hex string length");
byte[] bytes = new byte[hexString.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
return bytes;
}
static string ToHexString(byte[] bytes)
{
return BitConverter.ToString(bytes).Replace("-", "");
}
static void Main(string[] args) static void Main(string[] args)
{ {
// Only show header for help or analysis mode (not for encode/decode operations) // Only show header for help or analysis mode (not for encode/decode operations)
@@ -63,13 +80,16 @@ namespace V2GProtocol
{ {
switch (command) switch (command)
{ {
case "--decode" or "-d": case "--decode":
case "-d":
HandleDecodeCommand(args); HandleDecodeCommand(args);
break; break;
case "--encode" or "-e": case "--encode":
case "-e":
HandleEncodeCommand(args); HandleEncodeCommand(args);
break; break;
case "--help" or "-h": case "--help":
case "-h":
ShowUsage(); ShowUsage();
break; break;
default: default:
@@ -172,7 +192,7 @@ namespace V2GProtocol
Console.WriteLine(); Console.WriteLine();
} }
static string? GetOutputFilename(string[] args) static string GetOutputFilename(string[] args)
{ {
for (int i = 0; i < args.Length - 1; i++) for (int i = 0; i < args.Length - 1; i++)
{ {
@@ -188,7 +208,7 @@ namespace V2GProtocol
{ {
string input; string input;
byte[] exiBytes; byte[] exiBytes;
string? outputFile = GetOutputFilename(args); string outputFile = GetOutputFilename(args);
// Check if we have sufficient arguments for file/string input // Check if we have sufficient arguments for file/string input
if (args.Length >= 2) if (args.Length >= 2)
@@ -264,7 +284,7 @@ namespace V2GProtocol
{ {
// Plain hex // Plain hex
fileContent = fileContent.Replace(" ", "").Replace("-", "").Replace("0x", "").Replace("\n", "").Replace("\r", ""); fileContent = fileContent.Replace(" ", "").Replace("-", "").Replace("0x", "").Replace("\n", "").Replace("\r", "");
exiBytes = Convert.FromHexString(fileContent); exiBytes = FromHexString(fileContent);
} }
} }
else else
@@ -272,7 +292,7 @@ namespace V2GProtocol
// Unknown extension, try as plain hex // Unknown extension, try as plain hex
string fileContent = File.ReadAllText(input).Trim(); string fileContent = File.ReadAllText(input).Trim();
fileContent = fileContent.Replace(" ", "").Replace("-", "").Replace("0x", "").Replace("\n", "").Replace("\r", ""); fileContent = fileContent.Replace(" ", "").Replace("-", "").Replace("0x", "").Replace("\n", "").Replace("\r", "");
exiBytes = Convert.FromHexString(fileContent); exiBytes = FromHexString(fileContent);
} }
} }
else else
@@ -286,7 +306,7 @@ namespace V2GProtocol
return; return;
} }
exiBytes = Convert.FromHexString(exiHexString); exiBytes = FromHexString(exiHexString);
} }
try try
@@ -312,7 +332,7 @@ namespace V2GProtocol
} }
} }
static byte[]? ExtractV2GMessageFromHexDump(byte[] data) static byte[] ExtractV2GMessageFromHexDump(byte[] data)
{ {
// Find V2G Transfer Protocol signature (0x01FE) // Find V2G Transfer Protocol signature (0x01FE)
for (int i = 0; i < data.Length - 8; i++) for (int i = 0; i < data.Length - 8; i++)
@@ -338,7 +358,7 @@ namespace V2GProtocol
{ {
string xmlInput; string xmlInput;
string xmlContent; string xmlContent;
string? outputFile = GetOutputFilename(args); string outputFile = GetOutputFilename(args);
// Check if we have sufficient arguments for file/string input // Check if we have sufficient arguments for file/string input
if (args.Length >= 2) if (args.Length >= 2)
@@ -386,13 +406,13 @@ namespace V2GProtocol
Console.WriteLine($"Encoded binary saved to: {outputFile} ({exiBytes.Length} bytes)"); Console.WriteLine($"Encoded binary saved to: {outputFile} ({exiBytes.Length} bytes)");
// Also show hex string on console for reference // Also show hex string on console for reference
var exiHexString = Convert.ToHexString(exiBytes); var exiHexString = ToHexString(exiBytes);
Console.WriteLine($"Hex: {exiHexString}"); Console.WriteLine($"Hex: {exiHexString}");
} }
else else
{ {
// Output hex string to console // Output hex string to console
var exiHexString = Convert.ToHexString(exiBytes); var exiHexString = ToHexString(exiBytes);
Console.WriteLine(exiHexString); Console.WriteLine(exiHexString);
} }
} }
@@ -485,8 +505,8 @@ namespace V2GProtocol
{ {
// 이더넷 헤더 분석 (14 bytes) // 이더넷 헤더 분석 (14 bytes)
Console.WriteLine("Ethernet Header:"); Console.WriteLine("Ethernet Header:");
Console.WriteLine($" Destination MAC: {string.Join(":", data[0..6].Select(b => b.ToString("X2")))}"); Console.WriteLine($" Destination MAC: {string.Join(":", data.Take(6).Select(b => b.ToString("X2")))}");
Console.WriteLine($" Source MAC: {string.Join(":", data[6..12].Select(b => b.ToString("X2")))}"); Console.WriteLine($" Source MAC: {string.Join(":", data.Skip(6).Take(6).Select(b => b.ToString("X2")))}");
Console.WriteLine($" EtherType: 0x{(data[12] << 8 | data[13]):X4}"); Console.WriteLine($" EtherType: 0x{(data[12] << 8 | data[13]):X4}");
ushort etherType = (ushort)(data[12] << 8 | data[13]); ushort etherType = (ushort)(data[12] << 8 | data[13]);
@@ -508,8 +528,10 @@ namespace V2GProtocol
Console.WriteLine($" Hop Limit: {hopLimit}"); Console.WriteLine($" Hop Limit: {hopLimit}");
// IPv6 주소는 16바이트씩 // IPv6 주소는 16바이트씩
var srcAddr = data[(ipv6Offset + 8)..(ipv6Offset + 24)]; byte[] srcAddr = new byte[16];
var dstAddr = data[(ipv6Offset + 24)..(ipv6Offset + 40)]; byte[] dstAddr = new byte[16];
Array.Copy(data, ipv6Offset + 8, srcAddr, 0, 16);
Array.Copy(data, ipv6Offset + 24, dstAddr, 0, 16);
Console.WriteLine($" Source Address: {string.Join(":", Enumerable.Range(0, 8).Select(i => $"{srcAddr[i*2]:X2}{srcAddr[i*2+1]:X2}"))}"); Console.WriteLine($" Source Address: {string.Join(":", Enumerable.Range(0, 8).Select(i => $"{srcAddr[i*2]:X2}{srcAddr[i*2+1]:X2}"))}");
Console.WriteLine($" Destination Address: {string.Join(":", Enumerable.Range(0, 8).Select(i => $"{dstAddr[i*2]:X2}{dstAddr[i*2+1]:X2}"))}"); Console.WriteLine($" Destination Address: {string.Join(":", Enumerable.Range(0, 8).Select(i => $"{dstAddr[i*2]:X2}{dstAddr[i*2+1]:X2}"))}");
@@ -539,7 +561,8 @@ namespace V2GProtocol
if (tcpDataOffset < data.Length) if (tcpDataOffset < data.Length)
{ {
Console.WriteLine($"\nTCP Payload (starting at offset 0x{tcpDataOffset:X4}):"); Console.WriteLine($"\nTCP Payload (starting at offset 0x{tcpDataOffset:X4}):");
byte[] tcpPayload = data[tcpDataOffset..]; byte[] tcpPayload = new byte[data.Length - tcpDataOffset];
Array.Copy(data, tcpDataOffset, tcpPayload, 0, tcpPayload.Length);
// TCP 페이로드에서 V2G 메시지 찾기 // TCP 페이로드에서 V2G 메시지 찾기
if (tcpPayload.Length > 8 && tcpPayload[0] == 0x01 && tcpPayload[1] == 0xFE) if (tcpPayload.Length > 8 && tcpPayload[0] == 0x01 && tcpPayload[1] == 0xFE)

View File

@@ -8,6 +8,18 @@ namespace V2GProtocol
{ {
public class V2GDecoder public class V2GDecoder
{ {
static byte[] FromHexString(string hexString)
{
if (hexString.Length % 2 != 0)
throw new ArgumentException("Invalid hex string length");
byte[] bytes = new byte[hexString.Length / 2];
for (int i = 0; i < bytes.Length; i++)
{
bytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
return bytes;
}
private const byte V2G_TP_VERSION = 0x01; private const byte V2G_TP_VERSION = 0x01;
private const byte V2G_TP_INVERSE_VERSION = 0xFE; private const byte V2G_TP_INVERSE_VERSION = 0xFE;
@@ -420,7 +432,7 @@ namespace V2GProtocol
if (byteCounts[i] > 0) if (byteCounts[i] > 0)
{ {
double p = (double)byteCounts[i] / totalBytes; double p = (double)byteCounts[i] / totalBytes;
entropy -= p * Math.Log2(p); entropy -= p * (Math.Log(p) / Math.Log(2));
} }
} }
@@ -447,7 +459,7 @@ namespace V2GProtocol
foreach (var keyword in v2gKeywords) foreach (var keyword in v2gKeywords)
{ {
if (text.Contains(keyword, StringComparison.OrdinalIgnoreCase)) if (text.ToLower().Contains(keyword.ToLower()))
xmlIndicators += 2; xmlIndicators += 2;
} }
@@ -488,7 +500,7 @@ namespace V2GProtocol
foreach (var keyword in keywords) foreach (var keyword in keywords)
{ {
if (dataAsString.Contains(keyword, StringComparison.OrdinalIgnoreCase)) if (dataAsString.ToLower().Contains(keyword.ToLower()))
{ {
found.Add(keyword); found.Add(keyword);
} }
@@ -1057,7 +1069,7 @@ namespace V2GProtocol
var sessionId = ExtractValueFromXML(xmlContent, "SessionID"); var sessionId = ExtractValueFromXML(xmlContent, "SessionID");
if (!string.IsNullOrEmpty(sessionId) && sessionId.Length == 16) // 8 bytes as hex if (!string.IsNullOrEmpty(sessionId) && sessionId.Length == 16) // 8 bytes as hex
{ {
var sessionBytes = Convert.FromHexString(sessionId); var sessionBytes = FromHexString(sessionId);
foreach (byte b in sessionBytes) foreach (byte b in sessionBytes)
{ {
if (b >= 0x30 && b <= 0x5A) // ASCII range if (b >= 0x30 && b <= 0x5A) // ASCII range

View File

@@ -2,9 +2,8 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net48</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <LangVersion>9.0</LangVersion>
<Nullable>enable</Nullable>
<AssemblyName>V2GDecoder</AssemblyName> <AssemblyName>V2GDecoder</AssemblyName>
</PropertyGroup> </PropertyGroup>