ModuleEmulator Class |
Namespace: Demo3D.PLC.Rockwell.Emulator.ModuleEmulators
public sealed class ModuleEmulator : IDisposable
The ModuleEmulator type exposes the following members.
| Name | Description | |
|---|---|---|
| ConfigurationName |
The module configuration name.
| |
| Configured |
True if at least one Assembly ForwardOpen has been received and processed.
| |
| DeviceName |
Module device part of the tag.
| |
| IPAddress |
IP address of the device.
| |
| IsLogicLocked |
True if the current thread holds the module emulator logic lock.
| |
| ModuleType |
The module type name.
| |
| Properties |
Properties set by the module emulator factory.
Always null for RPI events.
| |
| Slot |
Slot number.
| |
| SymbolName |
Module symbol name.
|
| Name | Description | |
|---|---|---|
| AddEndLogic |
Registers an action to be called at the end of every logic run but only if the action returned is invoked during the run.
| |
| AddPushBatch |
Adds actions to be called once at the end of a Push from Model batch.
| |
| AfterOutputs |
Calls all IAfterOutputs events.
| |
| BeforeInputs |
Calls all IBeforeInputs events.
| |
| ComputeInputs |
Calls all ComputeInputs events.
| |
| ConfigChanged |
Calls all ConfigChanged events.
| |
| ExportAutoSymbols |
A function for reflecting and adding [Auto] tags from the module emulator type into the symbol table.
| |
| FindAuto(Object) |
Find members with the AutoAttribute.
| |
| FindAutoT(Object) |
Find members with the AutoAttribute.
| |
| GetModelTime |
Returns the current time.
| |
| GetSystemTime |
Returns the device system time.
| |
| Install(ModuleEmulatorConfiguration) |
Installs a ModuleEmulator for a specified EDSInfo and connection point(s).
| |
| Install(String, String, Type, EDSInfo, EDSMask, Int64, ValueTupleDeviceArea, UInt32) |
Installs a ModuleEmulator for a specified EDSInfo and connection point(s) for a single connection.
| |
| Install(String, String, Type, EDSInfo, EDSMask, Int64, ValueTupleDeviceArea, UInt32, ModuleEmulatorSymbolParameters, Object) |
Installs a ModuleEmulator for a specified EDSInfo and connection point(s) for each connection.
| |
| ProcessOutputs |
Calls all ProcessOutputs events.
| |
| Schedule |
Schedule an event at a time + duration.
| |
| ScheduleAfter(Double, Action, Action) |
Schedule an event in model/virtual time, or real time if model/virtual time isn't possible.
| |
| ScheduleAfter(ModelTime, Action, Action) |
Schedule an event in model/virtual time.
| |
| ScheduleAfter(RealTime, Action, Action) |
Schedule an event in real time.
| |
| ScheduleAt(ModelTime, Action, Action) |
Schedule an event in model/virtual time.
| |
| ScheduleAt(RealTime, Action, Action) |
Schedule an event in real time.
|
| Name | Description | |
|---|---|---|
| OnAfterLogic |
Occurs after logic has run that might have affected the module emulator outputs.
|
The following example shows a simple 5094 IF8 Module Emulator
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
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); } } }