inverter testing passed
This commit is contained in:
parent
d498e8624a
commit
80caa1c631
@ -62,8 +62,8 @@
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="row h-100 align-content-center">
|
||||
<span class="fs-5 @TemperatureCss()">
|
||||
@(status?.HeatSinkTemperature) C°
|
||||
<span class="fs-6 text-muted">
|
||||
@(status?.WorkingMode)
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -205,16 +205,6 @@
|
||||
private static double RoundToOneDecimal(double? val)
|
||||
=> Math.Round(val ?? 0, 1);
|
||||
|
||||
private static string TemperatureCss()
|
||||
{
|
||||
return status?.HeatSinkTemperature switch
|
||||
{
|
||||
>= 55 and < 65 => "text-danger",
|
||||
>= 65 => "text-danger fw-bolder blinktext",
|
||||
_ => "text-muted"
|
||||
};
|
||||
}
|
||||
|
||||
public static async Task StartStatusStreaming(string basePath)
|
||||
{
|
||||
//note: only reason we have a full-time stream download is because there's a bug in
|
||||
|
||||
@ -375,11 +375,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SetChargeCurrent(Setting settingName, byte value)
|
||||
private async Task SetChargeCurrent(Setting setting, byte value)
|
||||
{
|
||||
isSuccess = false;
|
||||
|
||||
switch (settingName)
|
||||
switch (setting)
|
||||
{
|
||||
case Setting.CombinedChargeCurrent:
|
||||
currentButton = Button.MaxCombinedChargeCurrent;
|
||||
@ -392,10 +392,12 @@
|
||||
break;
|
||||
}
|
||||
|
||||
if (await Http.GetStringAsync($"api/settings/set-setting/{settingName}/{value}") == "true")
|
||||
var xxx = $"api/settings/set-setting/{setting}/{value}";
|
||||
|
||||
if (await Http.GetStringAsync($"api/settings/set-setting/{setting}/{value}") == "true")
|
||||
{
|
||||
isSuccess = true;
|
||||
UpdateLocalSetting(settingName, value);
|
||||
UpdateLocalSetting(setting, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ namespace InverterMon.Server.Endpoints.GetStatus;
|
||||
public class Endpoint : EndpointWithoutRequest<object>
|
||||
{
|
||||
public FelicitySolarInverter Inverter { get; set; } = null!;
|
||||
public IHostApplicationLifetime AppLife { get; set; } = null!;
|
||||
|
||||
public override void Configure()
|
||||
{
|
||||
@ -28,32 +29,32 @@ public class Endpoint : EndpointWithoutRequest<object>
|
||||
|
||||
async IAsyncEnumerable<InverterStatus> GetDataStream([EnumeratorCancellation] CancellationToken c)
|
||||
{
|
||||
while (!c.IsCancellationRequested)
|
||||
while (!c.IsCancellationRequested && !AppLife.ApplicationStopping.IsCancellationRequested)
|
||||
{
|
||||
if (Env.IsDevelopment())
|
||||
{
|
||||
var status = new InverterStatus
|
||||
{
|
||||
OutputVoltage = Random.Shared.Next(240),
|
||||
LoadWatts = Random.Shared.Next(3500),
|
||||
LoadPercentage = Random.Shared.Next(100),
|
||||
BatteryVoltage = Random.Shared.Next(24),
|
||||
BatteryChargeCurrent = Random.Shared.Next(20),
|
||||
BatteryDischargeCurrent = Random.Shared.Next(300),
|
||||
HeatSinkTemperature = Random.Shared.Next(300),
|
||||
PVInputCurrent = Random.Shared.Next(300),
|
||||
PVInputVoltage = Random.Shared.Next(300),
|
||||
PVInputWatt = Random.Shared.Next(1000),
|
||||
PV_MaxCapacity = 1000,
|
||||
BatteryCapacity = 100
|
||||
};
|
||||
|
||||
yield return status;
|
||||
}
|
||||
else
|
||||
// if (Env.IsDevelopment())
|
||||
// {
|
||||
// var status = new InverterStatus
|
||||
// {
|
||||
// OutputVoltage = Random.Shared.Next(240),
|
||||
// LoadWatts = Random.Shared.Next(3500),
|
||||
// LoadPercentage = Random.Shared.Next(100),
|
||||
// BatteryVoltage = Random.Shared.Next(24),
|
||||
// BatteryChargeCurrent = Random.Shared.Next(20),
|
||||
// BatteryDischargeCurrent = Random.Shared.Next(300),
|
||||
// HeatSinkTemperature = Random.Shared.Next(300),
|
||||
// PVInputCurrent = Random.Shared.Next(300),
|
||||
// PVInputVoltage = Random.Shared.Next(300),
|
||||
// PVInputWatt = Random.Shared.Next(1000),
|
||||
// PV_MaxCapacity = 1000,
|
||||
// BatteryCapacity = 100
|
||||
// };
|
||||
//
|
||||
// yield return status;
|
||||
// }
|
||||
// else
|
||||
yield return Inverter.Status;
|
||||
|
||||
await Task.Delay(1000, c);
|
||||
await Task.Delay(2000, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,25 +17,25 @@ public class Endpoint : EndpointWithoutRequest<CurrentSettings>
|
||||
|
||||
public override async Task HandleAsync(CancellationToken c)
|
||||
{
|
||||
if (Env.IsDevelopment())
|
||||
{
|
||||
var res = new CurrentSettings
|
||||
{
|
||||
BackToBatteryVoltage = 48.1,
|
||||
BackToGridVoltage = 48.2,
|
||||
FloatChargeVoltage = 48.3,
|
||||
ChargePriority = ChargePriority.OnlySolar,
|
||||
DischargeCuttOffVoltage = 48.4,
|
||||
BulkChargeVoltage = 48.5,
|
||||
MaxACChargeCurrent = 10,
|
||||
MaxCombinedChargeCurrent = 20,
|
||||
OutputPriority = OutputPriority.SolarFirst,
|
||||
SystemSpec = UserSettings.ToSystemSpec()
|
||||
};
|
||||
await SendAsync(res, cancellation: c);
|
||||
|
||||
return;
|
||||
}
|
||||
// if (Env.IsDevelopment())
|
||||
// {
|
||||
// var res = new CurrentSettings
|
||||
// {
|
||||
// BackToBatteryVoltage = 48.1,
|
||||
// BackToGridVoltage = 48.2,
|
||||
// FloatChargeVoltage = 48.3,
|
||||
// ChargePriority = ChargePriority.OnlySolar,
|
||||
// DischargeCuttOffVoltage = 48.4,
|
||||
// BulkChargeVoltage = 48.5,
|
||||
// MaxACChargeCurrent = 10,
|
||||
// MaxCombinedChargeCurrent = 20,
|
||||
// OutputPriority = OutputPriority.SolarFirst,
|
||||
// SystemSpec = UserSettings.ToSystemSpec()
|
||||
// };
|
||||
// await SendAsync(res, cancellation: c);
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@ -14,16 +14,17 @@ public class Endpoint : Endpoint<Shared.Models.SetSetting, bool>
|
||||
|
||||
public override async Task HandleAsync(Shared.Models.SetSetting r, CancellationToken c)
|
||||
{
|
||||
if (Env.IsDevelopment())
|
||||
{
|
||||
await SendAsync(true, cancellation: c);
|
||||
|
||||
return;
|
||||
}
|
||||
// if (Env.IsDevelopment())
|
||||
// {
|
||||
// await SendAsync(true, cancellation: c);
|
||||
//
|
||||
// return;
|
||||
// }
|
||||
|
||||
try
|
||||
{
|
||||
Inverter.SetSetting(r.Setting, r.Value);
|
||||
await SendAsync(true, cancellation: c);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
@ -57,19 +57,39 @@ public sealed class FelicitySolarInverter
|
||||
{
|
||||
var regs = ReadRegisters(StatusStartAddress, StatusRegisterCount);
|
||||
|
||||
// WorkingMode = regs[0], // 0x1101: Working mode (offset 0)
|
||||
lock (_lock)
|
||||
{
|
||||
if (!_serialPort.IsOpen)
|
||||
return;
|
||||
}
|
||||
|
||||
// BatteryChargingStage = regs[1], // 0x1102: Battery charging stage (offset 1)
|
||||
|
||||
Status.WorkingMode = (WorkingMode)regs[0]; // 0x1101: Working mode (offset 0)
|
||||
Status.BatteryVoltage = regs[7] / 100.0; // 0x1108: Battery voltage (offset 0x1108 - 0x1101 = 7)
|
||||
Status.BatteryDischargeCurrent = regs[8]; // 0x1109: Battery current (offset 8) -- signed value
|
||||
Status.BatteryChargeCurrent = regs[8]; // 0x1109: Battery current (offset 8) -- signed value
|
||||
Status.BatteryDischargeWatts = regs[9]; // 0x110A: Battery power (offset 9) -- signed value
|
||||
Status.BatteryChargeWatts = regs[9]; // 0x110A: Battery power (offset 9) -- signed value
|
||||
|
||||
var disCur = ChargeStatus(regs[8]); // 0x1109: Battery current (offset 8) -- signed value
|
||||
Status.BatteryDischargeCurrent = disCur.IsDischarge ? disCur.PositiveValue : 0;
|
||||
Status.BatteryChargeCurrent = disCur.IsDischarge is false ? disCur.PositiveValue : 0;
|
||||
|
||||
var disPow = ChargeStatus(regs[9]); // 0x110A: Battery power (offset 9) -- signed value
|
||||
Status.BatteryDischargeWatts = disPow.IsDischarge ? disPow.PositiveValue : 0;
|
||||
Status.BatteryChargeWatts = disPow.IsDischarge is false ? disPow.PositiveValue : 0;
|
||||
|
||||
Status.OutputVoltage = regs[16] / 10.0; // 0x1111: AC output voltage (offset 0x1111 - 0x1101 = 16)
|
||||
Status.GridVoltage = regs[22] / 10.0; // 0x1111: AC output voltage (offset 0x1117 - 0x1101 = 22)
|
||||
Status.LoadWatts = regs[29]; // 0x111E: AC output active power (offset 0x111E - 0x1101 = 29)
|
||||
Status.LoadPercentage = regs[31]; // 0x1120: Load percentage (offset 0x1120 - 0x1101 = 31)
|
||||
Status.PVInputVoltage = regs[37] / 10.0; // 0x1126: PV input voltage (offset 0x1126 - 0x1101 = 37)
|
||||
Status.PVInputWatt = regs[41]; // 0x112A: PV input power (offset 0x112A - 0x1101 = 41) -- signed value
|
||||
|
||||
static (bool IsDischarge, short PositiveValue) ChargeStatus(short value)
|
||||
{
|
||||
var isNegative = value < 0;
|
||||
var positiveValue = isNegative ? (short)-value : value;
|
||||
|
||||
return (isNegative, positiveValue);
|
||||
}
|
||||
}
|
||||
|
||||
// The settings registers we need are located between 0x211F and 0x2159.
|
||||
@ -206,6 +226,9 @@ public sealed class FelicitySolarInverter
|
||||
|
||||
var response = SendModbusRequest(frame);
|
||||
|
||||
if (response.Length == 0)
|
||||
return [];
|
||||
|
||||
// Expected response structure:
|
||||
// [Slave Address][Function Code][Byte Count][Data...][CRC Lo][CRC Hi]
|
||||
|
||||
@ -228,6 +251,9 @@ public sealed class FelicitySolarInverter
|
||||
{
|
||||
lock (_lock) //prevent concurrent access
|
||||
{
|
||||
if (!_serialPort.IsOpen)
|
||||
return [];
|
||||
|
||||
_serialPort.DiscardInBuffer();
|
||||
_serialPort.DiscardOutBuffer();
|
||||
|
||||
|
||||
@ -22,9 +22,11 @@ class StatusRetriever(
|
||||
await Task.Delay(5000);
|
||||
}
|
||||
|
||||
log.LogInformation("Connected to inverter at device address: [{port}]", port);
|
||||
|
||||
appLife.ApplicationStopping.Register(inverter.Close);
|
||||
|
||||
while (!c.IsCancellationRequested)
|
||||
while (!c.IsCancellationRequested && !appLife.ApplicationStopping.IsCancellationRequested)
|
||||
{
|
||||
inverter.Status.BatteryCapacity = userSettings.BatteryCapacity;
|
||||
inverter.Status.PV_MaxCapacity = userSettings.PV_MaxCapacity;
|
||||
@ -41,9 +43,16 @@ class StatusRetriever(
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
db.UpdateTodaysPvGeneration(c);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//do nothing
|
||||
}
|
||||
|
||||
await Task.Delay(2000);
|
||||
await Task.Delay(2000, CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,7 @@ public class UserSettings
|
||||
public int Id { get; set; } = 1;
|
||||
public int PV_MaxCapacity { get; set; } = 1000;
|
||||
public int BatteryCapacity { get; set; } = 100;
|
||||
public float BatteryNominalVoltage { get; set; } = 25.6f;
|
||||
public double BatteryNominalVoltage { get; set; } = 25.6f;
|
||||
public int SunlightStartHour { get; set; } = 6;
|
||||
public int SunlightEndHour { get; set; } = 18;
|
||||
public int[] PVGraphRange => new[] { 0, (SunlightEndHour - SunlightStartHour) * 60 };
|
||||
|
||||
@ -23,11 +23,12 @@ bld.Services
|
||||
.AddSingleton<FelicitySolarInverter>()
|
||||
.AddSingleton<JkBms>();
|
||||
|
||||
if (!bld.Environment.IsDevelopment())
|
||||
{
|
||||
bld.Services
|
||||
// if (!bld.Environment.IsDevelopment())
|
||||
// {
|
||||
bld.Services
|
||||
.AddHostedService<StatusRetriever>();
|
||||
}
|
||||
|
||||
// }
|
||||
|
||||
bld.Services.AddFastEndpoints(o => o.SourceGeneratorDiscoveredTypes = DiscoveredTypes.All);
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
||||
"applicationUrl": "http://localhost:80;https://localhost:443",
|
||||
"applicationUrl": "http://localhost:80",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
||||
@ -1,15 +1,12 @@
|
||||
{
|
||||
"LaunchSettings": {
|
||||
"DeviceAddress": "/dev/ttyUSB1",
|
||||
"DeviceAddress": "COM3",
|
||||
"JkBmsAddress": "/dev/ttyUSB0",
|
||||
"WebPort": 80
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Debug",
|
||||
"Microsoft.Hosting.Lifetime": "Debug",
|
||||
"FastEndpoints.StartupTimer": "Debug"
|
||||
"Default": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -65,7 +65,7 @@ public class BMSStatus
|
||||
public double AvgPowerWatts => Math.Round(AvgCurrentAmps * PackVoltage, 0, MidpointRounding.AwayFromZero);
|
||||
|
||||
[JsonPropertyName("u")]
|
||||
public float PackNominalVoltage { get; set; }
|
||||
public double PackNominalVoltage { get; set; }
|
||||
|
||||
public string GetTimeString()
|
||||
{
|
||||
|
||||
@ -12,3 +12,14 @@ public enum Setting
|
||||
BackToGrid = 8,
|
||||
BackToBattery = 9
|
||||
}
|
||||
|
||||
public enum WorkingMode
|
||||
{
|
||||
POWER = 0,
|
||||
STANDBY = 1,
|
||||
BYPASS = 2,
|
||||
BATTERY = 3,
|
||||
FAULT = 4,
|
||||
LINE = 5,
|
||||
CHARGING = 6
|
||||
}
|
||||
@ -38,7 +38,7 @@ public class InverterStatus
|
||||
public double GridVoltage { get; set; }
|
||||
|
||||
[JsonPropertyName("l")]
|
||||
public int HeatSinkTemperature { get; set; }
|
||||
public WorkingMode WorkingMode { get; set; }
|
||||
|
||||
[JsonPropertyName("m")]
|
||||
public double LoadCurrent => LoadWatts == 0 ? 0 : LoadWatts / OutputVoltage;
|
||||
@ -69,7 +69,7 @@ public class InverterStatus
|
||||
|
||||
pvInputWatt = value;
|
||||
var interval = (DateTime.Now - pvInputWattHourLastComputed).TotalSeconds;
|
||||
PVInputWattHour += value / (3600 / Convert.ToDouble(interval));
|
||||
PVInputWattHour += value / (3600 / interval);
|
||||
pvInputWattHourLastComputed = DateTime.Now;
|
||||
}
|
||||
}
|
||||
@ -81,7 +81,7 @@ public class InverterStatus
|
||||
public int PV_MaxCapacity { get; set; }
|
||||
|
||||
[JsonPropertyName("v")]
|
||||
public int PVPotential => PVInputWatt > 0 ? Convert.ToInt32(Convert.ToDouble(PVInputWatt) / PV_MaxCapacity * 100) : 0;
|
||||
public int PVPotential => PVInputWatt > 0 ? Convert.ToInt32(PVInputWatt / PV_MaxCapacity * 100) : 0;
|
||||
|
||||
int pvInputWatt;
|
||||
DateTime pvInputWattHourLastComputed;
|
||||
|
||||
@ -4,7 +4,7 @@ public class SystemSpec
|
||||
{
|
||||
public int PV_MaxCapacity { get; set; } = 1000;
|
||||
public int BatteryCapacity { get; set; } = 100;
|
||||
public float BatteryNominalVoltage { get; set; } = 25.6f;
|
||||
public double BatteryNominalVoltage { get; set; } = 25.6f;
|
||||
public int SunlightStartHour { get; set; } = 6;
|
||||
public int SunlightEndHour { get; set; } = 18;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user