Skip to content

Commit

Permalink
Merge pull request #1817 from pkuehnel/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
pkuehnel authored Feb 11, 2025
2 parents 2499394 + d42f7fe commit 145aee7
Show file tree
Hide file tree
Showing 35 changed files with 798 additions and 124 deletions.
66 changes: 66 additions & 0 deletions PkSoftwareService.Custom.Backend/InMemorySink.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using Serilog.Core;
using Serilog.Events;
using Serilog.Formatting.Display;

namespace PkSoftwareService.Custom.Backend;

public class InMemorySink : ILogEventSink
{
private readonly int _capacity;
private readonly Queue<string> _logMessages;
private readonly object _syncRoot = new object();
private readonly MessageTemplateTextFormatter _formatter;

/// <summary>
/// Creates a new InMemorySink.
/// </summary>
/// <param name="outputTemplate">The output template (should match your Console sink).</param>
/// <param name="formatProvider">Optional format provider.</param>
/// <param name="capacity">Max number of messages to store.</param>
public InMemorySink(string outputTemplate, IFormatProvider? formatProvider = null, int capacity = 20000)
{
_capacity = capacity;
_logMessages = new Queue<string>(capacity);
_formatter = new MessageTemplateTextFormatter(outputTemplate, formatProvider);
}

public void Emit(LogEvent logEvent)
{
// Format the log event into a string.
using var writer = new StringWriter();
_formatter.Format(logEvent, writer);
var message = writer.ToString();

// Ensure thread safety while enqueuing/dequeuing.
lock (_syncRoot)
{
if (_logMessages.Count >= _capacity)
{
_logMessages.Dequeue(); // remove oldest
}
_logMessages.Enqueue(message);
}
}

/// <summary>
/// Returns a snapshot of the current log messages.
/// </summary>
public List<string> GetLogs()
{
lock (_syncRoot)
{
return _logMessages.Select(x => x.Trim()).ToList();
}
}

/// <summary>
/// Optionally clear all logs.
/// </summary>
public void Clear()
{
lock (_syncRoot)
{
_logMessages.Clear();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog" Version="4.0.0" />
</ItemGroup>

</Project>
6 changes: 6 additions & 0 deletions TeslaSolarCharger.sln
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeslaSolarCharger.SharedMod
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TeslaSolarCharger.Services", "TeslaSolarCharger.Services\TeslaSolarCharger.Services.csproj", "{21A8DB64-E449-474E-94DD-360C30D1756A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PkSoftwareService.Custom.Backend", "PkSoftwareService.Custom.Backend\PkSoftwareService.Custom.Backend.csproj", "{B9331EFC-2B31-4447-B6C3-3E898B560946}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -87,6 +89,10 @@ Global
{21A8DB64-E449-474E-94DD-360C30D1756A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{21A8DB64-E449-474E-94DD-360C30D1756A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{21A8DB64-E449-474E-94DD-360C30D1756A}.Release|Any CPU.Build.0 = Release|Any CPU
{B9331EFC-2B31-4447-B6C3-3E898B560946}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B9331EFC-2B31-4447-B6C3-3E898B560946}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B9331EFC-2B31-4447-B6C3-3E898B560946}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B9331EFC-2B31-4447-B6C3-3E898B560946}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
14 changes: 11 additions & 3 deletions TeslaSolarCharger/Client/Pages/BaseConfiguration.razor
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,21 @@ else
</InputComponent>

<InputComponent ValueId="teslaMateDbPort"
LabelText="TeslaMate Database Server Port"
UnitText=""
HelpText="You can use the internal port of the TeslaMate database container">
LabelText="TeslaMate Database Server Port"
UnitText=""
HelpText="You can use the internal port of the TeslaMate database container">
<InputFragment>
<InputNumber id="teslaMateDbPort" @bind-Value="_dtoBaseConfiguration.TeslaMateDbPort" class="form-control" placeholder=" " />
</InputFragment>
</InputComponent>
<InputComponent ValueId="teslaMateDbName"
LabelText="TeslaMate Database Name"
UnitText=""
HelpText="">
<InputFragment>
<InputText id="teslaMateDbName" @bind-Value="_dtoBaseConfiguration.TeslaMateDbDatabaseName" class="form-control" placeholder=" " />
</InputFragment>
</InputComponent>
<InputComponent ValueId="teslaMateDbUser"
LabelText="TeslaMate Database Username"
UnitText=""
Expand Down
41 changes: 36 additions & 5 deletions TeslaSolarCharger/Client/Pages/CarSettings.razor
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
@using TeslaSolarCharger.Client.Wrapper
@using TeslaSolarCharger.Shared.Dtos.Ble
@using Newtonsoft.Json
@using TeslaSolarCharger.Client.Helper.Contracts
@using TeslaSolarCharger.Shared.Enums
@inject HttpClient HttpClient
@inject IHttpClientHelper HttpClientHelper
@inject ISnackbar Snackbar

<PageTitle>Car Settings</PageTitle>
Expand Down Expand Up @@ -68,22 +70,36 @@ else
@if (carBasicConfiguration.Item.UseFleetTelemetry && !carBasicConfiguration.Item.IncludeTrackingRelevantFields)
{
<MudAlert Severity="Severity.Info"
NoIcon="true"
ContentAlignment="HorizontalAlignment.Left">
NoIcon="true"
ContentAlignment="HorizontalAlignment.Left">
<h5>Home Address of Tesla is used to determine if car is at home</h5>
As tracking relevant fields are not included, the home address set in the car is used to determine if the car is at home. This means the Home Geofence set in <MudLink Href="/BaseConfiguration">Base Configuration</MudLink> is not used for this car.
</MudAlert>
}
@if (carBasicConfiguration.Item.UseFleetTelemetry)
{
<GenericInput T="bool"
For="() => carBasicConfiguration.Item.IncludeTrackingRelevantFields"
OnValueChanged="Redraw"/>
For="() => carBasicConfiguration.Item.IncludeTrackingRelevantFields"
OnValueChanged="Redraw" />
}
}

</EditFormComponent>

@if (carBasicConfiguration.Item.ShouldBeManaged && carBasicConfiguration.Item.UseFleetTelemetry)
{
<h3>Fleet Telemetry Config</h3>
@if (_fleetTelemetryConfigs.TryGetValue(carBasicConfiguration.Item.Vin, out var fleetTelemetryConfig))
{
<div>
<h5>Current Fleet Telemetry Config</h5>
<div>@fleetTelemetryConfig</div>
</div>
}
<RightAlignedButtonComponent ButtonText="Get Fleet Telemetry Config"
OnButtonClicked="_ => GetFleetTelemetryConfig(carBasicConfiguration.Item.Vin)"></RightAlignedButtonComponent>
}

@if (_vinsToShowBleTest.Contains(carBasicConfiguration.Item.Vin))
{
<hr />
Expand All @@ -98,7 +114,6 @@ else
<p>
@result
</p>

</div>

}
Expand Down Expand Up @@ -152,6 +167,7 @@ else
private Dictionary<string, string> _pairingResults = new();
private Dictionary<string, DtoBleCommandResult> _bleTestResults = new();
private Dictionary<string, DtoBleCommandResult> _bleWakeUpTestResults = new();
private Dictionary<string, string> _fleetTelemetryConfigs = new();

private HashSet<string> _loadingVins = new();

Expand Down Expand Up @@ -252,4 +268,19 @@ else
}
}

