//This file was generated by the LASAL2 CodeGenerator  -- 
//Please, do not edit this file (it might be overwritten by the next generator run)

//{{LSL_DEFINES

// always allocate blocks for 5 more SDOs
#define SBF_SDO_BUFFER_ALLOC_BLOCK 5

// time between the CRC-Checks (CRC-Check to find out if configuration has changed)
#define SBF_CRC_CHECK_TIME  5000

// time between the system time of plc is written to scp firmware
#define SBF_SYSTIME_SET_TIME  (5 SEC)  //(24 hours) 

// time between we read out the retrycounter from the module.
#define SBF_RETRY_COUNTER_WAIT_TIME 10000

// time between the check of the flex con state
#define SBF_FLEX_CON_STATE_CHECK_TIME 2000

#define SBF_TDO_BUFFER_SIZE 50

// time for SDO transfer
#define SBF_SDO_TRANSFERTIME 30 // maximum measured time at mover was 22ms + 3ms reserve

// timeout for optional modules to be synchron
#define SBF_OPTIONAL_MODULE_SYNC_TIMEOUT  5000

// maximum number of tries to execute an asynchronous command (5 = if 5 consecutive tries to make an asynchronous communication fail, the class goes into error _AsyncComError)
#define SBF_SDO_MAX_TRIES 5

#define SBF_RETRY_COUNTER_AVAILABLE_MAJOR_REV 1000
#define SBF_RETRY_COUNTER_AVAILABLE_MINOR_REV 338

#define SBF_MAX_BUS_USAGE         128
#define SBF_BUS_USAGE_PER_PDO_DO  127

// defines to lock readout of the config against using the config in the realtime method (necessary for the fast unsafe variables)
#define SBF_LOCKCFG_NOT_USED  0
#define SBF_LOCKCFG_VALID     1
#define SBF_LOCKCFG_WAIT4RT   2


// if defined, the DO traffic is monitored
//#define DO_DEBUG
#ifdef DO_DEBUG
#pragma message("DO_DEBUG for Safety-Modules activated! Needs more CPU ressources!")
#endif

//}}LSL_DEFINES

//{{LSL_DECLARATION
#include "..\..\Source\interfaces\lsl_st_safety.h"
#include "..\..\Class\SafetyManager\SafetyManager.h"
#include "..\..\Source\BusInterface.h"
#include "..\..\Class\SafetyBaseFSoE\SafetyBaseFSoE.h"
#include "..\..\Source\interfaces\lsl_st_hardwaretree.h"

(*!
<Class
	Name               = "SafetyBaseFSoE"
	Revision           = "1.38"
	GUID               = "{7B09BE5F-9F0C-4D32-AF0D-D03A17AC196B}"
	RealtimeTask       = "false"
	CyclicTask         = "false"
	BackgroundTask     = "true"
	DefBackground      = "100 ms"
	Sigmatek           = "true"
	OSInterface        = "false"
	HighPriority       = "false"
	Automatic          = "false"
	UpdateMode         = "Prescan"
	IconPath           = ".\Class\Safety.ico"
	SharedCommandTable = "true"
	Objectsize         = "(670,120)">
	<Channels>
		<Server Name="ApplCRC" GUID="{C417B39A-384A-4DE1-9CA9-408891187AED}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Shows the CRC of the safety application (see CRC Dialog in the SafetyDesigner)"/>
		<Server Name="ClassSvr" GUID="{3D1411CE-19C8-4543-AD7B-B5D6468E55FD}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false"/>
		<Server Name="DesignerPrjConfigCRC" GUID="{E12043F9-5599-42B0-9770-CEE5696A2542}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Shows the CRC of the whole SafetyDesigner project configuration (see CRC Dialog in the SafetyDesigner)"/>
		<Server Name="ErrorState" GUID="{9CC210B0-1231-4523-AAD3-6ED052F7BE24}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="0: no error&#13;&#10;1: error&#13;&#10;2: communication timeout on input data from other safety modules"/>
		<Server Name="FirmwareVersion" GUID="{36902EC1-1639-4CEF-AD06-B273246BF0B1}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Firmware version&#13;&#10;&#13;&#10;Format:&#13;&#10;16#XXXXYYYY&#13;&#10;&#13;&#10;XXXX = Major Revision&#13;&#10;YYYY = Minor Revision"/>
		<Server Name="GetDiagConnect" GUID="{23777096-2D0D-42D8-A262-210C68A8CB88}" Visualized="false" Initialize="false" WriteProtected="true" Retentive="false"/>
		<Server Name="HwCRC" GUID="{DC50FC4B-2EEC-40C9-A9AF-C2AC7D27D81D}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Shows the CRC of the hardware configuration (see CRC Dialog in the SafetyDesigner)"/>
		<Server Name="QuitComError" GUID="{0C9064D6-DD1D-44A6-9529-EDC23EDF3155}" Visualized="true" Initialize="false" WriteProtected="false" Retentive="false" Comment="Write to this server to quit communication errors (indicated if Server ErrorState = 2)&#13;&#10;This server can also quit general errors (Server ErrorState = 1) if the safety.dlm is used.&#13;&#10;&#13;&#10;To detect the used quit method, read the server directly after writing. In both cases the error quitting is done asynchronously (= not immediately).&#13;&#10;&#13;&#10;Server shows actual quit-state&#13;&#10;  2..busy with quitting general errors of the current module and all its submodules (safety dll version 6 or higher required)&#13;&#10;  1..busy with quitting communication error&#13;&#10;  0..ready&#13;&#10; -1..error at creating a new safety state&#13;&#10; -2..failed to disable user prompt for quit error&#13;&#10; -3..failed to select module via safety number&#13;&#10; -4..error at establishing connection to module (safety designer must not be online at the same time!)&#13;&#10; -5..failed to send the quit error command to the module&#13;&#10; -6..failed to delete the created safety state"/>
		<Server Name="RunState" GUID="{AA55B58D-145B-437C-86B5-344D77645EF9}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="0: not running&#13;&#10;1: running in operational or temporary operational mode"/>
		<Server Name="SafetyNumber" GUID="{2E25F877-7C24-4A9B-BF75-3B6393B3C78F}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Shows the unique safety number of this module"/>
		<Server Name="SafetyRetryCounter" GUID="{CB6DBF5D-3482-4154-84FF-C4BD868CA0C5}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Shows the amount of all retries on the safety bus.&#13;&#10;-1 ... Retrycounter read out is not supported by this firmware version."/>
		<Server Name="SafetyState" GUID="{A8244A82-B5A2-481F-B2D6-D9F61CFF77FB}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Shows state of safety module&#13;&#10;&#13;&#10;_ModuleNotFound:&#13;&#10; - Module has not been found at the given position&#13;&#10;&#13;&#10;_WaitForSynchronicity:&#13;&#10; - Waiting to get synchrony with interacting modules&#13;&#10;&#13;&#10;_SafetyClassOK:&#13;&#10; - Module has been found and no errors reported&#13;&#10;&#13;&#10;_MemAllocFailed: &#13;&#10; - Failed to allocate or reallocate some memory for the module&#13;&#10;&#13;&#10;_ReadFWVerFailed:&#13;&#10; - Failed to read firmware version of module&#13;&#10;&#13;&#10;_ReinitConfig:&#13;&#10; - Only for CPU: Config of CPU has changed and update is in progress&#13;&#10;&#13;&#10;_ModFromCfgNotFound (Only for CPU):&#13;&#10; - Module in configuration, which is not physically available and HW-Class is placed&#13;&#10; - Module in configuration, which is not physically available and no HW-Class is placed&#13;&#10; - Module in configuration, which is not a CDIAS module (according to it&apos;s HW-Path)&#13;&#10;&#13;&#10;_UnsafeVarNotFound (Only for CPU):&#13;&#10; - Couldn&apos;t find an unsafe variable in the project. Compare spelling in Designer and in project.&#13;&#10;&#13;&#10;_UnknownCfgError (Only for CPU):&#13;&#10; - Error in reading config or creating routing tables occured."/>
		<Server Name="ServiceMode" GUID="{63FC066A-4EB8-40EE-A881-715A0F7A57CD}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="0: operational mode&#13;&#10;1: service mode or temporary operational mode&#13;&#10;2: check configuration phase"/>
		<Server Name="VoltageOK" GUID="{E7A96867-825D-4C1E-8DA0-79BC048837F3}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="0: voltage of output/pulse output supply not ok&#13;&#10;1: voltage of output/pulse output supply ok"/>
		<Client Name="DeviceID" Required="true" Internal="false"/>
		<Client Name="FileSys" Required="false" Internal="false"/>
		<Client Name="ServerUpdateTime" Required="false" Internal="false" DefValue="50" Comment="Time in ms, how long the Servers (unsafe variables in Safety Designer) should take to update.&#13;&#10;This Client is automatically updated, if the given time is too short to handle with the current amount of servers."/>
		<Client Name="ToBusInterface" Required="true" Internal="false"/>
		<Client Name="ToSigCLib" Required="false" Internal="false"/>
		<Client Name="ToStdLib" Required="false" Internal="false"/>
	</Channels>
	<Dependencies>
		<Files>
			<File Path=".\Source\interfaces\lsl_st_safety.h" Include="true"/>
			<File Path=".\Class\VaranManager_Base\VaranManager_Base.h" Include="false"/>
			<File Path=".\Class\SafetyManager\SafetyManager.h" Include="true"/>
			<File Path=".\Source\BusInterface.h" Include="true"/>
			<File Path=".\Class\SafetyBaseFSoE\SafetyBaseFSoE.h" Include="true"/>
			<File Path=".\Source\interfaces\lsl_st_hardwaretree.h" Include="true"/>
		</Files>
	</Dependencies>
	<RevDoku>
		<Owner Company="Sigmatek" Author="RamAnd"/>
		<Dokumentation Revision="1.38" Date="17.12.2024" Author="ZoePat" Company="Sigmatek" Description="Corrected possible access exception, if the safetyproject uses flexcon and safetyinputs are used across two or more SCPs (internal interface frames)."/>
		<Dokumentation Revision="1.37" Date="09.07.2024" Author="LanSte" Company="Sigmatek" Description="Corrected NewInst method GetDiagConnect::NewInst::SBCMD_SM_DIAG_SAVE_LOGFILE. 0 Termination of input parameter was not copied."/>
		<Dokumentation Revision="1.36" Date="25.07.2023" Author="LanSte" Company="Sigmatek" Description="Corrected handling of CMD_SM_REMOVE_INPUT_PDO if a FSoE Frame was split over several SDIAS DOs."/>
		<Dokumentation Revision="1.35" Date="19.01.2023" Author="EisMic" Company="Sigmatek" Description="The behavior that the offset of the PDO data pointers are not always calculated in the StartDO method was changed because there was an error when a VI02x module with x modules unplugged and plugged in again with fewer than x modules. In this case the PDO pointers were wrong which leads to wrong server data at the safety modules behind the SCP."/>
		<Dokumentation Revision="1.34" Date="08.07.2022" Author="RamAnd" Company="Sigmatek" Description="Added possibility to try again later if adding an input PDO only failed temporarily because of a certain init step being active at that moment.&#13;&#10;Corrected an error from the last modification that could have blocked slave configurations from being distributed."/>
		<Dokumentation Revision="1.33" Date="19.04.2022" Author="RamAnd" Company="Sigmatek" Description="While the safety configuration is read out, the SDO communication of the class is now paused. Otherwise this could lead to problems in the distribution of the configuration."/>
		<Dokumentation Revision="1.32" Date="16.03.2022" Author="RamAnd" Company="Sigmatek" Description="Added different quit error modi.&#13;&#10;Fixed a crash related to fast unsafe variables."/>
		<Dokumentation Revision="1.31" Date="08.03.2022" Author="RamAnd" Company="Sigmatek" Description="Modules are now also added to the local routing tabs even if the module is not present at the time.&#13;&#10;This way the entry in the table can later be updated when the module gets connected."/>
		<Dokumentation Revision="1.30" Date="31.01.2022" Author="EisMic" Company="Sigmatek" Description="Flag b_ReInitMarker is now set also if non optional module will be added to routing tables. &#13;&#10;This leads that the safety CPU will read out its config again when an optional module will be connected later&#13;&#10;and calls the newinst command CMD_SM_MODULE_REINIT."/>
		<Dokumentation Revision="1.29" Date="01.12.2021" Author="EisMic" Company="Sigmatek" Description="Always free FSoE Connection infos when calling GetFSoEConnectionInfos method."/>
		<Dokumentation Revision="1.28" Date="29.03.2021" Author="FurDom" Company="Sigmatek" Description="add support for FastUnsafe variables with SCP211"/>
		<Dokumentation Revision="1.27" Date="23.10.2020" Author="PieSte" Company="Sigmatek" Description="Added support for VSDC151 module."/>
		<Dokumentation Revision="1.26" Date="20.04.2021" Author="RamAnd" Company="Sigmatek" Description="Added 2 new servers ApplCRC and HwCRC to show the CRCs of the safety application logic as well as the hardware configuration."/>
		<Dokumentation Revision="1.25" Date="01.02.2021" Author="EisMic" Company="Sigmatek" Description="Corrected an error that caused the class to be stuck in _Wait4Synchronicity state when using unsafe variables."/>
		<Dokumentation Revision="1.24" Date="05.10.2020" Author="EisMic" Company="Sigmatek" Description="Added support to get the visual error code offset for the class SafetyDiagnosisInfo.&#13;&#10;Diagnosis info data is now fetched even if the the manager initialisation is not finished."/>
		<Dokumentation Revision="1.23" Date="30.09.2020" Author="RamAnd" Company="Sigmatek" Description="Sending the BusReady message to the safety module could have been blocked by a new InputPDO being added in the process. This would have led to the class being stuck in the _WaitForSynchronicity state. Adding InputPDOs is now prevented while the BusReady signal is being sent out."/>
		<Dokumentation Revision="1.22" Date="03.06.2020" Author="RamAnd" Company="Sigmatek" Description="The Initialisation is now skipped if the DeviceID at the current SDIAS Place is wrong. Otherwise it would lead to timeouts in the SSDO-transfer which would unnecessarily block the safety task."/>
		<Dokumentation Revision="1.21" Date="15.03.2020" Author="RamAnd" Company="Sigmatek" Description="If the module is connected during runtime, there could be a problem when creating the internal safety configuration which would lead to PDOs of this module not working."/>
		<Dokumentation Revision="1.20" Date="24.07.2019" Author="RamAnd" Company="Sigmatek" Description="Corrected comparison before reusing PDO Input buffer.&#13;&#10;Corrected the background task being unnecessarily blocked after power cycling the safe CPU while the SDIAS bus power supply still remains on."/>
		<Dokumentation Revision="1.19" Date="18.07.2019" Author="RamAnd" Company="Sigmatek" Description="Corrected that SDO forwarding would not work properly in some cases."/>
		<Dokumentation Revision="1.18" Date="25.06.2019" Author="RamAnd" Company="Sigmatek" Description="Added support for FlexCon functionality."/>
		<Dokumentation Revision="1.17" Date="06.12.2018" Author="EisMic" Company="Sigmatek" Description="Machinenumber from HGW1033 is now used as a 4 Byte value instead of a 1 byte value."/>
		<Dokumentation Revision="1.16" Date="05.11.2018" Author="PieSte" Company="Sigmatek" Description="The PLC cycle time is written periodically into the module so that this time is used in the safety log file."/>
		<Dokumentation Revision="1.15" Date="18.10.2018" Author="LanSte" Company="Sigmatek" Description="Ignore QuitComError if not in ErrorState.&#13;&#10;Added support for custom FSoE Connections."/>
		<Dokumentation Revision="1.14" Date="05.07.2018" Author="PieSte&#13;&#10;EisMic" Company="Sigmatek" Description="Added support for HGW1033."/>
		<Dokumentation Revision="1.13" Date="28.05.2018" Author="RamAnd" Company="Sigmatek" Description="Corrected some log messages."/>
		<Dokumentation Revision="1.12" Date="13.04.2018" Author="PieSte" Company="Sigmatek" Description="Add numbers for the assignment of the log messages."/>
		<Dokumentation Revision="1.11" Date="06.03.2018" Author="EisMic" Company="Sigmatek" Description="Attached header files no longer point to the installation directory of LASAL but instead now point to the the source/interfaces folder."/>
		<Dokumentation Revision="1.10" Date="23.01.2018" Author="RamAnd" Company="Sigmatek" Description="Log messages have been standardized for easier maintenance, easier use in any class and to allow spam filtering. Code for the logging functions is now only located in HwControl."/>
		<Dokumentation Revision="1.9" Date="29.11.2017" Author="FurDom" Company="Sigmatek" Description="Added support to read out the error information on the channels"/>
		<Dokumentation Revision="1.8" Date="22.08.2017" Author="RamAnd" Company="Sigmatek" Description="Fixed an error which prevented two SCP111s on the same PLC from exchanging interfaceframes in both directions."/>
		<Dokumentation Revision="1.7" Date="12.06.2017" Author="RamAnd" Company="Sigmatek" Description="When another module disconnects before the connection is marked as synchrony, the counter for modules to synchronize is now decremented so the next connect can get synchrony properly."/>
		<Dokumentation Revision="1.6" Date="01.03.2017" Author="EisMic" Company="Sigmatek" Description="The configuration of the module is now also read out if the module is connected during runtime."/>
		<Dokumentation Revision="1.5" Date="14.02.2017" Author="EisMic" Company="Sigmatek" Description="Added support for ADD_DYN_SLAVE command."/>
		<Dokumentation Revision="1.4" Date="10.02.2016" Author="RamAnd" Company="Sigmatek" Description="Support for FSoE via IP added."/>
		<Dokumentation Revision="1.3" Date="08.03.2016" Author="EisMic" Company="Sigmatek" Description="Added support for SafetyDiagnosisInfo hardwareclass."/>
		<Dokumentation Revision="1.2" Date="21.01.2016" Author="EisMic" Company="Sigmatek" Description="Added parameter calloption to UpdateNotStd method that we can write the outputs in RtPostScan instead of RtPreScan to improve the reaction speed."/>
		<Dokumentation Revision="1.1" Date="31.07.2015" Author="RamAnd" Company="Sigmatek" Description="Added server DesignerPrjConfigCRC to show the CRC of the SafetyDesigner project."/>
		<Dokumentation Revision="1.0" Date="15.04.2015" Author="RamAnd" Company="Sigmatek" Description="First library version"/>
	</RevDoku>
</Class>
*)
SafetyBaseFSoE : CLASS
	TYPE
	  DiagLogSaveSSW :
	  (
	    GetLogHeader,
	    GetLogFileBlocks,
	    GetLogHeader2,
	    SaveLogFile
	  )$UDINT;
#pragma pack(push, 1)
	  GetStateResponseType : STRUCT
	    RuntimeState : USINT;
	    ConfigState : USINT;
	    LoginLevel : USINT;
	    ConfigCRC : UDINT;
	    ListHeaderCRC : UDINT;
	    ErrorCount : UINT;
	    InterfaceCRC : HDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  DiagStructType : STRUCT
	    UserWantsDiagState : BOOL;
	    UserDiagStateReady : BOOL;
	    UserGetStateResp : GetStateResponseType;
	    UserWantsDiagInfo : BOOL;
	    UserDiagInfoReady : BOOL;
	    UserGetDiagInfoResp : SafetyDiagInfo;
	    ucChoiceDiagInfo : USINT;
	    ucChoiceSaveLog : USINT;
	    DPNE : ARRAY [0..99] OF CHAR;
	    SavingLogInProgress : BOOL;
	    SavingLogSSW : DiagLogSaveSSW;
	    SavingLogSuccessful : SINT;
	    LogHeader : STRUCT
	      uControllerID : USINT;  //! <Type Comment="ID of µController (0 = µC1, else µC2)" Name="DiagStructType.LogHeader.uControllerID"/>
	      LogMemSize : UDINT;  //! <Type Comment="in Bytes" Name="DiagStructType.LogHeader.LogMemSize"/>
	      WriteCounter : UDINT;
	    END_STRUCT;
	    pLogFileMem : ^USINT;
	    LogMemToGet : UDINT;
	    ReadBaseAdr : UDINT;
	    BytesToGet : USINT;
	    UserWantsErrorInfo : BOOL;
	    UserErrorInfoReady : BOOL;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  FirmwareVersionType : STRUCT
	    RevMinor : UDINT;
	    RevMajor : UINT;
	    ModuleType : UINT;
	    BootLoaderVer : UDINT;
	  END_STRUCT;
#pragma pack(pop)
	  FrameTypeType : BSINT
	  [
	    1 Cfg,
	    2 ToP,
	    3 Standard,
	    4 PDO,
	    5 TDO,
	    6 Reserved1,
	    7 Reserved2,
	    8 Response,
	  ];
#pragma pack(push, 1)
	  PathInfoType : STRUCT
	    p_us_Path : ^USINT;
	    p_us_PathComp : ^USINT;
	    ui_PathCompLen : UINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  PDOInputBuffer : STRUCT
	    ud_p2Buffer : UDINT;
	    ui_PDOLen : UINT;
	    b_TimeSynchron : BOOL;
	    ud_SafetyNr : HDINT;
	    b_IsOptional : BOOL;
	    b_IsInterfaceFrame : BOOL;
	    ui_TargetOffset : UINT;
	    p_TargetMemory : pVoid;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  PDOType : STRUCT
	    CRC : HDINT;
	    Byte5_Zero : USINT;
	    FrameType : FrameTypeType;
	    Byte7_Zero : USINT;
	    Length : USINT;
	    SourceAdr : HDINT;
	    SourceAdrCopy : HDINT;
	    msTimeStamp : DINT;
	    DataAndCopy : ARRAY [0..107] OF USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  UnsafeIORoutingElement : STRUCT
	    ud_Thisp : UDINT;
	    ud_Address : HDINT;
	    sd_OldValue : DINT;
	    sd_NewValue4External : DINT;
	    UnsafeType : t_UnsafeVarType;
	    BitMask : BDINT
	    [
	    ];
	    NameLength : UINT;
	    pName : ^CHAR;
	    b_AlreadyTransmitted : BOOL;
	    b_IsValid : BOOL;
	    pNext : ^UnsafeIORoutingElement;
	  END_STRUCT;
#pragma pack(pop)
	  pUnsafeIORoutingElement : ^UnsafeIORoutingElement;
	  RequestType :
	  (
	    _NoOpenRequest,
	    _ReadData,
	    _ReadValues,
	    _WriteValues,
	    _GetCRC,
	    _SetBusReady,
	    _DiagState,
	    _DiagInfo,
	    _GetSafetyNbr:=8,
	    _GetFWVersion:=9,
	    _ReadRetryCounter,
	    _ErrorChannelInfo,
	    _WriteSysTime,
	    _WriteFlexConCommand,
	    _ReadFlexConNodeState:=14,
	    _ReadFlexConHostState
	  )$UDINT;
#pragma pack(push, 1)
	  RoutElement : STRUCT
	    ud_SafeNumber : HDINT;
	    ud_Thisp : UDINT;
	    p_CompressedPath : ^USINT;
	    ui_ComprPathLen : UINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  SDOHeader : STRUCT  //! <Type Public="true" Name="SDOHeader"/>
	    StdHeader : t_SDOStdHeader;
	    SrcAdr : HDINT;
	    DstAdr : HDINT;
	    SessId : UDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  SDORespStd : STRUCT
	    SDOHeader : SDOHeader;
	    us_Returncode : USINT;
	    us_Data : USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  SDOBufferStructure : STRUCT
	    us_Status : USINT;
	    SDOStructure : SDORespStd;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  SDORequReadConfig : STRUCT
	    StdHeader : t_SDOStdHeader;
	    ud_Address : HDINT;
	    us_Length : USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  SDORespReadConfig : STRUCT
	    StdHeader : t_SDOStdHeader;
	    us_Returncode : USINT;
	    us_Length : USINT;
	    ud_Data : UDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  SDOStdResp : STRUCT
	    StdHeader : t_SDOStdHeader;
	    us_Returncode : USINT;
	    us_Data : USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  SDOviaPath : STRUCT
	    StdHeader : t_SDOStdHeader;
	    ZeroBytes : DINT;
	    TopologyPath : ARRAY [0..35] OF USINT;
	  END_STRUCT;
#pragma pack(pop)
	  SetBusReadySSW :
	  (
	    WaitPDOsSyncron:=0,
	    WaitAllModulesSyncron:=1,
	    SendSetBusReady:=2,
	    SetBusReadyFinished:=3
	  )$UDINT;
#pragma pack(push, 1)
	  StandardFrameType : STRUCT
	    SDOHeader : t_SDOStdHeader;
	    aData : ARRAY [0..63] OF USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_AddDynSlaveResponse : STRUCT
	    Header : SDOHeader;
	    ReturnCode : USINT;
	    ConnectionIndex : UINT;
	    OffsetSafeInData : UINT;
	    LenSafeInData : UINT;
	    OffsetSafeOutData : UINT;
	    LenSafeOutData : UINT;
	    CommID : UINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_CRCInfo : STRUCT
	    ApplCRC : HDINT;
	    HwCRC : HDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_DataObjectCheck : STRUCT
	    LastTime : UDINT;
	    TimeDiff : UDINT;
	    TimeDiffMin : UDINT;
	    TimeDiffMax : UDINT;
	    SameFrameCnt : DINT;
	    LastHeader : t_SDOStdHeader;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_DOInfo : STRUCT
	    pData : pVoid;
	    Handle : UDINT;
	    Length : USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_DynamicParameterHeader : STRUCT
	    Place : UDINT;
	    ID : UDINT;
	    Length : UDINT;
	  END_STRUCT;
#pragma pack(pop)
	  t_FastUnsafeIOs : BDINT
	  [
	  ];
	  t_FeatureEnable : BDINT
	  [
	    1 TransmissionTimeInConfig,
	    2 FSoEFlags_4ByteSize,
	  ];
#pragma pack(push, 1)
	  t_FlexConNodeState : STRUCT
	    Command : UDINT;
	    StateMachine : UDINT;
	    HostSafetyNumber : HDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_FlexConHostState : STRUCT
	    CurrentReadOffset : UINT;
	    BytesMissing : UINT;
	    LastRequestedLength : USINT;
	    NoConnections : UDINT;
	    aConnections : ARRAY [0..MAX_FSOE_CONNECTIONS-1] OF HDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_FlexCon : STRUCT
	    FoundNodeInConfig : BOOL;
	    FoundHostInConfig : BOOL;
	    pCommand : ^UDINT;
	    pStateMachine : ^UDINT;
	    pHostSafetyNumber : ^HDINT;
	    CommandRequest : UDINT;
	    ud_LastNodeStateTimeStamp : UDINT;
	    LastNodeState : t_FlexConNodeState;
	    ud_LastHostStateTimeStamp : UDINT;
	    LastHostState : t_FlexConHostState;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_FSoEConnections : STRUCT
	    ConnectionNo : UDINT;
	    pFirstConnection : ^t_FSoEConnectionDetails;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_FSoERoutingInfoElement : STRUCT
	    SlaveAddress : UINT;
	    SafeOutputFrameSize : UINT;
	    SafeInputFrameSize : UINT;
	    SafeOutputFrameOffset : UINT;
	    SafeInputFrameOffset : UINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_HGW : STRUCT
	    pThis : pVirtualBase;
	    udMachineNrb : UDINT;
	    hdSafetyNrb : HDINT;
	    sdRequired : DINT;
	    pInputPDO : ^void;
	    pOutputPDO : ^void;
	    InputPDOLength : UINT;
	    OutputPDOLength : UINT;
	    InputPDOOffset : UINT;
	    OutputPDOOffset : UINT;
	    uiSlotID : UINT;
	    uiCommunicationID : UINT;
	    uiTransmissionTime_ms : DINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_LastSDOCmd : STRUCT
	    StructItem : DINT;
	  END_STRUCT;
#pragma pack(pop)
	  t_LogOnceMarker : BDINT
	  [
	    1 NoThisForUnsafeIn,
	    2 WrongUnsafeVarType,
	    4 InvalidSchedulerState,
	    5 InvalidNewInstCmd,
	  ];
#pragma pack(push, 1)
	  t_LogValues : STRUCT
	    ByteLen : UINT;
	    Offset : UINT;
	    DOIndex : UINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_PDOSizeInfo : STRUCT
	    Version : UDINT;
	    PDORdLen : UINT;
	    PDOWrLen : UINT;
	    FastUnsafeAvailable : UDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_PDOSizeInfoV2 : STRUCT
	    Version : UDINT;
	    PDORdLen : UINT;
	    PDOWrLen : UINT;
	    uiNumberFastUnsafeRdVar : UINT;
	    uiNumberFastUnsafeWrVar : UINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_StatusInfo : STRUCT
	    PDOReadLength : UINT;
	    PDOWriteLength : UINT;
	  END_STRUCT;
#pragma pack(pop)
	  UnsafeReadPackages : ARRAY [0..14] OF HDINT;
	  UnsafeSchedulerSSW :
	  (
	    UnsafeSchedulerSetBusReady,
	    UnsafeSchedulerGetDiagVars,
	    UnsafeSchedulerNonPriorData,
	    UnsafeSchedulerWriteData,
	    UnsafeSchedulerReadData,
	    UnsafeSchedulerDiagState,
	    UnsafeSchedulerDiagInfo,
	    UnsafeSchedulerGetRetryCounter,
	    UnsafeSchedulerChannelError,
	    UnsafeSchedulerGetFlexConNodeState:=9,
	    UnsafeSchedulerGetFlexConHostState:=10
	  )$UDINT;
#pragma pack(push, 1)
	  UnsafeWritePackage : STRUCT
	    ud_Address : HDINT;
	    Value : DINT;
	  END_STRUCT;
#pragma pack(pop)
	  UnsafeWritePackages : ARRAY [0..SBF_MAX_UNSAFE_WRITE_PACKAGES-1] OF UnsafeWritePackage;
#pragma pack(push, 1)
	  VaranDOSettingsRd : STRUCT
	    us_Command : USINT;
	    ud_Address : UDINT;
	    us_ByteCnt : USINT;
	    us_CommandRecv : USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  VaranDOSettingsWr : STRUCT
	    us_Command : USINT;
	    ud_Address : UDINT;
	    us_ByteCnt : USINT;
	  END_STRUCT;
#pragma pack(pop)
	END_TYPE
  //Servers:
	ClassSvr 	: SvrChCmd_DINT;
	SafetyState 	: SvrCh_SafetyConfigStateType;
	VoltageOK 	: SvrCh_DINT;
	RunState 	: SvrCh_DINT;
	ServiceMode 	: SvrCh_DINT;
	ErrorState 	: SvrCh_DINT;
	QuitComError 	: SvrCh_DINT;
	FirmwareVersion 	: SvrCh_HDINT;
	SafetyNumber 	: SvrCh_HDINT;
	SafetyRetryCounter 	: SvrCh_DINT;
	DesignerPrjConfigCRC 	: SvrCh_HDINT;
	ApplCRC 	: SvrCh_HDINT;
	HwCRC 	: SvrCh_HDINT;
	GetDiagConnect 	: SvrChCmd_DINT;
  //Clients:
	ToBusInterface 	: CltChCmd_DINT;
	ServerUpdateTime 	: CltCh_UDINT;
	DeviceID 	: CltCh_UDINT;
	FileSys 	: CltChCmd__FileSys;
	ToStdLib 	: CltChCmd__StdLib;
	ToSigCLib 	: CltChCmd_SigCLib;
  //Variables:
		sFirmwareVersion 	: SafetyBaseFSoE::FirmwareVersionType;
		ui_cnt_PDOInputBuffer 	: UINT;
		aPDOInputBuffer : ARRAY [0..SBF_PDO_MAX_NUMBER-1] OF SafetyBaseFSoE::PDOInputBuffer;

		PDOOutputIsUsed 	: BOOL;
		PDOOutputBuffer : ARRAY [0..SBF_SPDO_MAX_LENGTH-1] OF USINT;

		ui_cnt_SDOInputBuffer 	: UINT;
		p_SDOInputBuffer 	: ^UDINT;
		InputBufferAllocatedNo 	: UDINT;
		p_SDOOutputBuffer 	: ^SafetyBaseFSoE::SDORespStd;
		a_RoutTable : ARRAY [0..SBF_MAX_NUMBER_ROUT_ELEMENTS-1] OF SafetyBaseFSoE::RoutElement;

		p_RoutTable 	: ^SafetyBaseFSoE::RoutElement;
		ui_CntRoutElements 	: UINT;
		us_FirstScan 	: USINT;
		ReadDOsUsed 	: UINT;
		aReadPDOs : ARRAY [0..8] OF t_DOInfo;

		ReadFastUnsafeDOsUsed 	: UINT;
		aFastUnsafeReadDOs : ARRAY [0..1] OF t_DOInfo;

		WriteDOsUsed 	: UINT;
		aWritePDOs : ARRAY [0..8] OF t_DOInfo;

		WriteFastUnsafeDOsUsed 	: UINT;
		aFastUnsafeWriteDOs : ARRAY [0..1] OF t_DOInfo;

		TempReadDO 	: t_DOInfo;
		TempWriteDO 	: t_DOInfo;
		pPDOLengthInfoData 	: ^t_StatusInfo;
		pFastUnsafeOutputs 	: ^UDINT;
		pLEDandIOStates 	: ^USINT;
		pFastUnsafeInputs 	: ^UDINT;
		pUnsafeOutWrData 	: ^USINT;			//! <Variable Comment="pointer to unsafe enable signals (only valid if outputs are available)" Name="pUnsafeOutWrData"/>
		pGPDR1Rd 	: ^UINT;
		pGPDR1Wr 	: ^UINT;
		p_Handle_SDO_Read 	: ^SafetyBaseFSoE::SDOBufferStructure;
		p_Handle_SDO_Write 	: ^SafetyBaseFSoE::SDOBufferStructure;
		pISafety 	: ^LSL_SAFETY;
		us_CntSequNr 	: USINT;
		ui_CntUnsafeInputs 	: UINT;
		ui_CntFastUnsafeInputs 	: UINT;
		MaxVarsPerWritePackage 	: USINT;
		WriteFramesNeeded 	: UDINT;
		p_UnsafeInputsTable 	: ^SafetyBaseFSoE::UnsafeIORoutingElement;
		p_FastUnsafeInputsTable 	: ^UnsafeIORoutingElement;
		ui_CntUnsafeOutputs 	: UINT;
		ui_CntFastUnsafeOutputs 	: UINT;
		MaxVarsPerReadPackage 	: USINT;
		ReadFramesNeeded 	: UDINT;
		p_UnsafeOutputsTable 	: ^SafetyBaseFSoE::UnsafeIORoutingElement;
		p_FastUnsafeOutputsTable 	: ^UnsafeIORoutingElement;
		StandardRequest 	: SafetyBaseFSoE::StandardFrameType;
		StandardResponse 	: SafetyBaseFSoE::StandardFrameType;
		StandardSeqNr 	: USINT;
		UnsafeReadTransNr 	: UDINT;
		UnsafeWriteTransNr 	: UDINT;
		UnsafeWritingActive 	: BOOL;
		LastUnsafeUpdate 	: UDINT;
		pUnsafeOutElement 	: ^SafetyBaseFSoE::UnsafeIORoutingElement;
		pUnsafeInElement 	: ^SafetyBaseFSoE::UnsafeIORoutingElement;
		pSafetyCDIASBase 	: ^SafetyCDIAS_Base;			//! <Variable Comment="this pointer exists just to create a dependency to class SafetyCDIAS_Base (we need the global variables)" Name="pSafetyCDIASBase"/>
		b_CheckCRC 	: BOOL;
		ud_LastCrcCheck 	: UDINT;
		b_GetNewConfiguration 	: BOOL;
		b_CheckConfigured 	: BOOL;
		ud_LastCheckConfig 	: UDINT;
		LastRequest 	: SafetyBaseFSoE::RequestType;
		PDOSingleRunActive 	: USINT;
		DiagVars 	: SafetyBaseFSoE::DiagStructType;
		us_SetBusReadyState 	: SafetyBaseFSoE::SetBusReadySSW;
		UnsafeScheduler 	: SafetyBaseFSoE::UnsafeSchedulerSSW;
		ud_UnsafeTimeoutCounter 	: UDINT;
		ud_TransfertimePerPackage 	: UDINT;
		p_BlockedTransferMemory 	: ^void;
		ud_BlockedTransferMemorySize 	: UDINT;
		b_ReadConfigFailed 	: BOOL;
		ud_ServerUpdateTime 	: UDINT;
		ud_UnsafeVarsNotFound 	: UDINT;			//! <Variable Comment="remembers how many servers for unsafe variables could not be found during the readout of the configuration (is reset after showing the error _UnsafeVarNotFound)" Name="ud_UnsafeVarsNotFound"/>
		InitStep 	: DINT;			//! <Variable Comment="contains the actual init step (0-2)" Name="InitStep"/>
		b_ManagerInitialisationFinished 	: BOOL;			//! <Variable Comment="TRUE means the safety manager is ready and we can start with PDO and TDO transfers as well as standard SDO transfers (before only SSDOs between modules are possible)" Name="b_ManagerInitialisationFinished"/>
		PrjNameMemLen 	: UDINT;			//! <Variable Comment="length of memory for project name and project revision (is combined in 1 string, seperated by a zero termination)" Name="PrjNameMemLen"/>
		PrjNameStrLen 	: UDINT;			//! <Variable Comment="length of project name string (pPrjNameStr)" Name="PrjNameStrLen"/>
		pPrjNameStr 	: ^CHAR;			//! <Variable Comment="pointer to a project name string if available" Name="pPrjNameStr"/>
		PrjRevStrLen 	: UDINT;			//! <Variable Comment="length of project revision string (pPrjRevStr)" Name="PrjRevStrLen"/>
		pPrjRevStr 	: ^CHAR;			//! <Variable Comment="pointer to a project revision string if available" Name="pPrjRevStr"/>
		b_GotNewRevision 	: BOOL;			//! <Variable Comment="remembers if there was a block SB_BLKTYPE_CFG_REV in the configuration which holds a valid project name and revision to display on servers&#13;&#10;if there is none, the servers show &quot;&lt;not available&gt;&quot;" Name="b_GotNewRevision"/>
		PDOWrite 	: SafetyBaseFSoE::t_DataObjectCheck;			//! <Variable Comment="only for debug reasons to check for different frames and the time difference between them (including mininum and maximum)" Name="PDOWrite"/>
		PDORead 	: SafetyBaseFSoE::t_DataObjectCheck;			//! <Variable Comment="only for debug reasons to check for different frames and the time difference between them (including mininum and maximum)" Name="PDORead"/>
		SDOWrite 	: SafetyBaseFSoE::t_DataObjectCheck;			//! <Variable Comment="only for debug reasons to check for different frames and the time difference between them (including mininum and maximum)" Name="SDOWrite"/>
		SDORead 	: SafetyBaseFSoE::t_DataObjectCheck;			//! <Variable Comment="only for debug reasons to check for different frames and the time difference between them (including mininum and maximum)" Name="SDORead"/>
		pConfigMem 	: ^void;			//! <Variable Comment="pointer to memory to hold the local configuration (extended in 512 byte block steps)" Name="pConfigMem"/>
		ConfigMemSize 	: UDINT;			//! <Variable Comment="size allocated for the config memory at pConfigMem" Name="ConfigMemSize"/>
		ConfigMemSizeUsed 	: UDINT;			//! <Variable Comment="size used from the config memory (pConfigMem)" Name="ConfigMemSizeUsed"/>
		ModuleCfgNo 	: USINT;			//! <Variable Comment="number of modules found in the configuration. for the linked list of them see pFirstModuleCfg" Name="ModuleCfgNo"/>
		pFirstModuleCfg 	: ^t_ModuleListElem;			//! <Variable Comment="pointer to a linked list where the modules found in the configuration are stored" Name="pFirstModuleCfg"/>
		PdoCfgNo 	: USINT;			//! <Variable Comment="number of PDO informations in the linked list (pFirstPdoCfg)" Name="PdoCfgNo"/>
		pFirstPdoCfg 	: ^t_PdoListElem;			//! <Variable Comment="if the configurations holds PDO information, it will be stored in this linked list" Name="pFirstPdoCfg"/>
		SlaveCfgNo 	: USINT;			//! <Variable Comment="if this module holds slave configurations (organized as linked list), this is the number of slave configurations available in the linked list (pFirstSlaveCfg)" Name="SlaveCfgNo"/>
		pFirstSlaveCfg 	: ^t_SlaveListElem;			//! <Variable Comment="if this module holds slave configurations (organized as linked list), this is the pointer to the first" Name="pFirstSlaveCfg"/>
		MyPDOReadSize 	: UINT;			//! <Variable Comment="contains the size needed for the read interface PDO. this value is used to create the data object." Name="MyPDOReadSize"/>
		MyPDOWriteSize 	: UINT;			//! <Variable Comment="contains the size needed for the write PDOs. this value is used to create the data object." Name="MyPDOWriteSize"/>
		uiPDOReadSizeFastUnsafeVar 	: UINT;
		uiPDOWriteSizeFastUnsafeVar 	: UINT;
		OldRunState 	: DINT;			//! <Variable Comment="remembers the oldrunstate&#13;&#10;&#13;&#10;necessary to detect the positive edge, which means the safety cpu has started running&#13;&#10;in this case all unsafe input variables (actual server values) are sent to the safety cpu" Name="OldRunState"/>
		b_FastUnsafeInputsActive 	: BOOL;			//! <Variable Comment="TRUE if there are fast unsafe inputs in the configuration which the class has to handle" Name="b_FastUnsafeInputsActive"/>
		b_FastUnsafeOutputsActive 	: BOOL;			//! <Variable Comment="TRUE if there are fast unsafe outputs in the configuration which the class has to handle" Name="b_FastUnsafeOutputsActive"/>
		NewConfigType 	: t_ConfigStateType;			//! <Variable Comment="this variable remembers the config format of the module&#13;&#10;new means, it does support the new time sync method (hw class triggers time sync)&#13;&#10;if there is any safety module which does not support the new method, all modules have to use the old method" Name="NewConfigType"/>
		bd_LogOnceMarker 	: SafetyBaseFSoE::t_LogOnceMarker;			//! <Variable Comment="if an error message should be printed only once even if the error occurs multiple times, this variable is used to remember the messages already printed" Name="bd_LogOnceMarker"/>
		ud_OptionalTimeoutStart 	: UDINT;			//! <Variable Comment="ops.tabsolute timer when the safety manager is finished with adminstrative work&#13;&#10;if there are optional modules we don&apos;t wait forever for everything to get synchron because the optional modules may not be available" Name="ud_OptionalTimeoutStart"/>
		ud_AsyComRetryCounter 	: UDINT;			//! <Variable Comment="counts up if the actual SDO command fails.&#13;&#10;if a certain number of retries also fail an error message will be generated" Name="ud_AsyComRetryCounter"/>
		b_ManagerReadyForSDO 	: BOOL;			//! <Variable Comment="TRUE means the safety manager has finished first time connecting the domains or there is no safety manager&#13;&#10;SDO transfer only works if this is true" Name="b_ManagerReadyForSDO"/>
		NeedsHwControl 	: ^HwControl;			//! <Variable Comment="this is needed for some global variable declaration" Name="NeedsHwControl"/>
		b_ManualQuit 	: BOOL;			//! <Variable Comment="this marks if a manual quit of communication errors has been done&#13;&#10;the quit mechanism will resend the bus ready command, where normally no PDOs are sent before the errors are being quit&#13;&#10;this variable is used to override the waiting for the bus ready to be finished and allows PDOs to be sent during the bus ready resend." Name="b_ManualQuit"/>
		b_ModuleAccessReady 	: BOOL;
		Online 	: DINT;			//! <Variable Comment="connection state of the module&#13;&#10;0 = not connected&#13;&#10;1 = connected and access init and module init was successful&#13;&#10;&#13;&#10;every action that requires a connected module has to use this variable" Name="Online"/>
		b_VaranAvailable 	: BOOL;			//! <Variable Comment="TRUE if there is a VARAN between the SPS and the module. Move-DOs can only be installed if this is true." Name="b_VaranAvailable"/>
		p_ObjectPath 	: ^USINT;			//! <Variable Comment="pointer to the object path which is needed to add the module to the safety task and get the pointers for SDO handling from the OS" Name="p_ObjectPath"/>
		InputsCount 	: USINT;			//! <Variable Comment="holds the amount of projected safe inputs" Name="InputsCount"/>
		OutputsCount 	: USINT;			//! <Variable Comment="holds the amount of projected safe outputs" Name="OutputsCount"/>
		DOStartFailCounter 	: UDINT;			//! <Variable Comment="counts up if starting a DO fails" Name="DOStartFailCounter"/>
		DOStopFailCounter 	: UDINT;			//! <Variable Comment="counts up if stopping a DO fails" Name="DOStopFailCounter"/>
		b_DOsAlreadyStarted 	: BOOL;			//! <Variable Comment="FALSE at the first time when the DOs should be started&#13;&#10;&#13;&#10;then the pointers to the write and read data are going to be corrected&#13;&#10;&#13;&#10;TRUE after the pointer correction (only needs to be done once)" Name="b_DOsAlreadyStarted"/>
		b_Deactivated 	: BOOL;			//! <Variable Comment="TRUE if the object is disabled through any circumstances (invalid place, varan connection transparent...)" Name="b_Deactivated"/>
		ud_ConfigReadTime 	: UDINT;			//! <Variable Comment="time for the last config readout in µs" Name="ud_ConfigReadTime"/>
		PdoSizeInfo 	: t_PDOSizeInfo;			//! <Variable Comment="info from last readout (last config change, boot of project, hardware connected,..)&#13;&#10;&#13;&#10;info which has been used to create the DOs can be found on:&#13;&#10;MyPDOReadSize,&#13;&#10;MyIFPDOReadSize,&#13;&#10;MyPDOWriteSize,&#13;&#10;AdditionalSPDOs,&#13;&#10;ActivateFastUnsafeIOs" Name="PdoSizeInfo"/>
		PdoSizeInfoV2 	: t_PDOSizeInfoV2;
		ActivateFastUnsafeIOs 	: BOOL;			//! <Variable Comment="holds the information, if the fast unsafe variables has been activated in the safety designer project" Name="ActivateFastUnsafeIOs"/>
		b_Reconnect 	: BOOL;
		a_RetryCnt : ARRAY [0..31] OF USINT;
			//! <Variable Comment="Array of data about the safetyretrycounter from the module." Name="a_RetryCnt"/>
		ud_RetryCntTimeMark 	: UDINT;			//! <Variable Comment="Timestamp of last retry coutner readout." Name="ud_RetryCntTimeMark"/>
		us_OldRetryCounter 	: USINT;			//! <Variable Comment="Marker for old retry counter." Name="us_OldRetryCounter"/>
		CachedAddr 	: UDINT;
		CachedSize 	: UDINT;
		Cache : ARRAY [0..SBF_SDO_MAX_LENGTH-1] OF USINT;

		b_RetryCounterAvailable 	: BOOL;			//! <Variable Comment="Shows if a retry counter readout is available. Depends on the firmware version." Name="b_RetryCounterAvailable"/>
		SchedulerStepNo 	: DINT;			//! <Variable Comment="Shows the amount of steps of the scheduler" Name="SchedulerStepNo"/>
		QuitErrorAvailable 	: DINT;
		QuitErrorParameter 	: DINT;
		ConnectionInfos 	: t_FSoEConnections;
		b_LogFirstDone 	: USINT;
		InvalidPDOSizeCnt 	: DINT;
		aLogValues : ARRAY [0..SBF_PDO_MAX_NUMBER-1] OF t_LogValues;

		b_NeedApplicationRestart 	: BOOL;
		b_NoSafetyLogged 	: BOOL;
		b_DisableSafetyRetryCnt 	: BOOL;
		bdFeatureEnable 	: t_FeatureEnable;
		b_ReInitMarker 	: BOOL;
		b_PostInitPassed 	: BOOL;
		aErrorMem : ARRAY [0..256] OF USINT;

		remainingPacketlength 	: INT;
		actoffset 	: UDINT;
		sHGW 	: t_HGW;
		b_SetSysTime 	: BOOL;
		ud_LastSetSysTime 	: UDINT;
		sFlexCon 	: t_FlexCon;
		OldFakePDORead 	: PDOType;
		OldFakePDOWrite 	: PDOType;
		b_WaitForFirmwareBoot 	: BOOL;
		DeviceIDOk 	: BOOL;
		CRCInfo 	: t_CRCInfo;
		uiNumberFastUnsafeWr 	: UINT;
		uiNumberFastUnsafeRd 	: UINT;
		pFastUnsafeOutBuffer 	: pVoid;
		pFastUnsafeInBuffer 	: pVoid;
		usAnalogInputByteSize 	: USINT;
		usAnalogInputReadDOsUsed 	: USINT;
		aAnalogInputReadDOs : ARRAY [0..1] OF t_DOInfo;

		udConfigValid 	: UDINT;
  //Functions:
	
	FUNCTION VIRTUAL GLOBAL Init;
				//! <Function Comment="constructor" Name="SafetyBaseFSoE"/>
	FUNCTION SafetyBaseFSoE
		VAR_OUTPUT
			ret_code 	: ConfStates;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL CyWork
		VAR_INPUT
			EAX 	: UDINT;
		END_VAR
		VAR_OUTPUT
			state (EAX) 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL RtWork
		VAR_INPUT
			EAX 	: UDINT;
		END_VAR
		VAR_OUTPUT
			state (EAX) 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL Background
		VAR_INPUT
			EAX 	: UDINT;
		END_VAR
		VAR_OUTPUT
			state (EAX) 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL CheckVariant
		VAR_INPUT
			us_ActVariant 	: USINT;
		END_VAR
		VAR_OUTPUT
			b_IsOK 	: BOOL;
		END_VAR;
				//! <Function Comment="add SDO to incoming message buffer; This function is called from the SDO sending class." Name="AddSDOToBuffer"/>
	FUNCTION VIRTUAL AddSDOToBuffer
		VAR_INPUT
			p_us_message 	: ^USINT;
			us_length 	: USINT;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="write incoming PDO from other module to hardware " Name="WritePDO2Module"/>
	FUNCTION VIRTUAL WritePDO2Module;
	
	FUNCTION VIRTUAL InitSafetyBase
		VAR_OUTPUT
			retcode 	: BOOL;
		END_VAR;
	
	FUNCTION VIRTUAL InitBuffers
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="allocate buffers for reading config data and creating routing tables" Name="GetRoutingData"/>
	FUNCTION VIRTUAL GetRoutingData
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="read config data via SDOs and fill routing tables" Name="ReadSDOConfig"/>
	FUNCTION VIRTUAL ReadSDOConfig
		VAR_INPUT
			p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
			p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="send request for reading config data" Name="SDOReadCfgRequ"/>
	FUNCTION VIRTUAL SDOReadCfgRequ
		VAR_INPUT
			p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
			p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
			ud_Address 	: UDINT;
			us_Length 	: USINT;
			b_UseCache 	: BOOL := TRUE;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="send request with no additional data" Name="SDOStdRequ"/>
	FUNCTION VIRTUAL SDOStdRequ
		VAR_INPUT
			us_Command 	: USINT;
			p_us_RespData 	: ^USINT;
			us_RespLength 	: USINT;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="add data objects for PDO, state etc." Name="AddDO"/>
	FUNCTION VIRTUAL AddDO
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
				//! <Function Comment="start data objects" Name="StartDO"/>
	FUNCTION VIRTUAL StartDO;
				//! <Function Comment="stop data objects" Name="StopDO"/>
	FUNCTION VIRTUAL StopDO;
				//! <Function Comment="returns pointer to the own outgoing PDO buffer" Name="GetPointer2PDOBuffer"/>
	FUNCTION VIRTUAL GetPointer2PDOBuffer
		VAR_OUTPUT
			ud_PDOBuffer 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL AddUnsafeRoutElement
		VAR_INPUT
			pRoutTable 	: ^SafetyBaseFSoE::pUnsafeIORoutingElement;
			pRoutElementsNo 	: ^UINT;
			ud_Address 	: UDINT;
			ui_NameLen 	: UINT;
			pNameNewElement 	: ^CHAR;
			BitMask 	: BDINT := 0;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL AddFastUnsafeRoutElement
		VAR_INPUT
			pRoutTable 	: ^pUnsafeIORoutingElement;
			pRoutElementsNo 	: ^UINT;
			ud_Address 	: UDINT;
			ui_NameLen 	: UINT;
			pNameNewElement 	: ^CHAR;
			BitMask 	: BDINT := 0;
			bWriteVar 	: BOOL;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="read this-pointer from correct object and add it to the routing tables" Name="AddModuleToRoutingTabs"/>
	FUNCTION VIRTUAL AddModuleToRoutingTabs
		VAR_INPUT
			pModule 	: ^SafetyBaseFSoE::PathInfoType;
			pListHeader 	: ^SafetyBaseFSoE::PathInfoType;
			b_IsOptional 	: BOOL;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="add module to routing table" Name="AddRoutingElement"/>
	FUNCTION VIRTUAL AddRoutingElement
		VAR_INPUT
			ud_SafeNumber 	: HDINT;
			ud_thisp 	: UDINT;
			pPathInfo 	: ^SafetyBaseFSoE::PathInfoType;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="get this-pointer of routing-element based on safety-number" Name="GetRoutingPointerByNumber"/>
	FUNCTION VIRTUAL GetRoutingPointerByNumber
		VAR_INPUT
			ud_SafeNumber 	: HDINT;
		END_VAR
		VAR_OUTPUT
			ud_thisp 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL UpdateSafetyNbrAtRoutingElement
		VAR_INPUT
			PathLen 	: UINT;
			pCompressedPath 	: ^USINT;
			SafetyNbr 	: HDINT;
		END_VAR
		VAR_OUTPUT
			Retval 	: DINT;
		END_VAR;
				//! <Function Comment="This function is called during initialization by the Safe-CPU class." Name="ConfigSafetyInput"/>
	FUNCTION VIRTUAL ConfigSafetyInput
		VAR_INPUT
			ud_SafeNrCPU 	: HDINT;
			ud_thisp 	: UDINT;
			p_PDOBuffer 	: ^UDINT;
		END_VAR
		VAR_OUTPUT
			ud_SafeNrModul 	: HDINT;
		END_VAR;
				//! <Function Comment="This function is called during initialization by the Safe-CPU class." Name="ConfigSafetyOutput"/>
	FUNCTION VIRTUAL ConfigSafetyOutput
		VAR_INPUT
			ud_SafeNrCPU 	: HDINT;
			ud_thisp 	: UDINT;
			ud_p_PDOBuffer 	: UDINT;
			pObject 	: ^PathInfoType;
			b_IsOptional 	: BOOL;
		END_VAR
		VAR_OUTPUT
			ud_SafeNrModul 	: HDINT;
		END_VAR;
				//! <Function Comment="get this-pointer of routing-element based on Crc of hardware-path" Name="GetRoutingPointerByPath"/>
	FUNCTION VIRTUAL GetRoutingPointerByPath
		VAR_INPUT
			p_ActObjectPath 	: ^USINT;
			ActPathLen 	: UINT;
		END_VAR
		VAR_OUTPUT
			ud_thisp 	: UDINT;
		END_VAR;
				//! <Function Comment="add PDO buffer to list of PDO input buffers" Name="AddPDOToInputBuffer"/>
	FUNCTION VIRTUAL AddPDOToInputBuffer
		VAR_INPUT
			ud_p2Buffer 	: UDINT;
			ud_SafetyNr 	: HDINT;
			b_IsOptional 	: BOOL;
			ui_PdoLen 	: UINT := 0;
			ui_TargetOffset 	: UINT := 0;
			b_IsInterfaceFrame 	: BOOL := FALSE;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL ConfigSafetyCPU
		VAR_OUTPUT
			ud_SafeNrModul 	: HDINT;
		END_VAR;
				//! <Function Comment="get pointer on PDO input buffer element" Name="GetPDOInputBuffer"/>
	FUNCTION VIRTUAL GetPDOInputBuffer
		VAR_INPUT
			ud_p2Buffer 	: UDINT;
			ud_SafetyNr 	: HDINT;
			p_FoundSafetyNr 	: ^BOOL := NIL;
			b_IsInterfaceFrame 	: BOOL;
			ui_TargetOffset 	: UINT;
		END_VAR
		VAR_OUTPUT
			p_InputBuffer 	: ^SafetyBaseFSoE::PDOInputBuffer;
		END_VAR;
				//! <Function Comment="Add module to safety task in OS and get pointers on SDO buffers." Name="AddModuleToSafetyTask"/>
	FUNCTION VIRTUAL AddModuleToSafetyTask
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="The methode returns the module ID which the module must have. It has to be overwritten." Name="GetModuleID"/>
	FUNCTION VIRTUAL GetModuleID
		VAR_OUTPUT
			ud_ModuleID 	: UDINT;
		END_VAR;
				//! <Function Comment="Returns the task priority; A value &lt;&gt; 0 changes the order of executing the update methods. 0 = highest = default priority;" Name="GetTaskPriority"/>
	FUNCTION VIRTUAL GetTaskPriority
		VAR_OUTPUT
			us_TaskPriority 	: USINT;
		END_VAR;
				//! <Function Comment="initialize safety module (e.g. CPU) specific data" Name="InitSafeModule"/>
	FUNCTION VIRTUAL InitSafeModule
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="requests own safety number from module via SDO communication" Name="GetSafetyNumber"/>
	FUNCTION VIRTUAL GetSafetyNumber
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="update of non SDO and PDO data" Name="UpDateNotStd"/>
	FUNCTION VIRTUAL UpDateNotStd
		VAR_INPUT
			calloption 	: UDINT;
		END_VAR;
				//! <Function Comment="extract module path out of config data" Name="ReadModulePath"/>
	FUNCTION VIRTUAL ReadModulePath
		VAR_INPUT
			p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
			p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
			p_ud_Address 	: ^UDINT;
			pModule 	: ^SafetyBaseFSoE::PathInfoType;
			ud_ModulePathLen_Max 	: UDINT;
			ud_ModulePathCompLen_Max 	: UDINT;
			us_SDOMaxDataSize 	: USINT;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
				//! <Function Comment="returns the pointer on routing element with searched this-pointer" Name="GetRoutingElementByThis"/>
	FUNCTION VIRTUAL GetRoutingElementByThis
		VAR_INPUT
			ud_thisp 	: UDINT;
		END_VAR
		VAR_OUTPUT
			p_RoutElement 	: ^SafetyBaseFSoE::RoutElement;
		END_VAR;
	
	FUNCTION VIRTUAL GetThisViaServerName
		VAR_INPUT
			pObjectDotServer 	: ^CHAR;
		END_VAR
		VAR_OUTPUT
			ThisPtr 	: ^void;
		END_VAR;
	
	FUNCTION VIRTUAL LogValue
		VAR_INPUT
			pString 	: ^CHAR;
			Value 	: UDINT;
			Value2 	: UDINT := 16#FFFFFFFF;
			Value3 	: UDINT := 16#FFFFFFFF;
		END_VAR;
				//! <Function Comment="log error messages" Name="LogError"/>
	FUNCTION VIRTUAL LogError
		VAR_INPUT
			e_msg 	: ^CHAR;
		END_VAR;
	
	FUNCTION VIRTUAL InitPathInfo
		VAR_INPUT
			pPathInfo 	: ^SafetyBaseFSoE::PathInfoType;
			pInitPath 	: ^USINT := NIL;
			pInitPathComp 	: ^USINT := NIL;
		END_VAR;
	
	FUNCTION VIRTUAL ResetConfig;
	
	FUNCTION VIRTUAL RemoveCPUFromTable
		VAR_INPUT
			ud_SafetyNr 	: HDINT;
		END_VAR;
	
	FUNCTION VIRTUAL CheckUnsafeUpdateCycle;
				//! <Function Comment="Make a request to fetch the diagnosis data (RunState, ConfigState, Login Level, ErrorCounterIOState)" Name="FetchDiagState"/>
	FUNCTION VIRTUAL GLOBAL FetchDiagState;
				//! <Function Comment="Check if the diagnosis data have arrived (if so, retcode = TRUE)." Name="GetDiagState"/>
	FUNCTION VIRTUAL GLOBAL GetDiagState
		VAR_INPUT
			pDiagState 	: ^SafetyDiagState;			//! <Variable Comment="Pointer to the data where the diagnosis state should be written." Name="GetDiagState.pDiagState"/>
		END_VAR
		VAR_OUTPUT
			StateReady 	: BOOL;			//! <Variable Comment="FALSE...State is not ready (value of &quot;input parameter&quot; not changed)&#13;&#10;TRUE....State is ready (input parameter)" Name="GetDiagState.StateReady"/>
		END_VAR;
				//! <Function Comment="Make a request to fetch the diagnosis information data (Controller ID, Act Error Code, First Error Code, Reasoncode 0,1)" Name="FetchDiagInfo"/>
	FUNCTION VIRTUAL GLOBAL FetchDiagInfo
		VAR_INPUT
			ucChoice 	: USINT;			//! <Variable Comment="   0..µC1&#13;&#10;&lt;&gt; 0..µC2" Name="FetchDiagInfo.ucChoice"/>
		END_VAR;
				//! <Function Comment="Get diagnosis information of the safety CPU.&#13;&#10;Check if the diagnosis data have arrived (if so, retcode = TRUE)." Name="GetDiagInfo"/>
	FUNCTION VIRTUAL GLOBAL GetDiagInfo
		VAR_INPUT
			pDiagInfo 	: ^SafetyDiagInfo;			//! <Variable Comment="Pointer to the data where the diagnosis information should be written." Name="GetDiagInfo.pDiagInfo"/>
		END_VAR
		VAR_OUTPUT
			StateReady 	: BOOL;			//! <Variable Comment="FALSE...No new data available&#13;&#10;TRUE....New Data available" Name="GetDiagInfo.StateReady"/>
		END_VAR;
				//! <Function Comment="saves the log file from a specified µC to a file (not readable for user, send it to sigmatek support)" Name="SaveLog"/>
	FUNCTION VIRTUAL GLOBAL SaveLog
		VAR_INPUT
			ucChoice 	: USINT;			//! <Variable Comment="   0..µC1&#13;&#10;&lt;&gt; 0..µC2" Name="SaveLog.ucChoice"/>
			pDPNE 	: ^CHAR;			//! <Variable Comment="Pointer to the Directory Path Name Extension (e.g. C:\Filename.bin)" Name="SaveLog.pDPNE"/>
		END_VAR
		VAR_OUTPUT
			Success 	: DINT;			//! <Variable Comment="TRUE...saving log to drive is starting&#13;&#10;FALSE..couldn&apos;t start, other log saving process running" Name="SaveLog.Success"/>
		END_VAR;
				//! <Function Comment="Returns the state if Save Log is active or not." Name="SaveLogActive"/>
	FUNCTION VIRTUAL GLOBAL SaveLogActive
		VAR_OUTPUT
			InProgress 	: BOOL;			//! <Variable Comment="0 .. Save Log not active.&#13;&#10;1 .. Save Log is active." Name="SaveLogActive.InProgress"/>
		END_VAR;
	
	FUNCTION VIRTUAL SDORequ
		VAR_INPUT
			us_Command 	: USINT;
			p_us_RequData 	: ^USINT;
			us_RequLength 	: USINT;
			p_us_RespData 	: ^USINT;
			us_RespLength 	: USINT;
		END_VAR
		VAR_OUTPUT
			sd_retval 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL BlockedTransferLength
		VAR_INPUT
			ud_Datalength 	: UDINT;
			us_MaxBlocklength 	: USINT;
		END_VAR
		VAR_OUTPUT
			us_ActBlocklength 	: USINT;
		END_VAR;
	
	FUNCTION VIRTUAL RecordFrame
		VAR_INPUT
			pFrame 	: ^DINT;
			FrameState 	: DINT;
			IsFSoEPDO 	: BOOL := FALSE;
		END_VAR;
	
	FUNCTION VIRTUAL AddFrame
		VAR_INPUT
			pFrame 	: ^t_SDOStdHeader;
			FrameState 	: DINT;
			CallerSafetyNr 	: HDINT;
		END_VAR;
	
	FUNCTION VIRTUAL ShowProjectRevision;
	
	FUNCTION DOCheck
		VAR_INPUT
			pFrame 	: ^t_SDOStdHeader;
			pCheckStruct 	: ^SafetyBaseFSoE::t_DataObjectCheck;
		END_VAR;
	
	FUNCTION VIRTUAL CalcPDOLength
		VAR_INPUT
			BoolVarNo 	: UDINT;
			DintVarNo 	: UDINT;
		END_VAR
		VAR_OUTPUT
			PdoLen 	: USINT;
		END_VAR;
	
	FUNCTION VIRTUAL AddModuleToCfg
		VAR_INPUT
			ModuleCfg 	: t_ModuleCfg;
		END_VAR
		VAR_OUTPUT
			retcode 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL AddPdoToCfg
		VAR_INPUT
			PdoCfg 	: t_PdoCfg;
		END_VAR
		VAR_OUTPUT
			retcode 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL AddSlaveToCfg
		VAR_INPUT
			pModule 	: ^SafetyBaseFSoE::PathInfoType;
			b_IsOptional 	: BOOL;
			b_IsInterface 	: BOOL := FALSE;
			InterfaceCRC 	: HDINT := 0;
		END_VAR;
	
	FUNCTION VIRTUAL FreeCfgLists;
	
	FUNCTION VIRTUAL CreateAndSendCfg;
	
	FUNCTION SendAllUnsafeValues;
	
	FUNCTION GetDestinationCRCs;
	
	FUNCTION CountBits
		VAR_INPUT
			BitMask 	: BDINT;
		END_VAR
		VAR_OUTPUT
			BitNo 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL IsOptimizedCPU
		VAR_OUTPUT
			IsOptimized 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL CheckConfigMemSize
		VAR_INPUT
			AddConfigSize 	: UDINT;
		END_VAR
		VAR_OUTPUT
			pCfg 	: ^void;
		END_VAR;
	
	FUNCTION DToH
		VAR_INPUT
			pt 	: ^CHAR;
			value 	: DINT;
		END_VAR;
	
	FUNCTION LogHWPath
		VAR_INPUT
			pString 	: ^CHAR;
			pHWPath 	: ^USINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL ConnectEvent;
	
	FUNCTION VIRTUAL GLOBAL DisconnectEvent;
	
	FUNCTION VIRTUAL GLOBAL InterruptEvent;
	
	FUNCTION SetDORunState
		VAR_INPUT
			Handle 	: UDINT;
			NewState 	: t_BusIFRunState;
		END_VAR;
	
	FUNCTION VIRTUAL GetConfiguration;
	
	FUNCTION VIRTUAL SetSafetyState
		VAR_INPUT
			NewState 	: SafetyConfigStateType;
		END_VAR;
	
	FUNCTION VIRTUAL RefreshAsySrv;
	
	FUNCTION VIRTUAL SetRetryCounter;
	
	FUNCTION IsInCache
		VAR_INPUT
			ud_Address 	: UDINT;
			us_Length 	: USINT;
		END_VAR
		VAR_OUTPUT
			ItsInside 	: BOOL;
		END_VAR;
	
	FUNCTION AddToCache
		VAR_INPUT
			p_SDORequestBuffer 	: ^SDORequReadConfig;
			p_SDOResponseBuffer 	: ^SDORespReadConfig;
		END_VAR;
	
	FUNCTION GetFSoEConnectionInfos
		VAR_INPUT
			p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
			p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
		END_VAR;
	
	FUNCTION FreeFSoEConnectionInfos
		VAR_INPUT
			bFreeAlways 	: BOOL := false;
		END_VAR;
	
	FUNCTION CheckFSoEConnections;
	
	FUNCTION GetNewMemSize
		VAR_INPUT
			Required 	: UDINT;
		END_VAR
		VAR_OUTPUT
			NextBiggerBlockSize 	: UDINT;
		END_VAR;
	
	FUNCTION CheckAddDynSlaveResponse
		VAR_INPUT
			pAddDynSlaveRsp 	: ^t_AddDynSlaveResponse;
		END_VAR
		VAR_OUTPUT
			ResponseOk 	: BOOL;
		END_VAR;
	
	FUNCTION GetChannelInfo
		VAR_INPUT
			pErrorMemUser 	: ^UINT;
			pErrorMem 	: ^USINT;
			ErrorDataLength 	: INT;
		END_VAR
		VAR_OUTPUT
			StateReady 	: BOOL;
		END_VAR;
	
	FUNCTION VIRTUAL GetDiagVisualOffsetErrorCode
		VAR_OUTPUT
			udOffset 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL SafeActModuleConfig
		VAR_INPUT
			sActModuleConfig 	: t_ModuleCfg;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL FetchChannelInfo;
	
	FUNCTION VIRTUAL GLOBAL QuitError
		VAR_INPUT
			Mode 	: DINT;
		END_VAR
		VAR_OUTPUT
			Retval 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL ClassSvr::NewInst
		VAR_INPUT
			pPara 	: ^CmdStruct;
			pResult 	: ^results;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL QuitComError::Write
		VAR_INPUT
			input (EAX) 	: DINT;
		END_VAR
		VAR_OUTPUT
			result (EAX) 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL GetDiagConnect::NewInst
		VAR_INPUT
			pPara 	: ^CmdStruct;
			pResult 	: ^results;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
  //Tables:
	FUNCTION @STD
		VAR_OUTPUT
			ret_code	: CONFSTATES;
		END_VAR;
	FUNCTION GLOBAL TAB @CT_;
END_CLASS;

#pragma usingLtd _FileSys
#pragma usingLtd _StdLib
#pragma usingLtd SigCLib


//}}LSL_DECLARATION


FUNCTION GLOBAL TAB SafetyBaseFSoE::@CT_
0$UINT,
2#0100000000000000$UINT, //TY_SAFETYBASEFSOE
1$UINT, 38$UINT, (SIZEOF(::SafetyBaseFSoE))$UINT, 
14$UINT, 6$UINT, 0$UINT, 
TO_UDINT(3466252013), "SafetyBaseFSoE", //Class
TO_UDINT(0), 0, 0$UINT, 0$UINT, //Baseclass
//Servers:
(::SafetyBaseFSoE.ClassSvr.pMeth)$UINT, _CH_CMD$UINT, 2#0000000000000000$UINT, TO_UDINT(619352855), "ClassSvr", 
(::SafetyBaseFSoE.SafetyState.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(1504224158), "SafetyState", 
(::SafetyBaseFSoE.VoltageOK.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(2158378107), "VoltageOK", 
(::SafetyBaseFSoE.RunState.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(2759549320), "RunState", 
(::SafetyBaseFSoE.ServiceMode.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(1978843086), "ServiceMode", 
(::SafetyBaseFSoE.ErrorState.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(2975757027), "ErrorState", 
(::SafetyBaseFSoE.QuitComError.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(1389350282), "QuitComError", 
(::SafetyBaseFSoE.FirmwareVersion.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(389440282), "FirmwareVersion", 
(::SafetyBaseFSoE.SafetyNumber.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(2430374202), "SafetyNumber", 
(::SafetyBaseFSoE.SafetyRetryCounter.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(1159647258), "SafetyRetryCounter", 
(::SafetyBaseFSoE.DesignerPrjConfigCRC.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(349909197), "DesignerPrjConfigCRC", 
(::SafetyBaseFSoE.ApplCRC.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(2412485999), "ApplCRC", 
(::SafetyBaseFSoE.HwCRC.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(3992965099), "HwCRC", 
(::SafetyBaseFSoE.GetDiagConnect.pMeth)$UINT, _CH_CMD$UINT, 2#0000000000000000$UINT, TO_UDINT(1383671281), "GetDiagConnect", 
//Clients:
(::SafetyBaseFSoE.ToBusInterface.pCh)$UINT, _CH_CLT_CMD$UINT, 2#0000000000000010$UINT, TO_UDINT(1682766337), "ToBusInterface", 
(::SafetyBaseFSoE.ServerUpdateTime.pCh)$UINT, _CH_CLT_DATA$UINT, 2#0000000000000000$UINT, TO_UDINT(247674937), "ServerUpdateTime", 
(::SafetyBaseFSoE.DeviceID.pCh)$UINT, _CH_CLT_DATA$UINT, 2#0000000000000010$UINT, TO_UDINT(4261957967), "DeviceID", 
(::SafetyBaseFSoE.FileSys.pCh)$UINT, _CH_CLT_OBJ$UINT, 2#0000000000000000$UINT, TO_UDINT(1422438244), "FileSys", TO_UDINT(545279513), "_FileSys", 1$UINT, 20$UINT, 
(::SafetyBaseFSoE.ToStdLib.pCh)$UINT, _CH_CLT_OBJ$UINT, 2#0000000000000000$UINT, TO_UDINT(2501598121), "ToStdLib", TO_UDINT(3689364044), "_StdLib", 2$UINT, 0$UINT, 
(::SafetyBaseFSoE.ToSigCLib.pCh)$UINT, _CH_CLT_OBJ$UINT, 2#0000000000000000$UINT, TO_UDINT(2700849093), "ToSigCLib", TO_UDINT(77440046), "SigCLib", 0$UINT, 42$UINT, 
END_FUNCTION


#define USER_CNT_SafetyBaseFSoE 71

TYPE
	_LSL_STD_VMETH	: STRUCT
			CmdTable	: CMDMETH;
			UserFcts	: ARRAY[0..USER_CNT_SafetyBaseFSoE] OF ^Void;
	END_STRUCT;
END_TYPE

FUNCTION SafetyBaseFSoE::@STD
	VAR_OUTPUT
		ret_code	: CONFSTATES;
	END_VAR
	VAR
		vmt	: _LSL_STD_VMETH;
	END_VAR

	//Command Methods
	InitCmdTable (nCmd := nSTDCMD + USER_CNT_SafetyBaseFSoE, pCmd := #vmt.CmdTable);
	vmt.CmdTable.NewInstr		:= #NewInst();
	vmt.CmdTable.Init		:= #Init();
	vmt.CmdTable.CyWork		:= #CyWork();
	vmt.CmdTable.RtWork		:= #RtWork();
#pragma warning (disable : 74)
	vmt.UserFcts[0]		:= #Background();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[1]		:= #CheckVariant();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[2]		:= #AddSDOToBuffer();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[3]		:= #WritePDO2Module();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[4]		:= #InitSafetyBase();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[5]		:= #InitBuffers();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[6]		:= #GetRoutingData();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[7]		:= #ReadSDOConfig();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[8]		:= #SDOReadCfgRequ();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[9]		:= #SDOStdRequ();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[10]		:= #AddDO();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[11]		:= #StartDO();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[12]		:= #StopDO();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[13]		:= #GetPointer2PDOBuffer();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[14]		:= #AddUnsafeRoutElement();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[15]		:= #AddFastUnsafeRoutElement();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[16]		:= #AddModuleToRoutingTabs();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[17]		:= #AddRoutingElement();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[18]		:= #GetRoutingPointerByNumber();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[19]		:= #UpdateSafetyNbrAtRoutingElement();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[20]		:= #ConfigSafetyInput();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[21]		:= #ConfigSafetyOutput();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[22]		:= #GetRoutingPointerByPath();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[23]		:= #AddPDOToInputBuffer();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[24]		:= #ConfigSafetyCPU();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[25]		:= #GetPDOInputBuffer();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[26]		:= #AddModuleToSafetyTask();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[27]		:= #GetModuleID();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[28]		:= #GetTaskPriority();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[29]		:= #InitSafeModule();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[30]		:= #GetSafetyNumber();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[31]		:= #UpDateNotStd();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[32]		:= #ReadModulePath();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[33]		:= #GetRoutingElementByThis();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[34]		:= #GetThisViaServerName();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[35]		:= #LogValue();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[36]		:= #LogError();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[37]		:= #InitPathInfo();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[38]		:= #ResetConfig();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[39]		:= #RemoveCPUFromTable();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[40]		:= #CheckUnsafeUpdateCycle();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[41]		:= #FetchDiagState();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[42]		:= #GetDiagState();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[43]		:= #FetchDiagInfo();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[44]		:= #GetDiagInfo();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[45]		:= #SaveLog();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[46]		:= #SaveLogActive();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[47]		:= #SDORequ();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[48]		:= #BlockedTransferLength();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[49]		:= #RecordFrame();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[50]		:= #AddFrame();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[51]		:= #ShowProjectRevision();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[52]		:= #CalcPDOLength();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[53]		:= #AddModuleToCfg();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[54]		:= #AddPdoToCfg();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[55]		:= #AddSlaveToCfg();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[56]		:= #FreeCfgLists();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[57]		:= #CreateAndSendCfg();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[58]		:= #IsOptimizedCPU();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[59]		:= #CheckConfigMemSize();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[60]		:= #ConnectEvent();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[61]		:= #DisconnectEvent();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[62]		:= #InterruptEvent();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[63]		:= #GetConfiguration();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[64]		:= #SetSafetyState();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[65]		:= #RefreshAsySrv();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[66]		:= #SetRetryCounter();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[67]		:= #GetDiagVisualOffsetErrorCode();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[68]		:= #SafeActModuleConfig();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[69]		:= #FetchChannelInfo();

#pragma warning (default : 74)
#pragma warning (disable : 74)
	vmt.UserFcts[70]		:= #QuitError();

#pragma warning (default : 74)
	ClassSvr.pMeth		:= StoreCmd (pCmd := #vmt.CmdTable, SHARED);

	IF ClassSvr.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;
	QuitComError.pMeth			:= StoreMethod( #M_RD_DIRECT(), #QuitComError::Write() );
	IF QuitComError.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;
	_memcpy((#vmt.CmdTable)$^USINT, GetDiagConnect.pMeth, SIZEOF(CMDMETH));
	vmt.CmdTable.nCmds		:= nSTDCMD;
	vmt.CmdTable.NewInstr		:= #GetDiagConnect::NewInst();
	GetDiagConnect.pMeth		:= StoreCmd (pCmd := #vmt.CmdTable, SHARED);

	IF GetDiagConnect.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;
	ret_code	:= SafetyBaseFSoE();

END_FUNCTION

#pragma usingLtd HwControl
#pragma usingLtd SafetyCDIAS_Base

//{{LSL_IMPLEMENTATION
VAR_EXTERNAL
  pAnalyzerThis   : ^DINT;
  pAnalyzerMethod : ^UDINT;
  pSafetyManagerThis   : pVirtualBase;
// b_SafetyOnLocalCDIAS = TRUE means there is a CDIAS safety module on local CDIAS/SDIAS Bus => using internal move command is not possible
  b_SafetyOnLocalCDIAS : BOOL;
  ModuleSyncNo : DINT; // Number of modules which need to be synchron before everyone sends the set bus ready signal
END_VAR

FUNCTION VIRTUAL SafetyBaseFSoE::AddSDOToBuffer
  VAR_INPUT
    p_us_message 	: ^USINT;
    us_length 	: USINT;
  END_VAR
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR

  // if the message is ok and there is space left in the buffer => save it
  if ( p_us_message ) & (us_length <= SBF_SDO_MAX_LENGTH) & (ui_cnt_SDOInputBuffer < 1000) then
  
    if ( p_SDOInputBuffer = NIL ) then
    
      sd_retval := -2;
      return;
      
    end_if;

    // if the allocated memory is full, we allocate some more
    if ui_cnt_SDOInputBuffer = InputBufferAllocatedNo then
    
      //reallocate list
      InputBufferAllocatedNo += SBF_SDO_BUFFER_ALLOC_BLOCK;
      p_SDOInputBuffer$^void := ToStdLib.Realloc(mptr := p_SDOInputBuffer$^void, newsize := InputBufferAllocatedNo * SBF_SDO_MAX_LENGTH);
      
      if ( p_SDOInputBuffer = NIL ) then
        SetSafetyState(NewState:=_MemAllocFailed);
        sd_retval := -3;
        return;
      end_if;

    end_if;

    //copy SDO into new list element
    ToStdLib.MemCpy( dest    := ( p_SDOInputBuffer + ( ui_cnt_SDOInputBuffer * SBF_SDO_MAX_LENGTH))$^void
                    , source  := p_us_message
                    , size    := us_length
                    );

    ui_cnt_SDOInputBuffer += 1;
    
    sd_retval := 0;
  else
  
    sd_retval := -1;
    
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::InitSafetyBase
  VAR_OUTPUT
    retcode 	: BOOL;
  END_VAR
  VAR
    tempCmd : CmdStruct;
    _result : results;
    dataADDCallback : t_BusIFAddCallback;
  END_VAR

  retcode := FALSE;

#ifdef DO_DEBUG
  PDOWrite.TimeDiffMin := 16#FFFFFFFF;
  PDORead.TimeDiffMin  := 16#FFFFFFFF;

  SDOWrite.TimeDiffMin := 16#FFFFFFFF;
  SDORead.TimeDiffMin  := 16#FFFFFFFF;

  TDOWrite.TimeDiffMin := 16#FFFFFFFF;
  TDORead.TimeDiffMin  := 16#FFFFFFFF;
#endif

  // read the client values
  ServerUpdateTime := ServerUpdateTime.Read();
  DeviceID := DeviceID.Read();

  // login as operator
  dataADDCallback.pthis := this$pVirtualBase;

  tempCmd.uiCmd     := _BUS_IF_ADD_CALLBACK;
  tempCmd.aPara[0]  := (#dataADDCallback)$DINT;      
  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
  if _result.aData[0]$t_BusIFRetvalErrorcode <> _BUS_IF_RETVAL_OK then
    LogError("@01C2 (SafetyBaseFSoE::InitSafetyBase) Failed to install callback at bus interface.");
  end_if;

  tempCmd.uiCmd     := _BUS_IF_GET_HW_PATH;
  tempCmd.aPara[0]  := this$DINT;      
  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
  if _result.aData[0]$t_BusIFRetvalErrorcode <> _BUS_IF_RETVAL_OK then
    LogError("@01C3 (SafetyBaseFSoE::InitSafetyBase) Failed to get hardware path.");
  else
    p_ObjectPath := _result.aData[4]$^USINT;
  end_if;  

  // check if we're connected via VARAN
  tempCmd.uiCmd     := _BUS_IF_IS_VARAN_AVAILABLE;
  
  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
  if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
    b_VaranAvailable := _result.aData[4]$BOOL;
  else
    LogError("@01C4 (SafetyBaseFSoE::InitSafetyBase) Failed to get connection state via BusInterface.");
  end_if;
  
  // if this module is not connected via VARAN the MovCommand of VARAN can't be used
  if b_VaranAvailable = FALSE then
    b_SafetyOnLocalCDIAS := TRUE;
  end_if;

  // first thing to do in cyclic is to set the bus ready
  LastRequest := _NoOpenRequest;
  
  // initialize pointers with NIL (they get valid pointers in a later init-run if an object of the analyzer or safety manager class is available)
  pAnalyzerThis   := NIL;
  pAnalyzerMethod := NIL;

  pSafetyManagerThis   := NIL;

  // use static memory for routing table
  p_RoutTable := #a_RoutTable[0];

  //initialize buffers
  if ( InitBuffers() ) then
    return;
  end_if;

  //no safety interface -> no work
  if ( pISafety = NIL ) then
    return;
  end_if;
  
  retcode := TRUE;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddDO
  VAR_OUTPUT
    ret_code 	: iprStates;
  END_VAR
  VAR
    tempCmd         : CmdStruct;
    _result         : results;
    dataADDDO       : t_AddDOCfg;
    dataADDTASK     : t_TaskCfg;
    Offset          : HINT;
    Length          : USINT;
    i               : USINT;
    tmpLength       : UINT;
  END_VAR

  ret_code := READY;

  // this stays the same for every DO
  tempCmd.uiCmd     := _BUS_IF_ADD_DO;
  tempCmd.aPara[0]  := (#dataADDDO)$DINT;
  
  // first we add all read accesses (this part also stays the same)
  dataADDDO.Priority                := _BUS_IF_ISO;
  dataADDDO.RdWr                    := _BUS_IF_READ;
  dataADDDO.ud_WaitForAccessHandle  := 0;
  
  if uiPDOReadSizeFastUnsafeVar then
  
    tmpLength := uiPDOReadSizeFastUnsafeVar;
    // calculate how many DOs are necessary    
    ReadFastUnsafeDOsUsed := ((uiPDOReadSizeFastUnsafeVar - 1) / SBF_MAX_BUS_USAGE) +1;

    for i := 0 to ReadFastUnsafeDOsUsed -1 do
      // read PDO
      if tmpLength > SBF_MAX_BUS_USAGE then
        aFastUnsafeReadDOs[i].Length  := SBF_MAX_BUS_USAGE;      
        tmpLength                     -= SBF_MAX_BUS_USAGE;           //get length for second DO
      else 
        aFastUnsafeReadDOs[i].Length  := tmpLength$USINT; //rest length
      end_if;
      
      //get read offset
      dataADDDO.ud_addressoff_read  := SBF_OFFSET_FAST_UNSAFE_VAR + SBF_MAX_BUS_USAGE * i;
  
      dataADDDO.ui_length_read  := aFastUnsafeReadDOs[i].Length;
      dataADDDO.ppDataRead      := #aFastUnsafeReadDOs[i].pData;
      
      ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
      if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
        aFastUnsafeReadDOs[i].Handle := _result.aData$t_AddDORet.Handle;
      else
        LogValue("@0786 (SafetyBaseFSoE::AddDO) Failed to add a DO for PDO read data in triple buffer. Retcode: 0x{0}", Value:= _result.aData[0]$UDINT);
        ret_code := ERROR;
      end_if;    
    
    end_for;
  end_if;


  if MyPDOReadSize then

    // calculate how many DOs are necessary
    ReadDOsUsed := ((MyPDOReadSize-1) / SBF_BUS_USAGE_PER_PDO_DO) + 1;

    for i := 0 to ReadDOsUsed-1 do
      // read PDO
      
      // the length of the last is the rest
      if i = ReadDOsUsed-1 then
        aReadPDOs[i].Length         := to_usint(MyPDOReadSize - SBF_BUS_USAGE_PER_PDO_DO*(ReadDOsUsed-1));
      else
      // all others are the max length
        aReadPDOs[i].Length         := SBF_BUS_USAGE_PER_PDO_DO;
      end_if;
      
      // the first access is on the main offset, the others start behind so the FPGA can detect the begin of new data
      if i = 0 then
        dataADDDO.ud_addressoff_read  := SBF_OFFSET_SPDO_DATA_RD;
      else
        dataADDDO.ud_addressoff_read  := SBF_OFFSET_SPDO_DATA_RD + (SBF_MAX_BUS_USAGE - SBF_BUS_USAGE_PER_PDO_DO);
      end_if;

      dataADDDO.ui_length_read      := aReadPDOs[i].Length;   
      dataADDDO.ppDataRead          := #aReadPDOs[i].pData;
      
      ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
      if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
        aReadPDOs[i].Handle := _result.aData$t_AddDORet.Handle;
      else
        LogValue("@01C5 (SafetyBaseFSoE::AddDO) Failed to add a DO for PDO read data. Retcode: 0x{0}", Value:= _result.aData[0]$UDINT);
        ret_code := ERROR;
      end_if;
    end_for;
  end_if;


  // read fast unsafe IOs, PDO Length info, GPDR1 and io states
  
  // calculate length and offset of the access
  
  // we have always 4 byte for the PDO length info, 2 Byte for the GPDR1 and 5 bytes LED Status
  Length := 4 + 2 + 5;
  
  if ActivateFastUnsafeIOs then
    Offset := SBF_OFFSET_GPDR2_RD;
    Length += (SBF_OFFSET_SPDO_LENGTH - SBF_OFFSET_GPDR2_RD);
  else
    Offset := SBF_OFFSET_SPDO_LENGTH;
  end_if;
    
  // now add some bytes for the input and output states (2 bit per IO) + 1 to round up + 4 for "header" info (LED, state bits and hbg state info)
  Length += (((InputsCount + OutputsCount) * 2 - 1) / 8) + 1 + 4;
  
  dataADDDO.ud_addressoff_read  := Offset;
  dataADDDO.ui_length_read      := Length;
  dataADDDO.ppDataRead          := #TempReadDO.pData;
  
  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
  if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
    TempReadDO.Handle  := _result.aData$t_AddDORet.Handle;
    
    // select this DO for periphery reset detection   
    tempCmd.uiCmd     := _BUS_IF_ADD_RESET_DETECTION_4_DO;
    tempCmd.aPara[0]  := TempReadDO.Handle$DINT;
    
    // don't check return code, this may not be supported by all bus interfaces
    ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
    
    
    // set the add DO command for the next DOs
    tempCmd.uiCmd     := _BUS_IF_ADD_DO;
    tempCmd.aPara[0]  := (#dataADDDO)$DINT;
  else
    LogError("@01C8 (SafetyBaseFSoE::AddDO) Failed to add a DO for time sync handshake read.");
    ret_code := ERROR;
  end_if;
  
  //Add data object/s for Analog Input data (only if analog input data is available)
  if usAnalogInputByteSize then

    // calculate how many DOs are necessary
    usAnalogInputReadDOsUsed := ((usAnalogInputByteSize-1) / SBF_BUS_USAGE_PER_PDO_DO) + 1;

    //Go through all PDOs
    for i := 0 to usAnalogInputReadDOsUsed-1 do
      
      // the length of the last is the rest
      if i = usAnalogInputReadDOsUsed-1 then
        aAnalogInputReadDOs[i].Length         := to_usint(usAnalogInputByteSize - SBF_MAX_BUS_USAGE*(usAnalogInputReadDOsUsed-1));
      else
      // all others are the max length
        aAnalogInputReadDOs[i].Length         := SBF_MAX_BUS_USAGE;
      end_if;
      
      // the first access is on the main offset, the others start behind so the FPGA can detect the begin of new data
      if i = 0 then
        dataADDDO.ud_addressoff_read  := SBF_OFFSET_ANALOG_VALUE_TRIPLE_BUFFER_RD;
      else
        dataADDDO.ud_addressoff_read  := SBF_OFFSET_ANALOG_VALUE_TRIPLE_BUFFER_RD + SBF_MAX_BUS_USAGE;
      end_if;

      dataADDDO.ui_length_read      := aAnalogInputReadDOs[i].Length;   
      dataADDDO.ppDataRead          := #aAnalogInputReadDOs[i].pData;
      
      ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
      if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
        aAnalogInputReadDOs[i].Handle := _result.aData$t_AddDORet.Handle;
      else
        LogValue("@01C5 (SafetyBaseFSoE::AddDO) Failed to add a DO for PDO read data. Retcode: 0x{0}", Value:= _result.aData[0]$UDINT);
        ret_code := ERROR;
      end_if;
    end_for;
  end_if;
  

  // now we add all write accesses (this part stays the same)
  dataADDDO.Priority                := _BUS_IF_ISO;
  dataADDDO.RdWr                    := _BUS_IF_WRITE;
  dataADDDO.ud_WaitForAccessHandle  := 0;

  if uiPDOWriteSizeFastUnsafeVar then

    tmpLength := uiPDOWriteSizeFastUnsafeVar;    
    // calculate how many DOs are necessary
    WriteFastUnsafeDOsUsed := ((uiPDOWriteSizeFastUnsafeVar -1) / SBF_MAX_BUS_USAGE) + 1;
    
    for i := 0 to WriteFastUnsafeDOsUsed -1 do
    
      if tmpLength > SBF_MAX_BUS_USAGE then
        aFastUnsafeWriteDOs[i].Length   := SBF_MAX_BUS_USAGE;        
        tmpLength                       -= SBF_MAX_BUS_USAGE; //get length for second DO
      else        
        aFastUnsafeWriteDOs[i].Length  := tmpLength$USINT;
      end_if;
      
      //get write offset
      dataADDDO.ud_addressoff_write  := SBF_OFFSET_FAST_UNSAFE_VAR + SBF_MAX_BUS_USAGE * i;  
      
      dataADDDO.ui_length_write       := aFastUnsafeWriteDOs[i].Length;
      dataADDDO.ppDataWrite           := #aFastUnsafeWriteDOs[i].pData;
      
      ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
      if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
        aFastUnsafeWriteDOs[i].Handle := _result.aData$t_AddDORet.Handle;
      else
        LogValue("@0786 (SafetyBaseFSoE::AddDO) Failed to add a DO for PDO write data in triple buffer. Retcode: 0x{0}", Value:= _result.aData[0]$UDINT);
        ret_code := ERROR;
      end_if;
    end_for;   
  end_if;

  if MyPDOWriteSize then

    // calculate how many DOs are necessary
    WriteDOsUsed := ((MyPDOWriteSize-1) / SBF_BUS_USAGE_PER_PDO_DO) + 1;

    for i := 0 to WriteDOsUsed-1 do
      // read PDO
      
      // the length of the last is the rest
      if i = WriteDOsUsed-1 then
        aWritePDOs[i].Length          := to_usint(MyPDOWriteSize - SBF_BUS_USAGE_PER_PDO_DO*(WriteDOsUsed-1));
      else
      // all others are the max length
        aWritePDOs[i].Length          := SBF_BUS_USAGE_PER_PDO_DO;
      end_if;
      
      // the first access is on the main offset, the others start behind so the FPGA can detect the begin of new data
      if i = 0 then
        dataADDDO.ud_addressoff_write   := SBF_OFFSET_SPDO_DATA_WR;
      else
        dataADDDO.ud_addressoff_write   := SBF_OFFSET_SPDO_DATA_WR + (SBF_MAX_BUS_USAGE - SBF_BUS_USAGE_PER_PDO_DO);
      end_if;
      
      dataADDDO.ui_length_write       := aWritePDOs[i].Length;
      dataADDDO.ppDataWrite           := #aWritePDOs[i].pData;
      
      ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
      if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
        aWritePDOs[i].Handle := _result.aData$t_AddDORet.Handle;
      else
        LogError("@01CA (SafetyBaseFSoE::AddDO) Failed to add a DO for PDO write data.");
        ret_code := ERROR;
      end_if;
    end_for;
  end_if;


  // write fast unsafe variables (if activated) and unsafe outputs (enable signals for safe outputs)

  // calculate length and offset of the access
  
  // we always have the GPDR1 register
  Length := 2;
  
  if ActivateFastUnsafeIOs then
    Offset := SBF_OFFSET_GPDR2_WR;
    Length += (SBF_OFFSET_GPDR1_WR - SBF_OFFSET_GPDR2_WR);
  else
    Offset := SBF_OFFSET_GPDR1_WR;
  end_if;

  // 1 bit per output
  if OutputsCount then
    Length += ((OutputsCount - 1) / 8) + 1;
  end_if;
  
  dataADDDO.ud_addressoff_write  := Offset;
  dataADDDO.ui_length_write      := Length;
  dataADDDO.ppDataWrite          := #TempWriteDO.pData;
  
  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
  if _result.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
    TempWriteDO.Handle  := _result.aData$t_AddDORet.Handle;
  else
    LogError("@01C8 (SafetyBaseFSoE::AddDO) Failed to add a DO for time sync handshake write data.");
    ret_code := ERROR;
  end_if;


   // add realtime task
  dataADDTASK.Priority    := _BUS_IF_ISO;
  dataADDTASK.callOptions := (CALL_OPTION_RT_PRESCAN or CALL_OPTION_RT_POSTSCAN);
  dataADDTASK.pthis       := this$UDINT;
   
  tempCmd.uiCmd     := _BUS_IF_ADD_TASK;
  tempCmd.aPara[0]  := (#dataADDTASK)$DINT;
  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
  
  if _result.aData[0]$t_BusIFRetvalErrorcode <> _BUS_IF_RETVAL_OK then
    LogError("@01CD (SafetyBaseFSoE::AddDO) Failed to install realtime tasks.");
    ret_code := ERROR;
  end_if;


   // add cyclic task
  dataADDTASK.Priority    := _BUS_IF_ASY;
  dataADDTASK.callOptions := 0;
  dataADDTASK.pthis       := this$UDINT;
   
  tempCmd.uiCmd     := _BUS_IF_ADD_TASK;
  tempCmd.aPara[0]  := (#dataADDTASK)$DINT;
  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result);
  
  if _result.aData[0]$t_BusIFRetvalErrorcode <> _BUS_IF_RETVAL_OK then
    LogError("@01CE (SafetyBaseFSoE::AddDO) Failed to install a cyclic task.");
    ret_code := ERROR;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetPointer2PDOBuffer
  VAR_OUTPUT
    ud_PDOBuffer 	: UDINT;
  END_VAR

  PDOOutputIsUsed := TRUE;

  //set pointer to address of buffer pointer
  ud_PDOBuffer := ( #PDOOutputBuffer )$UDINT;
   
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddRoutingElement
	VAR_INPUT
		ud_SafeNumber 	: HDINT;
		ud_thisp 	: UDINT;
		pPathInfo 	: ^SafetyBaseFSoE::PathInfoType;
	END_VAR
	VAR_OUTPUT
		sd_retval 	: DINT;
	END_VAR
  VAR
    p_RoutElement   : ^RoutElement;
    ui_PathLength   : UINT;
    NewElement      : BOOL;
  END_VAR

  if ( ud_thisp <> NIL ) then
    if ui_CntRoutElements < SBF_MAX_NUMBER_ROUT_ELEMENTS then

      NewElement := TRUE;
    
      if ui_CntRoutElements then
        //check if object this-pointer already exists in routing table
        p_RoutElement := GetRoutingElementByThis( ud_thisp := ud_thisp );
      else
        p_RoutElement := NIL;
      end_if;

      if p_RoutElement = NIL then
        //add new element
      
        //set pointer to empty element on last position
        p_RoutElement := p_RoutTable + ( ui_CntRoutElements ) * sizeof(RoutElement);
      else
        // we don't have a new element, we just update an existing
        NewElement := FALSE;
      end_if;
    
      //add path

      // path isn't stored if it's the routing table of safe input module, because
      // it doesn't need the information
      if pPathInfo & pPathInfo^.p_us_Path & pPathInfo^.p_us_PathComp 
         & ((NewElement = TRUE) | ((NewElement = FALSE) & (p_RoutElement^.p_CompressedPath = NIL) & (p_RoutElement^.ui_ComprPathLen = 0))) // only if it isn't already set
         then
          
        // the whole object path isn't stored, because the path can't be used for routing of SDOs;
        // The length of the path varies and it's possible, that the path is too long
        // to transfer it in SDO header. Instead of it the compressed path is used for addressing.
        
        ui_PathLength := pPathInfo^.ui_PathCompLen;
        p_RoutElement^.p_CompressedPath$pVoid := ToStdLib.Malloc(size := ui_PathLength);

        if ( p_RoutElement^.p_CompressedPath = NIL ) then
        
          sd_retval                         := -1;
          SetSafetyState(NewState:=_MemAllocFailed);
          return;
          
        end_if;

        // store compressed path into actual routing element
        ToStdLib.MemCpy(dest := p_RoutElement^.p_CompressedPath, source := pPathInfo^.p_us_PathComp, size := ui_PathLength);      
        p_RoutElement^.ui_ComprPathLen := ui_PathLength;
      elsif NewElement then
          
        // not routing table of safety CPU of safety output
        p_RoutElement^.p_CompressedPath := NIL;
        p_RoutElement^.ui_ComprPathLen := 0;
      end_if;
        
      //insert data
      p_RoutElement^.ud_SafeNumber := ud_SafeNumber;
      p_RoutElement^.ud_thisp      := ud_thisp;
      
      if NewElement then
        //increase counter
        ui_CntRoutElements += 1;
      end_if;
        
      sd_retval := 0;
    else
    
      sd_retval := -1;
      LogError("@01CF (SafetyBaseFSoE::AddRoutingElement) Maximum number of possible routing elements reached.");
    end_if;
  else
  
    sd_retval := -1;
  
    LogError("@01D1 (SafetyBaseFSoE::AddRoutingElement) NIL pointer given as this pointer.");
    
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetRoutingPointerByNumber
  VAR_INPUT
    ud_SafeNumber 	: HDINT;
  END_VAR
  VAR_OUTPUT
    ud_thisp 	: UDINT;
  END_VAR
  VAR
    i               : UINT;
    p_RoutElement   : ^RoutElement;
  END_VAR

  ud_thisp := 0;
  
  //no valid safety number
  if ( ud_SafeNumber = 0 ) then
    return;
  end_if;
  
  //set pointer on first routing element
  p_RoutElement := p_RoutTable;
  
  //check each element
  if ( ui_CntRoutElements > 0 ) then
    for i := 0 to ui_CntRoutElements - 1 by 1 do 
    
      //if searched safety number is found, this-pointer is returned
      if ( p_RoutElement^.ud_SafeNumber = ud_SafeNumber ) then
      
        ud_thisp := p_RoutElement^.ud_thisp;
        return;
        
      end_if;
      
      //set pointer to next element
      p_RoutElement += sizeof ( RoutElement );
    
    end_for;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetRoutingPointerByPath
  VAR_INPUT
    p_ActObjectPath 	: ^USINT;
    ActPathLen 	: UINT;
  END_VAR
  VAR_OUTPUT
    ud_thisp 	: UDINT;
  END_VAR
  VAR
    i                 : UINT;
    p_RoutElement     : ^RoutElement;
    retcode : UINT;
  END_VAR

  ud_thisp := 0;
  
  //set pointer on first routing element
  p_RoutElement := p_RoutTable;
  
  //check each element
  if ( ui_CntRoutElements > 0 ) then
    for i := 0 to ui_CntRoutElements - 1 by 1 do 
    
      if p_RoutElement^.p_CompressedPath & (p_RoutElement^.ui_ComprPathLen = ActPathLen) then
      
        //if searched path is found, this-pointer is returned
        retcode := ToStdLib.MemCmp(dest:= p_ActObjectPath, source:= p_RoutElement^.p_CompressedPath , size:= ActPathLen);
        
        if retcode = 0 then
        
          ud_thisp := p_RoutElement^.ud_thisp;
          return;
          
        end_if;
      end_if;
      
      //set pointer to next element
      p_RoutElement += sizeof ( RoutElement );
    
    end_for;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::ConfigSafetyInput
  VAR_INPUT
    ud_SafeNrCPU 	: HDINT;
    ud_thisp 	: UDINT;
    p_PDOBuffer 	: ^UDINT;
  END_VAR
  VAR_OUTPUT
    ud_SafeNrModul 	: HDINT;
  END_VAR

  ud_SafeNrModul := 0;

  if ( ud_SafeNrCPU ) then
  
    //add CPU to routing table for SDO
    if ( AddRoutingElement( ud_SafeNumber   := ud_SafeNrCPU
                          , ud_thisp        := ud_thisp
                          , pPathInfo       := NIL
                          ) = 0 ) then
    
      //return own safety number
      ud_SafeNrModul := SafetyNumber;
      
    end_if;
    
    //set pointer to address of buffer pointer
    p_PDOBuffer^ := GetPointer2PDOBuffer();
  else
    b_ReInitMarker := TRUE;
    LogError("@01D2 (SafetyBaseFSoE::ConfigSafetyInput) 0 is an invalid safety number for the safety CPU.");
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::ConfigSafetyOutput
  VAR_INPUT
    ud_SafeNrCPU 	: HDINT;
    ud_thisp 	: UDINT;
    ud_p_PDOBuffer 	: UDINT;
    pObject 	: ^PathInfoType;
    b_IsOptional 	: BOOL;
  END_VAR
  VAR_OUTPUT
    ud_SafeNrModul 	: HDINT;
  END_VAR
  VAR
  	sd_retval : DINT;
  END_VAR

  ud_SafeNrModul := 0;
  
  if ( ud_SafeNrCPU ) then
  
    //add CPU to routing table for SDO
    if ( AddRoutingElement( ud_SafeNumber   := ud_SafeNrCPU
                          , ud_thisp        := ud_thisp
                          , pPathInfo       := pObject
                          ) = 0 ) then
    
      //return own safety number and object path
      ud_SafeNrModul := SafetyNumber;
      
    end_if;

    //add PDO of CPU to list of input elements
    sd_retval := AddPDOToInputBuffer(ud_p2Buffer         := ud_p_PDOBuffer
                                   , ud_SafetyNr         := ud_SafeNrCPU
                                   , b_IsOptional        := b_IsOptional
                                    );
    
    if sd_retval <> 0 then
      LogValue("@0777 (SafetyBaseFSoE::ConfigSafetyOutput) AddPDOToInputBuffer failed with retval 0x{0}.",sd_retval$UDINT);
    end_if;
                      
  else
    b_ReInitMarker := TRUE;  
    LogError("@01D2 (SafetyBaseFSoE::ConfigSafetyOutput) 0 is an invalid safety number for the safety CPU.");
  end_if;  
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::ConfigSafetyCPU
  VAR_OUTPUT
    ud_SafeNrModul 	: HDINT;
  END_VAR
  VAR
    sd_retval : DINT;  	
  END_VAR

  // if there is no safetynumber available yet, try to get it
  if SafetyNumber = 0 then
    if Online then
      sd_retval := SDOStdRequ ( us_Command      := SBF_SDO_COMMAND_GET_SAFENBR
                              , p_us_RespData   := ( #SafetyNumber )$^USINT
                              , us_RespLength   := sizeof(SafetyNumber)
                              );
      
      if ( sd_retval <> 0 ) then
        if b_NoSafetyLogged = FALSE then
          b_NoSafetyLogged := TRUE;
          LogError("@01D8 (SafetyBaseFSoE::ConfigSafetyCPU) [SL] No Safety Number available!");
        end_if;
      end_if;
    else
      if b_NoSafetyLogged = FALSE then
        b_NoSafetyLogged := TRUE;
        LogError("@01D8 (SafetyBaseFSoE::ConfigSafetyCPU) [SL] No Safety Number available!");
      end_if;
    end_if;
  end_if;

  //return own safety number
  ud_SafeNrModul := SafetyNumber;      
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddPDOToInputBuffer
  VAR_INPUT
    ud_p2Buffer 	        : UDINT;
    ud_SafetyNr 	        : HDINT;
    b_IsOptional 	        : BOOL;
    ui_PdoLen 	          : UINT;(* := 0 *)
    ui_TargetOffset 	    : UINT;(* := 0 *)
    b_IsInterfaceFrame 	  : BOOL;(* := FALSE *)
  END_VAR
  VAR_OUTPUT
    sd_retval 	          : DINT;
  END_VAR
  VAR
    p_BufferElement       : ^PDOInputBuffer;
    ui_LenAct             : UINT;
    b_FoundOnlyBySafetyNr : BOOL;
  END_VAR
  
  sd_retval := 0;
  
  //check if input buffer is already in list
  p_BufferElement := GetPDOInputBuffer(ud_p2Buffer := ud_p2Buffer, ud_SafetyNr, #b_FoundOnlyBySafetyNr, b_IsInterfaceFrame, ui_TargetOffset);
    
  //buffer already in list and it's no interface frame
  if ( p_BufferElement ) & (b_IsInterfaceFrame = FALSE) then
    // if it has been found by number and the length of the PDO matches => only change buffer and target offset (dynamic reassignment e.g. used for automated guided vehicles)
    if b_FoundOnlyBySafetyNr & (ui_PdoLen = p_BufferElement^.ui_PDOLen) then
      p_BufferElement^.ud_p2Buffer        := ud_p2Buffer;
      p_BufferElement^.ui_TargetOffset    := ui_TargetOffset;
      p_BufferElement^.p_TargetMemory     := aWritePDOs[ui_TargetOffset / SBF_BUS_USAGE_PER_PDO_DO].pData + (ui_TargetOffset MOD SBF_BUS_USAGE_PER_PDO_DO);
    end_if;

    return;
  elsif ui_cnt_PDOInputBuffer < SBF_PDO_MAX_NUMBER then
    // check if it's on the border
    if ((ui_TargetOffset MOD SBF_BUS_USAGE_PER_PDO_DO) + ui_PdoLen) > SBF_BUS_USAGE_PER_PDO_DO then
    
      // calculate how many bytes we can store in the actual DO
      ui_LenAct := SBF_BUS_USAGE_PER_PDO_DO - (ui_TargetOffset MOD SBF_BUS_USAGE_PER_PDO_DO);
    
      // we need another virtual PDO for the rest (split up the copy commands in real time) => recursion!
      sd_retval := AddPDOToInputBuffer(ud_p2Buffer + ui_LenAct, ud_SafetyNr, b_IsOptional, ui_PdoLen - ui_LenAct, ui_TargetOffset + ui_LenAct, b_IsInterfaceFrame);
//#ifdef HWK_SHOW_PRAGMA_MESSAGES      
//#pragma message("RamAnd 21.10.2014 11:18 \ testweise Rekursion loggen")
//LogValue("@ZZZZ (SafetyBaseFSoE::AddPDOToInputBuffer) Used recursion because of offset 0x{0} and length 0x{1}", Value:=ui_TargetOffset, Value2:=ui_LenAct);
//#endif
      ui_PdoLen := ui_LenAct;
    end_if;

#ifdef SAFETY_DEBUG_LOGGING_ACTIVATED      
    LogValue(pString:="Adding new buffer (InputPDO). IsInterfaceFrame 0x{0}; New TargetOffset 0x{1}, SafetyNbr: 0x{2}", Value:=b_IsInterfaceFrame, Value2:=ui_TargetOffset, Value3:=ud_SafetyNr);
#endif

    // only possible before the setbusready or after but not during
    if (us_SetBusReadyState <> WaitPDOsSyncron) & (us_SetBusReadyState <> SetBusReadyFinished) then
      sd_retval := -2;
      return;
    end_if;


    aPDOInputBuffer[ui_cnt_PDOInputBuffer].ud_p2Buffer        := ud_p2Buffer;
    //*****************************************************************************
    
    aPDOInputBuffer[ui_cnt_PDOInputBuffer].b_TimeSynchron     := TRUE; // at FSoE we do not have to wait for time synchronisation
    aPDOInputBuffer[ui_cnt_PDOInputBuffer].ud_SafetyNr        := ud_SafetyNr;
    aPDOInputBuffer[ui_cnt_PDOInputBuffer].ui_PDOLen          := ui_PDOLen;
    aPDOInputBuffer[ui_cnt_PDOInputBuffer].b_IsOptional       := b_IsOptional;
    aPDOInputBuffer[ui_cnt_PDOInputBuffer].b_IsInterfaceFrame := b_IsInterfaceFrame;
    aPDOInputBuffer[ui_cnt_PDOInputBuffer].ui_TargetOffset    := ui_TargetOffset;
    aPDOInputBuffer[ui_cnt_PDOInputBuffer].p_TargetMemory     := aWritePDOs[ui_TargetOffset / SBF_BUS_USAGE_PER_PDO_DO].pData + (ui_TargetOffset MOD SBF_BUS_USAGE_PER_PDO_DO);

    if ui_cnt_PDOInputBuffer = 0 then
      // only when adding the first input PDO we increment our module counter
      ModuleSyncNo += 1;
    end_if;

    ui_cnt_PDOInputBuffer += 1;    
  else
    // no more space available
    sd_retval := -1;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::WritePDO2Module
  VAR
    p_PDOInBufferAct  : ^PDOInputBuffer;
    i                 : UINT;
//    TargetDOIndex     : UINT;
//    j                 : UINT;
  END_VAR

  p_PDOInBufferAct := #aPDOInputBuffer[0];
  for i := 1 to ui_cnt_PDOInputBuffer do
//#ifdef HWK_SHOW_PRAGMA_MESSAGES      
//    if b_LogFirstDone = 0 then
//      // search for the DO used here (if there is more than 1)
//      if WriteDOsUsed > 1 then
//        // initialize with invalid
//        TargetDOIndex := 16#FF;
//        for j := 0 to (WriteDOsUsed-1) do
//          // check if it's in this DO
//          if (p_PDOInBufferAct^.p_TargetMemory >= aWritePDOs[j].pData) & (p_PDOInBufferAct^.p_TargetMemory < aWritePDOs[j].pData + aWritePDOs[j].Length) then
//            TargetDOIndex := j;
//            exit;
//          end_if;
//        end_for;
//      else
//        TargetDOIndex := 0;
//      end_if;
//
//      aLogValues[i-1].ByteLen := p_PDOInBufferAct^.ui_PDOLen;
//      aLogValues[i-1].Offset  := p_PDOInBufferAct^.ui_TargetOffset;
//      aLogValues[i-1].DOIndex := TargetDOIndex;
//    end_if;
//#endif

    // copy single FSoE PDO frames to target memory
    _memcpy(p_PDOInBufferAct^.p_TargetMemory, p_PDOInBufferAct^.ud_p2Buffer$pVoid, p_PDOInBufferAct^.ui_PDOLen);

    p_PDOInBufferAct += sizeof(PDOInputBuffer);
  end_for;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  if WriteDOsUsed then
    RecordFrame(pFrame:=aWritePDOs[0].pData$^DINT, FrameState:=1, IsFSoEPDO:=TRUE);
  end_if;
#endif

    
//#ifdef HWK_SHOW_PRAGMA_MESSAGES      
//#pragma message("RamAnd 21.10.2014 10:40 \ testweise beim ersten durchlauf die Zugriffe loggen")
//  if b_LogFirstDone = 0 then
//    // ready to log
//    b_LogFirstDone := 1;
//  end_if;
//#endif
//
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddModuleToSafetyTask
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR
  
  //register module as safety module in OS and receive pointers on SDO buffers
  sd_retval := SAFETY_iAddModuleToSafetyTask( uiMaster  := 0
                                            , pucNode   := p_ObjectPath
                                            , pucRdSDO  := ( #p_Handle_SDO_Read )$^UDINT
                                            , pucWrSDO  := ( #p_Handle_SDO_Write )$^UDINT
                                            );
  
  //look for an error
  if ( sd_retval ) then
    
    LogError("@01D5 (SafetyBaseFSoE::AddModuleToSafetyTask) Module not added to Safety Communication Task.");
    
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetModuleID
  VAR_OUTPUT
    ud_ModuleID 	: UDINT;
  END_VAR

  // no module
  ud_ModuleID := 16#ff;
  
END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::Init
  VAR
    MyPara 	  : CmdStruct;
    MyResult	: results;
    FeatureEnableRequest : t_FeatureEnable;
  END_VAR
//============================ Profiler start======================================================
#ifdef DM_Profiler 
  InitTimeMeasurement(usMode := DMTIME_RT);
  InitTimeMeasurement(usMode := DMTIME_CY);
  InitTimeMeasurement(usMode := DMTIME_RTPOSTSCAN);
#endif
//============================ Profiler end========================================================

  // don't initialize anything if the module is deactivated
  if b_Deactivated then
    return;
  end_if;

  case InitStep of
//**********************************************************************************************************************************************************
    0:
      // first check if the module may be deactivated
      MyPara.uiCmd     := _BUS_IF_GET_DEACTIVATED_STATE;
      
      ToBusInterface.NewInst(pPara:=#MyPara, pResult:=#MyResult);
      if MyResult.aData[0]$t_BusIFRetvalErrorcode = _BUS_IF_RETVAL_OK then
        b_Deactivated := MyResult.aData[4]$BOOL;
        if b_Deactivated then
          return;
        end_if;
      else
        LogError("@01D6 (SafetyBaseFSoE::Init) Failed to get deactivation state via BusInterface.");
      end_if;
    
      // initialize
      if InitSafetyBase() then
        InitStep += 1;
      end_if;
    
//**********************************************************************************************************************************************************
    1:      
      InitStep += 1;
      
//**********************************************************************************************************************************************************
   2:
      // initialize safety module specific data (cpu, input, output) in last init and send OK to CPU
      if us_FirstScan = 3 then
        if pSafetyManagerThis then
          MyPara.uiCmd := CMD_SM_FSOE_AVAILABLE;
          pSafetyManagerThis^.NewInst(#MyPara, #MyResult);
          
          // make compatibility settings
          MyPara.uiCmd := CMD_SM_CHECK_COMPATIBILITY;
          
          // request the features
          FeatureEnableRequest := 0;
          FeatureEnableRequest.TransmissionTimeInConfig := TRUE;
          FeatureEnableRequest.FSoEFlags_4ByteSize      := TRUE;
          
          MyPara.aPara[0]$t_FeatureEnable := FeatureEnableRequest;

          // reset returncode
          MyResult.uiLng := 0;
          MyResult.aData[0]$t_FeatureEnable := 0;
          
          pSafetyManagerThis^.NewInst(#MyPara, #MyResult);

          // take the return code as our common features
          if MyResult.uiLng = sizeof(t_FeatureEnable) then
            bdFeatureEnable := MyResult.aData[0]$t_FeatureEnable;
            
            // check if there are bits in the requests that are not in the enabled features
            if bdFeatureEnable <> FeatureEnableRequest then
              LogError("@01D3 (SafetyBaseFSoE::Init) At least one requested feature is not available at the SafetyManager class. Use latest HwControl version!");
            end_if;
          else
            // command in safety manager not available
            LogError("@01D3 (SafetyBaseFSoE::Init) At least one requested feature is not available at the SafetyManager class. Use latest HwControl version!");
          end_if;
        end_if;
      elsif (us_FirstScan = 10) then
        // log in to safety manager, if there is one
        if pSafetyManagerThis then
          MyPara.uiCmd := CMD_SM_ADD_MODULE;
          MyPara.aPara[0] := DeviceID$DINT;
          MyPara.aPara[1] := THIS$DINT;
          pSafetyManagerThis^.NewInst(#MyPara, #MyResult);
          
          // check if quit error is avaialble
          MyPara.uiCmd := CMD_SM_QUIT_ERROR_AVAILABLE;
          MyPara.aPara[0] := THIS$DINT;
          pSafetyManagerThis^.NewInst(#MyPara, #MyResult);

          QuitErrorAvailable := MyResult.aData[0]$DINT;
        else
          // if there is no manager, we don't have to wait in cywork until the initialisation is finished
          b_ManagerInitialisationFinished := TRUE;
          b_ManagerReadyForSDO := TRUE;
        end_if;

        InitSafeModule();
      end_if;
//**********************************************************************************************************************************************************
  end_case;

  us_FirstScan += 1;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetTaskPriority
  VAR_OUTPUT
    us_TaskPriority 	: USINT;
  END_VAR

  // highest = default-priority (no change in update order)
  us_TaskPriority := 0;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::InitSafeModule
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR

  sd_retval := 0;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::StartDO
  VAR
    i : USINT;
  END_VAR

  //Always calculate the pointer to the read and write data because the pointers 'TempReadDO.pData' and 'TempWriteDO.pData' can be changed at plug&play behind a VI02x if 
  //the amount of modules behind the VI02x changes

  // seperate the data pointers for fast unsafe inputs (if activated) and unsafe outputs (enable signals for safe outputs)
  // first check if this DO is available
  if TempWriteDO.pData then
    if ActivateFastUnsafeIOs then
      pFastUnsafeInputs$pVoid   := TempWriteDO.pData;
      pGPDR1Wr                  := pFastUnsafeInputs + (SBF_OFFSET_GPDR1_WR - SBF_OFFSET_GPDR2_WR);
    else
      pFastUnsafeInputs         := NIL;
      pGPDR1Wr$pVoid            := TempWriteDO.pData;
    end_if;
          
    if OutputsCount then
      pUnsafeOutWrData          := pGPDR1Wr + (SBF_OFFSET_UNSAFE_VAR_WR - SBF_OFFSET_GPDR1_WR);
    else
      pUnsafeOutWrData          := NIL;
    end_if;
  end_if;
  
  if TempReadDO.pData then
    if ActivateFastUnsafeIOs then
      pFastUnsafeOutputs$pVoid  := TempReadDO.pData;
      pPDOLengthInfoData        := pFastUnsafeOutputs + (SBF_OFFSET_SPDO_LENGTH - SBF_OFFSET_GPDR2_RD);
    else
      pFastUnsafeOutputs        := NIL;
      pPDOLengthInfoData$pVoid  := TempReadDO.pData;
    end_if;
    
    pGPDR1Rd                    := pPDOLengthInfoData + (SBF_OFFSET_GPDR1_RD - SBF_OFFSET_SPDO_LENGTH);
    pLEDandIOStates             := pGPDR1Rd + (SBF_OFFSET_IO_STATES - SBF_OFFSET_GPDR1_RD);
  end_if;

  // enable the DOs
  SetDORunState(Handle:=TempReadDO.Handle, NewState:=_BUS_IF_RUN_STATE_CONT_RUN);
  
  SetDORunState(Handle:=TempWriteDO.Handle, NewState:=_BUS_IF_RUN_STATE_CONT_RUN);

  if ReadDOsUsed then
    for i := 0 to ReadDOsUsed-1 do
      SetDORunState(Handle:=aReadPDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_CONT_RUN);
    end_for;
  end_if;

  if WriteDOsUsed then
    for i := 0 to WriteDOsUsed-1 do
      SetDORunState(Handle:=aWritePDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_CONT_RUN);
    end_for;
  end_if;
  
  if usAnalogInputReadDOsUsed then
    for i := 0 to usAnalogInputReadDOsUsed-1 do
      SetDORunState(Handle:=aAnalogInputReadDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_CONT_RUN);
    end_for;
  end_if;

  if ReadFastUnsafeDOsUsed then
    for i := 0 to ReadFastUnsafeDOsUsed-1 do
      SetDORunState(Handle:=aFastUnsafeReadDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_CONT_RUN);
    end_for;  
  end_if;
  
  if WriteFastUnsafeDOsUsed then
    for i := 0 to WriteFastUnsafeDOsUsed-1 do
      SetDORunState(Handle:=aFastUnsafeWriteDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_CONT_RUN);
    end_for;
  end_if;  

  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::StopDO
  VAR
    i : USINT;
  END_VAR

  // disable the DOs
  SetDORunState(Handle:=TempReadDO.Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);
  
  SetDORunState(Handle:=TempWriteDO.Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);

  if ReadDOsUsed then
    for i := 0 to ReadDOsUsed-1 do
      SetDORunState(Handle:=aReadPDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);
    end_for;
  end_if;

  if WriteDOsUsed then
    for i := 0 to WriteDOsUsed-1 do
      SetDORunState(Handle:=aWritePDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);
    end_for;
  end_if;
  
  if usAnalogInputReadDOsUsed then
    for i := 0 to usAnalogInputReadDOsUsed-1 do
      SetDORunState(Handle:=aAnalogInputReadDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);
    end_for;        
  end_if;

  if ReadFastUnsafeDOsUsed then
    for i := 0 to ReadFastUnsafeDOsUsed-1 do
      SetDORunState(Handle:=aFastUnsafeReadDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);
    end_for;
  end_if;  
 
  if WriteFastUnsafeDOsUsed then
    for i := 0 to WriteFastUnsafeDOsUsed-1 do
      SetDORunState(Handle:=aFastUnsafeWriteDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);
    end_for;
  end_if; 
 
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::InitBuffers
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR

  sd_retval := -1;
  
  //allocate memory for outgoing SDO (one SDO possible)
  p_SDOOutputBuffer$^void := ToStdLib.Malloc(size := SBF_SDO_MAX_LENGTH);
  
  if ( p_SDOOutputBuffer = NIL ) then
    SetSafetyState(NewState:=_MemAllocFailed);
    return;
  end_if;

  //allocate memory for incoming SDO list (number of SDO is dynamical) 
  //following elements will be added dynamical
  
  //allocate list
  InputBufferAllocatedNo := SBF_SDO_BUFFER_ALLOC_BLOCK;
  p_SDOInputBuffer$^void := ToStdLib.Malloc(size := InputBufferAllocatedNo * SBF_SDO_MAX_LENGTH);
  
  if ( p_SDOInputBuffer = NIL ) then
    SetSafetyState(NewState:=_MemAllocFailed);
    return;
  end_if;

  //all buffers initialized
  sd_retval := 0;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetSafetyNumber
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR
  VAR
    MyPara 	  : CmdStruct;
    MyResult	: results;
  END_VAR

  sd_retval := SDOStdRequ ( us_Command      := SBF_SDO_COMMAND_GET_SAFENBR
                          , p_us_RespData   := ( #SafetyNumber )$^USINT
                          , us_RespLength   := sizeof(SafetyNumber)
                          );
  
  if ( sd_retval <> 0 ) then
    if b_NoSafetyLogged = FALSE then
      b_NoSafetyLogged := TRUE;
      LogError("@01D8 (SafetyBaseFSoE::GetSafetyNumber) [SL] No Safety Number available!");
    end_if;
  elsif pConfigMem then
    // store safety number in config memory
    pConfigMem^$HDINT := SafetyNumber;    
  end_if;

  sd_retval := SDOStdRequ ( us_Command      := SBF_SDO_COMMAND_GET_FW_VERSION
                          , p_us_RespData   := ( #sFirmwareVersion )$^USINT
                          , us_RespLength   := sizeof(FirmwareVersionType)
                          );
  
  if ( sd_retval = 0 ) then
    FirmwareVersion := sFirmwareVersion.RevMinor OR (sFirmwareVersion.RevMajor SHL 16);

    // Minor Versions starting with 9xxx mark Bootloader versions
    if (sFirmwareVersion.RevMinor >= 9000) & (sFirmwareVersion.RevMinor < 10000) then
      LogError("@01D9 (SafetyBaseFSoE::GetSafetyNumber) Bootloader version of safety firmware detected! Download a firmware or contact Sigmatek Support!");
    end_if;
    
    //Minor Versions higher than 338 mark versions which support the readout of the safety retry counters.
    if (sFirmwareVersion.RevMinor >= SBF_RETRY_COUNTER_AVAILABLE_MINOR_REV) & (sFirmwareVersion.RevMajor = SBF_RETRY_COUNTER_AVAILABLE_MAJOR_REV) then
      b_RetryCounterAvailable := TRUE;
    else
      b_RetryCounterAvailable := FALSE;
      //Call Method to set retrycounter servers of safety modules to -1
      SetRetryCounter();
    end_if;
  else
    SetSafetyState(NewState:=_ReadFWVerFailed);
    LogError("@01DA (SafetyBaseFSoE::GetSafetyNumber) No Firmware Version available!");
  end_if;
  
  // add own safety number to routing table
  sd_retval := AddRoutingElement( ud_SafeNumber := SafetyNumber
                                , ud_thisp      := this$UDINT
                                , pPathInfo     := NIL
                                );

  // give the manager more details about the module
  if pSafetyManagerThis then
    MyPara.uiCmd := CMD_SM_ADD_MODULE_INFO;
    MyPara.aPara[0] := THIS$DINT;
    MyPara.aPara[1] := SafetyNumber$DINT;
    MyPara.aPara[2] := FirmwareVersion$DINT;
    MyPara.aPara[3] := 16#FF; // fake FPGA_Version (only needed for CDIAS CPUs)
    MyPara.aPara[4] := to_DINT(p_ObjectPath^$USINT) + 1; // size of path (length + 1 for the length information)
    MyPara.aPara[5] := p_ObjectPath$DINT;
    pSafetyManagerThis^.NewInst(#MyPara, #MyResult);
  else
    LogError("@01DB (SafetyBaseFSoE::GetSafetyNumber) Couldn't find object of HwControl class, which is needed for safety time synchronisation!");
  end_if;

END_FUNCTION


FUNCTION SafetyBaseFSoE::SafetyBaseFSoE
  VAR_OUTPUT
    ret_code 	: ConfStates;
  END_VAR
  
#ifdef HWC_LogInit
  HWC_LogInit();
#endif

  //get interface for safety functions
  if ( OS_CILGET(INTERFACE_SAFETY, #pISafety)) then
    pISafety := NIL;
    
    LogError("@01DC (SafetyBaseFSoE::SafetyBaseFSoE) No Safety Interface found!");
  end_if;

  // initialize it with FALSE, if there is anything on local CDIAS it will change to TRUE
  b_SafetyOnLocalCDIAS := FALSE;

  // initialize module count with 0
  ModuleSyncNo := 0;
    
  ret_code := C_OK;

END_FUNCTION


#pragma warning(disable:0073);
FUNCTION VIRTUAL SafetyBaseFSoE::UpDateNotStd
  VAR_INPUT
    calloption 	: UDINT;
  END_VAR

END_FUNCTION
#pragma warning(default:0073);


FUNCTION VIRTUAL SafetyBaseFSoE::ReadSDOConfig
	VAR_INPUT
		p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
		p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
	END_VAR
	VAR_OUTPUT
		sd_retval 	: DINT;
	END_VAR
  VAR
    ud_Address                    : UDINT;
    ud_ListLength_Address         : UDINT;
    ud_ListLength                 : UDINT;
    ud_TmpCnt                     : UDINT;
    i                             : UDINT;
    Module                        : PathInfoType;
    ModulePath                    : ARRAY [0..127] OF USINT;
    ModulePathComp                : ARRAY [0..127] OF USINT;
    ListHeader                    : PathInfoType;
    ListPath                      : ARRAY [0..127] OF USINT;
    ListPathComp                  : ARRAY [0..127] OF USINT;
    us_DataLength_Max             : USINT;
    ud_ListBaseAddress            : UDINT;
    ud_ListAddressMax             : UDINT;
    ud_CntUnsafeOutputs           : UDINT;
    ud_CntUnsafeInputs            : UDINT;
    ud_ActAddress                 : UDINT;  // actual offset in application memory (where to find the values in run mode)
    ud_UnsafeBDINT_Offset         : UDINT;  // actual offset in application memory (where to find the unsafe variables in run mode)
    ud_ActBlockType               : UDINT;
    ud_TmpAddress                 : UDINT;
    ud_TmpStrLen                  : UDINT;
    p_TmpBlockedData              : ^void;
    ud_ActStrLen                  : UDINT;
    us_BlockedTransferLength      : USINT;
    BoolOuts                      : UDINT;
    DintOuts                      : UDINT;    
    TempModuleConfig              : t_ModuleCfg;
    pChar                         : ^CHAR;
    TempPath                      : ARRAY [0..99] OF USINT;
    PathOfThisCPU                 : ARRAY [0..99] OF USINT;
    GotOwnPath                    : BOOL;
    TempPdoConfig                 : t_PdoCfg;
    pBitMasks                     : ^BDINT;
    ActBitMask                    : BDINT;
    ud_skipLength                 : UDINT; //++pr
    b_CfgTypeV2                   : BOOL; // FALSE means there is no support of new time synchronisation/new general purpose register
    TmpCRC                        : HDINT;
    ud_ContainerBlockType         : UDINT;
    b_CfgTypeV3                   : BOOL;
    ud_ConfigLength               : UDINT;
    hd_DynParaVersion             : UDINT;
    DynamicParameterHeader        : t_DynamicParameterHeader;
    b_IsFSoE                      : BOOL;
    DynHeadSize                   : UDINT;
    AddressServiceDataSize        : UDINT;
    b_IprCodeLengthAvailable      : BOOL;  //TRUE, SCP211 is used
    ud_CntFastUnsafeOutputs       : UDINT;
    ud_CntFastUnsafeInputs        : UDINT;
    ud_FastUnsafe_Offset_In       : UDINT; // actual offset in application memory (where to find the unsafe variables in run mode)
    ud_FastUnsafe_Offset_Out      : UDINT; // actual offset in application memory (where to find the unsafe variables in run mode)
    ud_TmpLength                  : UDINT;
  END_VAR
    
  // reset flags before checking the config
  NewConfigType := ConfigState_NotInitialized;
  b_IprCodeLengthAvailable := FALSE;
  sFlexCon.FoundNodeInConfig := FALSE;
  sFlexCon.FoundHostInConfig := FALSE;  

  // also reset the servers
  if sFlexCon.pCommand & sFlexCon.pStateMachine & sFlexCon.pHostSafetyNumber then
    sFlexCon.pCommand^          := 0;
    sFlexCon.pStateMachine^     := 0;
    sFlexCon.pHostSafetyNumber^ := 0;
  end_if;


  //initialize header-data for request
  
  //calculate length without crc
  p_SDORequestBuffer^.StdHeader.Len := sizeof(SDORequReadConfig) - sizeof(SDOHeader.StdHeader.CRC);
  
  //type
  p_SDORequestBuffer^.StdHeader.Typ := SDO_STD;
  
  //command for reading config data
  p_SDORequestBuffer^.StdHeader.Cmd := SBF_SDO_COMMAND_READ;
  
  
  //read crc of whole config data
  
  //send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_CRC_ALL
                              , us_Length           := 4
                              );

  //no response
  if ( sd_retval <> 0 ) then
    LogError("@01DD (SafetyBaseFSoE::ReadSDOConfig) Error reading CRC of config data.");
    return;
  end_if;
  
  // save crc and remember the time
  DesignerPrjConfigCRC := p_SDOResponseBuffer^.ud_Data;
  ud_LastCrcCheck := ops.tAbsolute;
  b_CheckCRC := TRUE;
  
  //read length of whole config data
  
  //send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_LEN_ALL
                              , us_Length           := 4
                              );

  //no response
  if ( sd_retval <> 0 ) then
    LogError("@01DE (SafetyBaseFSoE::ReadSDOConfig) Error reading length of config data.");
    return;
  end_if;
  
  //no config data available
  if ( p_SDOResponseBuffer^.ud_Data = 16#FFFF_FFFF ) then
    LogError("@01DF (SafetyBaseFSoE::ReadSDOConfig) No config data available..");
    b_ReadConfigFailed := TRUE;
    return;
  end_if;
  
  ud_ConfigLength := p_SDOResponseBuffer^.ud_Data;
  
  
  // get BlockType
  //send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_BLOCKTYPE
                              , us_Length           := 4
                              );
  //no response, error
  if ( sd_retval <> 0 ) then
    LogError("@01E0 (SafetyBaseFSoE::ReadSDOConfig) Error getting the block type of the header.");
    return;
  end_if;  

  ud_ContainerBlockType := p_SDOResponseBuffer^.ud_Data;

  // check if it's a new version of the configuration
  if (ud_ContainerBlockType = SBF_BLKTYPE_CFG_CONTAINER_V3)       |
     (ud_ContainerBlockType = SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE)     |
     (ud_ContainerBlockType = SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE_OPTIONAL) then

    //read dynamic header length (how many data are attached to the header)
    
    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_LEN_HEAD
                                , us_Length           := 4
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@01E1 (SafetyBaseFSoE::ReadSDOConfig) Error reading length of dynamic header data.");
      return;
    end_if;

    // plausibility check for data length
    if p_SDOResponseBuffer^.ud_Data > 1000 then
      sd_retval := -1;
      LogError("@01E2 (SafetyBaseFSoE::ReadSDOConfig) Invalid length of dynamic header data.");
      return;
    end_if;

    // the length info contains: size of length info + size of time info + size of additional data

    //start address of first list: address of dynamic length + length according to length info
    ud_ListBaseAddress := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_LEN_HEAD + p_SDOResponseBuffer^.ud_Data;
    
    DynHeadSize := p_SDOResponseBuffer^.ud_Data;
    
    // if we got at least 4 byte additional header data, it's the version of the dynamic parameters => read it
    if DynHeadSize >= 12 then      
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_LEN_HEAD + 8
                                  , us_Length           := 4
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@01E3 (SafetyBaseFSoE::ReadSDOConfig) Error reading version of dynamic header data.");
        return;
      end_if;
      
      hd_DynParaVersion := p_SDOResponseBuffer^.ud_Data;
    else
      LogError("@01E4 (SafetyBaseFSoE::ReadSDOConfig) Data structure error in config! No bytes for the dynamic parameter version (according to length info).");
      return;
    end_if;

    // if we got at least 8 byte additional header data, the next 4 bytes are the flags => read it
    if DynHeadSize >= 16 then
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_LEN_HEAD + 12
                                  , us_Length           := 4
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@023D (SafetyBaseFSoE::ReadSDOConfig) Error reading flags of dynamic header data.");
        return;
      end_if;
      
      b_IsFSoE := (p_SDOResponseBuffer^.ud_Data AND 1) <> 0;
      
      // if we got the FSoE bit set => read the FSoE config
      if b_IsFSoE then
        GetFSoEConnectionInfos(p_SDORequestBuffer   := p_SDORequestBuffer
                             , p_SDOResponseBuffer  := p_SDOResponseBuffer
                             );
      end_if;
      
      if DynHeadSize >= 20 then
        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_LEN_HEAD + 16
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@023D (SafetyBaseFSoE::ReadSDOConfig) Error reading flags of dynamic header data.");
          return;
        end_if;
     
        b_IprCodeLengthAvailable := (p_SDOResponseBuffer^.ud_Data AND 1) <> 0;
        
        // if there are fast unsafe variables to read
        if uiPDOReadSizeFastUnsafeVar then

          //malloc FastUnsafe Output Buffer
          pFastUnsafeOutBuffer := ToStdLib.Malloc(size:=uiPDOReadSizeFastUnsafeVar);
          
          if pFastUnsafeOutBuffer = NIL then
            LogError("@0784 (SafetyBaseFSoE::ReadSDOConfig) Failed to allocate memory for outputbuffer.");
            return;
          else
            // init buffer
            _memset(dest:=pFastUnsafeOutBuffer, usByte:=0x00, cntr:=uiPDOReadSizeFastUnsafeVar);
          end_if;
        end_if;

        // if there are fast unsafe variables to write
        if uiPDOWriteSizeFastUnsafeVar then
        
          //malloc FastUnsafe Input Buffer
          pFastUnsafeInBuffer := ToStdLib.Malloc(size:=uiPDOWriteSizeFastUnsafeVar);
          
          if pFastUnsafeInBuffer = NIL then
            LogError("@0785 (SafetyBaseFSoE::ReadSDOConfig) Failed to allocate memory for inputbuffer.");
            return;
          else
            // init buffer
            _memset(dest:=pFastUnsafeInBuffer, usByte:=0x00, cntr:=uiPDOWriteSizeFastUnsafeVar);
          end_if;       
        end_if;
      end_if;

 
    else
      LogError("@01E4 (SafetyBaseFSoE::ReadSDOConfig) Data structure error in config! No bytes for the dynamic parameter version (according to length info).");
      return;
    end_if;
  else
    // old version => no dynamic header, the first list starts where the dynamic header length would be

    //start address of first list: address of time info + size of time info
    ud_ListBaseAddress := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_LEN_HEAD + sizeof(UDINT);
  end_if;

  GotOwnPath := FALSE;
  
  //highest address for config data
  ud_ListAddressMax := SDO_ADDRESS_CONFIG_DATA + SDO_OFFSET_CONFIG_LEN_ALL + ud_ConfigLength;

  //max. length of data to read with one request
  us_DataLength_Max := SBF_SDO_MAX_LENGTH - sizeof(SDORespReadConfig) + sizeof(SDORespReadConfig.ud_Data);
  
  while ( ud_ListBaseAddress < ud_ListAddressMax ) do
    
    //reset static pointers every run, because ReadModulePath sets it to NIL if no path is avaliable
    InitPathInfo(#ListHeader, #ListPath[0], #ListPathComp[0]);
      
    //read path in list-header
    sd_retval := ReadModulePath ( p_SDORequestBuffer          := p_SDORequestBuffer
                                , p_SDOResponseBuffer         := p_SDOResponseBuffer
                                , p_ud_Address                := #ud_ListBaseAddress
                                , pModule                     := #ListHeader
                                , ud_ModulePathLen_Max        := sizeof(ListPath)
                                , ud_ModulePathCompLen_Max    := sizeof(ListPathComp)
                                , us_SDOMaxDataSize           := us_DataLength_Max
                                );

    //no response, error
    if ( sd_retval <> 0 ) then
      LogError("@01E5 (SafetyBaseFSoE::ReadSDOConfig) Error reading module path out of config data.");
      return;
    end_if;

    // remember our own uncompressed path (first listheader path in the memory)
    if ListHeader.p_us_Path & (GotOwnPath = FALSE) then
      GotOwnPath := TRUE;
      _memcpy(ptr1:=#PathOfThisCPU[0], ptr2:=ListHeader.p_us_Path, cntr:=(ListHeader.p_us_Path$^UINT)^ + 2);      
    end_if;

    //read length of download data in list-header (=LengthOverall) and save address
    //to calculate later the address of next list-header
    //address = last address + 4 byte (CRC)
    ud_ListLength_Address := ud_ListBaseAddress + 4;
    
    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := ud_ListLength_Address
                                , us_Length           := 4
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@01E6 (SafetyBaseFSoE::ReadSDOConfig) Failed to read out the length of the download data within the configuration.");
      return;
    end_if;
    
    //save length of list
    ud_ListLength := p_SDOResponseBuffer^.ud_Data;
    
   
    //                                                   v
    //address of LengthOverall + 4 bytes (LengthOverall, BlockType, ModuleID)
    ud_Address := ud_ListLength_Address + 4;

    // get BlockType
    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := ud_Address
                                , us_Length           := 4
                                );
    //no response, error
    if ( sd_retval <> 0 ) then
      LogError("@01E7 (SafetyBaseFSoE::ReadSDOConfig) Error getting the block type.");
      return;
    end_if;  

    ud_ActBlockType := p_SDOResponseBuffer^.ud_Data;

    case ud_ActBlockType of
//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_CPU, 
      SBF_BLKTYPE_CFG_CPU_OPTIONAL , // if we don't need data from an external cpu
      SBF_BLKTYPE_CFG_MASTER_CPU: 
        //add other CPUs to routing table
  
        // if there is a valid module path
        if ListHeader.p_us_Path <> NIL then
          //add module to routing tabs
          sd_retval := AddModuleToRoutingTabs ( pModule       := #ListHeader
                                              , pListHeader   := #ListHeader
                                              , b_IsOptional  := (ud_ActBlockType = SBF_BLKTYPE_CFG_CPU_OPTIONAL)
                                              );
          
          if (sd_retval <> 0 ) then
            if ( SafetyState <> _ModFromCfgNotFound ) then
              LogError("@01E8 (SafetyBaseFSoE::ReadSDOConfig) Failed to add cpu module to routing tabs.");
              return;
            end_if;
          end_if;

          TempModuleConfig.Info := 0;
          TempModuleConfig.DestCRCNo := 0;
          TempModuleConfig.pDestCRCs := NIL;
          TempModuleConfig.PdoOutLen := 0;
          
          TempModuleConfig.Info.IsOptional  := (ud_ActBlockType = SBF_BLKTYPE_CFG_CPU_OPTIONAL);
          TempModuleConfig.InterfaceCRC     := 0;

          // Remember the path (uncompressed and compressed)
          TempModuleConfig.PathLen := ListHeader.p_us_Path^$UINT + 2;
          TempModuleConfig.pPath := ListHeader.p_us_Path;

          TempModuleConfig.CompPathLen := ListHeader.ui_PathCompLen;
          TempModuleConfig.pCompPath := ListHeader.p_us_PathComp;

          // if it's our own path, remember the safety number
          if (PathOfThisCPU[0]$UINT = (ListHeader.p_us_Path$^UINT)^) & (ToStdLib.MemCmp(#PathOfThisCPU[0], ListHeader.p_us_Path, PathOfThisCPU[0]$UINT + 2) = 0) then       
            TempModuleConfig.SafetyNbr := SafetyNumber;
            //safe config of scp modul (own path)
            SafeActModuleConfig(TempModuleConfig);
          else
            TempModuleConfig.SafetyNbr := 0;
          end_if;

          // only when there is a manager
          if pSafetyManagerThis then
            AddModuleToCfg(TempModuleConfig);
          end_if;
        end_if;

//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_SLAVE_COMPR, 
      SBF_BLKTYPE_CFG_SLAVE_COMPR_OPTIONAL: //Blocktyp der über die komprimierte Gesamtkonfiguration einer Slave-CPU gegeben wird
        //add other CPUs to routing table

        // if there is a valid module path
        if ListHeader.p_us_Path <> NIL then
          //add module to routing tabs
          sd_retval := AddModuleToRoutingTabs ( pModule       := #ListHeader
                                              , pListHeader   := #ListHeader
                                              , b_IsOptional  := (ud_ActBlockType = SBF_BLKTYPE_CFG_SLAVE_COMPR_OPTIONAL)
                                              );
          
          if (sd_retval <> 0 ) then
            if ( SafetyState <> _ModFromCfgNotFound ) then
              LogError("@0270 (SafetyBaseFSoE::ReadSDOConfig) Slave: Failed to add cpu module to routing tabs.");
              return;
            end_if;
          end_if;
          
          // we skip the config till we get to the interface information of this slave (if it's the source of an interface frame we have to remember it)

          //address of LengthOverall + 3 * 4 bytes (LengthOverall, BlockType, ModuleID) => now the address points to FUB offset
          ud_Address := ud_ListLength_Address + 12;
          
          //skip FUB offset
          ud_Address += 4;
          
          // get no of FUB´s
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response, error
          if ( sd_retval <> 0 ) then
            LogError("@01E9 (SafetyBaseFSoE::ReadSDOConfig) Slave: Error getting the FUB number.");
            return;
          end_if;  

          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;
          
          // skip no of FUB´s and FUB´s
          ud_Address += (4 + (ud_TmpCnt * 4));

          // get length of path
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response, error
          if ( sd_retval <> 0 ) then
            LogError("@01EA (SafetyBaseFSoE::ReadSDOConfig) Slave: Error getting the path length.");
            return;
          end_if;  

          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;
          
          // skip length of path and the path
          ud_Address += (4 + ud_TmpCnt);

          // get length of comp path
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response, error
          if ( sd_retval <> 0 ) then
            LogError("@01EB (SafetyBaseFSoE::ReadSDOConfig) Slave: Error getting the comp path length.");
            return;
          end_if;  

          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;
          
          // skip length of comp path and the comp path
          ud_Address += (4 + ud_TmpCnt);

          // skip CRC and LengthOverall to get to the Blocktyp (Modul-Downloaddaten)
          ud_Address += 8;

          // get the block type
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response, error
          if ( sd_retval <> 0 ) then
            LogError("@01EC (SafetyBaseFSoE::ReadSDOConfig) Slave: Error getting the block type of a slave config.");
            return;
          end_if;  

          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          // check if it's a new version of the configuration
          if (ud_TmpCnt = SBF_BLKTYPE_CFG_CONTAINER_V3)       |
             (ud_TmpCnt = SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE)     |
             (ud_TmpCnt = SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE_OPTIONAL) then

            // skip blocktype to get to the dynamic header length (how many data are attached to the header)
            ud_Address += 4;

            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@01ED (SafetyBaseFSoE::ReadSDOConfig) Error reading length of dynamic header data of a slave config.");
              return;
            end_if;

            // plausibility check for data length
            if p_SDOResponseBuffer^.ud_Data > 1000 then
              sd_retval := -1;
              LogError("@01EE (SafetyBaseFSoE::ReadSDOConfig) Invalid length of dynamic header data of a slave config.");
              return;
            end_if;


            // the length info contains: size of length info + size of time info + size of additional data
            
            // skip the additional data to get to the path length
            ud_Address += p_SDOResponseBuffer^.ud_Data;
          else
            // old version => no dynamic header, so we skip only blocktype and time info
            ud_Address += 8;
          end_if;

          // get the path length
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response, error
          if ( sd_retval <> 0 ) then
            LogError("@01EA (SafetyBaseFSoE::ReadSDOConfig) Slave: Error getting the path length.");
            return;
          end_if;  

          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          // skip length of path and the path
          ud_Address += (4 + ud_TmpCnt);

          // get length of comp path
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response, error
          if ( sd_retval <> 0 ) then
            LogError("@01EB (SafetyBaseFSoE::ReadSDOConfig) Slave: Error getting the comp path length.");
            return;
          end_if;  

          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          // skip length of comp path and the comp path
          ud_Address += (4 + ud_TmpCnt);

          // skip CRC, LengthOverall, Blocktyp (Modul-Downloaddaten), ModuleID
          ud_Address += 16;
          
          // get the InterfaceCRC
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response, error
          if ( sd_retval <> 0 ) then
            LogError("@01EF (SafetyBaseFSoE::ReadSDOConfig) Slave: Error getting the Inteface-CRC.");
            return;
          end_if;  

          TmpCRC := p_SDOResponseBuffer^.ud_Data;

          
          //add slave information to config
          AddSlaveToCfg ( pModule       := #ListHeader
                        , b_IsOptional  := (ud_ActBlockType = SBF_BLKTYPE_CFG_SLAVE_COMPR_OPTIONAL)
                        , b_IsInterface := (TmpCRC <> 0)
                        , InterfaceCRC  := TmpCRC
                        );

        end_if;

//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_REV: // revision
        
        // check if the list is long enough for a String (more bytes than just the header)
        if ud_ListLength > 12 then
        
          //address of LengthOverall + 3 * 4 bytes (LengthOverall, BlockType, ModuleID) => now the address points to the revision string
          ud_Address := ud_ListLength_Address + 12;
          
          //read out the string (length = LengthOverall - length header)
          
          // get memory for the string
          if pPrjNameStr = NIL then
            PrjNameMemLen := ud_ListLength$USINT - 12;
            pPrjNameStr$^void := ToStdLib.Malloc(size := PrjNameMemLen + 1);
          else
            if PrjNameMemLen <> (ud_ListLength$USINT - 12) then
              PrjNameMemLen := ud_ListLength$USINT - 12;
              pPrjNameStr$^void := ToStdLib.ReAlloc(mptr:=pPrjNameStr, newsize:=PrjNameMemLen + 1);
            end_if;
          end_if;

          // check if we got a memory
          if pPrjNameStr then

            _memset(dest:=pPrjNameStr, usByte:=0, cntr:=PrjNameMemLen+1);
           
            ud_ActStrLen := ud_TmpStrLen := PrjNameMemLen;
            ud_TmpAddress := ud_Address;        
            
            IF p_BlockedTransferMemory = NIL THEN
              sd_retval := -100;
              SetSafetyState(NewState:=_MemAllocFailed);
              RETURN;
            END_IF;
            
            IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
              p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
              IF p_BlockedTransferMemory = NIL THEN
                ud_BlockedTransferMemorySize := 0;
                sd_retval := -100;
                SetSafetyState(NewState:=_MemAllocFailed);
                RETURN;
              ELSE
                ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
              END_IF;
            END_IF;

            ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

            p_TmpBlockedData := p_BlockedTransferMemory;
                    
            us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
            
            while us_BlockedTransferLength do
            
              // get name
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_TmpAddress
                                          , us_Length           := us_BlockedTransferLength
                                          , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                          );
              //no response
              if ( sd_retval <> 0 ) then
                LogError("@01F0 (SafetyBaseFSoE::ReadSDOConfig) Reading Revision failed.");
                return;
              end_if;

              ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                               source:=#p_SDOResponseBuffer^.ud_Data, 
                               size:=us_BlockedTransferLength);
              
              p_TmpBlockedData += us_BlockedTransferLength;
              ud_TmpAddress += us_BlockedTransferLength;
              ud_TmpStrLen -= us_BlockedTransferLength;
              
              us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
            
            end_while;
            
            ToStdLib.MemCpy(dest:=pPrjNameStr, 
                             source:=p_BlockedTransferMemory, 
                             size:=PrjNameMemLen+1);

            b_GotNewRevision := TRUE;

            // string is formed by: project name {TAB} project revision   => search for the tab
            pPrjRevStr := pPrjNameStr; // initialize pointer, because maybe there is no content in the projectname
            pChar := pPrjNameStr;
            while pChar^ do
              // check for tab sign
              if pChar^ = 9 then
                // at the next character our revision starts
                pPrjRevStr := pChar + sizeof(CHAR);
                
                // we change the tab to a string termination
                pChar^ := 0;
                
                // save the length of the name and the length of the project revision
                PrjNameStrLen := pChar$UDINT - pPrjNameStr$UDINT;
                PrjRevStrLen := PrjNameMemLen - PrjNameStrLen - sizeof(CHAR);
                exit;
              end_if;
              
              pChar += sizeof(CHAR);
            end_while;
            
            ShowProjectRevision();
          end_if;
        end_if;

//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_CRC: // CRC from Safety Application and HW-Tree
        
        // check if the list is long enough for a String (more bytes than just the header)
        if ud_ListLength > 12 then
        
          //address of LengthOverall + 3 * 4 bytes (LengthOverall, BlockType, ModuleID) => now the address points to the CRC values
          ud_Address := ud_ListLength_Address + 12;
          
          //read out the CRCs (length = 2 * 4 bytes)

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 2*sizeof(HDINT)
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0797 (SafetyBaseFSoE::ReadSDOConfig) Reading CRC info of the safety designer project failed.");
            return;
          end_if;

          CRCInfo := p_SDOResponseBuffer^.ud_Data$t_CRCInfo;
          
          ApplCRC := CRCInfo.ApplCRC;
          HwCRC   := CRCInfo.HwCRC;
        end_if;        

//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_INTERFACE, 
      SBF_BLKTYPE_CFG_INTERFACE_OPTIONAL: // interface frame

        TempPdoConfig.Info := 0;

        //address of LengthOverall + 3 * 4 bytes (LengthOverall, BlockType, Module ID) => points to interface CRC
        ud_Address := ud_ListLength_Address + 12;
   

//*****************************************************************************
//**   INTERFACE CRC                                                         **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F1 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading interface CRC of interface frame block failed.");
          return;
        end_if;

        TempPdoConfig.InterfaceCRC := p_SDOResponseBuffer^.ud_Data;

        // skip interface crc
        ud_Address += 4;
        
        
//*****************************************************************************
//**   LENGTH OF TOPOLOGY PATH OF INTERFACE FRAME                            **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F2 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading length of topology path of interface frame block failed.");
          return;
        end_if;


        // skip path length
        ud_Address += 4;
        
        us_BlockedTransferLength := p_SDOResponseBuffer^.ud_Data$USINT;


//*****************************************************************************
//**   TOPOLOGY PATH OF INTERFACE FRAME                                      **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := us_BlockedTransferLength
                                    , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F3 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading topology path of interface frame block failed.");
          return;
        end_if;


        // remember the destination path
        _memcpy(ptr1:=#TempPath[0], ptr2:=#p_SDOResponseBuffer^.ud_Data, cntr:=us_BlockedTransferLength);

        // this is the source of the interface frame
        TempPdoConfig.SourcePathLen := TempPath[0]$UINT + 2; 
        TempPdoConfig.pSourcePath   := #TempPath[0];


        // skip path
        ud_Address += us_BlockedTransferLength;
        

//*****************************************************************************
//**   LENGTH OF COMPRESSED TOPOLOGY PATH OF INTERFACE FRAME                 **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F4 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading length of compressed topology path of interface frame block failed.");
          return;
        end_if;


        // skip path length
        ud_Address += 4;
        
        us_BlockedTransferLength := p_SDOResponseBuffer^.ud_Data$USINT;
        

//*****************************************************************************
//**   COMPRESSED TOPOLOGY PATH OF INTERFACE FRAME                           **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := us_BlockedTransferLength
                                    , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F5 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading compressed topology path of interface frame block failed.");
          return;
        end_if;

        // if it's an IP address => we need the compressed path for the source
        if p_SDOResponseBuffer^.ud_Data$USINT = 6 then
          // we may need this later (e.g. if it's an IP address)
          _memcpy(ptr1:=#TempPath[0], ptr2:=#p_SDOResponseBuffer^.ud_Data, cntr:=us_BlockedTransferLength);
          
          // this is the source of the interface frame
          TempPdoConfig.SourcePathLen := us_BlockedTransferLength; 
          TempPdoConfig.Info.SrcPathIsCompressed := TRUE;
          
          if ((#p_SDOResponseBuffer^.ud_Data + 5)^$HDINT AND 16#00FFFFFF) = 0 then
            // special IP: (0.0.0.x)
            TempPdoConfig.Info.SrcPathIsCompressed := FALSE;  // Not valid IP
          end_if;
        end_if;
  
        // skip path and maximum cycle time
        ud_Address += us_BlockedTransferLength + 4;


  // here starts the data area (before we read only the list header

//*****************************************************************************
//**   INTERFACE CRC                                                         **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F6 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading CRC of interface frame block failed.");
          return;
        end_if;

        TempPdoConfig.InterfaceCRC := p_SDOResponseBuffer^.ud_Data;
        
        // skip interface CRC
        ud_Address += 4;


//*****************************************************************************
//**   NUMBER OF BOOL INTERFACE VARIABLES IN THE PDO                         **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F7 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading number of bool variables in the interface frame failed.");
          return;
        end_if;

        BoolOuts := p_SDOResponseBuffer^.ud_Data;

        // skip bool variables
        ud_Address += 4;

//*****************************************************************************
//**   NUMBER OF DINT INTERFACE VARIABLES IN THE PDO                         **
//*****************************************************************************

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F8 (SafetyBaseFSoE::ReadSDOConfig) Interface: Reading number of dint variables in the interface frame failed.");
          return;
        end_if;

        // skip dint variables
        ud_Address += 4;

        DintOuts := p_SDOResponseBuffer^.ud_Data;

        // if there are interface variables
        if BoolOuts | DintOuts then
          // calculate the length of the interface frame
          TempPdoConfig.PdoLen := CalcPDOLength(BoolOuts, DintOuts);

          // and the destination
          TempPdoConfig.DestPathLen   := PathOfThisCPU[0]$UINT + 2;
          TempPdoConfig.pDestPath     := #PathOfThisCPU[0];

          TempPdoConfig.Info.IsInterfacePDO := TRUE;
          TempPdoConfig.Info.IsOptional := (ud_ActBlockType = SBF_BLKTYPE_CFG_INTERFACE_OPTIONAL);

          // add the interface PDO to the list
          AddPdoToCfg(TempPdoConfig);
        else
          LogError("@01F9 (SafetyBaseFSoE::ReadSDOConfig) Interface: No interface variables found in interface blocktype.");
          return;
        end_if;

//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_MODULE_WITHOUT_CFG,
      SBF_BLKTYPE_CFG_MODULE_WITHOUT_CFG_OPT:      // module configurations  
        TempModuleConfig.Info := 0;
        TempModuleConfig.DestCRCNo := 0;
        TempModuleConfig.pDestCRCs := NIL;
        case ud_ActBlockType of          
          SBF_BLKTYPE_CFG_MODULE_WITHOUT_CFG_OPT:
            TempModuleConfig.Info.IsOptional := TRUE;
          else
            TempModuleConfig.Info.IsOptional := FALSE;
        end_case;
       
        BoolOuts := 0;
        DintOuts := 0;

        // Remember the path (uncompressed and compressed)
        TempModuleConfig.PathLen := ListHeader.p_us_Path^$UINT + 2;
        TempModuleConfig.pPath := ListHeader.p_us_Path;

        TempModuleConfig.CompPathLen := ListHeader.ui_PathCompLen;
        TempModuleConfig.pCompPath := ListHeader.p_us_PathComp;
        
        //add module to routing tabs
        sd_retval := AddModuleToRoutingTabs ( pModule       := #ListHeader
                                            , pListHeader   := #ListHeader
                                            , b_IsOptional  := TempModuleConfig.Info.IsOptional
                                            );
        
        if (sd_retval <> 0 ) then
          if ( SafetyState <> _ModFromCfgNotFound ) then
            LogError("@020D (SafetyBaseFSoE::ReadSDOConfig) Failed to add safe input module to routing tabs.");
            return;
          end_if;
        end_if;


//*****************************************************************************
//**   LIST HEADER                                                           **
//*****************************************************************************
//   Offset  Größe	Beschreibung
//    0x00	4 Byte	CRC32
//    0x04	4 Byte	Länge der Konfiguration (inkl. Längenfeld)
//    0x08	4 Byte	Block-Typ (nicht optional: 0x00000029; optional: 0x40000029)
//    0x0C	4 Byte	Modul-ID
//    0x10	4 Byte	Version der Konfiguration (0x00000000)
//*****************************************************************************

        //address of LengthOverall + 4 * 4 bytes (LengthOverall, BlockType, ModuleID, VersionConfig)
        ud_Address := ud_ListLength_Address + 16;       
      
//*****************************************************************************
//    FSoE Konfiguration
//*****************************************************************************
//    0x14	4 Byte	Anzahl an FSoE-Verbindungen (0x00000001)
//*****************************************************************************    
//    FSoE Konfiguration * Anzahl an FSoE-Verbindungen
//*****************************************************************************
//    0x18	4 Byte	Länge der FSoE-Konfiguration
//    0x1C	4 Byte	Flags (0x00000000)
//    0x20	2 Byte	Größe der sicheren Ausgänge in Byte
//    0x22	2 Byte	Größe der sicheren Eingänge in Byte
//    0x24	2 Byte	FSoE Slaveadresse
//    0x26	2 Byte	reserviert
//*****************************************************************************
                
        // skip entrys to read fsoe slave address
        ud_Address += 16;
        
        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01F6 (SafetyBaseFSoE::ReadSDOConfig) Reading interface CRC of interface frame block failed.");
          return;
        end_if;

        // only when there is a manager
        if pSafetyManagerThis then
          AddModuleToCfg(TempModuleConfig);
        end_if;        
      
//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_MODULE, 
      SBF_BLKTYPE_CFG_MODULE_V2, 
      SBF_BLKTYPE_CFG_MODULE_OPTIONAL, 
      SBF_BLKTYPE_CFG_CONTAINER_SLAVE, 
      SBF_BLKTYPE_CFG_CONTAINER_SLAVE_OPTIONAL,
      SBF_BLKTYPE_CFG_MODULE_V3,
      SBF_BLKTYPE_CFG_MODULE_V3_OPTIONAL,
      SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE,
      SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE_OPTIONAL:      // module configurations  
        TempModuleConfig.Info := 0;
        TempModuleConfig.DestCRCNo := 0;
        TempModuleConfig.pDestCRCs := NIL;
        case ud_ActBlockType of 
          SBF_BLKTYPE_CFG_MODULE_OPTIONAL,
          SBF_BLKTYPE_CFG_CONTAINER_SLAVE_OPTIONAL,
          SBF_BLKTYPE_CFG_MODULE_V3_OPTIONAL,
          SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE_OPTIONAL:
            TempModuleConfig.Info.IsOptional := TRUE;
          else
            TempModuleConfig.Info.IsOptional := FALSE;
        end_case;
       
        b_CfgTypeV2 := (ud_ActBlockType <> SBF_BLKTYPE_CFG_MODULE);

        // check if it's a V3 config, because the structure of the IO List is different
        if (ud_ActBlockType = SBF_BLKTYPE_CFG_MODULE_V3) | 
           (ud_ActBlockType = SBF_BLKTYPE_CFG_MODULE_V3_OPTIONAL) |
           (ud_ActBlockType = SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE) |
           (ud_ActBlockType = SBF_BLKTYPE_CFG_CONTAINER_V3_SLAVE_OPTIONAL) then
          b_CfgTypeV3 := TRUE;
        else
          b_CfgTypeV3 := FALSE;
        end_if;

        // if it's our own path, remember the type of module configuration
        if (PathOfThisCPU[0]$UINT = (ListHeader.p_us_Path$^UINT)^) & (ToStdLib.MemCmp(#PathOfThisCPU[0], ListHeader.p_us_Path, PathOfThisCPU[0]$UINT + 2) = 0) then
          // if config is already "old" dont change to "new". (can be set by block SBF_BLKTYPE_OLD_MODE_FLAG)
          if NewConfigType <> ConfigState_Old then
            NewConfigType := (b_CfgTypeV2$USINT + 1)$t_ConfigStateType;
          end_if;
        end_if;

        BoolOuts := 0;
        DintOuts := 0;

        // Remember the path (uncompressed and compressed)
        TempModuleConfig.PathLen := ListHeader.p_us_Path^$UINT + 2;
        TempModuleConfig.pPath := ListHeader.p_us_Path;

        TempModuleConfig.CompPathLen := ListHeader.ui_PathCompLen;
        TempModuleConfig.pCompPath := ListHeader.p_us_PathComp;


//*****************************************************************************
//**   LIST HEADER                                                           **
//*****************************************************************************


        //address of LengthOverall + 3 * 4 bytes (LengthOverall, BlockType, ModuleID)
        ud_Address := ud_ListLength_Address + 12;

        // only available at new modules
        if b_CfgTypeV2 then          

//*****************************************************************************
//**   INTERFACE CRC                                                         **
//*****************************************************************************

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@01F6 (SafetyBaseFSoE::ReadSDOConfig) Reading interface CRC of interface frame block failed.");
            return;
          end_if;

          TempPdoConfig.InterfaceCRC := p_SDOResponseBuffer^.ud_Data;

          // skip interface crc
          ud_Address += 4;
          
          
//*****************************************************************************
//**   LENGTH OF TOPOLOGY PATH OF INTERFACE FRAME                            **
//*****************************************************************************

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@01F2 (SafetyBaseFSoE::ReadSDOConfig) Reading length of topology path of interface frame block failed.");
            return;
          end_if;


          // skip path length
          ud_Address += 4;
          
          us_BlockedTransferLength := p_SDOResponseBuffer^.ud_Data$USINT;


//*****************************************************************************
//**   TOPOLOGY PATH OF INTERFACE FRAME                                      **
//*****************************************************************************

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := us_BlockedTransferLength
                                      , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@01F3 (SafetyBaseFSoE::ReadSDOConfig) Reading topology path of interface frame block failed.");
            return;
          end_if;

          // remember the destination path
          _memcpy(ptr1:=#TempPath[0], ptr2:=#p_SDOResponseBuffer^.ud_Data, cntr:=us_BlockedTransferLength);
          

          // skip path
          ud_Address += us_BlockedTransferLength;
          

//*****************************************************************************
//**   LENGTH OF COMPRESSED TOPOLOGY PATH OF INTERFACE FRAME                 **
//*****************************************************************************

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@01F4 (SafetyBaseFSoE::ReadSDOConfig) Reading length of compressed topology path of interface frame block failed.");
            return;
          end_if;


          // skip path length
          ud_Address += 4;
          
          us_BlockedTransferLength := p_SDOResponseBuffer^.ud_Data$USINT;
          

//*****************************************************************************
//**   COMPRESSED TOPOLOGY PATH OF INTERFACE FRAME                           **
//*****************************************************************************

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := us_BlockedTransferLength
                                      , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@01F5 (SafetyBaseFSoE::ReadSDOConfig) Reading compressed topology path of interface frame block failed.");
            return;
          end_if;

          ud_Address += us_BlockedTransferLength;

          // skip max. cycle time
          ud_Address += 4;        
        end_if;

        // on first 4 bytes is fix the system time
        ud_ActAddress := 4; 

        // address in memory for unsafe BDINT variables
        ud_UnsafeBDINT_Offset := 0;
        
        // address on the module to access the fast unsafe variables via SDIAS (offset 1 for the message counter)
        ud_FastUnsafe_Offset_Out  := 1;
        ud_FastUnsafe_Offset_In   := 1;


//*****************************************************************************
//**   FSoE CONNECTION LIST                                                  **
//*****************************************************************************

        if b_IsFSoE then
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0271 (SafetyBaseFSoE::ReadSDOConfig) Failed to get the number of FSoE elements.");
            return;
          end_if;

          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          // skip number of entries time
          ud_Address += 4;        

          if ud_TmpCnt then
            for i := 1 to ud_TmpCnt do
              // read size of structure

              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0272 (SafetyBaseFSoE::ReadSDOConfig) Failed to read size of FSoE structure element.");
                return;
              end_if;
              
              // skip the whole entry since we got our info earlier via GetFSoEConnectionInfos()
              ud_Address += p_SDOResponseBuffer^.ud_Data;        
            end_for;
          end_if;
        end_if;
(*

Aufbau Verbindungsliste:

Zuerst wird die Anzahl der Elemente in der Liste angegeben:

  ULONG nbrOfElements;                    // Anzahl der Elemente in der Liste

Danach kommen die einzelnen Listenelemente.
Der Anfang eines Listenelements ist auf 4 Byte ausgerichtet.

Aufbau des Listenelements:

  typedef struct FSOE_CONN_DSC
  {
    // Größe der Struktur (inkl. Längenfeld selbst)
    ULONG len;
    //! Flags
    //  Actually, type is 4 bytes in size but for compatibility reasons it is still used as 1 byte. As a result, bits 9-32 are currently not used.
    //  Bit1: 1, wenn FSoE-Master, 0=FSoE-Slave
    //  Bit2: 1, wenn die Slave-Adresse über den HW-Pfad ermittelt wird 
    //  Bit3: 1, wenn CRC von der FW ermittelt werden muss
    //  Bit4: 1, wenn Interface und Slave
    //  Bit5: 1, wenn Multichannel verwendet werden, aber ein Multichannel ist nicht vorhanden und der Eingang auf 1 gesetzt wird.
    //  Bit6: 1, wenn der Adressvergabeservice verwendet werden soll
    //  Bit7: 1, wenn der Master beim Slave die Connection ID anfordert
    //  Bit8: 1, wenn es sich um ein HGW handelt
    //  Bit9: 1, wenn Assign Slave On Demand 
    //  Bits 10-32: reserviert, sind auf 0 gesetzt und werden nicht ausgewertet 
  #define FSOE_CONN_FLAGS_MASTER                    (1 << 0)
  #define FSOE_CONN_FLAGS_USE_HWPATH_AS_SLAVEADDR   (1 << 1)
    ULONG flags;
    //! Größe der sicheren Prozessdaten, die vom FSoE Master an den FSoE Slave übertragen werden
    // Wert kann nur 1 oder ein vielfaches von 2 sein (1, 2, 4, 6, ...)
    USHORT sizeSafeOutputs;
    //! Größe der sicheren Prozessdaten, die vom FSoE Slave an den FSoE Master übertragen werden
    // Wert kann nur 1 oder ein vielfaches von 2 sein (1, 2, 4, 6, ...)
    USHORT sizeSafeInputs;
    //! Safety over EtherCAT Slave Adresse der Safety over EtherCAT Connection
    // Bei einem Master ist das die Adresse des Ziels, bei einem Slave die eigene Adresse.
    // Wert 0 = Wert wird aus dem EEProm geholt oder über den HW-Pfad ermittelt
    // (HW-Pfad, wenn Flag FSOE_CONN_FLAGS_USE_HWPATH_AS_SLAVEADDR gesetzt ist)
    USHORT slaveAddress;

    // ==== die folgenden Daten gibt es nur im Master ====

    //! ConnectionId der Safety over EtherCAT Connection
    // Wert 0 = Wert wird aus dem EEProm geholt
    USHORT connectionId;

    //! Länge der Kommunikationsparameter, muss immer 2 sein
    USHORT commParaLength;
    //! FSoE Watchdog-Zeit in ms (nur für den Master relevant)
    USHORT watchdog_ms;
    //! Größe der Anwendungsparameter
    USHORT applParaLength;
    /* 
----------------------------------------------------------------------
     * Bis hierher ist die Größe der Elemente in der Struktur fix.
     * Danach sind die Elemente mit variabler Größe angeordnet:
     *
     * Applikationsparameter:
     *    Die Applikationsparameter für ein SCP011 als FSoE Slave bestehen
     *    aus der Konfigurations-CRC (Listenheader) oder der Interface-CRC
     *    des Slaves.
     *
     * HW-Pfad: <-- ist auf 4 Byte ausgerichtet
     *    Wenn das Flag FSOE_CONN_FLAGS_USE_HWPATH_AS_SLAVEADDR gesetzt ist, dann
     *    ermittelt sich der Master die Adresse des Slaves anhand des hier
     *    angegebenen HW-Pfades.
     *    Wenn das Flag FSOE_CONN_FLAGS_USE_HWPATH_AS_SLAVEADDR nicht gesetzt ist,
     *    dann sind hier keine Daten vorhanden.
     *    Besteht aus:
     *    - normaler Pfad
     *    - komprimierter Pfad
     */

     // ==== ab hier wieder für Master und Slave ====

    /*
     * Füllbytes, damit die Größe der Struktur durch 4 teilbar ist
     */


*)


//*****************************************************************************
//**   INTERPRETER CODE                                                      **
//*****************************************************************************
        
        if b_IprCodeLengthAvailable = TRUE then
        
          
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0271 (SafetyBaseFSoE::ReadSDOConfig) Failed to get length of interpredercode.");
            return;
          end_if;
          
          //read length of interpretercode 
          ud_TmpLength := p_SDOResponseBuffer^.ud_Data;
          
          //set correct address
          ud_Address += ud_TmpLength;
          ud_Address += 4;
        else
          
     
          //read til end-command is reached
          while ( 1 ) do
            
            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );
            //no response, error
            if ( sd_retval <> 0 ) then
              LogError("@01FA (SafetyBaseFSoE::ReadSDOConfig) Reading next command failed.");
              return;
            end_if;
            
            //end-command found
            if ( p_SDOResponseBuffer^.ud_Data = SBF_SDO_CONFIG_END_CMD ) then
              exit;
            end_if;         
  
            //read next opcode
            //length of one command = 4 x 4 bytes
            ud_Address += 16;                

           
            //if there is no end-command found, the returncode of response will be <> OK, when the end of config data is missed
            //In this case the loop ends with returncode <> 0.
          end_while;

          //list of I/Os starts at last address + 16 (length of end command = 4x4 bytes)
          ud_Address += 16;          
        
       end_if;

    


//*****************************************************************************
//**   LOCAL BOOL INPUTS                                                     **
//*****************************************************************************

        //read number of local bool inputs
        
        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01FB (SafetyBaseFSoE::ReadSDOConfig) Reading number of local inputs failed.");
          return;
        end_if;

        // remember the number
        BoolOuts += p_SDOResponseBuffer^.ud_Data;
      
        // get offset in application memory
        ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 4;

        // skip number of entries
        p_SDORequestBuffer^.ud_Address += 4;
        
        if b_IsFSoE then
          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          if ud_TmpCnt then
            for i := 1 to ud_TmpCnt do
              // skip QSE, Filtertime, Modulkennung (SDIAS)
              p_SDORequestBuffer^.ud_Address += 12;
            
              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := p_SDORequestBuffer^.ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0273 (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for local inputs failed.");
                return;
              end_if;

              // skip number of entries and the entries
              p_SDORequestBuffer^.ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
            end_for;
          end_if;
        else
          // only available at new modules
          if b_CfgTypeV2 then          
            ud_skipLength := 12 * p_SDOResponseBuffer^.ud_Data; // QSE, Filtertime, Modulkennung(SDIAS)
          else
            ud_skipLength := 8 * p_SDOResponseBuffer^.ud_Data; // QSE, Filtertime
          end_if;

          p_SDORequestBuffer^.ud_Address += ud_skipLength;
        end_if;

//*****************************************************************************
//**   LOCAL BOOL OUTPUTS AS INPUTS                                          **
//*****************************************************************************

        // get number of safe outputs as inputs

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := p_SDORequestBuffer^.ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01FC (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe outputs as inputs failed.");
          return;
        end_if;

        // remember the number
        BoolOuts += p_SDOResponseBuffer^.ud_Data;

        // get offset in application memory
        ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 4;

        p_SDORequestBuffer^.ud_Address += 4;

        if b_IsFSoE then
          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          if ud_TmpCnt then
            for i := 1 to ud_TmpCnt do
              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := p_SDORequestBuffer^.ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0274 (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for local outputs as inputs failed.");
                return;
              end_if;

              // skip number of entries and the entries
              p_SDORequestBuffer^.ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
            end_for;
          end_if;
        end_if;


//*****************************************************************************
//**   SAFE BOOL OUTPUT VARIABLES                                            **
//*****************************************************************************

        // get number of safe bool output vars

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := p_SDORequestBuffer^.ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@01FD (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe bool output vars failed.");
          return;
        end_if;
        
        // remember the number
        BoolOuts += p_SDOResponseBuffer^.ud_Data;

        // get offset in application memory
        ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 4;

        p_SDORequestBuffer^.ud_Address += 4;

        if b_IsFSoE then
          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          if ud_TmpCnt then
            for i := 1 to ud_TmpCnt do
              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := p_SDORequestBuffer^.ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0275 (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for bool outputs failed.");
                return;
              end_if;

              // skip number of entries and the entries
              p_SDORequestBuffer^.ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
            end_for;
          end_if;
        end_if;

        // the new container has some additional content here
        if b_CfgTypeV3 then

//*****************************************************************************
//**   LOCAL DINT INPUTS                                                     **
//*****************************************************************************

          //read number of local dint inputs
          
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := p_SDORequestBuffer^.ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@01FE (SafetyBaseFSoE::ReadSDOConfig) Reading number of local DINT inputs failed.");
            return;
          end_if;

          // remember the number
          DintOuts += p_SDOResponseBuffer^.ud_Data;
        
          // get offset in application memory
          ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 8;

          p_SDORequestBuffer^.ud_Address += 4;

          if b_IsFSoE then
            ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

            if ud_TmpCnt then
              for i := 1 to ud_TmpCnt do
                // skip QSE, Filtertime, Modulkennung (SDIAS)
                p_SDORequestBuffer^.ud_Address += 12;
              
                //send request and receive data
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := p_SDORequestBuffer^.ud_Address
                                            , us_Length           := 4
                                            );

                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@0276 (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for local DINT inputs failed.");
                  return;
                end_if;

                // skip number of entries and the entries
                p_SDORequestBuffer^.ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
              end_for;
            end_if;
          else
            p_SDORequestBuffer^.ud_Address += 12 * p_SDOResponseBuffer^.ud_Data;
          end_if;

//*****************************************************************************
//**   LOCAL DINT OUTPUTS AS INPUTS                                          **
//*****************************************************************************

          // get number of safe dint outputs as inputs

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := p_SDORequestBuffer^.ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@01FF (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe DINT outputs as inputs failed.");
            return;
          end_if;

          // remember the number
          DintOuts += p_SDOResponseBuffer^.ud_Data;

          // get offset in application memory
          ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 8;

          p_SDORequestBuffer^.ud_Address += 4;

          if b_IsFSoE then
            ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

            if ud_TmpCnt then
              for i := 1 to ud_TmpCnt do              
                //send request and receive data
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := p_SDORequestBuffer^.ud_Address
                                            , us_Length           := 4
                                            );

                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@0277 (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for local DINT outputs as inputs failed.");
                  return;
                end_if;

                // skip number of entries and the entries
                p_SDORequestBuffer^.ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
              end_for;
            end_if;
          end_if;

        end_if;

//*****************************************************************************
//**   SAFE DINT OUTPUT VARIABLES                                            **
//*****************************************************************************

        // get number of safe dint output vars

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := p_SDORequestBuffer^.ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@0200 (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe dint output vars failed.");
          return;
        end_if;

        // only when there is a manager
        if pSafetyManagerThis then
          // remember the number
          DintOuts += p_SDOResponseBuffer^.ud_Data;

          // now we're ready to determine the PDO size of this module
          TempModuleConfig.PdoOutLen := CalcPDOLength(BoolOuts, DintOuts);
        end_if;

        // get offset in application memory
        ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 8;

        p_SDORequestBuffer^.ud_Address += 4;

        if b_IsFSoE then
          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          if ud_TmpCnt then
            for i := 1 to ud_TmpCnt do              
              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := p_SDORequestBuffer^.ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0278 (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for DINT outputs failed.");
                return;
              end_if;

              // skip number of entries and the entries
              p_SDORequestBuffer^.ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
            end_for;
          end_if;
        end_if;
        
        ud_Address := p_SDORequestBuffer^.ud_Address;

        // special unsafe variables (not supported by old modules)
        if b_CfgTypeV2 then          

//*****************************************************************************
//**   FAST UNSAFE OUTPUT VARIABLES                                          **
//*****************************************************************************
          //check if used a SCP111 or SCP211
          if b_IprCodeLengthAvailable = FALSE then
            // get number of fast unsafe output vars

            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0201 (SafetyBaseFSoE::ReadSDOConfig) Reading number of fast unsafe output vars failed.");
              return;
            end_if;

            // get offset in application memory
            ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 4;

            // if it's our own path, remember the fast unsafe settings
            if (PathOfThisCPU[0]$UINT = (ListHeader.p_us_Path$^UINT)^) & (ToStdLib.MemCmp(#PathOfThisCPU[0], ListHeader.p_us_Path, PathOfThisCPU[0]$UINT + 2) = 0) then       
              if (p_SDOResponseBuffer^.ud_Data <> 0) then
                if ActivateFastUnsafeIOs then
                  b_FastUnsafeOutputsActive := TRUE;
                else
                  // error => can't go on like this
                  LogError("@0202 (SafetyBaseFSoE::ReadSDOConfig) Number or size of module accesses increased with new safety project!");
                  LogError("@0203 (SafetyBaseFSoE::ReadSDOConfig) => Can't continue, because the new data is not covered by the actual accesses! Restart Application!");
                  
                  b_NeedApplicationRestart := TRUE;
                end_if;
              end_if;
            end_if;
            
            ud_Address += 4; 
            
          else
          
//*****************************************************************************
//**   FAST UNSAFE BOOL OUTPUT VARIABLES  SCP211                             **
//*****************************************************************************
            //read number of unsafe output vars
               
            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0204 (SafetyBaseFSoE::ReadSDOConfig) Reading number of unsafe bool output vars failed.");
              return;
            end_if;
            
            ud_CntFastUnsafeOutputs := p_SDOResponseBuffer^.ud_Data;
            
            ud_Address += 4;
                 
            // skip the names of unsafe outputs
            for i := 1 to ud_CntFastUnsafeOutputs do
            
              // get length of Name
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );
              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0205 (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe bool output name failed.");
                return;
              end_if;

              // prepare address to get name
              ud_Address += 4;
              
    //          ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_ActStrLen := ud_TmpStrLen;
              ud_TmpAddress := ud_Address;        
              
              IF p_BlockedTransferMemory = NIL THEN
                sd_retval := -100;
                SetSafetyState(NewState:=_MemAllocFailed);
                RETURN;
              END_IF;
              
              IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
                p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
                IF p_BlockedTransferMemory = NIL THEN
                  ud_BlockedTransferMemorySize := 0;
                  sd_retval := -100;
                  SetSafetyState(NewState:=_MemAllocFailed);
                  RETURN;
                ELSE
                  ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
                END_IF;
              END_IF;

              ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

              p_TmpBlockedData := p_BlockedTransferMemory;
                      
              us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              while us_BlockedTransferLength do
                
                // get name
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_TmpAddress
                                            , us_Length           := us_BlockedTransferLength
                                            , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("0206 (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe bool output name failed.");
                  return;
                end_if;

                ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                                 source:=#p_SDOResponseBuffer^.ud_Data, 
                                 size:=us_BlockedTransferLength);
                
                p_TmpBlockedData += us_BlockedTransferLength;
                ud_TmpAddress += us_BlockedTransferLength;
                ud_TmpStrLen -= us_BlockedTransferLength;
                
                us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              end_while;
                         
              sd_retval :=AddFastUnsafeRoutElement(pRoutTable:=#p_FastUnsafeOutputsTable,
                                                   pRoutElementsNo:=#ui_CntFastUnsafeOutputs,
                                                   ud_Address:=ud_FastUnsafe_Offset_Out,
                                                   ui_NameLen:=ud_ActStrLen$UINT,
                                                   pNameNewElement:=p_BlockedTransferMemory$^CHAR,
                                                   bWriteVar:=TRUE);
                                                   
              // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application                                                      
              
              // get offset in application memory
              ud_ActAddress += 4;
              ud_FastUnsafe_Offset_Out += 4;

              // prepare address for next unsafe output or for number of safe inputs
              ud_Address += ud_ActStrLen;

            end_for;
                                   
                                   
  //*****************************************************************************
  //**   FAST UNSAFE BDINT OUTPUT VARIABLES   SCP211                           **
  //*****************************************************************************

            //read number of unsafe BDINT output vars
               
            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0207 (SafetyBaseFSoE::ReadSDOConfig) Reading number of unsafe BDINT output vars failed.");
              return;
            end_if;

            ud_CntFastUnsafeOutputs := p_SDOResponseBuffer^.ud_Data;
            
            ud_Address += 4;
                   
            if ud_CntFastUnsafeOutputs then
              
              //SDO request for this command has 5 bytes additional data
              pBitMasks$^void := ToStdLib.Malloc(size := ud_CntFastUnsafeOutputs * sizeof(BDINT));
              
              if ( pBitMasks = NIL ) then
                sd_retval                         := -100;
                SetSafetyState(NewState:=_MemAllocFailed);
                LogError("@0208 (SafetyBaseFSoE::ReadSDOConfig) Failed to allocate memory for the BDINT variable bitmasks.");
                return;
              end_if;
              
              // get the bitmasks for the BDINT variables
              for i := 1 to ud_CntFastUnsafeOutputs do
              
                // get bitmask
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@0207 (SafetyBaseFSoE::ReadSDOConfig) Reading bit mask of unsafe BDINT output failed.");
                  return;
                end_if;

                // prepare address for the next one
                ud_Address += 4;
                
                // save the bitmask for adding the rout element
                (pBitMasks + (i-1) * sizeof(BDINT))^ := p_SDOResponseBuffer^.ud_Data;
              end_for;
              
              // skip the names of unsafe outputs
              for i := 1 to ud_CntFastUnsafeOutputs do
              
                // get length of Name
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@020A (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe BDINT output name failed.");
                  return;
                end_if;

                // prepare address to get name
                ud_Address += 4;
                
  //              ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
                ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
                ud_ActStrLen := ud_TmpStrLen;
                ud_TmpAddress := ud_Address;        
                
                IF p_BlockedTransferMemory = NIL THEN
                  sd_retval := -100;
                  SetSafetyState(NewState:=_MemAllocFailed);
                  RETURN;
                END_IF;
                
                IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
                  p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
                  IF p_BlockedTransferMemory = NIL THEN
                    ud_BlockedTransferMemorySize := 0;
                    sd_retval := -100;
                    SetSafetyState(NewState:=_MemAllocFailed);
                    RETURN;
                  ELSE
                    ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
                  END_IF;
                END_IF;

                ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

                p_TmpBlockedData := p_BlockedTransferMemory;
                        
                us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
                
                while us_BlockedTransferLength do
                  
                  // get name
                  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                                              , ud_Address          := ud_TmpAddress
                                              , us_Length           := us_BlockedTransferLength
                                              , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                              );
                  //no response
                  if ( sd_retval <> 0 ) then
                    LogError("@0207 (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe BDINT output name failed.");
                    return;
                  end_if;

                  ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                                   source:=#p_SDOResponseBuffer^.ud_Data, 
                                   size:=us_BlockedTransferLength);
                  
                  p_TmpBlockedData += us_BlockedTransferLength;
                  ud_TmpAddress += us_BlockedTransferLength;
                  ud_TmpStrLen -= us_BlockedTransferLength;
                  
                  us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
                
                end_while;
                
                ActBitMask := (pBitMasks + (i-1) * sizeof(BDINT))^;
                
                // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application
                                
                sd_retval :=AddFastUnsafeRoutElement(pRoutTable:=#p_FastUnsafeOutputsTable,
                                                     pRoutElementsNo:=#ui_CntFastUnsafeOutputs,
                                                     ud_Address:=ud_FastUnsafe_Offset_Out,     
                                                     ui_NameLen:=ud_ActStrLen$UINT,
                                                     pNameNewElement:=p_BlockedTransferMemory$^CHAR,
                                                     ActBitMask,
                                                     bWriteVar:=TRUE);
                
               
                // get offset in application memory
                ud_ActAddress += CountBits(ActBitMask) * sizeof(DINT);
                ud_FastUnsafe_Offset_Out += sizeof(DINT);


                // prepare address for next unsafe output or for number of safe inputs
                ud_Address += ud_ActStrLen;

              end_for;
              
              // free the bitmasks
              ToStdLib.Free(mptr:=pBitMasks);

            end_if;            
            
          end_if; //b_IprCodeLengthAvailable = FALSE
    
        end_if; //b_CfgTypeV2
 
//*****************************************************************************
//**   UNSAFE BOOL OUTPUT VARIABLES                                          **
//*****************************************************************************

        //read number of unsafe output vars
           
        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@0204 (SafetyBaseFSoE::ReadSDOConfig) Reading number of unsafe bool output vars failed.");
          return;
        end_if;

        ud_CntUnsafeOutputs := p_SDOResponseBuffer^.ud_Data;
        
        ud_Address += 4;

        // skip the names of unsafe outputs
        for i := 1 to ud_CntUnsafeOutputs do
        
          // get length of Name
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0205 (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe bool output name failed.");
            return;
          end_if;

          // prepare address to get name
          ud_Address += 4;
          
//          ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
          ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
          ud_ActStrLen := ud_TmpStrLen;
          ud_TmpAddress := ud_Address;        
          
          IF p_BlockedTransferMemory = NIL THEN
            sd_retval := -100;
            SetSafetyState(NewState:=_MemAllocFailed);
            RETURN;
          END_IF;
          
          IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
            p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
            IF p_BlockedTransferMemory = NIL THEN
              ud_BlockedTransferMemorySize := 0;
              sd_retval := -100;
              SetSafetyState(NewState:=_MemAllocFailed);
              RETURN;
            ELSE
              ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
            END_IF;
          END_IF;

          ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

          p_TmpBlockedData := p_BlockedTransferMemory;
                  
          us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
          
          while us_BlockedTransferLength do
            
            // get name
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_TmpAddress
                                        , us_Length           := us_BlockedTransferLength
                                        , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                        );
            //no response
            if ( sd_retval <> 0 ) then
              LogError("0206 (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe bool output name failed.");
              return;
            end_if;

            ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                             source:=#p_SDOResponseBuffer^.ud_Data, 
                             size:=us_BlockedTransferLength);
            
            p_TmpBlockedData += us_BlockedTransferLength;
            ud_TmpAddress += us_BlockedTransferLength;
            ud_TmpStrLen -= us_BlockedTransferLength;
            
            us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
          
          end_while;
          
          sd_retval := AddUnsafeRoutElement(pRoutTable:=#p_UnsafeOutputsTable, 
                                            pRoutElementsNo:=#ui_CntUnsafeOutputs, 
                                            ud_Address:=ud_ActAddress,
                                            ui_NameLen:=ud_ActStrLen$UINT, 
                                            pNameNewElement:=p_BlockedTransferMemory$^CHAR);

          // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application

          // get offset in application memory
          ud_ActAddress += 4;

          // prepare address for next unsafe output or for number of safe inputs
          ud_Address += ud_ActStrLen;

        end_for;


        // special unsafe variables (not supported by old modules)
        if b_CfgTypeV2 then          

//*****************************************************************************
//**   UNSAFE BDINT OUTPUT VARIABLES                                         **
//*****************************************************************************

          //read number of unsafe BDINT output vars
             
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0207 (SafetyBaseFSoE::ReadSDOConfig) Reading number of unsafe BDINT output vars failed.");
            return;
          end_if;

          ud_CntUnsafeOutputs := p_SDOResponseBuffer^.ud_Data;
          
          ud_Address += 4;
          
          if ud_CntUnsafeOutputs then
            
            //SDO request for this command has 5 bytes additional data
            pBitMasks$^void := ToStdLib.Malloc(size := ud_CntUnsafeOutputs * sizeof(BDINT));
            
            if ( pBitMasks = NIL ) then
              sd_retval                         := -100;
              SetSafetyState(NewState:=_MemAllocFailed);
              LogError("@0208 (SafetyBaseFSoE::ReadSDOConfig) Failed to allocate memory for the BDINT variable bitmasks.");
              return;
            end_if;
            

            // get the bitmasks for the BDINT variables
            for i := 1 to ud_CntUnsafeOutputs do
            
              // get bitmask
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );
              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0207 (SafetyBaseFSoE::ReadSDOConfig) Reading bit mask of unsafe BDINT output failed.");
                return;
              end_if;

              // prepare address for the next one
              ud_Address += 4;
              
              // save the bitmask for adding the rout element
              (pBitMasks + (i-1) * sizeof(BDINT))^ := p_SDOResponseBuffer^.ud_Data;
            end_for;
            
            // skip the names of unsafe outputs
            for i := 1 to ud_CntUnsafeOutputs do
            
              // get length of Name
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );
              //no response
              if ( sd_retval <> 0 ) then
                LogError("@020A (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe BDINT output name failed.");
                return;
              end_if;

              // prepare address to get name
              ud_Address += 4;
              
//              ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_ActStrLen := ud_TmpStrLen;
              ud_TmpAddress := ud_Address;        
              
              IF p_BlockedTransferMemory = NIL THEN
                sd_retval := -100;
                SetSafetyState(NewState:=_MemAllocFailed);
                RETURN;
              END_IF;
              
              IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
                p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
                IF p_BlockedTransferMemory = NIL THEN
                  ud_BlockedTransferMemorySize := 0;
                  sd_retval := -100;
                  SetSafetyState(NewState:=_MemAllocFailed);
                  RETURN;
                ELSE
                  ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
                END_IF;
              END_IF;

              ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

              p_TmpBlockedData := p_BlockedTransferMemory;
                      
              us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              while us_BlockedTransferLength do
                
                // get name
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_TmpAddress
                                            , us_Length           := us_BlockedTransferLength
                                            , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@0207 (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe BDINT output name failed.");
                  return;
                end_if;

                ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                                 source:=#p_SDOResponseBuffer^.ud_Data, 
                                 size:=us_BlockedTransferLength);
                
                p_TmpBlockedData += us_BlockedTransferLength;
                ud_TmpAddress += us_BlockedTransferLength;
                ud_TmpStrLen -= us_BlockedTransferLength;
                
                us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              end_while;
              
              ActBitMask := (pBitMasks + (i-1) * sizeof(BDINT))^;
              
              sd_retval := AddUnsafeRoutElement(pRoutTable:=#p_UnsafeOutputsTable, 
                                                pRoutElementsNo:=#ui_CntUnsafeOutputs, 
                                                ud_Address:=ud_UnsafeBDINT_Offset,
                                                ui_NameLen:=ud_ActStrLen$UINT, 
                                                pNameNewElement:=p_BlockedTransferMemory$^CHAR,
                                                ActBitMask);

              // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application
              
              // get offset in application memory
              ud_ActAddress += CountBits(ActBitMask) * sizeof(DINT);
              ud_UnsafeBDINT_Offset += sizeof(DINT);


              // prepare address for next unsafe output or for number of safe inputs
              ud_Address += ud_ActStrLen;

            end_for;
            
            // free the bitmasks
            ToStdLib.Free(mptr:=pBitMasks);
          end_if;
        end_if;

//*****************************************************************************
//**   SAFE INPUT VARIABLES                                                  **
//*****************************************************************************

        // get number of safe inputs
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@020B (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe inputs failed.");
          return;
        end_if;

        //number of safe inputs
        ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

        // get offset in application memory
        ud_ActAddress += ud_TmpCnt * 4;

        //address of path length of first safe input
        ud_Address := p_SDORequestBuffer^.ud_Address + 4;
        
        if ud_TmpCnt then
          //read path of all safe inputs and add them to routing lists
          for i := 1 to ud_TmpCnt do
          
            //reset static pointers every run, because ReadModulePath sets it to NIL if no path is avaliable
            InitPathInfo(#Module, #ModulePath[0], #ModulePathComp[0]);

            sd_retval := ReadModulePath ( p_SDORequestBuffer          := p_SDORequestBuffer
                                        , p_SDOResponseBuffer         := p_SDOResponseBuffer
                                        , p_ud_Address                := #ud_Address
                                        , pModule                     := #Module
                                        , ud_ModulePathLen_Max        := sizeof(ModulePath)
                                        , ud_ModulePathCompLen_Max    := sizeof(ModulePathComp)
                                        , us_SDOMaxDataSize           := us_DataLength_Max
                                        );
            
            //no response, error
            if ( sd_retval <> 0 ) then
              LogError("@020C (SafetyBaseFSoE::ReadSDOConfig) Reading path of safe input failed.");
              return;
            end_if;
            
            // if there is a valid module path
            if Module.p_us_Path <> NIL then
              //add module to routing tabs
              sd_retval := AddModuleToRoutingTabs ( pModule       := #Module
                                                  , pListHeader   := #ListHeader
                                                  , b_IsOptional  := TempModuleConfig.Info.IsOptional
                                                  );
              
              if (sd_retval <> 0 ) then
                if ( SafetyState <> _ModFromCfgNotFound ) then
                  LogError("@020D (SafetyBaseFSoE::ReadSDOConfig) Failed to add safe input module to routing tabs.");
                  return;
                end_if;
              end_if;
            end_if;

            if b_IsFSoE then
              // lower 2 Bytes: Index in connection list, upper 2 Bytes: Bit-Offset of the value inside the process data (not the FSoE frame)
              ud_Address += 4;
            else
              //address of path length = last address + 2 * 4 bytes ( RemoteIndex + MaxAge)
              ud_Address += 8;
            end_if;
          end_for;
        end_if;

        if b_CfgTypeV3 then

//*****************************************************************************
//**   SAFE DINT INPUT VARIABLES                                             **
//*****************************************************************************

          // get number of safe DINT inputs
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@020E (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe DINT inputs failed.");
            return;
          end_if;

          //number of safe inputs
          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          // get offset in application memory
          ud_ActAddress += ud_TmpCnt * 8;

          //address of path length of first safe input
          ud_Address := p_SDORequestBuffer^.ud_Address + 4;
          
          //read path of all safe inputs and add them to routing lists
          for i := 1 to ud_TmpCnt do
          
            //reset static pointers every run, because ReadModulePath sets it to NIL if no path is avaliable
            InitPathInfo(#Module, #ModulePath[0], #ModulePathComp[0]);
            
            sd_retval := ReadModulePath ( p_SDORequestBuffer          := p_SDORequestBuffer
                                        , p_SDOResponseBuffer         := p_SDOResponseBuffer
                                        , p_ud_Address                := #ud_Address
                                        , pModule                     := #Module
                                        , ud_ModulePathLen_Max        := sizeof(ModulePath)
                                        , ud_ModulePathCompLen_Max    := sizeof(ModulePathComp)
                                        , us_SDOMaxDataSize           := us_DataLength_Max
                                        );
            
            //no response, error
            if ( sd_retval <> 0 ) then
              LogError("@020F (SafetyBaseFSoE::ReadSDOConfig) Reading path of safe DINT input failed.");
              return;
            end_if;
            
            // if there is a valid module path
            if Module.p_us_Path <> NIL then
              //add module to routing tabs
              sd_retval := AddModuleToRoutingTabs ( pModule       := #Module
                                                  , pListHeader   := #ListHeader
                                                  , b_IsOptional  := TempModuleConfig.Info.IsOptional
                                                  );
              
              if (sd_retval <> 0 ) then
                if ( SafetyState <> _ModFromCfgNotFound ) then
                  LogError("@0210 (SafetyBaseFSoE::ReadSDOConfig) Failed to add safe DINT input module to routing tabs.");
                  return;
                end_if;
              end_if;
            end_if;

            if b_IsFSoE then
              // lower 2 Bytes: Index in connection list, upper 2 Bytes: Bit-Offset of the value inside the process data (not the FSoE frame)
              ud_Address += 4;
            else
              //address of path length = last address + 2 * 4 bytes ( RemoteIndex + MaxAge)
              ud_Address += 8;
            end_if;
          end_for;
        end_if;


        // only available at new modules
        if b_CfgTypeV2 then          

//*****************************************************************************
//**   SAFE INTERFACE INPUT VARIABLES                                        **
//*****************************************************************************

          // get number of safe interface input vars
          
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0211 (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe interface input variables failed.");
            return;
          end_if;

          // skip the number
          ud_Address += 4;

          //number of safe inputs
          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          // get offset in application memory
          ud_ActAddress += ud_TmpCnt * 4;

          if ud_TmpCnt then
            
            for i := 0 to (ud_TmpCnt-1) do
              // get length of the uncompressed path
              
              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0212 (SafetyBaseFSoE::ReadSDOConfig) Reading length of uncompressed interface topology path failed.");
                return;
              end_if;

              // skip length of top path
              ud_Address += 4;

              // skip top path
              ud_Address += p_SDOResponseBuffer^.ud_Data;

              // read length of compressed topology path

              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0279 (SafetyBaseFSoE::ReadSDOConfig) Reading length of compressed interface topology path failed.");
                return;
              end_if;

              // skip length of compressed top path and compressed top path
              ud_Address += 4 + p_SDOResponseBuffer^.ud_Data;

              if b_IsFSoE then
                // lower 2 Bytes: Index in connection list, upper 2 Bytes: Bit-Offset of the value inside the process data (not the FSoE frame)
                ud_Address += 4;
              else
                //address of path length = last address + 2 * 4 bytes ( RemoteIndex + MaxAge)
                ud_Address += 8;
              end_if;
            end_for;
          end_if;

(*
 4 Byte Anzahl
 4 Byte Länge vom Pfad, Pfad (vorne Pfad ":" dann CRC für da Interface dann bis auf 4 Byte aufgefüllt)
 4 Byte Länge vom komprimierten Pfad, komprimierter Pfad (vorne Pfad ":" dann CRC für da Interface dann bis auf 4 Byte aufgefüllt)
 4 Byte RemoteIndex
 4 Byte Maximales Alter
*)

        end_if;
        


        // only available at new containers
        if b_CfgTypeV3 then          

//*****************************************************************************
//**   SAFE DINT INTERFACE INPUT VARIABLES                                   **
//*****************************************************************************

          // get number of safe DINT interface input vars
          
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0213 (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe DINT interface input variables failed.");
            return;
          end_if;

          // skip the number
          ud_Address += 4;

          //number of safe inputs
          ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

          // get offset in application memory
          ud_ActAddress += ud_TmpCnt * 8;

          if ud_TmpCnt then
            
            for i := 0 to (ud_TmpCnt-1) do
              // get length of the uncompressed path
              
              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0214 (SafetyBaseFSoE::ReadSDOConfig) Reading length of uncompressed interface topology path failed at safe DINT input variables.");
                return;
              end_if;

              // skip length of top path
              ud_Address += 4;

              // skip top path
              ud_Address += p_SDOResponseBuffer^.ud_Data;

              // read length of compressed topology path

              //send request and receive data
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );

              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0215 (SafetyBaseFSoE::ReadSDOConfig) Reading length of compressed interface topology path failed at safe DINT input variables.");
                return;
              end_if;

              // skip length of compressed top path and compressed top path
              ud_Address += 4 + p_SDOResponseBuffer^.ud_Data;

              if b_IsFSoE then
                // lower 2 Bytes: Index in connection list, upper 2 Bytes: Bit-Offset of the value inside the process data (not the FSoE frame)
                ud_Address += 4;
              else
                //address of path length = last address + 2 * 4 bytes ( RemoteIndex + MaxAge)
                ud_Address += 8;
              end_if;
            end_for;
          end_if;

(*
 4 Byte Anzahl
 4 Byte Länge vom Pfad, Pfad (vorne Pfad ":" dann CRC für da Interface dann bis auf 4 Byte aufgefüllt)
 4 Byte Länge vom komprimierten Pfad, komprimierter Pfad (vorne Pfad ":" dann CRC für da Interface dann bis auf 4 Byte aufgefüllt)
 4 Byte RemoteIndex
 4 Byte Maximales Alter
*)

        end_if;
        
        
//*****************************************************************************
//**   FAST UNSAFE INPUT VARIABLES                                           **
//*****************************************************************************

        // only available at new modules
        if b_CfgTypeV2 then          
          
          if b_IprCodeLengthAvailable = FALSE then
            // get number of fast unsafe intput vars

            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0216 (SafetyBaseFSoE::ReadSDOConfig) Reading number of fast unsafe intput vars failed.");
              return;
            end_if;

            ud_Address += 4;

            // get offset in application memory
            ud_ActAddress += p_SDOResponseBuffer^.ud_Data * 4;
            // if it's our own path, remember the fast unsafe settings
            if (PathOfThisCPU[0]$UINT = (ListHeader.p_us_Path$^UINT)^) & (ToStdLib.MemCmp(#PathOfThisCPU[0], ListHeader.p_us_Path, PathOfThisCPU[0]$UINT + 2) = 0) then       
              if (p_SDOResponseBuffer^.ud_Data <> 0) then
                if ActivateFastUnsafeIOs then
                  b_FastUnsafeInputsActive := TRUE;
                else
                  // error => can't go on like this
                  LogError("@0202 (SafetyBaseFSoE::ReadSDOConfig) Number or size of module accesses increased with new safety project!");
                  LogError("@0203 (SafetyBaseFSoE::ReadSDOConfig) => Can't continue, because the new data is not covered by the actual accesses! Restart Application!");

                  b_NeedApplicationRestart := TRUE;
                end_if;
              end_if;
            end_if;          
          else  //b_IprCodeLengthAvailable = FALSE

//*****************************************************************************
//**   Fast UNSAFE BOOL INPUT VARIABLES     SCP211                           **
//*****************************************************************************              

            // here we go for unsafe bool input vars
            
            // get number of unsafe input vars
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0217 (SafetyBaseFSoE::ReadSDOConfig) Failed to read number of unsafe input vars.");
              return;
            end_if;

            ud_CntFastUnsafeInputs:= p_SDOResponseBuffer^.ud_Data;
            
            ud_Address += 4;
                        
            // skip the names of unsafe inputs
            for i := 1 to ud_CntFastUnsafeInputs do
            
              // get length of Name
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );
              //no response
              if ( sd_retval <> 0 ) then
                LogError("@0218 (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe input name failed.");
                return;
              end_if;
              
              // prepare address to get name
              ud_Address += 4;
              
    //          ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_ActStrLen := ud_TmpStrLen;
              ud_TmpAddress := ud_Address;        
              
              IF p_BlockedTransferMemory = NIL THEN
                sd_retval := -100;
                SetSafetyState(NewState:=_MemAllocFailed);
                RETURN;
              END_IF;
              
              IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
                p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
                IF p_BlockedTransferMemory = NIL THEN
                  ud_BlockedTransferMemorySize := 0;
                  sd_retval := -100;
                  SetSafetyState(NewState:=_MemAllocFailed);
                  RETURN;
                ELSE
                  ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
                END_IF;
              END_IF;

              ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

              p_TmpBlockedData := p_BlockedTransferMemory;
                      
              us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              while us_BlockedTransferLength do
              
                // get name
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_TmpAddress
                                            , us_Length           := us_BlockedTransferLength
                                            , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@0219 (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe input name failed.");
                  return;
                end_if;

                ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                                 source:=#p_SDOResponseBuffer^.ud_Data, 
                                 size:=us_BlockedTransferLength);
                
                p_TmpBlockedData += us_BlockedTransferLength;
                ud_TmpAddress += us_BlockedTransferLength;
                ud_TmpStrLen -= us_BlockedTransferLength;
                
                us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              end_while;
              

              sd_retval := AddFastUnsafeRoutElement(pRoutTable:=#p_FastUnsafeInputsTable, 
                                                    pRoutElementsNo:=#ui_CntFastUnsafeInputs,
                                                    ud_Address:=ud_FastUnsafe_Offset_In,
                                                    ui_NameLen:=ud_ActStrLen$UINT,
                                                    pNameNewElement:=p_BlockedTransferMemory$^CHAR,
                                                    bWriteVar:=FALSE);
              
              // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application
              // get offset in application memory
              ud_ActAddress += 4;
              ud_FastUnsafe_Offset_In += sizeof(DINT);

              // prepare address for next unsafe input
              ud_Address += ud_ActStrLen;

            end_for;
            
//*****************************************************************************
//**  FAST UNSAFE BDINT INPUT VARIABLES       SCP211                         **
//*****************************************************************************

            // here we go for unsafe BDINT input vars
            
            // get number of unsafe input vars
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@021A (SafetyBaseFSoE::ReadSDOConfig) Failed to read number of unsafe BDINT input vars.");
              return;
            end_if;


            ud_CntFastUnsafeInputs := p_SDOResponseBuffer^.ud_Data;
            
            ud_Address += 4;
                          
            if ud_CntFastUnsafeInputs then
                            
              //SDO request for this command has 5 bytes additional data
              pBitMasks$^void := ToStdLib.Malloc(size := ud_CntFastUnsafeInputs * sizeof(BDINT));
              
              if ( pBitMasks = NIL ) then
                sd_retval                         := -100;
                SetSafetyState(NewState:=_MemAllocFailed);
                LogError("@021B (SafetyBaseFSoE::ReadSDOConfig) Failed to allocate memory for the BDINT variable bitmasks.");
                return;
              end_if;
              
              // get the bitmasks for the BDINT variables
              for i := 1 to ud_CntFastUnsafeInputs do
              
                // get bitmask
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("021C (SafetyBaseFSoE::ReadSDOConfig) Reading bit mask of unsafe BDINT input failed.");
                  return;
                end_if;

                // prepare address for the next one
                ud_Address += 4;
                
                // save the bitmask for adding the rout element
                (pBitMasks + (i-1) * sizeof(BDINT))^ := p_SDOResponseBuffer^.ud_Data;
              end_for;
              
              // get the names of unsafe inputs
              for i := 1 to ud_CntFastUnsafeInputs do
              
                // get length of Name
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@021D (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe BDINT input name failed.");
                  return;
                end_if;

                // prepare address to get name
                ud_Address += 4;
                
  //              ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
                ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
                ud_ActStrLen := ud_TmpStrLen;
                ud_TmpAddress := ud_Address;        
                
                IF p_BlockedTransferMemory = NIL THEN
                  sd_retval := -100;
                  SetSafetyState(NewState:=_MemAllocFailed);
                  RETURN;
                END_IF;
                
                IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
                  p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
                  IF p_BlockedTransferMemory = NIL THEN
                    ud_BlockedTransferMemorySize := 0;
                    sd_retval := -100;
                    SetSafetyState(NewState:=_MemAllocFailed);
                    RETURN;
                  ELSE
                    ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
                  END_IF;
                END_IF;

                ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

                p_TmpBlockedData := p_BlockedTransferMemory;
                        
                us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
                
                while us_BlockedTransferLength do
                  
                  // get name
                  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                                              , ud_Address          := ud_TmpAddress
                                              , us_Length           := us_BlockedTransferLength
                                              , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                              );
                  //no response
                  if ( sd_retval <> 0 ) then
                    LogError("@021E (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe BDINT input name failed.");
                    return;
                  end_if;

                  ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                                   source:=#p_SDOResponseBuffer^.ud_Data, 
                                   size:=us_BlockedTransferLength);
                  
                  p_TmpBlockedData += us_BlockedTransferLength;
                  ud_TmpAddress += us_BlockedTransferLength;
                  ud_TmpStrLen -= us_BlockedTransferLength;
                  
                  us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
                
                end_while;
                
                ActBitMask := (pBitMasks + (i-1) * sizeof(BDINT))^;

                sd_retval := AddFastUnsafeRoutElement(pRoutTable:=#p_FastUnsafeInputsTable,
                                                      pRoutElementsNo:=#ui_CntFastUnsafeInputs,
                                                      ud_Address:=ud_FastUnsafe_Offset_In,
                                                      ui_NameLen:=ud_ActStrLen$UINT,
                                                      pNameNewElement:=p_BlockedTransferMemory$^CHAR,
                                                      BitMask:=ActBitMask,
                                                      bWriteVar:= FALSE);

                // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application

                // get offset in application memory
                ud_ActAddress += CountBits(ActBitMask) * sizeof(DINT);
                ud_FastUnsafe_Offset_In += sizeof(DINT);

                // prepare address for next unsafe output or for number of constants
                ud_Address += ud_ActStrLen;

              end_for;
              
              // free the bitmasks
              ToStdLib.Free(mptr:=pBitMasks);

            end_if;            
        end_if; 
      end_if; //b_CfgTypeV2
        
//*****************************************************************************
//**   UNSAFE BOOL INPUT VARIABLES                                           **
//*****************************************************************************

        // here we go for unsafe bool input vars
        
        // get number of unsafe input vars
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := ud_Address
                                    , us_Length           := 4
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@0217 (SafetyBaseFSoE::ReadSDOConfig) Failed to read number of unsafe input vars.");
          return;
        end_if;


        ud_CntUnsafeInputs := p_SDOResponseBuffer^.ud_Data;
        
        ud_Address += 4;

        // skip the names of unsafe inputs
        for i := 1 to ud_CntUnsafeInputs do
        
          // get length of Name
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );
          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0218 (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe input name failed.");
            return;
          end_if;

          // prepare address to get name
          ud_Address += 4;
          
//          ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
          ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
          ud_ActStrLen := ud_TmpStrLen;
          ud_TmpAddress := ud_Address;        
          
          IF p_BlockedTransferMemory = NIL THEN
            sd_retval := -100;
            SetSafetyState(NewState:=_MemAllocFailed);
            RETURN;
          END_IF;
          
          IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
            p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
            IF p_BlockedTransferMemory = NIL THEN
              ud_BlockedTransferMemorySize := 0;
              sd_retval := -100;
              SetSafetyState(NewState:=_MemAllocFailed);
              RETURN;
            ELSE
              ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
            END_IF;
          END_IF;

          ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

          p_TmpBlockedData := p_BlockedTransferMemory;
                  
          us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
          
          while us_BlockedTransferLength do
          
            // get name
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_TmpAddress
                                        , us_Length           := us_BlockedTransferLength
                                        , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                        );
            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0219 (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe input name failed.");
              return;
            end_if;

            ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                             source:=#p_SDOResponseBuffer^.ud_Data, 
                             size:=us_BlockedTransferLength);
            
            p_TmpBlockedData += us_BlockedTransferLength;
            ud_TmpAddress += us_BlockedTransferLength;
            ud_TmpStrLen -= us_BlockedTransferLength;
            
            us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
          
          end_while;
          
          sd_retval := AddUnsafeRoutElement(pRoutTable:=#p_UnsafeInputsTable, 
                                            pRoutElementsNo:=#ui_CntUnsafeInputs, 
                                            ud_Address:=ud_ActAddress,
                                            ui_NameLen:=ud_ActStrLen$UINT, 
                                            pNameNewElement:=p_BlockedTransferMemory$^CHAR);

          // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application

          // get offset in application memory
          ud_ActAddress += 4;

          // prepare address for next unsafe input
          ud_Address += ud_ActStrLen;

        end_for;


        // only available at new modules
        if b_CfgTypeV2 then          

//*****************************************************************************
//**   UNSAFE BDINT INPUT VARIABLES                                          **
//*****************************************************************************

          // here we go for unsafe BDINT input vars
          
          // get number of unsafe input vars
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@021A (SafetyBaseFSoE::ReadSDOConfig) Failed to read number of unsafe BDINT input vars.");
            return;
          end_if;


          ud_CntUnsafeInputs := p_SDOResponseBuffer^.ud_Data;
          
          ud_Address += 4;

          if ud_CntUnsafeInputs then
            
            //SDO request for this command has 5 bytes additional data
            pBitMasks$^void := ToStdLib.Malloc(size := ud_CntUnsafeInputs * sizeof(BDINT));
            
            if ( pBitMasks = NIL ) then
              sd_retval                         := -100;
              SetSafetyState(NewState:=_MemAllocFailed);
              LogError("@021B (SafetyBaseFSoE::ReadSDOConfig) Failed to allocate memory for the BDINT variable bitmasks.");
              return;
            end_if;
            

            // get the bitmasks for the BDINT variables
            for i := 1 to ud_CntUnsafeInputs do
            
              // get bitmask
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );
              //no response
              if ( sd_retval <> 0 ) then
                LogError("021C (SafetyBaseFSoE::ReadSDOConfig) Reading bit mask of unsafe BDINT input failed.");
                return;
              end_if;

              // prepare address for the next one
              ud_Address += 4;
              
              // save the bitmask for adding the rout element
              (pBitMasks + (i-1) * sizeof(BDINT))^ := p_SDOResponseBuffer^.ud_Data;
            end_for;
            
            // get the names of unsafe inputs
            for i := 1 to ud_CntUnsafeInputs do
            
              // get length of Name
              sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                          , p_SDOResponseBuffer := p_SDOResponseBuffer
                                          , ud_Address          := ud_Address
                                          , us_Length           := 4
                                          );
              //no response
              if ( sd_retval <> 0 ) then
                LogError("@021D (SafetyBaseFSoE::ReadSDOConfig) Reading length of unsafe BDINT input name failed.");
                return;
              end_if;

              // prepare address to get name
              ud_Address += 4;
              
//              ud_ActStrLen := ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_TmpStrLen := p_SDOResponseBuffer^.ud_Data;
              ud_ActStrLen := ud_TmpStrLen;
              ud_TmpAddress := ud_Address;        
              
              IF p_BlockedTransferMemory = NIL THEN
                sd_retval := -100;
                SetSafetyState(NewState:=_MemAllocFailed);
                RETURN;
              END_IF;
              
              IF ud_BlockedTransferMemorySize < (ud_TmpStrLen + 1) THEN
                p_BlockedTransferMemory := ToStdLib.ReAlloc(mptr:=p_BlockedTransferMemory, newsize:=GetNewMemSize(ud_TmpStrLen + 1));
                IF p_BlockedTransferMemory = NIL THEN
                  ud_BlockedTransferMemorySize := 0;
                  sd_retval := -100;
                  SetSafetyState(NewState:=_MemAllocFailed);
                  RETURN;
                ELSE
                  ud_BlockedTransferMemorySize := GetNewMemSize(ud_TmpStrLen + 1);
                END_IF;
              END_IF;

              ToStdLib.MemSet(dest:=p_BlockedTransferMemory, value:=0, size:=ud_BlockedTransferMemorySize);

              p_TmpBlockedData := p_BlockedTransferMemory;
                      
              us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              while us_BlockedTransferLength do
                
                // get name
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_TmpAddress
                                            , us_Length           := us_BlockedTransferLength
                                            , b_UseCache          := us_BlockedTransferLength < us_DataLength_Max
                                            );
                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@021E (SafetyBaseFSoE::ReadSDOConfig) Reading unsafe BDINT input name failed.");
                  return;
                end_if;

                ToStdLib.MemCpy(dest:=p_TmpBlockedData, 
                                 source:=#p_SDOResponseBuffer^.ud_Data, 
                                 size:=us_BlockedTransferLength);
                
                p_TmpBlockedData += us_BlockedTransferLength;
                ud_TmpAddress += us_BlockedTransferLength;
                ud_TmpStrLen -= us_BlockedTransferLength;
                
                us_BlockedTransferLength := BlockedTransferLength(ud_Datalength:=ud_TmpStrLen, us_MaxBlocklength:=60);
              
              end_while;
              
              ActBitMask := (pBitMasks + (i-1) * sizeof(BDINT))^;
              
              sd_retval := AddUnsafeRoutElement(pRoutTable:=#p_UnsafeInputsTable, 
                                                pRoutElementsNo:=#ui_CntUnsafeInputs, 
                                                ud_Address:=ud_UnsafeBDINT_Offset,
                                                ui_NameLen:=ud_ActStrLen$UINT, 
                                                pNameNewElement:=p_BlockedTransferMemory$^CHAR,
                                                ActBitMask);

              // if unsafe component not found (sd_retval <> 0), it's not problem for whole safe application

              // get offset in application memory
              ud_ActAddress += CountBits(ActBitMask) * sizeof(DINT);
              ud_UnsafeBDINT_Offset += sizeof(DINT);

              // prepare address for next unsafe output or for number of constants
              ud_Address += ud_ActStrLen;

            end_for;
            
            // free the bitmasks
            ToStdLib.Free(mptr:=pBitMasks);
          end_if;


//*****************************************************************************
//**   CONSTANTS                                                             **
//*****************************************************************************

          // constants

          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@021F (SafetyBaseFSoE::ReadSDOConfig) Reading number of constants failed.");
            return;
          end_if;

          // skip number of constants
          ud_Address += 4;

          // skip constants
          ud_Address += p_SDOResponseBuffer^.ud_Data * 4;

          
          if b_CfgTypeV3 then
          
//*****************************************************************************
//**   SAFE DINT CONSTANTS                                                   **
//*****************************************************************************

            // constants

            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0220 (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe DINT constants failed.");
              return;
            end_if;

            // skip number of safe dint constants
            ud_Address += 4;

            // skip constants (4 byte per safe DINT constant)
            ud_Address += p_SDOResponseBuffer^.ud_Data * 4;

          end_if;

//*****************************************************************************
//**   LOCAL OUTPUTS                                                         **
//*****************************************************************************

          // local outputs
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0221 (SafetyBaseFSoE::ReadSDOConfig) Reading number of local outputs failed.");
            return;
          end_if;

          // skip number of local outputs
          ud_Address += 4;

          // skip local outputs
          //++pr
          // only available at new modules
          if b_CfgTypeV2 then          
            ud_Address += p_SDOResponseBuffer^.ud_Data * 8; // Used-Flag, Modulkennung(SDIAS)
          else
            ud_Address += p_SDOResponseBuffer^.ud_Data * 4; // Used-Flag
          end_if;
          
          
          if b_CfgTypeV3 then
          
//*****************************************************************************
//**   LOCAL DINT OUTPUTS                                                    **
//*****************************************************************************

            // local outputs
            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0222 (SafetyBaseFSoE::ReadSDOConfig) Reading number of local outputs failed.");
              return;
            end_if;

            // skip number of local outputs
            ud_Address += 4;

            // skip local outputs
            //++pr
            // only available at new modules
            if b_CfgTypeV2 then          
              ud_Address += p_SDOResponseBuffer^.ud_Data * 8; // Used-Flag, Modulkennung(SDIAS)
            else
              ud_Address += p_SDOResponseBuffer^.ud_Data * 4; // Used-Flag
            end_if;
            
          end_if;


//*****************************************************************************
//**   SAFE BOOL INTERFACE OUTPUT VARIABLES                                  **
//*****************************************************************************

          // safe bool interface output vars
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0223 (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe BOOL interface output variables failed.");
            return;
          end_if;

          // skip safe BOOL interface output variables
          ud_Address += 4;

          // count the interface output variables
          BoolOuts := p_SDOResponseBuffer^.ud_Data;

          if b_IsFSoE then
            ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

            if ud_TmpCnt then
              for i := 1 to ud_TmpCnt do              
                //send request and receive data
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );

                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@027A (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for BOOL interface outputs failed.");
                  return;
                end_if;

                // skip number of entries and the entries
                ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
              end_for;
            end_if;
          end_if;

//*****************************************************************************
//**   SAFE DINT INTERFACE OUTPUT VARIABLES                                  **
//*****************************************************************************

          // safe dint interface output vars
          //send request and receive data
          sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                      , p_SDOResponseBuffer := p_SDOResponseBuffer
                                      , ud_Address          := ud_Address
                                      , us_Length           := 4
                                      );

          //no response
          if ( sd_retval <> 0 ) then
            LogError("@0224 (SafetyBaseFSoE::ReadSDOConfig) Reading number of safe DINT interface output variables failed.");
            return;
          end_if;

          // skip number of safe dint interface output vars
          ud_Address += 4;

          // count the interface output variables
          DintOuts := p_SDOResponseBuffer^.ud_Data;

          if b_IsFSoE then
            ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

            if ud_TmpCnt then
              for i := 1 to ud_TmpCnt do              
                //send request and receive data
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );

                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@027B (SafetyBaseFSoE::ReadSDOConfig) Reading number of FSoE connections for DINT interface outputs failed.");
                  return;
                end_if;

                // skip number of entries and the entries
                ud_Address += 4 + 4 * p_SDOResponseBuffer^.ud_Data;
              end_for;
            end_if;
          end_if;


//*****************************************************************************
//**   FSBs = Functional Safety Blocks                                       **
//*****************************************************************************

          ud_skipLength := 0;

          // skip FSBs! structure: CRC, Length.... until the length is 0
          repeat
            
            // skip CRC and length of FSB
            ud_Address += 4 + ud_skipLength;

            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0225 (SafetyBaseFSoE::ReadSDOConfig) Reading length of FSB failed.");
              return;
            end_if;

            ud_skipLength := p_SDOResponseBuffer^.ud_Data;

          until ud_skipLength = 0 end_repeat;
          
          // skip length of block with 0 length
          ud_Address += 4;

          if b_CfgTypeV3 then

//*****************************************************************************
//**   DYNAMIC PARAMETERS (e.g. for SSI Module)                              **
//*****************************************************************************

// Version of Parameters is saved in hd_DynParaVersion
// if it's 16#0001, then we read out the number of parameters which will follow


// structure: 
// number of parameters
// for every parameter: place, id, length (including the 4 byte for the length itself), data

            //send request and receive data
            sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                        , p_SDOResponseBuffer := p_SDOResponseBuffer
                                        , ud_Address          := ud_Address
                                        , us_Length           := 4
                                        );

            //no response
            if ( sd_retval <> 0 ) then
              LogError("@0226 (SafetyBaseFSoE::ReadSDOConfig) Reading number of dynamic parameters failed.");
              return;
            end_if;

            ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

            // skip number of parameters
            ud_Address += 4;

            if ud_TmpCnt then
              for i := 1 to ud_TmpCnt do
                //send request and receive data
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := sizeof(t_DynamicParameterHeader)
                                            );

                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@0227 (SafetyBaseFSoE::ReadSDOConfig) Reading header of dynamic parameter failed.");
                  return;
                end_if;

                DynamicParameterHeader := p_SDOResponseBuffer^.ud_Data$t_DynamicParameterHeader;
                
                AddressServiceDataSize := 0;
                
                // skip size of header
                ud_Address += sizeof(t_DynamicParameterHeader);

                // data for pdo length info:
                // this data is also mirrored to SBF_SDO_ADDRESS_SPECIAL_INFO
                
//                  Marked with Place = 1000
//
//                  (4 Byte Length)
//                   4 Byte Version (starting at 0)
//                   1 Byte Length Rd PDO
//                   1 Byte Length Rd PDO IF
//                   1 Byte Length Wr PDO
//                   1 Byte Number of write PDOs (without additional it would be 1), msb = fast unsafe active
                
                if (DynamicParameterHeader.Place = SBF_CONFIG_PDO_INFO_PLACE) & (DynamicParameterHeader.Length >= (sizeof(UDINT) + sizeof(t_PDOSizeInfo))) then
                  //send request and receive data
                  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                                              , ud_Address          := ud_Address
                                              , us_Length           := sizeof(t_PDOSizeInfo)
                                              );

                  //no response
                  if ( sd_retval <> 0 ) then
                    LogError("@0228 (SafetyBaseFSoE::ReadSDOConfig) Reading dynamic parameter for pdo size failed.");
                    return;
                  end_if;
                  
                  ud_Address += sizeof(t_PDOSizeInfo);
                  
                  AddressServiceDataSize := (ud_ListLength_Address + ud_ListLength) - ud_Address;
                  
                  PdoSizeInfo := p_SDOResponseBuffer^.ud_Data$t_PDOSizeInfo;
                  
                  //check version
                  if PdoSizeInfo.Version = 1 then
                    // if any sizes increased or we would need more DOs
                    if (PdoSizeInfo.PDORdLen > MyPDOReadSize)     |
                       (PdoSizeInfo.PDOWrLen > MyPDOWriteSize)    |
                       ((PdoSizeInfo.FastUnsafeAvailable <> 0) > ActivateFastUnsafeIOs) then
                    
                      // error => can't go on like this
                      LogError("@0202 (SafetyBaseFSoE::ReadSDOConfig) Number or size of module accesses increased with new safety project!");
                      LogError("@0203 (SafetyBaseFSoE::ReadSDOConfig) => Can't continue, because the new data is not covered by the actual accesses! Restart Application!");

                      b_NeedApplicationRestart := TRUE;
                    end_if;                  
                  else
                    // new interpretercode version 
                    PdoSizeInfoV2 := p_SDOResponseBuffer^.ud_Data$t_PDOSizeInfoV2;
                    
                    if  (PdoSizeInfoV2.PDORdLen > MyPDOReadSize)                       |
                        (PdoSizeInfoV2.PDOWrLen > MyPDOWriteSize)                      |
                        (PdoSizeInfoV2.uiNumberFastUnsafeRdVar > uiNumberFastUnsafeRd) |
                        (PdoSizeInfoV2.uiNumberFastUnsafeWrVar > uiNumberFastUnsafeWr) then
                    
                      // error => can't go on like this
                      LogError("@0202 (SafetyBaseFSoE::ReadSDOConfig) Number or size of module accesses increased with new safety project!");
                      LogError("@0203 (SafetyBaseFSoE::ReadSDOConfig) => Can't continue, because the new data is not covered by the actual accesses! Restart Application!");

                      b_NeedApplicationRestart := TRUE;                    
                    end_if;
                  end_if;

                  exit;
                end_if;
                
                // skip the parameter data (length of the length is also included)
                ud_Address += DynamicParameterHeader.Length - 4;
              end_for;
              
              if AddressServiceDataSize then
(*

  Data structure of address service data

  4 Byte Anzahl der MultiChannel
    Pro MultiChannel:
    4 Byte Anzahl der Verwendungen des Multichannel
    4 Byte Index des Gesamtergebnisses
    1 Byte Art der Verknüpfung (0 = UND)
    1 Byte Defaultbelegung des Werts
    2 Byte Reserved (für 4 Byte Alignment)
      Pro Verwendung (siehe erster Eintrag in den MultiChannelDaten):
      4 Byte FSoE Daten
  4 Byte Kommunikations-ID (Default 0)
  4 Byte Anzahl der Adressbereiche
    Pro Adressbereich:
    4 Byte Startadresse
    4 Byte Länge Adressbereich
  4 Byte Anzahl der Connection-ID-Bereiche
    Pro Bereich:
    4 Byte Startadresse
    4 Byte Länge Bereich         

*)
                // get the number of multichannels 
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );

                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@027C (SafetyBaseFSoE::ReadSDOConfig) Reading address service data failed.");
                  return;
                end_if;
                
                ud_TmpCnt := p_SDOResponseBuffer^.ud_Data;

                // skip number of multichannels
                ud_Address += 4;

                if ud_TmpCnt then
                  for i := 1 to ud_TmpCnt do
                    // get the number of usages 
                    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                                , ud_Address          := ud_Address
                                                , us_Length           := 4
                                                );

                    //no response
                    if ( sd_retval <> 0 ) then
                      LogError("@027C (SafetyBaseFSoE::ReadSDOConfig) Reading address service data failed.");
                      return;
                    end_if;
                    
                    // skip the rest of this multichannel
                    ud_Address += 12 + (p_SDOResponseBuffer^.ud_Data * 4);
                  end_for;
                end_if;
                
                // skip the communication ID
                ud_Address += 4;
                
                // read the number of address areas
                sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                            , p_SDOResponseBuffer := p_SDOResponseBuffer
                                            , ud_Address          := ud_Address
                                            , us_Length           := 4
                                            );

                //no response
                if ( sd_retval <> 0 ) then
                  LogError("@027C (SafetyBaseFSoE::ReadSDOConfig) Reading address service data failed.");
                  return;
                end_if;
                
                // if there are any => the module got an address service
                if p_SDOResponseBuffer^.ud_Data then
                  TempModuleConfig.Info.IsAddressService := TRUE;
                end_if;
              end_if;
            end_if;
          end_if;

          // if there are interface variables
          if BoolOuts | DintOuts then
            // calculate the length of the interface frame
            TempPdoConfig.PdoLen := CalcPDOLength(BoolOuts, DintOuts);

            TempPdoConfig.Info := 0;
            TempPdoConfig.Info.IsInterfacePDO := TRUE;
            TempPdoConfig.Info.IsOptional := (ud_ActBlockType = SBF_BLKTYPE_CFG_MODULE_OPTIONAL);
            
            // if it's a broadcast => mark it
            if (TempPath[0]$UINT = 2) & (TempPath[0]$HDINT = SBF_INTERFACE_FRAME_BROADCAST) then
              TempPdoConfig.Info.IFBroadCast := TRUE;
            end_if;
            
            // this is the source of the interface frame
            TempPdoConfig.SourcePathLen := ListHeader.p_us_Path^$UINT + 2;
            TempPdoConfig.pSourcePath   := ListHeader.p_us_Path;

            // and the destination
            TempPdoConfig.DestPathLen   := TempPath[0]$UINT + 2;
            TempPdoConfig.pDestPath     := #TempPath[0];

            // add the interface PDO to the list
//            AddPdoToCfg(TempPdoConfig);
            
            TempModuleConfig.Info.IsInterfaceSource := TRUE;
            TempModuleConfig.Info.IsInterfaceDest   := FALSE;
            TempModuleConfig.InterfaceCRC := TempPdoConfig.InterfaceCRC;
          else
            TempModuleConfig.Info.IsInterfaceSource := FALSE;
            TempModuleConfig.Info.IsInterfaceDest   := FALSE;
            TempModuleConfig.InterfaceCRC := 0;
          end_if;
        else
          TempModuleConfig.Info.IsInterfaceSource := FALSE;
          TempModuleConfig.Info.IsInterfaceDest   := FALSE;
          TempModuleConfig.InterfaceCRC := 0;
        end_if;

        // if it's our own path, remember the safety number
        if (PathOfThisCPU[0]$UINT = (ListHeader.p_us_Path$^UINT)^) & (ToStdLib.MemCmp(#PathOfThisCPU[0], ListHeader.p_us_Path, PathOfThisCPU[0]$UINT + 2) = 0) then       
          TempModuleConfig.SafetyNbr := SafetyNumber;
          //safe config of scp modul (own path)
          SafeActModuleConfig(TempModuleConfig);        
        else
          TempModuleConfig.SafetyNbr := 0;
        end_if;

        // only when there is a manager
        if pSafetyManagerThis then
          AddModuleToCfg(TempModuleConfig);
        end_if;

//**********************************************************************************************************************************************************
      SBF_BLKTYPE_OLD_MODE_FLAG: // flag fore FW to use old config-mode
        // we need the old TimeSync config-type
        NewConfigType := ConfigState_Old;

//**********************************************************************************************************************************************************
      SBF_BLKTYPE_CFG_XML: // configuration for uploading the project to the safetydesigner (network placement information, names of bits,...)
        // no useful information here for us
        
//**********************************************************************************************************************************************************
    else
      // skip it = do nothing
//      TRACE("Found unhandled Blocktype in configuration.");
//**********************************************************************************************************************************************************
    end_case;

    ud_ListBaseAddress := ud_ListLength_Address + ud_ListLength;
  end_while;
  
  if ( ( sd_retval = 0 ) & ( SafetyState = _ModFromCfgNotFound ) ) then
    sd_retval := -1;
  end_if;
    
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::SDOReadCfgRequ
  VAR_INPUT
    p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
    p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
    ud_Address 	: UDINT;
    us_Length 	: USINT;
    b_UseCache 	: BOOL;(* := TRUE *)
  END_VAR
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR

  // check if we have the data in the cache
  if b_UseCache & IsInCache(ud_Address, us_Length) then
    // use it from the cache

    // the address may be used from outside this function
    p_SDORequestBuffer^.ud_Address := ud_Address;
    
    _memcpy(ptr1:=#p_SDOResponseBuffer^.ud_Data, ptr2:=#Cache[0] + (ud_Address - CachedAddr), cntr:=us_Length);
    
    // everything is fine
    sd_retval := 0;
  else

    //sequence number
    p_SDORequestBuffer^.StdHeader.SeqNr := us_CntSequNr;
    
    //increase counter for sequence number
    if us_CntSequNr < 16#7F then
      us_CntSequNr += 1;
    else
      us_CntSequNr := 0;
    end_if;
    
    //address 
    p_SDORequestBuffer^.ud_Address := ud_Address;
    
    if b_UseCache then
      // get max size
      p_SDORequestBuffer^.us_Length := SBF_SDO_MAX_LENGTH - (sizeof(SDORespReadConfig) - sizeof(SDORespReadConfig.ud_Data));
    else
      //length of expected data
      p_SDORequestBuffer^.us_Length := us_Length;   
    end_if;
    
    //calculate CRC (starting with length of data until the end of command)
    p_SDORequestBuffer^.StdHeader.CRC := 0;

    //send request
    sd_retval := SAFETY_iSendRequest( uiMaster    := 0
                                    , pucNode     := p_ObjectPath
                                    , uiInLength  := sizeof(SDORequReadConfig)
                                    , pucInput    := p_SDORequestBuffer$^USINT
                                    , uiOutLength := sizeof(SDORespReadConfig) - sizeof(SDORespReadConfig.ud_Data) + p_SDORequestBuffer^.us_Length
                                    , pucOutput   := p_SDOResponseBuffer$^USINT
                                    );
   
    //no response
    if ( sd_retval <> 0 ) then
      LogError("@0229 (SafetyBaseFSoE::SDOReadCfgRequ) SAFETY_iSendRequest failed.");
      
      // we remember to retry in a few seconds
      b_ReadConfigFailed := TRUE;
      return;
    end_if;

    //check sequence number to exclude a possible wrong response
    if ( p_SDORequestBuffer^.StdHeader.SeqNr <> p_SDOResponseBuffer^.StdHeader.SeqNr ) then
      // we remember to retry in a few seconds
      b_ReadConfigFailed := TRUE;
      //log not routable SDOs
      LogValue("@022A (SafetyBaseFSoE::SDOReadCfgRequ) Invalid Sequence number in SDO Standard Response. Got 0x{0} instead of expected 0x{1}", to_udint(p_SDOResponseBuffer^.StdHeader.SeqNr), to_udint(p_SDORequestBuffer^.StdHeader.SeqNr));
      return;
    end_if;
      
    //check returncode of SDO
    if ( p_SDOResponseBuffer^.us_Returncode <> SBF_SDO_RETCODE_OK ) then
      //log not routable SDOs
      LogValue("@022B (SafetyBaseFSoE::SDOReadCfgRequ) Safety module 0x{0}: Retcode of SDO Standard Response was invalid. Retcode: 0x{1}", SafetyNumber$UDINT, to_udint(p_SDOResponseBuffer^.us_Returncode));
      
  //    // when reading fails because safety designer is writing at the same moment
  //    if p_SDOResponseBuffer^.us_Returncode = SDO_MICROCTRL_DIFFER then
        // we remember to retry in a few seconds
        b_ReadConfigFailed := TRUE;
  //    end_if;
      
      return;
    end_if;
      
    //check length of read data
    if ( p_SDOResponseBuffer^.us_Length = 0 ) then
      LogError("@022C (SafetyBaseFSoE::SDOReadCfgRequ) Response length is 0.");
      sd_retval := -104;
      return;
    elsif b_UseCache then
      // everything is fine => add the new data to the cache
      AddToCache(p_SDORequestBuffer, p_SDOResponseBuffer);
    end_if;  
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetRoutingData
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR
  VAR
    SDORequestBuffer        : SDORequReadConfig;
    SDOResponseBuffer       : ARRAY [0..SBF_SDO_MAX_LENGTH-1] OF USINT;
    p_SDORequestBuffer 	    : ^SDORequReadConfig;
    p_SDOResponseBuffer 	  : ^SDORespReadConfig;
    NotAvailableStr         : ARRAY [0..99] OF CHAR;
    Time1, Time2            : UDINT;
  END_VAR

  // get module specific configuration information
  GetConfiguration();

  //SDO request for this command has 5 bytes additional to the header
  p_SDORequestBuffer := #SDORequestBuffer;
  
  //SDO response for this command can have maximum additional data
  p_SDOResponseBuffer := (#SDOResponseBuffer)$^SDORespReadConfig;
    
  repeat
    // if not used => continue
    // if valid    => set to not used and continue
    // if wait4rt  => wait in the loop until the state changes to valid
  until (sigclib_atomic_cmpxchgU32(pValue:=#udConfigValid, cmpVal:=SBF_LOCKCFG_VALID, newVal:=SBF_LOCKCFG_NOT_USED) < SBF_LOCKCFG_WAIT4RT) end_repeat;
  
  b_ReadConfigFailed := FALSE;

  b_NeedApplicationRestart := FALSE;
  
  // if there is a revision in the configuration this variable changes to TRUE
  b_GotNewRevision := FALSE;

Time1 := OS_READMICROSEC();
  //read config data via SDOs and add safety modules to routing lists
  sd_retval := ReadSDOConfig(p_SDORequestBuffer, p_SDOResponseBuffer);
Time2 := OS_READMICROSEC();
ud_ConfigReadTime := Time2 - Time1;

  // set error to restart application afterwards to have a complete configuration (needed for routing to share the slave configurations with only 1 restart of the application)
  if b_NeedApplicationRestart then
    SetSafetyState(NewState:=_DOsIncreasedRestartApp);
    b_GetNewConfiguration := FALSE; // don't try again (unless the CRC changes again)
    sd_retval := -1;
  end_if;

  // if there is no revision in the configuration => clear it
  if (b_GotNewRevision = FALSE) then

    // free memory, if available  
    if pPrjNameStr & pPrjRevStr then
      ToStdLib.Free(pPrjNameStr);
    end_if;

    // use local memory to show "not available"
    _strcpy(dest:=#NotAvailableStr[0], src:="<not available>");
    PrjNameMemLen := _strlen(src:=#NotAvailableStr[0]);
    PrjNameStrLen := PrjRevStrLen := PrjNameMemLen - 1;
    pPrjNameStr := pPrjRevStr := #NotAvailableStr[0];

    // write the strings to the string objects
    ShowProjectRevision();

    // there is no memory, so reset the pointer and length informations
    pPrjNameStr := NIL;
    pPrjRevStr := NIL;

    PrjNameMemLen := 0;
    PrjNameStrLen := 0;
    PrjRevStrLen := 0;
  end_if;
  
  // if reading failed, because designer is writing at the same time => we try again in a few seconds
  if b_ReadConfigFailed then
   
    // next time we check for the configured state of the cpu
    b_CheckConfigured := TRUE;
    b_GetNewConfiguration := FALSE; // don't try again while the config is invalid

    // check again in background
    SetSafetyState(NewState:=_ReinitConfig);
    
    // and wait at least 3 secs
    ud_LastCheckConfig := ops.tAbsolute;
  
  // error in configuration -> no work
  elsif ( sd_retval <> 0 ) & (SafetyState <> _ReinitConfig) then  
    if SafetyState = _WaitForSynchronicity then
      SetSafetyState(NewState:=_UnknownCfgError);
    end_if;
  
    LogError("@022D (SafetyBaseFSoE::GetRoutingData) Reading Safety Configuration failed!");
  else        
    // read config succeeded, check update cycle of unsafe variables
    CheckUnsafeUpdateCycle();
    
    
    // check for interface frame destination modules and mark them
    GetDestinationCRCs();
    
    // if there is a safety manager create a configuration message and send it to the safety manager
    if pSafetyManagerThis then
      CreateAndSendCfg();
    end_if;
    
    // last thing to do is to start realtime and cyclic work (they do nothing in _ReinitConfig state)
    if ud_UnsafeVarsNotFound then
      SetSafetyState(NewState:=_UnsafeVarNotFound);
      ud_UnsafeVarsNotFound := 0;
    else
      if us_SetBusReadyState = SetBusReadyFinished then
        // we're already synchron from a former configuration phase
        SetSafetyState(NewState:=_SafetyClassOK);
      else
        SetSafetyState(NewState:=_WaitForSynchronicity);
      end_if;
    end_if;
    
    // set config valid
    sigclib_atomic_swpU32(pValue:=#udConfigValid, swpVal:=SBF_LOCKCFG_VALID);
  end_if;

  // free temporary configuration lists if there were any
  FreeCfgLists();
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::ReadModulePath
	VAR_INPUT
		p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
		p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
		p_ud_Address 	: ^UDINT;
		pModule 	: ^SafetyBaseFSoE::PathInfoType;
		ud_ModulePathLen_Max 	: UDINT;
		ud_ModulePathCompLen_Max 	: UDINT;
		us_SDOMaxDataSize 	: USINT;
	END_VAR
	VAR_OUTPUT
		sd_retval 	: DINT;
	END_VAR
  VAR
    ud_ModulePathLen_Act    : UDINT;
    p_ud_ModulePath_Act     : ^UDINT;
  END_VAR

  //read length of module path
      
  //send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := p_ud_Address^
                              , us_Length           := 4
                              );

  //no response
  if ( sd_retval <> 0 ) then
    LogError("@022E (SafetyBaseFSoE::ReadModulePath) Failed to read length of module path.");
    return;
  end_if;
  
  //length of module path
  ud_ModulePathLen_Act := p_SDOResponseBuffer^.ud_Data;
  
  //length is zero, check compressed path
  if ( ud_ModulePathLen_Act > 0 ) then
        
    // if it's too small => error
    if ( ud_ModulePathLen_Act > ud_ModulePathLen_Max ) then
      sd_retval                         := -100;
      SetSafetyState(NewState:=_MemAllocFailed);
      return;          
    end_if;
        
    //read path
        
    //address of first byte of path
    p_ud_Address^ += 4;
        
    //set pointer to first byte of target buffer
    p_ud_ModulePath_Act := ( pModule^.p_us_Path )$^UDINT;
        
    //read path block by block, if path is longer than max. length
    while ( ud_ModulePathLen_Act > us_SDOMaxDataSize ) do
          
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := p_ud_Address^
                                  , us_Length           := us_SDOMaxDataSize
                                  , b_UseCache          := FALSE
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@022F (SafetyBaseFSoE::ReadModulePath) Failed to read block of module path.");
        return;
      end_if;
          
      //copy part of path
      ToStdLib.MemCpy(dest := p_ud_ModulePath_Act , source := #p_SDOResponseBuffer^.ud_Data, size := us_SDOMaxDataSize);
            
      //rest of path to be read
      ud_ModulePathLen_Act -= us_SDOMaxDataSize;
          
      //address of next block
      p_ud_Address^ += us_SDOMaxDataSize;
          
      //set pointer to next free byte of target buffer
      p_ud_ModulePath_Act += us_SDOMaxDataSize;
          
    end_while;

    // if there is something left
    if ud_ModulePathLen_Act then
      //read last bytes of path
        
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := p_ud_Address^
                                  , us_Length           := ud_ModulePathLen_Act$USINT
                                  , b_UseCache          := ud_ModulePathLen_Act$USINT < us_SDOMaxDataSize
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0230 (SafetyBaseFSoE::ReadModulePath) Failed to read last bytes of module path.");
        return;
      end_if;
            
      ToStdLib.MemCpy(dest := p_ud_ModulePath_Act, source := #p_SDOResponseBuffer^.ud_Data, size := ud_ModulePathLen_Act);
    end_if;
    
    //address of next byte after path
    //address of last block + length of last read block
    p_ud_Address^ += ud_ModulePathLen_Act;
  else
    pModule^.p_us_Path := NIL;
    
    //address of compressed path
    p_ud_Address^ += 4;
  end_if;

// ============================================================================================================================

  // compressed module path starts here

  // read length of module path
      
  // send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := p_ud_Address^
                              , us_Length           := 4
                              );

  //no response
  if ( sd_retval <> 0 ) then
    LogError("@0231 (SafetyBaseFSoE::ReadModulePath) Failed to read length of compressed module path.");
    return;
  end_if;
  
  //length of module path
  ud_ModulePathLen_Act := p_SDOResponseBuffer^.ud_Data;
  
  pModule^.ui_PathCompLen := to_uint(ud_ModulePathLen_Act);
  
  //if length is zero, leave method
  if ( ud_ModulePathLen_Act = 0 ) then
    if pModule^.p_us_PathComp <> NIL then
      pModule^.p_us_PathComp := NIL;
    end_if;

    // address of CRC
    p_ud_Address^ += 4;

    return;
  end_if;

  //if it's too small => error
  if ( ud_ModulePathLen_Act > ud_ModulePathCompLen_Max ) then         
    sd_retval                         := -100;
    SetSafetyState(NewState:=_MemAllocFailed);
    return;
  end_if;

      
  //read path
      
  //address of first byte of path
  p_ud_Address^ += 4;

  //set pointer to first byte of target buffer
  p_ud_ModulePath_Act := pModule^.p_us_PathComp$^UDINT;

  //read path block by block, if path is longer than max. length
  while ( ud_ModulePathLen_Act > us_SDOMaxDataSize ) do
        
    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := p_ud_Address^
                                , us_Length           := us_SDOMaxDataSize
                                , b_UseCache          := FALSE
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@0232 (SafetyBaseFSoE::ReadModulePath) Failed to read block of compressed module path.");
      return;
    end_if;
        
    //copy part of path
    ToStdLib.MemCpy(dest := p_ud_ModulePath_Act , source := #p_SDOResponseBuffer^.ud_Data, size := us_SDOMaxDataSize);
          
    //rest of path to be read
    ud_ModulePathLen_Act -= us_SDOMaxDataSize;
        
    //address of next block
    p_ud_Address^ += us_SDOMaxDataSize;
        
    //set pointer to next free byte of target buffer
    p_ud_ModulePath_Act += us_SDOMaxDataSize;
        
  end_while;

  // if there is something left
  if ud_ModulePathLen_Act then

    //read last bytes of path
      
    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := p_ud_Address^
                                , us_Length           := ud_ModulePathLen_Act$USINT
                                , b_UseCache          := ud_ModulePathLen_Act$USINT < us_SDOMaxDataSize
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@0233 (SafetyBaseFSoE::ReadModulePath) Failed to read last bytes of compressed module path.");
      return;
    end_if;
          
    ToStdLib.MemCpy(dest := p_ud_ModulePath_Act, source := #p_SDOResponseBuffer^.ud_Data, size := ud_ModulePathLen_Act);
  end_if;
  
  //address of next byte after path
  //address of last block + length of last read block
  p_ud_Address^ += ud_ModulePathLen_Act;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddModuleToRoutingTabs
  VAR_INPUT
    pModule 	: ^SafetyBaseFSoE::PathInfoType;
    pListHeader 	: ^SafetyBaseFSoE::PathInfoType;
    b_IsOptional 	: BOOL;
  END_VAR
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR
  VAR
    p_TargetObject            : pVirtualBase;
    p_SafetyBase              : pVirtualBase;
    ud_IO_SafeNumber          : HDINT;
    ud_PDO_Buffer             : UDINT;
    MyPara 	                  : CmdStruct;
    MyResult	                : results;
  END_VAR

//#pragma message("RamAnd 29.10.2014 10:26 \ hier kann vermutlich config in/out/cpu entfallen. einfach nur die entsprechenden module merken")

  //get this-pointer of object in network that is located with transfered path
  MyPara.uiCmd := _BUS_IF_GET_OBJECT_THIS_PTR;
  MyPara.aPara[0] := pModule^.p_us_Path$DINT;
  sd_retval$iprStates := ToBusInterface.NewInst(#MyPara, #MyResult);    
  p_TargetObject := MyResult.aData[0]$pVirtualBase;

  //this-pointer found
  if ( p_TargetObject ) then
    
    //add output module
    
    //If found this-pointer equals the own this-pointer, then the module has to be added to output-list.
    //This module (CPU) provides the safety input values for the output module (path in list-header).
    if ( p_TargetObject = this ) then

      //the output module is addressed by the ListHeaderPath     
      //get this-pointer of object in network that is located with transfered path
      MyPara.uiCmd := _BUS_IF_GET_OBJECT_THIS_PTR;
      MyPara.aPara[0] := pListHeader^.p_us_Path$DINT;
      sd_retval$iprStates := ToBusInterface.NewInst(#MyPara, #MyResult);    
      p_TargetObject := MyResult.aData[0]$pVirtualBase;
      
      //this-pointer found
      if ( p_TargetObject ) then

        MyPara.uiCmd := CMD_SM_GET_MODULE_TYPE;
        sd_retval$iprStates := p_TargetObject^.NewInst(#MyPara, #MyResult);    

        //check if it's a safety module with FSoE
        if ( MyResult.aData[0]$UDINT = SM_TYPE_SAFETY_MODULE_FSOE ) then        
      
          //object is based on SafetyBaseFSoE
          p_SafetyBase := ( p_TargetObject )$pVirtualBase;
      
          //get pointer on PDO output buffer
          ud_PDO_Buffer := GetPointer2PDOBuffer();
          
          //configure safety output module and receive safety number
          //inform the output module about safety number and object path of the safe CPU

          //get safety number
          MyPara.uiCmd := CMD_SM_CONFIG_SAFETY_CPU;
          
          sd_retval$iprStates := p_SafetyBase^.NewInst(#MyPara, #MyResult);    

          ud_IO_SafeNumber := MyResult.aData[0]$UDINT;
    
          //output module successfully configured
          if ( ud_IO_SafeNumber <> 0 ) then

            //add module to routing table
            sd_retval := AddRoutingElement( ud_SafeNumber  := ud_IO_SafeNumber
                                          , ud_thisp       := p_SafetyBase$UDINT
                                          , pPathInfo      := pListHeader
                                          );
          else
            sd_retval := 0; // could be an optional module
          end_if;
        else
          LogError("@027D (SafetyBaseFSoE::AddModuleToRoutingTabs) Output module: Trying to add a module to safety routing table which does not support FSoE.");
          LogHWPath("HW-Path of Module: {0}", pListHeader^.p_us_Path);
        end_if;
      else
        LogError("@0235 (SafetyBaseFSoE::AddModuleToRoutingTabs) Output module: No Object of HW-class found for module to be added to safety routing table.");
        LogHWPath("HW-Path of Module: {0}", pListHeader^.p_us_Path);
      end_if;



    //add other cpu module

    elsif pModule = pListHeader then
    
      //the output module is addressed by the ListHeaderPath     
      //get this-pointer of object in network that is located with transfered path
      MyPara.uiCmd := _BUS_IF_GET_OBJECT_THIS_PTR;
      MyPara.aPara[0] := pModule^.p_us_Path$DINT;
      sd_retval$iprStates := ToBusInterface.NewInst(#MyPara, #MyResult);    
      p_TargetObject := MyResult.aData[0]$pVirtualBase;
      
      //this-pointer found
      if ( p_TargetObject ) then
      
        //check if it's a safety module
        MyPara.uiCmd := CMD_SM_GET_MODULE_TYPE;
        sd_retval$iprStates := p_TargetObject^.NewInst(#MyPara, #MyResult);    

        if ( MyResult.aData[0]$UDINT = SM_TYPE_SAFETY_MODULE_FSOE ) then        
      
          //object is based on SafetyBaseFSoE
          p_SafetyBase := p_TargetObject;
          
          //get safety number
          MyPara.uiCmd := CMD_SM_CONFIG_SAFETY_CPU;
          
          p_SafetyBase^.NewInst(#MyPara, #MyResult);    

          ud_IO_SafeNumber := MyResult.aData[0]$UDINT;
    
          //add module to routing table
          sd_retval := AddRoutingElement( ud_SafeNumber  := ud_IO_SafeNumber
                                        , ud_thisp       := p_SafetyBase$UDINT
                                        , pPathInfo      := pListHeader
                                        );

        else
          LogError("@027E (SafetyBaseFSoE::AddModuleToRoutingTabs) CPU module: Trying to add a module to safety routing table which does not support FSoE.");          
          LogHWPath("HW-Path of Module: {0}", pListHeader^.p_us_Path);
        end_if;
      else
        LogError("@0238 (SafetyBaseFSoE::AddModuleToRoutingTabs) CPU module: No Object of HW-class found for module to be added to safety routing table.");
        LogHWPath("HW-Path of Module: {0}", pListHeader^.p_us_Path);
      end_if;
      
    
    //add input module
    
    else
      //It's a safety input module, so it has to be added to safety input list.
      //check if found object represents a safety module
      MyPara.uiCmd := CMD_SM_GET_MODULE_TYPE;
      sd_retval$iprStates := p_TargetObject^.NewInst(#MyPara, #MyResult);    
      if ( MyResult.aData[0]$UDINT = SM_TYPE_SAFETY_MODULE_FSOE ) then
            
        //object is based on SafetyBaseFSoE
        p_SafetyBase := p_TargetObject;

        //get safety number
        MyPara.uiCmd := CMD_SM_CONFIG_SAFETY_CPU;
        
        sd_retval$iprStates := p_SafetyBase^.NewInst(#MyPara, #MyResult);    

        ud_IO_SafeNumber := MyResult.aData[0]$UDINT;

        //input module successfully configured
        if ( ud_IO_SafeNumber <> 0 ) then

          if ( sd_retval = 0 ) then
            
            //add module to routing table
            sd_retval := AddRoutingElement( ud_SafeNumber  := ud_IO_SafeNumber
                                          , ud_thisp       := p_SafetyBase$UDINT
                                          , pPathInfo      := pModule
                                          );
        
          else
            LogError("@0239 (SafetyBaseFSoE::AddModuleToRoutingTabs) Input module: Failed to add module to input buffer.");            
            LogHWPath("HW-Path of Module: {0}", pListHeader^.p_us_Path);
          end_if;
        else
          // module in configuration, which is not physically available but hw-class is placed
          sd_retval := 0; // could be an optional module
        end_if;
      else
        LogError("@027E (SafetyBaseFSoE::AddModuleToRoutingTabs) Input module: Trying to add a module to safety routing table which does not support FSoE.");          
        LogHWPath("HW-Path of Module: {0}", pModule^.p_us_Path);
      end_if;
    end_if;
  else
    // module in configuration, which is not physically available and no hw-class is placed
    LogError("@023B (SafetyBaseFSoE::AddModuleToRoutingTabs) Module in safety configuration, where no HW-Class is placed (maybe optional module).");
    LogHWPath("HW-Path of Module: {0}", pModule^.p_us_Path);
    sd_retval := 0;
  end_if;  

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetRoutingElementByThis
  VAR_INPUT
    ud_thisp 	: UDINT;
  END_VAR
  VAR_OUTPUT
    p_RoutElement 	: ^SafetyBaseFSoE::RoutElement;
  END_VAR
  VAR
    i1                  : UINT;
    p_RoutElement_Act   : ^RoutElement;
  END_VAR

  p_RoutElement := NIL;
  
  //set pointer on first routing element
  p_RoutElement_Act := p_RoutTable;
  
  //check each element
  for i1 := 0 to ui_CntRoutElements - 1 do 
  
    //if searched this is found, pointer on routing element is returned
    if ( p_RoutElement_Act^.ud_Thisp = ud_thisp ) then
      p_RoutElement := p_RoutElement_Act;
      return;
    end_if;
    
    //set pointer to next element
    p_RoutElement_Act += sizeof ( RoutElement );
  
  end_for;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::SDOStdRequ
  VAR_INPUT
    us_Command 	: USINT;
    p_us_RespData 	: ^USINT;
    us_RespLength 	: USINT;
  END_VAR
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR
  VAR
    pSDORequestBuffer   : ^t_SDOStdHeader;
    pSDOResponseBuffer  : ^SDOStdResp;
    RespBuffer          : ARRAY [0..127] OF USINT;
    RequBuffer          : ARRAY [0..127] OF USINT;
    us_OutLength        : USINT;
  END_VAR

  //SDO requests for this commands don't have any further data
  pSDORequestBuffer$^void := #RequBuffer[0];
  pSDOResponseBuffer$^void := #RespBuffer[0];
  
  //SDO response buffer (length header + rc + data)
  us_OutLength := sizeof(t_SDOStdHeader) + 1 + us_RespLength;
  
  //calculate length without crc
  pSDORequestBuffer^.len := sizeof(t_SDOStdHeader) - sizeof(t_SDOStdHeader.CRC);
  
  //type
  pSDORequestBuffer^.typ := SDO_STD;
  
  //sequence number
  pSDORequestBuffer^.SeqNr := us_CntSequNr;
  
  //increase counter for sequence number
  if us_CntSequNr < 16#7F then
    us_CntSequNr += 1;
  else
    us_CntSequNr := 0;
  end_if;
  
  //command to inform CPU about initializing classes is ok
  pSDORequestBuffer^.cmd := us_Command;
  
  //set CRC-field to 0
  pSDORequestBuffer^.CRC := 0;

  //send request
  sd_retval := SAFETY_iSendRequest( uiMaster    := 0
                                  , pucNode     := p_ObjectPath
                                  , uiInLength  := sizeof(t_SDOStdHeader)
                                  , pucInput    := pSDORequestBuffer$^USINT
                                  , uiOutLength := us_OutLength
                                  , pucOutput   := pSDOResponseBuffer$^USINT
                                  );  

  //no response
  if ( sd_retval = 0 ) then

    //check sequence number to exclude a possible wrong response
    if ( pSDORequestBuffer^.SeqNr <> pSDOResponseBuffer^.StdHeader.SeqNr ) then
      sd_retval := -102;
      //log not routable SDOs
      LogValue("@022A (SafetyBaseFSoE::SDOStdRequ) Invalid Sequence number in SDO Standard Response. Got 0x{0} instead of expected 0x{1}", to_udint(pSDOResponseBuffer^.StdHeader.SeqNr), to_udint(pSDORequestBuffer^.SeqNr));
      
    //check returncode of SDO
    elsif ( pSDOResponseBuffer^.us_Returncode <> SBF_SDO_RETCODE_OK ) then
      //log not routable SDOs
      LogValue("@022B (SafetyBaseFSoE::SDOStdRequ) Safety module 0x{0}: Retcode of SDO Standard Response was invalid. Retcode: 0x{1}", SafetyNumber$UDINT, to_udint(pSDOResponseBuffer^.us_Returncode));
      sd_retval := -103;
    end_if;
  else
    //log not routable SDOs
    LogValue("@023E (SafetyBaseFSoE::SDOStdRequ) SAFETY_iSendRequest failed with retcode: -0x{0}", to_udint(ABS(sd_retval)));
  end_if;
  
  //return response
  if ( ( sd_retval = 0 ) & ( p_us_RespData <> NIL )) then
  
    ToStdLib.MemCpy(dest := p_us_RespData, source := #pSDOResponseBuffer^.us_Data, size := us_RespLength);
  
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetPDOInputBuffer
  VAR_INPUT
    ud_p2Buffer 	: UDINT;
    ud_SafetyNr 	: HDINT;
    p_FoundSafetyNr 	: ^BOOL;(* := NIL *)
    b_IsInterfaceFrame 	: BOOL;
    ui_TargetOffset 	: UINT;
  END_VAR
  VAR_OUTPUT
    p_InputBuffer 	: ^SafetyBaseFSoE::PDOInputBuffer;
  END_VAR
  VAR
    i1                : UINT;
    p_InputBuffer_Act : ^PDOInputBuffer;
  END_VAR

  p_InputBuffer := NIL;
  
  if p_FoundSafetyNr then
    p_FoundSafetyNr^ := FALSE;
  end_if;

  if ui_cnt_PDOInputBuffer then
  
    //set pointer on first input buffer element
    p_InputBuffer_Act := #aPDOInputBuffer[0];
    
    //check each element
    for i1 := 0 to (ui_cnt_PDOInputBuffer-1) do 
    
      //if searched pointer is found, pointer on input buffer element is returned
      if (p_InputBuffer_Act^.ud_p2Buffer        = ud_p2Buffer )       & 
         (p_InputBuffer_Act^.b_IsInterfaceFrame = b_IsInterfaceFrame) &
         
         // if it's an interface frame, the offset has to be the same (otherwise it could be a dynamically connected FlexCon)
        ((b_IsInterfaceFrame = FALSE) |
         (p_InputBuffer_Act^.ui_TargetOffset    = ui_TargetOffset))   then
        // even if found by SafetyNumber, if found by buffer => reset the found by safetynumber
        p_FoundSafetyNr^ := FALSE;
        p_InputBuffer := p_InputBuffer_Act;
        return;
      end_if;
      
      if p_FoundSafetyNr & (p_InputBuffer_Act^.ud_SafetyNr        = ud_SafetyNr)        &
                           (p_InputBuffer_Act^.b_IsInterfaceFrame = b_IsInterfaceFrame) & 

                           // if it's an interface frame, the offset has to be the same (otherwise it could be a dynamically connected FlexCon)
                          ((b_IsInterfaceFrame = FALSE) |
                           (p_InputBuffer_Act^.ui_TargetOffset    = ui_TargetOffset))   then
        p_FoundSafetyNr^ := TRUE;
        p_InputBuffer := p_InputBuffer_Act;
//        return;
      end_if;

      
      //set pointer to next element
      p_InputBuffer_Act += sizeof ( PDOInputBuffer );
    
    end_for;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetThisViaServerName
  VAR_INPUT
    pObjectDotServer 	: ^CHAR;
  END_VAR
  VAR_OUTPUT
    ThisPtr 	: ^void;
  END_VAR
  VAR
    ChannelMode : CHMODE;
  END_VAR
  
  ThisPtr := NIL;

  if pObjectDotServer = NIL then
    return;
  end_if;
  
  // get thispointer to server via name
  ChannelMode := _LookUpEmbed_C(pObjectDotServer, #ThisPtr, NIL);

  // check if it's a server
  case ChannelMode of
    _CH_CMD,
    _CH_SVR:
    
  else
    ThisPtr := NIL;
    LogError("@023F (SafetyBaseFSoE::GetThisViaServerName) Couldn't get thispointer of server: ");
    HWC_LogPrintf(pThis:=THIS, format:="@023F %s", p0:=pObjectDotServer);
    ud_UnsafeVarsNotFound += 1;
  end_case;

END_FUNCTION

#pragma warning(disable: 73);
FUNCTION VIRTUAL SafetyBaseFSoE::LogError
  VAR_INPUT
    e_msg 	: ^CHAR;
  END_VAR
  
#ifdef HWC_LogError
  HWC_LogError(this, e_msg);
#endif

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::LogValue
  VAR_INPUT
    pString 	: ^CHAR;
    Value 	: UDINT;
    Value2 	: UDINT;(* := 16#FFFFFFFF *)
    Value3 	: UDINT;(* := 16#FFFFFFFF *)
  END_VAR
  
#ifdef HWC_LogValue3
  HWC_LogValue3(this, pString, Value, Value2, Value3);
#endif

END_FUNCTION
#pragma warning(default: 73);


FUNCTION VIRTUAL SafetyBaseFSoE::AddUnsafeRoutElement
  VAR_INPUT
    pRoutTable 	: ^SafetyBaseFSoE::pUnsafeIORoutingElement;
    pRoutElementsNo 	: ^UINT;
    ud_Address 	: UDINT;
    ui_NameLen 	: UINT;
    pNameNewElement 	: ^CHAR;
    BitMask 	: BDINT;(* := 0 *)
  END_VAR
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR
  VAR
    p_RoutElement   : ^UnsafeIORoutingElement;
    LinearMemory : BOOL;
  END_VAR 

  sd_retval := -1;

  // check parameters
  if (ui_NameLen = 0) | (pNameNewElement = NIL) then
    LogError("@0240 (SafetyBaseFSoE::AddUnsafeRoutElement) Invalid call parameters for the variable name (invalid configuration in safety cpu).");
    return;
  end_if;

  // on CSCP011 the SDO_COMMAND_WRITE_VALUES didn't exist, so we have to make a difference here
  if (pRoutTable = #p_UnsafeOutputsTable) then
    LinearMemory := TRUE;
  else
    LinearMemory := FALSE;
  end_if;

  //With the first element, the table must be positioned
  if ( pRoutTable^ = NIL ) then
  
    //allocate memory for first routing element
    pRoutTable^$^void := ToStdLib.Malloc(size := sizeof(UnsafeIORoutingElement));
  
    if ( pRoutTable^ = NIL ) then
  
      sd_retval                         := -1;
      SetSafetyState(NewState:=_MemAllocFailed);
      return;
      
    end_if;
    
    p_RoutElement := pRoutTable^;
    
  else
    
    //add new element
    
    //add space for one element to table (linked list element for inputs and linear data area for outputs)
    if LinearMemory = FALSE then
    // search for a place
      p_RoutElement := pRoutTable^;
      while p_RoutElement^.pNext do
        p_RoutElement := p_RoutElement^.pNext;
      end_while;
      p_RoutElement^.pNext$^void := ToStdLib.Malloc(sizeof(UnsafeIORoutingElement));

      if ( p_RoutElement^.pNext = NIL ) then
      
        sd_retval                         := -1;
        SetSafetyState(NewState:=_MemAllocFailed);
        return;
        
      end_if;

      // set pointer to new element
      p_RoutElement := p_RoutElement^.pNext;
    else
      pRoutTable^$^void := ToStdLib.ReAlloc(mptr := pRoutTable^, newsize := ( ( pRoutElementsNo^ + 1 ) * sizeof(UnsafeIORoutingElement)));

      if ( pRoutTable^ = NIL ) then
      
        sd_retval                         := -1;
        SetSafetyState(NewState:=_MemAllocFailed);
        return;
        
      end_if;
      
      //set pointer to empty element on last position
      p_RoutElement := pRoutTable^ + ( pRoutElementsNo^ ) * sizeof(UnsafeIORoutingElement);
    end_if;
  end_if;  

  if p_RoutElement then
    //allocate memory for name
    p_RoutElement^.pName$^void := ToStdLib.Malloc(size := ui_NameLen);
  
    if ( p_RoutElement^.pName = NIL ) then
  
      sd_retval                         := -1;
      SetSafetyState(NewState:=_MemAllocFailed);
      return;
      
    end_if;

    p_RoutElement^.NameLength := ui_NameLen;
    _memcpy(ptr1:=p_RoutElement^.pName, ptr2:=pNameNewElement, cntr:=ui_NameLen);

    // unsafe bdint variables are localized in a different memory area
    if BitMask then
      p_RoutElement^.ud_Address := SBF_SDO_ADDRESS_UNSAFE_BDINT + ud_Address;
    else
      p_RoutElement^.ud_Address := SBF_SDO_ADDRESS_APP_DATA + ud_Address;
    end_if;
    
    p_RoutElement^.BitMask := BitMask;
    
    // initialize with invalid
    p_RoutElement^.b_IsValid            := FALSE;
    p_RoutElement^.sd_OldValue          := (16#80000000)$DINT;
    p_RoutElement^.sd_NewValue4External := (16#80000000)$DINT;

    // remember that we haven't transmitted this value so far
    p_RoutElement^.b_AlreadyTransmitted := FALSE;

    // initialize next pointer
    p_RoutElement^.pNext := NIL;
    
    //increase counter
    pRoutElementsNo^ += 1;
    
    // check if name exists in PLC
    p_RoutElement^.ud_Thisp   := GetThisViaServerName(pNameNewElement)$UDINT;
    if (pRoutTable = #p_UnsafeOutputsTable) & p_RoutElement^.BitMask then
      p_RoutElement^.UnsafeType := BDINTOutput;
    else
      p_RoutElement^.UnsafeType := NormalUnsafe;
    end_if;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::InitPathInfo
	VAR_INPUT
		pPathInfo 	: ^SafetyBaseFSoE::PathInfoType;
		pInitPath 	: ^USINT;(* := NIL *)
		pInitPathComp 	: ^USINT;(* := NIL *)
	END_VAR

  pPathInfo^.p_us_Path := pInitPath;
  pPathInfo^.p_us_PathComp := pInitPathComp;
  pPathInfo^.ui_PathCompLen := 0;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::ResetConfig
  VAR
    p_SafetyBase        : pVirtualBase;
    p_RoutElement       : ^RoutElement;
    p_UnsafeRoutElement : ^UnsafeIORoutingElement;
    i                   : UINT;
    MyPara              : CmdStruct;
    MyResult            : results;
  END_VAR
  
  // tell all modules, which are connected to this cpu (through the old configuration) that they should delete the cpu from their routing table
  // must be more than 1, because 1st is always own entry
  if p_RoutTable & (ui_CntRoutElements > 1) then
    //set pointer on 2nd routing element
    p_RoutElement := p_RoutTable + sizeof(RoutElement);
    for i := 1 to (ui_CntRoutElements - 1) do
      // clear everything except this object (done after the for loop)
      if p_RoutElement^.ud_Thisp <> THIS$UDINT then
        // get thispointer of routing element
        p_SafetyBase := (p_RoutElement^.ud_Thisp)$pVirtualBase;
        
        // call other safety-class to remove the safetynumber from the routing table
        MyPara.uiCmd := CMD_SM_REMOVE_CPU_FROM_TABLE;
        MyPara.aPara[0] := SafetyNumber$DINT;
        
        p_SafetyBase^.NewInst(#MyPara, #MyResult);    

//        p_SafetyBase^.RemoveCPUFromTable(ud_SafetyNr := SafetyNumber);        
      end_if;

      // free memory of compressed path
      if p_RoutElement^.p_CompressedPath then
        ToStdLib.Free(mptr:=p_RoutElement^.p_CompressedPath);
      end_if;

      // set pointer on next routing element
      p_RoutElement += sizeof(RoutElement);
    end_for;
    
    // reduce local routing table to the cpu's own entry
    ui_CntRoutElements := 1;
  end_if;

  // on CSCP011 the SDO_COMMAND_WRITE_VALUES didn't exist, so we have to make a difference here
  while p_UnsafeInputsTable do
    p_UnsafeRoutElement := p_UnsafeInputsTable;
    p_UnsafeInputsTable := p_UnsafeInputsTable^.pNext;
    
    ToStdLib.Free(mptr:=p_UnsafeRoutElement^.pName);
    ToStdLib.Free(mptr:=p_UnsafeRoutElement);
  end_while;
  ui_CntUnsafeInputs := 0;

  // unsafe outputs are always in linear memory
  if p_UnsafeOutputsTable then
    ToStdLib.Free(mptr:=p_UnsafeOutputsTable);
    p_UnsafeOutputsTable := NIL;
  end_if;
  ui_CntUnsafeOutputs := 0;
  
  // reset no of SDO input buffer
  ui_cnt_SDOInputBuffer := 0;
  
  // if the old configuration is reset, check for the configured state of the cpu
  b_CheckConfigured := TRUE;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::RemoveCPUFromTable
  VAR_INPUT
    ud_SafetyNr 	: HDINT;
  END_VAR
  VAR
    p_RoutElement   : ^RoutElement;
    i               : UDINT;
    FoundIndex      : UDINT;
    pPDOInputBuffer : ^PDOInputBuffer;
    Found : BOOL;
  END_VAR

  // if we are a cpu, do nothing
  if b_CheckCRC then
    return;
  end_if;
  
  //check each element (must be more than 1, because 1st is always own entry)
  if p_RoutTable & (ui_CntRoutElements > 1) then
    //set pointer on 2nd routing element
    p_RoutElement := p_RoutTable + sizeof(RoutElement);
    for i := 1 to (ui_CntRoutElements - 1) do
    
      // if we found safenumber, delete element from table
      if ( p_RoutElement^.ud_SafeNumber = ud_SafetyNr ) then
      
        // at last element we don't need to copy data, just decrease buffersize
        if i <> (ui_CntRoutElements - 1) then
          _memcpy(p_RoutElement, p_RoutElement + sizeof(RoutElement), sizeof(RoutElement) * (ui_CntRoutElements - 1 - i));
        end_if;
        
        // free 1 element and decrease element counter
        ui_CntRoutElements -= 1;

        exit;
      end_if;
      
      // set pointer on next routing element
      p_RoutElement += sizeof(RoutElement);
    end_for;
  end_if;  
    
  // reset no of SDO input buffer
  ui_cnt_SDOInputBuffer := 0;

  // check if we received a PDO from this CPU => if so, delete it from the PDO input buffer
  if ui_cnt_PDOInputBuffer then   
    // search for the PDO
    Found := FALSE;
    pPDOInputBuffer := #aPDOInputBuffer[0];
    for FoundIndex := 0 to (ui_cnt_PDOInputBuffer-1) do
      if pPDOInputBuffer^.ud_SafetyNr = ud_SafetyNr then
        Found := TRUE;
        exit;
      end_if;
      
      pPDOInputBuffer += sizeof(PDOInputBuffer);
    end_for;

    // if we found the PDO => remove it from memory (move the memory behind the found PDO over the place of the found PDO)
    if Found then
      // if it's last entry (no entries have to be moved) => we only have to copy some memory if it's not the last
      if FoundIndex < (ui_cnt_PDOInputBuffer-1) then
        for i := FoundIndex to (ui_cnt_PDOInputBuffer-2) do
          aPDOInputBuffer[i] := aPDOInputBuffer[i+1];
        end_for;
      end_if;
      ui_cnt_PDOInputBuffer -= 1;

      if ui_cnt_PDOInputBuffer = 0 then
        if ModuleSyncNo & (SafetyState <> _SafetyClassOK) then
          ModuleSyncNo -= 1;
        end_if;
      end_if;
    end_if;
  end_if;  

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::CheckUnsafeUpdateCycle

  // we can only use 56 byte pay load (instead of 64) because fw drops the 8 byte used for addresses in safe SDO

  if ui_CntUnsafeOutputs then
  // maximum of read variables in 1 frame is 15 (56 Bytes pay load - 1 Byte Returncode - 1 Byte Length = 54 Bytes => 13x4Bytes = 52)
    MaxVarsPerReadPackage := (SBF_SDO_MAX_LENGTH - (sizeof(SDOHeader) - sizeof(SDOHeader.SessId)) - sizeof(SDORespReadConfig.us_Returncode) - sizeof(SDORespReadConfig.us_Length)) / sizeof(DINT);
    ReadFramesNeeded := ((ui_CntUnsafeOutputs - 1) / MaxVarsPerReadPackage) + 1;
  else
    ReadFramesNeeded := 0;
  end_if;

  if ui_CntUnsafeInputs then
  // maximum of write variables in 1 frame is 14 (56 Bytes pay load - 4 Byte Address - 1 Byte Length = 51 Bytes => 12x4Bytes = 48)
    MaxVarsPerWritePackage := (SBF_SDO_MAX_LENGTH - (sizeof(SDOHeader) - sizeof(SDOHeader.SessId)) - sizeof(SDORequReadConfig.ud_Address) - sizeof(SDORequReadConfig.us_Length)) / sizeof(DINT);
    WriteFramesNeeded := ((ui_CntUnsafeInputs - 1) / MaxVarsPerWritePackage) + 1;
  else
    WriteFramesNeeded := 0;
  end_if;

  // check if user has given us enough time
  ud_TransfertimePerPackage := SBF_SDO_TRANSFERTIME;
  
  if ( (WriteFramesNeeded + ReadFramesNeeded) * ud_TransfertimePerPackage ) > ServerUpdateTime then
    ServerUpdateTime := ( WriteFramesNeeded + ReadFramesNeeded ) * ud_TransfertimePerPackage;
    ServerUpdateTime.Write(input:=ServerUpdateTime);

    ud_ServerUpdateTime := ( ReadFramesNeeded ) * ud_TransfertimePerPackage;
  end_if;  

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::FetchDiagState

  DiagVars.UserDiagStateReady := FALSE;
  DiagVars.UserWantsDiagState := TRUE;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::GetDiagState
  VAR_INPUT
    pDiagState 	: ^SafetyDiagState;
  END_VAR
  VAR_OUTPUT
    StateReady 	: BOOL;
  END_VAR
  
  StateReady := DiagVars.UserDiagStateReady;
  
  // if data ready, we save it into user structure
  if StateReady then
    pDiagState^.RunState            := DiagVars.UserGetStateResp.RuntimeState;
    pDiagState^.ConfigState         := DiagVars.UserGetStateResp.ConfigState;
    pDiagState^.LoginLevel          := DiagVars.UserGetStateResp.LoginLevel;
    pDiagState^.ErrorCounterIOState := DiagVars.UserGetStateResp.ErrorCount;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::FetchDiagInfo
  VAR_INPUT
    ucChoice 	: USINT;
  END_VAR

  DiagVars.ucChoiceDiagInfo  := ucChoice;
  DiagVars.UserDiagInfoReady := FALSE;
  DiagVars.UserWantsDiagInfo := TRUE;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::GetDiagInfo
  VAR_INPUT
    pDiagInfo 	: ^SafetyDiagInfo;
  END_VAR
  VAR_OUTPUT
    StateReady 	: BOOL;
  END_VAR
  
  StateReady := DiagVars.UserDiagInfoReady;
  
  // if data ready, we save it into user structure
  if StateReady then
    pDiagInfo^ := DiagVars.UserGetDiagInfoResp;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::SDORequ
  VAR_INPUT
    us_Command 	: USINT;
    p_us_RequData 	: ^USINT;
    us_RequLength 	: USINT;
    p_us_RespData 	: ^USINT;
    us_RespLength 	: USINT;
  END_VAR
  VAR_OUTPUT
    sd_retval 	: DINT;
  END_VAR
  VAR
    pSDORequestBuffer   : ^t_SDOStdHeader;
    pSDOResponseBuffer  : ^SDOStdResp;
    RespBuffer          : ARRAY [0..127] OF USINT;
    RequBuffer          : ARRAY [0..127] OF USINT;
    us_InLength         : USINT;
    us_OutLength        : USINT;
  END_VAR

  //SDO requests for this commands don't have any further data
  pSDORequestBuffer$^void := #RequBuffer[0];
  pSDOResponseBuffer$^void := #RespBuffer[0];  
   
  //SDO request buffer (length header + data)
  us_InLength := sizeof(t_SDOStdHeader) + us_RequLength;
  
  //SDO response buffer (length header + rc + data)
  us_OutLength := sizeof(t_SDOStdHeader) + 1 + us_RespLength;
  
  //calculate length without crc
  pSDORequestBuffer^.len := sizeof(t_SDOStdHeader) - sizeof(t_SDOStdHeader.CRC) + us_RequLength;
  
  //type
  pSDORequestBuffer^.typ := SDO_STD;
  
  //sequence number
  pSDORequestBuffer^.SeqNr := us_CntSequNr;

  _memcpy(ptr1:=pSDORequestBuffer + sizeof(t_SDOStdHeader), ptr2:=p_us_RequData, cntr:=us_RequLength);

  //increase counter for sequence number
  if us_CntSequNr < 16#7F then
    us_CntSequNr += 1;
  else
    us_CntSequNr := 0;
  end_if;
  
  //command to inform CPU about initializing classes is ok
  pSDORequestBuffer^.cmd := us_Command;
  
  //set CRC-field to 0
  pSDORequestBuffer^.CRC := 0;
  
  //send request
  sd_retval := SAFETY_iSendRequest( uiMaster    := 0
                                  , pucNode     := p_ObjectPath
                                  , uiInLength  := us_InLength
                                  , pucInput    := pSDORequestBuffer$^USINT
                                  , uiOutLength := us_OutLength
                                  , pucOutput   := pSDOResponseBuffer$^USINT
                                  );

  //no response
  if ( sd_retval = 0 ) then

    //check sequence number to exclude a possible wrong response
    if ( pSDORequestBuffer^.SeqNr <> pSDOResponseBuffer^.StdHeader.SeqNr ) then
      sd_retval := -102;
      
    //check returncode of SDO
    elsif ( pSDOResponseBuffer^.us_Returncode <> SBF_SDO_RETCODE_OK ) then
      sd_retval := -103;
    end_if;
  
  end_if;
  
  //return response
  if ( ( sd_retval = 0 ) & ( p_us_RespData <> NIL )) then
    ToStdLib.MemCpy(dest := p_us_RespData, source := #pSDOResponseBuffer^.us_Data, size := us_RespLength);
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::SaveLog
  VAR_INPUT
    ucChoice 	: USINT;
    pDPNE 	: ^CHAR;
  END_VAR
  VAR_OUTPUT
    Success 	: DINT;
  END_VAR
  
  Success := FALSE;

  if DiagVars.SavingLogInProgress = FALSE then
  
    if _strlen(src:=pDPNE) < sizeof(DiagVars.DPNE) then
      
      DiagVars.SavingLogSuccessful := FALSE; //Reset Value
      DiagVars.ucChoiceSaveLog := ucChoice;
      
      _strcpy(dest:=#DiagVars.DPNE[0], src:=pDPNE);
      
      // Set Flags after string and data was copied
      DiagVars.SavingLogInProgress := TRUE;
      Success := TRUE;
      
    end_if;
    
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::SaveLogActive
  VAR_OUTPUT
    InProgress 	: BOOL;
  END_VAR
  
  InProgress := DiagVars.SavingLogInProgress;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::BlockedTransferLength
  VAR_INPUT
    ud_Datalength 	: UDINT;
    us_MaxBlocklength 	: USINT;
  END_VAR
  VAR_OUTPUT
    us_ActBlocklength 	: USINT;
  END_VAR

  IF ud_Datalength > to_UDINT(us_MaxBlocklength) THEN
    us_ActBlocklength := us_MaxBlocklength;
  ELSE
    us_ActBlocklength := to_USINT(ud_Datalength);
  END_IF;

END_FUNCTION

#pragma warning(disable: 73)
FUNCTION VIRTUAL SafetyBaseFSoE::AddFrame
  VAR_INPUT
    pFrame 	: ^t_SDOStdHeader;
    FrameState 	: DINT;
    CallerSafetyNr 	: HDINT;
  END_VAR

END_FUNCTION
#pragma warning(default: 73)

FUNCTION VIRTUAL SafetyBaseFSoE::RecordFrame
  VAR_INPUT
    pFrame 	: ^DINT;
    FrameState 	: DINT;
    IsFSoEPDO 	: BOOL;(* := FALSE *)
  END_VAR
  VAR
    ThisBackup  : pVoid;
    TempSafetyNr: HDINT;
    Length      : USINT;
  END_VAR

  if pAnalyzerThis & pAnalyzerMethod then
    
    TempSafetyNr := SafetyNumber;
  
    if IsFSoEPDO then
           
      // if write
      if FrameState then
        if aWritePDOs[0].Length > sizeof(PDOType.DataAndCopy) then
          Length := sizeof(PDOType.DataAndCopy);
        else
          Length := aWritePDOs[0].Length;
        end_if;

        // if it's the same content as last time
        if (_memcmp(ptr1:=pFrame, ptr2:=#OldFakePDOWrite.DataAndCopy[0], cntr:=Length) = 0) then
          // don't record it
          return;
        end_if;

        // fake the header
        OldFakePDOWrite.Length              := sizeof(PDOType) - sizeof(PDOType.CRC) - sizeof(PDOType.DataAndCopy) + Length;
        OldFakePDOWrite.FrameType.PDO       := TRUE;
        OldFakePDOWrite.FrameType.Reserved2 := TRUE;        

        // store the PDO data (remember it as the last recorded)
        // Copy data to OldFakePDOWrite.SourceAdr because SourceAdr, SourceAdrCopy and msTimeStamp are not used by FSoE PDOs
        _memcpy(ptr1:=#OldFakePDOWrite.SourceAdr, ptr2:=pFrame, cntr:=Length);

        pFrame := (#OldFakePDOWrite)$^DINT;

      // if read
      else
        if aReadPDOs[0].Length > sizeof(PDOType.DataAndCopy) then
          Length := sizeof(PDOType.DataAndCopy);
        else
          Length := aReadPDOs[0].Length;
        end_if;

        // if it's the same content as last time
        if (_memcmp(ptr1:=pFrame, ptr2:=#OldFakePDORead.DataAndCopy[0], cntr:=Length) = 0) then
          // don't record it
          return;
        end_if;

        // fake the header
        OldFakePDORead.Length              := sizeof(PDOType) - sizeof(PDOType.CRC) - sizeof(PDOType.DataAndCopy) + Length;
        OldFakePDORead.FrameType.PDO       := TRUE;
        OldFakePDORead.FrameType.Reserved2 := TRUE;        

        // store the PDO data (remember it as the last recorded)
        // Copy data to OldFakePDOWrite.SourceAdr because SourceAdr, SourceAdrCopy and msTimeStamp are not used by FSoE PDOs
        _memcpy(ptr1:=#OldFakePDORead.SourceAdr, ptr2:=pFrame, cntr:=Length);

        pFrame := (#OldFakePDORead)$^DINT;
      end_if;            
    end_if;
  
    ThisBackup := THIS;
    
    THIS := pAnalyzerThis$^SafetyBaseFSoE;
    pAnalyzerMethod$AddFrame(pFrame$^t_SDOStdHeader, FrameState, TempSafetyNr);
    
    THIS := ThisBackup$^SafetyBaseFSoE;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::ShowProjectRevision

// overload this for CPU modules
  
END_FUNCTION


FUNCTION SafetyBaseFSoE::DOCheck
  VAR_INPUT
    pFrame 	: ^t_SDOStdHeader;
    pCheckStruct 	: ^SafetyBaseFSoE::t_DataObjectCheck;
  END_VAR
  VAR
    DOCheckHeader : t_SDOStdHeader;
    ActTime : UDINT;
  END_VAR

  // check if input parameters are correct
  if pFrame & pFrame^.Len & pCheckStruct then
    DOCheckHeader := pFrame^;
    
    // only if we got a different frame
    if DOCheckHeader <> pCheckStruct^.LastHeader then
      ActTime := OS_READMICROSEC();
      
      pCheckStruct^.TimeDiff := ActTime - pCheckStruct^.LastTime;
      
      if pCheckStruct^.TimeDiff > pCheckStruct^.TimeDiffMax then
        if pCheckStruct^.TimeDiffMax = 0 then
          pCheckStruct^.TimeDiffMax := 1;
        else
          pCheckStruct^.TimeDiffMax := pCheckStruct^.TimeDiff;
        end_if;
      end_if;

      if pCheckStruct^.TimeDiff < pCheckStruct^.TimeDiffMin then
        pCheckStruct^.TimeDiffMin := pCheckStruct^.TimeDiff;
      end_if;


      pCheckStruct^.LastTime := ActTime;      
    else
      // if it's the same frame again: problem!
      pCheckStruct^.SameFrameCnt += 1;
    end_if;
    pCheckStruct^.LastHeader := DOCheckHeader;
  end_if; 

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::CalcPDOLength
  VAR_INPUT
    BoolVarNo 	: UDINT;
    DintVarNo 	: UDINT;
  END_VAR
  VAR_OUTPUT
    PdoLen 	: USINT;
  END_VAR
  VAR
    PayLoad : USINT;
  END_VAR

  // we have at least the header
  PdoLen := sizeof(PDOType) - sizeof(PDOType.DataAndCopy);
  PayLoad := 0;
  
  // now add some space for the needed data
  if BoolVarNo then
    // 1 byte (type + number of values) + 1 byte index + 2 bits for every value (4 per byte)
    PayLoad += 2 + ((BoolVarNo$USINT + 3) / 4);

    // make the it 2 byte aligned (if it isn't already) => only if no dint variables, otherwise it is done one at the DINT variables
    if (PayLoad MOD 2) & (DintVarNo = 0) then
      PayLoad := PayLoad + (2 - (PayLoad MOD 2)); //increase memory, up to a value, which could be divided by 2
    end_if;
  end_if;

  if DintVarNo then
    // 1 byte (type + number of values) + 1 byte index + 1 byte errorflags per 8 values + 4 byte for every value
    PayLoad += 2 + (((DintVarNo$USINT - 1 ) / 8) + 1) + DintVarNo$USINT * 4;
    
    // make the it 2 byte aligned (if it isn't already)
    if (PayLoad MOD 2) then
      PayLoad := PayLoad + (2 - (PayLoad MOD 2)); //increase memory, up to a value, which could be divided by 2
    end_if;
  end_if;

  // duplicate it
  PayLoad *= 2;

  // add it to the total length
  PdoLen += PayLoad; 

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddModuleToCfg
  VAR_INPUT
    ModuleCfg 	: t_ModuleCfg;
  END_VAR
  VAR_OUTPUT
    retcode 	: DINT;
  END_VAR
  VAR
    pHelp : ^t_ModuleListElem;
  END_VAR

  retcode := 0;

  if pFirstModuleCfg = NIL then
    pFirstModuleCfg$pVoid := ToStdLib.Malloc(size:=sizeof(t_ModuleListElem));
    pHelp := pFirstModuleCfg;
  else
    pHelp := pFirstModuleCfg;
    // if searched path has been found, we already got this module in our list
    if (ModuleCfg.PathLen = pHelp^.ModuleInfo.PathLen) & (ToStdLib.MemCmp(dest:= ModuleCfg.pPath, source:= pHelp^.ModuleInfo.pPath , size:= ModuleCfg.PathLen) = 0) then
      // already in buffer => leave function
      return;
    end_if;
    while pHelp^.pNext <> NIL do
      // if searched path has been found, we already got this module in our list
      if (ModuleCfg.PathLen = pHelp^.ModuleInfo.PathLen) & (ToStdLib.MemCmp(dest:= ModuleCfg.pPath, source:= pHelp^.ModuleInfo.pPath , size:= ModuleCfg.PathLen) = 0) then
        // already in buffer => leave function
        return;
      else
        pHelp := pHelp^.pNext;
      end_if;
    end_while;
    
    // if searched path has been found, we already got this module in our list
    if (ModuleCfg.PathLen = pHelp^.ModuleInfo.PathLen) & (ToStdLib.MemCmp(dest:= ModuleCfg.pPath, source:= pHelp^.ModuleInfo.pPath , size:= ModuleCfg.PathLen) = 0) then
      // already in buffer => leave function
      return;
    else
      pHelp^.pNext$pvoid := ToStdLib.Malloc(size:=sizeof(t_ModuleListElem));
      pHelp := pHelp^.pNext;
    end_if;
  end_if;
  
  if pHelp <> NIL then
    // allocate memory for the module path
    pHelp^.ModuleInfo.pPath$pVoid := ToStdLib.Malloc(size:=ModuleCfg.PathLen);
    if pHelp^.ModuleInfo.pPath = NIL then
      SetSafetyState(NewState:=_MemAllocFailed);
      retcode := -1;
      return;
    end_if;

    _memcpy(ptr1:=pHelp^.ModuleInfo.pPath, ptr2:=ModuleCfg.pPath, cntr:=ModuleCfg.PathLen);

    // allocate memory for the module path
    pHelp^.ModuleInfo.pCompPath$pVoid := ToStdLib.Malloc(size:=ModuleCfg.CompPathLen);
    if pHelp^.ModuleInfo.pCompPath = NIL then
      SetSafetyState(NewState:=_MemAllocFailed);

      retcode := -1;
      return;
    end_if;

    _memcpy(ptr1:=pHelp^.ModuleInfo.pCompPath, ptr2:=ModuleCfg.pCompPath, cntr:=ModuleCfg.CompPathLen);

    pHelp^.ModuleInfo.Info        := ModuleCfg.Info;
    pHelp^.ModuleInfo.SafetyNbr   := ModuleCfg.SafetyNbr;
    pHelp^.ModuleInfo.InterfaceCRC:= ModuleCfg.InterfaceCRC;
    pHelp^.ModuleInfo.DestCRCNo   := ModuleCfg.DestCRCNo;
    phelp^.ModuleInfo.pDestCRCs   := ModuleCfg.pDestCRCs;
    pHelp^.ModuleInfo.PathLen     := ModuleCfg.PathLen;
    pHelp^.ModuleInfo.CompPathLen := ModuleCfg.CompPathLen;
    pHelp^.ModuleInfo.PdoOutLen   := ModuleCfg.PdoOutLen;
    
    pHelp^.pNext                  := NIL;
    
    ModuleCfgNo += 1;
  else
    SetSafetyState(NewState:=_MemAllocFailed);
    retcode := -1;
    return;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddPdoToCfg
  VAR_INPUT
    PdoCfg 	: t_PdoCfg;
  END_VAR
  VAR_OUTPUT
    retcode 	: DINT;
  END_VAR
  VAR
    pHelp : ^t_PdoListElem;
  END_VAR
//#pragma message("RamAnd 04.11.2014 17:18 \ die Aufrufe dieser Methode wurden unterbunden, da diese PDO-Infos für FSoE keine Relevanz haben")

  retcode := 0;

  if pFirstPdoCfg = NIL then
    pFirstPdoCfg$pVoid := ToStdLib.Malloc(size:=sizeof(t_PdoListElem));
    pHelp := pFirstPdoCfg;
  else
    pHelp := pFirstPdoCfg;
    // if searched path has been found, we already got this pdo in our list
    if (PdoCfg.SourcePathLen = pHelp^.PdoInfo.SourcePathLen) & (PdoCfg.DestPathLen = pHelp^.PdoInfo.DestPathLen) &
      (PdoCfg.InterfaceCRC = pHelp^.PdoInfo.InterfaceCRC) &
      (ToStdLib.MemCmp(dest:= PdoCfg.pSourcePath, source:= pHelp^.PdoInfo.pSourcePath , size:= PdoCfg.SourcePathLen) = 0) &
      (ToStdLib.MemCmp(dest:= PdoCfg.pDestPath,   source:= pHelp^.PdoInfo.pDestPath   , size:= PdoCfg.DestPathLen  ) = 0) then
      // already in buffer => leave function
      return;
    end_if;
    while pHelp^.pNext <> NIL do
      // if searched path has been found, we already got this pdo in our list
      if (PdoCfg.SourcePathLen = pHelp^.PdoInfo.SourcePathLen) & (PdoCfg.DestPathLen = pHelp^.PdoInfo.DestPathLen) &
        (PdoCfg.InterfaceCRC = pHelp^.PdoInfo.InterfaceCRC) &
        (ToStdLib.MemCmp(dest:= PdoCfg.pSourcePath, source:= pHelp^.PdoInfo.pSourcePath , size:= PdoCfg.SourcePathLen) = 0) &
        (ToStdLib.MemCmp(dest:= PdoCfg.pDestPath,   source:= pHelp^.PdoInfo.pDestPath   , size:= PdoCfg.DestPathLen  ) = 0) then
        // already in buffer => leave function
        return;
      else
        pHelp := pHelp^.pNext;
      end_if;
    end_while;
    
    // if searched path has been found, we already got this pdo in our list
    if (PdoCfg.SourcePathLen = pHelp^.PdoInfo.SourcePathLen) & (PdoCfg.DestPathLen = pHelp^.PdoInfo.DestPathLen) &
      (PdoCfg.InterfaceCRC = pHelp^.PdoInfo.InterfaceCRC) &
      (ToStdLib.MemCmp(dest:= PdoCfg.pSourcePath, source:= pHelp^.PdoInfo.pSourcePath , size:= PdoCfg.SourcePathLen) = 0) &
      (ToStdLib.MemCmp(dest:= PdoCfg.pDestPath,   source:= pHelp^.PdoInfo.pDestPath   , size:= PdoCfg.DestPathLen  ) = 0) then
      // already in buffer => leave function
      return;
    else
      pHelp^.pNext$pvoid := ToStdLib.Malloc(size:=sizeof(t_PdoListElem));
      pHelp := pHelp^.pNext;
    end_if;
  end_if;
  
  if pHelp <> NIL then
    // allocate memory for the module source path
    pHelp^.PdoInfo.pSourcePath$pVoid := ToStdLib.Malloc(size:=PdoCfg.SourcePathLen);
    if pHelp^.PdoInfo.pSourcePath = NIL then
      SetSafetyState(NewState:=_MemAllocFailed);
      retcode := -1;
      return;
    end_if;

    _memcpy(ptr1:=pHelp^.PdoInfo.pSourcePath, ptr2:=PdoCfg.pSourcePath, cntr:=PdoCfg.SourcePathLen);

    // allocate memory for the module source path
    pHelp^.PdoInfo.pDestPath$pVoid := ToStdLib.Malloc(size:=PdoCfg.DestPathLen);
    if pHelp^.PdoInfo.pDestPath = NIL then
      SetSafetyState(NewState:=_MemAllocFailed);
      retcode := -1;
      return;
    end_if;

    _memcpy(ptr1:=pHelp^.PdoInfo.pDestPath, ptr2:=PdoCfg.pDestPath, cntr:=PdoCfg.DestPathLen);

    pHelp^.PdoInfo.Info           := PdoCfg.Info;
    pHelp^.PdoInfo.InterfaceCRC   := PdoCfg.InterfaceCRC;
    pHelp^.PdoInfo.SourcePathLen  := PdoCfg.SourcePathLen;
    pHelp^.PdoInfo.DestPathLen    := PdoCfg.DestPathLen;
    // length is only zero for non-interface pdos
    if PdoCfg.Info.IsInterfacePDO = FALSE then
      pHelp^.PdoInfo.PdoLen       := 0;   // the length is determined later in method CreateAndSendCfg
    else
      pHelp^.PdoInfo.PdoLen       := PdoCfg.PdoLen;
    end_if;
    pHelp^.PdoInfo.PDO_ID         := 255; // the ID is assigned later by the safety manager of the master domain
    pHelp^.pNext                  := NIL;

    PdoCfgNo += 1;
  else
    SetSafetyState(NewState:=_MemAllocFailed);
    retcode := -1;
    return;
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddSlaveToCfg
  VAR_INPUT
    pModule 	: ^SafetyBaseFSoE::PathInfoType;
    b_IsOptional 	: BOOL;
    b_IsInterface 	: BOOL;(* := FALSE *)
    InterfaceCRC 	: HDINT;(* := 0 *)
  END_VAR
  VAR
    pHelp : ^t_SlaveListElem;
  END_VAR

  if pFirstSlaveCfg = NIL then
    pFirstSlaveCfg$pVoid := ToStdLib.Malloc(size:=sizeof(t_SlaveListElem));
    pHelp := pFirstSlaveCfg;
  else
    pHelp := pFirstSlaveCfg;
    while pHelp^.pNext <> NIL do
      //if searched path is found, we already got this module in our list
      if (pModule^.ui_PathCompLen = pHelp^.SlaveInfo.CompPathLen) &
        (ToStdLib.MemCmp(dest:= pModule^.p_us_PathComp, source:= pHelp^.SlaveInfo.pCompPath , size:= pModule^.ui_PathCompLen) = 0) then
        // already in buffer => leave function
        return;
      else
        pHelp := pHelp^.pNext;
      end_if;
    end_while;

    //if searched path is found, we already got this module in our list
    if (pModule^.ui_PathCompLen = pHelp^.SlaveInfo.CompPathLen) &
      (ToStdLib.MemCmp(dest:= pModule^.p_us_PathComp, source:= pHelp^.SlaveInfo.pCompPath , size:= pModule^.ui_PathCompLen) = 0) then
      // already in buffer => leave function
      return;
    end_if;
    
    pHelp^.pNext$pvoid := ToStdLib.Malloc(size:=sizeof(t_SlaveListElem));
    pHelp := pHelp^.pNext;
  end_if;
  
  if pHelp <> NIL then
    // allocate memory for the module path
    pHelp^.SlaveInfo.PathLen := pModule^.p_us_Path^$UINT + 2;
    pHelp^.SlaveInfo.pPath$pVoid := ToStdLib.Malloc(size:=pHelp^.SlaveInfo.PathLen);
    if pHelp^.SlaveInfo.pPath = NIL then
      SetSafetyState(NewState:=_MemAllocFailed);
      LogError("@0243 (SafetyBaseFSoE::AddSlaveToCfg) Error: Failed to allocate memory for the module path.");
      return;
    end_if;

    _memcpy(ptr1:=pHelp^.SlaveInfo.pPath, ptr2:=pModule^.p_us_Path, cntr:=pHelp^.SlaveInfo.PathLen);

    // allocate memory for the module path
    pHelp^.SlaveInfo.CompPathLen := pModule^.ui_PathCompLen;
    pHelp^.SlaveInfo.pCompPath$pVoid := ToStdLib.Malloc(size:=pHelp^.SlaveInfo.CompPathLen);
    if pHelp^.SlaveInfo.pCompPath = NIL then
      SetSafetyState(NewState:=_MemAllocFailed);
      LogError("@0244 (SafetyBaseFSoE::AddSlaveToCfg) Error: Failed to allocate memory for the compressed module path.");
      return;
    end_if;

    _memcpy(ptr1:=pHelp^.SlaveInfo.pCompPath, ptr2:=pModule^.p_us_PathComp, cntr:=pHelp^.SlaveInfo.CompPathLen);

    pHelp^.SlaveInfo.Info             := 0;
    pHelp^.SlaveInfo.Info.IsOptional  := b_IsOptional;
    pHelp^.SlaveInfo.Info.IsInterface := b_IsInterface;

    pHelp^.SlaveInfo.InterfaceCRC := InterfaceCRC;

    pHelp^.pNext                  := NIL;
    
    SlaveCfgNo += 1;
  else
    SetSafetyState(NewState:=_MemAllocFailed);
    LogError("@0243 (SafetyBaseFSoE::AddSlaveToCfg) Error: Failed to allocate memory for the new list-element.");
    return;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::FreeCfgLists
  VAR
    pHelpModule : ^t_ModuleListElem;
    pHelpPdo : ^t_PdoListElem;
    pHelpSlave : ^t_SlaveListElem;
//    pHelpUnsafeVar : ^t_UnsafeVarListElem;
  END_VAR

  // while we got module cfgs 
  while pFirstModuleCfg do
    pHelpModule := pFirstModuleCfg;
    pFirstModuleCfg := pFirstModuleCfg^.pNext;
    
    if pHelpModule^.ModuleInfo.pDestCRCs then
      ToStdLib.Free(pHelpModule^.ModuleInfo.pDestCRCs);
    end_if;
    ToStdLib.Free(pHelpModule^.ModuleInfo.pPath);
    ToStdLib.Free(pHelpModule^.ModuleInfo.pCompPath);
    ToStdLib.Free(pHelpModule);
  end_while;
  pFirstModuleCfg := NIL;
  ModuleCfgNo := 0;

  // while we got pdo cfgs
  while pFirstPdoCfg do
    pHelpPdo := pFirstPdoCfg;
    pFirstPdoCfg := pFirstPdoCfg^.pNext;
    
    ToStdLib.Free(pHelpPdo^.PdoInfo.pSourcePath);
    ToStdLib.Free(pHelpPdo^.PdoInfo.pDestPath);
    ToStdLib.Free(pHelpPdo);
  end_while;
  pFirstPdoCfg := NIL;
  PdoCfgNo := 0;

  // while we got slave cfgs 
  while pFirstSlaveCfg do
    pHelpSlave := pFirstSlaveCfg;
    pFirstSlaveCfg := pFirstSlaveCfg^.pNext;
    
    ToStdLib.Free(pHelpSlave^.SlaveInfo.pPath);
    ToStdLib.Free(pHelpSlave^.SlaveInfo.pCompPath);
    ToStdLib.Free(pHelpSlave);
  end_while;
  pFirstSlaveCfg := NIL;
  SlaveCfgNo := 0;

  FreeFSoEConnectionInfos();

// memory is freed by safety manager
//  // while we got unsafevar cfgs
//  while pFirstUnsafeCfg do
//    pHelpUnsafeVar := pFirstUnsafeCfg;
//    pFirstUnsafeCfg := pFirstUnsafeCfg^.pNext;
//    
//    ToStdLib.Free(pHelpUnsafeVar^.UnsafeVarInfo.pName);
//    ToStdLib.Free(pHelpUnsafeVar);
//  end_while;
//  pFirstUnsafeCfg := NIL;
//  UnsafeCfgNo := 0;
  
  // reduze size of config mem used to 4 (only safety number of cpu module)
  ConfigMemSizeUsed := 4;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::CreateAndSendCfg
  VAR
    pHelpModule       : ^t_ModuleListElem;
    pHelpPdo          : ^t_PdoListElem;
    pHelpSlave        : ^t_SlaveListElem;
    pHelpConnection   : ^t_FSoEConnectionDetails;
    pCfg              : ^void;
    TmpPathLen        : UINT;
    MyPara 	          : CmdStruct;
    MyResult	        : results;
    AddConfigSize     : UDINT;
    i                 : UDINT;
  END_VAR

(*
    Memory structure:
      4 Byte: SafetyNumber of CPU (is already there from initialisation in CSCP011_IM::InitSafeModule)
      1 Byte: Number of module configurations which are following
      for every module configuration (first is always the CPU itself)
        1 Byte: Info
        4 Byte: Interface Frame Source SafetyNumber (0 if it is no source for interface frames)
        4 Byte: Interface Frame CRC
        1 Byte: Number of destination inferface frame CRCs
        x Byte: Destination interface frame CRCs (number * sizeof(HDINT)
        2 Byte: Length of hardware path
        x Byte (depending on length in the 2 byte before): Hardwarepath
        2 Byte: Length of compressed hardware path
        x Byte (depending on length in the 2 byte before): Compressed Hardwarepath
        1 Byte: PDO out length for this module
      1 Byte: Number of pdo configurations which are following
      for every PDO configuration
        1 Byte: PDO length
        1 Byte: Interface frame info
        4 Byte: Interface CRC
        2 Byte: Length of source hardware path
        x Byte (depending on length in the 2 byte before): Hardwarepath of source
        2 Byte: Length of destination hardware path
        x Byte (depending on length in the 2 byte before): Hardwarepath of destination       
      1 Byte: Number of slave configurations which are following
      for every slave configuration
        1 Byte: Info
        4 Byte: InterfaceCRC
        2 Byte: Length of hardware path
        x Byte (depending on length in the 2 byte before): Hardwarepath
        2 Byte: Length of compressed hardware path
        x Byte (depending on length in the 2 byte before): Compressed Hardwarepath
      2 Byte: Number of connection infos which are following
      for every FSoE connection info
        2 Byte: ConnectionID
        1 Byte: ConnectionState
        1 Byte: Flags // Actually, type is 4 bytes in size but for compatibility reasons it is still used as 1 byte. As a result, bits 9-32 are currently not used.
        4 Byte: Master safety number

only if feature available:        
        2 Byte: Transmission time in ms

        2 Byte: Offset where to read from master
        2 Byte: Size of data to read from master (and write to slave)
        2 Byte: Offset where to write to master
        2 Byte: Size of data to write to master (and read from slave)
        2 Byte: SlaveAddress
        2 Byte: Length of slave hardware path
        x Byte (depending on length in the 2 byte before): Slave hardware path
        2 Byte: Length of compressed slave hardware path
        x Byte (depending on length in the 2 byte before): Compressed slave hardware path
        2 Byte: Offset where to read from slave
        2 Byte: Offset where to write to slave
*)

  // check if there is memory and if the only thing inside is the safety number
  if pConfigMem & (ConfigMemSizeUsed = 4) then
    // insert the safety number
    pConfigMem^$UDINT := SafetyNumber;
  
    // pCfg points to the next memory we have to write to => set it to the memory after the safety number
    pCfg := pConfigMem + ConfigMemSizeUsed;
        
    // add all module configurations to memory
    if pFirstModuleCfg then
      // add number of module configurations to memory (no need to check the memory here, because already 512 bytes are allocated)
      pCfg^$USINT       := ModuleCfgNo;
      pCfg              += sizeof(USINT);
      ConfigMemSizeUsed += sizeof(USINT);

      pHelpModule := pFirstModuleCfg;
      // go through list
      while pHelpModule do
        // add the elements
        
        // check if we have enough memory to add the next module
        AddConfigSize := sizeof(BSINT) + sizeof(HDINT) + sizeof(HDINT) + sizeof(UINT) + pHelpModule^.ModuleInfo.PathLen + sizeof(UINT) + pHelpModule^.ModuleInfo.CompPathLen + sizeof(USINT);
        if pHelpModule^.ModuleInfo.Info.IsInterfaceDest then
          AddConfigSize += sizeof(USINT) + pHelpModule^.ModuleInfo.DestCRCNo * sizeof(HDINT);
        end_if;

        pCfg := CheckConfigMemSize(AddConfigSize);
        if pCfg = NIL then
          // error message already logged by function
          return;
        end_if;

        // info
        pCfg^$BSINT := pHelpModule^.ModuleInfo.Info;
        pCfg              += sizeof(BSINT);
        ConfigMemSizeUsed += sizeof(BSINT);

        // safety number of interface frame source (if it is one)
        pCfg^$HDINT := pHelpModule^.ModuleInfo.SafetyNbr;
        pCfg              += sizeof(HDINT);
        ConfigMemSizeUsed += sizeof(HDINT);

        // interface CRC of interface frame
        pCfg^$HDINT := pHelpModule^.ModuleInfo.InterfaceCRC;
        pCfg              += sizeof(HDINT);
        ConfigMemSizeUsed += sizeof(HDINT);

        // if its also interface frame destination send the corresponding interface CRCs
        if pHelpModule^.ModuleInfo.Info.IsInterfaceDest then
          TmpPathLen := pHelpModule^.ModuleInfo.DestCRCNo; // save some time with this local variable
          pCfg^$USINT := TmpPathLen$USINT;
          pCfg              += sizeof(USINT);
          ConfigMemSizeUsed += sizeof(USINT);
       
          _memcpy(ptr1:=pCfg, ptr2:=pHelpModule^.ModuleInfo.pDestCRCs, cntr:=TmpPathLen * sizeof(HDINT));
          pCfg += TmpPathLen * sizeof(HDINT);
          ConfigMemSizeUsed += TmpPathLen * sizeof(HDINT);
        end_if;

        // uncompressed module path
        TmpPathLen := pHelpModule^.ModuleInfo.PathLen; // save some time with this local variable
        pCfg^$UINT := TmpPathLen;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
      
        _memcpy(ptr1:=pCfg, ptr2:=pHelpModule^.ModuleInfo.pPath, cntr:=TmpPathLen);
        pCfg += TmpPathLen;
        ConfigMemSizeUsed += TmpPathLen;

        // compressed module path
        TmpPathLen := pHelpModule^.ModuleInfo.CompPathLen; // save some time with this local variable
        pCfg^$UINT := TmpPathLen;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
      
        _memcpy(ptr1:=pCfg, ptr2:=pHelpModule^.ModuleInfo.pCompPath, cntr:=TmpPathLen);
        pCfg += TmpPathLen;
        ConfigMemSizeUsed += TmpPathLen;
      
        // PDO out length for this module (to assign the length to the pdo configurations)
        pCfg^$USINT := pHelpModule^.ModuleInfo.PdoOutLen;
        pCfg              += sizeof(USINT);
        ConfigMemSizeUsed += sizeof(USINT);
      
        pHelpModule := pHelpModule^.pNext;
      end_while;

      pCfg := CheckConfigMemSize(sizeof(USINT));
      if pCfg = NIL then
        // error message already logged by function
        return;
      end_if;

      // add number of pdo configurations to memory
      pCfg^$USINT := PdoCfgNo;
      pCfg              += sizeof(USINT);
      ConfigMemSizeUsed += sizeof(USINT);

      // add all pdo configurations to memory (module configs must be available, to find out the PDO length)
      if pFirstPdoCfg then
        pHelpPdo := pFirstPdoCfg;
        // go through list
        while pHelpPdo do
          // add the elements

          // check if we have enough memory to add the next module
          AddConfigSize := sizeof(USINT) + sizeof(BSINT) + sizeof(HDINT) + sizeof(UINT) + pHelpPdo^.PdoInfo.SourcePathLen + sizeof(UINT) + pHelpPdo^.PdoInfo.DestPathLen;
          pCfg := CheckConfigMemSize(AddConfigSize);
          if pCfg = NIL then
            // error message already logged by function
            return;
          end_if;

          // first of all we have to find out the pdo length of this element => search the module configurations until we find our source and copy the pdo length
          if pHelpPdo^.PdoInfo.PdoLen = 0 then
            pHelpModule := pFirstModuleCfg;
            // go through list
            while pHelpModule do
              // if the path of the source module matches, save the pdolength of this module
              if (pHelpPdo^.PdoInfo.SourcePathLen = pHelpModule^.ModuleInfo.PathLen) & (ToStdLib.MemCmp(dest:= pHelpPdo^.PdoInfo.pSourcePath, source:= pHelpModule^.ModuleInfo.pPath , size:= pHelpPdo^.PdoInfo.SourcePathLen) = 0) then
                pHelpPdo^.PdoInfo.PdoLen := pHelpModule^.ModuleInfo.PdoOutLen;
                exit;
              end_if;
              pHelpModule := pHelpModule^.pNext;
            end_while;
          end_if;

          
          // PDO Length
          pCfg^$USINT := pHelpPdo^.PdoInfo.PdoLen;
          pCfg              += sizeof(USINT);
          ConfigMemSizeUsed += sizeof(USINT);

          // info
          pCfg^$BSINT := pHelpPdo^.PdoInfo.Info;
          pCfg              += sizeof(BSINT);
          ConfigMemSizeUsed += sizeof(BSINT);

          // interface CRC
          pCfg^$HDINT := pHelpPdo^.PdoInfo.InterfaceCRC;
          pCfg              += sizeof(HDINT);
          ConfigMemSizeUsed += sizeof(HDINT);

          // Source Path Length
          TmpPathLen := pHelpPdo^.PdoInfo.SourcePathLen; // save some time with this local variable
          pCfg^$UINT := TmpPathLen;
          pCfg              += sizeof(UINT);
          ConfigMemSizeUsed += sizeof(UINT);
        
          // Source Path
          _memcpy(ptr1:=pCfg, ptr2:=pHelpPdo^.PdoInfo.pSourcePath, cntr:=TmpPathLen);
          pCfg += TmpPathLen;
          ConfigMemSizeUsed += TmpPathLen;

          // Destination Path Length
          TmpPathLen := pHelpPdo^.PdoInfo.DestPathLen; // save some time with this local variable
          pCfg^$UINT := TmpPathLen;
          pCfg              += sizeof(UINT);
          ConfigMemSizeUsed += sizeof(UINT);
        
          // Destination Path
          _memcpy(ptr1:=pCfg, ptr2:=pHelpPdo^.PdoInfo.pDestPath, cntr:=TmpPathLen);
          pCfg += TmpPathLen;
          ConfigMemSizeUsed += TmpPathLen;
        
          pHelpPdo := pHelpPdo^.pNext;
        end_while;
      end_if;

      pCfg := CheckConfigMemSize(sizeof(USINT));
      if pCfg = NIL then
        // error message already logged by function
        return;
      end_if;

      // add number of slave configurations to memory
      pCfg^$USINT       := SlaveCfgNo;
      pCfg              += sizeof(USINT);
      ConfigMemSizeUsed += sizeof(USINT);

      pHelpSlave := pFirstSlaveCfg;
      // go through list
      while pHelpSlave do
        // add the elements
        
        // check if we have enough memory to add the next module
        AddConfigSize := sizeof(BSINT) + sizeof(HDINT) + sizeof(UINT) + pHelpSlave^.SlaveInfo.PathLen + sizeof(UINT) + pHelpSlave^.SlaveInfo.CompPathLen;

        pCfg := CheckConfigMemSize(AddConfigSize);
        if pCfg = NIL then
          // error message already logged by function
          return;
        end_if;

        // info
        pCfg^$BSINT := pHelpSlave^.SlaveInfo.Info;
        pCfg              += sizeof(BSINT);
        ConfigMemSizeUsed += sizeof(BSINT);

        // InterfaceCRC
        pCfg^$HDINT := pHelpSlave^.SlaveInfo.InterfaceCRC;
        pCfg              += sizeof(HDINT);
        ConfigMemSizeUsed += sizeof(HDINT);

        // uncompressed Slave path
        TmpPathLen := pHelpSlave^.SlaveInfo.PathLen; // save some time with this local variable
        pCfg^$UINT := TmpPathLen;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
      
        _memcpy(ptr1:=pCfg, ptr2:=pHelpSlave^.SlaveInfo.pPath, cntr:=TmpPathLen);
        pCfg += TmpPathLen;
        ConfigMemSizeUsed += TmpPathLen;

        // compressed Slave path
        TmpPathLen := pHelpSlave^.SlaveInfo.CompPathLen; // save some time with this local variable
        pCfg^$UINT := TmpPathLen;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
      
        _memcpy(ptr1:=pCfg, ptr2:=pHelpSlave^.SlaveInfo.pCompPath, cntr:=TmpPathLen);
        pCfg += TmpPathLen;
        ConfigMemSizeUsed += TmpPathLen;
      
        pHelpSlave := pHelpSlave^.pNext;
      end_while;

    else
      // add number of module, pdo and slave configurations to memory (all as 0)
      
      pCfg := CheckConfigMemSize(3*sizeof(USINT));
      if pCfg = NIL then
        // error message already logged by function
        return;
      end_if;

      // no module configs
      pCfg^$USINT := 0;
      pCfg              += sizeof(USINT);
      ConfigMemSizeUsed += sizeof(USINT);

      // no pdo configs
      pCfg^$USINT := 0;
      pCfg              += sizeof(USINT);
      ConfigMemSizeUsed += sizeof(USINT);

      // no slave configs
      pCfg^$USINT := 0;
      pCfg              += sizeof(USINT);
      ConfigMemSizeUsed += sizeof(USINT);
    end_if;    

    pCfg := CheckConfigMemSize(sizeof(UINT));
    if pCfg = NIL then
      // error message already logged by function
      return;
    end_if;

    // number of connection details
    pCfg^$UINT := ConnectionInfos.ConnectionNo$UINT;
    pCfg              += sizeof(UINT);
    ConfigMemSizeUsed += sizeof(UINT);

    if ConnectionInfos.ConnectionNo then

      pHelpConnection := ConnectionInfos.pFirstConnection;
      for i := 0 to (ConnectionInfos.ConnectionNo-1) do
        // add the elements

        // check if we have enough memory to add the next connection
        AddConfigSize := sizeof(UINT) + sizeof(USINT) + sizeof(USINT) + bdFeatureEnable.FSoEFlags_4ByteSize * 3 * sizeof(USINT) + sizeof(HDINT) +
                         6 * sizeof(UINT) + pHelpConnection^.SlavePathLen + sizeof(UINT) + pHelpConnection^.SlavePathCompLen + 2 * sizeof(UINT);

        if bdFeatureEnable.TransmissionTimeInConfig then
          AddConfigSize += sizeof(UINT);
        end_if;

        pCfg := CheckConfigMemSize(AddConfigSize);
        if pCfg = NIL then
          // error message already logged by function
          return;
        end_if;

      
        // connection ID
        pCfg^$UINT        := pHelpConnection^.ConnectionID;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);

        // connection state
        pCfg^$USINT       := pHelpConnection^.ConnectionState$USINT;
        pCfg              += sizeof(USINT);
        ConfigMemSizeUsed += sizeof(USINT);

        // flags
        // In older Safety manager versions this flag is only used as 1 byte. Now we use 4 byte if safetymanager supports this
        if bdFeatureEnable.FSoEFlags_4ByteSize then
          pCfg^$t_FSoEFlags := pHelpConnection^.Flags;
          pCfg              += sizeof(t_FSoEFlags);
          ConfigMemSizeUsed += sizeof(t_FSoEFlags);
        else
          pCfg^$USINT       := pHelpConnection^.Flags$USINT;
          pCfg              += sizeof(USINT);
          ConfigMemSizeUsed += sizeof(USINT);
        end_if;

        // master safety number
        pCfg^$HDINT       := pHelpConnection^.OwnerSafetyNbr;
        pCfg              += sizeof(HDINT);
        ConfigMemSizeUsed += sizeof(HDINT);

        // the feature is not available in older safety manager classes, so we have to do this for compatibility reasons
        if bdFeatureEnable.TransmissionTimeInConfig then
          // transmission time settings from safety designer
          pCfg^$UINT        := pHelpConnection^.TransmissionTime_ms;
          pCfg              += sizeof(UINT);
          ConfigMemSizeUsed += sizeof(UINT);
        end_if;

        // offset where to read from master
        pCfg^$UINT        := pHelpConnection^.MasterReadOffset;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);

        // size of data to read from master (and write to slave)
        pCfg^$UINT        := pHelpConnection^.MasterReadSize;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);

        // offset where to write to master
        pCfg^$UINT        := pHelpConnection^.MasterWriteOffset;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);

        // size of data to write to master (and read from slave)
        pCfg^$UINT        := pHelpConnection^.MasterWriteSize;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
        
        // slave address
        pCfg^$UINT        := pHelpConnection^.SlaveAddress;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
        
        // uncompressed slave path
        TmpPathLen        := pHelpConnection^.SlavePathLen; // save some time with this local variable
        pCfg^$UINT        := TmpPathLen;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
      
        _memcpy(ptr1:=pCfg, ptr2:=pHelpConnection^.pSlavePath, cntr:=TmpPathLen);
        pCfg              += TmpPathLen;
        ConfigMemSizeUsed += TmpPathLen;

        // compressed slave path
        TmpPathLen        := pHelpConnection^.SlavePathCompLen; // save some time with this local variable
        pCfg^$UINT        := TmpPathLen;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
      
        _memcpy(ptr1:=pCfg, ptr2:=pHelpConnection^.pSlavePathComp, cntr:=TmpPathLen);
        pCfg              += TmpPathLen;
        ConfigMemSizeUsed += TmpPathLen;
        
        // offset where to read from slave
        pCfg^$UINT        := pHelpConnection^.SlaveReadOffset;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);

        // offset where to write to slave
        pCfg^$UINT        := pHelpConnection^.SlaveWriteOffset;
        pCfg              += sizeof(UINT);
        ConfigMemSizeUsed += sizeof(UINT);
      
        pHelpConnection += sizeof(t_FSoEConnectionDetails);
      end_for;    
    end_if;  


    MyPara.uiCmd := CMD_SM_FSOE_CFG_TO_MANAGER;
    MyPara.aPara[0] := ConfigMemSizeUsed$DINT;
    MyPara.aPara[1] := pConfigMem$DINT;
    if pSafetyManagerThis^.NewInst(#MyPara, #MyResult) = READY then
      // the manager takes care of our memory from now on
      pFirstSlaveCfg := NIL;
    end_if;
    
    // now tell the manager, what kind of configuration we have
    MyPara.uiCmd := CMD_SM_NEW_CFG_TYPE;
    MyPara.aPara[0] := SafetyNumber$DINT;
    MyPara.aPara[1] := NewConfigType;
    pSafetyManagerThis^.NewInst(#MyPara, #MyResult);
  else
    LogError("@0245 (SafetyBaseFSoE::CreateAndSendCfg) No config memory or invalid memory size usage.");
  end_if;

END_FUNCTION

#pragma warning(disable: 73)
FUNCTION VIRTUAL SafetyBaseFSoE::CheckVariant
  VAR_INPUT
    us_ActVariant 	: USINT;
  END_VAR
  VAR_OUTPUT
    b_IsOK 	: BOOL;
  END_VAR

  // if the overwritten methods are getting deleted, we already have a code for compatiblity here
  b_IsOK := TRUE; // FALSE;

END_FUNCTION
#pragma warning(default: 73)

FUNCTION SafetyBaseFSoE::SendAllUnsafeValues
  VAR
    pHelpUnsafeOut : ^UnsafeIORoutingElement;
  END_VAR

  // mark all unsafe values as not sent
  pHelpUnsafeOut := p_UnsafeInputsTable;
  while pHelpUnsafeOut do
    pHelpUnsafeOut^.b_AlreadyTransmitted := FALSE;
    
    pHelpUnsafeOut := pHelpUnsafeOut^.pNext;
  end_while;
  
  // reset the actual pointer
  pUnsafeInElement := NIL;

END_FUNCTION


FUNCTION SafetyBaseFSoE::GetDestinationCRCs
  VAR
    pHelpModule : ^t_ModuleListElem;
    pHelpPdo : ^t_PdoListElem;
  END_VAR

  // go through the module list
  pHelpModule := pFirstModuleCfg;
  while pHelpModule do
    pHelpPdo := pFirstPdoCfg;
    while pHelpPdo do
      // search in the PDOs if this module is an interface frame destination somewhere
      if pHelpPdo^.PdoInfo.Info.IsInterfacePDO then 
        if (pHelpPdo^.PdoInfo.DestPathLen = pHelpModule^.ModuleInfo.PathLen) &
         (ToStdLib.MemCmp(dest:= pHelpModule^.ModuleInfo.pPath, source:= pHelpPdo^.PdoInfo.pDestPath , size:= pHelpModule^.ModuleInfo.PathLen) = 0) then
          pHelpModule^.ModuleInfo.Info.IsInterfaceDest := TRUE;
          if pHelpModule^.ModuleInfo.DestCRCNo & pHelpModule^.ModuleInfo.pDestCRCs then
            pHelpModule^.ModuleInfo.pDestCRCs$^void := ToStdLib.ReAlloc(mptr:=pHelpModule^.ModuleInfo.pDestCRCs, newsize:=(pHelpModule^.ModuleInfo.DestCRCNo+1) * sizeof(HDINT));
          else
            pHelpModule^.ModuleInfo.pDestCRCs$^void := ToStdLib.Malloc(sizeof(HDINT));
          end_if;
          
          (pHelpModule^.ModuleInfo.pDestCRCs + pHelpModule^.ModuleInfo.DestCRCNo * sizeof(HDINT))^ := pHelpPdo^.PdoInfo.InterfaceCRC;
          
          pHelpModule^.ModuleInfo.DestCRCNo += 1;
        end_if;
      else
        // if this is module is optional and it's the source of the PDO => the pdo is also optional
        if pHelpModule^.ModuleInfo.Info.IsOptional & (pHelpPdo^.PdoInfo.SourcePathLen = pHelpModule^.ModuleInfo.PathLen) &
         (ToStdLib.MemCmp(dest:= pHelpModule^.ModuleInfo.pPath, source:= pHelpPdo^.PdoInfo.pSourcePath , size:= pHelpModule^.ModuleInfo.PathLen) = 0) then
          pHelpPdo^.PdoInfo.Info.IsOptional := TRUE;
        end_if;
      end_if;
          
      pHelpPdo := pHelpPdo^.pNext;
    end_while;
  
    pHelpModule := pHelpModule^.pNext;
  end_while;

END_FUNCTION


FUNCTION SafetyBaseFSoE::CountBits
  VAR_INPUT
    BitMask 	: BDINT;
  END_VAR
  VAR_OUTPUT
    BitNo 	: UDINT;
  END_VAR
  
  BitNo := 0;
  
  while BitMask do
    BitMask := BitMask AND (BitMask - 1);
    BitNo += 1;
  end_while;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::IsOptimizedCPU
  VAR_OUTPUT
    IsOptimized 	: DINT;
  END_VAR

  // every derivation of this class is an optimized cpu, which means the SBF_SDO_COMMAND_WRITE_VALUES command is available to save time in communication of unsafe variables
  IsOptimized := TRUE;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::CheckConfigMemSize
  VAR_INPUT
    AddConfigSize 	: UDINT;
  END_VAR
  VAR_OUTPUT
    pCfg 	: ^void;
  END_VAR
  VAR
    pTempMem  : ^void;
  END_VAR

  pCfg := NIL;
  
  if ((ConfigMemSizeUsed + AddConfigSize) > ConfigMemSize) then
    // if there isn't enough memory => reallocate
    pTempMem := ToStdLib.ReAlloc(mptr:=pConfigMem, newsize:=GetNewMemSize(ConfigMemSizeUsed + AddConfigSize));
    IF pTempMem <> NIL THEN
      pConfigMem := pTempMem;
      ConfigMemSize := GetNewMemSize(ConfigMemSizeUsed + AddConfigSize);
    ELSE
      SetSafetyState(NewState:=_MemAllocFailed);
      LogError("@0246 (SafetyBaseFSoE::CreateAndSendCfg) Failed to reallocate configuration memory!");
      return;
    END_IF;
  end_if;

  pCfg := pConfigMem + ConfigMemSizeUsed;
          
END_FUNCTION


FUNCTION SafetyBaseFSoE::DToH
  VAR_INPUT
    pt 	: ^CHAR;
    value 	: DINT;
  END_VAR
  VAR
    val  : UDINT;
  END_VAR

  // this method converts a single byte into a zero-terminated hexstring like 0xA4 => "A4"
  val := value$UDINT;

  // zero termination
  (pt + 2)^ := 0;

  // start with the low nibble
  if (val AND 16#F) < 10 then
    (pt + 1)^ := (val AND 16#F)$USINT + '0';
  else
    (pt + 1)^ := (val AND 16#F)$USINT - 10 + 'A';
  end_if;

  // now get the high nibble
  val := val / 16;

  if (val AND 16#F) < 10 then
    pt^ := (val AND 16#F)$USINT + '0';
  else
    pt^ := (val AND 16#F)$USINT - 10 + 'A';
  end_if;
  
END_FUNCTION


FUNCTION SafetyBaseFSoE::LogHWPath
  VAR_INPUT
    pString 	: ^CHAR;
    pHWPath 	: ^USINT;
  END_VAR
  VAR
    TempStr       : ARRAY [0..99] OF CHAR;
    TraceVal      : USINT;
    i             : UINT;
    MyMsg         : ARRAY [0..599] OF CHAR;
  END_VAR

  if pString & pHWPath then

    _strncpy(dest:=#MyMsg[0], src:=pString, max:=200);
  
    TraceVal := pHWPath^;
    if TraceVal then
      TraceVal += 2; //show also the length
      i := 0;
      while (TraceVal >= 1) & (i < 95) do
        DToH(#TempStr[i], pHWPath^$USINT);
        i += 2; // 2 chars per byte
        TempStr[i] := ' ';
        i += 1;
        pHWPath += 1;
        TraceVal -= 1;
      end_while;
      TempStr[i] := 0;

      _strcat( dest:=#MyMsg[0], src:=#TempStr[0]);
    end_if;

#ifdef HWC_LogError
    HWC_LogError(this, #MyMsg[0]);
#endif
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::QuitComError::Write
  VAR_INPUT
    input (EAX) 	: DINT;
  END_VAR
  VAR_OUTPUT
    result (EAX) 	: DINT;
  END_VAR

  result := input;

  // if there is an error
  if ErrorState then
    // try to quit it
    QuitError(Mode:=0);
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::ClassSvr::NewInst
  VAR_INPUT
    pPara 	: ^CmdStruct;
    pResult 	: ^results;
  END_VAR
  VAR_OUTPUT
    ret_code 	: iprStates;
  END_VAR
  VAR
    OldClassState : SafetyConfigStateType;
    pPDOInputBuffer : ^PDOInputBuffer;
    i, j : UINT;
    Found : BOOL;
    sd_retval : DINT;
    GetStateResp   : GetStateResponseType;
    FoundNo : UINT;
    ud_p2BufferCompare : UDINT;
    LowestFoundidx : UINT;
  END_VAR

  ret_code := READY;

  CASE pPara^.uiCmd OF
//**********************************************************************************************************************************************************
    CMD_SM_INIT_FINISHED:
      // the safety manager has finished his administration work (e.g. connecting the domains)
      b_ManagerInitialisationFinished := TRUE;
      
      // optional modules may not be available
      ud_OptionalTimeoutStart := ops.tAbsolute;
      
      //If managerinitialisation is finished, we need to set the unsafe SSW correctly
      UnsafeScheduler := UnsafeSchedulerSetBusReady;      
      us_SetBusReadyState := WaitPDOsSyncron;      
      
//**********************************************************************************************************************************************************
    CMD_SM_FIRST_CONNECT_READY:
      // the safety manager has finished first time connecting the domains
      b_ManagerReadyForSDO := TRUE;

//**********************************************************************************************************************************************************
    CMD_SM_DISCONNECT:
      b_ManagerInitialisationFinished := FALSE;
      b_ManagerReadyForSDO := FALSE;
      
      // wait again to send bus ready signal
      if SafetyState <> _ReinitConfig then
        SetSafetyState(NewState:=_WaitForSynchronicity);
      end_if;
      UnsafeScheduler := UnsafeSchedulerSetBusReady;      
      us_SetBusReadyState := WaitPDOsSyncron;

//**********************************************************************************************************************************************************
    CMD_SM_SDO_TO_MODULE:
#ifdef DO_DEBUG
      DOCheck(pFrame:=(pPara^.aPara[1])$^t_SDOStdHeader, pCheckStruct:=#SDOWrite);
#endif

      AddSDOToBuffer(p_us_Message := pPara^.aPara[1]$^USINT, us_length := pPara^.aPara[0]$USINT);
                 
//**********************************************************************************************************************************************************
    CMD_SM_GET_SAFETY_NUMBER:
      // check if pointer is valid
      if pPara^.aPara[0] then
        ((pPara^.aPara[0])$^UDINT)^ := SafetyNumber;
      end_if;
//**********************************************************************************************************************************************************
    CMD_SM_ADD_INPUT_PDO:
      // check if the information if it's an interface frame (stored at pPara^.aPara[9]$BOOL) is valid  
      if pPara^.aPara[8]$HDINT = SM_IF_VALIDITY_MARK then
        sd_retval :=
        AddPDOToInputBuffer(ud_p2Buffer         := pPara^.aPara[0]$UDINT
                          , ud_SafetyNr         := pPara^.aPara[2]$UDINT
                          , b_IsOptional        := pPara^.aPara[4]$BOOL
                          , ui_PdoLen           := pPara^.aPara[3]$UINT
                          , ui_TargetOffset     := pPara^.aPara[7]$UINT
                          , b_IsInterfaceFrame  := pPara^.aPara[9]$BOOL
                          );
      else
        sd_retval :=
        AddPDOToInputBuffer(ud_p2Buffer         := pPara^.aPara[0]$UDINT
                          , ud_SafetyNr         := pPara^.aPara[2]$UDINT
                          , b_IsOptional        := pPara^.aPara[4]$BOOL
                          , ui_PdoLen           := pPara^.aPara[3]$UINT
                          , ui_TargetOffset     := pPara^.aPara[7]$UINT
                          );
      end_if;

      // if it is busy with sending the bus ready signal at the moment
      if sd_retval = -2 then
        // return that it's an error because it's busy
        ret_code := BUSY;
      elsif sd_retval then
        // otherwise just an error
        ret_code := ERROR;
      end_if;

//**********************************************************************************************************************************************************
    CMD_SM_ADD_OUTPUT_PDO:      
      // if it's an interface frame, we return the pointer to the second PDO frame which is the interface pdo frame
//      if pPara^.aPara[0] = 1 then
//        // skip the first PDO
//        pResult^.aData[0]$UDINT := GetPointer2IFPDOBuffer();    // pointer to local memory
//        pResult^.aData[4]$UDINT := ud_LocalMoveIFSourceAddr;    // pointer to DPRAM
//        pResult^.aData[8]$UDINT := ud_LocalIFReadHandle;        // handle for varan access
//        pResult^.aData[12]$UDINT:= ud_LocalIFReadOffset;        // offset inside the varan data
//      else
//        // otherwise just get the pointer to the PDO read data
//        pResult^.aData[0]$UDINT := GetPointer2PDOBuffer();      // pointer to local memory
//        pResult^.aData[4]$UDINT := ud_LocalMoveSourceAddr;      // pointer to DPRAM
//        pResult^.aData[8]$UDINT := ud_LocalReadHandle;          // handle for varan access
//        pResult^.aData[12]$UDINT:= ud_LocalReadOffset;          // offset insid the varan data
//      end_if;

      // return the pointer to the Buffer (is linear local memory, so the offset can be added later)
      pResult^.aData[0]$UDINT := GetPointer2PDOBuffer();
      pResult^.uiLng := 4;
      
//**********************************************************************************************************************************************************
    CMD_SM_REMOVE_INPUT_PDO:
      // we lost connection to other systems => we have to initialize again! remove old input PDOs
      
      if ui_cnt_PDOInputBuffer then
        // save the old state of the class and set it temporarily to "module not found" to prevent update rt from crashing
        OldClassState := SafetyState;
        SetSafetyState(NewState:=_ModuleNotFound);

        // search for the PDO
        FoundNo := 0; // FSoE Data may be split over several DOs
        pPDOInputBuffer := #aPDOInputBuffer[ui_cnt_PDOInputBuffer-1];
        ud_p2BufferCompare := pPara^.aPara[0]$UDINT;
        i := ui_cnt_PDOInputBuffer - 1;
        repeat
        
          if pPDOInputBuffer^.ud_p2Buffer = ud_p2BufferCompare then
            FoundNo += 1;
            LowestFoundidx := i;
            
            // Increment to Check if PDO is split over the following PDO buffer
            ud_p2BufferCompare += pPDOInputBuffer^.ui_PDOLen;
          
          elsif FoundNo > 0 then
            // Split PDO must be after each other in memory
            exit;
          end_if;
          
          pPDOInputBuffer -= sizeof(PDOInputBuffer);
          i -= 1;
        
        until pPDOInputBuffer < #aPDOInputBuffer[0] end_repeat;

        // if we found at least one PDO => remove them from memory (move the memory behind the found PDO over the place of the found PDO)
        if FoundNo then
          // if it's last entry (no entries have to be moved) => we only have to copy some memory if it's not the last
          if LowestFoundidx < (ui_cnt_PDOInputBuffer-FoundNo) then
            for j := LowestFoundidx to (ui_cnt_PDOInputBuffer-FoundNo-1) do
              aPDOInputBuffer[j] := aPDOInputBuffer[j+FoundNo];
            end_for;
          end_if;
          ui_cnt_PDOInputBuffer -= FoundNo;

          if ui_cnt_PDOInputBuffer = 0 then
            if ModuleSyncNo & (SafetyState <> _SafetyClassOK) then
              ModuleSyncNo -= 1;
            end_if;
          end_if;

//        else
//          LogError("@ZZZZ (SafetyBaseFSoE::State::NewInst) CMD_SM_REMOVE_INPUT_PDO failed! Couldn't find specified PDO!");
        end_if;

        // restore old class state
        SetSafetyState(NewState:=OldClassState);
//      else
//        LogError("@ZZZZ (SafetyBaseFSoE::State::NewInst) CMD_SM_REMOVE_INPUT_PDO failed! No input PDOs found!");
      end_if;


//*****************************************************************************
//** COMMANDS FROM BUS INTERFACE                                             **
//*****************************************************************************

//**********************************************************************************************************************************************************
    _BUS_IF_HWCONNECT :
      // device has been connected
      ConnectEvent();
      pResult^.aData[0] := _BUS_IF_RETVAL_OK;

//**********************************************************************************************************************************************************
    _BUS_IF_HWDISCONNECT :
      // device has been disconnected
      DisconnectEvent();
      pResult^.aData[0] := _BUS_IF_RETVAL_OK;

//**********************************************************************************************************************************************************
    _BUS_IF_HWINTERRUPT :
      // error at device which may lead to a disconnect or replace the disconnect callback
      InterruptEvent();
      pResult^.aData[0] := _BUS_IF_RETVAL_OK;

//**********************************************************************************************************************************************************
    _BUS_IF_CHECK_DEVICE_ID :
      // check for correct device id
      pResult^.aData[0] := (DeviceID = pPara^.aPara[0]$UDINT);
            
      DeviceIDOk := pResult^.aData[0]$BOOL;
      
//**********************************************************************************************************************************************************
    _BUS_IF_RESET_DETECTED:
      if SafetyState = _SafetyClassOK then
        SetSafetyState(NewState:=_LostPowerSupply);
      end_if;

//**********************************************************************************************************************************************************
    _BUS_IF_RESET_GONE_DETECTED:
      if SafetyState = _LostPowerSupply then
        SetSafetyState(NewState:=_SafetyClassOK);
      end_if;

//**********************************************************************************************************************************************************


//*****************************************************************************
//** COMMANDS FROM OTHER SAFETYMODULES                                       **
//*****************************************************************************

//**********************************************************************************************************************************************************
    CMD_SM_ADD_SDO_TO_BUFFER:
      sd_retval := AddSDOToBuffer(p_us_message:=pPara^.aPara[0]$^USINT, us_length:=pPara^.aPara[1]$USINT);
      if sd_retval then
        ret_code := ERROR;
      end_if;
      
      pResult^.aData[0]$DINT := sd_retval;
      
//**********************************************************************************************************************************************************
    CMD_SM_CONFIG_SAFETY_CPU:
      pResult^.aData[0]$UDINT := ConfigSafetyCpu();
      
//**********************************************************************************************************************************************************
    CMD_SM_CONFIG_SAFETY_INPUT:
      pResult^.aData[0]$UDINT := ConfigSafetyInput(ud_SafeNrCPU         := pPara^.aPara[0]$HDINT
                                                 , ud_thisp             := pPara^.aPara[1]$UDINT
                                                 , p_PDOBuffer          := pPara^.aPara[2]$^UDINT
                                                 );

//**********************************************************************************************************************************************************
    CMD_SM_CONFIG_SAFETY_OUTPUT:
      pResult^.aData[0]$UDINT := ConfigSafetyOutput(ud_SafeNrCPU        := pPara^.aPara[0]$HDINT
                                                  , ud_thisp            := pPara^.aPara[1]$UDINT
                                                  , ud_p_PDOBuffer      := pPara^.aPara[2]$UDINT
                                                  , pObject             := pPara^.aPara[3]$^PathInfoType
                                                  , b_IsOptional        := pPara^.aPara[6]$BOOL
                                                  );
                                                  
//**********************************************************************************************************************************************************
    CMD_SM_REMOVE_CPU_FROM_TABLE:
      RemoveCPUFromTable(ud_SafetyNr := pPara^.aPara[0]$HDINT);
      
//**********************************************************************************************************************************************************     
    CMD_SM_GET_MODULE_TYPE:
      pResult^.aData[0]$UDINT := SM_TYPE_SAFETY_MODULE_FSOE;
      
      
//**********************************************************************************************************************************************************     
    CMD_SM_MODULE_REINIT:
      if b_ReInitMarker then
        
        // next time we check for the configured state of the cpu
        b_CheckConfigured := TRUE;

        // check again in background
        SetSafetyState(NewState:=_ReinitConfig);
        
        b_ReInitMarker := FALSE;
      end_if;
      
//**********************************************************************************************************************************************************     
    CMD_SM_POST_INIT_FINISHED:
    
      // remember that we've been here
      b_PostInitPassed := TRUE;
    
      Found := FALSE;
      if Online then
        // get configuration if module is configured
        
        // get state via SDO communication
        sd_retval := SDOStdRequ ( us_Command      := SDO_COMMAND_GET_STATE
                                , p_us_RespData   := ( #GetStateResp )$^USINT
                                , us_RespLength   := sizeof(GetStateResponseType)
                                );
        
        // the cpu is configured, load the new configuration
        if ( sd_retval = 0 ) & 
           (GetStateResp.ConfigState <> CFG_STATE_UNKNOWN) &
           (GetStateResp.ConfigState <> CFG_STATE_INVALID) &
           (GetStateResp.ConfigState <> CFG_STATE_NOT_CONFIGURED) then
           
          //get configuration from safe CPU and build routing tables
          GetRoutingData();
          
          Found := TRUE;
        end_if;
      end_if;

      // if we didn't get the config here => try again later
      if Found = FALSE then
        //get configuration from safe CPU and build routing tables later in background
        b_GetNewConfiguration := FALSE;
        
          // next time we check for the configured state of the cpu
        b_CheckConfigured := TRUE;

        // check again in background
        SetSafetyState(NewState:=_ReinitConfig);
      end_if;

//**********************************************************************************************************************************************************
    CMD_SM_SET_NEW_SYNC_MODE:

//**********************************************************************************************************************************************************
    CMD_SM_UPDATE_SAFETY_NBR:
      // set new safety number for this routing element
      sd_retval :=
      UpdateSafetyNbrAtRoutingElement(PathLen         := pPara^.aPara[0]$UINT
                                    , pCompressedPath := pPara^.aPara[1]$^USINT
                                    , SafetyNbr       := pPara^.aPara[2]$HDINT
                                    );
                                    
      if sd_retval then
        ret_code := ERROR;
      end_if;
    
//**********************************************************************************************************************************************************
  else
    if bd_LogOnceMarker.InvalidNewInstCmd = FALSE then
      bd_LogOnceMarker.InvalidNewInstCmd := TRUE;
      LogValue("@024A (SafetyBaseFSoE::ClassSvr::NewInst) [SL] Received invalid command: 0x{0}", Value:=pPara^.uiCmd);
    end_if;
//**********************************************************************************************************************************************************
  END_CASE;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::Background
  VAR_INPUT
    EAX 	: UDINT;
  END_VAR
  VAR_OUTPUT
    state (EAX) 	: UDINT;
  END_VAR
  VAR
    sd_retval         : DINT;
    GetStateResp      : GetStateResponseType;
    LastWriteCounter  : UDINT;
    FileHandle        : DINT;
    MyPara 	          : CmdStruct;
    MyResult	        : results;
  END_VAR

  // if any LED is active, then firmware update is not active and we can do our work
  if (VoltageOK | RunState | ServiceMode | ErrorState) then    

    // if a quit error is pending
    if (QuitComError = 2) | (QuitComError = 3) then
      MyPara.uiCmd := CMD_SM_QUIT_ERROR_VIA_OS;
      MyPara.aPara[0] := THIS$DINT;
      MyPara.aPara[1] := SafetyNumber$DINT;

      MyPara.aPara[2] := SM_IF_VALIDITY_MARK$DINT;
      MyPara.aPara[3] := SM_QUIT_ERROR_CMD_VERSION;
      MyPara.aPara[4] := QuitErrorParameter;
      
      pSafetyManagerThis^.NewInst(#MyPara, #MyResult);
  
      QuitComError := MyResult.aData[0]$DINT;

      // refresh the server on the outer object
      RefreshAsySrv();
    end_if;

    // if we are reinitializing and we have a valid safety number
    if (SafetyState = _ReinitConfig) & SafetyNumber then
    
      // check every 3 seconds if the cpu is configured
      if b_CheckConfigured & ((ops.tAbsolute - ud_LastCheckConfig) > 3000) then
      
        // get state via SDO communication
        sd_retval := SDOStdRequ ( us_Command      := SBF_SDO_COMMAND_GET_STATE
                                , p_us_RespData   := ( #GetStateResp )$^USINT
                                , us_RespLength   := sizeof(GetStateResponseType)
                                );
        
        // the cpu is configured, load the new configuration
        if ( sd_retval = 0 ) & 
           (GetStateResp.ConfigState <> SBF_CFG_STATE_UNKNOWN) &
           (GetStateResp.ConfigState <> SBF_CFG_STATE_INVALID) &
           (GetStateResp.ConfigState <> SBF_CFG_STATE_NOT_CONFIGURED) then
           
          b_CheckConfigured := FALSE;
          
          b_GetNewConfiguration := TRUE;
        end_if;
        
        // remember the last time we checked for configurated cpu
        ud_LastCheckConfig := ops.tAbsolute;
      end_if;
        
      // if there is a new configuration => get it
      if b_GetNewConfiguration then
        // get configuration from safe CPU and build routing tables
        sd_retval := GetRoutingData();
        if sd_retval = 0 then
          b_GetNewConfiguration := FALSE;
          
          SendAllUnsafeValues();
        else
          LogError("@024B (SafetyBaseFSoE::Background) FAILED TO REINIT THE SAFECPU: .");
        end_if;
      end_if;
    elsif ConnectionInfos.ConnectionNo then
      // if there are still connections => check if they are initialized
      CheckFSoEConnections();
    end_if;

    // here we save the log file from the module to the harddrive (started by user)
    if DiagVars.SavingLogInProgress then
    
      case DiagVars.SavingLogSSW of

  // ********************************************************************************************************************************************
        GetLogHeader:     // get log header from module (contains size of log memory and write counter)

          // get log header via SDO communication
          sd_retval := SDORequ ( us_Command      := SBF_SDO_COMMAND_GET_LOG_HDR
                               , p_us_RequData   := #DiagVars.ucChoiceSaveLog
                               , us_RequLength   := 1
                               , p_us_RespData   := ( #DiagVars.LogHeader)$^USINT
                               , us_RespLength   := sizeof(DiagStructType.LogHeader)
                               );
          
          if ( sd_retval = 0 ) then
            DiagVars.LogMemToGet := DiagVars.LogHeader.LogMemSize;
            DiagVars.pLogFileMem$pVoid := ToStdLib.Malloc(size:=DiagVars.LogHeader.LogMemSize);
            //_memset(dest:=DiagVars.pLogFileMem$pVoid, usByte:=0, cntr:=DiagVars.LogHeader.LogMemSize);
            if DiagVars.ucChoiceSaveLog = 0 then
              DiagVars.ReadBaseAdr := SBF_SDO_ADDRESS_LOG_UC1;
            else
              DiagVars.ReadBaseAdr := SBF_SDO_ADDRESS_LOG_UC2;
            end_if;
            DiagVars.SavingLogSSW := GetLogFileBlocks;
            
          end_if;

  // ********************************************************************************************************************************************
        GetLogFileBlocks: // get the log memory from the application memory of the module (every cycle only a little bit to prevent runtime error in module)
          
          // we wait here until UpdateCy finishes our work
        
  // ********************************************************************************************************************************************
        GetLogHeader2:    // get log header from module again. if write counter changed, 
                          // we repeat GetLogFileBlocks. otherwise we continue to save the file
        
          LastWriteCounter := DiagVars.LogHeader.WriteCounter;
        
          // get LogHeader
          sd_retval := SDORequ ( us_Command      := SBF_SDO_COMMAND_GET_LOG_HDR
                               , p_us_RequData   := #DiagVars.ucChoiceSaveLog
                               , us_RequLength   := 1
                               , p_us_RespData   := ( #DiagVars.LogHeader)$^USINT
                               , us_RespLength   := sizeof(DiagStructType.LogHeader)
                               );
          
          if ( sd_retval = 0 ) then
          
            // if write counter is different, repeat last step
            if DiagVars.LogHeader.WriteCounter <> LastWriteCounter then
              DiagVars.SavingLogSSW := GetLogFileBlocks;

              DiagVars.LogMemToGet := DiagVars.LogHeader.LogMemSize;
              
              // realloc memory (maybe it's a different size)
              ToStdLib.Free(mptr:=DiagVars.pLogFileMem$pVoid);
              DiagVars.pLogFileMem$pVoid := ToStdLib.Malloc(size:=DiagVars.LogHeader.LogMemSize);
            else
              // write counter is equal. continue to last step
              DiagVars.SavingLogSSW := SaveLogFile;
            end_if;
          end_if;
        
  // ********************************************************************************************************************************************
        SaveLogFile:      // save the memory to the specified file (DPNE)

          // open file (create/overwrite always)
          FileHandle := FileSys.FileOpen(filename:=#DiagVars.DPNE[0], attributes:=ATT_CREATE_ALWAYS);
          
          DiagVars.SavingLogSuccessful := 1;
          
          if FileHandle >= 0 then
            
            // save content to it
            if FileSys.FileWrite(handle:=FileHandle, buffer:=DiagVars.pLogFileMem, length:=DiagVars.LogHeader.LogMemSize) <> DiagVars.LogHeader.LogMemSize$DINT then
              // failed to write to file
              LogError("@024C (SafetyBaseFSoE::Background) Failed to write to binary LogFile.");
              DiagVars.SavingLogSuccessful := -1;
            end_if;
            
            // close file
            FileSys.FileClose(handle:=FileHandle);
          else
            // failed to open file
            LogError("@024D (SafetyBaseFSoE::Background) Failed to open binary LogFile.");
            DiagVars.SavingLogSuccessful := -1;
          end_if;
          
          ToStdLib.Free(mptr:=DiagVars.pLogFileMem$pVoid);
          DiagVars.pLogFileMem := NIL;
          
          DiagVars.SavingLogSSW := GetLogHeader;
          DiagVars.SavingLogInProgress := FALSE;
        
      end_case;  
    end_if;
  end_if;
  
  state := READY;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::RtWork
  VAR_INPUT
    EAX 	: UDINT;
  END_VAR
  VAR_OUTPUT
    state (EAX) 	: UDINT;
  END_VAR
  VAR
    calloption    : UDINT;
    PDOLength     : UINT;
    pBuffer       : ^USINT;
    pDOInfo       : ^t_DOInfo;
    i             : USINT;
  END_VAR

  // Save state 
  // This must be the 1. line in this function or EAX may be overwritten by the generated AWL code
  calloption := EAX;

  if calloption = CALL_OPTION_RT_PRESCAN then    // Called from UpdateRt

//============================ Profiler start======================================================
#ifdef  DM_Profiler								
    StartTimeMeasurement(usMode := DMTIME_RT);
#endif	
//============================ Profiler end========================================================

    // do pdo communication only, if module has been found and initialized correctly and if there are some LEDs on (else a FW-Update is running)
    if Online & 
       (( SafetyState = _SafetyClassOK ) | ( SafetyState = _WaitForSynchronicity ) | ( SafetyState = _ReinitConfig ) | ( SafetyState = _ModFromCfgNotFound ) | ( SafetyState = _UnknownCfgError ) | ( SafetyState = _UnsafeVarNotFound )) then

      // get I/O state, runstate, error
      UpDateNotStd(calloption:=calloption);

      // wait until the safetymanager has initialized everything
      if b_ManagerInitialisationFinished = FALSE then
        return;
      end_if;

      // if any LED is active, then firmware update is not active and we can do our work
      if (VoltageOK | RunState | ServiceMode | ErrorState) then    
        // if we are in run state
        if RunState then
          if PDOSingleRunActive & WriteDOsUsed then
            PDOSingleRunActive -= 1;
            if PDOSingleRunActive = 0 then
              for i := 0 to WriteDOsUsed-1 do
                SetDORunState(Handle:=aWritePDOs[i].Handle, NewState:=_BUS_IF_RUN_STATE_NO_RUN);
              end_for;
            end_if;
          end_if;

          //read PDO from hardware, only if PDO is needed by other moduls
          if ( PDOOutputIsUsed ) & ReadDOsUsed then

            PDOLength := pPDOLengthInfoData^.PDOReadLength;

            // only if length is bigger than minimum and smaller than maximum
            if ( PDOLength >= SBF_SPDO_MIN_LENGTH ) & ( PDOLength <= MyPDOReadSize) then
              
              pBuffer := #PDOOutputBuffer[0];
              pDOInfo := #aReadPDOs[0];
              for i := 0 to (ReadDOsUsed-1) do
                _memcpy(ptr1:=pBuffer, ptr2:=pDOInfo^.pData, cntr:=pDOInfo^.Length);
                pBuffer += pDOInfo^.Length;
                pDOInfo += sizeof(t_DOInfo);
              end_for;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame(pFrame:=(#PDOOutputBuffer[0])$^DINT, FrameState:=0, IsFSoEPDO:=TRUE);
#endif
            else
              InvalidPDOSizeCnt += 1;
            end_if;
          end_if;            
        end_if;
      end_if;
    end_if;

//============================ Profiler start======================================================
#ifdef  DM_Profiler								
    StopTimeMeasurement(usMode := DMTIME_RT);
#endif	
//============================ Profiler end========================================================


  elsif calloption = CALL_OPTION_RT_POSTSCAN then // Called from UpdateRtPostScan

//============================ Profiler start======================================================
#ifdef  DM_Profiler								
    StartTimeMeasurement(usMode := DMTIME_RTPOSTSCAN);
#endif	
//============================ Profiler end========================================================

    // do pdo communication only, if module has been found and initialized correctly and if there are some LEDs on (else a FW-Update is running)
    if Online & 
       (( SafetyState = _SafetyClassOK ) | ( SafetyState = _WaitForSynchronicity ) | ( SafetyState = _ModFromCfgNotFound ) | ( SafetyState = _UnknownCfgError ) | ( SafetyState = _UnsafeVarNotFound )) then

      // if any LED is active, then firmware update is not active and we can do our work
      if (VoltageOK | RunState | ServiceMode | ErrorState) then     
        
        // write unsafe I/Os
        UpDateNotStd(calloption:=calloption);
      
        if RunState & ((us_SetBusReadyState = SetBusReadyFinished) | b_ManualQuit) then
          //write PDO to hardware
          if ( ui_cnt_PDOInputBuffer > 0 ) then          
            WritePDO2Module();
          end_if;
        end_if;
      end_if;
    end_if;

//============================ Profiler start======================================================
#ifdef  DM_Profiler								
    StopTimeMeasurement(usMode := DMTIME_RTPOSTSCAN);
#endif	
//============================ Profiler end========================================================
  end_if;

  state := READY;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::ConnectEvent
  VAR
    MyPara 	              : CmdStruct;
    MyResult	            : results;
  END_VAR

  //start DOs
  StartDO();

  if SafetyState = _ModuleNotFound then
    if ModuleSyncNo = 0 then
      SetSafetyState(NewState:=_SafetyClassOK);
    else
      SetSafetyState(NewState:=_WaitForSynchronicity);
    end_if;
  end_if;
  
  // if we we're already connected => tell the sdias manager we're back (safetythis has to be valid if b_Reconnect is set, therefore we don't have to check it here)
  if b_Reconnect then
    MyPara.uiCmd := CMD_SM_MODULE_RECONNECT;
    MyPara.aPara[0] := THIS$DINT;
    pSafetyManagerThis^.NewInst(#MyPara, #MyResult);
  end_if;

  // whenever the postinit has passed and we got a connect (module has been plugged in after booting the application) => get it's config
  if b_PostInitPassed = TRUE then
    // start reading the config in background (if we're in init the post-init read-out will be faster)
    
    // next time we check for the configured state of the cpu
    b_CheckConfigured := TRUE;

    // check again in background
    SetSafetyState(NewState:=_ReinitConfig);
  end_if;

// online is set if module init succeeds
//  Online := 1;  

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::DisconnectEvent
  VAR
    MyPara 	              : CmdStruct;
    MyResult	            : results;
  END_VAR
  
  //stop DOs
  StopDO();

  // reset time sync state and tell the manager
  if pSafetyManagerThis then
    MyPara.uiCmd := CMD_SM_MODULE_DISCONNECT;
    MyPara.aPara[0] := THIS$DINT;
    pSafetyManagerThis^.NewInst(#MyPara, #MyResult);
    
    b_Reconnect := TRUE;
  end_if;

  // clear request, otherwise problems could occur on next connect
  LastRequest := _NoOpenRequest;
  ud_AsyComRetryCounter := 0; // reset retry counter

  SafetyNumber := 0;
  FirmwareVersion := 0;
  
  // increment the config checksum to ensure the readout on reconnect (config readout also contains check of connected modules)
  DesignerPrjConfigCRC += 1;

  SetSafetyState(NewState:=_ModuleNotFound);
  
  // remove the cpu from all classes, which use it
  ResetConfig();

  if UnsafeScheduler <> UnsafeSchedulerSetBusReady then
    us_SetBusReadyState := WaitPDOsSyncron;
    UnsafeScheduler := UnsafeSchedulerSetBusReady;
  end_if;

  MaxVarsPerReadPackage := 0;
  ReadFramesNeeded := 0;
  MaxVarsPerWritePackage := 0;
  WriteFramesNeeded := 0;          
  
  Online := 0;
  
END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::InterruptEvent

END_FUNCTION


FUNCTION SafetyBaseFSoE::SetDORunState
  VAR_INPUT
    Handle 	: UDINT;
    NewState 	: t_BusIFRunState;
  END_VAR
  VAR
    dataSETRUNSTATE : t_SetRunStateCfg;
    tempCmd : CmdStruct;
    _result : results;
  END_VAR

  // set runstate of read
  dataSETRUNSTATE.Handle      := Handle;
  dataSETRUNSTATE.NewRunState := NewState;
  
  tempCmd.uiCmd         := _BUS_IF_SET_RUN_STATE;  
  tempCmd.aPara[0]      := (#dataSETRUNSTATE)$DINT; 

  ToBusInterface.NewInst(pPara:=#tempCmd, pResult:=#_result); 
  
  if _result.aData[0]$t_BusIFRetvalErrorcode <> _BUS_IF_RETVAL_OK then
    if NewState = _BUS_IF_RUN_STATE_NO_RUN then
      DOStopFailCounter += 1;
    else
      DOStartFailCounter += 1;
    end_if;
  end_if;  

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::CyWork
  VAR_INPUT
    EAX 	: UDINT;
  END_VAR
  VAR_OUTPUT
    state (EAX) 	: UDINT;
  END_VAR
  VAR
    p_SDO_act             : ^SDOHeader;
    us_SDO_length         : USINT;
    i                     : UINT;
    p_ReceiveObj          : pVirtualBase;
    p_SDOviaPath          : ^SDOviaPath;
    sd_retval             : DINT;
    IsStandardResp        : BOOL;
    SDORequestBuffer      : SDORequReadConfig;
    DataLen               : USINT;
    us_UnsafeSchedulerRun : USINT;
    p_PDOInBufferAct      : ^PDOInputBuffer;
    EverythingSynchron    : BOOL;
    MyPara 	              : CmdStruct;
    MyResult	            : results;
    UnsafeWrPackageNo     : USINT;
    aUnsafeWrPackages     : UnsafeWritePackages;
    UnsafeRdPackageNo     : USINT;
    aUnsafeRdPackages     : UnsafeReadPackages;
    TmpVal                : DINT;
    AddUnsafePackage      : BOOL;
    GotNewUnsafeValue     : BOOL;
    pHelpUnsafe           : ^UnsafeIORoutingElement;
    BDINTWriteVars        : USINT;
    pAddDynSlaveResp      : ^t_AddDynSlaveResponse;
  END_VAR

//============================ Profiler start======================================================
#ifdef  DM_Profiler								
  StartTimeMeasurement(usMode := DMTIME_CY);
#endif	
//============================ Profiler end========================================================

#ifdef HWK_SHOW_PRAGMA_MESSAGES      
  if b_LogFirstDone = 1 then
    for i := 0 to (ui_cnt_PDOInputBuffer-1) do
      LogValue("@027F (SafetyBaseFSoE::WritePDO2Module) Copy 0x{0} Bytes to offset 0x{1} of Write PDO Nr. 0x{2}", Value:=aLogValues[i].ByteLen, Value2:=aLogValues[i].Offset, Value3:=aLogValues[i].DOIndex);  
    end_for;
  
    b_LogFirstDone := 2;
  end_if;
#endif

  // do sdo communication only, if module has been found and initialized correctly and if there are some LEDs on (else a FW-Update is running)
  if Online & (VoltageOK | RunState | ServiceMode | ErrorState) &
     (( SafetyState <> _ModuleNotFound ) & ( SafetyState <> _MemAllocFailed ) & ( SafetyState <> _LostPowerSupply ) & ( SafetyState <> _ReadFWVerFailed ) & ( SafetyState <> _WrongSafetyHW ) & ( b_GetNewConfiguration = FALSE )) then // never check ( SafetyState <> _ReinitConfig ) here, because it would prevent the distribution of slave configs

    // wait until the safetymanager has initialized connection
    if (b_ManagerReadyForSDO = FALSE) then
      return;
    end_if;


    //Update of service messages doesn't take place in realtime

    //read SDO from module and send it to the target module
    //check, if SDO is available and valid
    if ( p_Handle_SDO_Read^.us_Status = SAFETY_SDO_DATA_VALID ) then
  
      //calculate length of SDO
      us_SDO_length := sizeof(SDOHeader.StdHeader.CRC) + p_Handle_SDO_Read^.SDOStructure.SDOHeader.StdHeader.Len;
      
      //copy SDO without first byte (=status byte)
      ToStdLib.MemCpy(dest := p_SDOOutputBuffer, source := #p_Handle_SDO_Read^.SDOStructure, size := us_SDO_length );

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame(p_SDOOutputBuffer$^DINT, 0);
#endif

      //mark data as received
      p_Handle_SDO_Read^.us_Status := SAFETY_SDO_NO_DATA;
    
      //find target object for routing
      p_ReceiveObj := NIL;

      IsStandardResp := FALSE;
      //check, if receiver is addressed by safety number or hardware path
      if ((p_SDOOutputBuffer^.SDOHeader.StdHeader.Typ$USINT AND SBF_SSDO_MASK_TYPE)$t_SafetyFrameType = SSDO_MOD) then
      
        //search this-pointer of receiver with safety number
        p_ReceiveObj := ( GetRoutingPointerByNumber(ud_SafeNumber := p_SDOOutputBuffer^.SDOHeader.DstAdr))$pVirtualBase;
        
      elsif ((p_SDOOutputBuffer^.SDOHeader.StdHeader.Typ$USINT AND SBF_SSDO_MASK_TYPE)$t_SafetyFrameType = SSDO_MOD_TP) then

        //search this-pointer of receiver with hardware path
        p_SDOviaPath := p_SDOOutputBuffer$^SDOviaPath;
        p_ReceiveObj := ( GetRoutingPointerByPath(p_ActObjectPath := #p_SDOviaPath^.TopologyPath[0], p_SDOviaPath^.StdHeader.Len - 8))$pVirtualBase;
      elsif ((p_SDOOutputBuffer^.SDOHeader.StdHeader.Typ$USINT AND SBF_SSDO_MASK_TYPE)$t_SafetyFrameType = SDO_STD) then
        IsStandardResp := TRUE;
        StandardResponse := (p_SDOOutputBuffer$^StandardFrameType)^;
        if StandardResponse.SDOHeader.SeqNr = (StandardSeqNr OR 16#80) then

          // if the microcontroller got powered again and we have an SDO response => also get the configuration (don't wait the whole time)
          //if b_WaitForFirmwareBoot = TRUE then // no if to be faster (if it's false, it does not matter if we set it again to false)
          b_WaitForFirmwareBoot := FALSE;
          //end_if;

          case LastRequest of
          //**********************************************************************************************************************************************************
            _NoOpenRequest: // error, no open request, but we got a response
              LogError("@0252 (SafetyBaseFSoE::CyWork) Standard frame response received, without open request.");
            
          //**********************************************************************************************************************************************************
            _SetBusReady:
              IF StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_SET_BUS_READY THEN
                if us_SetBusReadyState <> SendSetBusReady then
                  LogError("@0253 (SafetyBaseFSoE::CyWork) Got Response of SetBusReady, but SSW is not in the right step.");
                  return;
                end_if;
                us_SetBusReadyState := SetBusReadyFinished;
                b_ManualQuit := FALSE;
                UnsafeScheduler := UnsafeSchedulerGetDiagVars; // Go on with the action-steps, done here so we don't lose a whole cycle
                LastRequest := _NoOpenRequest;
                ud_AsyComRetryCounter := 0; // reset retry counter
                if SafetyState = _WaitForSynchronicity then
                  SetSafetyState(NewState:=_SafetyClassOK);
                end_if;
              else
                LogError("@0256 (SafetyBaseFSoE::CyWork) Invalid command: bus ready expected.");  
              END_IF;
            
          //**********************************************************************************************************************************************************
            _ReadData:
              // if we did read command, then save the value to corresponding server
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_READ then
                // if returncode is 0 read succeeded
                if StandardResponse.aData[0] = 0 then
                  // check if length is correct
                  if StandardResponse.aData[1] = StandardRequest.aData[4] then
                    // delete request
                    LastRequest := _NoOpenRequest;
                    ud_AsyComRetryCounter := 0; // reset retry counter

                    if (DiagVars.SavingLogSSW = GetLogFileBlocks) then
                      // save the last block in the memory for the file
                      _memcpy(ptr1:=DiagVars.pLogFileMem$pVoid + (DiagVars.LogHeader.LogMemSize - DiagVars.LogMemToGet), 
                              ptr2:=#StandardResponse.aData[2], 
                              cntr:=DiagVars.BytesToGet);

                      DiagVars.LogMemToGet -= DiagVars.BytesToGet;
                      
                      if DiagVars.LogMemToGet = 0 then
                        DiagVars.SavingLogSSW := GetLogHeader2;
                      end_if;
                    else
                      LogError("@0255 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid Read response (no diagnostic read active).");
                    end_if;
                  end_if;
                end_if;
              else
                LogError("@0256 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid command, read expected.");              
              end_if;

          //**********************************************************************************************************************************************************
            _ReadValues:
              // if we did read command, then save the value to corresponding server
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_READ_VALUES then
                // if returncode is 0 read succeeded
                if StandardResponse.aData[0] = 0 then
                  // check if length is correct
                  if StandardResponse.aData[1] = StandardRequest.aData[0] then
                    // delete request
                    LastRequest := _NoOpenRequest;
                    ud_AsyComRetryCounter := 0; // reset retry counter

                    // if we got some outputs in the routing table and a valid pointer, write values to servers
                    if ui_CntUnsafeOutputs & pUnsafeOutElement then
                      
                      // depending on length we do different number of server write-methods to set the data we just read
//                      pUnsafeOutElement := p_UnsafeOutputsTable + (UnsafeReadTransNr * MaxVarsPerReadPackage * sizeof(UnsafeIORoutingElement));  // the pointer is already on this element
                      for i := 0 to (StandardResponse.aData[1] - 1) do
                        // save the new value temporarily
                        TmpVal := StandardResponse.aData[2 + (i * sizeof(DINT))]$DINT;
                        if pUnsafeOutElement^.ud_Thisp then
                          if pUnsafeOutElement^.UnsafeType = BDINTOutput then
                            pUnsafeOutElement^.sd_OldValue := RD(pUnsafeOutElement^.ud_Thisp$^void)$DINT;
                            TmpVal := ((pUnsafeOutElement^.sd_OldValue$UDINT AND (NOT pUnsafeOutElement^.BitMask)) OR
                                      (TmpVal$UDINT AND pUnsafeOutElement^.BitMask))$DINT;
                            
                            // now we have to write the value to the server
                            WR(pUnsafeOutElement^.ud_Thisp$^void, TmpVal);              
                          else
                            WR(pUnsafeOutElement^.ud_Thisp$^void, TmpVal);                              
                          end_if;
                        elsif (pUnsafeOutElement^.UnsafeType = ExternalOutput) | (pUnsafeOutElement^.UnsafeType = ExternalBDINTOutput) then
                          pUnsafeOutElement^.sd_NewValue4External := TmpVal;
                          
                          // mark the value as valid
                          pUnsafeOutElement^.b_IsValid := TRUE;
                        end_if;
                        pUnsafeOutElement += sizeof(UnsafeIORoutingElement);
                      end_for;
                    end_if;
                  
                    UnsafeReadTransNr += 1;
                  end_if;
                end_if;
              else
                LogError("@0257 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid command, read values expected.");              
              end_if;
            
          //**********************************************************************************************************************************************************
            _WriteValues:
              if IsOptimizedCPU() then
                // if we did a write command, just check 
                if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_WRITE_VALUES then
                  // delete request
                  LastRequest := _NoOpenRequest;
                  ud_AsyComRetryCounter := 0; // reset retry counter

                  // if there is no more element to send or check for changes => proceed to the next step
                  if pUnsafeInElement = NIL then
                    UnsafeScheduler := UnsafeSchedulerReadData; //We need to set scheduler to next step....
                  end_if;                
                else
                  LogError("@0258 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid command, write expected.");          
                end_if;
              else
                // on CSCP011 the SDO_COMMAND_WRITE_VALUES didn't exist
                
                // if we did a write command, just check 
                if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_WRITE then
                  // delete request
                  LastRequest := _NoOpenRequest;
                  ud_AsyComRetryCounter := 0; // reset retry counter

                  if UnsafeWriteTransNr < (WriteFramesNeeded - 1) then
                    UnsafeWriteTransNr += 1;
                  else
                    UnsafeWriteTransNr := 0;
                    UnsafeWritingActive := FALSE;
                    UnsafeScheduler := UnsafeSchedulerReadData; //We need to set scheduler to next step....
                  end_if;
                else
                  LogError("@0258 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid command, write expected.");          
                end_if;
              end_if;


          //**********************************************************************************************************************************************************
            _GetCRC:
              // check if it's a response to a read command
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_READ then
                // if returncode is 0 read succeeded
                if StandardResponse.aData[0] = 0 then
                  // check if length is correct
                  if StandardResponse.aData[1] = 4 then
                  
                    // delete request
                    LastRequest := _NoOpenRequest;
                    ud_AsyComRetryCounter := 0; // reset retry counter

                    if DesignerPrjConfigCRC <> StandardResponse.aData[2]$HDINT then
                      // CRC has changed => save the new crc
                      DesignerPrjConfigCRC := StandardResponse.aData[2]$HDINT;
                      
                      // show that we read the config of the class again
                      SetSafetyState(NewState:=_ReinitConfig);

                      if pSafetyManagerThis then
                        MyPara.uiCmd := CMD_SM_NEW_SAFETY_PROJECT;
                        MyPara.aPara[0] := 4;
                        MyPara.aPara[4] := (#SafetyNumber)$DINT;
                        pSafetyManagerThis^.NewInst(#MyPara, #MyResult);    
                      end_if;

                      // remove the cpu from all classes, which use it
                      ResetConfig();
                      
                      // we have to return here because:
                      // the command CMD_SM_NEW_SAFETY_PROJECT will trigger a disconnect of this class which will set the unsafe scheduler to setbusready, 
                      // which would skip the synchron check in the actual cyclic run because everything of the old configuration is synchron now
                      return;
                    end_if;
                  end_if;
                end_if;
              end_if;
              
          //**********************************************************************************************************************************************************
            _WriteSysTime:
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_WRITE then
                // if returncode is 0 read succeeded
                if StandardResponse.aData[0] = 0 then
                  // delete request
                  LastRequest := _NoOpenRequest;
                  ud_AsyComRetryCounter := 0; // reset retry counter
                end_if;
              else
                LogError("@0258 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid command, write expected.");          
              end_if;  

          //**********************************************************************************************************************************************************
            _WriteFlexConCommand:
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_WRITE then
                // if returncode is 0 read succeeded
                if StandardResponse.aData[0] = 0 then
                  // delete request
                  LastRequest := _NoOpenRequest;
                  ud_AsyComRetryCounter := 0; // reset retry counter
                end_if;
              else
                LogError("@0258 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid command, write expected.");          
              end_if;             

          //**********************************************************************************************************************************************************
            _DiagState:
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_GET_STATE then
                _memcpy(ptr1 := #DiagVars.UserGetStateResp, 
                        ptr2 := #StandardResponse.aData + SIZEOF(USINT), 
                        cntr := sizeof(GetStateResponseType) );
                DiagVars.UserDiagStateReady := TRUE;
                DiagVars.UserWantsDiagState := FALSE;
                LastRequest := _NoOpenRequest;
                ud_AsyComRetryCounter := 0; // reset retry counter
              end_if;
              
          //**********************************************************************************************************************************************************
            _DiagInfo:
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_GET_DIAG_INFO then
                _memcpy(ptr1 := #DiagVars.UserGetDiagInfoResp, 
                        ptr2 := #StandardResponse.aData + SIZEOF(USINT), 
                        cntr := sizeof(SafetyDiagInfo) );
                DiagVars.UserDiagInfoReady := TRUE;
                DiagVars.UserWantsDiagInfo := FALSE;
                LastRequest := _NoOpenRequest;
                ud_AsyComRetryCounter := 0; // reset retry counter
              end_if;
              
          //**********************************************************************************************************************************************************
            _ReadRetryCounter:
              //if we did read command, then save the value to corresponding server
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_READ then
                // if returncode is 0 read succeeded
                if StandardResponse.aData[0] = 0 then
                  // check if length is correct
                  if StandardResponse.aData[1] = SBF_SDO_LENGTH_RETRY_COUNTER then
                    // delete request
                    LastRequest := _NoOpenRequest;
                    ud_AsyComRetryCounter := 0; // reset retry counter 
                    
                    //Only Write if Retry Counter has changed
                    if StandardResponse.aData[2] <> us_OldRetryCounter then
                      //if retry counter has changed, save the new retry counter
                      SafetyRetryCounter += to_usint(StandardResponse.aData[2] - us_OldRetryCounter);
                      us_OldRetryCounter := StandardResponse.aData[2];
                      
                      //0x20400000 is adress of retry counter: 1 byte amount of all retries of all modules, 16byte µC1, 16 byte µC2
                      //Save the values from the modules
                      ToStdLib.MemCpy(dest:=#a_RetryCnt[0], source:=#StandardResponse.aData[3], size:=SBF_SDO_LENGTH_RETRY_COUNTER);
                      
                      //Calls the method of class sdiassafetymanager
                      SetRetryCounter();  
                    end_if;
                  end_if;
                end_if;
              else
                LogError("@0257 (SafetyBaseFSoE::CyWork) SDO Receive: Invalid command, read values expected.");    
              end_if;
              
          //**********************************************************************************************************************************************************
            _ErrorChannelInfo:
              //if we did read command, then save the value to corresponding server
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_READ then
                // if returncode is 0 read succeeded            
                if StandardResponse.aData[0] = 0 then
                  // check if length is correct
                  if StandardResponse.aData[1] <> 0  then                  
                    _memcpy(ptr1 := #aErrorMem[actoffset], 
                            ptr2 := #StandardResponse.aData[2], 
                            cntr := StandardResponse.aData[1]);
                    if remainingPacketlength = 0 then
                      remainingPacketlength := StandardResponse.aData[2] - StandardResponse.aData[1];
                      actoffset := StandardResponse.aData[1];
                    else
                      remainingPacketlength -= StandardResponse.aData[1];
                      actoffset += StandardResponse.aData[1];
                    end_if;
                    
                    // delete request
                    LastRequest := _NoOpenRequest;
                    ud_AsyComRetryCounter := 0; // reset retry counter   
                    if remainingPacketlength <= 0  then
                      //Finish
                      DiagVars.UserErrorInfoReady := TRUE;
                      DiagVars.UserWantsErrorInfo := FALSE;
                      remainingPacketlength := 0; //reset remainingPacketlength
                      actoffset := 0; //reset offset
                    end_if;                                          
                  end_if;                
                end_if;            
              end_if;
              
          //**********************************************************************************************************************************************************
            _ReadFlexConNodeState:
              //if we did read command, then save the value to corresponding server
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_READ then
                // if returncode is 0 read succeeded            
                if StandardResponse.aData[0] = 0 then
                  // check if length is correct
                  if StandardResponse.aData[1] = sizeof(t_FlexConNodeState) then
                 
                    // store the structure locally
                    _memcpy(ptr1 := #sFlexCon.LastNodeState, 
                            ptr2 := #StandardResponse.aData[2], 
                            cntr := StandardResponse.aData[1]);
                            
                    // save the values to the server of the object
                    sFlexCon.pCommand^            := sFlexCon.LastNodeState.Command;
                    sFlexCon.pStateMachine^       := sFlexCon.LastNodeState.StateMachine;
                    sFlexCon.pHostSafetyNumber^   := sFlexCon.LastNodeState.HostSafetyNumber;
                    
                    // delete request
                    LastRequest := _NoOpenRequest;
                    ud_AsyComRetryCounter := 0; // reset retry counter 
                  end_if;                
                end_if;            
              end_if;
            
          //**********************************************************************************************************************************************************
            _ReadFlexConHostState:
              //if we did read command, then save the value to corresponding server
              if StandardResponse.SDOHeader.Cmd = SBF_SDO_COMMAND_READ then
                // if returncode is 0 read succeeded            
                if StandardResponse.aData[0] = 0 then
                  // check if length is correct (maximum the requested size)
                  if StandardResponse.aData[1] & (StandardResponse.aData[1] <= sFlexCon.LastHostState.LastRequestedLength) then
                    // if the size in the response is smaller than the requested (we already got everything in the first package)
                    if StandardResponse.aData[1] <= sFlexCon.LastHostState.LastRequestedLength then
                      // adjust the last requested length to the received for correct handling of the received data
                      sFlexCon.LastHostState.LastRequestedLength := StandardResponse.aData[1];
                    end_if;
                  
                    // if it's the first package
                    if sFlexCon.LastHostState.CurrentReadOffset = 0 then
                      // if length is ok and there is at least 1 connection but not more than the maximum
                      if (StandardResponse.aData[1] >= sizeof(HDINT)) & StandardResponse.aData[2]$HDINT & (StandardResponse.aData[2]$HDINT <= MAX_FSOE_CONNECTIONS) then
                        // the first 4 byte contain the number of connections following
                        sFlexCon.LastHostState.NoConnections := StandardResponse.aData[2]$HDINT;
                        _memcpy(ptr1:=#sFlexCon.LastHostState.aConnections[0], ptr2:=#StandardResponse.aData[2] + sizeof(HDINT), cntr:=sFlexCon.LastHostState.LastRequestedLength - sizeof(HDINT));

                        // calculate how many bytes are missing (what do we need minus what do we already have)
                        sFlexCon.LastHostState.BytesMissing       := sFlexCon.LastHostState.NoConnections$UINT * sizeof(HDINT) + sizeof(HDINT) - sFlexCon.LastHostState.LastRequestedLength;
                        sFlexCon.LastHostState.CurrentReadOffset  += sFlexCon.LastHostState.LastRequestedLength;
                      else
                        // no connection or too many to fit into the array
                        sFlexCon.LastHostState.NoConnections := 0;
                        
                        // nothing missing
                        sFlexCon.LastHostState.BytesMissing := 0;
                      end_if;
                    else
                      // offset is not zero => follow up package
                      _memcpy(ptr1:=#sFlexCon.LastHostState.aConnections[0] + sFlexCon.LastHostState.CurrentReadOffset - sizeof(HDINT), ptr2:=#StandardResponse.aData[2], cntr:=sFlexCon.LastHostState.LastRequestedLength);                      
                      
                      // update the bytes missing and the current reading offset
                      sFlexCon.LastHostState.BytesMissing      -= sFlexCon.LastHostState.LastRequestedLength;
                      sFlexCon.LastHostState.CurrentReadOffset += sFlexCon.LastHostState.LastRequestedLength;
                    end_if;                    
                  else
                    // wrong length => reset number of connections
                    sFlexCon.LastHostState.NoConnections := 0;
                    
                    // nothing missing
                    sFlexCon.LastHostState.BytesMissing := 0;
                  end_if;

                  // if we got all data
                  if sFlexCon.LastHostState.BytesMissing = 0 then

                    // update connection state at managing modules
                    MyPara.uiCmd := CMD_SM_UPDATE_FLEX_CON_HOST;
                    MyPara.aPara[0] := sFlexCon.LastHostState.NoConnections$DINT;
                    MyPara.aPara[1] := (#sFlexCon.LastHostState.aConnections[0])$DINT;
                    pSafetyManagerThis^.NewInst(#MyPara, #MyResult);  

                    // reset the offset so we start reading at 0 in the next round
                    sFlexCon.LastHostState.CurrentReadOffset := 0;

                    // reset timemark
                    sFlexCon.ud_LastHostStateTimeStamp := ops.tAbsolute;                  
                  end_if;

                  // delete request
                  LastRequest := _NoOpenRequest;
                  ud_AsyComRetryCounter := 0; // reset retry counter 

                end_if;            
              end_if;
            
          else
            LogError("@025A (SafetyBaseFSoE::CyWork) SDO Receive: Invalid standard frame response.");
          end_case;

          // if request is deleted, increase sequence number to set next request
          if LastRequest = _NoOpenRequest then
            if StandardSeqNr < 16#7F then
              StandardSeqNr += 1;
            else
              StandardSeqNr := 0;
            end_if;
          end_if;
        else
          LogError("@025B (SafetyBaseFSoE::CyWork) SDO Receive: Invalid sequence number in Standard SDO response.");
          LogValue("expected: 0x{0} / received: 0x{1}", Value:=(StandardSeqNr OR 16#80), Value2:=StandardResponse.SDOHeader.SeqNr);
        end_if;
      elsif ((p_SDOOutputBuffer^.SDOHeader.StdHeader.Typ$USINT AND SBF_SSDO_MASK_TYPE)$t_SafetyFrameType = SSDO_MOD_DEVADDR) then      
        
        //Check if we know our target
        p_ReceiveObj := ( GetRoutingPointerByNumber(ud_SafeNumber := p_SDOOutputBuffer^.SDOHeader.DstAdr))$pVirtualBase;
        
        //Forward data to manager if we found no routing target, otherwise the SDO will be directly send to target
        if p_ReceiveObj = NIL then
        
          // Forward to manager if possible
          if pSafetyManagerThis & us_SDO_length then
  #ifdef DO_DEBUG
            DOCheck(pFrame:=(p_SDOOutputBuffer)$^t_SDOStdHeader, pCheckStruct:=#SDORead);
  #endif

            if ( (p_SDOOutputBuffer^.SDOHeader.StdHeader.Cmd = SBF_SDO_COMMAND_ADD_DYN_SLAVE) | (p_SDOOutputBuffer^.SDOHeader.StdHeader.Cmd = SBF_SDO_COMMAND_ADD_DYN_SLAVE2) )& 
               (p_SDOOutputBuffer^.SDOHeader.StdHeader.Typ$USINT AND SBF_SSDO_MASK_RESPONSE) then    
              // we need a response big enough to hold our info, the returncode must be 0 and the offset/length info has to be plausible
              if ((p_SDOOutputBuffer^.SDOHeader.StdHeader.Len + sizeof(SDOHeader.StdHeader.CRC)) >= sizeof(t_AddDynSlaveResponse)) &
                  (p_SDOOutputBuffer^.us_Returncode = 0) & CheckAddDynSlaveResponse(p_SDOOutputBuffer$^t_AddDynSlaveResponse) then
        
                pAddDynSlaveResp := p_SDOOutputBuffer$^t_AddDynSlaveResponse;
        
                MyPara.aPara[2]       := THIS$DINT;

                MyPara.aPara[3]$HDINT := pAddDynSlaveResp^.OffsetSafeOutData; // offset to data from module        
                MyPara.aPara[4]$HDINT := pAddDynSlaveResp^.LenSafeOutData;    // size of data from module
                
                MyPara.aPara[5]$HDINT := pAddDynSlaveResp^.OffsetSafeInData;  // offset to data to module        
                MyPara.aPara[6]$HDINT := pAddDynSlaveResp^.LenSafeInData;     // size of data to module
              else
                _memset(dest:=#MyPara.aPara[2], usByte:=0, cntr:=5*sizeof(DINT));
              end_if;
            end_if;

            MyPara.uiCmd := CMD_SM_SDO_TO_MANAGER;
            MyPara.aPara[0] := to_dint(us_SDO_length);
            MyPara.aPara[1] := p_SDOOutputBuffer$DINT;
            pSafetyManagerThis^.NewInst(#MyPara, #MyResult);  
            
            IsStandardResp := TRUE;
          else
            //log not routable SDOs
            LogValue("@025E (SafetyBaseFSoE::CyWork) Safety module 0x{0}: SDO not routed to receiver {1}", SafetyNumber$UDINT, p_SDOOutputBuffer^.SDOHeader.DstAdr$UDINT);
          end_if;
        end_if;
      else
        LogError("@025C (SafetyBaseFSoE::CyWork) SDO Receive: Invalid frametype in Standard SDO response.");
      end_if;

      //if there is no object, the message will be lost
      if ( p_ReceiveObj ) then

        //send SDO to receiver object
        MyPara.uiCmd := CMD_SM_ADD_SDO_TO_BUFFER;
        MyPara.aPara[0] := p_SDOOutputBuffer$DINT;
        MyPara.aPara[1] := us_SDO_length;
        sd_retval$iprStates := p_ReceiveObj^.NewInst(#MyPara, #MyResult);    

//        sd_retval := p_ReceiveObj^.AddSDOToBuffer(p_us_Message := p_SDOOutputBuffer$^USINT, us_length := us_SDO_length);
        
        if sd_retval$iprStates = ERROR then
          // generate error message with returncode (add to logfile and debugger trace)
          LogValue("@025D (SafetyBaseFSoE::CyWork) AddSDOToBuffer failed: Returncode: -0x{0}", Value:=(0-sd_retval)$UDINT);
        end_if;
      
      elsif IsStandardResp = FALSE then

        if pSafetyManagerThis & us_SDO_length then
#ifdef DO_DEBUG
          DOCheck(pFrame:=(p_SDOOutputBuffer)$^t_SDOStdHeader, pCheckStruct:=#SDORead);
#endif

          // if the command is get safety number and the compressed path type is 2, it can only be in this system (no interface frame) and therefore forwarding to the manager would not make sense (the class should already find the other module with GetRoutingPointerByPath()
          if (p_SDOOutputBuffer^.SDOHeader.StdHeader.Cmd = SBF_SDO_COMMAND_GET_SAFENBR) & 
             (p_SDOOutputBuffer^.SDOHeader.StdHeader.Typ = SSDO_MOD_TP) & 
             (p_SDOOutputBuffer^.SDOHeader.DstAdr$USINT = 2) then
            // skip forwarding in this case
          else
            // forward it to the safety manager
            MyPara.uiCmd := CMD_SM_SDO_TO_MANAGER;
            MyPara.aPara[0] := to_dint(us_SDO_length);
            MyPara.aPara[1] := p_SDOOutputBuffer$DINT;
            pSafetyManagerThis^.NewInst(#MyPara, #MyResult);    
          end_if;
        else
          //log not routable SDOs
          LogValue("@025E (SafetyBaseFSoE::CyWork) Safety module 0x{0}: SDO not routed to receiver {1}", SafetyNumber$UDINT, p_SDOOutputBuffer^.SDOHeader.DstAdr$UDINT);
        end_if;
      end_if;

    elsif (LastRequest <> _NoOpenRequest) & (ud_TransfertimePerPackage > 0) then
      // count up for timeout
      ud_UnsafeTimeoutCounter += 1;
            
      if ( ud_UnsafeTimeoutCounter ) > ( 50 * ud_TransfertimePerPackage ) then
        CASE UnsafeScheduler OF

          UnsafeSchedulerSetBusReady:
            LogError("@025F (SafetyBaseFSoE::UpdateCy) Timeout on setting bus ready.");

          UnsafeSchedulerGetDiagVars:
            IF LastRequest = _ReadData THEN
              LogError("@0260 (SafetyBaseFSoE::UpdateCy) Timeout requesting DiagVars.");
            END_IF;
            UnsafeScheduler := UnsafeSchedulerNonPriorData;
            DiagVars.SavingLogSSW := GetLogFileBlocks;
            
          UnsafeSchedulerNonPriorData:
            if LastRequest = _WriteSysTime then
              LogError("@0497 (SafetyBaseFSoE::UpdateCy) Timeout writing the system time to module.");
            else
              LogError("@0261 (SafetyBaseFSoE::UpdateCy) Timeout requesting CRC.");            
            end_if;
            
            UnsafeScheduler := UnsafeSchedulerWriteData;
            
          UnsafeSchedulerWriteData:
            LogError("@0262 (SafetyBaseFSoE::UpdateCy) Timeout writing Data 'Unsafe Input'.");
            UnsafeScheduler := UnsafeSchedulerReadData;
            UnsafeWriteTransNr := 0;
            
          UnsafeSchedulerReadData:
            LogError("@0263 (SafetyBaseFSoE::UpdateCy) Timeout reading Data 'Unsafe Output'.");
            UnsafeScheduler := UnsafeSchedulerDiagState;
            UnsafeReadTransNr := 0;
            
          UnsafeSchedulerDiagState:
            LogError("@0264 (SafetyBaseFSoE::UpdateCy) Timeout on reading DiagState.");
            UnsafeScheduler := UnsafeSchedulerDiagInfo;
            
          UnsafeSchedulerDiagInfo:
            LogError("@0265 (SafetyBaseFSoE::UpdateCy) Timeout on reading DiagInfo.");
            UnsafeScheduler := UnsafeSchedulerGetRetryCounter;
          
          UnsafeSchedulerGetRetryCounter:
            LogError("@0266 (SafetyBaseFSoE::UpdateCy) Timeout on reading RetryCounter.");
            UnsafeScheduler := UnsafeSchedulerChannelError;
            
          UnsafeSchedulerChannelError:
            LogError("@0267 (SafetyBaseFSoE::UpdateCy) Timeout on reading ChannelErrorInfo.");
            UnsafeScheduler := UnsafeSchedulerGetFlexConNodeState;

          UnsafeSchedulerGetFlexConNodeState:
            LogError("@0267 (SafetyBaseFSoE::UpdateCy) Timeout on reading FlexConNodeState.");
            UnsafeScheduler := UnsafeSchedulerGetFlexConHostState;

          UnsafeSchedulerGetFlexConHostState:
            LogError("@0267 (SafetyBaseFSoE::UpdateCy) Timeout on reading FlexConHostState.");
            UnsafeScheduler := UnsafeSchedulerGetDiagVars;
          
        END_CASE;
        
        // we only increase the retry counter if the module is running
        if RunState then
          ud_AsyComRetryCounter += 1;        
        end_if;

        if ud_AsyComRetryCounter >= SBF_SDO_MAX_TRIES then
          SetSafetyState(NewState:=_AsyncComError);
          LogError("@0268 (SafetyBaseFSoE::UpdateCy) Too many consecutive asynchronous commands failed! Please contact Sigmatek Support!");
        else
          LastRequest := _NoOpenRequest;
          // if request is deleted, increase sequence number to set next request
          if StandardSeqNr < 16#7F then
            StandardSeqNr += 1;
          else
            StandardSeqNr := 0;
          end_if;
        end_if;
      end_if;
       
    end_if;

    //write to hardware from other module received SDO
    if ( ui_cnt_SDOInputBuffer > 0 ) & p_SDOInputBuffer then
      
      //set pointer to first SDO in the SDO list
      p_SDO_act   := p_SDOInputBuffer$^SDOHeader;
          
      //calculate length of SDO (CRC + data length)
      us_SDO_length := sizeof (SDOHeader.StdHeader.CRC) + p_SDO_act^.StdHeader.Len;
          
      //check, if buffer is empty
      if ( p_Handle_SDO_Write^.us_Status = SAFETY_SDO_NO_DATA ) then
      
#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame(p_SDO_act$^DINT, 1);
#endif

        //copy SDO into write-buffer
        ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := p_SDO_act, size := us_SDO_length);
        
        //mark SDO data as valid
        p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
                   
        //decrement amount of SDOs 
        ui_cnt_SDOInputBuffer -= 1;
          
        //if there is at least 1 SDO left, copy the remaining memory over the actual SDO
        if ( ui_cnt_SDOInputBuffer > 0 ) then
                   
          //remove first entry in the SDO list
          _memmove(p_SDOInputBuffer, (p_SDOInputBuffer + SBF_SDO_MAX_LENGTH), (ui_cnt_SDOInputBuffer * SBF_SDO_MAX_LENGTH));
        end_if;
      end_if;
    end_if;

    // if we have to quit the communication errors and there is no other request open at this time
    if (QuitComError = 1) & (LastRequest = _NoOpenRequest) then
      if UnsafeScheduler <> UnsafeSchedulerSetBusReady then
        us_SetBusReadyState := WaitPDOsSyncron; 
        UnsafeScheduler := UnsafeSchedulerSetBusReady;  
      end_if;
      
      QuitComError := 0;
      
      // refresh the server on the outer object
      RefreshAsySrv();
    end_if;

    if ( SafetyState <> _ReinitConfig ) & ( p_Handle_SDO_Write^.us_Status = SAFETY_SDO_NO_DATA ) then
      
      // priority request before checking the scheduler
      // if the channel is free and we have a command request for the flexible constellation node => send the command
      if (LastRequest = _NoOpenRequest) & (sFlexCon.FoundNodeInConfig) & (sFlexCon.CommandRequest) & b_ManagerInitialisationFinished then

        StandardRequest.SDOHeader.CRC  := 0;                               // CRC is always 0 in Std communication
        StandardRequest.SDOHeader.Cmd  := SBF_SDO_COMMAND_WRITE;           // write command
        StandardRequest.SDOHeader.Typ  := SDO_STD;                         // standard frame type
        StandardRequest.SDOHeader.SeqNr:= StandardSeqNr OR 16#80;          // sequence number
        StandardRequest.SDOHeader.Len  := 4 + 4 + 1 + sizeof(UDINT);       // header + destination address + length of data + size of data
        StandardRequest.aData[0]$UDINT := SBF_SDO_ADDRESS_FLEX_CON_NODE;   // address of the flex con data
        StandardRequest.aData[4]       := sizeof(UDINT);                   // length of data
        StandardRequest.aData[5]$UDINT := sFlexCon.CommandRequest;     // new command

        // reset marker to detect new commands
        sFlexCon.CommandRequest := 0;

        LastRequest := _WriteFlexConCommand;

        //copy SDO into write-buffer
        ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #StandardRequest, size := (sizeof(SDOHeader.StdHeader.CRC) + StandardRequest.SDOHeader.Len));
              
        //mark SDO data as valid
        p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
        
        if ( pISafety^.udVersion >= 16#1001 ) then
          SAFETY_iPushSafetyTask();
        end_if;

      end_if;

      // Start of scheduler; we need to share the time between reading, writing and diagnosis
      us_UnsafeSchedulerRun := 0;

      // number of steps in total minus 1, because the SetBusReady doesn't count here since it's not part of the rotation
      SchedulerStepNo := 10;
      
      // Check actions until we find somthing we need to do
      // all x (SchedulerStepNo) action-steps must be walked through during one cycle to make sure new data is fetched as fast as possible
      // SetBusReady is an exception: this step is only performed once on bootup!
      while (us_UnsafeSchedulerRun < SchedulerStepNo) & (LastRequest = _NoOpenRequest) do
        us_UnsafeSchedulerRun += 1;
        ud_UnsafeTimeoutCounter := 0; //No active request: we can reset the timeout-counter
        case UnsafeScheduler of
//**********************************************************************************************************************************************************
          UnsafeSchedulerSetBusReady:  // this step is excluded from the rotation (set at startup, on disconnect or quit error)
          
            //Set Bus Ready has to be done one time after the manager initialisation was finished
            //Other steps of the scheduler can be executed before the manager initialisation is finished (diag steps)
            if b_ManagerInitialisationFinished = TRUE then

              case us_SetBusReadyState of
              
              //-------------------------------------------------------------------------------------------------------------
                WaitPDOsSyncron: 
                
                  EverythingSynchron := TRUE;
                  
                  // it's possible that all PDOs are coming from a different domain, then we have no inputbuffer
                  if ui_cnt_PDOInputBuffer then
                    p_PDOInBufferAct := #aPDOInputBuffer[0];
                    for i := 0 to (ui_cnt_PDOInputBuffer-1) do
                      if p_PDOInBufferAct^.b_TimeSynchron = FALSE then
                        // we're still not synchron with this module. if it's optional it is probably not connected
                        if p_PDOInBufferAct^.b_IsOptional then
                          if (ops.tAbsolute - ud_OptionalTimeoutStart) <= SBF_OPTIONAL_MODULE_SYNC_TIMEOUT then
                            // if timeout not exceeded => not sychron (otherwise the variable will stay on it's old value)
                            EverythingSynchron := FALSE;
                            exit;
                          else
                            TRACE("Synchronisation timeout for optional module exceeded => assume it's synchon.");
                          end_if;
                        else
                          EverythingSynchron := FALSE;
                          exit;
                        end_if;
                      end_if;
                    
                      p_PDOInBufferAct +=  sizeof(PDOInputBuffer);
                    end_for;              
                    
                    if EverythingSynchron & (ModuleSyncNo > 0) then
                      ModuleSyncNo -= 1;

  #ifdef SAFETY_DEBUG_LOGGING_ACTIVATED
  //                  case ud_MyModuleID of
  //                    CSCP01x_ID:
  //                      TRACE("ModuleSyncNo wurde dekrementiert von: CSCP.");
  //                      
  //                    CSDI16x_ID:
  //                      TRACE("ModuleSyncNo wurde dekrementiert von: CSDI.");
  //                      
  //                    CSTO08x_ID:
  //                      TRACE("ModuleSyncNo wurde dekrementiert von: CSTO.");
  //                  end_case;
  #endif
                  end_if;
  #ifdef SAFETY_DEBUG_LOGGING_ACTIVATED
  //              elsif EverythingSynchron then
  //                  case ud_MyModuleID of
  //                    CSCP01x_ID:
  //                      TRACE("ModuleSyncNo wurde nicht verändert von: CSCP.");
  //                      
  //                    CSDI16x_ID:
  //                      TRACE("ModuleSyncNo wurde nicht verändert von: CSDI.");
  //                      
  //                    CSTO08x_ID:
  //                      TRACE("ModuleSyncNo wurde nicht verändert von: CSTO.");
  //                  end_case;
  #endif
                  end_if;
                  
                  if EverythingSynchron then
                    us_SetBusReadyState := WaitAllModulesSyncron;
                  end_if;

              //-------------------------------------------------------------------------------------------------------------
                WaitAllModulesSyncron:              
                  // wait for other modules to be synchron with each other
                  if (ModuleSyncNo = 0) & b_ManagerInitialisationFinished then
                    us_SetBusReadyState := SendSetBusReady;              
                  end_if;
                
              //-------------------------------------------------------------------------------------------------------------
                SendSetBusReady:              
                  // send the bus-ready command
                  SDORequestBuffer.StdHeader.CRC  := 0;                                                   // CRC is always 0 in Std communication
                  SDORequestBuffer.StdHeader.Cmd  := SBF_SDO_COMMAND_SET_BUS_READY;                        // set bus ready command
                  SDORequestBuffer.StdHeader.Typ  := SDO_STD;                                             // standard frame type
                  SDORequestBuffer.StdHeader.SeqNr:= StandardSeqNr OR 16#80;                              // sequence number
                  SDORequestBuffer.StdHeader.Len  := 4;                                                   // header
              
                  LastRequest := _SetBusReady;

  #ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
    RecordFrame((#SDORequestBuffer)$^DINT, 1);
  #endif

                  //copy SDO into write-buffer
                  ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #SDORequestBuffer, size := (sizeof(SDOHeader.StdHeader.CRC) + SDORequestBuffer.StdHeader.Len));
                        
                  //mark SDO data as valid
                  p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
                  
                  if ( pISafety^.udVersion >= 16#1001 ) then
                    SAFETY_iPushSafetyTask();
                  end_if;
                  
              //-------------------------------------------------------------------------------------------------------------
                SetBusReadyFinished:
                  // all ok, nothing to do here
                  
              end_case;
              us_UnsafeSchedulerRun := 16#FF; //make sure we don't repeat the while-loop until we have sent the bus-ready command
            
            else
              UnsafeScheduler := UnsafeSchedulerGetDiagVars;  //go on with next step
            end_if;
//**********************************************************************************************************************************************************
          UnsafeSchedulerGetDiagVars:

            //check, if buffer is empty and we got some unsafe data to get/set and crc check is finished
            if (DiagVars.SavingLogSSW = GetLogFileBlocks) then

              // check if more than blocksize to get
              if DiagVars.LogMemToGet > SBF_LOG_DATA_BLOCK_SIZE then
                // get a complete block
                DiagVars.BytesToGet := SBF_LOG_DATA_BLOCK_SIZE;
              else
                // get only the last bytes
                DiagVars.BytesToGet := DiagVars.LogMemToGet$USINT;
              end_if;

              StandardRequest.SDOHeader.Cmd   := SBF_SDO_COMMAND_READ;                         // read command
              StandardRequest.SDOHeader.Typ   := SDO_STD;                                     // standard frame type
              StandardRequest.SDOHeader.SeqNr := StandardSeqNr OR 16#80;                      // sequence number is in special area
              StandardRequest.SDOHeader.Len   := 4 + 4 + 1;                                   // header + destination address + length of data
              StandardRequest.aData[0]$UDINT  := DiagVars.ReadBaseAdr + (DiagVars.LogHeader.LogMemSize - DiagVars.LogMemToGet);// destination address
              StandardRequest.aData[4]        := DiagVars.BytesToGet$USINT;                   // length of data
                    
              LastRequest := _ReadData;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#StandardRequest)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #StandardRequest, size := (sizeof(SDOHeader.StdHeader.CRC) + StandardRequest.SDOHeader.Len));
              
              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;
            else
              UnsafeScheduler := UnsafeSchedulerNonPriorData;
            end_if;
           
//**********************************************************************************************************************************************************
          UnsafeSchedulerNonPriorData:
            
            // every 5 secs check for a new safety project inside the cpu 
            if b_CheckCRC & ((ops.tAbsolute - ud_LastCrcCheck) > SBF_CRC_CHECK_TIME ) then

              SDORequestBuffer.StdHeader.CRC  := 0;                                                         // CRC is always 0 in Std communication
              SDORequestBuffer.StdHeader.Cmd  := SBF_SDO_COMMAND_READ;                                       // read command
              SDORequestBuffer.StdHeader.Typ  := SDO_STD;                                                   // standard frame type
              SDORequestBuffer.StdHeader.SeqNr:= StandardSeqNr OR 16#80;                                    // sequence number
              SDORequestBuffer.StdHeader.Len  := 4 + 4 + 1;                                                 // header + destination address + length of data
              SDORequestBuffer.ud_Address     := SBF_SDO_ADDRESS_CONFIG_DATA + SBF_SDO_OFFSET_CONFIG_CRC_ALL; // address of crc
              SDORequestBuffer.us_Length      := 4;                                                         // length of crc

              LastRequest := _GetCRC;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#SDORequestBuffer)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #SDORequestBuffer, size := (sizeof(SDOHeader.StdHeader.CRC) + SDORequestBuffer.StdHeader.Len));
                    
              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;

              ud_LastCrcCheck := ops.tAbsolute;
              
            // every 5 seconds write system time of plc to scp
            elsif b_SetSysTime & ((ops.tAbsolute - ud_LastSetSysTime) > SBF_SYSTIME_SET_TIME ) then
            
              if (LastRequest = _NoOpenRequest) & (p_Handle_SDO_Write^.us_Status = SAFETY_SDO_NO_DATA) then
                StandardRequest.SDOHeader.CRC  := 0;                               // CRC is always 0 in Std communication
                StandardRequest.SDOHeader.Cmd  := SBF_SDO_COMMAND_WRITE;           // write command
                StandardRequest.SDOHeader.Typ  := SDO_STD;                         // standard frame type
                StandardRequest.SDOHeader.SeqNr:= StandardSeqNr OR 16#80;          // sequence number
                StandardRequest.SDOHeader.Len  := 4 + 4 + 1 + sizeof(UDINT);       // header + destination address + length of data + size of data
                StandardRequest.aData[0]$UDINT := SBF_SDO_ADDRESS_FW_SYSTIME;      // address of system time safety firmware
                StandardRequest.aData[4]       := sizeof(UDINT);                   // length of data
                StandardRequest.aData[5]$UDINT := ToSigCLib.UnixTimeStampGet();    // system time of plc as unix timestamp

                LastRequest := _WriteSysTime;

                //copy SDO into write-buffer
                ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #StandardRequest, size := (sizeof(SDOHeader.StdHeader.CRC) + StandardRequest.SDOHeader.Len));
                      
                //mark SDO data as valid
                p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
                
                if ( pISafety^.udVersion >= 16#1001 ) then
                  SAFETY_iPushSafetyTask();
                end_if;

                ud_LastSetSysTime := ops.tAbsolute;
              end_if;  
            
            else
              if RunState & us_SetBusReadyState = SetBusReadyFinished then
                // in operational (and SetbusReady is finished) also get unsafe-variables
                UnsafeScheduler := UnsafeSchedulerWriteData;
              else
                // not operational, we must skip the unsave vars
                UnsafeScheduler := UnsafeSchedulerDiagState;
                us_UnsafeSchedulerRun += 2;
              end_if;
            end_if;

//**********************************************************************************************************************************************************
          UnsafeSchedulerWriteData:
                        
            if IsOptimizedCPU() then
              if ui_CntUnsafeInputs then
                // if we have finished checking through all variables the last time => check from beginning (otherwise the pointer is on the element where we have to continue)
                if pUnsafeInElement = NIL then
                  pUnsafeInElement := p_UnsafeInputsTable;
                end_if;
                
                // we check if we have to write any of the unsafe input variables (only until our message is full)
                UnsafeWrPackageNo := 0;
                BDINTWriteVars := 0; // limit number of bdint write variables to 3 to ensure constant load of firmware
                while pUnsafeInElement & (UnsafeWrPackageNo < SBF_MAX_UNSAFE_WRITE_PACKAGES) & (BDINTWriteVars < SBF_MAX_UNSAFE_BDINTS_PER_WRITE) do

                  // we have to check if we add this one
                  GotNewUnsafeValue := FALSE;

                  // if the type is normal we can read the actual value from the server
                  if pUnsafeInElement^.UnsafeType = NormalUnsafe then
                    // check if pointer to server is valid
                    if pUnsafeInElement^.ud_Thisp then
                      // get the actual value from the server
                      TmpVal := RD((pUnsafeInElement^.ud_Thisp)$^void);

                      // we remember to update this if it's necessary
                      GotNewUnsafeValue := TRUE;                 
                    else
                      if bd_LogOnceMarker.NoThisForUnsafeIn = FALSE then
                        bd_LogOnceMarker.NoThisForUnsafeIn := TRUE;
                        LogError("@0269 (SafetyBaseFSoE::CyWork) [SL] No thispointer available for Server of local unsafe input variable.");
                      end_if;
                    end_if;
                  elsif pUnsafeInElement^.UnsafeType = ExternalInput then
                    // check if we already have a valid value there
                    if pUnsafeInElement^.b_IsValid then
                      // get the actual value from the element
                      TmpVal := pUnsafeInElement^.sd_NewValue4External;
                      
                      // we remember to update this if it's necessary
                      GotNewUnsafeValue := TRUE;
                    end_if;
                  else
                    if bd_LogOnceMarker.WrongUnsafeVarType = FALSE then
                      bd_LogOnceMarker.WrongUnsafeVarType := TRUE;
                      LogError("@026A (SafetyBaseFSoE::CyWork) [SL] Wrong unsafe variable type in unsafe input list.");              
                    end_if;
                  end_if;
                  
                  // if we have a new valid unsafe value => check if we have to send it
                  if GotNewUnsafeValue then
                    // if it hasn't been sent yet
                    if (pUnsafeInElement^.b_AlreadyTransmitted = FALSE) then
                      // remember that we sent this already
                      pUnsafeInElement^.b_AlreadyTransmitted := TRUE;
                      
                      // add this one
                      AddUnsafePackage := TRUE;
                    elsif TmpVal <> pUnsafeInElement^.sd_OldValue then
                      // if there were any changes on this value
                      
                      // add this one
                      AddUnsafePackage := TRUE;                
                    else
                      // don't add this one
                      AddUnsafePackage := FALSE;
                    end_if;                  

                    // if it is ready to be added => add it to send buffer
                    if AddUnsafePackage then
                      // add a package to send
                      aUnsafeWrPackages[UnsafeWrPackageNo].ud_Address := pUnsafeInElement^.ud_Address;
                      aUnsafeWrPackages[UnsafeWrPackageNo].Value      := TmpVal;
                      UnsafeWrPackageNo += 1;

                      // if it has a bitmask it's an unsafe bdint input variable
                      if pUnsafeInElement^.BitMask then
                        BDINTWriteVars += 1;
                      end_if;

                      // remember the actual as old value
                      pUnsafeInElement^.sd_OldValue := TmpVal;
                    end_if;
                  end_if;

                  pUnsafeInElement := pUnsafeInElement^.pNext;
                end_while;

                if UnsafeWrPackageNo then
                  DataLen := UnsafeWrPackageNo * sizeof(UnsafeWritePackage);
                
                  StandardRequest.SDOHeader.Cmd                 := SBF_SDO_COMMAND_WRITE_VALUES;    // write values command
                  StandardRequest.SDOHeader.Typ                 := SDO_STD;                        // standard frame type     
                  StandardRequest.SDOHeader.SeqNr               := StandardSeqNr OR 16#80;         // sequence number is in special area
                  StandardRequest.SDOHeader.Len                 := 4 + 4 + 1 + DataLen;            // header + destination address + length of data + data
                  StandardRequest.aData[0]                      := UnsafeWrPackageNo;              // number of packages
                  StandardRequest.aData[1]$UnsafeWritePackages  := aUnsafeWrPackages;              // packages

                  LastRequest := _WriteValues;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#StandardRequest)$^DINT, 1);
#endif

                  //copy SDO into write-buffer
                  ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #StandardRequest, size := (sizeof(SDOHeader.StdHeader.CRC) + StandardRequest.SDOHeader.Len));
                  
                  //mark SDO data as valid
                  p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
                  
                  if ( pISafety^.udVersion >= 16#1001 ) then
                    SAFETY_iPushSafetyTask();
                  end_if;
                else
                  UnsafeScheduler := UnsafeSchedulerReadData;
                end_if;
              else
                UnsafeScheduler := UnsafeSchedulerReadData;
              end_if;
            else
              // on CSCP011 the SDO_COMMAND_WRITE_VALUES didn't exist
              if ui_CntUnsafeInputs & WriteFramesNeeded then
                // if writing is not active, check if it's necessary to activate it
                if (UnsafeWritingActive = FALSE) then
                  pUnsafeInElement := p_UnsafeInputsTable;

                  for i := 0 to (ui_CntUnsafeInputs - 1) do
                    if pUnsafeInElement^.ud_Thisp then
                      sd_retval := RD((pUnsafeInElement^.ud_Thisp)$^void);        // data
                      if pUnsafeInElement^.sd_OldValue <> sd_retval then
                        UnsafeWritingActive := TRUE;
                        pUnsafeInElement^.sd_OldValue := sd_retval;
                      end_if;
                    end_if;
                    pUnsafeInElement += sizeof(UnsafeIORoutingElement);
                  end_for;
                end_if;

                // check if writing is already active
                if UnsafeWritingActive then
                  // pointer to the right unsafe input
                  pUnsafeInElement := p_UnsafeInputsTable + (UnsafeWriteTransNr * MaxVarsPerWritePackage * sizeof(UnsafeIORoutingElement));

                  // get length of data we write
                  if (UnsafeWriteTransNr < (WriteFramesNeeded-1)) | ((ui_CntUnsafeInputs MOD MaxVarsPerWritePackage) = 0) then
                    DataLen := MaxVarsPerWritePackage * sizeof(DINT);
                  else
                    DataLen := (ui_CntUnsafeInputs MOD MaxVarsPerWritePackage)$USINT * sizeof(DINT);
                  end_if;

                  StandardRequest.SDOHeader.Cmd   := SBF_SDO_COMMAND_WRITE;                        // write command
                  StandardRequest.SDOHeader.Typ   := SDO_STD;                                     // standard frame type     
                  StandardRequest.SDOHeader.SeqNr := StandardSeqNr OR 16#80;                      // sequence number is in special area
                  StandardRequest.SDOHeader.Len   := 4 + 4 + 1 + DataLen;                         // header + destination address + length of data + data
                  StandardRequest.aData[0]$UDINT  := pUnsafeInElement^.ud_Address;                // destination address
                  StandardRequest.aData[4]        := DataLen;                                     // length of data
                  
                  // depending on length we do different number of server read-methods to get the data we write
                  for i := 0 to (DataLen/sizeof(DINT)) - 1 do
                    if pUnsafeInElement^.ud_Thisp then
                      StandardRequest.aData[5 + (i * sizeof(DINT))]$DINT := pUnsafeInElement^.sd_OldValue;
                    else
                      // this server doesn't exist in lasal => always send 0
                      StandardRequest.aData[5 + (i * sizeof(DINT))]$DINT := 0;
                    end_if;

                    pUnsafeInElement += sizeof(UnsafeIORoutingElement);
                  end_for;

                  LastRequest := _WriteValues;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#StandardRequest)$^DINT, 1);
#endif

                  //copy SDO into write-buffer
                  ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #StandardRequest, size := (sizeof(SDOHeader.StdHeader.CRC) + StandardRequest.SDOHeader.Len));
                  
                  //mark SDO data as valid
                  p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
                  
                  if ( pISafety^.udVersion >= 16#1001 ) then
                    SAFETY_iPushSafetyTask();
                  end_if;
                else
                  UnsafeScheduler := UnsafeSchedulerReadData;
                end_if;      
              else
                UnsafeScheduler := UnsafeSchedulerReadData;
              end_if;
            end_if;

//**********************************************************************************************************************************************************
          UnsafeSchedulerReadData:

            if ui_CntUnsafeOutputs & ReadFramesNeeded then
              //check, if buffer is empty and we got some unsafe data to get/set and crc check is finished
              if (UnsafeReadTransNr < ReadFramesNeeded) //then
               & ( ((UnsafeReadTransNr = 0) & ( ops.tAbsolute - LastUnsafeUpdate > ud_ServerUpdateTime ) ) | (UnsafeReadTransNr <> 0) )then
                // here we request directly the unsafe IOs
                
                // sequence number is in special area
                StandardRequest.SDOHeader.SeqNr := StandardSeqNr OR 16#80;
                
                // we get an output
                pUnsafeOutElement := p_UnsafeOutputsTable + (UnsafeReadTransNr * MaxVarsPerReadPackage * sizeof(UnsafeIORoutingElement));
                
                // get length of data we read
                if (UnsafeReadTransNr < (ReadFramesNeeded - 1)) | ((ui_CntUnsafeOutputs MOD MaxVarsPerReadPackage) = 0) then
                  UnsafeRdPackageNo := MaxVarsPerReadPackage;
                else
                  UnsafeRdPackageNo := (ui_CntUnsafeOutputs MOD MaxVarsPerReadPackage)$USINT;
                end_if;
                
                DataLen := UnsafeRdPackageNo * sizeof(DINT);

                // create buffer with addresses of values to read
                pHelpUnsafe := pUnsafeOutElement;
                for i := 0 to (UnsafeRdPackageNo-1) do
                  aUnsafeRdPackages[i] := pHelpUnsafe^.ud_Address;
                  pHelpUnsafe += sizeof(UnsafeIORoutingElement);
                end_for;

                StandardRequest.SDOHeader.Cmd               := SBF_SDO_COMMAND_READ_VALUES;                  // read values command
                StandardRequest.SDOHeader.Typ               := SDO_STD;                                     // standard frame type
                StandardRequest.SDOHeader.SeqNr             := StandardSeqNr OR 16#80;                      // sequence number is in special area
                StandardRequest.SDOHeader.Len               := 4 + 1 + DataLen;                             // header + size + length of data
                StandardRequest.aData[0]                    := UnsafeRdPackageNo;                           // number of values to get
                StandardRequest.aData[1]$UnsafeReadPackages := aUnsafeRdPackages;                           // read addresses
                      
                LastRequest := _ReadValues;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#StandardRequest)$^DINT, 1);
#endif

                //copy SDO into write-buffer
                ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #StandardRequest, size := (sizeof(SDOHeader.StdHeader.CRC) + StandardRequest.SDOHeader.Len));
                
                //mark SDO data as valid
                p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
                
                if ( pISafety^.udVersion >= 16#1001 ) then
                  SAFETY_iPushSafetyTask();
                end_if;

              elsif (UnsafeReadTransNr >= ReadFramesNeeded) then
                // We have finished all read data, go on with the next step
                UnsafeScheduler := UnsafeSchedulerDiagState;
                LastUnsafeUpdate := ops.tAbsolute;
                UnsafeReadTransNr := 0;
              else
                // time until read is not yet over - go on
                UnsafeScheduler := UnsafeSchedulerDiagState;
              end_if;
            else
              UnsafeScheduler := UnsafeSchedulerDiagState;
            end_if;

//**********************************************************************************************************************************************************
          UnsafeSchedulerDiagState:

            // get the diag state (on user demand)
            if DiagVars.UserWantsDiagState then
              
              // get state via SDO communication                                  
              SDORequestBuffer.StdHeader.CRC  := 0;                                                   // CRC is always 0 in Std communication
              SDORequestBuffer.StdHeader.Cmd  := SBF_SDO_COMMAND_GET_STATE;                            // get-state command
              SDORequestBuffer.StdHeader.Typ  := SDO_STD;                                             // standard frame type
              SDORequestBuffer.StdHeader.SeqNr:= StandardSeqNr OR 16#80;                              // sequence number
              SDORequestBuffer.StdHeader.Len  := 4;                                                   // header
              SDORequestBuffer.ud_Address     := 0;
              SDORequestBuffer.us_Length      := 0;

              LastRequest := _DiagState;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#SDORequestBuffer)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #SDORequestBuffer, size := (sizeof(SDOHeader.StdHeader.CRC) + SDORequestBuffer.StdHeader.Len));
                    
              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;
            else
              UnsafeScheduler := UnsafeSchedulerDiagInfo;
            end_if;

//**********************************************************************************************************************************************************
            UnsafeSchedulerDiagInfo:
         
            // get the diag info (on user demand)
            if DiagVars.UserWantsDiagInfo then
              
              // get state via SDO communication                               
              StandardRequest.SDOHeader.CRC  := 0;                                                   // CRC is always 0 in Std communication
              StandardRequest.SDOHeader.Cmd  := SBF_SDO_COMMAND_GET_DIAG_INFO;                       // get diagnosis info command
              StandardRequest.SDOHeader.Typ  := SDO_STD;                                             // standard frame type
              StandardRequest.SDOHeader.SeqNr:= StandardSeqNr OR 16#80;                              // sequence number
              StandardRequest.SDOHeader.Len  := 4 + 1;                                               // header + µC-selection
              StandardRequest.aData[0] := DiagVars.ucChoiceDiagInfo;

              LastRequest := _DiagInfo;

#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#StandardRequest)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #StandardRequest, size := (sizeof(SDOHeader.StdHeader.CRC) + StandardRequest.SDOHeader.Len));
                    
              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;
              
            else
              UnsafeScheduler := UnsafeSchedulerGetRetryCounter;            
            end_if;
            
//**********************************************************************************************************************************************************
          UnsafeSchedulerGetRetryCounter:

            //read out the retry counters every 10seconds
            if (b_RetryCounterAvailable) & (b_DisableSafetyRetryCnt = FALSE) 
            & ((ops.tAbsolute - ud_RetryCntTimeMark) > SBF_RETRY_COUNTER_WAIT_TIME)  then
              
              SDORequestBuffer.StdHeader.CRC  := 0;                                                         // CRC is always 0 in Std communication
              SDORequestBuffer.StdHeader.Cmd  := SBF_SDO_COMMAND_READ;                                       // read command
              SDORequestBuffer.StdHeader.Typ  := SDO_STD;                                                   // standard frame type
              SDORequestBuffer.StdHeader.SeqNr:= StandardSeqNr OR 16#80;                                    // sequence number
              SDORequestBuffer.StdHeader.Len  := 4 + 4 + SBF_SDO_LENGTH_RETRY_COUNTER;                       // header + destination address + length of data
              SDORequestBuffer.ud_Address     := SBF_SDO_ADDRESS_RETRY_COUNTER;                              // address of retrycounter
              SDORequestBuffer.us_Length      := SBF_SDO_LENGTH_RETRY_COUNTER;                               // length of data        
              
              LastRequest := _ReadRetryCounter; 
                          
#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#SDORequestBuffer)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #SDORequestBuffer, size := (sizeof(SDOHeader.StdHeader.CRC) + SDORequestBuffer.StdHeader.Len));

              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;
              
              //reset timemark
              ud_RetryCntTimeMark := ops.tAbsolute;
              
            else
              UnsafeScheduler := UnsafeSchedulerChannelError; 
            end_if;
          
//**********************************************************************************************************************************************************
          UnsafeSchedulerChannelError:
            
            if DiagVars.UserWantsErrorInfo  then            
              SDORequestBuffer.StdHeader.CRC  := 0;                                                          // CRC is always 0 in Std communication
              SDORequestBuffer.StdHeader.Cmd  := SBF_SDO_COMMAND_READ;                                       // read command
              SDORequestBuffer.StdHeader.Typ  := SDO_STD;                                                    // standard frame type
              SDORequestBuffer.StdHeader.SeqNr:= StandardSeqNr OR 16#80;                                     // sequence number
              SDORequestBuffer.StdHeader.Len  := 4 + 4 + 1;                                                  // header + destination address + length of data
              SDORequestBuffer.ud_Address     := SBF_SDO_ADDRESS_ERROR_MEM;                                  // address of error memory
              SDORequestBuffer.us_Length      := SBF_SDO_LENGTH_ERROR_MESSAGE;                               // length of data (first byte contains number of following values + number of channels)                                              
              
              if remainingPacketlength <> 0 then
                // set offset if more than one package
                SDORequestBuffer.ud_Address     := (SBF_SDO_ADDRESS_ERROR_MEM + actoffset);                  // address of error memory
                // set new length for next package
                if remainingPacketlength > SBF_SDO_LENGTH_ERROR_MESSAGE then
                  SDORequestBuffer.us_Length      := SBF_SDO_LENGTH_ERROR_MESSAGE;                           
                else
                  SDORequestBuffer.us_Length      := remainingPacketlength$USINT;                                          
                end_if;
              end_if;                                               

              LastRequest := _ErrorChannelInfo; 
            
#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
RecordFrame((#SDORequestBuffer)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #SDORequestBuffer, size := (sizeof(SDOHeader.StdHeader.CRC) + SDORequestBuffer.StdHeader.Len));

              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;
              
              //reset timemark
              ud_RetryCntTimeMark := ops.tAbsolute;
                        
            else
              UnsafeScheduler := UnsafeSchedulerGetFlexConNodeState; 
            end_if;    

//**********************************************************************************************************************************************************
          UnsafeSchedulerGetFlexConNodeState:

            // if there is a flexible constellation in the configuration and also an object placed and connected, we check the state regularly
            if (sFlexCon.FoundNodeInConfig) & ((ops.tAbsolute - sFlexCon.ud_LastNodeStateTimeStamp) > SBF_FLEX_CON_STATE_CHECK_TIME) then

              SDORequestBuffer.StdHeader.CRC  := 0;                                                         // CRC is always 0 in Std communication
              SDORequestBuffer.StdHeader.Cmd  := SBF_SDO_COMMAND_READ;                                      // read command
              SDORequestBuffer.StdHeader.Typ  := SDO_STD;                                                   // standard frame type
              SDORequestBuffer.StdHeader.SeqNr:= StandardSeqNr OR 16#80;                                    // sequence number
              SDORequestBuffer.StdHeader.Len  := 4 + 4 + sizeof(t_FlexConNodeState);                        // header + destination address + length of data
              SDORequestBuffer.ud_Address     := SBF_SDO_ADDRESS_FLEX_CON_NODE;                             // address of retrycounter
              SDORequestBuffer.us_Length      := sizeof(t_FlexConNodeState);                                // length of data        
              
              LastRequest := _ReadFlexConNodeState; 
                          
#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#SDORequestBuffer)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #SDORequestBuffer, size := (sizeof(SDOHeader.StdHeader.CRC) + SDORequestBuffer.StdHeader.Len));

              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;
              
              //reset timemark
              sFlexCon.ud_LastNodeStateTimeStamp := ops.tAbsolute;

            else
              UnsafeScheduler := UnsafeSchedulerGetFlexConHostState; 
            end_if;    
            
//**********************************************************************************************************************************************************
          UnsafeSchedulerGetFlexConHostState:

            // if there is a flexible constellation in the configuration and also an object placed and connected, we check the state regularly
            if (sFlexCon.FoundHostInConfig) & ((ops.tAbsolute - sFlexCon.ud_LastHostStateTimeStamp) > SBF_FLEX_CON_STATE_CHECK_TIME) then

              // calcuate max length
              us_SDO_length := SBF_SDO_MAX_LENGTH - (sizeof(SDOHeader) - sizeof(SDOHeader.SessId)) - sizeof(SDORespReadConfig.us_Returncode) - sizeof(SDORespReadConfig.us_Length);

              // if it's the first read of this run, read as much as possible (we don't know how many connections we have yet)
              if sFlexCon.LastHostState.CurrentReadOffset = 0 then
                // get maximum number of bytes
                DataLen := us_SDO_length;
              else
                // if we still need more or equal to max size
                if sFlexCon.LastHostState.BytesMissing >= us_SDO_length then
                  // get maximum number of bytes
                  DataLen := us_SDO_length;                 
                else
                  // otherwise it's the last package, so get the rest that is missing
                  DataLen := sFlexCon.LastHostState.BytesMissing$USINT;
                end_if;
              end_if;

              SDORequestBuffer.StdHeader.CRC  := 0;                                                                         // CRC is always 0 in Std communication
              SDORequestBuffer.StdHeader.Cmd  := SBF_SDO_COMMAND_READ;                                                      // read command
              SDORequestBuffer.StdHeader.Typ  := SDO_STD;                                                                   // standard frame type
              SDORequestBuffer.StdHeader.SeqNr:= StandardSeqNr OR 16#80;                                                    // sequence number
              SDORequestBuffer.StdHeader.Len  := 4 + 4 + sizeof(HDINT);                                                     // header + destination address + length of data
              SDORequestBuffer.ud_Address     := SBF_SDO_ADDRESS_FLEX_CON_HOST + sFlexCon.LastHostState.CurrentReadOffset;  // address of retrycounter
              SDORequestBuffer.us_Length      := DataLen;                                                                   // length of data
              
              // remember the length to check it when we receive the answer
              sFlexCon.LastHostState.LastRequestedLength := DataLen;
              
              LastRequest := _ReadFlexConHostState; 
                          
#ifdef SAFETY_PROTOCOL_ANALYZER_ACTIVATED
  RecordFrame((#SDORequestBuffer)$^DINT, 1);
#endif

              //copy SDO into write-buffer
              ToStdLib.MemCpy(dest := #p_Handle_SDO_Write^.SDOStructure, source := #SDORequestBuffer, size := (sizeof(SDOHeader.StdHeader.CRC) + SDORequestBuffer.StdHeader.Len));

              //mark SDO data as valid
              p_Handle_SDO_Write^.us_Status := SAFETY_SDO_DATA_VALID;
              
              if ( pISafety^.udVersion >= 16#1001 ) then
                SAFETY_iPushSafetyTask();
              end_if;
              
            else
              UnsafeScheduler := UnsafeSchedulerGetDiagVars; 
            end_if;    

//**********************************************************************************************************************************************************
        else
          if bd_LogOnceMarker.InvalidSchedulerState = FALSE then
            bd_LogOnceMarker.InvalidSchedulerState := TRUE;
            LogError("@026B (SafetyBaseFSoE::CyWork) [SL] Undefined Scheduler-State.");
          end_if;
        end_case; 
      end_while;
    end_if;
  end_if;

  // at the first run we write all variables independent of any changes
  if RunState > OldRunState then
    SendAllUnsafeValues();
  end_if;
  OldRunState := RunState;

  state := READY;

//============================ Profiler start======================================================
#ifdef  DM_Profiler								
  StopTimeMeasurement(usMode := DMTIME_CY);
#endif	
//============================ Profiler end========================================================
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::GetConfiguration

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::SetSafetyState
  VAR_INPUT
    NewState 	: SafetyConfigStateType;
  END_VAR

  // use it locally => can also be forwarded in derivation
  SafetyState := NewState;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::RefreshAsySrv
  // derive this
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::SetRetryCounter

  SafetyRetryCounter := -1;
  
END_FUNCTION


FUNCTION SafetyBaseFSoE::IsInCache
  VAR_INPUT
    ud_Address 	: UDINT;
    us_Length 	: USINT;
  END_VAR
  VAR_OUTPUT
    ItsInside 	: BOOL;
  END_VAR

  if CachedSize &
     (ud_Address >= CachedAddr) &
     ((ud_Address + us_Length) <= (CachedAddr + CachedSize)) then
    ItsInside := TRUE;
  else
    ItsInside := FALSE;
  end_if;

END_FUNCTION


FUNCTION SafetyBaseFSoE::AddToCache
  VAR_INPUT
    p_SDORequestBuffer 	: ^SDORequReadConfig;
    p_SDOResponseBuffer 	: ^SDORespReadConfig;
  END_VAR

  if p_SDORequestBuffer^.us_Length then
    CachedAddr := p_SDORequestBuffer^.ud_Address;
    CachedSize := p_SDORequestBuffer^.us_Length;
    _memcpy(ptr1:=#Cache, ptr2:=#p_SDOResponseBuffer^.ud_Data, cntr:=p_SDORequestBuffer^.us_Length);
  end_if;

END_FUNCTION


FUNCTION SafetyBaseFSoE::GetFSoEConnectionInfos
  VAR_INPUT
    p_SDORequestBuffer 	: ^SafetyBaseFSoE::SDORequReadConfig;
    p_SDOResponseBuffer 	: ^SafetyBaseFSoE::SDORespReadConfig;
  END_VAR
  VAR
    Address         : UDINT;
    Size            : USINT;
    sd_retval       : DINT;
    i               : UDINT;
    pActConnection  : ^t_FSoEConnectionDetails;
    TempRoutingInfo : t_FSoERoutingInfoElement;
    NextAddress     : UDINT;
  END_VAR

  // always free the connection infos
  FreeFSoEConnectionInfos(bFreeAlways:=TRUE);

  // check if we already got the info:
  if ConnectionInfos.ConnectionNo | ConnectionInfos.pFirstConnection then
    LogError("@0280 (SafetyBaseFSoE::GetFSoEConnectionInfos) List already exists! May not have been resetted correctly!");
    return;
  end_if;

//*****************************************************************************
//** CONNECTION STATE INFO                                                   **
//*****************************************************************************
  
  // get number of connections
  Address := SBF_SDO_ADDRESS_FSOE_CONN_STATE;
  Size    := 2;

  //send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := Address
                              , us_Length           := Size
                              );

  //no response
  if ( sd_retval <> 0 ) then
    LogError("@0281 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading number of connections.");
    return;
  end_if;
  
  ConnectionInfos.ConnectionNo := p_SDOResponseBuffer^.ud_Data$UINT;

  if ConnectionInfos.ConnectionNo = 0 then
    // no connections available
    return;
  end_if;

  // allocate memory for the connections
  ConnectionInfos.pFirstConnection$pVoid := ToStdLib.Malloc(size:= ConnectionInfos.ConnectionNo * sizeof(t_FSoEConnectionDetails));
  
  if ConnectionInfos.pFirstConnection = NIL then
    LogError("@0282 (SafetyBaseFSoE::GetFSoEConnectionInfos) Failed to allocate memory for FSoE connection details.");
    return;
  end_if;
  
  // reset memory (also resets pNext although we are not using a linked list here)
  _memset(dest:=ConnectionInfos.pFirstConnection, usByte:=0, cntr:= ConnectionInfos.ConnectionNo * sizeof(t_FSoEConnectionDetails));

  Address += Size;
  Size    := 1;

  pActConnection := ConnectionInfos.pFirstConnection;

  // get connection states
  for i := 0 to ConnectionInfos.ConnectionNo-1 do
    // get state of connection

    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := Address
                                , us_Length           := Size
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@0283 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading connection state.");
      return;
    end_if;
    
    // get connection state
    pActConnection^.ConnectionState := (p_SDOResponseBuffer^.ud_Data$USINT AND 2#0111)$t_FSoEConnectionState;
    
    // get info if the module is master or slave at this connection
    pActConnection^.Flags.IsMaster  := (p_SDOResponseBuffer^.ud_Data$USINT AND 2#1000) <> 0;
    
    // save safety number
    pActConnection^.OwnerSafetyNbr  := SafetyNumber;
    
    // for easier debugging we set the correct next pointers
    if i = ConnectionInfos.ConnectionNo-1 then
      pActConnection^.pNext := NIL;
    else
      pActConnection^.pNext := pActConnection + sizeof(t_FSoEConnectionDetails);
    end_if;
    
    pActConnection                  += sizeof(t_FSoEConnectionDetails);
    Address                         += Size;
  end_for;

//*****************************************************************************
//** ROUTING INFO                                                            **
//*****************************************************************************

  // get number of connections
  Address := SBF_SDO_ADDRESS_FSOE_ROUTING_INFO;
  Size    := 2;

  //send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := Address
                              , us_Length           := Size
                              );

  //no response
  if ( sd_retval <> 0 ) then
    LogError("@0281 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading number of connections.");
    return;
  end_if;
  
  // check if it's the same number
  if ConnectionInfos.ConnectionNo <> p_SDOResponseBuffer^.ud_Data$UINT then
    LogError("@0285 (SafetyBaseFSoE::GetFSoEConnectionInfos) Number of connections does not match (case 1).");
    return;
  end_if;

  Address += Size;
  Size    := sizeof(t_FSoERoutingInfoElement);

  pActConnection := ConnectionInfos.pFirstConnection;

  // get connection states
  for i := 0 to ConnectionInfos.ConnectionNo-1 do
    // get routing info of connections

    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := Address
                                , us_Length           := Size
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@0286 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading routing info.");
      return;
    end_if;
    
    TempRoutingInfo := p_SDOResponseBuffer^.ud_Data$t_FSoERoutingInfoElement;
    
    // store the data in the connection info
    pActConnection^.SlaveAddress        := TempRoutingInfo.SlaveAddress;
    pActConnection^.MasterReadSize      := TempRoutingInfo.SafeOutputFrameSize;
    pActConnection^.MasterWriteSize     := TempRoutingInfo.SafeInputFrameSize;

    // the offset info depends on if we are master or slave
    if pActConnection^.Flags.IsMaster then
      pActConnection^.MasterReadOffset  := TempRoutingInfo.SafeOutputFrameOffset;
      pActConnection^.MasterWriteOffset := TempRoutingInfo.SafeInputFrameOffset;
    else
      pActConnection^.SlaveWriteOffset  := TempRoutingInfo.SafeOutputFrameOffset;
      pActConnection^.SlaveReadOffset   := TempRoutingInfo.SafeInputFrameOffset;
    end_if;
    
    pActConnection                  += sizeof(t_FSoEConnectionDetails);
    Address                         += Size;
  end_for;


//*****************************************************************************
//** CONNECTION INSTANCES                                                    **
//*****************************************************************************

  Address := SBF_SDO_ADDRESS_FSOE_CONN_INST;
  Size    := 4;

  //send request and receive data
  sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                              , p_SDOResponseBuffer := p_SDOResponseBuffer
                              , ud_Address          := Address
                              , us_Length           := Size
                              );

  //no response
  if ( sd_retval <> 0 ) then
    LogError("@0281 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading number of connections.");
    return;
  end_if;
  
  // check if it's the same number
  if ConnectionInfos.ConnectionNo <> p_SDOResponseBuffer^.ud_Data then
    LogError("@0287 (SafetyBaseFSoE::GetFSoEConnectionInfos) Number of connections does not match (case 2).");
    return;
  end_if;

  Address += Size;

  pActConnection := ConnectionInfos.pFirstConnection;

  // get connection states
  for i := 0 to ConnectionInfos.ConnectionNo-1 do
    // get routing instance info of connections

    Size := 4;

    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := Address
                                , us_Length           := Size
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@0288 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading size of routing instance info.");
      return;
    end_if;
    
    // calculate address of next connection (to check it later)
    NextAddress := Address + p_SDOResponseBuffer^.ud_Data;
    
    // get the flags
    Address   += Size;
    Size      := 4;

    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := Address
                                , us_Length           := Size
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@0289 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading flags of routing instance info.");
      return;
    end_if;

    if ((p_SDOResponseBuffer^.ud_Data AND 1) <> 0) <> pActConnection^.Flags.IsMaster then
      LogError("@028A (SafetyBaseFSoE::GetFSoEConnectionInfos) Master settings mismatch.");
      return;
    end_if;

    //! Flags
    //  Actually, type is 4 bytes in size but for compatibility reasons it is still used as 1 byte. As a result, bits 9-32 are currently not used.
    //  Bit1: 1, wenn FSoE-Master, 0=FSoE-Slave
    //  Bit2: 1, wenn die Slave-Adresse über den HW-Pfad ermittelt wird 
    //  Bit3: 1, wenn CRC von der FW ermittelt werden muss
    //  Bit4: 1, wenn Interface und Slave
    //  Bit5: 1, wenn Multichannel verwendet werden, aber ein Multichannel ist nicht vorhanden und der Eingang auf 1 gesetzt wird.
    //  Bit6: 1, wenn der Adressvergabeservice verwendet werden soll
    //  Bit7: 1, wenn der Master beim Slave die Connection ID anfordert
    //  Bit8: 1, wenn es sich um ein HGW handelt
    //  Bit9: 1, wenn Assign Slave On Demand 
    //  Bits 10-32: reserviert, sind auf 0 gesetzt und werden nicht ausgewertet 
    
    //check size of FSoEFlags type --> possible are 1 or 4 byte
    pActConnection^.Flags := p_SDOResponseBuffer^.ud_Data$t_FSoEFlags;

    if (pActConnection^.Flags AND SM_FLAGS_FLEXCON_MASK) = SM_FLAGS_FLEXCON_NODE then
#ifdef SAFETY_DEBUG_LOGGING_ACTIVATED      
      LogError(e_msg:="SafetyBaseFSoE: Found FSOE Connection of FlexConNode");
#endif
      sFlexCon.FoundNodeInConfig := TRUE;
    elsif (pActConnection^.Flags AND SM_FLAGS_FLEXCON_MASK) = SM_FLAGS_FLEXCON_HOST then
#ifdef SAFETY_DEBUG_LOGGING_ACTIVATED      
      LogError(e_msg:="SafetyBaseFSoE: Found FSOE Connection of FlexConHost");
#endif
      sFlexCon.FoundHostInConfig := TRUE;
    end_if;


    // get the net size of the safe output data (only the bits for the I/Os without crc and stuff)
    Address   += Size;
    Size      := 2;

    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := Address
                                , us_Length           := Size
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@028B (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading safe output net size of routing instance info.");
      return;
    end_if;

    // we don't need the net size yet

    // get the net size of the safe input data (only the bits for the I/Os without crc and stuff)
    Address   += Size;
    Size      := 2;

    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := Address
                                , us_Length           := Size
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@028C (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading safe input net size of routing instance info.");
      return;
    end_if;

    // we don't need the net size yet

    // get the slave address
    Address   += Size;
    Size      := 2;

    //send request and receive data
    sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                , p_SDOResponseBuffer := p_SDOResponseBuffer
                                , ud_Address          := Address
                                , us_Length           := Size
                                );

    //no response
    if ( sd_retval <> 0 ) then
      LogError("@028D (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading slave address of routing instance info.");
      return;
    end_if;
    
    // the following settings are only available in the master
    if pActConnection^.Flags.IsMaster then
      
      // get the connection ID
      Address   += Size;
      Size      := 2;
        
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@028E (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the connection ID of routing instance info.");
        return;
      end_if;
    
      pActConnection^.ConnectionID := p_SDOResponseBuffer^.ud_Data$UINT;

      // get transmission time
      Address   += Size;
      Size      := 2;

      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@028F (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the transmission time of routing instance info.");
        return;
      end_if;

      pActConnection^.TransmissionTime_ms := p_SDOResponseBuffer^.ud_Data$UINT;

      // get length of communication parameters (must be 2)
      Address   += Size;
      Size      := 2;
        
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0290 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the length of the communication parameters of routing instance info.");
        return;
      end_if;
    
      if p_SDOResponseBuffer^.ud_Data$UINT <> 2 then
        LogError("@0291 (SafetyBaseFSoE::GetFSoEConnectionInfos) Length of communication parameters is wrong.");
        return;
      end_if;

      // skip FSoE watchdog time in ms
      Address   += Size;
      Size      := 2;

      // get size of application parameters
      Address   += Size;
      Size      := 2;
    
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0292 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the length of the application parameters of routing instance info.");
        return;
      end_if;
      
      // skip length
      Address   += Size;
      
      // skip application parameters
      Size      := p_SDOResponseBuffer^.ud_Data$USINT;
      Address   += Size;
      
      // get to a 4 byte aligned address 
      if (Address MOD 4) then
        Address := Address + (4 - (Address MOD 4)); //increase memory, up to a value, which could be divided by 4
      end_if;

      // get size of hw path
      Size      := 4;

      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0293 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the hardware path length of routing instance info.");
        return;
      end_if;

      // get the hardware path
      Address   += Size;
      Size      := p_SDOResponseBuffer^.ud_Data$USINT;
      
      if Size = 0 then
        LogError("@0294 (SafetyBaseFSoE::GetFSoEConnectionInfos) Hardware path length is zero.");
        return;
      end_if;

      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0295 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the hardware path of routing instance info.");
        return;
      end_if;

      // save hardware path into structure
      pActConnection^.SlavePathLen      := Size;
      pActConnection^.pSlavePath$pVoid  := ToStdLib.Malloc(size:=Size);
      
      if pActConnection^.pSlavePath = NIL then
        LogError("@0296 (SafetyBaseFSoE::GetFSoEConnectionInfos) Failed to allocate memory for the slave hardware path at the routing instance info.");
        return;
      end_if;

      _memcpy(ptr1:=pActConnection^.pSlavePath, ptr2:=#p_SDOResponseBuffer^.ud_Data, cntr:=pActConnection^.SlavePathLen);

      Address         += Size;

      // get to a 4 byte aligned address 
      if (Address MOD 4) then
        Address := Address + (4 - (Address MOD 4)); //increase memory, up to a value, which could be divided by 4
      end_if;

      // get size of compressed hw path
      Size      := 4;

      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0297 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the compressed hardware path length of routing instance info.");
        return;
      end_if;

      // get the hardware path
      Address   += Size;
      Size      := p_SDOResponseBuffer^.ud_Data$USINT;

      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := Address
                                  , us_Length           := Size
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0298 (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the compressed hardware path of routing instance info.");
        return;
      end_if;

      // save compressed hardware path into structure
      pActConnection^.SlavePathCompLen      := Size;
      pActConnection^.pSlavePathComp$pVoid  := ToStdLib.Malloc(size:=Size);
      
      if pActConnection^.pSlavePathComp = NIL then
        LogError("@0299 (SafetyBaseFSoE::GetFSoEConnectionInfos) Failed to allocate memory for the compressed slave hardware path at the routing instance info.");
        return;
      end_if;

      _memcpy(ptr1:=pActConnection^.pSlavePathComp, ptr2:=#p_SDOResponseBuffer^.ud_Data, cntr:=pActConnection^.SlavePathCompLen);

      if  (pActConnection^.pSlavePathComp^ = 6) &
          ((pActConnection^.pSlavePathComp + 5)^$HDINT AND 16#00FFFFFF) = 0 then
         
        // special IP: (0.0.0.x)
        // if custom ip is detected change pathcomp type from 6 (IP) to 3 (Interface)
        pActConnection^.pSlavePathComp^ :=3;
      end_if;

      //Get slot ID and communication ID from HGW
      if pActConnection^.Flags.IsHGW then

        //Get machinenumber of FUB
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := SBF_SDO_ADDRESS_HGW_FUB_BASE_ADDRESS + SBF_SDO_ADDRESS_HGW_OFFSET_MACHINENUMBER
                                    , us_Length           := 4
                                    , b_UseCache          := FALSE
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@03DA (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the machinenumber of routing instance info.");
          return;
        end_if;
        
        sHGW.udMachineNrb := p_SDOResponseBuffer^.ud_Data$UDINT;
      
        // get the Slot ID
        Address   += Size;
        Size      := 2;
          
        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := Address
                                    , us_Length           := Size
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@03DB (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the communication ID of routing instance info.");
          return;
        end_if;
                
        sHGW.uiCommunicationID := p_SDOResponseBuffer^.ud_Data$UINT;
        
        // get the communication ID
        Address   += Size;
        Size      := 2;
          
        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := Address
                                    , us_Length           := Size
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@03DC (SafetyBaseFSoE::GetFSoEConnectionInfos) Error reading the slot ID of routing instance info.");
          return;
        end_if;

        sHGW.uiSlotID := p_SDOResponseBuffer^.ud_Data$UINT;
        
        //save fsoe pdo offsets and length
        sHGW.InputPDOLength  := pActConnection^.MasterWriteSize;
        sHGW.OutputPDOLength := pActConnection^.MasterReadSize;
        if pActConnection^.Flags.IsMaster then
          sHGW.InputPDOOffset  := pActConnection^.MasterWriteOffset;
          sHGW.OutputPDOOffset := pActConnection^.MasterReadOffset;
        else            
          sHGW.InputPDOOffset  := pActConnection^.SlaveWriteOffset;
          sHGW.OutputPDOOffset := pActConnection^.SlaveReadOffset;
        end_if;
        sHGW.uiTransmissionTime_ms := pActConnection^.TransmissionTime_ms;
      end_if;

      Address         += Size;

      // get to a 4 byte aligned address 
      if (Address MOD 4) then
        Address := Address + (4 - (Address MOD 4)); //increase memory, up to a value, which could be divided by 4
      end_if;
      
      if Address > NextAddress then
        LogError("@029A (SafetyBaseFSoE::GetFSoEConnectionInfos) Structure size mismatch.");
        return;
      end_if;
    end_if;

    Address         := NextAddress;       
    pActConnection  += sizeof(t_FSoEConnectionDetails);
  end_for;

END_FUNCTION


FUNCTION SafetyBaseFSoE::FreeFSoEConnectionInfos
	VAR_INPUT
		bFreeAlways 	: BOOL;(* := false *)
	END_VAR
  VAR
    i               : UDINT;
    pActConnection  : ^t_FSoEConnectionDetails;
  END_VAR

  if (ConnectionInfos.ConnectionNo = 0) | (ConnectionInfos.pFirstConnection = NIL) then
    // nothing to free
    return;
  end_if;
  
  //Check if we want to clear the connection infos always
  if bFreeAlways = false then  
    // check if all connections have been initialized
    pActConnection := ConnectionInfos.pFirstConnection;
    for i := 0 to ConnectionInfos.ConnectionNo-1 do
      if pActConnection^.ConnectionState = FCS_NOT_INITIALIZED then
        // not yet time to free them
        return;
      end_if;
      
      pActConnection += sizeof(t_FSoEConnectionDetails);
    end_for;
  end_if;

  pActConnection := ConnectionInfos.pFirstConnection;
  for i := 0 to ConnectionInfos.ConnectionNo-1 do
    // free hardware path memory

    if pActConnection^.pSlavePath then
      ToStdLib.Free(mptr:=pActConnection^.pSlavePath);
    end_if;

    if pActConnection^.pSlavePathComp then
      ToStdLib.Free(mptr:=pActConnection^.pSlavePathComp);
    end_if;
    
    pActConnection += sizeof(t_FSoEConnectionDetails);
  end_for;

  // free the connectionlist
  ToStdLib.Free(mptr:=ConnectionInfos.pFirstConnection);
  ConnectionInfos.pFirstConnection := NIL;
  ConnectionInfos.ConnectionNo := 0;

END_FUNCTION


FUNCTION SafetyBaseFSoE::CheckFSoEConnections
  VAR
    MyPara 	            : CmdStruct;
    MyResult	          : results;
    i                   : UDINT;
    pActConnection      : ^t_FSoEConnectionDetails;
    SDORequestBuffer    : SDORequReadConfig;
    SDOResponseBuffer   : ARRAY [0..SBF_SDO_MAX_LENGTH-1] OF USINT;
    p_SDORequestBuffer 	: ^SDORequReadConfig;
    p_SDOResponseBuffer : ^SDORespReadConfig;
    UnInifializedNo     : UDINT;
    sd_retval           : DINT;
  END_VAR

  // there were some of the FSoE connections not initialized when fetching the configuration data => update them

  UnInifializedNo := 0;

  //SDO request for this command has 5 bytes additional to the header
  p_SDORequestBuffer := #SDORequestBuffer;
  
  //SDO response for this command can have maximum additional data
  p_SDOResponseBuffer := (#SDOResponseBuffer)$^SDORespReadConfig;

  //calculate length without crc
  p_SDORequestBuffer^.StdHeader.Len := sizeof(SDORequReadConfig) - sizeof(SDOHeader.StdHeader.CRC);
  
  //type
  p_SDORequestBuffer^.StdHeader.Typ := SDO_STD;
  
  //command for reading config data
  p_SDORequestBuffer^.StdHeader.Cmd := SBF_SDO_COMMAND_READ;

  // reset cache, because it may contain an old access on the same address => we want fresh data
  CachedSize := 0;  

  pActConnection := ConnectionInfos.pFirstConnection;

  for i := 0 to ConnectionInfos.ConnectionNo-1 do
    if pActConnection^.ConnectionState = FCS_NOT_INITIALIZED then
      UnInifializedNo += 1;
    
      //send request and receive data
      sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                  , p_SDOResponseBuffer := p_SDOResponseBuffer
                                  , ud_Address          := SBF_SDO_ADDRESS_FSOE_CONN_STATE + 2 + i
                                  , us_Length           := 1
                                  );

      //no response
      if ( sd_retval <> 0 ) then
        LogError("@0283 (SafetyBaseFSoE::CheckFSoEConnections) Error reading connection state.");
        return;
      end_if;
      
      // get connection state
      pActConnection^.ConnectionState := (p_SDOResponseBuffer^.ud_Data$USINT AND 2#0111)$t_FSoEConnectionState;
    
      if pActConnection^.ConnectionState <> FCS_NOT_INITIALIZED then
        UnInifializedNo -= 1;
        
        // get the corresponding slave address number

        //send request and receive data
        sd_retval := SDOReadCfgRequ ( p_SDORequestBuffer  := p_SDORequestBuffer
                                    , p_SDOResponseBuffer := p_SDOResponseBuffer
                                    , ud_Address          := SBF_SDO_ADDRESS_FSOE_ROUTING_INFO + 2 + i * sizeof(t_FSoERoutingInfoElement)
                                    , us_Length           := sizeof(t_FSoERoutingInfoElement)
                                    );

        //no response
        if ( sd_retval <> 0 ) then
          LogError("@0286 (SafetyBaseFSoE::CheckFSoEConnections) Error reading routing info.");
          return;
        end_if;

        pActConnection^.SlaveAddress := p_SDOResponseBuffer^.ud_Data$t_FSoERoutingInfoElement.SlaveAddress;

        if pActConnection^.SlaveAddress = 0 then
          LogError("@029B (SafetyBaseFSoE::CheckFSoEConnections) Connection initialized but slave address is still zero!");
        end_if;
      end_if;
    end_if;

    pActConnection                  += sizeof(t_FSoEConnectionDetails);
  end_for;

  // if all connections are initialized => tell the safety manager
  if (UnInifializedNo = 0) & pSafetyManagerThis then
    MyPara.uiCmd    := CMD_SM_FSOE_CON_INITIALIZED;
    MyPara.aPara[0] := SafetyNumber$DINT;
    MyPara.aPara[1] := ConnectionInfos.ConnectionNo$DINT;
    MyPara.aPara[2] := ConnectionInfos.pFirstConnection$DINT;

    if pSafetyManagerThis^.NewInst(#MyPara, #MyResult) = READY then
      // we don't need this memory anymore
      FreeFSoEConnectionInfos();
    end_if;
  end_if;

END_FUNCTION

FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::GetDiagConnect::NewInst
  VAR_INPUT
    pPara 	: ^CmdStruct;
    pResult 	: ^Results;
  END_VAR
  VAR_OUTPUT
    ret_code 	: IprStates;
  END_VAR
  
  ret_code := READY;

  CASE pPara^.uiCmd OF
      
    CMD_SM_DIAG_GET_INFO: 
      
      //Subcommand
      case pPara^.aPara[0] of
//*****************************************************************************
        SBCMD_SM_DIAG_GET_INFO: 
          pResult^.aData[0] :=  GetDiagInfo(pDiagInfo:=(pPara^.aPara[1])$^SafetyDiagInfo);
               
//*****************************************************************************
        SBCMD_SM_DIAG_GET_STATE:
          pResult^.aData[0] :=  GetDiagState(pDiagState:=(pPara^.aPara[1])$^SafetyDiagState);
      
//*****************************************************************************
        SBCMD_SM_DIAG_FETCH_INFO:
      //Fetch Diag Info for uCx
          FetchDiagInfo(ucChoice:=pPara^.aPara[1]$USINT);          
    
//*****************************************************************************
        SBCMD_SM_DIAG_FETCH_STATE:
          FetchDiagState();
               
//*****************************************************************************
        SBCMD_SM_DIAG_IS_SAVE_LOG_ACTIVE:
          pResult^.aData[0] := SaveLogActive();
      
//*****************************************************************************
        SBCMD_SM_DIAG_SAVE_LOGFILE:
          pResult^.aData[0] := (SaveLog(ucChoice:=(pPara^.aPara[1])$USINT, pDPNE:=(pPara^.aPara[2])$^CHAR))$USINT;

//*****************************************************************************
        SBCMD_SM_DIAG_GET_SAVE_LOGFILE_STATE:
          pResult^.aData[0] := DiagVars.SavingLogSuccessful$USINT;
      
//*****************************************************************************
        SBCMD_SM_DIAG_GET_SAFETY_NUMBER:     
          pResult^.aData[0]$UDINT := SafetyNumber;
      
//*****************************************************************************
        SBCMD_SM_DIAG_GET_SAFETY_STATE:
          pResult^.aData[0] := SafetyState$USINT;

//*****************************************************************************          
        SBCMD_SM_DIAG_IS_DIAGNOSE_AVAILABLE:          
          pResult^.aData[0] := TRUE;
          
//*****************************************************************************          
        SBCMD_SM_DIAG_GET_CHANNEL_INFO:
          pResult^.aData[0] := GetChannelInfo(pErrorMemUser:=(pPara^.aPara[1])$^UINT, pErrorMem:=#aErrorMem[1], ErrorDataLength:=aErrorMem[0] - 1);
          pResult^.aData[1] := aErrorMem[0] - 1;
          
//*****************************************************************************          
        SBCMD_SM_DIAG_FETCH_CHANNEL_INFO:
          FetchChannelInfo();
          
//*****************************************************************************          
        SBCMD_SM_DIAG_GET_ERRORCODE_OFFSET:
                    
          //Check for valid version
          if pPara^.aPara[1] = 0x01 then  //Version
            
            ret_code := READY;          
            pResult^.aData[0] := 0x01;  //return version
            pResult^.aData[1]$UDINT := GetDiagVisualOffsetErrorCode();
            
            pResult^.uiLng := 12;
            
          else
            
            //invalid version, return error
            ret_code := ERROR;

            pResult^.aData[0] := 0x0;
            pResult^.uiLng    := 4;
            
          end_if;
          
        END_CASE;
    else
      TRACE("(SafetyBaseFSoE::GetDiagConnect::NewInst) Unknown command!");
      ret_code := ERROR;
  END_CASE;
  
END_FUNCTION


FUNCTION SafetyBaseFSoE::GetNewMemSize
  VAR_INPUT
    Required 	: UDINT;
  END_VAR
  VAR_OUTPUT
    NextBiggerBlockSize 	: UDINT;
  END_VAR
  
  // how many complete blocks are necessary
  NextBiggerBlockSize := Required / SBF_CONFIG_MEMORY_BLOCK_SIZE;
  
  // is there also an incomplete block?
  if Required MOD SBF_CONFIG_MEMORY_BLOCK_SIZE then
    NextBiggerBlockSize += 1;
  end_if; 
  
  // now that we have the number of blocks necessary => get number of bytes
  NextBiggerBlockSize *= SBF_CONFIG_MEMORY_BLOCK_SIZE;

END_FUNCTION

FUNCTION SafetyBaseFSoE::CheckAddDynSlaveResponse
  VAR_INPUT
    pAddDynSlaveRsp 	: ^t_AddDynSlaveResponse;
  END_VAR
  VAR_OUTPUT
    ResponseOk 	: BOOL;
  END_VAR

  // check if any of the offset/length combination exceeds our access to the module
  if ((pAddDynSlaveRsp^.OffsetSafeInData  + pAddDynSlaveRsp^.LenSafeInData ) > MyPDOWriteSize) | 
     ((pAddDynSlaveRsp^.OffsetSafeOutData + pAddDynSlaveRsp^.LenSafeOutData) > MyPDOReadSize ) then
    ResponseOk := FALSE;

    LogError("@029C (SafetyBaseFSoE::CheckAddDynSlaveResponse) Invalid offset/length combination of read or write data in AddDynSlave response.");
  else
    ResponseOk := TRUE;
  end_if;

END_FUNCTION


FUNCTION SafetyBaseFSoE::GetChannelInfo
  VAR_INPUT
    pErrorMemUser 	: ^UINT;
    pErrorMem 	: ^USINT;
    ErrorDataLength 	: INT;
  END_VAR
  VAR_OUTPUT
    StateReady 	: BOOL;
  END_VAR
  VAR
    i   : INT;
  END_VAR


  StateReady := DiagVars.UserErrorInfoReady;
  
  if StateReady then
    //copy error values in UserArray
    for i := 0 to ErrorDataLength-1 do
      
      pErrorMemUser^ := pErrorMem^; 
      
      if (pErrorMemUser^ <> 0) then
        pErrorMemUser^ += GetDiagVisualOffsetErrorCode()$UINT; //Add offset to each errorvalue so that it matches the value in SafetyDesigner.     
      end_if;        

      //set pointer to next entry
      pErrorMemUser += sizeof(UINT);
      pErrorMem += sizeof(USINT);
    end_for;   
  end_if;
 
END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::FetchChannelInfo
  
  DiagVars.UserErrorInfoReady := FALSE;
  DiagVars.UserWantsErrorInfo := TRUE;

END_FUNCTION


#pragma warning(disable:73);
FUNCTION VIRTUAL SafetyBaseFSoE::SafeActModuleConfig
  VAR_INPUT
    sActModuleConfig 	: t_ModuleCfg;
  END_VAR
  //overwrite this function at derived class to safe module config
END_FUNCTION
#pragma warning(default:73);


FUNCTION VIRTUAL SafetyBaseFSoE::GetDiagVisualOffsetErrorCode
	VAR_OUTPUT
		udOffset 	: UDINT;
	END_VAR
  
  //In the safety firmware for the CSCP and SCP there is only one byte for the errorcode
  //To have different errorcodes (between CSCP and SCP) the safetydesigner adds a fix offset of 1000 to the errorcodes of the SCP01x and SCP11x
  udOffset := 1000;

END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::AddFastUnsafeRoutElement
	VAR_INPUT
		pRoutTable 	: ^pUnsafeIORoutingElement;
		pRoutElementsNo 	: ^UINT;
		ud_Address 	: UDINT;
		ui_NameLen 	: UINT;
		pNameNewElement 	: ^CHAR;
		BitMask 	: BDINT;(* := 0 *)
		bWriteVar 	: BOOL;
	END_VAR
	VAR_OUTPUT
		sd_retval 	: DINT;
	END_VAR
  VAR
    p_RoutElement   : ^UnsafeIORoutingElement;
  END_VAR 
   
  sd_retval := -1;

  // check parameters
  if (ui_NameLen = 0) | (pNameNewElement = NIL) then
    LogError("@0240 (SafetyBaseFSoE::AddFastUnsafeRoutElement) Invalid call parameters for the variable name (invalid configuration in safety cpu).");
    return;
  end_if;    
  
  //With the first element, the table must be positioned
  if ( pRoutTable^ = NIL ) then
  
    //allocate memory for first routing element
    pRoutTable^$^void := ToStdLib.Malloc(size := sizeof(UnsafeIORoutingElement));
  
    if ( pRoutTable^ = NIL ) then
  
      sd_retval                         := -1;
      SetSafetyState(NewState:=_MemAllocFailed);
      return;
      
    end_if;
    
    p_RoutElement := pRoutTable^;
    
  else
    
    //add new element
    
    pRoutTable^$^void := ToStdLib.ReAlloc(mptr := pRoutTable^, newsize := ( ( pRoutElementsNo^ + 1 ) * sizeof(UnsafeIORoutingElement)));

    if ( pRoutTable^ = NIL ) then
    
      sd_retval                         := -1;
      SetSafetyState(NewState:=_MemAllocFailed);
      return;
      
    end_if;
    
    //set pointer to empty element on last position
    p_RoutElement := pRoutTable^ + ( pRoutElementsNo^ ) * sizeof(UnsafeIORoutingElement);

  end_if;  
  
  if p_RoutElement then
    //allocate memory for name
    p_RoutElement^.pName$^void := ToStdLib.Malloc(size := ui_NameLen);
  
    if ( p_RoutElement^.pName = NIL ) then
  
      sd_retval                         := -1;
      SetSafetyState(NewState:=_MemAllocFailed);
      return;
      
    end_if;

    p_RoutElement^.NameLength := ui_NameLen;
    _memcpy(ptr1:=p_RoutElement^.pName, ptr2:=pNameNewElement, cntr:=ui_NameLen);
    
    p_RoutElement^.BitMask := BitMask;
    
    // initialize with invalid
    p_RoutElement^.b_IsValid            := FALSE;
    p_RoutElement^.sd_OldValue          := (16#80000000)$DINT;
    p_RoutElement^.sd_NewValue4External := (16#80000000)$DINT;

    // remember that we haven't transmitted this value so far
    p_RoutElement^.b_AlreadyTransmitted := FALSE;
    
    if (aFastUnsafeReadDOs[0].pData <> NIL) | (aFastUnsafeWriteDOs[0].pData <> NIL) then
      
      //check if it's a write or read variable
      if bWriteVar = TRUE then
        //add output variable
        if pFastUnsafeOutBuffer then
          p_RoutElement^.ud_Address := pFastUnsafeOutBuffer$UDINT + ud_Address;         
        else
          LogError("@0788 (SafetyBaseFSoE::AddFastUnsafeRoutElement) Add Fast output Variable failed. Invalid Outputbuffer.");
          return;
        end_if;
    
      else
        //add Input variable
        if pFastUnsafeInBuffer then
          p_RoutElement^.ud_Address := pFastUnsafeInBuffer$UDINT + ud_Address;           
        else
          LogError("@0789 (SafetyBaseFSoE::AddFastUnsafeRoutElement) Add Fast input Variable failed. Invalid Inputbuffer.");
          return;        
        end_if;
  
      end_if;
    end_if;

    // initialize next pointer
    p_RoutElement^.pNext := NIL;
        
    // check if name exists in PLC
    p_RoutElement^.ud_Thisp   := GetThisViaServerName(pNameNewElement)$UDINT;
    if (pRoutTable = #p_UnsafeOutputsTable) & p_RoutElement^.BitMask then
      p_RoutElement^.UnsafeType := BDINTOutput;
    else
      p_RoutElement^.UnsafeType := NormalUnsafe;
    end_if;

    //increase counter
    pRoutElementsNo^ += 1;
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL SafetyBaseFSoE::UpdateSafetyNbrAtRoutingElement
	VAR_INPUT
		PathLen 	: UINT;
		pCompressedPath 	: ^USINT;
		SafetyNbr 	: HDINT;
	END_VAR
	VAR_OUTPUT
		Retval 	: DINT;
	END_VAR
  VAR
  	pRoutElement : ^RoutElement;
    i                 : UINT;
  END_VAR

  // initialize the retcode with error
  Retval := -1;

  // check if the path is among our routing elements

  //check each element
  if ( ui_CntRoutElements > 0 ) then
    //set pointer on first routing element
    pRoutElement := p_RoutTable;
    
    for i := 0 to (ui_CntRoutElements - 1) do 
      
      // if there's a path and the length matches
      if pRoutElement^.p_CompressedPath & (pRoutElement^.ui_ComprPathLen = PathLen) then
      
        // check the path content
        if ToStdLib.MemCmp(dest:=pCompressedPath, source:=pRoutElement^.p_CompressedPath , size:=PathLen) = 0 then

          // if it's the correct path, set the new safety number for that object
          pRoutElement^.ud_SafeNumber := SafetyNbr;

          // success
          Retval := 0;
          return;
          
        end_if;
      end_if;
      
      //set pointer to next element
      pRoutElement += sizeof ( RoutElement );
    end_for;
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL GLOBAL SafetyBaseFSoE::QuitError
	VAR_INPUT
		Mode 	: DINT;
	END_VAR
	VAR_OUTPUT
		Retval 	: DINT;
	END_VAR

  case Mode of
//**********************************************************************************************************************************************************
    0: // compatibility mode for SCP111 (if quit error via DLL available, use it with submodules. otherwise use the "only quit comm errors" variant)
      if QuitErrorAvailable then
        // initialize the parameter for compatible behavior (quit with submodules)
        QuitErrorParameter := 1;
        
        // remember to do it in next background
        QuitComError := 2;
      else
        // remember. that there has been a manual quit (for writing PDOs in PostScan)
        b_ManualQuit := TRUE;
        
        // remember to do it in next cyclic
        QuitComError := 1;
      end_if;

//**********************************************************************************************************************************************************
    1: // only quit the communication errors (send set bus ready signal)

      // remember. that there has been a manual quit (for writing PDOs in PostScan)
      b_ManualQuit := TRUE;
      
      // remember to do it in next cyclic
      QuitComError := 1;

//**********************************************************************************************************************************************************
    2: // quit error with submodules

      // check if the function is supported by the CIL interface
      if QuitErrorAvailable then
        // initialize the parameter to quit with submodules
        QuitErrorParameter := 1;
        
        // remember to do it in next background
        QuitComError := 2;
      else
        // function not available
        QuitComError := -8;
      end_if;
    
//**********************************************************************************************************************************************************
    3: // quit error without submodules (the current module only)
    
      // check if the function is supported by the CIL interface
      if QuitErrorAvailable then
        // initialize the parameter to quit without submodules
        QuitErrorParameter := 0;
        
        // remember to do it in next background
        QuitComError := 3;
      else
        // function not available
        QuitComError := -8;
      end_if;
    
//**********************************************************************************************************************************************************
  else
    // invalid mode selected
    QuitComError := -7;
  end_case;

  // refresh the server on the outer object
  RefreshAsySrv();

  // return the current server value
  Retval := QuitComError;

END_FUNCTION
