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); }
public static void Main(string[] args) { bool running = true; // use Ctrl-C to stop the programm Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; running = false; }; string portName = "/dev/ttyUSB3"; if (args.Length > 0) { portName = args [0]; } SerialPort port = new SerialPort(); port.PortName = portName; port.BaudRate = 9600; port.Parity = Parity.Even; port.Handshake = Handshake.None; port.Open(); LinkLayerParameters llParameters = new LinkLayerParameters(); llParameters.AddressLength = 1; llParameters.UseSingleCharACK = true; CS101Slave slave = new CS101Slave(port, llParameters); slave.DebugOutput = true; slave.LinkLayerAddress = 1; // for using the slave in balanced mode simple change the mode here: slave.LinkLayerMode = lib60870.linklayer.LinkLayerMode.UNBALANCED; slave.SetInterrogationHandler(myInterrogationHandler, null); slave.SetUserDataQueueSizes(50, 20); ASDU asdu = new ASDU(slave.Parameters, CauseOfTransmission.SPONTANEOUS, false, false, 0, 1, false); asdu.AddInformationObject(new StepPositionInformation(301, 1, false, new QualityDescriptor())); slave.EnqueueUserDataClass1(asdu); long lastTimestamp = SystemUtils.currentTimeMillis(); Int16 measuredValue = 0; TransparentFile file = new TransparentFile(1, 30000, NameOfFile.TRANSPARENT_FILE); byte[] fileData = new byte[1025]; for (int i = 0; i < 1025; i++) { fileData [i] = (byte)(i + 1); } file.AddSection(fileData); slave.GetAvailableFiles().AddFile(file); while (running) { slave.Run(); // call the protocol stack if ((SystemUtils.currentTimeMillis() - lastTimestamp) >= 5000) { lastTimestamp = SystemUtils.currentTimeMillis(); ASDU newAsdu = new ASDU(slave.Parameters, CauseOfTransmission.PERIODIC, false, false, 0, 1, false); newAsdu.AddInformationObject(new MeasuredValueScaled(110, measuredValue, new QualityDescriptor())); slave.EnqueueUserDataClass2(newAsdu); measuredValue++; } if (Console.KeyAvailable) { ConsoleKeyInfo keyInfo = Console.ReadKey(); if (keyInfo.KeyChar == 't') { slave.SendLinkLayerTestFunction(); } else { Console.WriteLine("Send spontaneous message"); bool value = false; if (keyInfo.KeyChar == 's') { value = true; } ASDU newAsdu = new ASDU(slave.Parameters, CauseOfTransmission.SPONTANEOUS, false, false, 0, 1, false); newAsdu.AddInformationObject(new SinglePointInformation(100, value, new QualityDescriptor())); slave.EnqueueUserDataClass1(newAsdu); } } } }
public static void Main(string[] args) { bool running = true; // use Ctrl-C to stop the programm Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; running = false; }; LinkLayerParameters llParameters = new LinkLayerParameters(); llParameters.AddressLength = 1; llParameters.TimeoutForACK = 500; llParameters.UseSingleCharACK = true; TcpServerVirtualSerialPort port = new TcpServerVirtualSerialPort(); //TcpClientVirtualSerialPort port = new TcpClientVirtualSerialPort("192.168.2.9", 2404); port.DebugOutput = true; port.Start(); CS101Slave slave = new CS101Slave(port, llParameters); slave.DebugOutput = true; slave.LinkLayerAddress = 3; slave.LinkLayerAddressOtherStation = 1; slave.LinkLayerMode = lib60870.linklayer.LinkLayerMode.BALANCED; slave.SetInterrogationHandler(myInterrogationHandler, null); slave.SetUserDataQueueSizes(50, 20); ASDU asdu = new ASDU(slave.Parameters, CauseOfTransmission.SPONTANEOUS, false, false, 0, 1, false); asdu.AddInformationObject(new StepPositionInformation(301, 1, false, new QualityDescriptor())); slave.EnqueueUserDataClass1(asdu); long lastTimestamp = SystemUtils.currentTimeMillis(); Int16 measuredValue = 0; TransparentFile file = new TransparentFile(1, 30000, NameOfFile.TRANSPARENT_FILE); byte[] fileData = new byte[1025]; for (int i = 0; i < 1025; i++) { fileData [i] = (byte)(i + 1); } file.AddSection(fileData); slave.GetAvailableFiles().AddFile(file); while (running) { slave.Run(); // call the protocol stack if ((SystemUtils.currentTimeMillis() - lastTimestamp) >= 5000) { lastTimestamp = SystemUtils.currentTimeMillis(); ASDU newAsdu = new ASDU(slave.Parameters, CauseOfTransmission.PERIODIC, false, false, 0, 1, false); newAsdu.AddInformationObject(new MeasuredValueScaled(110, measuredValue, new QualityDescriptor())); slave.EnqueueUserDataClass2(newAsdu); measuredValue++; } if (Console.KeyAvailable) { ConsoleKeyInfo keyInfo = Console.ReadKey(); if (keyInfo.KeyChar == 't') { slave.SendLinkLayerTestFunction(); } else { Console.WriteLine("Send spontaneous message"); bool value = false; if (keyInfo.KeyChar == 's') { value = true; } ASDU newAsdu = new ASDU(slave.Parameters, CauseOfTransmission.SPONTANEOUS, false, false, 0, 1, false); newAsdu.AddInformationObject(new SinglePointInformation(100, value, new QualityDescriptor())); slave.EnqueueUserDataClass1(newAsdu); } } Thread.Sleep(1); } port.Stop(); }