private async Task GetFleetTelemetryConfig(string vin)
{
var result = await HttpClientHelper.SendGetRequestAsync<DtoValue<string>>($"api/Config/GetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}");
string stringToDisplay;
if (result.HasError)
{
stringToDisplay = result.ErrorMessage ?? "No error message";
}
else
{
stringToDisplay = result.Data?.Value ?? "No data";
}
_fleetTelemetryConfigs[vin] = stringToDisplay;
}

}
142 changes: 142 additions & 0 deletions TeslaSolarCharger/Client/Pages/Support.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
@page "/support"
@using TeslaSolarCharger.Client.Helper.Contracts
@using TeslaSolarCharger.Shared.Dtos
@using TeslaSolarCharger.Shared.Dtos.Support

@inject IHttpClientHelper HttpClientHelper
@inject ISnackbar Snackbar
@inject NavigationManager NavigationManager

<h1>Support</h1>

<MudAlert Severity="Severity.Warning"
NoIcon="true"
ContentAlignment="HorizontalAlignment.Left">
<h5>Never share logs publicly</h5>
Logs might contain sensitive information like your vehicle's location. Do not share logs publicly.
</MudAlert>
<h3>General</h3>

<RightAlignedButtonComponent ButtonText="Download Logs"
StartIcon="@Icons.Material.Filled.Download"
OnButtonClicked="@(_ => NavigationManager.NavigateTo("api/Debug/DownloadLogs", true))"></RightAlignedButtonComponent>

