Click or drag to resize

ModuleEmulator Class

Represents a module emulator.
Inheritance Hierarchy
SystemObject
  Demo3D.PLC.Rockwell.Emulator.ModuleEmulatorsModuleEmulator

Namespace:  Demo3D.PLC.Rockwell.Emulator.ModuleEmulators
Assembly:  PLC (in PLC.dll) Version: 18.03.00
Syntax
C#
public sealed class ModuleEmulator : IDisposable

The ModuleEmulator type exposes the following members.

Properties
  NameDescription
Public propertyConfigurationName
The module configuration name.
Public propertyConfigured
True if at least one Assembly ForwardOpen has been received and processed.
Public propertyDeviceName
Module device part of the tag.
Public propertyIPAddress
IP address of the device.
Public propertyIsLogicLocked
True if the current thread holds the module emulator logic lock.
Public propertyModuleType
The module type name.
Public propertyProperties
Properties set by the module emulator factory. Always null for RPI events.
Public propertySlot
Slot number.
Public propertySymbolName
Module symbol name.
Top
Methods
  NameDescription
Public methodAddEndLogic
Registers an action to be called at the end of every logic run but only if the action returned is invoked during the run.
Public methodAddPushBatch
Adds actions to be called once at the end of a Push from Model batch.
Public methodAfterOutputs
Calls all IAfterOutputs events.
Public methodBeforeInputs
Calls all IBeforeInputs events.
Public methodComputeInputs
Public methodConfigChanged
Public methodStatic memberExportAutoSymbols
A function for reflecting and adding [Auto] tags from the module emulator type into the symbol table.
Public methodStatic memberFindAuto(Object)
Find members with the AutoAttribute.
Public methodStatic memberFindAutoT(Object)
Find members with the AutoAttribute.
Public methodGetModelTime
Returns the current time.
Public methodGetSystemTime
Returns the device system time.
Public methodStatic memberInstall(ModuleEmulatorConfiguration)
Installs a ModuleEmulator for a specified EDSInfo and connection point(s).
Public methodStatic memberInstall(String, String, Type, EDSInfo, EDSMask, Int64, ValueTupleDeviceArea, UInt32)
Installs a ModuleEmulator for a specified EDSInfo and connection point(s) for a single connection.
Public methodStatic memberInstall(String, String, Type, EDSInfo, EDSMask, Int64, ValueTupleDeviceArea, UInt32, ModuleEmulatorSymbolParameters, Object)
Installs a ModuleEmulator for a specified EDSInfo and connection point(s) for each connection.
Public methodProcessOutputs
Public methodSchedule
Schedule an event at a time + duration.
Public methodScheduleAfter(Double, Action, Action)
Schedule an event in model/virtual time, or real time if model/virtual time isn't possible.
Public methodScheduleAfter(ModelTime, Action, Action)
Schedule an event in model/virtual time.
Public methodScheduleAfter(RealTime, Action, Action)
Schedule an event in real time.
Public methodScheduleAt(ModelTime, Action, Action)
Schedule an event in model/virtual time.
Public methodScheduleAt(RealTime, Action, Action)
Schedule an event in real time.
Top
Events
  NameDescription
Public eventOnAfterLogic
Occurs after logic has run that might have affected the module emulator outputs.
Top
Examples

The following example shows a simple 5094 IF8 Module Emulator

C#
using System;
using Demo3D.Common;
using Demo3D.Net.Protocols;
using Demo3D.PLC.Comms.CPF;
using Demo3D.PLC.Rockwell.Emulator.ModuleEmulators;

namespace Examples.DeviceEmulation {
    /// <summary>
    /// 5094 IF8 device emulation.
    /// </summary>
    public sealed class DE_5094_IF8 : IConstructRPI, IProcessOutputs {
        [Auto] ModuleEmulator moduleEmulator;
        [Auto] public uint    po_RunIdle;
        uint                  runIdle;
        ISystemTime           systemTime;
        bool                  synchronized;

        sealed class Channel : IComputeInputs, IBeforeInputs {
            readonly DE_5094_IF8 emulator;

            [Auto]      public float c_LowEngineering;
            [Auto]      public float c_HighEngineering;
            [Auto]      public float c_LowSignal;
            [Auto]      public float c_HighSignal;
            [Auto]      public float i_Data;
            [Auto, RPI] public short i_RollingTimestamp;
            [Auto]      public float pi_Data;

            internal Channel(DE_5094_IF8 emulator) => this.emulator = emulator;

            // Called in response to updates from Emulate3D, to compute the values to send to the PLC.
            void IComputeInputs.ComputeInputs() {
                if (emulator.po_RunIdle == 0) return;  // PLC is disconnected or in program mode
                var signalRange      = c_HighSignal      - c_LowSignal;
                var engineeringRange = c_HighEngineering - c_LowEngineering;
                i_Data               = ((pi_Data - c_LowSignal) * (engineeringRange / signalRange)) + c_LowEngineering;
            }

