internal ClientConnection(Socket socket, TlsSecurityInformation tlsSecInfo, APCIParameters apciParameters, ApplicationLayerParameters parameters, Server server, ASDUQueue asduQueue, bool debugOutput)
        {
            connectionsCounter++;
            connectionID = connectionsCounter;

            this.remoteEndpoint = (IPEndPoint)socket.RemoteEndPoint;

            this.apciParameters = apciParameters;
            this.alParameters   = parameters;
            this.server         = server;
            this.asduQueue      = asduQueue;
            this.debugOutput    = debugOutput;

            ResetT3Timeout();

            maxSentASDUs   = apciParameters.K;
            this.sentASDUs = new SentASDU[maxSentASDUs];

            //receivedASDUs = new ConcurrentQueue<ASDU>();
            receivedASDUs = new Queue <ASDU>();

            waitingASDUsHighPrio = new Queue <BufferFrame>();

            socketStream    = new NetworkStream(socket);
            this.socket     = socket;
            this.tlsSecInfo = tlsSecInfo;

            this.fileServer = new FileServer(this, server.GetAvailableFiles(), DebugLog);

            Thread workerThread = new Thread(HandleConnection);

            workerThread.Start();
        }
Exemple #2
0
        public override void Encode(Frame frame, ApplicationLayerParameters parameters, bool isSequence)
        {
            base.Encode(frame, parameters, isSequence);

            frame.SetNextByte((byte)(value % 0x100));
            frame.SetNextByte((byte)((value / 0x100) % 0x100));
            frame.SetNextByte((byte)((value / 0x10000) % 0x100));
            frame.SetNextByte((byte)(value / 0x1000000));
        }
        public Server(APCIParameters apciParameters, ApplicationLayerParameters alParameters, TlsSecurityInformation securityInfo)
        {
            this.apciParameters = apciParameters;
            this.alParameters   = alParameters;
            this.securityInfo   = securityInfo;

            if (securityInfo != null)
            {
                this.localPort = 19998;
            }
        }
Exemple #4
0
 private Integer32Object(ApplicationLayerParameters parameters, byte[] msg, int startIndex, bool isSequence)
     : base(parameters, msg, startIndex, isSequence)
 {
     if (!isSequence)
     {
         startIndex += parameters.SizeOfIOA;                 /* skip IOA */
     }
     value  = msg [startIndex++];
     value += ((int)msg [startIndex++] * 0x100);
     value += ((int)msg [startIndex++] * 0x10000);
     value += ((int)msg [startIndex++] * 0x1000000);
 }
        public ASDUQueue(int maxQueueSize, ApplicationLayerParameters parameters, Action <string> DebugLog)
        {
            enqueuedASDUs = new ASDUQueueEntry[maxQueueSize];

            for (int i = 0; i < maxQueueSize; i++)
            {
                enqueuedASDUs[i].asdu  = new BufferFrame(new byte[260], 6);
                enqueuedASDUs[i].state = QueueEntryState.NOT_USED;
            }

            this.oldestQueueEntry    = -1;
            this.latestQueueEntry    = -1;
            this.numberOfAsduInQueue = 0;
            this.maxQueueSize        = maxQueueSize;
            this.parameters          = parameters;
            this.DebugLog            = DebugLog;
        }
Exemple #6
0
        private static bool interrogationHandler(object parameter, IMasterConnection connection, ASDU asdu, byte qoi)
        {
            Console.WriteLine("Interrogation for group " + qoi);

            ApplicationLayerParameters cp = connection.GetApplicationLayerParameters();

            connection.SendACT_CON(asdu, false);

            // send sequence of information objects
            ASDU newAsdu = new ASDU(cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 1, 1, true);

            rfile( );
            ToServer(ref newAsdu);

            connection.SendASDU(newAsdu);

            connection.SendACT_TERM(asdu);

            return(true);
        }
