Skip to content

Commit

Permalink
feature/nits (#3)
Browse files Browse the repository at this point in the history
* add serilog to spell checker
* correct spelling
* combine writes
* update deps
* log version on startup
* exclude .env
* add versioning tool
* add publish script
  • Loading branch information
melgish authored Jul 20, 2024
1 parent 7e7c0e2 commit 8222fc4
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 94 deletions.
12 changes: 12 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-version-cli": {
"version": "3.0.3",
"commands": [
"dotnet-version"
]
}
}
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -488,4 +488,5 @@ array_layout_x.json
appsettings.local.json
appsettings.docker.json
compose.override.yaml
.mono
.mono
.env
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"Anar",
"appsettings",
"Arien",
"Enphase"
"Enphase",
"Serilog"
]
}
16 changes: 11 additions & 5 deletions Anar.Tests/Anar.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,18 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" Version="8.4.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" Version="8.7.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
7 changes: 4 additions & 3 deletions Anar/Anar.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,17 @@
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>dotnet-Anar-e3577f76-ab06-4227-8831-6d6e7e2c7374</UserSecretsId>
<Version>1.0.0</Version>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="InfluxDB.Client" Version="4.14.0" />
<PackageReference Include="InfluxDB.Client" Version="4.16.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Options.DataAnnotations" Version="8.0.0" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="8.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
<PackageReference Include="Serilog.Settings.Configuration" Version="8.0.2" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
</ItemGroup>


Expand Down
45 changes: 27 additions & 18 deletions Anar/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Reflection;

using Anar;
using Anar.Extensions;
using Anar.Services;
Expand All @@ -10,27 +12,34 @@
.WriteTo.Console()
.CreateBootstrapLogger();

