Files
mapo-mono/MP.MONO.ADAPTER.OPC/Program.cs
T
2022-06-21 11:22:51 +02:00

399 lines
15 KiB
C#

using System;
using System.Globalization;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Opc.Ua;
using Opc.Ua.Configuration;
using Microsoft.Extensions.Configuration;
using MP.MONO.ADAPTER;
using MP.MONO.Core;
using MP.MONO.Core.CONF;
using MP.MONO.Core.DTO;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System.Collections.Generic;
using static MP.MONO.Core.Enums;
namespace MP.MONO.ADAPTER.OPC
{
/// <summary>
/// The program.
/// </summary>
public static class Program
{
private static string redisConf = "";
private static string confPath = "";
private static string lineSep = "---------------------------------------------";
private static Logger Log = LogManager.GetCurrentClassLogger();
private static bool verboseLog = false;
private static bool logWriting = false;
private static AlarmReportingMode alarmMode = AlarmReportingMode.ND;
/// <summary>
/// Redis channel manager
/// </summary>
private static ISubscriber sub { get; set; }
/// <summary>
/// Redis DB
/// </summary>
private static IDatabase? redisDb { get; set; }
private static Dictionary<string, int> LogSimulator = new Dictionary<string, int>();
private static Dictionary<string, DateTime> LastSend = new Dictionary<string, DateTime>();
private static DateTime lastLog = DateTime.Now.AddMinutes(-1);
/// <summary>
/// Main entry point.
/// </summary>
public static async Task Main(string[] args)
{
TextWriter output = Console.Out;
output.WriteLine("Egalware | MP.MONO.ADAPTER | OPC UA Console Client");
output.WriteLine("OPC UA library: {0} @ {1} -- {2}",
Utils.GetAssemblyBuildNumber(),
Utils.GetAssemblyTimestamp().ToString("G", CultureInfo.InvariantCulture),
Utils.GetAssemblySoftwareVersion());
// init parte config, vedere https://blog.hildenco.com/2020/05/configuration-in-net-core-console.html
var env = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
var builder = new ConfigurationBuilder()
.AddJsonFile($"appsettings.json", true, true)
.AddJsonFile($"appsettings.{env}.json", true, true)
.AddEnvironmentVariables();
var config = builder.Build();
// imposto variabili di base
redisConf = config.GetConnectionString("Redis");
confPath = Path.Combine(Directory.GetCurrentDirectory(), "conf");
Random rand = new Random();
List<MachineStatus>? statusList = new List<MachineStatus>();
List<MachineMode>? modeList = new List<MachineMode>();
// fix numero minimo dei thread pool x evitare collasso chiamate redis
ThreadPool.SetMinThreads(10, 10);
logInfo(lineSep, true, true);
logInfo($"Starting Machine ADAPTER", true, true);
logInfo($"Redis server param: {redisConf.Substring(0, 20)}...", false, true);
logInfo(lineSep, true, true);
logInfo("", true, true);
// Setup REDIS
ConnectionMultiplexer.SetFeatureFlag("preventthreadtheft", true);
ConnectionMultiplexer redis = ConnectionMultiplexer.Connect(redisConf);
sub = redis.GetSubscriber();
redisDb = redis.GetDatabase();
// salvo configurazioni in redis
setupConfRedis();
// modalità gestione allarmi
alarmMode = config.GetValue<AlarmReportingMode>("OptPar:AlarmMode");
await redisDb.StringSetAsync(Constants.ALARMS_MODE_KEY, JsonConvert.SerializeObject(alarmMode));
// avvio il vero e proprio programma di comunicaizone OPC-UA
var currIob = new IobOpcUa(confPath, config);
logInfo($"Started IOB at path {confPath}", true, true);
#if false
// The application name and config file names
var applicationName = "MP.MONO.ADAPTER.OPC";
var configSectionName = "MpMonoAdapterOpc";
var usage = $"Usage: dotnet {applicationName}.dll [OPTIONS]";
// command line options
bool showHelp = false;
bool autoAccept = false;
bool logConsole = false;
bool appLog = false;
bool renewCertificate = false;
string password = null;
int timeout = Timeout.Infinite;
Mono.Options.OptionSet options = new Mono.Options.OptionSet {
usage,
{ "h|help", "show this message and exit", h => showHelp = h != null },
{ "a|autoaccept", "auto accept certificates (for testing only)", a => autoAccept = a != null },
{ "c|console", "log to console", c => logConsole = c != null },
{ "l|log", "log app output", c => appLog = c != null },
{ "p|password=", "optional password for private key", (string p) => password = p },
{ "r|renew", "renew application certificate", r => renewCertificate = r != null },
{ "t|timeout=", "timeout in seconds to exit application", (int t) => timeout = t * 1000 },
};
try
{
// parse command line and set options
var extraArg = ConsoleUtils.ProcessCommandLine(output, args, options, ref showHelp, false);
// connect Url?
Uri serverUrl = new Uri("opc.tcp://localhost:62541/Quickstarts/ReferenceServer");
if (!string.IsNullOrEmpty(extraArg))
{
serverUrl = new Uri(extraArg);
}
// log console output to logger
if (logConsole && appLog)
{
output = new LogWriter();
}
// Define the UA Client application
ApplicationInstance.MessageDlg = new ApplicationMessageDlg(output);
CertificatePasswordProvider PasswordProvider = new CertificatePasswordProvider(password);
ApplicationInstance application = new ApplicationInstance
{
ApplicationName = applicationName,
ApplicationType = ApplicationType.Client,
ConfigSectionName = configSectionName,
CertificatePasswordProvider = PasswordProvider
};
// load the application configuration.
var config = await application.LoadApplicationConfiguration(silent: false);
// setup the logging
ConsoleUtils.ConfigureLogging(config, applicationName, logConsole, Microsoft.Extensions.Logging.LogLevel.Information);
// delete old certificate
if (renewCertificate)
{
await application.DeleteApplicationInstanceCertificate().ConfigureAwait(false);
}
// check the application certificate.
bool haveAppCertificate = await application.CheckApplicationInstanceCertificate(false, minimumKeySize: 0).ConfigureAwait(false);
if (!haveAppCertificate)
{
throw new ErrorExitException("Application instance certificate invalid!", ExitCode.ErrorCertificate);
}
// wait for timeout or Ctrl-C
var quitEvent = ConsoleUtils.CtrlCHandler();
// connect to a server until application stopped
bool quit = false;
DateTime start = DateTime.UtcNow;
int waitTime = int.MaxValue;
do
{
if (timeout > 0)
{
waitTime = timeout - (int)DateTime.UtcNow.Subtract(start).TotalMilliseconds;
if (waitTime <= 0)
{
break;
}
}
// create the UA Client object and connect to configured server.
UAClient uaClient = new UAClient(application.ApplicationConfiguration, output, ClientBase.ValidateResponse)
{
AutoAccept = autoAccept
};
bool connected = await uaClient.ConnectAsync(serverUrl.ToString());
if (connected)
{
// Run tests for available methods.
uaClient.ReadNodes();
uaClient.WriteNodes();
uaClient.Browse();
uaClient.CallMethod();
uaClient.SubscribeToDataChanges();
// Wait for some DataChange notifications from MonitoredItems
quit = quitEvent.WaitOne(Math.Min(30_000, waitTime));
uaClient.Disconnect();
}
else
{
output.WriteLine("Could not connect to server! Retry in 10 seconds or Ctrl-C to quit.");
quit = quitEvent.WaitOne(Math.Min(10_000, waitTime));
}
} while (!quit);
output.WriteLine("\nClient stopped.");
}
catch (Exception ex)
{
output.WriteLine(ex.Message);
}
#endif
logInfo("Running - press CTRL-C to stop ADAPTER", false, true);
logInfo("", false, true);
// wait for timeout or Ctrl-C
var quitEvent = ConsoleUtils.CtrlCHandler();
bool quit = false;
DateTime start = DateTime.UtcNow;
int waitTime = int.MaxValue;
// Ciclo infinito x attesa chiusura con CTRL-C
do
{
// se non fosse connesso... riprovo la connessione...
if (!currIob.connectionOk)
{
currIob.tryConnect();
}
// attesa...
Thread.Sleep(100);
// verifico se c'è evento quit
quit = quitEvent.WaitOne(Math.Min(1_000, waitTime));
} while (!quit);
// disconnetto alla fine...
if (currIob.connectionOk)
{
currIob.tryDisconnect();
}
}
/// <summary>
/// verifica esistenza file oppure lo crea...
/// </summary>
private static void checkFilePresent(string filePath)
{
// verific presenza file log...
if (!File.Exists(filePath))
{
File.WriteAllText(filePath, $"{filePath} created!");
}
}
/// <summary>
/// Setup e salvataggio redis delle conf (es modi/stati)
/// </summary>
private static void setupConfRedis()
{
ConfigManager configManager = new ConfigManager(redisConf, confPath);
if (alarmMode == AlarmReportingMode.RawList)
{
_ = configManager.getAlarmsRawConf();
}
else
{
_ = configManager.getAlarmsBankConf();
}
// altre conf
_ = configManager.getMachineModeConf();
_ = configManager.getMachineStatusConf();
_ = configManager.getParamsConf();
}
/// <summary>
/// Effettua log INFO su file e se richiesto su console
/// </summary>
private static void logInfo(string msg, bool log2file = true, bool log2console = false)
{
if (log2console)
{
Console.WriteLine(msg);
}
if (log2file)
{
Log.Info(msg);
}
}
/// <summary>
/// Effettua log ERROR su file e se richiesto su console
/// </summary>
private static void logError(string msg, bool log2file = true, bool log2console = false)
{
if (log2console)
{
Console.WriteLine(msg);
}
if (log2file)
{
Log.Error(msg);
}
}
private static void saveAndSendMessage(string memKey, string value, string notifyChannel, string message)
{
// effettuo la scrittura nell'area di memoria indicata SE passato intervallo minimo
bool doSend = true;
if (LastSend.ContainsKey(memKey))
{
if (DateTime.Now.Subtract(LastSend[memKey]).TotalSeconds < 60)
{
doSend = false;
}
}
else
{
LastSend.Add(memKey, DateTime.Now);
}
if (doSend)
{
redisDb.StringSetAsync(memKey, value);
LastSend[memKey] = DateTime.Now;
logInfo($"Redis Cache Key: {memKey}");
}
//redisDb.SetAdd(memKey, value);
// invio notifica tramite il canale richiesto
sub.Publish(notifyChannel, message);
if (verboseLog)
{
logInfo($"[{notifyChannel}] key: {memKey} | val: {value} | message: {message}");
}
else
{
try
{
if (!logWriting)
{
if (LogSimulator.ContainsKey(notifyChannel))
{
LogSimulator[notifyChannel]++;
}
else
{
LogSimulator.Add(notifyChannel, 1);
}
logWriting = true;
// vedo se loggare...
DateTime adesso = DateTime.Now;
if (adesso.Subtract(lastLog).TotalSeconds > 15)
{
lastLog = adesso;
logInfo(lineSep);
// lavoro su copia...
var LogSimulatorCopy = new Dictionary<string, int>(LogSimulator);
foreach (var item in LogSimulatorCopy)
{
logInfo($"Redis mQueue {item.Key,-20}{item.Value,12}");
}
logInfo(lineSep);
}
logWriting = false;
}
}
catch (Exception ex)
{
logError($"ERROR{Environment.NewLine}{ex}");
}
}
}
}
}