static void Main(string[] args) { Log("{json:scada} IEC60870-5-104 Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); if (args.Length > 0) // first argument in number of the driver instance { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if (args.Length > 1) // second argument is logLevel { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); var Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <IEC10X_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (IEC10X_connection isrv in conns) { if (isrv.ipAddresses.Length < 1) { Log("Missing ipAddresses list!"); Environment.Exit(-1); } IEC10Xconns.Add(isrv); Log(isrv.name.ToString() + " - New Connection"); } if (IEC10Xconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to process redundancy control Thread thrMongoRedundacy = new Thread(() => ProcessRedundancyMongo(JSConfig)); thrMongoRedundacy.Start(); // start thread to update acquired data to database Thread thrMongo = new Thread(() => ProcessMongo(JSConfig)); thrMongo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCmd = new Thread(() => ProcessMongoCmd(JSConfig)); thrMongoCmd.Start(); Log("Setting up IEC Connections & ASDU handlers..."); int cntIecSrv = 0; foreach (IEC10X_connection srv in IEC10Xconns) { var apcipars = new APCIParameters(); apcipars.K = srv.k; apcipars.W = srv.w; apcipars.T0 = srv.t0; apcipars.T1 = srv.t1; apcipars.T2 = srv.t2; apcipars.T3 = srv.t3; var alpars = new ApplicationLayerParameters(); alpars.SizeOfCOT = srv.sizeOfCOT; alpars.SizeOfCA = srv.sizeOfCA; alpars.SizeOfIOA = srv.sizeOfIOA; alpars.OA = srv.localLinkAddress; var tcpPort = 2404; string[] ipAddrPort = srv.ipAddresses[0].Split(':'); if (ipAddrPort.Length > 1) { if (int.TryParse(ipAddrPort[1], out _)) { tcpPort = System.Convert.ToInt32(ipAddrPort[1]); } } var con = new Connection(ipAddrPort[0], tcpPort, apcipars, alpars); con.Parameters.OA = srv.localLinkAddress; srv.connection = con; srv.CntGI = srv.giInterval - 3; srv.CntTestCommand = srv.testCommandInterval - 1; srv.CntTimeSync = 0; srv.CntTestCommandSeq = 0; if (LogLevel >= LogLevelDebug) { con.DebugOutput = true; } con.SetASDUReceivedHandler(AsduReceivedHandler, cntIecSrv); con.SetConnectionHandler(ConnectionHandler, cntIecSrv); // create timer to increment counters each second srv.TimerCnt = new System.Timers.Timer(); srv.TimerCnt.Interval = 1000; srv.TimerCnt.Elapsed += (sender, e) => MyElapsedMethod(sender, e, srv);
static void Main(string[] args) { Log("{json:scada} IEC60870-5-101 Server Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); if ( args.Length > 0 // first argument in number of the driver instance ) { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if ( args.Length > 1 // second argument is logLevel ) { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); // connect to MongoDB Database server Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <IEC10X_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (IEC10X_connection isrv in conns) { IEC10Xconns.Add(isrv); Log(isrv.name.ToString()); } if (IEC10Xconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to dequeue iec data and send to connections Thread thrDeqIecInfo = new Thread(() => DequeueIecInfo()); thrDeqIecInfo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCS = new Thread(() => ProcessMongoCS(JSConfig)); thrMongoCS.Start(); Log("Setting up IEC Connections & ASDU handlers..."); int cntIecSrv = 0; foreach (IEC10X_connection srv in IEC10Xconns) { TcpClientVirtualSerialPort virtualPort = null; SerialPort port = null; if (srv.portName.Contains(":")) { var hostport = srv.portName.Split(":"); virtualPort = new TcpClientVirtualSerialPort(hostport[0], System.Convert.ToInt32(hostport[1])); if (LogLevel >= LogLevelDebug) { virtualPort.DebugOutput = true; } virtualPort.Start(); } else { port = new SerialPort(); port.PortName = srv.portName; port.BaudRate = srv.baudRate; switch (srv.parity.ToLower()) { default: // Even is the starndard parity for 101 case "even": port.Parity = Parity.Even; break; case "none": port.Parity = Parity.None; break; case "odd": port.Parity = Parity.Odd; break; case "mark": port.Parity = Parity.Mark; break; case "space": port.Parity = Parity.Space; break; } switch (srv.stopBits.ToLower()) { default: case "one": port.StopBits = StopBits.One; break; case "one5": case "onepointfive": port.StopBits = StopBits.OnePointFive; break; case "two": port.StopBits = StopBits.Two; break; } switch (srv.handshake.ToLower()) { default: case "none": port.Handshake = Handshake.None; break; case "xon": case "xonxoff": port.Handshake = Handshake.XOnXOff; break; case "rts": case "requesttosend": port.Handshake = Handshake.RequestToSend; break; case "rtsxon": case "requesttosendxonxoff": port.Handshake = Handshake.RequestToSendXOnXOff; break; } port.Open(); port.DiscardInBuffer(); } LinkLayerParameters llParameters = new LinkLayerParameters(); llParameters.AddressLength = srv.sizeOfLinkAddress; llParameters.TimeoutForACK = srv.timeoutForACK; llParameters.TimeoutRepeat = srv.timeoutRepeat; llParameters.UseSingleCharACK = srv.useSingleCharACK; CS101Slave slave; if (port != null) { slave = new CS101Slave(port, llParameters); } else { slave = new CS101Slave(virtualPort, llParameters); } slave.Parameters.SizeOfCOT = srv.sizeOfCOT; slave.Parameters.SizeOfCA = srv.sizeOfCA; slave.Parameters.SizeOfIOA = srv.sizeOfIOA; slave.Parameters.OA = srv.localLinkAddress; if (LogLevel >= LogLevelDebug) { slave.DebugOutput = true; } slave.LinkLayerAddress = srv.localLinkAddress; slave.LinkLayerMode = lib60870.linklayer.LinkLayerMode.UNBALANCED; slave.SetInterrogationHandler(InterrogationHandler, cntIecSrv); slave.SetUserDataQueueSizes(srv.maxQueueSize, srv.maxQueueSize); srv.server = slave; slave.SetASDUHandler(AsduReceivedHandler, cntIecSrv); // slave.Start(); Log(srv.name + " - New server listening on " + srv.portName); cntIecSrv++; } Thread.Sleep(1000); bool running = true; Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; running = false; }; Log("Press [CTRL]+[C] to terminate..."); int cnt = 1; do { try { foreach (IEC10X_connection srv in IEC10Xconns) { srv.server.Run(); } if (Client == null) { // retry connection IsMongoLive = false; Client = new MongoClient(JSConfig.mongoConnectionString); DB = Client.GetDatabase(JSConfig.mongoDatabaseName); IsMongoLive = true; } if ((cnt % 20) == 0) // each 1 second test mongo connection { IsMongoLive = DB .RunCommandAsync((Command <BsonDocument>) "{ping:1}") .Wait(1000); if (!IsMongoLive) { throw new Exception("Error on MongoDB connection "); } } } catch (Exception e) { // Disconnects to retry after some time IsMongoLive = false; Client = null; Log("Exception"); Log(e); Log(e .ToString() .Substring(0, e.ToString().IndexOf(Environment.NewLine))); System.Threading.Thread.Sleep(100); } Thread.Sleep(50); cnt++; }while (running); Log("Exiting application!"); Environment.Exit(0); }
static void Main(string[] args) { Log("{json:scada} IEC60870-5-104 Server Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); if ( args.Length > 0 // first argument in number of the driver instance ) { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if ( args.Length > 1 // second argument is logLevel ) { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); // connect to MongoDB Database server Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <IEC10X_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (IEC10X_connection isrv in conns) { IEC10Xconns.Add(isrv); Log(isrv.name.ToString()); } if (IEC10Xconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to dequeue iec data and send to connections Thread thrDeqIecInfo = new Thread(() => DequeueIecInfo()); thrDeqIecInfo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCS = new Thread(() => ProcessMongoCS(JSConfig)); thrMongoCS.Start(); Log("Setting up IEC Connections & ASDU handlers..."); int cntIecSrv = 0; foreach (IEC10X_connection srv in IEC10Xconns) { var apcipars = new APCIParameters(); apcipars.K = srv.k; apcipars.W = srv.w; apcipars.T0 = srv.t0; apcipars.T1 = srv.t1; apcipars.T2 = srv.t2; apcipars.T3 = srv.t3; var alpars = new ApplicationLayerParameters(); alpars.SizeOfCOT = srv.sizeOfCOT; alpars.SizeOfCA = srv.sizeOfCA; alpars.SizeOfIOA = srv.sizeOfIOA; alpars.OA = srv.localLinkAddress; var server = new Server(apcipars, alpars); srv.server = server; if (srv.serverModeMultiActive) { server.ServerMode = ServerMode.CONNECTION_IS_REDUNDANCY_GROUP; } else { server.ServerMode = ServerMode.SINGLE_REDUNDANCY_GROUP; } var localBindIpAddress = "0.0.0.0"; var tcpPort = 2404; string[] ipAddrPort = srv.ipAddressLocalBind.Split(':'); if (ipAddrPort.Length > 0) { if (ipAddrPort[0] != "") { localBindIpAddress = ipAddrPort[0]; } } if (ipAddrPort.Length > 1) { if (int.TryParse(ipAddrPort[1], out _)) { tcpPort = System.Convert.ToInt32(ipAddrPort[1]); } } server.SetLocalAddress(localBindIpAddress); server.SetLocalPort(tcpPort); //RedundancyGroup redGroup = new RedundancyGroup("catch all"); //server.AddRedundancyGroup(redGroup); if (LogLevel >= LogLevelDebug) { server.DebugOutput = true; } server.MaxQueueSize = srv.maxQueueSize; server.MaxOpenConnections = srv.maxClientConnections; Log(srv.name + " - Max Queue Size: " + server.MaxQueueSize); Log(srv.name + " - Max Client Connections: " + server.MaxOpenConnections); server.SetConnectionRequestHandler( ConnectionRequestHandler, cntIecSrv ); server.SetConnectionEventHandler( ConnectionEventHandler, cntIecSrv ); server.SetInterrogationHandler( InterrogationHandler, cntIecSrv ); server.SetASDUHandler(AsduReceivedHandler, cntIecSrv); server.Start(); Log(srv.name + " - New server listening on " + localBindIpAddress + ":" + tcpPort); cntIecSrv++; } Thread.Sleep(1000); bool running = true; Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; running = false; }; Log("Press [CTRL]+[C] to terminate..."); do { try { if (Client == null) { // retry connection Client = new MongoClient(JSConfig.mongoConnectionString); DB = Client.GetDatabase(JSConfig.mongoDatabaseName); } IsMongoLive = DB .RunCommandAsync((Command <BsonDocument>) "{ping:1}") .Wait(10000); if (!IsMongoLive) { throw new Exception("Error on MongoDB connection "); } //foreach (IEC104_connection srv in IEC10Xconns) //{ //} } catch (Exception e) { // Disconnects to retry after some time Client = null; Log("Exception Mongo"); Log(e); Log(e .ToString() .Substring(0, e.ToString().IndexOf(Environment.NewLine))); System.Threading.Thread.Sleep(3000); } Thread.Sleep(1000); }while (running); Log("Exiting application!"); Environment.Exit(0); /* Synchronize clock of the controlled station */ //con.SendClockSyncCommand(1 /* CA */, new CP56Time2a(DateTime.Now)); }
public static void Main(string[] args) { /* * * //Instantiate the tag with the proper mapper and datatype * var myTag = new Tag<RealPlcMapper, float>() * { * Name = "PLC1FLOAT0", * Gateway = "127.0.0.1", * Path = "1,0", * PlcType = PlcType.ControlLogix, * Protocol = Protocol.ab_eip, * UseConnectedMessaging = true, * Timeout = TimeSpan.FromSeconds(5) * }; * * //Initialize the tag to set up structures and prepare for read/write * //This is optional as an optimization before using the tag * //If omitted, the tag will initialize on the first Read() or Write() * myTag.Initialize(); * * //The value is held locally and only synchronized on Read() or Write() * myTag.Value = (float)3337.431; * * //Transfer Value to PLC * myTag.Write(); * * var myTag2 = new Tag<DintPlcMapper, int>() * { * Name = "PLC1ANA1", * Gateway = "127.0.0.1", * Path = "1,0", * PlcType = PlcType.ControlLogix, * UseConnectedMessaging = true, * Protocol = Protocol.ab_eip, * Timeout = TimeSpan.FromSeconds(5) * }; * * //Initialize the tag to set up structures and prepare for read/write * //This is optional as an optimization before using the tag * //If omitted, the tag will initialize on the first Read() or Write() * myTag2.Initialize(); * * //The value is held locally and only synchronized on Read() or Write() * myTag2.Value = 2233; * * //Transfer Value to PLC * myTag2.Write(); * * var myTag3 = new Tag<RealPlcMapper, float[]>() * { * Name = "SCADA", * Gateway = "127.0.0.1", * Path = "1,0", * PlcType = PlcType.ControlLogix, * UseConnectedMessaging = true, * Protocol = Protocol.ab_eip, * ArrayDimensions = new int[] { 10, 10 }, * Timeout = TimeSpan.FromSeconds(5) * }; * * //Initialize the tag to set up structures and prepare for read/write * //This is optional as an optimization before using the tag * //If omitted, the tag will initialize on the first Read() or Write() * myTag3.Initialize(); * * //The value is held locally and only synchronized on Read() or Write() * myTag3.Value = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; * * //Transfer Value to PLC * myTag3.Write(); * * myTag.Dispose(); * myTag2.Dispose(); * myTag3.Dispose(); */ Log("{json:scada} PLC TAG Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using libplctag version " + LibPlcTag.VersionMajor + "." + LibPlcTag.VersionMinor + "." + LibPlcTag.VersionPatch); if (args.Length > 0) // first argument in number of the driver instance { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if (args.Length > 1) // second argument is logLevel { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); var Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <PLC_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (PLC_connection isrv in conns) { if (isrv.ipAddresses.Length < 1) { Log("Missing ipAddresses list!"); Environment.Exit(-1); } PLCconns.Add(isrv); Log(isrv.name.ToString() + " - New Connection"); } if (PLCconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } switch (LogLevel) { case LogLevelNoLog: LibPlcTag.DebugLevel = DebugLevel.Detail; break; default: case LogLevelBasic: LibPlcTag.DebugLevel = DebugLevel.Warn; break; case LogLevelDetailed: LibPlcTag.DebugLevel = DebugLevel.Info; break; case LogLevelDebug: LibPlcTag.DebugLevel = DebugLevel.Detail; break; } // start thread to process redundancy control Thread thrMongoRedundacy = new Thread(() => ProcessRedundancyMongo(JSConfig)); thrMongoRedundacy.Start(); // start thread to update acquired data to database Thread thrMongo = new Thread(() => ProcessMongo(JSConfig)); thrMongo.Start(); Log("Setting up IEC Connections & ASDU handlers..."); foreach (PLC_connection srv in PLCconns) { var collection = DB.GetCollection <rtData>(RealtimeDataCollectionName); var filter = Builders <rtData> .Filter.Eq("protocolSourceConnectionNumber", srv.protocolConnectionNumber); var documents = collection.Find(filter).ToList(); var enumerator = documents.GetEnumerator(); var tags = Enumerable.Range(0, documents.Count) .Select(i => { enumerator.MoveNext(); var plctp = PlcType.ControlLogix; switch (srv.plc.ToLower()) { default: case "lgx": case "compactlogix": case "contrologix": case "controllogix": plctp = PlcType.ControlLogix; break; case "pccc": case "lgxpccc": case "logixpccc": plctp = PlcType.LogixPccc; break; case "omron": case "omronnjnx": case "omron-njnx": case "micro800": case "micrologix800": case "mlgx800": plctp = PlcType.Micro800; break; case "mlgx": case "micrologix": plctp = PlcType.MicroLogix; break; case "plc5": plctp = PlcType.Plc5; break; case "slc500": plctp = PlcType.Slc500; break; } if (enumerator.Current.protocolSourceASDU.ToString().Contains("[") && enumerator.Current.protocolSourceASDU.ToString().Contains("]")) { var p1 = enumerator.Current.protocolSourceASDU.ToString().IndexOf("["); var p2 = enumerator.Current.protocolSourceASDU.ToString().IndexOf("]"); var p3 = enumerator.Current.protocolSourceObjectAddress.ToString().IndexOf("["); var datatype = enumerator.Current.protocolSourceASDU.ToString().Substring(0, p1).ToLower(); var arrlens = enumerator.Current.protocolSourceASDU.ToString().Substring(p1 + 1, p2 - p1 - 1); var arrlen = System.Convert.ToInt32(arrlens); switch (datatype) { case "bool": case "boolean": { var tag = new Tag <BoolPlcMapper, bool[]>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString().Substring(0, p3), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ArrayDimensions = new int[] { arrlen, 1 }, ReadCacheMillisecondDuration = srv.readCacheMs }; var tagFound = false; // avoid tag re-insertion foreach (var tg in srv.listBoolArrayTags) { if (tg.Name == tag.Name) { tagFound = true; } } if (!tagFound) { tag.Initialize(); srv.listBoolArrayTags.Add(tag); } } break; case "byte": case "sint": case "int8": { var tag = new Tag <SintPlcMapper, sbyte[]>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString().Substring(0, p3), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ArrayDimensions = new int[] { arrlen, 1 }, ReadCacheMillisecondDuration = srv.readCacheMs }; var tagFound = false; // avoid tag re-insertion foreach (var tg in srv.listSintArrayTags) { if (tg.Name == tag.Name) { tagFound = true; } } if (!tagFound) { tag.Initialize(); srv.listSintArrayTags.Add(tag); } } break; default: case "int": case "int16": { var tag = new Tag <IntPlcMapper, short[]>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString().Substring(0, p3), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ArrayDimensions = new int[] { arrlen, 1 }, ReadCacheMillisecondDuration = srv.readCacheMs }; var tagFound = false; // avoid tag re-insertion foreach (var tg in srv.listIntArrayTags) { if (tg.Name == tag.Name) { tagFound = true; } } if (!tagFound) { tag.Initialize(); srv.listIntArrayTags.Add(tag); } } break; case "dint": case "int32": { var tag = new Tag <DintPlcMapper, int[]>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString().Substring(0, p3), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ArrayDimensions = new int[] { arrlen, 1 }, ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listDintArrayTags.Add(tag); var tagFound = false; // avoid tag re-insertion foreach (var tg in srv.listDintArrayTags) { if (tg.Name == tag.Name) { tagFound = true; } } if (!tagFound) { tag.Initialize(); srv.listDintArrayTags.Add(tag); } } break; case "lint": case "int64": { var tag = new Tag <LintPlcMapper, long[]>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString().Substring(0, p3), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ArrayDimensions = new int[] { arrlen, 1 }, ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listLintArrayTags.Add(tag); var tagFound = false; // avoid tag re-insertion foreach (var tg in srv.listLintArrayTags) { if (tg.Name == tag.Name) { tagFound = true; } } if (!tagFound) { tag.Initialize(); srv.listLintArrayTags.Add(tag); } } break; case "real": case "float32": { var tag = new Tag <RealPlcMapper, float[]>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString().Substring(0, p3), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ArrayDimensions = new int[] { arrlen, 1 }, }; var tagFound = false; // avoid tag re-insertion foreach (var tg in srv.listRealArrayTags) { if (tg.Name == tag.Name) { tagFound = true; } } if (!tagFound) { tag.Initialize(); srv.listRealArrayTags.Add(tag); } } break; case "lreal": case "float64": { var tag = new Tag <LrealPlcMapper, double[]>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString().Substring(0, p1 + 1), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ArrayDimensions = new int[] { arrlen, 1 }, ReadCacheMillisecondDuration = srv.readCacheMs }; var tagFound = false; // avoid tag re-insertion foreach (var tg in srv.listLrealArrayTags) { if (tg.Name == tag.Name) { tagFound = true; } } if (!tagFound) { tag.Initialize(); srv.listLrealArrayTags.Add(tag); } } break; } } else { switch (enumerator.Current.protocolSourceASDU.ToString().ToLower()) { case "bool": case "boolean": { var tag = new Tag <BoolPlcMapper, bool>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString(), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listBoolTags.Add(tag); } break; case "byte": case "sint": case "int8": { var tag = new Tag <SintPlcMapper, sbyte>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString(), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listSintTags.Add(tag); } break; default: case "int": case "int16": { var tag = new Tag <IntPlcMapper, short>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString(), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listIntTags.Add(tag); } break; case "dint": case "int32": { var tag = new Tag <DintPlcMapper, int>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString(), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listDintTags.Add(tag); } break; case "lint": case "int64": { var tag = new Tag <LintPlcMapper, long>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString(), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listLintTags.Add(tag); } break; case "real": case "float32": { var tag = new Tag <RealPlcMapper, float>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString(), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listRealTags.Add(tag); } break; case "lreal": case "float64": { var tag = new Tag <LrealPlcMapper, double>() { Name = enumerator.Current.protocolSourceObjectAddress.ToString(), Gateway = srv.ipAddresses[0], Path = enumerator.Current.protocolSourceCommonAddress.ToString(), PlcType = plctp, Protocol = (srv.protocol.ToLower() == "modbus") ? Protocol.modbus_tcp : Protocol.ab_eip, UseConnectedMessaging = srv.useConnectedMsg, Timeout = TimeSpan.FromMilliseconds(1000), ReadCacheMillisecondDuration = srv.readCacheMs }; tag.Initialize(); srv.listLrealTags.Add(tag); } break; } } return(0); }) .ToList(); // A thread for scanning each connection Thread thrPlcScan = new Thread(() => ProcessPLCScan(srv)); thrPlcScan.Start(); } // start thread to watch for commands in the database using a change stream Thread thrMongoCmd = new Thread(() => ProcessMongoCmd(JSConfig)); thrMongoCmd.Start(); do { Thread.Sleep(500); if (!Console.IsInputRedirected) { if (Console.KeyAvailable) { if (Console.ReadKey().Key == ConsoleKey.Escape) { Log("Exiting application!"); Environment.Exit(0); } else { Log("Press 'Esc' key to terminate..."); } } } } while (true); }
static int Main(string[] args) { Log(DriverMessage); Log("Driver version " + DriverVersion); Log("Using opendnp3 version 3.1.1"); if (args.Length > 0) // first argument in number of the driver instance { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if (args.Length > 1) // second argument is logLevel { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); var Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <DNP3_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (DNP3_connection isrv in conns) { if (isrv.ipAddresses.Length < 1 && isrv.portName.Trim() == "") { Log("No ipAddresses or port name defined on conenction! " + isrv.name); Environment.Exit(-1); } DNP3conns.Add(isrv); Log(isrv.name.ToString() + " - New Connection"); } if (DNP3conns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to process redundancy control Thread thrMongoRedundacy = new Thread(() => ProcessRedundancyMongo(JSConfig)); thrMongoRedundacy.Start(); // start thread to update acquired data to database Thread thrMongo = new Thread(() => ProcessMongo(JSConfig)); thrMongo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCmd = new Thread(() => ProcessMongoCmd(JSConfig)); thrMongoCmd.Start(); Log("Setting up connections & ASDU handlers..."); mgr = DNP3ManagerFactory.CreateManager(2 * Environment.ProcessorCount, new PrintingLogAdapter()); foreach (DNP3_connection srv in DNP3conns) { uint logLevel = LogLevels.NONE; if (LogLevel >= LogLevelBasic) { logLevel = LogLevels.NORMAL; } if (LogLevel >= LogLevelDetailed) { logLevel = LogLevels.NORMAL | LogLevels.APP_COMMS; } if (LogLevel >= LogLevelDebug) { logLevel = LogLevels.ALL; } MyChannelListener chlistener = new MyChannelListener(); chlistener.dnp3conn = srv; IChannel channel = null; // can be tcp, udp, tls, or serial if (srv.ipAddresses.Length > 0) // TCP, TLS or UDP { if (srv.ipAddressLocalBind.Trim() != "") { // UDP // look for the same channel config already created (multi-drop case) // if found, just reuse foreach (DNP3_connection conn in DNP3conns) { if (!(conn.channel is null)) { if (conn.ipAddressLocalBind.Trim() != "" && srv.ipAddressLocalBind.Trim() == conn.ipAddressLocalBind.Trim() && srv.ipAddresses[0] == conn.ipAddresses[0]) { channel = conn.channel; break; } } } if (!(channel is null)) { Log(srv.name + " - Reusing channel..."); } else { Log(srv.name + " - Creating UDP channel..."); ushort localUdpPort = 20000; string[] localIpAddrPort = srv.ipAddressLocalBind.Split(':'); if (localIpAddrPort.Length > 1) { if (int.TryParse(localIpAddrPort[1], out _)) { localUdpPort = System.Convert.ToUInt16(localIpAddrPort[1]); } } ushort remoteUdpPort = 20000; string[] remoteIpAddrPort = srv.ipAddresses[0].Split(':'); if (remoteIpAddrPort.Length > 1) { if (int.TryParse(remoteIpAddrPort[1], out _)) { remoteUdpPort = System.Convert.ToUInt16(remoteIpAddrPort[1]); } } channel = mgr.AddUDPChannel( "UDP:" + srv.name, logLevel, new ChannelRetry(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(30), TimeSpan.FromSeconds(5)), new IPEndpoint(localIpAddrPort[0], localUdpPort), new IPEndpoint(remoteIpAddrPort[0], remoteUdpPort), chlistener ); } } else { // TCP or TLS ushort tcpPort = 20000; string[] ipAddrPort = srv.ipAddresses[0].Split(':'); if (ipAddrPort.Length > 1) { if (int.TryParse(ipAddrPort[1], out _)) { tcpPort = System.Convert.ToUInt16(ipAddrPort[1]); } } // look for the same channel config already created (multi-drop case) // if found, just reuse foreach (DNP3_connection conn in DNP3conns) { if (!(conn.channel is null)) { if (srv.ipAddresses.SequenceEqual(conn.ipAddresses)) { channel = conn.channel; break; } } } if (!(channel is null)) { Log(srv.name + " - Reusing channel..."); }
public static Int32 BulkWriteLimit = 1000; // limit of each bulk write to mongodb //public static int OPCDefaultPublishingInterval = 2500; //public static int OPCDefaultSamplingInterval = 1000; //public static uint OPCDefaultQueueSize = 10; public static int Main(string[] args) { Log("{json:scada} OPC-UA Client Driver - Copyright 2021 RLO"); Log("Driver version " + DriverVersion); Log("Using UA-.NETStandard library from the OPC Foundation."); if (args.Length > 0) // first argument in number of the driver instance { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if (args.Length > 1) // second argument is logLevel { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); var Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <OPCUA_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (OPCUA_connection isrv in conns) { if (isrv.endpointURLs.Length < 1) { Log("Missing remote endpoint URLs list!"); Environment.Exit(-1); } OPCUAconns.Add(isrv); Log(isrv.name.ToString() + " - New Connection"); } if (OPCUAconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to process redundancy control var thrMongoRedundacy = new Thread(() => ProcessRedundancyMongo(JSConfig)); thrMongoRedundacy.Start(); // start thread to update acquired data to database var thrMongo = new Thread(() => ProcessMongo(JSConfig)); thrMongo.Start(); // thrMongo.Priority = ThreadPriority.AboveNormal; // start thread to watch for commands in the database using a change stream //var thrMongoCmd = // new Thread(() => // ProcessMongoCmd(JSConfig)); //thrMongoCmd.Start(); Log("Setting up OPC-UA Connections & ASDU handlers..."); var thrSrvs = new List <Thread>(); foreach (OPCUA_connection srv in OPCUAconns) { srv.connection = new OPCUAClient(srv); // srv.connection.Run(); srv.thrOPCStack = new Thread(() => srv.connection.Run()); srv.thrOPCStack.Start(); } Thread.Sleep(1000); do { foreach (OPCUA_connection srv in OPCUAconns) { } Thread.Sleep(1000); if (!Console.IsInputRedirected) { if (Console.KeyAvailable) { if (Console.ReadKey().Key == ConsoleKey.Escape) { Log("Exiting application!"); Environment.Exit(0); } else { Log("Press 'Esc' key to terminate..."); } } } } while (true); // return (int)OPCUAClient.ExitCode; }
static void Main(string[] args) { Log("{json:scada} IEC60870-5-104 Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); if (args.Length > 0) // first argument in number of the driver instance { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if (args.Length > 1) // second argument is logLevel { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); var Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <IEC10X_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (IEC10X_connection isrv in conns) { if (isrv.ipAddresses.Length < 1) { Log("Missing ipAddresses list!"); Environment.Exit(-1); } IEC10Xconns.Add(isrv); Log(isrv.name.ToString() + " - New Connection"); } if (IEC10Xconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to process redundancy control Thread thrMongoRedundacy = new Thread(() => ProcessRedundancyMongo(JSConfig)); thrMongoRedundacy.Start(); // start thread to update acquired data to database Thread thrMongo = new Thread(() => ProcessMongo(JSConfig)); thrMongo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCmd = new Thread(() => ProcessMongoCmd(JSConfig)); thrMongoCmd.Start(); Log("Setting up IEC Connections & ASDU handlers..."); int cntIecSrv = 0; foreach (IEC10X_connection srv in IEC10Xconns) { var apcipars = new APCIParameters(); apcipars.K = srv.k; apcipars.W = srv.w; apcipars.T0 = srv.t0; apcipars.T1 = srv.t1; apcipars.T2 = srv.t2; apcipars.T3 = srv.t3; var alpars = new ApplicationLayerParameters(); alpars.SizeOfCOT = srv.sizeOfCOT; alpars.SizeOfCA = srv.sizeOfCA; alpars.SizeOfIOA = srv.sizeOfIOA; alpars.OA = srv.localLinkAddress; TlsSecurityInformation secInfo = null; if (srv.localCertFilePath != "") { try { // Own certificate has to be a pfx file that contains the private key X509Certificate2 ownCertificate = new X509Certificate2(srv.localCertFilePath); // Create a new security information object to configure TLS secInfo = new TlsSecurityInformation(null, ownCertificate); // Add allowed server certificates - not required when AllowOnlySpecificCertificates == false secInfo.AddAllowedCertificate(new X509Certificate2(srv.peerCertFilePath)); // Add a CA certificate to check the certificate provided by the server - not required when ChainValidation == false secInfo.AddCA(new X509Certificate2(srv.rootCertFilePath)); // Check if the certificate is signed by a provided CA secInfo.ChainValidation = srv.chainValidation; // Check that the shown server certificate is in the list of allowed certificates secInfo.AllowOnlySpecificCertificates = srv.allowOnlySpecificCertificates; } catch (Exception e) { Log(srv.name + " - Error configuring TLS certficates."); Log(srv.name + " - " + e.Message); Environment.Exit(1); } } var tcpPort = 2404; string[] ipAddrPort = srv.ipAddresses[0].Split(':'); if (ipAddrPort.Length > 1) { if (int.TryParse(ipAddrPort[1], out _)) { tcpPort = System.Convert.ToInt32(ipAddrPort[1]); } } var con = new Connection(ipAddrPort[0], tcpPort, apcipars, alpars); con.Parameters.OA = srv.localLinkAddress; srv.conn1 = con; srv.conn2 = con; srv.connection = con; srv.CntGI = srv.giInterval - 3; srv.CntTestCommand = srv.testCommandInterval - 1; srv.CntTimeSync = 0; srv.CntTestCommandSeq = 0; if (LogLevel >= LogLevelDebug) { con.DebugOutput = true; } con.SetASDUReceivedHandler(AsduReceivedHandler, cntIecSrv); con.SetConnectionHandler(ConnectionHandler, cntIecSrv); if (srv.ipAddresses.Length > 1) // is there a secondary server ? { string[] ipAddrPort2 = srv.ipAddresses[1].Split(':'); if (ipAddrPort2.Length > 1) { if (int.TryParse(ipAddrPort2[1], out _)) { tcpPort = System.Convert.ToInt32(ipAddrPort2[1]); } } var c2 = new Connection(ipAddrPort2[0], tcpPort, apcipars, alpars); con.Parameters.OA = srv.localLinkAddress; srv.conn2 = c2; srv.connection = c2; // force initial swap to primary server if (LogLevel >= LogLevelDebug) { c2.DebugOutput = true; } c2.SetASDUReceivedHandler(AsduReceivedHandler, cntIecSrv); c2.SetConnectionHandler(ConnectionHandler, cntIecSrv); } if (srv.localCertFilePath != "" && secInfo != null) { srv.conn1.SetTlsSecurity(secInfo); srv.conn2.SetTlsSecurity(secInfo); } // create timer to increment counters each second srv.TimerCnt = new System.Timers.Timer(); srv.TimerCnt.Interval = 1000; srv.TimerCnt.Elapsed += (sender, e) => MyElapsedMethod(sender, e, srv);
public static void Main(string[] args) { Log("{json:scada} IEC60870-5-101 Driver - Copyright 2020 RLO"); Log("Driver version " + DriverVersion); Log("Using lib60870.NET version " + LibraryCommon.GetLibraryVersionString()); if (args.Length > 0) // first argument in number of the driver instance { int num; bool res = int.TryParse(args[0], out num); if (res) { ProtocolDriverInstanceNumber = num; } } if (args.Length > 1) // second argument is logLevel { int num; bool res = int.TryParse(args[1], out num); if (res) { LogLevel = num; } } string fname = JsonConfigFilePath; if (args.Length > 2) // third argument is config file name { if (File.Exists(args[2])) { fname = args[2]; } } if (!File.Exists(fname)) { fname = JsonConfigFilePathAlt; } if (!File.Exists(fname)) { Log("Missing config file " + JsonConfigFilePath); Environment.Exit(-1); } Log("Reading config file " + fname); string json = File.ReadAllText(fname); JSConfig = JsonSerializer.Deserialize <JSONSCADAConfig>(json); if ( JSConfig.mongoConnectionString == "" || JSConfig.mongoConnectionString == null ) { Log("Missing MongoDB connection string in JSON config file " + fname); Environment.Exit(-1); } // Log("MongoDB connection string: " + JSConfig.mongoConnectionString); if ( JSConfig.mongoDatabaseName == "" || JSConfig.mongoDatabaseName == null ) { Log("Missing MongoDB database name in JSON config file " + fname); Environment.Exit(-1); } Log("MongoDB database name: " + JSConfig.mongoDatabaseName); if (JSConfig.nodeName == "" || JSConfig.nodeName == null) { Log("Missing nodeName parameter in JSON config file " + fname); Environment.Exit(-1); } Log("Node name: " + JSConfig.nodeName); // connect to MongoDB Database server var Client = ConnectMongoClient(JSConfig); var DB = Client.GetDatabase(JSConfig.mongoDatabaseName); // read and process instances configuration var collinsts = DB .GetCollection <protocolDriverInstancesClass >(ProtocolDriverInstancesCollectionName); var instances = collinsts .Find(inst => inst.protocolDriver == ProtocolDriverName && inst.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && inst.enabled == true) .ToList(); var foundInstance = false; foreach (protocolDriverInstancesClass inst in instances) { if ( ProtocolDriverName == inst.protocolDriver && ProtocolDriverInstanceNumber == inst.protocolDriverInstanceNumber ) { foundInstance = true; if (!inst.enabled) { Log("Driver instance [" + ProtocolDriverInstanceNumber.ToString() + "] disabled!"); Environment.Exit(-1); } Log("Instance: " + inst.protocolDriverInstanceNumber.ToString()); var nodefound = false; foreach (var name in inst.nodeNames) { if (JSConfig.nodeName == name) { nodefound = true; } } if (!nodefound) { Log("Node '" + JSConfig.nodeName + "' not found in instances configuration!"); Environment.Exit(-1); } DriverInstance = inst; break; } break; // process just first result } if (!foundInstance) { Log("Driver instance [" + ProtocolDriverInstanceNumber + "] not found in configuration!"); Environment.Exit(-1); } // read and process connections configuration for this driver instance var collconns = DB .GetCollection <IEC10X_connection>(ProtocolConnectionsCollectionName); var conns = collconns .Find(conn => conn.protocolDriver == ProtocolDriverName && conn.protocolDriverInstanceNumber == ProtocolDriverInstanceNumber && conn.enabled == true) .ToList(); foreach (IEC10X_connection isrv in conns) { IEC10Xconns.Add(isrv); Log(isrv.name.ToString() + " - New Connection"); } if (IEC10Xconns.Count == 0) { Log("No connections found!"); Environment.Exit(-1); } // start thread to process redundancy control Thread thrMongoRedundacy = new Thread(() => ProcessRedundancyMongo(JSConfig)); thrMongoRedundacy.Start(); // start thread to update acquired data to database Thread thrMongo = new Thread(() => ProcessMongo(JSConfig)); thrMongo.Start(); // start thread to watch for commands in the database using a change stream Thread thrMongoCmd = new Thread(() => ProcessMongoCmd(JSConfig)); thrMongoCmd.Start(); Log("Setting up IEC Connections & ASDU handlers..."); int cntIecSrv = 0; foreach (IEC10X_connection srv in IEC10Xconns) { TcpClientVirtualSerialPort virtualPort = null; SerialPort port = null; if (srv.portName.Contains(":")) { var hostport = srv.portName.Split(":"); virtualPort = new TcpClientVirtualSerialPort(hostport[0], System.Convert.ToInt32(hostport[1])); if (LogLevel >= LogLevelDebug) { virtualPort.DebugOutput = true; } virtualPort.Start(); } else { port = new SerialPort(); port.PortName = srv.portName; port.BaudRate = srv.baudRate; switch (srv.parity.ToLower()) { default: // Even is the starndard parity for 101 case "even": port.Parity = Parity.Even; break; case "none": port.Parity = Parity.None; break; case "odd": port.Parity = Parity.Odd; break; case "mark": port.Parity = Parity.Mark; break; case "space": port.Parity = Parity.Space; break; } switch (srv.stopBits.ToLower()) { default: case "one": port.StopBits = StopBits.One; break; case "one5": case "onepointfive": port.StopBits = StopBits.OnePointFive; break; case "two": port.StopBits = StopBits.Two; break; } switch (srv.handshake.ToLower()) { default: case "none": port.Handshake = Handshake.None; break; case "xon": case "xonxoff": port.Handshake = Handshake.XOnXOff; break; case "rts": case "requesttosend": port.Handshake = Handshake.RequestToSend; break; case "rtsxon": case "requesttosendxonxoff": port.Handshake = Handshake.RequestToSendXOnXOff; break; } port.Open(); port.DiscardInBuffer(); } LinkLayerParameters llParameters = new LinkLayerParameters(); llParameters.AddressLength = srv.sizeOfLinkAddress; llParameters.TimeoutForACK = srv.timeoutForACK; llParameters.TimeoutRepeat = srv.timeoutRepeat; llParameters.UseSingleCharACK = srv.useSingleCharACK; ApplicationLayerParameters alpars = new ApplicationLayerParameters(); alpars.SizeOfCOT = srv.sizeOfCOT; alpars.SizeOfCA = srv.sizeOfCA; alpars.SizeOfIOA = srv.sizeOfIOA; alpars.OA = srv.localLinkAddress; CS101Master master; if (port != null) { Log("Serial Port: " + srv.portName); master = new CS101Master(port, LinkLayerMode.UNBALANCED, llParameters, alpars); } else { Log("Virtual Serial Port: " + srv.portName); master = new CS101Master(virtualPort, LinkLayerMode.UNBALANCED, llParameters, alpars); } // master.OwnAddress = srv.localLinkAddress; master.SetTimeouts(srv.timeoutMessage, srv.timeoutCharacter); master.AddSlave(srv.remoteLinkAddress); master.SlaveAddress = srv.remoteLinkAddress; srv.master = master; srv.CntGI = srv.giInterval - 5; srv.CntTestCommand = srv.testCommandInterval - 2; srv.CntTimeSync = srv.timeSyncInterval; if (LogLevel >= LogLevelDebug) { master.DebugOutput = true; } master.SetASDUReceivedHandler(AsduReceivedHandlerPre, cntIecSrv); master.SetLinkLayerStateChangedHandler(linkLayerStateChanged, cntIecSrv); master.SetReceivedRawMessageHandler(rcvdRawMessageHandler, cntIecSrv); master.Start(); // create timer to increment counters each second srv.TimerCnt = new System.Timers.Timer(); srv.TimerCnt.Interval = 1000; srv.TimerCnt.Elapsed += (sender, e) => MyElapsedMethod(sender, e, srv);