Skip to content

Commit

Permalink
rewrite device logic
Browse files Browse the repository at this point in the history
  • Loading branch information
dj-nitehawk committed Apr 15, 2023
1 parent 55d5163 commit 6bfb643
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 66 deletions.
2 changes: 0 additions & 2 deletions src/Server/InverterMon.Server.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="HidSharp" Version="2.1.0" />
<PackageReference Include="SerialPortLib" Version="1.1.1" />
<PackageReference Include="FastEndpoints" Version="5.8.1" />
<!--<PackageReference Include="FastEndpoints.Swagger" Version="5.8.1" />-->
<PackageReference Include="LiteDB" Version="5.0.16" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="7.0.5" />
</ItemGroup>
Expand Down
64 changes: 9 additions & 55 deletions src/Server/InverterService/CommandExecutor.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
using HidSharp;
using System.Diagnostics;
using System.Text;
using ICommand = InverterMon.Server.InverterService.Commands.ICommand;

namespace InverterMon.Server.InverterService;

internal class CommandExecutor : BackgroundService
{
private readonly CommandQueue queue;
private DeviceStream? dev;
private readonly ILogger<CommandExecutor> log;
private readonly IConfiguration confing;

Expand Down Expand Up @@ -36,36 +33,30 @@ private bool Connect()
{
var devPath = confing["LaunchSettings:DeviceAddress"] ?? "/dev/hidraw0";

dev = DeviceList.Local
.GetDevices(
types: DeviceTypes.Hid | DeviceTypes.Serial,
filter: d => DeviceFilterHelper.MatchHidDevices(d, 0x0665, 0x5161) || DeviceFilterHelper.MatchSerialDevices(d, devPath))
.FirstOrDefault()?.Open();

if (dev is null)
if (!Inverter.Connect(devPath, log))
{
return false;
}
else
{
log.LogInformation("connected to inverter at: [{adr}]", dev.Device.DevicePath);
log.LogInformation("connected to inverter at: [{adr}]", devPath);
return true;
}
}

protected override async Task ExecuteAsync(CancellationToken c)
protected override async Task ExecuteAsync(CancellationToken ct)
{
var delay = 0;
var timeout = TimeSpan.FromMinutes(5);

while (!c.IsCancellationRequested && delay <= timeout.TotalMilliseconds)
while (!ct.IsCancellationRequested && delay <= timeout.TotalMilliseconds)
{
var cmd = queue.GetCommand();
if (cmd is not null)
{
try
{
await ExecuteCommand(cmd, dev!, c);
await ExecuteCommand(cmd, ct);
queue.IsAcceptingCommands = true;
delay = 0;
queue.RemoveCommand();
Expand All @@ -79,54 +70,17 @@ protected override async Task ExecuteAsync(CancellationToken c)
}
else
{
await Task.Delay(500, c);
await Task.Delay(500, ct);
}
}

log.LogError("command execution halted due to excessive failures!");
}

private static async Task ExecuteCommand(ICommand command, Stream port, CancellationToken c)
private static async Task ExecuteCommand(ICommand command, CancellationToken ct)
{
command.Start();
byte[]? cmdBytes = Encoding.ASCII.GetBytes(command.CommandString);
ushort crc = CalculateXmodemCrc16(command.CommandString);

byte[]? buf = new byte[cmdBytes.Length + 3];
Array.Copy(cmdBytes, buf, cmdBytes.Length);
buf[cmdBytes.Length] = (byte)(crc >> 8);
buf[cmdBytes.Length + 1] = (byte)(crc & 0xff);
buf[cmdBytes.Length + 2] = 0x0d;

await port.WriteAsync(buf, c);
byte[]? buffer = new byte[1024];
int pos = 0;
do
{
int readCount = await port.ReadAsync(buffer.AsMemory(pos, buffer.Length - pos), c);
if (readCount > 0)
pos += readCount;
}
while (!buffer.Any(b => b == 0x0d));

command.Parse(Encoding.ASCII.GetString(buffer, 0, pos - 3).Sanitize());
await Inverter.Write(command.CommandString, ct);
command.Parse(await Inverter.Read(ct));
command.End();
}

private static ushort CalculateXmodemCrc16(string data)
{
ushort crc = 0;
for (int i = 0; i < data.Length; i++)
{
crc ^= (ushort)(data[i] << 8);
for (int j = 0; j < 8; j++)
{
if ((crc & 0x8000) != 0)
crc = (ushort)((crc << 1) ^ 0x1021);
else
crc <<= 1;
}
}
return crc;
}
}
132 changes: 132 additions & 0 deletions src/Server/InverterService/Inverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
using System.IO.Ports;
using System.Text;

namespace InverterMon.Server.InverterService;

public static class Inverter
{
private static SerialPort? _serialPort;
private static FileStream? _fileStream;

public static bool Connect(string devicePath, ILogger logger)
{
try
{
if (devicePath.Contains("/hidraw", StringComparison.OrdinalIgnoreCase))
{
_fileStream = new FileStream(devicePath, FileMode.Open, FileAccess.ReadWrite);
return true;
}
else if (devicePath.Contains("/ttyUSB", StringComparison.OrdinalIgnoreCase) || devicePath.Contains("COM", StringComparison.OrdinalIgnoreCase))
{
_serialPort = new SerialPort(devicePath)
{
BaudRate = 2400,
Parity = Parity.None,
DataBits = 8,
StopBits = StopBits.One,
Handshake = Handshake.None
};
_serialPort.Open();
return true;
}
else
{
logger.LogError("device path [{path}] is not acceptable!", devicePath);
}
}
catch (Exception x)
{
logger.LogError("connection error at [{path}]. reason: [{reason}]", devicePath, x.Message);
}
return false;
}

private static readonly byte[] _writeBuffer = new byte[512];
public static Task Write(string command, CancellationToken ct)
{
byte[] cmdBytes = Encoding.ASCII.GetBytes(command);
ushort crc = CalculateXmodemCrc16(command);

Buffer.BlockCopy(cmdBytes, 0, _writeBuffer, 0, cmdBytes.Length);
_writeBuffer[cmdBytes.Length] = (byte)(crc >> 8);
_writeBuffer[cmdBytes.Length + 1] = (byte)(crc & 0xff);
_writeBuffer[cmdBytes.Length + 2] = 0x0d;

if (_fileStream != null)
{
return _fileStream.WriteAsync(_writeBuffer, 0, cmdBytes.Length + 3, ct);
}
else if (_serialPort != null)
{
return _serialPort.BaseStream.WriteAsync(_writeBuffer, 0, cmdBytes.Length + 3, ct);
}
return Task.CompletedTask;
}

private static readonly byte[] _readBuffer = new byte[1024];
public static async Task<string> Read(CancellationToken ct)
{
int pos = 0;
const byte eol = 0x0d;

if (_fileStream != null)
{
do
{
int readCount = await _fileStream.ReadAsync(_readBuffer.AsMemory(pos, _readBuffer.Length - pos), ct);
if (readCount > 0)
{
pos += readCount;
for (int i = pos - readCount; i < pos; i++)
{
if (_readBuffer[i] == eol)
return Encoding.ASCII.GetString(_readBuffer, 0, i - 2).Sanitize();
}
}
}
while (pos < _readBuffer.Length);
}
else if (_serialPort != null)
{
do
{
int readCount = await _serialPort.BaseStream.ReadAsync(_readBuffer.AsMemory(pos, _readBuffer.Length - pos), ct);
if (readCount > 0)
{
pos += readCount;
for (int i = pos - readCount; i < pos; i++)
{
if (_readBuffer[i] == eol)
return Encoding.ASCII.GetString(_readBuffer, 0, i - 2).Sanitize();
}
}
}
while (pos < _readBuffer.Length);
}
else
{
throw new InvalidOperationException("inverter not connected.");
}
throw new InvalidOperationException("buffer overflow.");
}

private static ushort CalculateXmodemCrc16(string data)
{
ushort crc = 0;
int length = data.Length;

for (int i = 0; i < length; i++)
{
crc ^= (ushort)(data[i] << 8);
for (int j = 0; j < 8; j++)
{
if ((crc & 0x8000) != 0)
crc = (ushort)((crc << 1) ^ 0x1021);
else
crc <<= 1;
}
}
return crc;
}
}
9 changes: 5 additions & 4 deletions src/Server/appsettings.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
{
"LaunchSettings": {
"DeviceAddress": "/dev/ttyUSB0",
"JkBmsAddress": "/dev/ttyUSB1",
"DeviceAddress": "/dev/hidraw0",
"JkBmsAddress": "/dev/ttyUSB0",
"WebPort": 80,
"TroubleMode": "no"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.Hosting.Lifetime": "Error"
"Microsoft.AspNetCore": "Error",
"Microsoft.Hosting.Lifetime": "Error",
"FastEndpoints.StartupTimer": "None"
}
}
}
7 changes: 2 additions & 5 deletions src/changelog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
## changelog
- decrease bms timeout
- fix NAK error msg on startup
- disable blazor unhandled exception modal
- remove swagger
- auto reload after 2 minute inactivity (helps with mobile browser suspension)
- rewrite device access logic.
- optimize speed and reduce allocations.

0 comments on commit 6bfb643

Please sign in to comment.