From fb3a7bdc11cc998d223d31686f0b9d72903f105b Mon Sep 17 00:00:00 2001 From: Valentin Saugnier Date: Wed, 27 Mar 2024 20:27:54 +0100 Subject: [PATCH] Changes --- Monitor/Monitor/Components/Stat.razor | 83 ++++++++++ Monitor/Monitor/Controllers/DevController.cs | 6 +- .../Monitor/Extensions/DateTimeExtensions.cs | 7 +- .../Monitor/Extensions/StringExtensions.cs | 14 ++ Monitor/Monitor/MainLayout.razor | 76 +++++++-- .../Monitor/Models/ImageMagickParameters.cs | 2 +- Monitor/Monitor/Models/MonitorState.cs | 5 - Monitor/Monitor/Models/MqttEntity.cs | 6 +- .../Models/SerialMessages/McuSystemData.cs | 11 ++ .../Monitor/Models/SerialMessages/Message.cs | 1 - .../Monitor/Models/SerialMessages/TimeData.cs | 17 -- Monitor/Monitor/Models/StatsView.cs | 43 ++++++ Monitor/Monitor/Monitor.csproj | 6 +- Monitor/Monitor/MqttConnect.cs | 11 ++ Monitor/Monitor/Pages/History.razor | 145 ++++++++++++++++-- Monitor/Monitor/Pages/Index.razor | 100 ++++++------ Monitor/Monitor/Pages/Settings.razor | 8 + Monitor/Monitor/Program.cs | 11 +- Monitor/Monitor/Services/CameraService.cs | 3 +- .../Services/EntitiesManagerService.cs | 4 +- Monitor/Monitor/Services/MonitorService.cs | 17 +- .../Monitor/Services/SerialMessageService.cs | 6 +- Monitor/Monitor/Workers/AWorker.cs | 2 +- Monitor/Monitor/Workers/AprsIsApp.cs | 4 +- .../Workers/SerialPortMcuCommandsApp.cs | 11 +- Monitor/Monitor/appsettings.Development.json | 2 +- cubecell_monitoring/include/Command.h | 2 + cubecell_monitoring/include/Config.h | 4 +- cubecell_monitoring/include/MpptMonitor.h | 2 +- cubecell_monitoring/include/System.h | 4 +- cubecell_monitoring/include/WeatherSensors.h | 2 +- cubecell_monitoring/lib/Aprs/Aprs.cpp | 16 +- cubecell_monitoring/lib/Aprs/Aprs.h | 3 +- cubecell_monitoring/lib/Aprs/test/test.cpp | 10 +- cubecell_monitoring/src/Command.cpp | 6 + cubecell_monitoring/src/Communication.cpp | 4 +- cubecell_monitoring/src/MpptMonitor.cpp | 4 +- cubecell_monitoring/src/System.cpp | 43 +++--- cubecell_monitoring/src/WeatherSensors.cpp | 4 +- infos.odt | Bin 23877 -> 0 bytes scripts/rsync.sh | 1 - scripts/serial_message_file.sh | 29 ++++ 42 files changed, 541 insertions(+), 194 deletions(-) create mode 100644 Monitor/Monitor/Components/Stat.razor delete mode 100644 Monitor/Monitor/Models/SerialMessages/TimeData.cs create mode 100644 Monitor/Monitor/Models/StatsView.cs create mode 100644 Monitor/Monitor/MqttConnect.cs delete mode 100644 infos.odt create mode 100644 scripts/serial_message_file.sh diff --git a/Monitor/Monitor/Components/Stat.razor b/Monitor/Monitor/Components/Stat.razor new file mode 100644 index 0000000..218a471 --- /dev/null +++ b/Monitor/Monitor/Components/Stat.razor @@ -0,0 +1,83 @@ +@using Monitor.Models +@using System.Globalization + + + + + @if (ShowMin) + { + + + + } + + + + + + + + + @if (ShowMin) + { + + + + } + + + + + + + + + @if (ShowMin) + { + + + + } + + + + + + + + + + +@code { + [Parameter] + public required StatsView Data { get; set; } + + [Parameter] + public required string Title { get; set; } + + [Parameter] + public string? Suffix { get; set; } + + [Parameter] + public bool ShowMin { get; set; } = true; + + [Parameter] + public Type Type { get; set; } = typeof(double); + + private int SizeMd => ShowMin ? 8 : 12; + + private string GetValue(double value) + { + if (Type == typeof(DateTime)) + { + return DateTimeOffset.FromUnixTimeSeconds((long)value).ToString("dd/MM HH:mm:ss", CultureInfo.CurrentCulture); + } + + if (Type == typeof(TimeSpan)) + { + return TimeSpan.FromSeconds(value).ToString(); + } + + return value.ToString(CultureInfo.CurrentCulture); + } +} \ No newline at end of file diff --git a/Monitor/Monitor/Controllers/DevController.cs b/Monitor/Monitor/Controllers/DevController.cs index b925e76..b198f95 100644 --- a/Monitor/Monitor/Controllers/DevController.cs +++ b/Monitor/Monitor/Controllers/DevController.cs @@ -1,7 +1,9 @@ using System.Globalization; +using AprsSharp.AprsParser; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Monitor.Context; +using Monitor.Extensions; using Monitor.Services; namespace Monitor.Controllers; @@ -17,9 +19,9 @@ public class DevController( private readonly DataContext _context = contextFactory.CreateDbContext(); [HttpGet("test")] - public object? Test() + public void Test() { - return default; + Console.WriteLine("F4HVV-15>F4HVV,WIDE1-1:!/7V,.Ot#4I!!G Solar NPR+Lora digi+Cam |!!!i!\"!>!W!!|".ToAprsPacket().Sender); } [HttpGet("reset_database")] diff --git a/Monitor/Monitor/Extensions/DateTimeExtensions.cs b/Monitor/Monitor/Extensions/DateTimeExtensions.cs index ce7a97b..806af80 100644 --- a/Monitor/Monitor/Extensions/DateTimeExtensions.cs +++ b/Monitor/Monitor/Extensions/DateTimeExtensions.cs @@ -6,5 +6,10 @@ public static DateTime ToFrench(this DateTime dateTime) { var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Paris"); return TimeZoneInfo.ConvertTimeFromUtc(dateTime, timeZone); - } + } + + public static long ToUnixTimestamp(this DateTime dateTime) + { + return (long)dateTime.ToUniversalTime().Subtract(new DateTime(1970, 1, 1)).TotalSeconds; + } } \ No newline at end of file diff --git a/Monitor/Monitor/Extensions/StringExtensions.cs b/Monitor/Monitor/Extensions/StringExtensions.cs index 0cd74da..29938b5 100644 --- a/Monitor/Monitor/Extensions/StringExtensions.cs +++ b/Monitor/Monitor/Extensions/StringExtensions.cs @@ -1,3 +1,5 @@ +using AprsSharp.AprsParser; + namespace Monitor.Extensions; public static class StringExtensions @@ -51,4 +53,16 @@ public static long ToLong(this string? value, long? defaultValue = null) return valueLong; } + + public static Packet ToAprsPacket(this string value) + { + try + { + return new Packet(value); + } + catch (Exception) + { + return new Packet($"{value.Split(":")[0]}: "); + } + } } \ No newline at end of file diff --git a/Monitor/Monitor/MainLayout.razor b/Monitor/Monitor/MainLayout.razor index d8f3706..da8dac2 100644 --- a/Monitor/Monitor/MainLayout.razor +++ b/Monitor/Monitor/MainLayout.razor @@ -1,4 +1,10 @@ -@inherits Microsoft.AspNetCore.Components.LayoutComponentBase +@using Microsoft.AspNetCore.WebUtilities +@using Monitor.Extensions +@using Monitor.Services +@using System.Reactive.Linq +@inherits Microsoft.AspNetCore.Components.LayoutComponentBase +@inject NavigationManager NavManager +@inject INotificationService NotificationService
@@ -6,15 +12,18 @@ Tableau de bord Historique - - - Paramètres - - - État JSON - Caméras - - + @if (_isAdmin) + { + + + Paramètres + + + État JSON + Caméras + + + }
@@ -25,4 +34,49 @@ -
\ No newline at end of file + + +@code +{ + private bool _isAdmin; + + protected override void OnInitialized() + { + base.OnInitialized(); + + var uri = NavManager.ToAbsoluteUri(NavManager.Uri); + _isAdmin = QueryHelpers.ParseQuery(uri.Query).ContainsKey("admin"); + + EntitiesManagerService.Entities.GpioWifi.ValueChanges() + .Merge(EntitiesManagerService.Entities.GpioNpr.ValueChanges()) + .Merge(EntitiesManagerService.Entities.StatusBoxOpened.ValueChanges()) + .Merge(EntitiesManagerService.Entities.FeatureSleepEnabled.ValueChanges()) + .Merge(EntitiesManagerService.Entities.MpptPowerEnabled.ValueChanges()) + .Merge(EntitiesManagerService.Entities.FeatureAprsDigipeaterEnabled.ValueChanges()) + .Merge(EntitiesManagerService.Entities.FeatureSleepEnabled.ValueChanges()) + .Merge(EntitiesManagerService.Entities.FeatureAprsPositionEnabled.ValueChanges()) + .Merge(EntitiesManagerService.Entities.FeatureAprsTelemetryEnabled.ValueChanges()) + .Merge(EntitiesManagerService.Entities.FeatureWatchdogSafetyEnabled.ValueChanges()) + .Merge(EntitiesManagerService.Entities.WatchdogEnabled.ValueChanges()) + .SubscribeAsync(Notify); + + EntitiesManagerService.Entities.McuStatus.ValueChanges() + .Merge(EntitiesManagerService.Entities.MpptStatus.ValueChanges()) + .SubscribeAsync(Notify); + + EntitiesManagerService.Entities.WatchdogPowerOffTime.ValueChanges() + .Merge(EntitiesManagerService.Entities.WatchdogCounter.ValueChanges() + .Where(v => v.value - v.old >= TimeSpan.FromSeconds(15)) + ).SubscribeAsync(Notify); + } + + private async Task Notify((T? old, T? value, string id) val) + { + await NotificationService.Info(new NotificationConfig + { + Key = val.id, + Message = $"Changement d'état pour {val.id.ToUpper().Replace("/", " ")}", + Description = $"Passage de {(val.old != null ? val.old : "-")} à {val.value}.", + }); + } +} diff --git a/Monitor/Monitor/Models/ImageMagickParameters.cs b/Monitor/Monitor/Models/ImageMagickParameters.cs index bf1ed2f..1048413 100644 --- a/Monitor/Monitor/Models/ImageMagickParameters.cs +++ b/Monitor/Monitor/Models/ImageMagickParameters.cs @@ -2,7 +2,7 @@ namespace Monitor.Models; public class ImageMagickParameters { - private readonly List _commands = new(); + private readonly List _commands = []; private int? _translateX = 0; private int? _translateY = 0; diff --git a/Monitor/Monitor/Models/MonitorState.cs b/Monitor/Monitor/Models/MonitorState.cs index 5501147..6af6b51 100644 --- a/Monitor/Monitor/Models/MonitorState.cs +++ b/Monitor/Monitor/Models/MonitorState.cs @@ -14,11 +14,6 @@ public class MonitorState Type = "weather" }; - public TimeData Time { get; set; } = new() - { - Type = "time" - }; - public McuSystemData McuSystem { get; set; } = new() { State = "no data", diff --git a/Monitor/Monitor/Models/MqttEntity.cs b/Monitor/Monitor/Models/MqttEntity.cs index 101e1b8..00babeb 100644 --- a/Monitor/Monitor/Models/MqttEntity.cs +++ b/Monitor/Monitor/Models/MqttEntity.cs @@ -44,9 +44,11 @@ public ConfigEntity(string id, bool retain = false, T? initialValue = default, b SetValue(initialValue); } - public IObservable<(T? old, T? value)> ValueChanges(bool onlyIfDifferent = true) + public IObservable<(T? old, T? value, string id)> ValueChanges(bool onlyIfDifferent = true) { - return _valueSubject.AsObservable().Where(v => !onlyIfDifferent || v.old?.Equals(v.value) != true); + return _valueSubject.AsObservable() + .Where(v => !onlyIfDifferent || v.old?.Equals(v.value) != true) + .Select(v => (v.old, v.value, Id)); } public IObservable ValueStringAsync() diff --git a/Monitor/Monitor/Models/SerialMessages/McuSystemData.cs b/Monitor/Monitor/Models/SerialMessages/McuSystemData.cs index 03e128e..6f55c66 100644 --- a/Monitor/Monitor/Models/SerialMessages/McuSystemData.cs +++ b/Monitor/Monitor/Models/SerialMessages/McuSystemData.cs @@ -9,6 +9,12 @@ public class McuSystemData : Message [JsonPropertyName("boxOpened")] public bool BoxOpened { get; set; } + + [JsonPropertyName("time")] + public long Timestamp { get; set; } + + [JsonPropertyName("uptime")] + public long Uptime { get; set; } [JsonPropertyName("watchdogSafety")] public bool WatchdogSafetyEnabled { get; set; } @@ -30,4 +36,9 @@ public class McuSystemData : Message [JsonIgnore] public bool IsAlert => State == "alert"; + + [JsonIgnore] + public DateTimeOffset DateTime => DateTimeOffset.FromUnixTimeSeconds(Timestamp); + [JsonIgnore] + public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime); } \ No newline at end of file diff --git a/Monitor/Monitor/Models/SerialMessages/Message.cs b/Monitor/Monitor/Models/SerialMessages/Message.cs index 470b4f7..7a39277 100644 --- a/Monitor/Monitor/Models/SerialMessages/Message.cs +++ b/Monitor/Monitor/Models/SerialMessages/Message.cs @@ -7,7 +7,6 @@ namespace Monitor.Models.SerialMessages; [JsonDerivedType(typeof(LoraData))] [JsonDerivedType(typeof(McuSystemData))] [JsonDerivedType(typeof(MpptData))] -[JsonDerivedType(typeof(TimeData))] [JsonDerivedType(typeof(WeatherData))] public class Message { diff --git a/Monitor/Monitor/Models/SerialMessages/TimeData.cs b/Monitor/Monitor/Models/SerialMessages/TimeData.cs deleted file mode 100644 index 24d1ced..0000000 --- a/Monitor/Monitor/Models/SerialMessages/TimeData.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Monitor.Models.SerialMessages; - -public class TimeData : Message -{ - [JsonPropertyName("state")] - public long Timestamp { get; set; } - - [JsonPropertyName("uptime")] - public long Uptime { get; set; } - - [JsonIgnore] - public DateTimeOffset DateTime => DateTimeOffset.FromUnixTimeSeconds(Timestamp); - [JsonIgnore] - public TimeSpan UptimeTimeSpan => TimeSpan.FromSeconds(Uptime); -} \ No newline at end of file diff --git a/Monitor/Monitor/Models/StatsView.cs b/Monitor/Monitor/Models/StatsView.cs new file mode 100644 index 0000000..8612883 --- /dev/null +++ b/Monitor/Monitor/Models/StatsView.cs @@ -0,0 +1,43 @@ +namespace Monitor.Models; + +public class StatsView +{ + public class UnitData + { + public DateTime DateTime { get; set; } + + public double Value { get; set; } + } + + public class Data + { + public double Average { get; set; } + public double Min { get; set; } + public double Max { get; set; } + + public void FromData(IQueryable data) + { + if (data.Any()) + { + Average = Math.Round(data.Average(d => d.Value), 2); + Min = Math.Round(data.Min(d => d.Value), 2); + Max = Math.Round(data.Max(d => d.Value), 2); + } + } + } + + public Data Total { get; set; } = new(); + public Data TotalToday { get; set; } = new(); + public Data TotalYesterday { get; set; } = new(); + + public void FromData(IQueryable data) + { + Total.FromData(data); + + var today = DateTime.Today; + var yesterday = DateTime.Today.AddDays(-1); + + TotalToday.FromData(data.Where(d => d.DateTime.Date == today)); + TotalYesterday.FromData(data.Where(d => d.DateTime.Date == yesterday)); + } +} \ No newline at end of file diff --git a/Monitor/Monitor/Monitor.csproj b/Monitor/Monitor/Monitor.csproj index 7ba7376..5839534 100644 --- a/Monitor/Monitor/Monitor.csproj +++ b/Monitor/Monitor/Monitor.csproj @@ -41,15 +41,15 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/Monitor/Monitor/MqttConnect.cs b/Monitor/Monitor/MqttConnect.cs new file mode 100644 index 0000000..18faa4a --- /dev/null +++ b/Monitor/Monitor/MqttConnect.cs @@ -0,0 +1,11 @@ +using Monitor.Services; + +namespace Monitor; + +public class MqttConnect(EntitiesManagerService entitiesManagerService) : BackgroundService +{ + protected override async Task ExecuteAsync(CancellationToken cancellationToken) + { + await entitiesManagerService.ConnectMqtt(); + } +} \ No newline at end of file diff --git a/Monitor/Monitor/Pages/History.razor b/Monitor/Monitor/Pages/History.razor index ee2a08a..e4f9513 100644 --- a/Monitor/Monitor/Pages/History.razor +++ b/Monitor/Monitor/Pages/History.razor @@ -1,11 +1,11 @@ @page "/History" -@using Microsoft.EntityFrameworkCore -@using Monitor.Context -@using Monitor.Context.Entities @using Monitor.Models @using System.Globalization +@using Monitor.Context +@using Microsoft.EntityFrameworkCore @using Monitor.Extensions +@using Monitor.Components @inject IDbContextFactory DbContextFactory @@ -14,15 +14,65 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @code { + private readonly StatsView _uptime = new(); + private readonly StatsView _wakeup = new(); + private readonly StatsView _dozeOff = new(); + private readonly StatsView _voltageBattery = new(); + private readonly StatsView _currentBattery = new(); + private readonly StatsView _currentSolar = new(); + private readonly StatsView _loraRx = new(); + private readonly StatsView _temperature = new(); + private readonly StatsView _humidity = new(); + private readonly StatsView _pressure = new(); + protected override async Task OnInitializedAsync() { await UpdateData(); @@ -35,11 +85,72 @@ var context = await DbContextFactory.CreateDbContextAsync(); - List mpptData = await context.Mppts.OrderByDescending(m => m.Id) + _uptime.FromData(context.Systems.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.Uptime + })); + + var wakeupDozeOff = context.Systems.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.CreatedAt.ToUnixTimestamp() + }).OrderBy(d => d.DateTime).GroupBy(d => d.DateTime).ToList().AsQueryable(); + + _wakeup.FromData(wakeupDozeOff.Select(d => d.First())); + _dozeOff.FromData(wakeupDozeOff.Select(d => d.Last())); + + _voltageBattery.FromData(context.Mppts.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.BatteryVoltage / 1000.0 + })); + + _currentBattery.FromData(context.Mppts.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.BatteryCurrent + })); + + _currentSolar.FromData(context.Mppts.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.SolarCurrent + })); + + _loraRx.FromData(context.LoRas.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.IsTx ? 0 : 1 + }).GroupBy(d => d.DateTime.Date, (time, data) => new StatsView.UnitData + { + DateTime = time, + Value = data.Sum(d => d.Value) + })); + + _temperature.FromData(context.Weathers.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.Temperature + })); + + _humidity.FromData(context.Weathers.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.Humidity + })); + + _pressure.FromData(context.Weathers.Select(d => new StatsView.UnitData + { + DateTime = d.CreatedAt, + Value = d.Pressure + })); + + var mpptData = await context.Mppts.OrderByDescending(m => m.Id) .Take(500) .Reverse() .ToListAsync(); - List weatherData = await context.Weathers.OrderByDescending(m => m.Id) + var weatherData = await context.Weathers.OrderByDescending(m => m.Id) .Take(500) .Reverse() .ToListAsync(); @@ -115,11 +226,11 @@ await base.OnAfterRenderAsync(firstRender); } - List> DataVoltageBattery { get; set; } = new(); - List> DataVoltageSolar { get; set; } = new(); - List> DataCurrent { get; set; } = new(); - List> DataWeather { get; set; } = new(); - List> DataWeatherPressure { get; set; } = new(); + List> DataVoltageBattery { get; set; } = []; + List> DataVoltageSolar { get; set; } = []; + List> DataCurrent { get; set; } = []; + List> DataWeather { get; set; } = []; + List> DataWeatherPressure { get; set; } = []; readonly LineConfig _configVoltageBattery = new() { @@ -249,8 +360,8 @@ { Text = "Pression atmosphérique (hPa)" }, - Min = 800, - Max = 1200, + Min = 950, + Max = 1050, Visible = true }, Legend = new Legend diff --git a/Monitor/Monitor/Pages/Index.razor b/Monitor/Monitor/Pages/Index.razor index d3225b1..57cac0c 100644 --- a/Monitor/Monitor/Pages/Index.razor +++ b/Monitor/Monitor/Pages/Index.razor @@ -19,20 +19,20 @@ - - + + - - + + - - + + - - + + - - + + @@ -41,26 +41,26 @@ Color="@(EntitiesManagerService.Entities.MpptChargeCurrent.Value > 0 ? "green" : "red")"> - - + + - - + + - - + + - - + + - - + + - - + + - - + + @@ -70,17 +70,17 @@ Color="@(EntitiesManagerService.Entities.SolarIsDay.Value ? "orange" : "darkblue")"> - - + + - - + + - - + + - - + + @@ -90,17 +90,17 @@ Color="@(EntitiesManagerService.Entities.StatusBoxOpened.Value ? "red" : "")"> - - + + - - + + - - + + - - + + @@ -112,14 +112,14 @@ : "blue")"> - - + + - - + + - - + + @@ -127,11 +127,11 @@ - + - + @@ -236,7 +236,7 @@ _timer?.Dispose(); } - private List _loras = new(); // If we use LoRa Entity class, We have error CS1503 - private List _loraLastHeards = new(); + private List _loras = []; // If we use LoRa Entity class, We have error CS1503 + private List _loraLastHeards = []; private Timer? _timer; } \ No newline at end of file diff --git a/Monitor/Monitor/Pages/Settings.razor b/Monitor/Monitor/Pages/Settings.razor index b574a76..6cdd0f5 100644 --- a/Monitor/Monitor/Pages/Settings.razor +++ b/Monitor/Monitor/Pages/Settings.razor @@ -164,6 +164,9 @@ + + + @@ -210,6 +213,11 @@ } } + private void RefreshMcuData() + { + SerialMessageService.GetDataJson(); + } + public void Dispose() { _timer?.Dispose(); diff --git a/Monitor/Monitor/Program.cs b/Monitor/Monitor/Program.cs index 6041017..3427ccf 100644 --- a/Monitor/Monitor/Program.cs +++ b/Monitor/Monitor/Program.cs @@ -105,13 +105,4 @@ await app.RunAsync(); -Console.WriteLine("Stopped"); - - -public class MqttConnect(EntitiesManagerService entitiesManagerService) : BackgroundService -{ - protected override async Task ExecuteAsync(CancellationToken cancellationToken) - { - await entitiesManagerService.ConnectMqtt(); - } -} \ No newline at end of file +Console.WriteLine("Stopped"); \ No newline at end of file diff --git a/Monitor/Monitor/Services/CameraService.cs b/Monitor/Monitor/Services/CameraService.cs index 10c316b..c8f0cab 100644 --- a/Monitor/Monitor/Services/CameraService.cs +++ b/Monitor/Monitor/Services/CameraService.cs @@ -29,7 +29,8 @@ public CameraService(ILogger logger, IConfiguration configuration { var configurationSection = configuration.GetSection("Cameras"); _message = configurationSection.GetValue("Message"); - _fswebcamParameters = configurationSection.GetSection("Devices").Get>()?.ToList() ?? new List(); + _fswebcamParameters = configurationSection.GetSection("Devices").Get>()?.ToList() ?? + []; _storagePath = Path.Combine( configuration.GetValueOrThrow("StoragePath"), diff --git a/Monitor/Monitor/Services/EntitiesManagerService.cs b/Monitor/Monitor/Services/EntitiesManagerService.cs index a3aeaac..fa98809 100644 --- a/Monitor/Monitor/Services/EntitiesManagerService.cs +++ b/Monitor/Monitor/Services/EntitiesManagerService.cs @@ -18,10 +18,10 @@ public class EntitiesManagerService : AService, IAsyncDisposable private readonly string _topicBase; private readonly string _clientId; - private readonly IMqttClient _mqttClient; private readonly IConfigurationSection _configurationSection; + private readonly IMqttClient _mqttClient; private readonly DataContext _context; - private readonly List _entities = new(); + private readonly List _entities = []; private readonly IScheduler _scheduler; public EntitiesManagerService(ILogger logger, diff --git a/Monitor/Monitor/Services/MonitorService.cs b/Monitor/Monitor/Services/MonitorService.cs index 4b6c86d..5542a20 100644 --- a/Monitor/Monitor/Services/MonitorService.cs +++ b/Monitor/Monitor/Services/MonitorService.cs @@ -2,6 +2,7 @@ using Microsoft.EntityFrameworkCore; using Monitor.Context; using Monitor.Context.Entities; +using Monitor.Extensions; using Monitor.Models; using Monitor.Models.SerialMessages; @@ -36,6 +37,9 @@ public async Task UpdateStateFromMessage(Message message) EntitiesManagerService.Entities.FeatureAprsPositionEnabled.SetValue(systemData.AprsPositionEnabled); EntitiesManagerService.Entities.FeatureSleepEnabled.SetValue(systemData.Sleep); EntitiesManagerService.Entities.TemperatureRtc.SetValue(systemData.TemperatureRtc); + EntitiesManagerService.Entities.McuUptime.SetValue(systemData.UptimeTimeSpan); + + systemService.SetTime(State.McuSystem.DateTime.DateTime); break; case WeatherData weatherData: Logger.LogDebug("New weather data received : {message}", weatherData); @@ -92,15 +96,6 @@ public async Task UpdateStateFromMessage(Message message) }); await context.SaveChangesAsync(); break; - case TimeData timeData: - Logger.LogDebug("New time data received : {message}", timeData); - - State.Time = timeData; - - EntitiesManagerService.Entities.McuUptime.SetValue(timeData.UptimeTimeSpan); - - systemService.SetTime(State.Time.DateTime.DateTime); - break; case GpioData gpioData: Logger.LogDebug("New GPIO data received : {message}", gpioData); @@ -116,7 +111,7 @@ public async Task UpdateStateFromMessage(Message message) Wifi = gpioData.Wifi, Uptime = (long)EntitiesManagerService.Entities.SystemUptime.Value.TotalSeconds, BoxOpened = State.McuSystem.BoxOpened, - McuUptime = State.Time.Uptime, + McuUptime = State.McuSystem.Uptime, TemperatureRtc = State.McuSystem.TemperatureRtc }); await context.SaveChangesAsync(); @@ -141,7 +136,7 @@ public async Task UpdateStateFromMessage(Message message) try { - Packet packet = new(loraData.Payload); + var packet = loraData.Payload.ToAprsPacket(); sender = packet.Sender; } catch (Exception e) diff --git a/Monitor/Monitor/Services/SerialMessageService.cs b/Monitor/Monitor/Services/SerialMessageService.cs index fc75ec2..990013c 100644 --- a/Monitor/Monitor/Services/SerialMessageService.cs +++ b/Monitor/Monitor/Services/SerialMessageService.cs @@ -53,7 +53,6 @@ public Message ParseMessage(string input) { "system" => JsonSerializer.Deserialize(input), "weather" => JsonSerializer.Deserialize(input), - "time" => JsonSerializer.Deserialize(input), "mppt" => JsonSerializer.Deserialize(input), "lora" => JsonSerializer.Deserialize(input), "gpio" => JsonSerializer.Deserialize(input), @@ -152,6 +151,11 @@ public void SendLora(string message) SendCommand($"lora \"{message}\""); } + public void GetDataJson() + { + SendCommand($"jsons"); + } + private void SetEepromMcu(int address, int value) { SendCommand($"set {address} {value}"); diff --git a/Monitor/Monitor/Workers/AWorker.cs b/Monitor/Monitor/Workers/AWorker.cs index 14d538b..d35fca5 100644 --- a/Monitor/Monitor/Workers/AWorker.cs +++ b/Monitor/Monitor/Workers/AWorker.cs @@ -14,7 +14,7 @@ public abstract class AWorker : IHostedService, IAsyncDisposable protected readonly IServiceProvider Services; protected readonly IScheduler Scheduler; protected readonly EntitiesManagerService EntitiesManagerService; - private readonly List _disposables = new(); + private readonly List _disposables = []; protected static readonly TimeSpan RetryDuration = TimeSpan.FromSeconds(5); diff --git a/Monitor/Monitor/Workers/AprsIsApp.cs b/Monitor/Monitor/Workers/AprsIsApp.cs index 71a7b91..89fae8c 100644 --- a/Monitor/Monitor/Workers/AprsIsApp.cs +++ b/Monitor/Monitor/Workers/AprsIsApp.cs @@ -74,10 +74,10 @@ private void ComputeReceivedPacket(Packet packet) try { - Packet packetToSend = new(_callsign, new List { "TCPIP", _callsign }, packet.InfoField); + Packet packetToSend = new(packet.Sender, new List { "TCPIP", _callsign }, packet.InfoField); var packetToSendTnc2 = packetToSend.EncodeTnc2(); - Logger.LogInformation("Packet {packet} ready to be send to RF", packetToSendTnc2); + Logger.LogInformation("Packet ready to be send to RF: {packet}", packetToSendTnc2); _serialMessageService.SendLora(packetToSendTnc2); } diff --git a/Monitor/Monitor/Workers/SerialPortMcuCommandsApp.cs b/Monitor/Monitor/Workers/SerialPortMcuCommandsApp.cs index 4755123..b038783 100644 --- a/Monitor/Monitor/Workers/SerialPortMcuCommandsApp.cs +++ b/Monitor/Monitor/Workers/SerialPortMcuCommandsApp.cs @@ -11,17 +11,16 @@ public class SerialPortMcuCommandsApp : ASerialPortApp IConfiguration configuration) : base(logger, serviceProvider, configuration, "SerialPortMessage", @"{""type"":""system"",""state"":""started"",""boxOpened"":false} - {""type"":""time"",""state"":2313942038,""uptime"":2} {""type"":""mppt"",""batteryVoltage"":12268,""batteryCurrent"":2,""solarVoltage"":163,""solarCurrent"":2,""currentCharge"":0,""status"":136,""night"":true,""alert"":false,""watchdogEnabled"":false,""watchdogPowerOffTime"":10,""watchdogCounter"":0,""powerEnabled"":true,""powerOnVoltage"":11500,""powerOffVoltage"":11300,""statusString"":""NIGHT""} - {""type"":""weather"",""temperature"":20.50,""humidity"":55} + {""type"":""weather"",""temperature"":20.50,""humidity"":55,""pressure"":978} {""type"":""gpio"",""wifi"":false,""npr"":false,""ldr"":500} {""type"":""lora"",""state"":""tx"",""payload"":""F4HVV-15>F4HVV-10,RFONLY:!/7V&-OstcI!!G Solaire camera + NPR70""} - {""type"":""system"",""state"":""alert"",""boxOpened"":true} + {""type"":""system"",""state"":""alert"",""boxOpened"":true,""time"":2313942038,""uptime"":20} {""type"":""gpio"",""wifi"":false,""npr"":true,""ldr"":300} - {""type"":""time"",""state"":2313942055,""uptime"":20} - {""type"":""weather"",""temperature"":20.0,""humidity"":50} + {""type"":""weather"",""temperature"":20.0,""humidity"":50,""pressure"":998} {""type"":""mppt"",""batteryVoltage"":12100,""batteryCurrent"":20,""solarVoltage"":1630,""solarCurrent"":20,""currentCharge"":220,""status"":136,""night"":true,""alert"":false,""watchdogEnabled"":false,""watchdogPowerOffTime"":10,""watchdogCounter"":0,""powerEnabled"":true,""powerOnVoltage"":11500,""powerOffVoltage"":11300,""statusString"":""NIGHT""} - {""type"":""lora"",""state"":""tx"",""payload"":""F4HVV-15>F4HVV,WIDE1-1:!/7V,.Ot#4I!!G Solar NPR+Lora digi+Cam |!!!i!""!>!W!!|""}") + {""type"":""lora"",""state"":""tx"",""payload"":""F4HVV-15>F4HVV,WIDE1-1:!/7V,.Ot#4I!!G Solar NPR+Lora digi+Cam |!!!i!""!>!W!!|""} + {""type"":""lora"",""state"":""rx"",""payload"":""F4HVV-9>F4HVV: Test !""}") { _serialMessageService = Services.GetRequiredService(); } diff --git a/Monitor/Monitor/appsettings.Development.json b/Monitor/Monitor/appsettings.Development.json index 59a5ba6..39a4090 100644 --- a/Monitor/Monitor/appsettings.Development.json +++ b/Monitor/Monitor/appsettings.Development.json @@ -11,7 +11,7 @@ }, "StoragePath": "/tmp/dotnet_monitor_storage", "SerialPortMessage": { - "Simulate": false, + "Simulate": true, "Path": "/dev/serial/by-id/usb-1a86_USB2.0-Serial-if00-port0" }, "Cameras": { diff --git a/cubecell_monitoring/include/Command.h b/cubecell_monitoring/include/Command.h index 08c5802..f81f200 100644 --- a/cubecell_monitoring/include/Command.h +++ b/cubecell_monitoring/include/Command.h @@ -41,6 +41,8 @@ class Command { static void doSetTime(MyCommandParser::Argument *args, char *response); static void doSleep(MyCommandParser::Argument *args, char *response); + + static void doGetJson(MyCommandParser::Argument *args, char *response); }; #endif //CUBECELL_MONITORING_COMMAND_H diff --git a/cubecell_monitoring/include/Config.h b/cubecell_monitoring/include/Config.h index ad759a6..527003a 100644 --- a/cubecell_monitoring/include/Config.h +++ b/cubecell_monitoring/include/Config.h @@ -36,7 +36,7 @@ #define INTERVAL_WEATHER 30000 #define INTERVAL_MPPT 30000 -#define INTERVAL_TIME 30000 +#define INTERVAL_STATE 30000 #define TIME_PAUSE_SCREEN 1500 #define TIME_SCREEN_ON 30000 @@ -81,6 +81,8 @@ #define COLOR_GREEN 0x005000 #define COLOR_CYAN 0x00bbff +#define CURRENT_YEAR 2024 + #ifdef USE_SOFT_SERIAL #include extern softSerial *SerialPi; diff --git a/cubecell_monitoring/include/MpptMonitor.h b/cubecell_monitoring/include/MpptMonitor.h index 42e4dc1..1d2363e 100644 --- a/cubecell_monitoring/include/MpptMonitor.h +++ b/cubecell_monitoring/include/MpptMonitor.h @@ -11,7 +11,7 @@ class MpptMonitor { explicit MpptMonitor(System *system, TwoWire &wire); bool begin(); - bool update(); + bool update(bool force = false); bool setWatchdog(uint16_t powerOffTime, uint8_t timeoutTime = WATCHDOG_TIMEOUT); bool setPowerOnOff(uint16_t powerOnVoltage, uint16_t powerOffVoltage); diff --git a/cubecell_monitoring/include/System.h b/cubecell_monitoring/include/System.h index 7b163da..8e0b169 100644 --- a/cubecell_monitoring/include/System.h +++ b/cubecell_monitoring/include/System.h @@ -31,6 +31,7 @@ class System { bool isBoxOpened() const; void setFunctionAllowed(byte function, bool allowed); void sleep(uint64_t time); + void resetTimerJson(); inline bool isFunctionAllowed(byte function) const { return functionsAllowed[function]; @@ -68,7 +69,7 @@ class System { Timer timerStatus = Timer(INTERVAL_STATUS_APRS, true); Timer timerPosition = Timer(INTERVAL_POSITION_APRS, true); Timer timerTelemetry = Timer(INTERVAL_TELEMETRY_APRS, false); - Timer timerTime = Timer(INTERVAL_TIME, true); + Timer timerState = Timer(INTERVAL_STATE, true); Timer timerScreen = Timer(TIME_SCREEN_ON); Timer timerBoxOpened = Timer(INTERVAL_ALARM_BOX_OPENED_APRS, true); Timer timerSecond = Timer(1000, true); @@ -79,7 +80,6 @@ class System { #endif TimerEvent_t *wakeUpEvent; - void showTime(); void turnScreenOn(); void turnScreenOff(); diff --git a/cubecell_monitoring/include/WeatherSensors.h b/cubecell_monitoring/include/WeatherSensors.h index 40e2540..65006d1 100644 --- a/cubecell_monitoring/include/WeatherSensors.h +++ b/cubecell_monitoring/include/WeatherSensors.h @@ -14,7 +14,7 @@ class WeatherSensors { explicit WeatherSensors(System *system); bool begin(); - bool update(); + bool update(bool force = false); inline float getTemperature() const { return temperature; diff --git a/cubecell_monitoring/lib/Aprs/Aprs.cpp b/cubecell_monitoring/lib/Aprs/Aprs.cpp index a471a4c..b08ac8d 100644 --- a/cubecell_monitoring/lib/Aprs/Aprs.cpp +++ b/cubecell_monitoring/lib/Aprs/Aprs.cpp @@ -59,7 +59,7 @@ uint8_t Aprs::encode(AprsPacket* aprsPacket, char* aprsResult) { sprintf_P(&aprsResult[strlen(aprsResult)], PSTR(" %s"), aprsPacket->comment); // Normally, 40 max for position } - if (aprsPacket->type == Position && aprsPacket->position.withTelemetry) { + if (aprsPacket->type == Position && aprsPacket->position.withTelemetry && !aprsPacket->telemetries.legacy) { appendTelemetries(aprsPacket, aprsResult); } @@ -224,7 +224,7 @@ void Aprs::appendTelemetries(AprsPacket *aprsPacket, char* aprsResult) { strcpy(&aprsResult[strlen(aprsResult)], bufferBase91); for (auto telemetry : aprsPacket->telemetries.telemetriesAnalog) { - ax25Base91Enc(bufferBase91, 2, abs(telemetry.value)); + ax25Base91Enc(bufferBase91, 2, (uint16_t) abs(telemetry.value)); strcpy(&aprsResult[strlen(aprsResult)], bufferBase91); } @@ -241,14 +241,18 @@ void Aprs::appendTelemetries(AprsPacket *aprsPacket, char* aprsResult) { strcat_P(aprsResult, PSTR("|")); break; case Telemetry: - if (aprsPacket->telemetries.telemetrySequenceNumber > 999) { + if (aprsPacket->telemetries.legacy && aprsPacket->telemetries.telemetrySequenceNumber > 999) { aprsPacket->telemetries.telemetrySequenceNumber = 0; } sprintf_P(&aprsResult[strlen(aprsResult)], PSTR("T#%d,"), aprsPacket->telemetries.telemetrySequenceNumber); for (auto telemetry : aprsPacket->telemetries.telemetriesAnalog) { - sprintf_P(&aprsResult[strlen(aprsResult)], formatDouble(telemetry.value), telemetry.value); + if (aprsPacket->telemetries.legacy) { + sprintf_P(&aprsResult[strlen(aprsResult)], PSTR("%d"), (uint8_t) abs(telemetry.value)); + } else { + sprintf_P(&aprsResult[strlen(aprsResult)], formatDouble(telemetry.value), telemetry.value); + } strcat_P(aprsResult, PSTR(",")); } @@ -410,7 +414,7 @@ void Aprs::appendWeather(AprsWeather *weather, char* aprsResult) { strcat_P(aprsResult, PSTR(".....")); } - strcat_P(aprsResult, weather->device); +// strcat_P(aprsResult, weather->device); } char* Aprs::ax25Base91Enc(char *destination, uint8_t width, uint32_t value) { @@ -491,7 +495,7 @@ void Aprs::reset(AprsPacket *aprsPacket) { aprsPacket->message.ackToReject[0] = '\0'; aprsPacket->message.destination[0] = '\0'; - aprsPacket->weather.device[0] = '\0'; +// aprsPacket->weather.device[0] = '\0'; aprsPacket->weather.temperatureFahrenheit = 0; aprsPacket->weather.humidity = 0; aprsPacket->weather.pressure = 0; diff --git a/cubecell_monitoring/lib/Aprs/Aprs.h b/cubecell_monitoring/lib/Aprs/Aprs.h index af3d3ac..53862d0 100644 --- a/cubecell_monitoring/lib/Aprs/Aprs.h +++ b/cubecell_monitoring/lib/Aprs/Aprs.h @@ -59,7 +59,7 @@ typedef struct { bool useRainSinceMidnight = false; bool useHumidity = false; bool usePressure = false; - char device[WEATHER_DEVICE_LENGTH]{}; +// char device[WEATHER_DEVICE_LENGTH]{}; } AprsWeather; typedef struct { @@ -82,6 +82,7 @@ typedef struct { AprsTelemetry telemetriesBoolean[MAX_TELEMETRY_BOOLEAN]{}; uint16_t telemetrySequenceNumber = 0; char projectName[TELEMETRY_PROJECT_NAME_LENGTH]{}; + bool legacy = false; } AprsTelemetries; typedef struct { diff --git a/cubecell_monitoring/lib/Aprs/test/test.cpp b/cubecell_monitoring/lib/Aprs/test/test.cpp index 70f9ced..2ca5239 100644 --- a/cubecell_monitoring/lib/Aprs/test/test.cpp +++ b/cubecell_monitoring/lib/Aprs/test/test.cpp @@ -18,8 +18,8 @@ int main() { 40.12345, 5.12345, 123, - 45, - 678 + 45, // 83 Km/h + 678 // 206 m }; packet.telemetries.telemetrySequenceNumber = 7544; strcpy(packet.telemetries.projectName, "Test project"); @@ -38,7 +38,7 @@ int main() { }; packet.telemetries.telemetriesAnalog[2] = { "", - 1656, + -1656.45, "", { 1, -2.0987, 34.5 @@ -94,7 +94,7 @@ int main() { }; packet.telemetries.telemetriesBoolean[7] = { "", - 0, + 1, }; packet.type = Position; @@ -171,7 +171,7 @@ int main() { packet.weather.usePressure = true; packet.position.withWeather = true; packet.position.withTelemetry = false; - strcpy(packet.weather.device, "Test"); +// strcpy(packet.weather.device, "Test"); size = Aprs::encode(&packet, encoded_packet); printf("\nAPRS Weather Size : %d\n%s\n", size, encoded_packet); diff --git a/cubecell_monitoring/src/Command.cpp b/cubecell_monitoring/src/Command.cpp index 8b23e4f..31cea20 100644 --- a/cubecell_monitoring/src/Command.cpp +++ b/cubecell_monitoring/src/Command.cpp @@ -22,6 +22,7 @@ Command::Command(System *system) { parser.registerCommand(PSTR("set"), PSTR("uu"), doSetEeprom); parser.registerCommand(PSTR("time"), PSTR("u"), doSetTime); parser.registerCommand(PSTR("sleep"), PSTR("u"), doSleep); + parser.registerCommand(PSTR("jsons"), PSTR(""), doGetJson); } bool Command::processCommand(const char *command) { @@ -133,3 +134,8 @@ void Command::doSleep(MyCommandParser::Argument *args, char *response) { sprintf_P(response, PSTR("KO")); } + +void Command::doGetJson(MyCommandParser::Argument *args, char *response) { + system->resetTimerJson(); + sprintf_P(response, PSTR("OK")); +} diff --git a/cubecell_monitoring/src/Communication.cpp b/cubecell_monitoring/src/Communication.cpp index e89199a..a6f0b64 100644 --- a/cubecell_monitoring/src/Communication.cpp +++ b/cubecell_monitoring/src/Communication.cpp @@ -20,7 +20,7 @@ Communication::Communication(System *system) : system(system) { aprsPacketTx.weather.useTemperature = true; aprsPacketTx.weather.usePressure = true; - strcpy_P(aprsPacketTx.weather.device, PSTR("4HVV")); +// strcpy_P(aprsPacketTx.weather.device, PSTR("4HVV")); strcpy_P(aprsPacketTx.telemetries.projectName, PSTR("Data")); @@ -177,7 +177,7 @@ void Communication::sendTelemetry() { strcpy_P(aprsPacketTx.destination, PSTR(APRS_DESTINATION)); aprsPacketTx.content[0] = '\0'; - sprintf_P(aprsPacketTx.comment, PSTR("Chg:%s Up:%lds"), mpptChg::getStatusAsString(system->mpptMonitor.getStatus()), millis() / 1000); + sprintf_P(aprsPacketTx.comment, PSTR("Chg:%s Up:%ld"), mpptChg::getStatusAsString(system->mpptMonitor.getStatus()), millis() / 1000); aprsPacketTx.telemetries.telemetrySequenceNumber = telemetrySequenceNumber++; aprsPacketTx.telemetries.telemetriesAnalog[0].value = system->mpptMonitor.getVoltageBattery(); aprsPacketTx.telemetries.telemetriesAnalog[1].value = system->mpptMonitor.getCurrentBattery(); diff --git a/cubecell_monitoring/src/MpptMonitor.cpp b/cubecell_monitoring/src/MpptMonitor.cpp index 5174885..d7fcf84 100644 --- a/cubecell_monitoring/src/MpptMonitor.cpp +++ b/cubecell_monitoring/src/MpptMonitor.cpp @@ -20,8 +20,8 @@ bool MpptMonitor::begin() { return true; } -bool MpptMonitor::update() { - if (!timer.hasExpired()) { +bool MpptMonitor::update(bool force) { + if (!force && !timer.hasExpired()) { return false; } diff --git a/cubecell_monitoring/src/System.cpp b/cubecell_monitoring/src/System.cpp index a6072ff..6e8b187 100644 --- a/cubecell_monitoring/src/System.cpp +++ b/cubecell_monitoring/src/System.cpp @@ -119,14 +119,10 @@ void System::update() { } } - if (forceSendTelemetry || timerTime.hasExpired()) { - showTime(); - - gpio.printJson(); - + if (forceSendTelemetry || timerState.hasExpired()) { printJsonSystem(PSTR("running")); - timerTime.restart(); + timerState.restart(); } mpptMonitor.update(); @@ -250,7 +246,7 @@ void System::displayText(const char *title, const char *content, uint16_t pause) DateTime System::nowToString(char *result) { DateTime now = RTClib::now(); - if (now.year() < 2023) { + if (now.year() < CURRENT_YEAR) { Log.warningln("[TIME] Use System instead of RTC"); now = DateTime(TimerGetSysTime().Seconds); } @@ -258,24 +254,6 @@ DateTime System::nowToString(char *result) { return now; } -void System::showTime() { - DateTime now = nowToString(bufferText); - - if (now.year() != 2023) { - return; - } - - serialJsonWriter - .beginObject() - .property(F("type"), PSTR("time")) - .property(F("state"), now.unixtime()) - .property(F("uptime"), millis() / 1000) - .endObject(); SerialPi->println(); - - Log.infoln(F("[TIME] %s. Temperature RTC: %dC"), bufferText, RTC.getTemperature()); - displayText(PSTR("Time"), bufferText, 1000); -} - void System::setTimeFromRTcToInternalRtc(uint64_t epoch) { TimerSysTime_t currentTime; currentTime.Seconds = epoch; @@ -306,18 +284,27 @@ void System::setFunctionAllowed(byte function, bool allowed) { } void System::printJsonSystem(const char *state) { + DateTime now = nowToString(bufferText); + serialJsonWriter .beginObject() .property(F("type"), PSTR("system")) .property(F("state"), (char*)state) .property(F("boxOpened"), isBoxOpened()) .property(F("temperatureRtc"), RTC.getTemperature()) + .property(F("time"), now.unixtime()) + .property(F("uptime"), millis() / 1000) .property(F("watchdogSafety"), functionsAllowed[EEPROM_ADDRESS_WATCHDOG_SAFETY]) .property(F("aprsDigipeater"), functionsAllowed[EEPROM_ADDRESS_APRS_DIGIPEATER]) .property(F("aprsTelemetry"), functionsAllowed[EEPROM_ADDRESS_APRS_TELEMETRY]) .property(F("aprsPosition"), functionsAllowed[EEPROM_ADDRESS_APRS_POSITION]) .property(F("sleep"), functionsAllowed[EEPROM_ADDRESS_SLEEP]) .endObject(); SerialPi->println(); + + gpio.printJson(); + + Log.infoln(F("[TIME] %s. Temperature RTC: %fC"), bufferText, RTC.getTemperature()); + displayText(PSTR("Time"), bufferText, 1000); } void System::sleep(uint64_t time) { @@ -338,3 +325,9 @@ void System::sleep(uint64_t time) { lowPowerHandler(); } + +void System::resetTimerJson() { + timerState.hasExpired(); + mpptMonitor.update(true); + weatherSensors.update(true); +} diff --git a/cubecell_monitoring/src/WeatherSensors.cpp b/cubecell_monitoring/src/WeatherSensors.cpp index 388095f..d2b49e1 100644 --- a/cubecell_monitoring/src/WeatherSensors.cpp +++ b/cubecell_monitoring/src/WeatherSensors.cpp @@ -9,8 +9,8 @@ bool WeatherSensors::begin() { return sensor.begin(); } -bool WeatherSensors::update() { - if (!timer.hasExpired()) { +bool WeatherSensors::update(bool force) { + if (!force && !timer.hasExpired()) { return false; } diff --git a/infos.odt b/infos.odt deleted file mode 100644 index e33d8b8561af41604c4dbe03741a5178f2cdd995..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23877 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 diff --git a/scripts/rsync.sh b/scripts/rsync.sh index 5ea2919..0105c7a 100755 --- a/scripts/rsync.sh +++ b/scripts/rsync.sh @@ -5,6 +5,5 @@ if pgrep -f "rsync -rvz" >/dev/null; then exit 1 fi - rsync -rvz --size-only --progress /home/debian/docker/storage/ rsync://192.168.1.254:873/opi #rsync -avz --no-perms --no-owner --progress --no-group no-times --size-only /home/debian/docker/storage/ rsync://192.168.1.254:873/opi diff --git a/scripts/serial_message_file.sh b/scripts/serial_message_file.sh new file mode 100644 index 0000000..6e466b1 --- /dev/null +++ b/scripts/serial_message_file.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Vérifier si le fichier de verrouillage existe +if pgrep -f "serial_message" >/dev/null; then + echo "Script is already running. Exiting." + exit 1 +fi + +# Configuration +SERIAL_PORT="/dev/ttyUSB0" # Port série à écouter +API_ENDPOINT="http://example.com/api" # Endpoint de l'API + +# Écouter le port série en continu +socat -d -d pty,raw,echo=0,link=/tmp/virtualcom0,ignoreeof,b38400 "$SERIAL_PORT" & + +# Boucle pour écouter le port série +while true; do + # Lire une ligne du port série + json=$(head -n 1 < /tmp/virtualcom0) + + # Vérifier si le JSON est différent du précédent et n'est pas vide + if [ -n "$json" ]; then + # Envoyer le JSON à l'API via une requête POST + curl -X POST -H "Content-Type: application/json" -d "$json" "$API_ENDPOINT" + fi + + # Attendre un court instant avant de lire la prochaine ligne + sleep 1 +done