Exemplo n.º 1
0
        /// <summary>Contructor</summary>
        public ModbusServerFunctionPortAdapter(string partID, SerialIO.PortConfig portConfig, IModbusFCServer fcServer, ADUType aduType, byte unitID, bool responseToAllUnits)
            : base(partID, initialSettings: SimpleActivePartBaseSettings.DefaultVersion2.Build(waitTimeLimit: (0.2).FromSeconds()))
        {
            this.fcServer = fcServer;

            Timeout = portConfig.ReadTimeout;
            portConfig.ReadTimeout = TimeSpan.FromSeconds(Math.Max(0.1, Timeout.TotalSeconds));

            port = SerialIO.Factory.CreatePort(portConfig);
            portBaseStateObserver = new SequencedRefObjectSourceObserver <IBaseState, Int32>(port.BaseStateNotifier);

            IPortBehavior portBehavior = port.PortBehavior;

            IDictionary <string, Logging.IMesgEmitter> emitters = new Dictionary <string, Logging.IMesgEmitter>()
            {
                { "Issue", Log.Error }, { "Debug", Log.Debug }, { "Trace", Log.Trace }
            };

            serverFunctionContainer = new ServerFunctionContainer()
            {
                ADUType = aduType, Emitters = emitters, UnitID = unitID, RTUAddr = unitID, MBAPUnitID = unitID, RespondToAllTargets = responseToAllUnits
            };

            FlushPeriod = (portBehavior.IsDatagramPort ? TimeSpan.FromSeconds(0.0) : TimeSpan.FromSeconds(0.1));

            portReadAction = port.CreateReadAction(portReadActionParam = new ReadActionParam()
            {
                WaitForAllBytes = false
            });
            portWriteAction        = port.CreateWriteAction(portWriteActionParam = new WriteActionParam());
            portFlushAction        = port.CreateFlushAction(FlushPeriod);
            portReinitializeAction = port.CreateGoOnlineAction(true);

            portReadAction.NotifyOnComplete.AddItem(threadWakeupNotifier);
            portWriteAction.NotifyOnComplete.AddItem(threadWakeupNotifier);
            portFlushAction.NotifyOnComplete.AddItem(threadWakeupNotifier);
            portReinitializeAction.NotifyOnComplete.AddItem(threadWakeupNotifier);

            port.BaseStateNotifier.NotificationList.AddItem(threadWakeupNotifier);

            AddMainThreadStartingAction(() => port.StartPart());
            AddMainThreadStoppingAction(() => port.StopPart());

            AddExplicitDisposeAction(() => Fcns.DisposeOfObject(ref port));
        }
Exemplo n.º 2
0
        /// <summary>
        /// Provides the server specific version of the Part's Main Loop Service method.
        /// </summary>
        protected override void PerformMainLoopService()
        {
            base.PerformMainLoopService();

            InnerServiceFCServerAndStateRelay();

            bool portIsConnected = portBaseStateObserver.Object.IsConnected;

            if (portWriteAction.ActionState.IsPendingCompletion || portFlushAction.ActionState.IsPendingCompletion || portReinitializeAction.ActionState.IsPendingCompletion)
            {
                // we cannot service a new request until the write, flush, and/or reinitialize actions started in any prior service loop have completed.
                return;
            }

            bool resyncCommandStream = false;
            bool startWrite          = false;

            if (portIsConnected && portReadAction.ActionState.CanStart)
            {
                if (portReadAction.ActionState.IsComplete)
                {
                    serverFunctionContainer.requestAdu.PktBuf.numBytes = portReadActionParam.BytesRead;

                    string   ec        = null;
                    TimeSpan bufferAge = bufferFillStartTime.Age;
                    if (serverFunctionContainer.AttemptToDecodeRequestPkt(out ec))
                    {
                        if (String.IsNullOrEmpty(ec))
                        {
                            Log.Trace.Emit("Attempting to perform request ADU:{0}", serverFunctionContainer.requestAdu);

                            if (serverFunctionContainer.ServiceDecodedRequest(fcServer))
                            {
                                startWrite = true;
                            }
                            else
                            {
                                Log.Trace.Emit("Decoded request produced no response [ADU:{0}]", serverFunctionContainer.requestAdu);
                            }
                        }
                        else
                        {
                            Log.Error.Emit("Invalid request received: {0} [numBytes:{1}]", ec, portReadActionParam.BytesRead);
                            resyncCommandStream = true;
                        }

                        portReadActionParam.BytesRead = 0;
                    }
                    else if (portReadActionParam.BytesRead > 0)
                    {
                        if (port.PortBehavior.IsDatagramPort)
                        {
                            Log.Warning.Emit("Invalid partial request received from datagram port.  Discarding {0} bytes", portReadActionParam.BytesRead);

                            portReadActionParam.BytesRead = 0;
                        }
                        else if (bufferAge > Timeout)
                        {
                            Log.Warning.Emit("Incomplete Partial Request timeout after {0:f3} seconds.  Discarding {0} bytes", bufferAge.TotalSeconds, portReadActionParam.BytesRead);

                            portReadActionParam.BytesRead = 0;
                        }
                    }
                    else
                    {
                        Log.Trace.Emit("Empty read completed");
                    }
                }

                if (!resyncCommandStream)
                {
                    // start the read immediately even if we are also starting a write (keep the interface primed)
                    // do not start the next read if we need to resync the command stream

                    if (portReadActionParam.BytesRead == 0)
                    {
                        bufferFillStartTime = QpcTimeStamp.Now;
                    }

                    if (portReadActionParam.Buffer == null)
                    {
                        portReadActionParam.Buffer      = serverFunctionContainer.requestAdu.PktBuf.bytes;
                        portReadActionParam.BytesToRead = serverFunctionContainer.requestAdu.PktBuf.bytes.Length;
                    }

                    portReadAction.Start();
                }
            }
            else if (portReadAction.ActionState.IsComplete && portReadActionParam.BytesRead > 0)
            {
                Log.Debug.Emit("Discarding {0} bytes of read data: port is no longer connected");
                portReadActionParam.BytesRead = 0;
            }

            if (!portIsConnected)
            {
            }
            else if (startWrite)
            {
                Log.Trace.Emit("Writing response ADU:{0}", serverFunctionContainer.responseAdu);

                portWriteActionParam.Buffer       = serverFunctionContainer.responseAdu.PktBuf.bytes;
                portWriteActionParam.BytesToWrite = serverFunctionContainer.responseAdu.PktBuf.numBytes;
                portWriteActionParam.BytesWritten = 0;

                portWriteAction.Start();
            }
            else if (resyncCommandStream)
            {
                IPortBehavior portBehavior = port.PortBehavior;

                if (portBehavior.IsDatagramPort)
                {
                    // there is nothing to do for datagram ports.
                    // Each message is a seperate item so there is no "leftover" bytes from prior requests that we might need to get rid of.
                }
                else if (portBehavior.IsByteStreamPort && portBehavior.IsNetworkPort)
                {
                    Log.Debug.Emit("Forcing port to reset current connection after protocol decode error (drop and immediately reconnect)");
                    portReinitializeAction.Start();
                }
                else
                {
                    portFlushAction.Start();
                }
            }
        }