            // Called in the IO process immediately before sending input to the PLC.
            void IBeforeInputs.BeforeInputs() {
                var isSynchronized = emulator.systemTime.IsSynchronized;
                i_RollingTimestamp = !isSynchronized ? (short)0 : (short)((emulator.systemTime.SystemTimeMicroseconds / 1000) & 0x7fff);
            }
        }

        [Auto, Array(Width = 8), TagName(Name = "Ch{i:00}")]
        readonly Channel[] points = new Channel[8];

        // IO process construct method.
        void IConstructRPI.Construct(IComponentLogger logger) {
            systemTime = moduleEmulator.GetSystemTime();
        }

        void IProcessOutputs.ProcessOutputs() {
            if (po_RunIdle != runIdle) {
                runIdle = po_RunIdle;
                if (runIdle != 0) moduleEmulator.ComputeInputs();
            }
        }

        // Installs this device emulator into the Emulate3D device network emulator.
        public static IDisposable Install() {
            var if8 = new EDSInfo(DeviceVendor.Rockwell, DeviceType.RAMisc, productCode: 315, 0, 0);
            return ModuleEmulator.Install("5094-IF8", "", typeof(DE_5094_IF8), if8, EDSMask.ProductMatch);
        }
    }
}

The following example shows a simple 5094 OB16 Module Emulator

C#
using System;
using Demo3D.Common;
using Demo3D.PLC.Comms.CIP;
using Demo3D.PLC.Comms.CIP.Nodes;
using Demo3D.PLC.Comms.CPF;
using Demo3D.PLC.Rockwell.Emulator.ModuleEmulators;

namespace Examples.DeviceEmulation {
    /// <summary>
    /// 5094 IF8 device emulation.
    /// </summary>
    public sealed class DE_5094_OB16 : IConstructEmulator {
        [Auto] public uint po_Status;
        [Auto] public uint po_RunIdle;

        [AutoInstance(ClassID.TimeSync, 1)] TimeSync timeSync;

        sealed class Point : IProcessOutputs {
            readonly DE_5094_OB16 emulator;

            bool  oldProgMode;
            bool  oldFaulted;
            ulong faultStartTime;
            bool  faultFinalState;

            [Auto] public bool c_ProgMode;
            [Auto] public bool c_ProgValue;
            [Auto] public bool c_ProgramToFaultEn;
            [Auto] public bool c_FaultMode;
            [Auto] public bool c_FaultValue;
            [Auto] public byte c_FaultValueStateDuration;
            [Auto] public bool c_FaultFinalState;
            [Auto] public bool i_Data;
            [Auto] public bool o_Data;

            internal Point(DE_5094_OB16 emulator) => this.emulator = emulator;

            public void ProcessOutputs() {
                // determine the current state
                var faulted  = (emulator.po_Status & 1) == 0;
                var progMode = !faulted && emulator.po_RunIdle == 0;

                // if we faulted while in program mode, and c_ProgramToFaultEn is unset
                // then behave as if we're still in program mode
                if (faulted && oldProgMode && !c_ProgramToFaultEn) {
                    faulted  = false;
                    progMode = true;
                }

                if (faulted) {
                    // we're faulted
                    // don't bother timing if the timer already expired, c_FaultFinalStateDuration is Forever, or we have no clock
                    if (!faultFinalState && c_FaultValueStateDuration != 0 && (emulator.timeSync?.IsSynchronized ?? false)) {
                        var timeNow = emulator.timeSync.SystemTimeMicroseconds;

                        if (!oldFaulted) {
                            // we just faulted, start timing
                            faultStartTime = timeNow;
                        } else {
                            // we were already faulted, did the timer expire?
                            var diffSeconds = (timeNow - faultStartTime) / 1000000;
                            faultFinalState = diffSeconds >= c_FaultValueStateDuration;
                        }
                    }

                    // faultFinalState is set when the c_FaultFinalStateDuration timer expired
                    // otherwise if c_FaultMode, force the output to c_FaultValue
                    if (faultFinalState)   i_Data = c_FaultFinalState;
                    else if (!c_FaultMode) i_Data = c_FaultValue;

                } else {
                    // we're not faulted
                    faultFinalState = false;

                    if (progMode) {
                        // we're in program mode
                        // if c_ProgMode is unset, force the output to c_ProgValue
                        if (!c_ProgMode) i_Data = c_ProgValue;

                    } else {
                        // running ok
                        i_Data = o_Data;
                    }
                }

                // remember last state
                oldProgMode = progMode;
                oldFaulted  = faulted;
            }
        }

        [Auto, Array(Width = 16), TagName(Name = "Pt{i:00}")]
        readonly Point[] points = new Point[16];

        public void Construct(MessageRouter module, IComponentLogger logger) {
            timeSync.PTPEnable = true;
        }

        // Installs this device emulator into the Emulate3D device network emulator.
        public static IDisposable Install() {
            var ob16 = new EDSInfo(DeviceVendor.Rockwell, DeviceType.GeneralPurposeDiscreteIO, productCode: 399, 0, 0);
            return ModuleEmulator.Install("5094-OB16", "", typeof(DE_5094_OB16), ob16, EDSMask.ProductMatch);
        }
    }
}
See Also