<h3>Car Debug Details</h3>
@if (_debugCars == default)
{
<PlaceholderComponent Count="3"></PlaceholderComponent>
}
else
{
<MudExpansionPanels MultiExpansion="true">
@foreach (var car in _debugCars)
{
<MudExpansionPanel Text="@car.Value.Name">
<div>ID: @car.Key</div>
<div>VIN: @car.Value.Vin</div>
<div>Name: @car.Value.Name</div>
<div>Is Available in Tesla account: @car.Value.IsAvailableInTeslaAccount</div>
<div>Should be managed: @car.Value.ShouldBeManaged</div>


@if (car.Value.Vin != default && _fleetTelemetryGetConfigs.TryGetValue(car.Value.Vin, out var config))
{
<h4>Fleet Telemetry Config</h4>
<pre>@config</pre>
}

<RightAlignedButtonComponent ButtonText="Get Fleet Telemetry Config"
IsLoading="@_isFleetTelemetryLoading"
IsDisabled="@(car.Value.Vin == default || !car.Value.IsAvailableInTeslaAccount)"
DisabledToolTipText="@(car.Value.IsAvailableInTeslaAccount ? null : "Can not check config as car is not part of Tesla account")"
OnButtonClicked="@(() => GetFleetTelemetryConfig(car.Value.Vin))"></RightAlignedButtonComponent>

@if (car.Value.Vin != default && _fleetTelemetrySetResults.TryGetValue(car.Value.Vin, out var result))
{
<h4>Fleet Telemetry SetResult</h4>
<pre>@result</pre>
}

<RightAlignedButtonComponent ButtonText="Normal Fleet Configuration Set"
IsLoading="@_isFleetTelemetryLoading"
IsDisabled="@(car.Value.Vin == default || !car.Value.IsAvailableInTeslaAccount)"
DisabledToolTipText="@(car.Value.IsAvailableInTeslaAccount ? null : "Can not set config as car is not part of Tesla account")"
OnButtonClicked="@(() => SetFleetTelemetryConfig(car.Value.Vin, false))"></RightAlignedButtonComponent>
<RightAlignedButtonComponent ButtonText="Force Fleet Configuration Set"
IsLoading="@_isFleetTelemetryLoading"
IsDisabled="@(car.Value.Vin == default || !car.Value.IsAvailableInTeslaAccount)"
DisabledToolTipText="@(car.Value.IsAvailableInTeslaAccount ? null : "Can not set config as car is not part of Tesla account")"
OnButtonClicked="@(() => SetFleetTelemetryConfig(car.Value.Vin, true))"></RightAlignedButtonComponent>
</MudExpansionPanel>
}
</MudExpansionPanels>
}



@code {
private readonly Dictionary<string, string> _fleetTelemetryGetConfigs = new();

private readonly Dictionary<string, string> _fleetTelemetrySetResults = new();

private Dictionary<int, DtoDebugCar>? _debugCars;

private bool _isFleetTelemetryLoading;


protected override async Task OnInitializedAsync()
{
var cars = await HttpClientHelper.SendGetRequestWithSnackbarAsync<Dictionary<int, DtoDebugCar>>("api/Debug/GetCars");
if (cars != default)
{
_debugCars = cars;
}
}


private async Task GetFleetTelemetryConfig(string? vin)
{
if (vin == default)
{
Snackbar.Add("VIN is unknown", Severity.Error);
return;
}

_isFleetTelemetryLoading = true;
var result = await HttpClientHelper.SendGetRequestAsync<DtoValue<string>>($"api/Debug/GetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}");
string stringToDisplay;
if (result.HasError)
{
stringToDisplay = result.ErrorMessage ?? "No error message";
}
else
{
stringToDisplay = result.Data?.Value ?? "No data";
}
_fleetTelemetryGetConfigs[vin] = stringToDisplay;
_isFleetTelemetryLoading = false;
}

private async Task SetFleetTelemetryConfig(string? vin, bool forceReconfiguration)
{
if (vin == default)
{
Snackbar.Add("VIN is unknown", Severity.Error);
return;
}

_isFleetTelemetryLoading = true;
var result = await HttpClientHelper.SendPostRequestAsync<DtoValue<string>>($"api/Debug/SetFleetTelemetryConfiguration?vin={Uri.EscapeDataString(vin)}&forceReconfiguration={forceReconfiguration}", null);
string stringToDisplay;
if (result.HasError)
{
stringToDisplay = result.ErrorMessage ?? "No error message";
}
else
{
stringToDisplay = result.Data?.Value ?? "No data";
}
_fleetTelemetrySetResults[vin] = stringToDisplay;
_isFleetTelemetryLoading = false;
}
}
5 changes: 5 additions & 0 deletions TeslaSolarCharger/Client/Shared/NavMenu.razor
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
<span class="oi oi-cog" aria-hidden="true"></span> Base Configuration
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link px-3" href="support">
<span class="oi oi-code" aria-hidden="true"></span> Support
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link px-3" href="backupAndRestore">
<span class="oi oi-data-transfer-download" aria-hidden="true"></span> Backup and Restore
Expand Down
Loading

0 comments on commit 145aee7

Please sign in to comment.