From fb47ce09abfef711505dbef20eb505ee9e51e0d4 Mon Sep 17 00:00:00 2001 From: Maier Stephan SI Date: Mon, 22 Jul 2024 09:01:44 +0300 Subject: [PATCH] Tagessicherung --- scr/FSI.BT.IR.Plc.TimeSync.sln | 25 +++++ .../FSI.BT.IR.Plc.TimeSync.csproj | 36 ++++++ scr/FSI.BT.IR.Plc.TimeSync/Program.cs | 11 ++ .../Properties/launchSettings.json | 12 ++ .../Settings/AppContext.cs | 74 +++++++++++++ .../Settings/Context.cs | 97 +++++++++++++++++ scr/FSI.BT.IR.Plc.TimeSync/SyncPlcTime.cs | 103 ++++++++++++++++++ scr/FSI.BT.IR.Plc.TimeSync/Worker.cs | 71 ++++++++++++ .../appsettings.Development.json | 8 ++ scr/FSI.BT.IR.Plc.TimeSync/appsettings.json | 14 +++ scr/FSI.BT.IR.Plc.TimeSync/config.json | 29 +++++ scr/FSI.BT.IR.Plc.TimeSync/nlog.config | 36 ++++++ 12 files changed, 516 insertions(+) create mode 100644 scr/FSI.BT.IR.Plc.TimeSync.sln create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/FSI.BT.IR.Plc.TimeSync.csproj create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/Program.cs create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/Properties/launchSettings.json create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/Settings/AppContext.cs create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/Settings/Context.cs create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/SyncPlcTime.cs create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/Worker.cs create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/appsettings.Development.json create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/appsettings.json create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/config.json create mode 100644 scr/FSI.BT.IR.Plc.TimeSync/nlog.config diff --git a/scr/FSI.BT.IR.Plc.TimeSync.sln b/scr/FSI.BT.IR.Plc.TimeSync.sln new file mode 100644 index 0000000..5d5ab17 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34723.18 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FSI.BT.IR.Plc.TimeSync", "FSI.BT.IR.Plc.TimeSync\FSI.BT.IR.Plc.TimeSync.csproj", "{23DFF9D8-7A25-4465-865B-3D1834AF5725}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {23DFF9D8-7A25-4465-865B-3D1834AF5725}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {23DFF9D8-7A25-4465-865B-3D1834AF5725}.Debug|Any CPU.Build.0 = Debug|Any CPU + {23DFF9D8-7A25-4465-865B-3D1834AF5725}.Release|Any CPU.ActiveCfg = Release|Any CPU + {23DFF9D8-7A25-4465-865B-3D1834AF5725}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {539E3901-0BD7-4435-8501-3C27CC52E614} + EndGlobalSection +EndGlobal diff --git a/scr/FSI.BT.IR.Plc.TimeSync/FSI.BT.IR.Plc.TimeSync.csproj b/scr/FSI.BT.IR.Plc.TimeSync/FSI.BT.IR.Plc.TimeSync.csproj new file mode 100644 index 0000000..4cde1ce --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/FSI.BT.IR.Plc.TimeSync.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + enable + enable + dotnet-FSI.BT.IR.Plc.TimeSync-965c3cf3-0f37-484a-865d-4762bb9fe30e + + + + + + + + + + + + + + Never + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + diff --git a/scr/FSI.BT.IR.Plc.TimeSync/Program.cs b/scr/FSI.BT.IR.Plc.TimeSync/Program.cs new file mode 100644 index 0000000..80a2443 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/Program.cs @@ -0,0 +1,11 @@ +using FSI.BT.IR.Plc.TimeSync; +using FSI.BT.IR.Plc.TimeSync.Settings; +using static FSI.BT.IR.Plc.TimeSync.Settings.Context; + +var builder = Host.CreateApplicationBuilder(args); + +builder.Services.AddHostedService(); +builder.Services.AddSingleton(); + +using IHost host = builder.Build(); +host.Run(); \ No newline at end of file diff --git a/scr/FSI.BT.IR.Plc.TimeSync/Properties/launchSettings.json b/scr/FSI.BT.IR.Plc.TimeSync/Properties/launchSettings.json new file mode 100644 index 0000000..8f4a42c --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/Properties/launchSettings.json @@ -0,0 +1,12 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "FSI.BT.IR.Plc.TimeSync": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/scr/FSI.BT.IR.Plc.TimeSync/Settings/AppContext.cs b/scr/FSI.BT.IR.Plc.TimeSync/Settings/AppContext.cs new file mode 100644 index 0000000..a6a7155 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/Settings/AppContext.cs @@ -0,0 +1,74 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Microsoft.Extensions.Primitives; +using static FSI.BT.IR.Plc.TimeSync.Settings.Context; + +namespace FSI.BT.IR.Plc.TimeSync.Settings +{ + public class AppContext : ISettings + { + + private Cfg _cfg; + + + public AppContext() + { + LoadSettings(); + LoadCfg(); + + } + + private void LoadSettings() + { + var values = new ConfigurationBuilder() + .AddJsonFile("appsettings.json", optional: true, reloadOnChange: false) + .Build(); + + Settings = values.Get(); + + var tmo = Settings.Logging.LogLevel.MicrosoftHostingLifetime; + var verstion = Settings.Version.ToString(); + } + + private void LoadCfg() + { + var values = new ConfigurationBuilder() + .AddJsonFile("config.json", optional: true, reloadOnChange: true) + .Build(); + + Cfg = values.Get(); + + Action onChange = () => + { + Cfg = values.Get(); + }; + + ChangeToken.OnChange(() => values.GetReloadToken(), onChange); + } + + public Context.Settings Settings { get; set; } + + public Cfg Cfg + { + get { return _cfg; } + set + { + _cfg = value; + RaisePropertyChanged(); + } + } + + private void RaisePropertyChanged([CallerMemberName] string propertyName = null) + { + // Null means no subscribers to the event + var handler = PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + } +} + diff --git a/scr/FSI.BT.IR.Plc.TimeSync/Settings/Context.cs b/scr/FSI.BT.IR.Plc.TimeSync/Settings/Context.cs new file mode 100644 index 0000000..0d8ccab --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/Settings/Context.cs @@ -0,0 +1,97 @@ +using System.ComponentModel; +using System.Configuration; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace FSI.BT.IR.Plc.TimeSync.Settings +{ + public class Context + { + public interface ISettings : INotifyPropertyChanged + { + public Settings Settings { get; set; } + + public Cfg Cfg { get; set; } + } + + public record Settings + { + public Version Version { get; set; } + public Logging Logging { get; set; } + } + + public record Version + { + public uint Major { get; set; } = 0; + public uint Minor { get; set; } = 0; + + public uint Patch { get; set; } = 0; + + public string? Optional { get; set; } + + public override string ToString() + { + return Major.ToString() + "." + Minor.ToString() + "." + Patch.ToString() + ((Optional == string.Empty || Optional == null) ? "" : "-" + Optional); + } + + + + } + + public class Logging + { + public Loglevel LogLevel { get; set; } + } + + public class Loglevel + { + public string Default { get; set; } + + [ConfigurationKeyName("Microsoft.Hosting.Lifetime")] + public string MicrosoftHostingLifetime { get; set; } + } + + public record Cfg + { + + public List Plcs { get; set; } + + public string NtpServer { get; set; } + } + + public record Plc : IPlc + { + public string Name { get; set; } + public string Description { get; set; } + public string Adress { get; set; } + public int Rack { get; set; } + public int Slot { get; set; } + public int UpdateIntervall { get; set; } + public int TimeDifference { get; set; } + public bool LocalTime { get; set; } + public bool Enable { get; set; } + } + + public interface IPlc + { + public string Name { get; set; } + + public string Description { get; set; } + + public string Adress { get; set; } + + public int Rack { get; set; } + + public int Slot { get; set; } + + public int UpdateIntervall { get; set; } + + public int TimeDifference { get; set; } + + public bool LocalTime { get; set; } + + public bool Enable { get; set; } + } + } + +} diff --git a/scr/FSI.BT.IR.Plc.TimeSync/SyncPlcTime.cs b/scr/FSI.BT.IR.Plc.TimeSync/SyncPlcTime.cs new file mode 100644 index 0000000..89492fe --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/SyncPlcTime.cs @@ -0,0 +1,103 @@ +using NLog; +using Sharp7; +using GuerrillaNtp; +using static FSI.BT.IR.Plc.TimeSync.Settings.Context; + + +namespace FSI.BT.IR.Plc.TimeSync +{ + internal class SyncPlcTime : IPlc + { + private Logger _log = LogManager.GetCurrentClassLogger(); + private const string DATE_TIME_FORMAT = "dd.MM.yyyy HH:mm:ss.fff"; + + public SyncPlcTime(IPlc plc) + { + Name = plc.Name; + Description = plc.Description; + Adress = plc.Adress; + Rack = plc.Rack; + Slot = plc.Slot; + UpdateIntervall = plc.UpdateIntervall; + TimeDifference = plc.TimeDifference; + LocalTime = plc.LocalTime; + Enable = plc.Enable; + } + + public string Name { get; set; } + public string Description { get; set; } + public string Adress { get; set; } + public int Rack { get; set; } + public int Slot { get; set; } + public int UpdateIntervall { get; set; } + + public int TimeDifference { get; set; } + + public bool LocalTime { get; set; } + + public bool Enable { get; set; } + + public string NtpServer { get; set; } + + + public async Task Snyc(CancellationToken cancellationToken) => + await Task.Run(async () => + { + while (!cancellationToken.IsCancellationRequested) + { + var plc = new S7Client(); + + var connectionRslt = plc.ConnectTo(Adress, Rack, Slot); + + if (connectionRslt == 0) + { + _log.Debug(Name + " Verbindung hergestellt."); + } + else + { + _log.Error(Name + " Verbindung nicht hergestellt."); + _log.Error(Name + " Fehler: " + plc.ErrorText(connectionRslt)); + return; + } + + var plcDateTime = new DateTime(); + plc.GetPlcDateTime(ref plcDateTime); + _log.Debug(Name + " SPS Zeit (aktuell): " + plcDateTime.ToString(DATE_TIME_FORMAT)); + + var client = new NtpClient(NtpServer); + NtpClock clock = client.Query(); + + DateTime ntpDateTime; + + if (LocalTime) + { + _log.Debug(Name + " UTC Zeit: " + clock.Now.ToString(DATE_TIME_FORMAT)); + ntpDateTime = clock.Now.DateTime; + } + else + { + _log.Debug(Name + " Ortszeit: " + clock.UtcNow.ToString(DATE_TIME_FORMAT)); + ntpDateTime = clock.UtcNow.DateTime; + } + + var timeSpan = Math.Abs((plcDateTime - ntpDateTime).TotalMilliseconds); + if (TimeDifference > 0 && timeSpan >= TimeDifference) + { + _log.Debug(Name + " Zeitdifferenz " + timeSpan + " ms überschritten"); + _log.Info(Name + " neue Zeit: " + ntpDateTime.ToString(DATE_TIME_FORMAT)); + var temp = plc.SetPlcDateTime(ntpDateTime); + } + else if (TimeDifference == 0) + { + _log.Info(Name + " neue Zeit: " + ntpDateTime.ToString(DATE_TIME_FORMAT)); + var temp = plc.SetPlcDateTime(ntpDateTime); + } + + plc.Disconnect(); + _log.Debug(Name + " Verbindung getrennt."); + + await Task.Delay(UpdateIntervall, cancellationToken); + } + }, cancellationToken); + } +} diff --git a/scr/FSI.BT.IR.Plc.TimeSync/Worker.cs b/scr/FSI.BT.IR.Plc.TimeSync/Worker.cs new file mode 100644 index 0000000..574a551 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/Worker.cs @@ -0,0 +1,71 @@ +using NLog; +using static FSI.BT.IR.Plc.TimeSync.Settings.Context; + +namespace FSI.BT.IR.Plc.TimeSync +{ + public class Worker(ISettings settings) : BackgroundService + { + private Logger _log = LogManager.GetCurrentClassLogger(); + private List _taskList; + private CancellationTokenSource _tokenSource; + private CancellationToken _stoppingToken; + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + + + settings.PropertyChanged += Settings_PropertyChanged; + + StartTasks(); + + } + + private void Settings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) + { + StopTasks(); + StartTasks(); + } + + private void StartTasks() + { + + if (_taskList != null) + return; + + _taskList = new List(); + _tokenSource = new CancellationTokenSource(); + _stoppingToken = _tokenSource.Token; + + foreach (var plc in settings.Cfg.Plcs) + { + + if (plc.Enable) + { + var timeSync = new SyncPlcTime(plc); + timeSync.NtpServer = settings.Cfg.NtpServer; + timeSync.Snyc(_stoppingToken); + _taskList.Add(timeSync); + _log.Info(plc.Name + " Task gestartet."); + } + } + + } + + private async void StopTasks() + { + _tokenSource.Cancel(); + _stoppingToken = _tokenSource.Token; + + foreach (var task in _taskList) + { + task.Snyc(_stoppingToken); + _log.Info(task.Name + " Task beendet."); + } + + _taskList?.Clear(); + _taskList = null; + } + + + } +} diff --git a/scr/FSI.BT.IR.Plc.TimeSync/appsettings.Development.json b/scr/FSI.BT.IR.Plc.TimeSync/appsettings.Development.json new file mode 100644 index 0000000..b2dcdb6 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/scr/FSI.BT.IR.Plc.TimeSync/appsettings.json b/scr/FSI.BT.IR.Plc.TimeSync/appsettings.json new file mode 100644 index 0000000..de26e29 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/appsettings.json @@ -0,0 +1,14 @@ +{ + "Version": { + "Major": 0, + "Minor": 0, + "Patch": 0, + "Optional": "alpha" + }, + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/scr/FSI.BT.IR.Plc.TimeSync/config.json b/scr/FSI.BT.IR.Plc.TimeSync/config.json new file mode 100644 index 0000000..eb46f81 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/config.json @@ -0,0 +1,29 @@ +{ + + "Plcs": [ + { + "Name": "PL1 FA", + "Description": "Beschreibung", + "Adress": "10.10.199.95", + "Rack": 0, + "Slot": 2, + "UpdateIntervall": 10000, + "TimeDifference": 10, + "LocalTime": false, + "Enable": true + }, + { + "Name": "PL1 FA 123", + "Description": "PL1 Formanlage", + "Adress": "10.10.199.95", + "Rack": 0, + "Slot": 2, + "UpdateIntervall": 60000, + "TimeDifference": 0, + "LocalTime": true, + "Enable": false + } + ], + "NtpServer": "10.10.199.41" + +} diff --git a/scr/FSI.BT.IR.Plc.TimeSync/nlog.config b/scr/FSI.BT.IR.Plc.TimeSync/nlog.config new file mode 100644 index 0000000..9951845 --- /dev/null +++ b/scr/FSI.BT.IR.Plc.TimeSync/nlog.config @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file