Exemple #7
0
 InformationObject IPrivateIOFactory.Decode(ApplicationLayerParameters parameters, byte[] msg, int startIndex, bool isSequence)
 {
     return(new Integer32Object(parameters, msg, startIndex, isSequence));
 }
        AsduReceivedHandler(object parameter, IMasterConnection connection, ASDU asdu)
        {
            var srv        = IEC10Xconns[(int)parameter];
            var conNameStr = srv.name + " - ";

            Log(conNameStr + asdu.ToString(), LogLevelDetailed);

            try
            {
                if (IsMongoLive)
                {
                    var DB             = Client.GetDatabase(JSConfig.mongoDatabaseName);
                    var collection_rtd =
                        DB
                        .GetCollection
                        <rtData>(RealtimeDataCollectionName);
                    Double   val        = 0;
                    Int32    objaddr    = 0;
                    Int32    dur        = 0;
                    Boolean  isselect   = false;
                    Boolean  cmdhastime = false;
                    DateTime cmdtime    = new DateTime();

                    Double  dstkconv1 = 1;
                    Double  dstkconv2 = 0;
                    Boolean dstsbo    = false;
                    Int32   dstdur    = 0;

                    Double  srcval      = 0;
                    Int32   srcobjaddr  = 0;
                    Int32   srcconn     = 0;
                    Boolean srcsbo      = false;
                    Int32   srcdur      = 0;
                    Int32   srcasdu     = 0;
                    Int32   srcca       = 0;
                    Int32   srcpointkey = 0;
                    String  srctag      = "";
                    Double  srckconv1   = 1;
                    Double  srckconv2   = 0;

                    switch (asdu.TypeId)
                    {
                    case TypeID.C_SC_NA_1:     // 45
                    {
                        var cmd = (SingleCommand)asdu.GetElement(0);
                        isselect = cmd.Select;
                        dur      = cmd.QU;
                        objaddr  = cmd.ObjectAddress;
                        val      = System.Convert.ToDouble(cmd.State);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_SC_TA_1:     // 58
                    {
                        var cmd = (SingleCommandWithCP56Time2a)asdu.GetElement(0);
                        isselect   = cmd.Select;
                        dur        = cmd.QU;
                        objaddr    = cmd.ObjectAddress;
                        val        = System.Convert.ToDouble(cmd.State);
                        cmdtime    = cmd.Timestamp.GetDateTime();
                        cmdhastime = true;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_DC_NA_1:     // 46
                    {
                        var cmd = (DoubleCommand)asdu.GetElement(0);
                        isselect = cmd.Select;
                        dur      = cmd.QU;
                        objaddr  = cmd.ObjectAddress;
                        if (cmd.State != DoubleCommand.ON && cmd.State != DoubleCommand.OFF)
                        {
                            connection.SendACT_CON(asdu, true);         // activation confirm negative
                            Log(conNameStr + "  Invalid double state command " + cmd.State, LogLevelBasic);
                            LastPointKeySelectedOk = 0;
                            return(true);
                        }
                        val = cmd.State == DoubleCommand.ON ? 1 : 0;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_DC_TA_1:     // 59
                    {
                        var cmd = (DoubleCommandWithCP56Time2a)asdu.GetElement(0);
                        isselect = cmd.Select;
                        dur      = cmd.QU;
                        objaddr  = cmd.ObjectAddress;
                        if (cmd.State != DoubleCommand.ON && cmd.State != DoubleCommand.OFF)
                        {
                            connection.SendACT_CON(asdu, true);         // activation confirm negative
                            Log(conNameStr + "  Invalid double state command " + cmd.State, LogLevelBasic);
                            LastPointKeySelectedOk = 0;
                            return(true);
                        }
                        cmdtime    = cmd.Timestamp.GetDateTime();
                        cmdhastime = true;
                        val        = cmd.State == DoubleCommand.ON ? 1 : 0;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_RC_NA_1:     // 47
                    {
                        var cmd = (StepCommand)asdu.GetElement(0);
                        isselect = cmd.Select;
                        dur      = cmd.QU;
                        objaddr  = cmd.ObjectAddress;
                        if (cmd.State != StepCommandValue.HIGHER && cmd.State != StepCommandValue.LOWER)
                        {
                            connection.SendACT_CON(asdu, true);         // activation confirm negative
                            Log(conNameStr + "  Invalid step state command " + cmd.State, LogLevelBasic);
                            LastPointKeySelectedOk = 0;
                            return(true);
                        }
                        val = cmd.State == StepCommandValue.HIGHER ? 1 : 0;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_RC_TA_1:     // 60
                    {
                        var cmd = (StepCommandWithCP56Time2a)asdu.GetElement(0);
                        isselect = cmd.Select;
                        dur      = cmd.QU;
                        objaddr  = cmd.ObjectAddress;
                        if (cmd.State != StepCommandValue.HIGHER && cmd.State != StepCommandValue.LOWER)
                        {
                            connection.SendACT_CON(asdu, true);         // activation confirm negative
                            Log(conNameStr + "  Invalid step state command " + cmd.State, LogLevelBasic);
                            LastPointKeySelectedOk = 0;
                            return(true);
                        }
                        cmdtime    = cmd.Timestamp.GetDateTime();
                        cmdhastime = true;
                        val        = cmd.State == StepCommandValue.HIGHER ? 1 : 0;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_SE_NA_1:     // 48
                    {
                        var cmd = (SetpointCommandNormalized)asdu.GetElement(0);
                        isselect = cmd.QOS.Select;
                        dur      = cmd.QOS.QL;
                        objaddr  = cmd.ObjectAddress;
                        val      = cmd.NormalizedValue;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_SE_TA_1:     // 61
                    {
                        var cmd = (SetpointCommandNormalizedWithCP56Time2a)asdu.GetElement(0);
                        isselect   = cmd.QOS.Select;
                        dur        = cmd.QOS.QL;
                        objaddr    = cmd.ObjectAddress;
                        cmdtime    = cmd.Timestamp.GetDateTime();
                        cmdhastime = true;
                        val        = cmd.NormalizedValue;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_SE_NB_1:     // 49
                    {
                        var cmd = (SetpointCommandScaled)asdu.GetElement(0);
                        isselect = cmd.QOS.Select;
                        dur      = cmd.QOS.QL;
                        objaddr  = cmd.ObjectAddress;
                        val      = System.Convert.ToDouble(cmd.ScaledValue);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_SE_TB_1:     // 62
                    {
                        var cmd = (SetpointCommandScaledWithCP56Time2a)asdu.GetElement(0);
                        isselect   = cmd.QOS.Select;
                        dur        = cmd.QOS.QL;
                        objaddr    = cmd.ObjectAddress;
                        cmdtime    = cmd.Timestamp.GetDateTime();
                        cmdhastime = true;
                        val        = System.Convert.ToDouble(cmd.ScaledValue);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_SE_NC_1:     // 50
                    {
                        var cmd = (SetpointCommandShort)asdu.GetElement(0);
                        isselect = cmd.QOS.Select;
                        dur      = cmd.QOS.QL;
                        objaddr  = cmd.ObjectAddress;
                        val      = System.Convert.ToDouble(cmd.Value);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_SE_TC_1:     // 63
                    {
                        var cmd = (SetpointCommandShortWithCP56Time2a)asdu.GetElement(0);
                        isselect   = cmd.QOS.Select;
                        dur        = cmd.QOS.QL;
                        objaddr    = cmd.ObjectAddress;
                        cmdtime    = cmd.Timestamp.GetDateTime();
                        cmdhastime = true;
                        val        = System.Convert.ToDouble(cmd.Value);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_BO_NA_1:     // 51
                    {
                        var cmd = (Bitstring32Command)asdu.GetElement(0);
                        isselect = false;
                        dur      = 0;
                        objaddr  = cmd.ObjectAddress;
                        val      = System.Convert.ToDouble(cmd.Value);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_BO_TA_1:     // 64
                    {
                        var cmd = (Bitstring32CommandWithCP56Time2a)asdu.GetElement(0);
                        isselect   = false;
                        dur        = 0;
                        objaddr    = cmd.ObjectAddress;
                        cmdtime    = cmd.Timestamp.GetDateTime();
                        cmdhastime = true;
                        val        = System.Convert.ToDouble(cmd.Value);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.P_ME_NA_1:     // 110
                    {
                        var cmd = (ParameterNormalizedValue)asdu.GetElement(0);
                        isselect = false;
                        dur      = cmd.QPM;
                        objaddr  = cmd.ObjectAddress;
                        val      = System.Convert.ToDouble(cmd.NormalizedValue);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.P_ME_NB_1:     // 111
                    {
                        var cmd = (ParameterScaledValue)asdu.GetElement(0);
                        isselect = false;
                        dur      = cmd.QPM;
                        objaddr  = cmd.ObjectAddress;
                        val      = System.Convert.ToDouble(cmd.ScaledValue);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.P_ME_NC_1:     // 112
                    {
                        var cmd = (ParameterFloatValue)asdu.GetElement(0);
                        isselect = false;
                        dur      = cmd.QPM;
                        objaddr  = cmd.ObjectAddress;
                        val      = System.Convert.ToDouble(cmd.Value);
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.P_AC_NA_1:     // 113
                    {
                        var cmd = (ParameterActivation)asdu.GetElement(0);
                        isselect = false;
                        dur      = cmd.QPA;
                        objaddr  = cmd.ObjectAddress;
                        val      = 0;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_RP_NA_1:     // 105 reset process
                    {
                        var cmd = (ResetProcessCommand)asdu.GetElement(0);
                        isselect = false;
                        dur      = cmd.QRP;
                        objaddr  = cmd.ObjectAddress;
                        val      = 0;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    // case TypeID.C_IC_NA_1: // 100 already managed by interrogation handler function
                    case TypeID.C_TS_NA_1:     // 104 test command
                    {
                        Log(conNameStr + "  Test command C_TS_NA_1", LogLevelDetailed);
                        connection.SendACT_CON(asdu, false);         // activation confirm positive
                    }
                        return(true);

                    case TypeID.C_TS_TA_1:     // 107 test command
                    {
                        Log(conNameStr + "  Test command C_TS_TA_1", LogLevelDetailed);
                        connection.SendACT_CON(asdu, false);         // activation confirm positive
                    }
                        return(true);

                    case TypeID.C_CI_NA_1:     // 101
                    {
                        var cmd = (CounterInterrogationCommand)asdu.GetElement(0);
                        isselect = false;
                        dur      = cmd.QCC;
                        objaddr  = cmd.ObjectAddress;        // should be zero
                        val      = 0;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_RD_NA_1:     // 102
                    {
                        var cmd = (ReadCommand)asdu.GetElement(0);
                        objaddr = cmd.ObjectAddress;
                        Log(conNameStr + "  " + cmd.ToString() + " Obj Address " + objaddr, LogLevelBasic);
                    }
                    break;

                    case TypeID.C_CS_NA_1:     // 103 clock sync
                    {
                        ClockSynchronizationCommand qsc =
                            (ClockSynchronizationCommand)asdu.GetElement(0);
                        connection.SendACT_CON(asdu, false);         // activation confirm positive
                        Log(conNameStr + "  Received clock sync command with time " +
                            qsc.NewTime.ToString(), LogLevelBasic);
                        LastPointKeySelectedOk = 0;
                        return(true);
                    }

                    default:
                        connection.SendACT_CON(asdu, true);
                        Log(conNameStr + "  Not implemented type of ASDU received: " + asdu.TypeId, LogLevelBasic);
                        LastPointKeySelectedOk = 0;
                        return(true);
                    }

                    var filter1 = Builders <rtData> .Filter.Eq("protocolDestinations.protocolDestinationConnectionNumber", srv.protocolConnectionNumber);

                    var filter2 = Builders <rtData> .Filter.Eq("protocolDestinations.protocolDestinationCommonAddress", asdu.Ca);

                    var filter3 = Builders <rtData> .Filter.Eq("protocolDestinations.protocolDestinationObjectAddress", objaddr);

                    var filter4 = Builders <rtData> .Filter.Eq("protocolDestinations.protocolDestinationASDU", asdu.TypeId);

                    var filter = Builders <rtData>
                                 .Filter
                                 .And(filter1, filter2, filter3, filter4);

                    if (asdu.TypeId == TypeID.C_RD_NA_1)
                    { // READ COMMAND, look for object by object address of any type to send it
                        filter = Builders <rtData>
                                 .Filter
                                 .And(filter1, filter2, filter3);
                    }

                    var list =
                        collection_rtd.Find(filter).ToList();
                    if (list.Count > 0)
                    {
                        Log(conNameStr + "  Command found.", LogLevelBasic);
                        foreach (var dst in list[0].protocolDestinations)
                        {
                            if (dst.protocolDestinationConnectionNumber == srv.protocolConnectionNumber)
                            {
                                dstkconv1 = dst.protocolDestinationKConv1.ToDouble();
                                dstkconv2 = dst.protocolDestinationKConv2.ToDouble();
                                dstsbo    = dst.protocolDestinationCommandUseSBO.ToBoolean();
                                dstdur    = dst.protocolDestinationCommandDuration.ToInt32();

                                if (asdu.TypeId == TypeID.C_RD_NA_1)
                                {                                        // READ REQUEST
                                    connection.SendACT_CON(asdu, false); // activation confirm positive
                                    ApplicationLayerParameters cp =
                                        srv.server.GetApplicationLayerParameters();
                                    var newAsdu = new ASDU(cp,
                                                           CauseOfTransmission.REQUEST,
                                                           false,
                                                           false,
                                                           System.Convert.ToByte(dst.protocolDestinationCommonAddress),
                                                           dst.protocolDestinationCommonAddress.ToInt32(),
                                                           true);
                                    var quality = new QualityDescriptor();
                                    quality.Invalid = list[0].invalid.ToBoolean() ||
                                                      list[0].overflow.ToBoolean() ||
                                                      list[0].transient.ToBoolean();
                                    quality.Substituted = list[0].substituted.ToBoolean();
                                    quality.Blocked     = false;
                                    quality.NonTopical  = false;
                                    InformationObject io = BuildInfoObj(
                                        System.Convert.ToInt32(dst.protocolDestinationASDU),
                                        System.Convert.ToInt32(dst.protocolDestinationObjectAddress),
                                        list[0].value.ToDouble(),
                                        false,
                                        0,
                                        quality
                                        );
                                    if (io != null)
                                    {
                                        newAsdu.AddInformationObject(io);
                                        srv.server.EnqueueASDU(newAsdu);
                                    }
                                    return(true);
                                }

                                if (isselect && !dstsbo)
                                {                                       // tried a select when there is no select
                                    connection.SendACT_CON(asdu, true); // activation confirm negative
                                    Log(conNameStr + "  Select tried but not expected!", LogLevelBasic);
                                    LastPointKeySelectedOk = 0;
                                    return(true);
                                }

                                if (dur != dstdur)
                                {                                       // duration spec different than expected, reject commmand
                                    connection.SendACT_CON(asdu, true); // activation confirm negative
                                    Log(conNameStr + "  QU/QL command qualifier not expected: " + dur + ", " + dstdur + " wanted ", LogLevelBasic);
                                    LastPointKeySelectedOk = 0;
                                    return(true);
                                }

                                srcconn     = list[0].protocolSourceConnectionNumber.ToInt32();
                                srcdur      = list[0].protocolSourceCommandDuration.ToInt32();
                                srcobjaddr  = list[0].protocolSourceObjectAddress.ToInt32();
                                srcasdu     = list[0].protocolSourceASDU.ToInt32();
                                srcca       = list[0].protocolSourceCommonAddress.ToInt32();
                                srckconv1   = list[0].kconv1.ToDouble();
                                srckconv2   = list[0].kconv2.ToDouble();
                                srcpointkey = list[0]._id.ToInt32();
                                srctag      = list[0].tag.ToString();
                                break;
                            }
                        }
                    }
                    else
                    {
                        connection.SendACT_CON(asdu, true); // activation confirm negative
                        if (asdu.TypeId == TypeID.C_RD_NA_1)
                        {
                            Log(conNameStr + "  Request to read object not found, address: " + objaddr, LogLevelBasic);
                        }
                        else
                        {
                            Log(conNameStr + "  Command not found!", LogLevelBasic);
                        }
                        LastPointKeySelectedOk = 0;
                        return(true);
                    }

                    if (srcasdu == 0)
                    {
                        Log(conNameStr + "  Command rejected!", LogLevelBasic);
                        connection.SendACT_CON(asdu, true); // activation confirm negative
                        LastPointKeySelectedOk = 0;
                        return(true);
                    }

                    if (cmdhastime) // check command time
                    {
                        if (DateTime.Now.Subtract(cmdtime).TotalSeconds > timeToExpireCommandsWithTime)
                        { // expired
                            Log(conNameStr + "  Command with time expired after " +
                                timeToExpireCommandsWithTime + "s, diff: " +
                                (DateTime.Now.Subtract(cmdtime).TotalSeconds - timeToExpireCommandsWithTime) + "s",
                                LogLevelBasic);
                            connection.SendACT_CON(asdu, true); // activation confirm negative
                            LastPointKeySelectedOk = 0;
                            return(true);
                        }
                    }

                    connection.SendACT_CON(asdu, false); // activation confirm positive

                    if (isselect)
                    {
                        LastPointKeySelectedOk = srcpointkey; // flag selected point
                        Log(conNameStr + "  Select!", LogLevelBasic);
                        return(true);                         // do not forward a select
                    }

                    if (!isselect && dstsbo && LastPointKeySelectedOk != srcpointkey)
                    {  // tried execute without select first when there is select expected
                        connection.SendACT_CON(asdu, true);
                        Log(conNameStr + "  Tried execute without select first!", LogLevelBasic);
                        LastPointKeySelectedOk = 0;
                        return(true);
                    }
                    LastPointKeySelectedOk = 0;
                    Log(conNameStr + "  Execute (forward to queue)!", LogLevelBasic);

                    switch (asdu.TypeId)
                    {
                    case TypeID.C_SC_NA_1:     // 45
                    case TypeID.C_SC_TA_1:     // 58
                    case TypeID.C_DC_NA_1:     // 46
                    case TypeID.C_DC_TA_1:     // 59
                    case TypeID.C_RC_NA_1:     // 47
                    case TypeID.C_RC_TA_1:     // 60
                        if (dstkconv1 == -1)   // invert digital for kconv1 -1
                        {
                            val = val == 0 ? 1 : 0;
                        }
                        break;

                    case TypeID.C_SE_NA_1:     // 48
                    case TypeID.C_SE_TA_1:     // 61
                    case TypeID.C_SE_NB_1:     // 49
                    case TypeID.C_SE_TB_1:     // 62
                    case TypeID.C_SE_NC_1:     // 50
                    case TypeID.C_SE_TC_1:     // 63
                    case TypeID.P_ME_NA_1:     // 110
                    case TypeID.P_ME_NB_1:     // 111
                    case TypeID.P_ME_NC_1:     // 112
                    case TypeID.P_AC_NA_1:     // 113
                    case TypeID.C_RP_NA_1:     // 105
                        val = val * dstkconv1 + dstkconv2;
                        break;

                    case TypeID.C_BO_NA_1:     // 51
                    case TypeID.C_BO_TA_1:     // 64
                        if (dstkconv1 == -1)   // invert digital bits for kconv1 -1
                        {
                            val = System.Convert.ToInt32(val);
                        }
                        break;

                    default:
                        break;
                    }

                    switch ((TypeID)srcasdu)
                    {
                    case TypeID.C_SC_NA_1:     // 45
                    case TypeID.C_SC_TA_1:     // 58
                    case TypeID.C_DC_NA_1:     // 46
                    case TypeID.C_DC_TA_1:     // 59
                    case TypeID.C_RC_NA_1:     // 47
                    case TypeID.C_RC_TA_1:     // 60
                        if (srckconv1 == -1)   // invert digital for kconv1 -1
                        {
                            srcval = val == 0 ? 1 : 0;
                        }
                        else
                        {
                            srcval = val;
                        }
                        break;

                    case TypeID.C_SE_NA_1:     // 48
                    case TypeID.C_SE_TA_1:     // 61
                    case TypeID.C_SE_NB_1:     // 49
                    case TypeID.C_SE_TB_1:     // 62
                    case TypeID.C_SE_NC_1:     // 50
                    case TypeID.C_SE_TC_1:     // 63
                    case TypeID.P_ME_NA_1:     // 110
                    case TypeID.P_ME_NB_1:     // 111
                    case TypeID.P_ME_NC_1:     // 112
                    case TypeID.P_AC_NA_1:     // 113
                    case TypeID.C_RP_NA_1:     // 105
                        srcval = val * srckconv1 + srckconv2;
                        break;

                    case TypeID.C_BO_NA_1:     // 51
                    case TypeID.C_BO_TA_1:     // 64
                        if (srckconv1 == -1)   // invert digital bits for kconv1 -1
                        {
                            srcval = ~System.Convert.ToInt32(val);
                        }
                        else
                        {
                            srcval = System.Convert.ToInt32(val);
                        }
                        break;

                    default:
                        break;
                    }

                    // not sure how to detect the client connection as can be more than one
                    String orgip = "";
                    foreach (var conn in srv.clientConnections)
                    {
                        orgip = orgip + srv.clientConnections[0].RemoteEndpoint.ToString() + " ";
                    }

                    var collection_cmd =
                        DB
                        .GetCollection
                        <rtCommandNoAck>(CommandsQueueCollectionName);
                    var rtcmd = new rtCommandNoAck
                    {
                        protocolSourceConnectionNumber = srcconn,
                        protocolSourceCommonAddress    = srcca,
                        protocolSourceObjectAddress    = srcobjaddr,
                        protocolSourceASDU             = srcasdu,
                        protocolSourceCommandDuration  = srcdur,
                        protocolSourceCommandUseSBO    = srcsbo,
                        pointKey            = srcpointkey,
                        tag                 = srctag,
                        value               = srcval,
                        valueString         = srcval.ToString(),
                        originatorUserName  = "******" + srv.name,
                        originatorIpAddress = orgip,
                        timeTag             = DateTime.Now
                    };
                    collection_cmd.InsertOne(rtcmd);
                }
            }
            catch (Exception e)
            {
                Log("  Exception Mongo");
                Log(e);
                Log(e
                    .ToString()
                    .Substring(0,
                               e.ToString().IndexOf(Environment.NewLine)));
            }

            return(true);
        }
 /// <summary>
 /// Create a new server using the provided connection parameters.
 /// </summary>
 /// <param name="parameters">Connection parameters</param>
 public Server(APCIParameters apciParameters, ApplicationLayerParameters alParameters)
 {
     this.apciParameters = apciParameters;
     this.alParameters   = alParameters;
 }
Exemple #10
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));
        }
        InterrogationHandler(
            object parameter,
            IMasterConnection connection,
            ASDU asdu,
            byte qoi
            )
        {
            var srv        = IEC10Xconns[(int)parameter];
            var conNameStr = srv.name + " - ";

            Log(conNameStr + "[" + qoi + "] Group interrogation BEGIN", LogLevelBasic);

            try
            {
                var Client     = new MongoClient(JSConfig.mongoConnectionString);
                var DB         = Client.GetDatabase(JSConfig.mongoDatabaseName);
                var collection =
                    DB.GetCollection <rtData>(RealtimeDataCollectionName);

                ApplicationLayerParameters cp =
                    connection.GetApplicationLayerParameters();

                // query mongodb for all data to distribute in this connection

                // for group 20 (general interogation) request filter by all in destination connection but those marked with group -1
                // for other groups requests filter by by those marked with group this same group
                var filter_conn = Builders <rtData> .Filter.Eq("protocolDestinations.protocolDestinationConnectionNumber", srv.protocolConnectionNumber);

                var filter_cmd = Builders <rtData> .Filter.Ne("origin", "command");

                var filter_gen_int = Builders <rtData> .Filter.And(filter_conn, filter_cmd, Builders <rtData> .Filter.Ne("protocolDestinations.protocolDestinationGroup", -1));

                var filter_oth_grp = Builders <rtData> .Filter.And(filter_conn, filter_cmd,
                                                                   Builders <rtData> .Filter.Or(
                                                                       Builders <rtData> .Filter.Eq("protocolDestinations.protocolDestinationGroup", qoi), // accept 20-36 or 0-16
                                                                       Builders <rtData> .Filter.Eq("protocolDestinations.protocolDestinationGroup", qoi - 20)
                                                                       )
                                                                   );

                var sort = Builders <rtData> .Sort.Descending("$natural");

                var options = new FindOptions <rtData, rtData>();
                options.Sort = sort;
                var filter = filter_gen_int;
                if (qoi != 20)
                {
                    filter = filter_oth_grp;
                }
                var list =
                    collection.Find(filter).ToList();
                int CompareASDUInDest(rtData x, rtData y)
                {
                    var asdu1 = x.protocolDestinations[0].protocolDestinationASDU;

                    foreach (var dst in x.protocolDestinations)
                    {
                        if (dst.protocolDestinationConnectionNumber == srv.protocolConnectionNumber)
                        {
                            asdu1 = dst.protocolDestinationASDU;
                        }
                    }
                    var asdu2 = y.protocolDestinations[0].protocolDestinationASDU;

                    foreach (var dst in y.protocolDestinations)
                    {
                        if (dst.protocolDestinationConnectionNumber == srv.protocolConnectionNumber)
                        {
                            asdu2 = dst.protocolDestinationASDU;
                        }
                    }
                    if (asdu1 == asdu2)
                    {
                        return(0);
                    }
                    if (asdu1 > asdu2)
                    {
                        return(1);
                    }
                    return(-1);
                }

                list.Sort(CompareASDUInDest); // order by ASDU

                Log(conNameStr + "[" + qoi + "] Group request, " + list.Count() + " objects to send.", LogLevelBasic);

                connection.SendACT_CON(asdu, false); // confirm positive
                var  lastasdu   = -1;
                var  cntasduobj = 0;
                ASDU nwasdu     = null;
                foreach (rtData entry in list)
                {
                    Log(conNameStr + "[" + qoi + "] " + entry.tag.ToString() + " " + entry.value + " Key " + entry._id, LogLevelDetailed);
                    foreach (var dst in entry.protocolDestinations)
                    {
                        var q = new QualityDescriptor();
                        q.Invalid     = entry.invalid.ToBoolean();
                        q.Substituted = entry.substituted.ToBoolean();
                        q.NonTopical  = false;
                        q.Blocked     = false;
                        q.Overflow    = entry.overflow.ToBoolean();
                        if (dst.protocolDestinationConnectionNumber == srv.protocolConnectionNumber)
                        {
                            if ((lastasdu != dst.protocolDestinationASDU && cntasduobj > 0) || cntasduobj >= 30)
                            {
                                if (nwasdu != null)
                                {
                                    Log(conNameStr + "[" + qoi + "] Send ASDU TI:" + System.Convert.ToByte(nwasdu.TypeId) + "." + nwasdu.TypeId + " CA:" + nwasdu.Ca +
                                        " with " + nwasdu.NumberOfElements + " objects.");
                                    connection.SendASDU(nwasdu);
                                    nwasdu     = null;
                                    cntasduobj = 0;
                                    lastasdu   = -1;
                                }
                            }
                            switch (dst.protocolDestinationASDU.ToInt32())
                            {
                            case 1:
                            case 30:
                                if (cntasduobj == 0)
                                {
                                    nwasdu =
                                        new ASDU(cp,
                                                 (CauseOfTransmission)qoi,
                                                 false,
                                                 false,
                                                 System.Convert.ToByte(cp.OA),
                                                 System.Convert.ToInt32(dst.protocolDestinationCommonAddress.ToDouble()),
                                                 false);
                                }
                                if (nwasdu != null)
                                {
                                    var bval = entry.value != 0 ? true : false;
                                    if (dst.protocolDestinationKConv1 == -1)
                                    {
                                        bval = !bval;
                                    }
                                    nwasdu
                                    .AddInformationObject(new SinglePointInformation(System.Convert.ToInt32(dst.protocolDestinationObjectAddress.ToDouble()),
                                                                                     bval,
                                                                                     q));
                                    cntasduobj++;
                                }
                                break;

                            case 3:
                            case 31:
                                if (cntasduobj == 0)
                                {
                                    nwasdu =
                                        new ASDU(cp,
                                                 (CauseOfTransmission)qoi,
                                                 false,
                                                 false,
                                                 System.Convert.ToByte(cp.OA),
                                                 System.Convert.ToInt32(dst.protocolDestinationCommonAddress.ToDouble()),
                                                 false);
                                }
                                if (nwasdu != null)
                                {
                                    var dpval = entry.value != 0 ? DoublePointValue.ON : DoublePointValue.OFF;
                                    if (dst.protocolDestinationKConv1 == -1)
                                    {
                                        dpval = entry.value != 0 ? DoublePointValue.OFF : DoublePointValue.ON;
                                    }
                                    if (entry.transient.ToBoolean())
                                    {
                                        dpval = DoublePointValue.INTERMEDIATE;
                                    }
                                    nwasdu
                                    .AddInformationObject(new DoublePointInformation(System.Convert.ToInt32(dst.protocolDestinationObjectAddress.ToDouble()),
                                                                                     dpval,
                                                                                     q));
                                    cntasduobj++;
                                }
                                break;

                            case 5:
                            case 32:
                                if (cntasduobj == 0)
                                {
                                    nwasdu =
                                        new ASDU(cp,
                                                 (CauseOfTransmission)qoi,
                                                 false,
                                                 false,
                                                 System.Convert.ToByte(cp.OA),
                                                 System.Convert.ToInt32(dst.protocolDestinationCommonAddress.ToDouble()),
                                                 false);
                                }
                                if (nwasdu != null)
                                {
                                    var val = dst.protocolDestinationKConv1.ToDouble() * System.Convert.ToDouble(entry.value) + dst.protocolDestinationKConv2.ToDouble();
                                    if (val > 63)
                                    {
                                        val        = 63;
                                        q.Overflow = true;
                                    }
                                    else
                                    if (val < -64)
                                    {
                                        val        = -64;
                                        q.Overflow = true;
                                    }
                                    nwasdu
                                    .AddInformationObject(new StepPositionInformation(System.Convert.ToInt32(dst.protocolDestinationObjectAddress.ToDouble()),
                                                                                      System.Convert.ToInt16(val),
                                                                                      entry.transient.ToBoolean(),
                                                                                      q));
                                    cntasduobj++;
                                }
                                break;

                            case 9:
                            case 34:
                                if (cntasduobj == 0)
                                {
                                    nwasdu =
                                        new ASDU(cp,
                                                 (CauseOfTransmission)qoi,
                                                 false,
                                                 false,
                                                 System.Convert.ToByte(cp.OA),
                                                 System.Convert.ToInt32(dst.protocolDestinationCommonAddress.ToDouble()),
                                                 false);
                                }
                                if (nwasdu != null)
                                {
                                    var val = dst.protocolDestinationKConv1.ToDouble() * System.Convert.ToDouble(entry.value) + dst.protocolDestinationKConv2.ToDouble();
                                    if (val > 32767)
                                    {
                                        val        = 32767;
                                        q.Overflow = true;
                                    }
                                    else
                                    if (val < -32768)
                                    {
                                        val        = -32768;
                                        q.Overflow = true;
                                    }
                                    nwasdu
                                    .AddInformationObject(new MeasuredValueNormalized(System.Convert.ToInt32(dst.protocolDestinationObjectAddress.ToDouble()),
                                                                                      System.Convert.ToInt16(val),
                                                                                      new QualityDescriptor()));
                                    cntasduobj++;
                                }
                                break;

                            case 11:
                            case 35:
                                if (cntasduobj == 0)
                                {
                                    nwasdu =
                                        new ASDU(cp,
                                                 (CauseOfTransmission)qoi,
                                                 false,
                                                 false,
                                                 System.Convert.ToByte(cp.OA),
                                                 System.Convert.ToInt32(dst.protocolDestinationCommonAddress.ToDouble()),
                                                 false);
                                }
                                if (nwasdu != null)
                                {
                                    var val = dst.protocolDestinationKConv1.ToDouble() * System.Convert.ToDouble(entry.value) + dst.protocolDestinationKConv2.ToDouble();
                                    if (val > 32767)
                                    {
                                        val        = 32767;
                                        q.Overflow = true;
                                    }
                                    else
                                    if (val < -32768)
                                    {
                                        val        = -32768;
                                        q.Overflow = true;
                                    }
                                    nwasdu
                                    .AddInformationObject(new MeasuredValueScaled(System.Convert.ToInt32(dst.protocolDestinationObjectAddress.ToDouble()),
                                                                                  System.Convert.ToInt16(val),
                                                                                  new QualityDescriptor()));
                                    cntasduobj++;
                                }
                                break;

                            case 13:
                            case 36:
                                if (cntasduobj == 0)
                                {
                                    nwasdu =
                                        new ASDU(cp,
                                                 (CauseOfTransmission)qoi,
                                                 false,
                                                 false,
                                                 System.Convert.ToByte(cp.OA),
                                                 System.Convert.ToInt32(dst.protocolDestinationCommonAddress.ToDouble()),
                                                 false);
                                }
                                if (nwasdu != null)
                                {
                                    var val = dst.protocolDestinationKConv1.ToDouble() * System.Convert.ToDouble(entry.value) + dst.protocolDestinationKConv2.ToDouble();
                                    nwasdu
                                    .AddInformationObject(new MeasuredValueShort(System.Convert.ToInt32(dst.protocolDestinationObjectAddress.ToDouble()),
                                                                                 System.Convert.ToSingle(val),
                                                                                 new QualityDescriptor()));
                                    cntasduobj++;
                                }
                                break;

                            default:
                                break;
                            }
                            lastasdu = dst.protocolDestinationASDU.ToInt32();
                            break;
                        }
                    }
                }
                if (nwasdu != null)
                {
                    Log(conNameStr + "[" + qoi + "] Send ASDU TI:" + System.Convert.ToByte(nwasdu.TypeId) + "." + nwasdu.TypeId + " CA:" + nwasdu.Ca +
                        " with " + nwasdu.NumberOfElements + " objects.", LogLevelBasic);
                    connection.SendASDU(nwasdu);
                    nwasdu = null;
                }
                connection.SendACT_TERM(asdu);
                Log(conNameStr + "[" + qoi + "] Group interrogation END", LogLevelBasic);
            }
            catch (Exception e)
            {
                if (e.Message == "Connection not active")
                {
                    return(true);
                }

                Log("Exception on Interrogation");
                Log(e);
                Log(e
                    .ToString()
                    .Substring(0, e.ToString().IndexOf(Environment.NewLine)));
                System.Threading.Thread.Sleep(3000);
                connection.SendACT_CON(asdu, true); // negative confirm
                return(true);
            }

            return(true);
        }
Exemple #12
0
 // a process to dequeue iec data and send to connections
 static void DequeueIecInfo()
 {
     do
     {
         var sentSomething = false;
         foreach (IEC10X_connection
                  srv
                  in
                  IEC10Xconns
                  )
         {
             InfoCA ica;
             if (srv.infoCAQueue.TryDequeue(out ica))
             {
                 sentSomething = true;
                 var conNameStr = srv.name + " - ";
                 ApplicationLayerParameters alp =
                     srv.server.Parameters;
                 var newAsdu = new ASDU(alp,
                                        CauseOfTransmission.SPONTANEOUS,
                                        false,
                                        false,
                                        System.Convert.ToByte(alp.OA),
                                        ica.ca,
                                        false);
                 newAsdu.AddInformationObject(ica.io);
                 var    cnt = 1;
                 InfoCA ica2;
                 // keep the same asdu while same type and common address, and size fits
                 while (srv.infoCAQueue.TryPeek(out ica2))                             // do not remove from queue
                 {
                     if (ica2.ca == ica.ca && ica2.io.Type == ica.io.Type && cnt < 30) // check for same data type/ca and size
                     {
                         if (srv.infoCAQueue.TryDequeue(out ica2))                     // will remove from queue only to send
                         {
                             newAsdu.AddInformationObject(ica2.io);
                             cnt++;
                         }
                         else
                         {
                             break; // could not remove from queue, move on to enqueue asdu
                         }
                     }
                     else
                     {   // changed type or common address, move on to enqueue asdu
                         break;
                     }
                 }
                 srv.server.EnqueueUserDataClass1(newAsdu);
                 if (LogLevel >= LogLevelBasic)
                 {
                     Log(conNameStr + "Spont ASDU Type: " + ica.io.Type + " with " + newAsdu.NumberOfElements + " objects", LogLevelBasic);
                 }
             }
         }
         if (!sentSomething)    // if nothing was sent
         {
             Thread.Sleep(200); // let's give some time to wait for more data
         }
     } while (true);
 }
Exemple #13
0
        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);
Exemple #14
0
        public static void Main(string[] args)
        {
            bool running = true;

            Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
                e.Cancel = true;
                running  = false;
            };

            // specify application layer parameters (CS 101 and CS 104)
            var alParams = new ApplicationLayerParameters();

            alParams.SizeOfCA      = 2;
            alParams.SizeOfIOA     = 3;
            alParams.MaxAsduLength = 249;

            // specify APCI parameters (only CS 104)
            var apciParameters = new APCIParameters();

            apciParameters.K  = 12;
            apciParameters.W  = 8;
            apciParameters.T0 = 10;
            apciParameters.T1 = 15;
            apciParameters.T2 = 10;
            apciParameters.T3 = 20;

            Server server = new Server(apciParameters, alParams);

            server.DebugOutput = true;

            server.MaxQueueSize = 10;
            server.EnqueueMode  = EnqueueMode.REMOVE_OLDEST;

            server.SetInterrogationHandler(interrogationHandler, null);

            server.SetASDUHandler(asduHandler, null);

            server.Start();

            ASDU newAsdu            = new ASDU(server.GetApplicationLayerParameters(), CauseOfTransmission.INITIALIZED, false, false, 0, 1, false);
            EndOfInitialization eoi = new EndOfInitialization(0);

            newAsdu.AddInformationObject(eoi);
            server.EnqueueASDU(newAsdu);

            int waitTime = 1000;

            while (running)
            {
                Thread.Sleep(100);

                if (waitTime > 0)
                {
                    waitTime -= 100;
                }
                else
                {
                    newAsdu = new ASDU(server.GetApplicationLayerParameters(), CauseOfTransmission.PERIODIC, false, false, 0, 1, false);

                    newAsdu.AddInformationObject(new MeasuredValueScaled(110, -1, new QualityDescriptor()));

                    server.EnqueueASDU(newAsdu);

                    waitTime = 1000;
                }
            }

            Console.WriteLine("Stop server");
            server.Stop();
        }
Exemple #15
0
        // This process watches (via change stream) for point updates
        // Forward data to its connections
        static async void ProcessMongoCS(JSONSCADAConfig jsConfig)
        {
            do
            {
                try
                {
                    var Client     = ConnectMongoClient(jsConfig);
                    var DB         = Client.GetDatabase(jsConfig.mongoDatabaseName);
                    var collection =
                        DB
                        .GetCollection
                        <rtData>(RealtimeDataCollectionName);

                    bool isMongoLive =
                        DB
                        .RunCommandAsync((Command <BsonDocument>) "{ping:1}")
                        .Wait(1000);
                    if (!isMongoLive)
                    {
                        throw new Exception("Error on connection " + jsConfig.mongoConnectionString);
                    }

                    Log("MongoDB CMD CS - Start listening for realtime data updates via changestream...");
                    // observe updates and replaces, avoid updates with sourceDataUpdateField (those are handled by cs_data_processor.js)
                    var filter = "{ $or: [{ $and:[{ 'updateDescription.updatedFields.sourceDataUpdate': { $exists: false } },{ operationType: 'update' }] }, { operationType: 'replace'}] }";

                    var pipeline =
                        new EmptyPipelineDefinition <ChangeStreamDocument <rtData
                                                                           >
                                                     >().Match(filter);
                    var changeStreamOptions = new ChangeStreamOptions
                    {
                        FullDocument = ChangeStreamFullDocumentOption.UpdateLookup
                    };
                    using (var cursor = await collection.WatchAsync(pipeline, changeStreamOptions))
                    {
                        await cursor
                        .ForEachAsync(change =>
                        {
                            // process change event, only process updates and replaces
                            if (
                                change.OperationType == ChangeStreamOperationType.Update ||
                                change.OperationType == ChangeStreamOperationType.Replace
                                )
                            {
                                if (change.FullDocument != null)
                                {
                                    if (change.FullDocument.protocolDestinations != null)
                                    {
                                        foreach (var dst in change.FullDocument.protocolDestinations)
                                        {
                                            foreach (IEC10X_connection
                                                     srv
                                                     in
                                                     IEC10Xconns
                                                     )
                                            {
                                                if (dst.protocolDestinationConnectionNumber == srv.protocolConnectionNumber)
                                                {
                                                    var conNameStr = srv.name + " - ";
                                                    ApplicationLayerParameters alp =
                                                        srv.server.GetApplicationLayerParameters();
                                                    var quality     = new QualityDescriptor();
                                                    quality.Invalid = change.FullDocument.invalid.ToBoolean() ||
                                                                      change.FullDocument.overflow.ToBoolean() ||
                                                                      change.FullDocument.transient.ToBoolean();
                                                    quality.Substituted    = change.FullDocument.substituted.ToBoolean();
                                                    quality.Blocked        = false;
                                                    quality.NonTopical     = false;
                                                    CP56Time2a cp56timesrc = null;
                                                    if (change.FullDocument.timeTagAtSource != null)
                                                    {
                                                        cp56timesrc         = new CP56Time2a(System.Convert.ToDateTime(change.FullDocument.timeTagAtSource).AddHours(dst.protocolDestinationHoursShift.ToDouble()));
                                                        cp56timesrc.Invalid = false;
                                                        if (change.FullDocument.timeTagAtSourceOk != null)
                                                        {
                                                            cp56timesrc.Invalid = !change.FullDocument.timeTagAtSourceOk.ToBoolean();
                                                        }
                                                        else
                                                        {
                                                            cp56timesrc.Invalid = true;
                                                        }
                                                    }
                                                    var io = BuildInfoObj(
                                                        dst.protocolDestinationASDU.ToInt32(),
                                                        dst.protocolDestinationObjectAddress.ToInt32(),
                                                        change.FullDocument.value.ToDouble(),
                                                        false,
                                                        0,
                                                        quality,
                                                        cp56timesrc
                                                        );
                                                    if (io != null)
                                                    {
                                                        // queue data to make possible to assemble an ASDU with many elements, will send on DequeueIecInfo
                                                        InfoCA ica = new InfoCA()
                                                        {
                                                            io = io,
                                                            ca = dst.protocolDestinationCommonAddress.ToInt32()
                                                        };
                                                        srv.infoCAQueue.Enqueue(ica);

                                                        if (LogLevel >= LogLevelDetailed)
                                                        {
                                                            Log(conNameStr + "Spont Tag:" +
                                                                change.FullDocument.tag + " Value:" + change.FullDocument.value +
                                                                " Key:" + change.FullDocument._id + " TI:" + dst.protocolDestinationASDU.ToInt32() + " CA:" + dst.protocolDestinationCommonAddress + (cp56timesrc == null ? "" : " " + cp56timesrc.ToString()),
                                                                LogLevelDetailed);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        });
                    }
                }
                catch (Exception e)
                {
                    Log("Exception MongoCmd");
                    Log(e);
                    Log(e
                        .ToString()
                        .Substring(0,
                                   e.ToString().IndexOf(Environment.NewLine)));
                    System.Threading.Thread.Sleep(3000);
                }
            }while (true);
        }
 public Server(uint port)
 {
     this.localPort      = (int)port;
     this.apciParameters = new APCIParameters();
     this.alParameters   = new ApplicationLayerParameters();
 }
Exemple #17
0
        private static bool interrogationHandler(object parameter, IMasterConnection connection, ASDU asdu, byte qoi)
        {
            Console.WriteLine("Interrogation for group " + qoi);

            ApplicationLayerParameters cp = connection.GetApplicationLayerParameters();

            connection.SendACT_CON(asdu, false);

            // send information objects
            ASDU newAsdu = new ASDU(cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, false);

            newAsdu.AddInformationObject(new MeasuredValueScaled(100, -1, new QualityDescriptor()));

            newAsdu.AddInformationObject(new MeasuredValueScaled(101, 23, new QualityDescriptor()));

            newAsdu.AddInformationObject(new MeasuredValueScaled(102, 2300, new QualityDescriptor()));

            connection.SendASDU(newAsdu);

            newAsdu = new ASDU(cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 3, 1, false);

            newAsdu.AddInformationObject(new MeasuredValueScaledWithCP56Time2a(103, 3456, new QualityDescriptor(), new CP56Time2a(DateTime.Now)));

            connection.SendASDU(newAsdu);

            newAsdu = new ASDU(cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, false);

            newAsdu.AddInformationObject(new SinglePointWithCP56Time2a(104, true, new QualityDescriptor(), new CP56Time2a(DateTime.Now)));

            connection.SendASDU(newAsdu);

            // send sequence of information objects
            newAsdu = new ASDU(cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, true);

            newAsdu.AddInformationObject(new SinglePointInformation(200, true, new QualityDescriptor()));
            newAsdu.AddInformationObject(new SinglePointInformation(201, false, new QualityDescriptor()));
            newAsdu.AddInformationObject(new SinglePointInformation(202, true, new QualityDescriptor()));
            newAsdu.AddInformationObject(new SinglePointInformation(203, false, new QualityDescriptor()));
            newAsdu.AddInformationObject(new SinglePointInformation(204, true, new QualityDescriptor()));
            newAsdu.AddInformationObject(new SinglePointInformation(205, false, new QualityDescriptor()));
            newAsdu.AddInformationObject(new SinglePointInformation(206, true, new QualityDescriptor()));
            newAsdu.AddInformationObject(new SinglePointInformation(207, false, new QualityDescriptor()));

            connection.SendASDU(newAsdu);

            newAsdu = new ASDU(cp, CauseOfTransmission.INTERROGATED_BY_STATION, false, false, 2, 1, true);

            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(300, -1.0f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(301, -0.5f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(302, -0.1f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(303, .0f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(304, 0.1f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(305, 0.2f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(306, 0.5f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(307, 0.7f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(308, 0.99f));
            newAsdu.AddInformationObject(new MeasuredValueNormalizedWithoutQuality(309, 1f));

            connection.SendASDU(newAsdu);

            connection.SendACT_TERM(asdu);

            return(true);
        }
 /// <summary>
 /// Create a new server using default connection parameters
 /// </summary>
 public Server()
 {
     this.apciParameters = new APCIParameters();
     this.alParameters   = new ApplicationLayerParameters();
 }
Exemple #19
0
        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);
Exemple #20
0
        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);