try {

var builder = Host.CreateApplicationBuilder(args);
builder.Configuration.AddDockerConfiguration();
try
{
var name = Assembly.GetExecutingAssembly().GetName();
Log.Information("{AssemblyName} v{Version}", name.Name, name.Version);

// Now that configuration is augmented add the real logger
// using appsettings and services.
builder.Services.AddSerilog((services, cfg) => cfg
.ReadFrom.Configuration(builder.Configuration)
.ReadFrom.Services(services)
);
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration.AddDockerConfiguration();

builder.Services.AddGatewayClient();
builder.Services.AddInfluxDB();
builder.Services.AddHostedService<Worker>();
// Now that configuration is augmented add the real logger
// using appsettings and services.
builder.Services.AddSerilog((services, cfg) => cfg
.ReadFrom.Configuration(builder.Configuration)
.ReadFrom.Services(services)
);

var host = builder.Build();
host.Run();
builder.Services.AddGatewayClient();
builder.Services.AddInfluxDB();
builder.Services.AddHostedService<Worker>();

} catch (Exception ex) {
Log.Fatal(ex, "Host terminated unexpectedly");
} finally {
Log.CloseAndFlush();
var host = builder.Build();
host.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly");
}
finally
{
Log.CloseAndFlush();
}
12 changes: 1 addition & 11 deletions Anar/Services/Influx/IInfluxService.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,8 @@
using InfluxDB.Client.Writes;

namespace Anar.Services;

internal interface IInfluxService {
/// <summary>
/// Sums lastReportWatts for all inverters in the collection and writes
/// total based on lastReportDate of the most recent inverter report..
/// </summary>
/// <param name="inverters">Collection of inverters to total</param>
/// <param name="cancellationToken"></param>
Task WriteTotalsAsync(IEnumerable<Inverter> inverters, CancellationToken cancellationToken = default);

/// <summary>
/// Writes values for each inverter
/// Writes values for each inverter, as well as totals
/// </summary>
/// <param name="inverters">Collection of inverters to write</param>
/// <param name="cancellationToken"></param>
Expand Down
68 changes: 20 additions & 48 deletions Anar/Services/Influx/InfluxService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,84 +6,55 @@

namespace Anar.Services;

internal sealed class InfluxService : IInfluxService {
internal sealed class InfluxService : IInfluxService
{
private readonly ILogger<InfluxService> _logger;
private readonly InfluxOptions _options;
public InfluxService(
ILogger<InfluxService> logger,
IOptions<InfluxOptions> options
) {
)
{
_logger = logger;
_options = options.Value;
}

public async Task WriteTotalsAsync(IEnumerable<Inverter> inverters, CancellationToken cancellationToken = default) {
// https://enphase.com/download/accessing-iq-gateway-local-apis-or-local-ui-token-based-authentication
// Early on, it was possible to read totals from an endpoint.
// /api/v1/production. At some point my system firmware was upgraded and
// that endpoint only returns zeros. The alternative endpoint
// ivp/pdm/energy does not work either. That one returns a 401.


public async Task WriteInvertersAsync(IEnumerable<Inverter> inverters, CancellationToken cancellationToken = default)
{
// Do not log empty collections.
if (!inverters.Any())
{
_logger.LogDebug("No totals to write");
_logger.LogDebug("No inverters to write");
return;
}

// https://enphase.com/download/accessing-iq-gateway-local-apis-or-local-ui-token-based-authentication
// Early on, it was possible to read totals from an endpoint.
// /api/v1/production. At some point my system firmware was upgraded and
// that endpoint only returns zeros. The alternative endpoint
// ivp/pdm/energy does not work either. That one returns a 401.

// So instead just add up all the inverter values.

// Original timestamp on the inverter to eliminate duplicates.
// Total will be logged using most recent report.
var points = inverters.Select(i => i.ToPointData()).ToArray();
// Summing watts for current output.
// Use most-recent inverter report to set time on total.
var wattsNow = inverters.Sum(i => i.LastReportWatts);
var now = inverters.Max(i => i.LastReportDate);
_logger.LogInformation("CurrentOutput {Timestamp} {WattsNow}", now, wattsNow);

try {
using var client = new InfluxDBClient(_options.Uri.ToString(), _options.Token);
var api = client.GetWriteApiAsync();
await api.WritePointAsync(PointData
.Measurement("totals")
.Field("wattsNow", wattsNow)
.Timestamp(now, WritePrecision.S),
_options.Bucket,
_options.Organization,
cancellationToken
);
}
catch (Exception ex)
try
{
_logger.LogWarning(ex, "Failed to write totals data");
}
}

public async Task WriteInvertersAsync(IEnumerable<Inverter> inverters, CancellationToken cancellationToken = default) {
// Do not log empty collections.
if (!inverters.Any()) {
_logger.LogDebug("No inverters to write");
return;
}
try {
using var client = new InfluxDBClient(_options.Uri.ToString(), _options.Token);
var api = client.GetWriteApiAsync();

// Original timestamp on the inverter to eliminate duplicates.
// Total will be logged using most recent report.
var points = inverters.Select(i => i.ToPointData()).ToArray();

await api.WritePointsAsync(
points,
_options.Bucket,
_options.Organization,
cancellationToken
);

// Summing watts for current output.
// Use most-recent inverter report to set time on total.
var wattsNow = inverters.Sum(i => i.LastReportWatts);
var now = inverters.Max(i => i.LastReportDate);
_logger.LogInformation("CurrentOutput {Timestamp} {WattsNow}", now, wattsNow);

await api.WritePointAsync(PointData
.Measurement("totals")
.Field("wattsNow", wattsNow)
Expand All @@ -93,7 +64,8 @@ await api.WritePointAsync(PointData
cancellationToken
);
}
catch (Exception ex) {
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to write inverter data");
}
}
Expand Down
13 changes: 8 additions & 5 deletions Anar/Worker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,30 @@ IInfluxService influxService
_influxService = influxService;
}

private async Task ProcessData(CancellationToken stoppingToken) {
private async Task ProcessData(CancellationToken stoppingToken)
{
_logger.LogDebug("Processing");
var inverters = await _gatewayClient.GetInvertersAsync(stoppingToken);
await _influxService.WriteInvertersAsync(inverters, stoppingToken);
await _influxService.WriteTotalsAsync(inverters, stoppingToken);
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
try {
try
{
_logger.LogInformation("Starting polling");
while (!stoppingToken.IsCancellationRequested)
{
await ProcessData(stoppingToken);
await _timer.WaitForNextTickAsync(stoppingToken);
}
}
catch (OperationCanceledException) {
catch (OperationCanceledException)
{
// These are expected on CTRL-C shutdown
}
catch (Exception ex) {
catch (Exception ex)
{
_logger.LogError(ex, "Worker error");
}
}
Expand Down
2 changes: 1 addition & 1 deletion QUERIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ The queries below can be used in the InfluxDB Gui or Grafana for building
dashboards.

## Current Output (Now)
This used to be available via `/api/v1/production`. It stopped working on my system after a firmeware upgrade. You may have better luck.
This used to be available via `/api/v1/production`. It stopped working on my system after a firmware upgrade. You may have better luck.

```javascript
from(bucket: "solar")
Expand Down
2 changes: 1 addition & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ services:
dockerfile: ./Dockerfile
container_name: "anar"
hostname: "anar"
image: "127.0.0.1/library/anar:latest"
image: "anar"
25 changes: 25 additions & 0 deletions publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

export $(grep -v '^#' .env | xargs)

PROJECT_FILE="Anar/Anar.csproj"
VERSION=$(grep -oPm1 "(?<=Version>)[^<]+" "${PROJECT_FILE}" | xargs)
if [ -z "${VERSION}" ]; then
echo "Unable to determine application version."
exit 1
fi

LATEST_NAME="anar:latest"
IMAGE_NAME="anar:${VERSION}"

echo "Build ${IMAGE_NAME}"
docker build -t "${LATEST_NAME}" -f "Anar/Dockerfile" "Anar"

if [ -z "${APP_REGISTRY}" ]; then
echo "Registry has not been set. Image will not be published."
else
docker tag "${LATEST_NAME}" "${APP_REGISTRY}/${IMAGE_NAME}"
docker tag "${LATEST_NAME}" "${APP_REGISTRY}/${LATEST_NAME}"
docker push "${APP_REGISTRY}/${IMAGE_NAME}"
docker push "${APP_REGISTRY}/${LATEST_NAME}"
fi

0 comments on commit 8222fc4

Please sign in to comment.