Neuerstellung
This commit is contained in:
25
scr/FSI.BT.IR.Plc.TimeSync.sln
Normal file
25
scr/FSI.BT.IR.Plc.TimeSync.sln
Normal file
@@ -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
|
||||||
37
scr/FSI.BT.IR.Plc.TimeSync/FSI.BT.IR.Plc.TimeSync.csproj
Normal file
37
scr/FSI.BT.IR.Plc.TimeSync/FSI.BT.IR.Plc.TimeSync.csproj
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk.Worker">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<UserSecretsId>dotnet-FSI.BT.IR.Plc.TimeSync-965c3cf3-0f37-484a-865d-4762bb9fe30e</UserSecretsId>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||||
|
<PackageReference Include="Config.Net" Version="5.2.0" />
|
||||||
|
<PackageReference Include="GuerrillaNtp" Version="3.1.0" />
|
||||||
|
<PackageReference Include="Json.Net" Version="1.0.33" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||||
|
<PackageReference Include="NLog.Extensions.Logging" Version="5.3.11" />
|
||||||
|
<PackageReference Include="Sharp7" Version="1.1.84" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Update="Settings\AppContext.cs">
|
||||||
|
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Content Update="appsettings.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="config.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
<Content Update="nlog.config">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</Content>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
10
scr/FSI.BT.IR.Plc.TimeSync/Program.cs
Normal file
10
scr/FSI.BT.IR.Plc.TimeSync/Program.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using FSI.BT.IR.Plc.TimeSync;
|
||||||
|
using static FSI.BT.IR.Plc.TimeSync.Settings.Context;
|
||||||
|
|
||||||
|
var builder = Host.CreateApplicationBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddHostedService<Worker>();
|
||||||
|
builder.Services.AddSingleton<ISettings, FSI.BT.IR.Plc.TimeSync.Settings.AppContext>();
|
||||||
|
|
||||||
|
using IHost host = builder.Build();
|
||||||
|
host.Run();
|
||||||
12
scr/FSI.BT.IR.Plc.TimeSync/Properties/launchSettings.json
Normal file
12
scr/FSI.BT.IR.Plc.TimeSync/Properties/launchSettings.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
scr/FSI.BT.IR.Plc.TimeSync/Settings/AppContext.cs
Normal file
71
scr/FSI.BT.IR.Plc.TimeSync/Settings/AppContext.cs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using CommandLine;
|
||||||
|
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;
|
||||||
|
private string[] _args;
|
||||||
|
|
||||||
|
public AppContext()
|
||||||
|
{
|
||||||
|
_args = new string[0];
|
||||||
|
LoadSettings();
|
||||||
|
LoadCfg();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadSettings()
|
||||||
|
{
|
||||||
|
var values = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: false)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Settings = values.Get<Context.Settings>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LoadCfg()
|
||||||
|
{
|
||||||
|
var values = new ConfigurationBuilder()
|
||||||
|
.AddJsonFile("config.json", optional: true, reloadOnChange: true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
Cfg = values.Get<Context.Cfg>();
|
||||||
|
|
||||||
|
Action onChange = () =>
|
||||||
|
{
|
||||||
|
Cfg = values.Get<Context.Cfg>();
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
12
scr/FSI.BT.IR.Plc.TimeSync/Settings/ConsoleArgs.cs
Normal file
12
scr/FSI.BT.IR.Plc.TimeSync/Settings/ConsoleArgs.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using CommandLine;
|
||||||
|
|
||||||
|
namespace FSI.BT.IR.Plc.TimeSync.Settings
|
||||||
|
{
|
||||||
|
public class ConsoleArgs
|
||||||
|
{
|
||||||
|
[Option('v', "version", Required = false, HelpText = "Version")]
|
||||||
|
public bool Version { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
239
scr/FSI.BT.IR.Plc.TimeSync/Settings/Context.cs
Normal file
239
scr/FSI.BT.IR.Plc.TimeSync/Settings/Context.cs
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
|
||||||
|
namespace FSI.BT.IR.Plc.TimeSync.Settings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Datenbasis von config.json und appsettings.json
|
||||||
|
/// Einstellungen und Konfigurationsdaten
|
||||||
|
/// </summary>
|
||||||
|
public class Context
|
||||||
|
{
|
||||||
|
public interface ISettings : INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Daten von appsettings.json
|
||||||
|
/// </summary>
|
||||||
|
public Settings Settings { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Daten von config.json
|
||||||
|
/// </summary>
|
||||||
|
public Cfg Cfg { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Daten von appsettings.json
|
||||||
|
/// </summary>
|
||||||
|
public record Settings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Versions-Informationen
|
||||||
|
/// </summary>
|
||||||
|
public Version Version { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Build-Informationen
|
||||||
|
/// </summary>
|
||||||
|
public Build Build { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logging-Einstellungen
|
||||||
|
/// </summary>
|
||||||
|
public Logging Logging { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Versions-Informationen
|
||||||
|
/// </summary>
|
||||||
|
public record Version
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Haupt-Versionsnummer
|
||||||
|
/// </summary>
|
||||||
|
public uint Major { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Unter-Versionsnummer
|
||||||
|
/// </summary>
|
||||||
|
public uint Minor { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Patch/Hotfix
|
||||||
|
/// </summary>
|
||||||
|
public uint Patch { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// optinoale Versionsinformationen
|
||||||
|
/// </summary>
|
||||||
|
public string? Optional { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// gibt die Versions-Nummer zurück
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Versionsnummer</returns>
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Major.ToString() + "." + Minor.ToString() + "." + Patch.ToString() + ((Optional == string.Empty || Optional == null) ? "" : "-" + Optional);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Build-Informationen
|
||||||
|
/// </summary>
|
||||||
|
public record Build
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ersteller
|
||||||
|
/// </summary>
|
||||||
|
public string Creator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Organisation
|
||||||
|
/// </summary>
|
||||||
|
public string Organization { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Erstellungsjahr
|
||||||
|
/// </summary>
|
||||||
|
public int CreationYear { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Beschreibung
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Logging
|
||||||
|
{
|
||||||
|
public Loglevel LogLevel { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logging-Einstellungen
|
||||||
|
/// </summary>
|
||||||
|
public class Loglevel
|
||||||
|
{
|
||||||
|
public string Default { get; set; }
|
||||||
|
|
||||||
|
[ConfigurationKeyName("Microsoft.Hosting.Lifetime")]
|
||||||
|
public string MicrosoftHostingLifetime { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Daten von config.json
|
||||||
|
/// </summary>
|
||||||
|
public record Cfg
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Spsen, deren Zeit mit NTP-Server syncronsiert werden sollen
|
||||||
|
/// </summary>
|
||||||
|
public List<Plc> Plcs { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// NTP-Server Adresse
|
||||||
|
/// </summary>
|
||||||
|
public string NtpServer { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SPS-Daten
|
||||||
|
/// </summary>
|
||||||
|
public record Plc : IPlc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Beschreibung
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IP-Adresse
|
||||||
|
/// </summary>
|
||||||
|
public string Adress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rack-Nummer
|
||||||
|
/// </summary>
|
||||||
|
public int Rack { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Slot-Nummer
|
||||||
|
/// </summary>
|
||||||
|
public int Slot { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update-Intervall, in der die Zeit überprüft werden soll.
|
||||||
|
/// </summary>
|
||||||
|
public int UpdateIntervall { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zeitdifferenz, ab der die SPS-Zeit angepasst werden soll.
|
||||||
|
/// </summary>
|
||||||
|
public int TimeDifference { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Locale Zeit wird an die SPS gesendet - nicht UTC-Zeit
|
||||||
|
/// </summary>
|
||||||
|
public bool LocalTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Soll SPS-Zeit synchronisiert werden
|
||||||
|
/// </summary>
|
||||||
|
public bool Enable { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IPlc
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Beschreibung
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IP-Adresse
|
||||||
|
/// </summary>
|
||||||
|
public string Adress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rack-Nummer
|
||||||
|
/// </summary>
|
||||||
|
public int Rack { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Slot-Nummer
|
||||||
|
/// </summary>
|
||||||
|
public int Slot { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update-Intervall, in der die Zeit überprüft werden soll.
|
||||||
|
/// </summary>
|
||||||
|
public int UpdateIntervall { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zeitdifferenz, ab der die SPS-Zeit angepasst werden soll.
|
||||||
|
/// </summary>
|
||||||
|
public int TimeDifference { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Locale Zeit wird an die SPS gesendet - nicht UTC-Zeit
|
||||||
|
/// </summary>
|
||||||
|
public bool LocalTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Soll SPS-Zeit synchronisiert werden
|
||||||
|
/// </summary>
|
||||||
|
public bool Enable { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
164
scr/FSI.BT.IR.Plc.TimeSync/SyncPlcTime.cs
Normal file
164
scr/FSI.BT.IR.Plc.TimeSync/SyncPlcTime.cs
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
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
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private const string DATE_TIME_FORMAT = "dd.MM.yyyy HH:mm:ss.fff"; // Zeitformat
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
private Logger _log = LogManager.GetCurrentClassLogger(); // Nlog
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Name
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Beschreibung
|
||||||
|
/// </summary>
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// IP-Adresse
|
||||||
|
/// </summary>
|
||||||
|
public string Adress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rack-Nummer
|
||||||
|
/// </summary>
|
||||||
|
public int Rack { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Slot-Nummer
|
||||||
|
/// </summary>
|
||||||
|
public int Slot { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Update-Intervall, in der die Zeit überprüft werden soll.
|
||||||
|
/// </summary>
|
||||||
|
public int UpdateIntervall { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Zeitdifferenz, ab der die SPS-Zeit angepasst werden soll.
|
||||||
|
/// </summary>
|
||||||
|
public int TimeDifference { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Locale Zeit wird an die SPS gesendet - nicht UTC-Zeit
|
||||||
|
/// </summary>
|
||||||
|
public bool LocalTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Soll SPS-Zeit synchronisiert werden
|
||||||
|
/// </summary>
|
||||||
|
public bool Enable { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// NTP-Server Adresse
|
||||||
|
/// </summary>
|
||||||
|
public string NtpServer { get; set; }
|
||||||
|
|
||||||
|
public async Task Snyc(CancellationToken cancellationToken) =>
|
||||||
|
await Task.Run(async () =>
|
||||||
|
{
|
||||||
|
var plc = new S7Client(); // SPS-Verbindung
|
||||||
|
var client = new NtpClient(NtpServer); // NTP-Client
|
||||||
|
NtpClock clock = client.Query(); // NTP-Client Uhrzeit
|
||||||
|
var plcDateTime = new DateTime(); // Uhrzeit SPS
|
||||||
|
DateTime ntpDateTime; // Uhrzeit NTP-Client
|
||||||
|
|
||||||
|
while (!cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Verbindung mit SPS-Aufbauen
|
||||||
|
var connectionRslt = plc.ConnectTo(Adress, Rack, Slot);
|
||||||
|
|
||||||
|
// Verbindungsstatus überprüfen
|
||||||
|
if (connectionRslt == 0) // Verbindung i.O.
|
||||||
|
{
|
||||||
|
_log.Debug(Name + " Verbindung hergestellt.");
|
||||||
|
}
|
||||||
|
else // Verbindung n.i.O.
|
||||||
|
{
|
||||||
|
_log.Error(Name + " Verbindung nicht hergestellt.");
|
||||||
|
_log.Error(Name + " Fehler: " + plc.ErrorText(connectionRslt));
|
||||||
|
await Task.Delay(UpdateIntervall, cancellationToken); // Warten bis zum nächsten Verbindungsversuch (Zeiten aus config.json)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Error(Name + " " + ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
plc.GetPlcDateTime(ref plcDateTime); // Uhrzeit aus SPS auslesen
|
||||||
|
_log.Debug(Name + " SPS Zeit (aktuell): " + plcDateTime.ToString(DATE_TIME_FORMAT));
|
||||||
|
|
||||||
|
if (LocalTime) // lokale Zeit/Ortszeit
|
||||||
|
{
|
||||||
|
_log.Debug(Name + " Ortszeit: " + clock.Now.ToString(DATE_TIME_FORMAT));
|
||||||
|
ntpDateTime = clock.Now.DateTime; // lokale Zeit/Ortszeit von NTP-Server
|
||||||
|
}
|
||||||
|
else // UTC - Zeit
|
||||||
|
{
|
||||||
|
_log.Debug(Name + " UTC-Zeit: " + clock.UtcNow.ToString(DATE_TIME_FORMAT));
|
||||||
|
ntpDateTime = clock.UtcNow.DateTime; // UTC-Zeit von NTP-Server
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zeitdiffernz zwischen SPS-Zeit und Zeit von NTP-Server berechnen
|
||||||
|
var timeSpan = Math.Abs((plcDateTime - ntpDateTime).TotalMilliseconds);
|
||||||
|
|
||||||
|
if (
|
||||||
|
TimeDifference > 0 // Zeitdifferenz aus Einstellungen > 0 ms
|
||||||
|
&& timeSpan >= TimeDifference // Zeitdifferenz überprüfung
|
||||||
|
)
|
||||||
|
{
|
||||||
|
_log.Debug(Name + " Zeitdifferenz " + timeSpan + " ms überschritten");
|
||||||
|
var temp = plc.SetPlcDateTime(ntpDateTime); // Zeit an SPS senden
|
||||||
|
_log.Info(Name + " neue Zeit: " + ntpDateTime.ToString(DATE_TIME_FORMAT));
|
||||||
|
}
|
||||||
|
else if (TimeDifference == 0)
|
||||||
|
{
|
||||||
|
var temp = plc.SetPlcDateTime(ntpDateTime); // Zeit an SPS senden
|
||||||
|
_log.Info(Name + " neue Zeit: " + ntpDateTime.ToString(DATE_TIME_FORMAT));
|
||||||
|
}
|
||||||
|
|
||||||
|
plc.Disconnect(); // Verbindung zur SPS trennen
|
||||||
|
_log.Debug(Name + " Verbindung getrennt.");
|
||||||
|
|
||||||
|
_log.Debug(Name + " Start Task-Wartezeit");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(UpdateIntervall, cancellationToken); // Warten bis zum nächsten Verbindungsversuch (Zeiten aus config.json)
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Error(Name + " " + ex.Message);
|
||||||
|
}
|
||||||
|
_log.Debug(Name + " Ende Task-Wartezeit");
|
||||||
|
}
|
||||||
|
}, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
85
scr/FSI.BT.IR.Plc.TimeSync/Worker.cs
Normal file
85
scr/FSI.BT.IR.Plc.TimeSync/Worker.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
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<SyncPlcTime> _taskList;
|
||||||
|
private CancellationTokenSource _tokenSource;
|
||||||
|
private CancellationToken _stoppingToken;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Standard Taks
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="stoppingToken"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
|
{
|
||||||
|
|
||||||
|
settings.PropertyChanged += Settings_PropertyChanged; // Event, bei <20>nderungen an der Config-Datei
|
||||||
|
|
||||||
|
StartTasks(); // Tasks, die beim Start ausgef<65>hrt werden
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Event, bei <20>nderungen an der config.json.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
private void Settings_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
StopTasks();
|
||||||
|
StartTasks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tasks, die beim Start ausgef<65>hrt werden sollen.
|
||||||
|
/// </summary>
|
||||||
|
private void StartTasks()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (_taskList != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_taskList = new List<SyncPlcTime>();
|
||||||
|
_tokenSource = new CancellationTokenSource();
|
||||||
|
_stoppingToken = _tokenSource.Token;
|
||||||
|
|
||||||
|
// Schleife <20>ber alle Spsen
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// alle Taks werden gestoppt.
|
||||||
|
/// </summary>
|
||||||
|
private async void StopTasks()
|
||||||
|
{
|
||||||
|
_tokenSource.Cancel();
|
||||||
|
_stoppingToken = _tokenSource.Token;
|
||||||
|
|
||||||
|
// Schleife <20>ber alle Spsen
|
||||||
|
foreach (var task in _taskList)
|
||||||
|
{
|
||||||
|
task.Snyc(_stoppingToken);
|
||||||
|
_log.Info(task.Name + " Task beendet.");
|
||||||
|
}
|
||||||
|
|
||||||
|
_taskList?.Clear();
|
||||||
|
_taskList = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
scr/FSI.BT.IR.Plc.TimeSync/appsettings.Development.json
Normal file
8
scr/FSI.BT.IR.Plc.TimeSync/appsettings.Development.json
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
scr/FSI.BT.IR.Plc.TimeSync/appsettings.json
Normal file
20
scr/FSI.BT.IR.Plc.TimeSync/appsettings.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"Version": {
|
||||||
|
"Major": 0,
|
||||||
|
"Minor": 0,
|
||||||
|
"Patch": 0,
|
||||||
|
"Optional": "alpha"
|
||||||
|
},
|
||||||
|
"Build": {
|
||||||
|
"Creator": "Stephan Maier",
|
||||||
|
"Organization": "Fondium Singen GmbH",
|
||||||
|
"CreationYear": "2024",
|
||||||
|
"Description": ""
|
||||||
|
},
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.Hosting.Lifetime": "Information"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
scr/FSI.BT.IR.Plc.TimeSync/config.json
Normal file
27
scr/FSI.BT.IR.Plc.TimeSync/config.json
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
36
scr/FSI.BT.IR.Plc.TimeSync/nlog.config
Normal file
36
scr/FSI.BT.IR.Plc.TimeSync/nlog.config
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
autoReload="true"
|
||||||
|
throwExceptions="false">
|
||||||
|
|
||||||
|
<variable name="appName" value="FSI.BT.IR.Plc.TimeSync" />
|
||||||
|
|
||||||
|
<!-- the targets to write to -->
|
||||||
|
<targets>
|
||||||
|
<!-- write logs to file -->
|
||||||
|
<target xsi:type="File"
|
||||||
|
name="logfile"
|
||||||
|
fileName="d:/logs/${appName}/${appName}.log"
|
||||||
|
archiveFileName ="d:/logs/${appName}/{#}_${appName}.log"
|
||||||
|
archiveNumbering ="Date" archiveEvery="Day"
|
||||||
|
archiveDateFormat="yyyyMMdd"/>
|
||||||
|
|
||||||
|
<target name="viewer"
|
||||||
|
xsi:type="NLogViewer"
|
||||||
|
includeSourceInfo="true"
|
||||||
|
address="udp://FDESINB0166:9999"/>
|
||||||
|
|
||||||
|
</targets>
|
||||||
|
|
||||||
|
<!-- rules to map from logger name to target -->
|
||||||
|
<rules>
|
||||||
|
<logger name="*"
|
||||||
|
minlevel="Trace"
|
||||||
|
writeTo="logfile,logconsole,console" />
|
||||||
|
|
||||||
|
<logger name="*"
|
||||||
|
minlevel="Debug"
|
||||||
|
writeTo="viewer" />
|
||||||
|
</rules>
|
||||||
|
</nlog>
|
||||||
Reference in New Issue
Block a user