//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

//Address to access the Dataobject list via SPI SDO Read
#define SDIAS_SPI_DATA_OBJECT_LIST_PAGE_ADDRESS               16#FFFF
#define SDIAS_SPI_DATA_OBJECT_LIST_PAGE_OFFSET                16#00
#define SDIAS_SPI_DATA_OBJECT_LIST_IDENTIFIER                 16#12345678

//Defines the number of optional list elements in the SPI List

#define SDIAS_SPI_NUMBER_OF_OPTIONAL_ELEMENTS                 16   

#define SDIAS_SPI_HEADER_BEFORE_OPTIONAL_ELEMENTS             16#18
#define SDIAS_SPI_HARDWARE_VERSION_OFFSET                     16#C
#define SDIAS_SPI_VENDOR_ID_OFFSET                            16#0

#define SDIAS_SPI_MASTER_ADRESS_TRANSMIT_BUFFER0              16#0000
#define SDIAS_SPI_MASTER_ADRESS_TRANSMIT_BUFFER1              16#0080
#define SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER0               16#0000
#define SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER1               16#0080
#define SDIAS_SPI_MASTER_ADRESS_PAGE_ADRESS                   16#0100
#define SDIAS_SPI_MASTER_ADRESS_PREDEFINED_COMMAND_REGISTER   16#0102
#define SDIAS_SPI_MASTER_ADRESS_SINGLE_COMMAND_REGISTER       16#0103
#define SDIAS_SPI_MASTER_ADRESS_STATUS_REGISTER               16#0104
#define SDIAS_SPI_MASTER_ADRESS_REQUEST_REGISTER              16#0105
#define SDIAS_SPI_MASTER_ADRESS_GRANT_REGISTER                16#0106
#define SDIAS_SPI_MASTER_ADRESS_CLEAR_REQUEST_REGISTER        16#0107
#define SDIAS_SPI_MASTER_ADRESS_FEATURE_REGISTER              16#0108

#define SDIAS_SPI_MASTER_SOFTWARE_REQUEST                     1

#define SDIAS_SPI_MASTER_COMMAND_PAGE_READ                    16#01
#define SDIAS_SPI_MASTER_COMMAND_PAGE_PROGRAM                 16#02
#define SDIAS_SPI_MASTER_COMMAND_SECTOR_ERASE                 16#03
#define SDIAS_SPI_MASTER_COMMAND_REBOOT_FPGA                  16#F0

#define SDIAS_SPI_MASTER_STATE_BUSY                           16#00
#define SDIAS_SPI_MASTER_STATE_READY                          16#01

#define SDIAS_SPI_SECTOR_SIZE                                 65536 // 256*256
#define SDIAS_SPI_PAGE_SIZE                                   256

#define SDIAS_SPI_WRITE_MAX_RETRIES                           2
#define SDIAS_SPI_TIMEOUT                                     500  //timeout for spi accesses
#define SDIAS_SPI_LOCK_TIMEOUT                                6   //this value is multiplied with the buscycletime [µs] (6*1000=6.000 msec timeout)


//SPI List IDs
#define SDIAS_SPI_IDENTIFICATION_LIST_ID  1
#define SDIAS_SPI_FLASH_CONFIGURATION_ID  2
#define SDIAS_SPI_VENDOR_NAME_ID          3
#define SDIAS_SPI_DEVICE_NAME_ID          4
#define SDIAS_SPI_SERIAL_NUMBER_ID        5
#define SDIAS_SPI_ORDERING_NUMBER_ID      6
#define SDIAS_SPI_DOCUMENTATION_PDF_ID    7
#define SDIAS_SPI_CALIB_DATA_ID           10
#define SDIAS_SPI_PDO_INFORMATION_ID      128

//SPI Access
#define SDIAS_SPI_ACCESS_ID_HW_TREE          1
#define SDIAS_SPI_ACCESS_ID_HWK_INTERNAL     2
//}}LSL_DEFINES

//{{LSL_DECLARATION
#include "..\..\Class\SdiasManager\SdiasManager.h"
#include "..\..\Class\SdiasBase\SdiasBase.h"
#include "..\..\Class\HwControl\HwControl.h"
#include "..\..\Source\interfaces\lsl_st_hardwaretree.h"
#include "..\..\Source\interfaces\lsl_st_mt.h"

(*!
<Class
	Name               = "SdiasBase"
	Revision           = "1.60"
	GUID               = "{A44D11C6-5133-4AE2-9A40-4704050522AE}"
	RealtimeTask       = "false"
	CyclicTask         = "false"
	BackgroundTask     = "false"
	Sigmatek           = "true"
	OSInterface        = "false"
	HighPriority       = "false"
	Automatic          = "false"
	UpdateMode         = "Prescan"
	IconPath           = ".\Class\Tool.ico"
	SharedCommandTable = "true"
	Objectsize         = "(398,120)"
	Comment            = "The Base hardware class for SDIAS Modules.">
	<Channels>
		<Server Name="ClassState" GUID="{070CD62D-F011-403B-A668-118E2A1842A7}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Server shows the current Status of the Hardware Class."/>
		<Server Name="DeviceID" GUID="{1F67076F-D511-400A-8CEF-0F0E0579EC0B}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Server shows the DeviceID of the Hardware Module."/>
		<Server Name="FPGAVersion" GUID="{D5EF701A-412A-4C34-AA2E-FFC7D82C2F86}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Server shows the actual FPGA Version of the Module in the format x.y&#13;&#10;&#13;&#10;e.g. 16#10 means v1.0"/>
		<Server Name="HwVersion" GUID="{6BF31076-362C-4517-9DA7-F776C9F85012}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="shows hw version in the format xx.yy (16#0120 means v1.20)"/>
		<Server Name="LEDControl" GUID="{FA9DCFC5-B668-4B7B-8E80-E0410DEF9ED8}" Visualized="true" Initialize="true" DefValue="0" WriteProtected="false" Retentive="false" Comment="Sets a LED on Hardware, to detect Module.&#13;&#10;0 ... LED off&#13;&#10;1 ... LED on&#13;&#10;2 ... blinking slow&#13;&#10;3 ... blinking fast"/>
		<Server Name="RetryCounter" GUID="{833C80D1-4726-4DCB-B1AB-C268D7D72245}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Number of Retries on SDIAS-Bus."/>
		<Server Name="SDOState" GUID="{1C3F8C9A-F083-486D-A096-B459EA29A017}" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="State of the actual SDO Transmission."/>
		<Server Name="SerialNo" GUID="{68FBC413-E3CB-4059-BC09-1D975B4D4819}" Class="String" Visualized="true" Initialize="false" WriteProtected="true" Retentive="false" Comment="Serial number of the Hardware Module."/>
		<Client Name="Place" Required="false" Internal="false" DefValue="0" Comment="Place of SDIAS Modul [0..63]."/>
		<Client Name="Required" Required="true" Internal="false" DefValue="1" Comment="0 = module is not required&#13;&#10;1 = module is required (CPU will crash if module is missing)"/>
		<Client Name="SdiasIn" Required="true" Internal="false" Comment="This Clients must be connected to a SDIAS_Out_[x] Server."/>
		<Client Name="SerialNoStr" Required="true" Internal="true"/>
		<Client Name="ToStdLib" Required="false" Internal="false" Comment="This client gets automatically connected to class _StdLib."/>
	</Channels>
	<Dependencies>
		<Files>
			<File Path=".\Class\SdiasManager\SdiasManager.h" Include="true"/>
			<File Path=".\Class\SdiasBase\SdiasBase.h" Include="true"/>
			<File Path=".\Class\HwControl\HwControl.h" Include="true"/>
			<File Path=".\Source\interfaces\lsl_st_hardwaretree.h" Include="true"/>
			<File Path=".\Source\interfaces\lsl_st_mt.h" Include="true"/>
			<File Path=".\Class\SigCLib\SigCLibNew.h" Include="false"/>
		</Files>
	</Dependencies>
	<RevDoku>
		<Owner Company="Sigmatek" Author="RamAnd"/>
		<Dokumentation Revision="1.60" Date="12.11.2019" Author="RamAnd" Company="Sigmatek" Description="Detached the logging functionality from HwControl into a new class HwcLogging, therefore the dependency at this class has been changed to HwcLogging."/>
		<Dokumentation Revision="1.59" Date="09.08.2018" Author="PieSte" Company="Sigmatek" Description="Add function for SPI write (only used internal)."/>
		<Dokumentation Revision="1.58" Date="31.07.2018" Author="LanSte&#13;&#10;ZoePat" Company="Sigmatek" Description="Added support for dynamic accesses.&#13;&#10;Added NewInst-command &quot;SDIAS_CLT_GET_INFO_PLACE&quot; to get the place."/>
		<Dokumentation Revision="1.57" Date="13.03.2018" Author="LanSte&#13;&#10;PieSte" Company="Sigmatek" Description="Added method GetHWTimeStamp.&#13;&#10;Add numbers for the assignment of the log messages."/>
		<Dokumentation Revision="1.56" 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.55" Date="23.01.2018" Author="RamAnd&#13;&#10;ZoePat" 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.&#13;&#10;If module is deacitvated by using DEACITVATED_LSL the client place is also set to the value DEACTIVATED_LSL."/>
		<Dokumentation Revision="1.54" Date="22.06.2017" Author="RamAnd" Company="Sigmatek" Description="Mutex-Pointers that have been added in v1.53 have not been initialized, which could have lead to a crash on RTK platforms (on Salamander the memory is initialized with 0 at startup)."/>
		<Dokumentation Revision="1.53" Date="10.05.2017" Author="RamAnd" Company="Sigmatek" Description="When adding an SDO (i.e. via ReadSPIData) from background priority it was possible that the adding process got interrupted by receiving the answer to this SDO which is handled in cyclic priority. In the error case the answer was unexpected and therefore ignored. Then the class signals a timeout while waiting for the answer, which already arrived before waiting for it. Added mutex and changed instruction order to prevent this error."/>
		<Dokumentation Revision="1.52" Date="01.03.2016" Author="EisMic" Company="Sigmatek" Description="Corrected BoundExceed Error at Write method of LEDControl Server when Place = LSL_DEACTIVATED."/>
		<Dokumentation Revision="1.51" Date="22.06.2015" Author="EisMic" Company="Sigmatek" Description="Don&apos;t reset values from server FPGAVersion, SerialNo and Device ID when module gets disconnected."/>
		<Dokumentation Revision="1.50" Date="17.03.2015" Author="RamAnd" Company="Sigmatek" Description="Added HW Version."/>
		<Dokumentation Revision="1.49" Date="10.02.2015" Author="PieSte" Company="Sigmatek" Description="Corrected read spi data for hardwaretree in methode GetSPIDataFromID."/>
		<Dokumentation Revision="1.48" Date="04.11.2014" Author="PieSte" Company="Sigmatek" Description="Added possibility  to read more data from spi flash then one sdo access.&#13;&#10;Set methode GetDeviceID to global.&#13;&#10;Add methode to calculate checksum of spi flash."/>
		<Dokumentation Revision="1.47" Date="30.09.2014" Author="RamAnd" Company="Sigmatek" Description="Added possibility to create a SDIAS control access."/>
		<Dokumentation Revision="1.46" Date="25.08.2014" Author="LanSte" Company="Sigmatek" Description="Moved defaut call of &apos;GetPointer2ObjectPath&apos; to last init phase, so that it can be overwritten by other classes. (SdiasSafetyManager)"/>
		<Dokumentation Revision="1.45" Date="24.06.2014" Author="EisMic" Company="Sigmatek" Description="Added support for reading hardware diagnosis and hardwaretree entry."/>
		<Dokumentation Revision="1.44" Date="17.07.2014" Author="ZoePat" Company="Sigmatek" Description="Added function StartSDOIntelligent() to send a intelligent SDO to a module."/>
		<Dokumentation Revision="1.43" Date="02.07.2014" Author="PieSte" Company="Sigmatek" Description="Added NewInst commands to forward them to SdiasManager."/>
		<Dokumentation Revision="1.42" Date="18.06.2014" Author="RamAnd" Company="Sigmatek" Description="Added new firmware error state bit which is set at not supported bus cycle times."/>
		<Dokumentation Revision="1.41" Date="12.03.2014" Author="RamAnd" Company="Sigmatek" Description="Added possibility to use internal logging functions via NewInst."/>
		<Dokumentation Revision="1.40" Date="22.01.2014" Author="PieSte" Company="Sigmatek" Description="Read Client Required and Place in Init SdiasBase"/>
		<Dokumentation Revision="1.30" Date="05.12.2013" Author="RamAnd" Company="Sigmatek" Description="Changed login mechanism to sdias manager class to be independent of bus accesses.&#13;&#10;Added method AddAccesses to define special accesses which are not covered by the SDIAS info block."/>
		<Dokumentation Revision="1.20" Date="30.08.2013" Author="RamAnd" Company="Sigmatek" Description="Added UpdateCy for SDIAS modules."/>
		<Dokumentation Revision="1.10" Date="01.08.2013" Author="ObeChr&#13;&#10;ObeChr&#13;&#10;RamAnd&#13;&#10;RamAnd&#13;&#10;RamAnd" Company="Sigmatek" Description="Renamed Variables&#13;&#10;Added Comments to Clients / Server / Methods / Types&#13;&#10;Created hardware paths for SDIAS Clients (needed for safety).&#13;&#10;The class now also shows the FPGA Version on a new Server.&#13;&#10;In the first realtime cycles the classes showed _ClassOk, but there was no actual IO information. Now the ClassState of SDIAS Clients is stored temporary during init and shown at the server after init."/>
		<Dokumentation Revision="1.0" Date="18.07.2013" Author="RamAnd" Company="Sigmatek" Description="First library version"/>
	</RevDoku>
	<Network Name="SdiasBase">
		<!-- List of Components in this network -->
		<Components>
			<Object
				Name       = "SerialNoStr"
				GUID       = "{016F794C-857F-482F-8C65-B5EE24158A3F}"
				Class      = "String"
				Position   = "(210,270)"
				Visualized = "true">
				<Channels>
					<Server Name="Data"/>
					<Client Name="SingleRealloc" Value="1"/>
				</Channels>
			</Object>
		</Components>
		<Comments>
		</Comments>
		<!-- List of Connections in this network -->
		<Connections>
			<Connection Source="this.SerialNoStr" Destination="SerialNoStr.Data"/>
			<Connection Source="this.SerialNo" Destination="SerialNoStr.Data" Vertices="(698,450),(526,360),"/>
		</Connections>
		<!-- Headerfiles -->
		<Options>
		</Options>
	</Network>
</Class>
*)
SdiasBase : CLASS
	TYPE
	  RequiredReferenceToHWCLogging : ^HwcLogging;
	  RequiredReferenceToSigCLib : ^SigCLib;
	  t_b_SdiasFWState : BINT  //! <Type Public="true" Comment="Statebits of FW" Name="t_b_SdiasFWState"/>
	  [
	    1 VoltageNotOk,  //! <Type Comment="Problem with Supply-Voltage of uC." Name="t_b_SdiasFWState.VoltageNotOk"/>
	    2 NoSync,  //! <Type Comment="No Sync from SDIAS Message." Name="t_b_SdiasFWState.NoSync"/>
	    3 FlashDataCRCError,  //! <Type Comment="CRC-Error in Flash." Name="t_b_SdiasFWState.FlashDataCRCError"/>
	    4 RAMDataCRCError,  //! <Type Comment="CRC-Error in RAM." Name="t_b_SdiasFWState.RAMDataCRCError"/>
	    5 FlashUnsafe,  //! <Type Comment="No valid EEProm / Flash - Version used." Name="t_b_SdiasFWState.FlashUnsafe"/>
	    6 BusTimeNotSupported,  //! <Type Comment="Measured bus cycle time is not supported by this module" Name="t_b_SdiasFWState.BusTimeNotSupported"/>
	    7 ToggleBit,
	  ];
	  t_b_SingleLog : BDINT
	  [
	    1 LedControlFailed,
	    2 GetHwPathFailed,
	  ];
	  t_e_Get_SPI_Data_SSW :
	  (
	    _GetDataObjectList:=0,
	    _CheckID,
	    _AllocateMemory:=1,
	    _GetData
	  )$UDINT;
	  t_e_SPI_SSW :
	  (
	    _GetVendorName:=0,
	    _GetDeviceID_HWVersion:=1,
	    _GetDeviceName:=2,
	    _GetOrderingNo:=3,
	    _SPIDataReady:=4
	  )$UDINT;
	  t_e_SPIWriteSSW :
	  (
	    _SPIWrite_Idle,
	    _AllocateMemory,
	    _SetRequest,
	    _ReadGrant,
	    _ReadSectorData_SetPageAddress,
	    _ReadSectorData_Wait4SetPageAddress,
	    _ReadSectorData_SetReadPageCommand,
	    _ReadSectorData_Wait4SetReadPageCommand,
	    _ReadSectorData_ReadReceiveBufferPart1,
	    _ReadSectorData_ReadReceiveBufferPart2,
	    _EraseSector_SetPageAddress,
	    _EraseSector_Wait4SetPageAddress,
	    _EraseSector_SetEraseSectorCommand,
	    _EraseSector_Wait4SetEraseSectorCommand,
	    _WriteSectorData_SetPageAddress,
	    _WriteSectorData_Wait4SetPageAddress,
	    _WriteSectorData_WriteTransmitBufferPart1,
	    _WriteSectorData_WriteTransmitBufferPart2,
	    _WriteSectorData_SetWritePageCommand,
	    _WriteSectorData_Wait4SetWritePageCommand,
	    _VerifySectorData_SetPageAddress,
	    _VerifySectorData_Wait4SetPageAddress,
	    _VerifySectorData_SetReadPageCommand,
	    _VerifySectorData_Wait4SetReadPageCommand,
	    _VerifySectorData_ReadReceiveBufferPart1,
	    _VerifySectorData_ReadReceiveBufferPart2,
	    _VerifySectorData_CompareData,
	    _ClearRequest,
	    _DisableDataObjects,
	    _FreeMemory,
	    _FinishSPIWrite
	  )$UDINT;
	  t_GetObjectList_SSW :
	  (
	    _GetObjList,
	    _GetOptionalLists:=1
	  )$UDINT;
	  t_ResponseInfo :
	  (
	    idle,
	    wait,
	    valid
	  )$UDINT;
#pragma pack(push, 1)
	  t_s_ErrorInfo : STRUCT  //! <Type Comment="ErrorInformations for SDO-Communication." Name="t_s_ErrorInfo"/>
	    Offset : HINT;  //! <Type Comment="Address-Offset of SDO-Command." Name="t_s_ErrorInfo.Offset"/>
	    Length : USINT;  //! <Type Comment="Data-Length of SDO-Command." Name="t_s_ErrorInfo.Length"/>
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_s_ErrorInfoSPI : STRUCT
	    PageAdress : HINT;
	    PageOffset : HSINT;
	    Length : USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_s_FWConfigStateStd : STRUCT
	    hCRC : HINT;
	    uDataLength : UINT;
	    hFWVersion : UINT;
	    MessageCounter : USINT;
	    Reserved : USINT;
	    ProtocolVersionSupported : USINT;
	    ProtocolVersionActive : USINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_s_SdiasFWStateStandard : STRUCT  //! <Type Public="true" Comment="Statebits of FW." Name="t_s_SdiasFWStateStandard"/>
	    ErrorBits : t_b_SdiasFWState;  //! <Type Comment="Statebits of FW." Name="t_s_SdiasFWStateStandard.ErrorBits"/>
	    Reserved : UINT;  //! <Type Comment="Reserved for Future Use." Name="t_s_SdiasFWStateStandard.Reserved"/>
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_s_SPI_AccessControl : STRUCT
	    udAccessHandle : UDINT;
	    usAccessLength : USINT;
	    usAccessOffset : USINT;
	    pControlByte : t_SDIAS_pControlByte;
	    pStateByte : t_SDIAS_pStateByte;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_s_SPIProtect : STRUCT
	    eSPIAccessProtect :
	    (
	      _NotLocked,
	      _HWTree,
	      _HWKInternal
	    )$UDINT;
	    udSPILastLockTime : UDINT;
	    bActivateProtectionCheck : BOOL;
	  END_STRUCT;
#pragma pack(pop)
	  t_SPI_PDO_DataWrite : ARRAY [0..SDIAS_MAX_PDO_SIZE-1] OF USINT;
	  t_SPI_PDO_DataRead : ARRAY [0..SDIAS_MAX_PDO_SIZE-1] OF USINT;
	  usWriteModes : USINT;  //! <Type Comment="defines write mode for spi data&#13;&#10; 0 .. read sector - erase sector - write sector&#13;&#10; 1 .. read page - erase sector - write page&#13;&#10; 2 .. erase sector - write sector&#13;&#10; 3 .. erase sector - write page&#13;&#10; 4 .. write sector&#13;&#10; 5 .. write page" Name="usWriteModes"/>
#pragma pack(push, 1)
	  t_s_SPIWriteVars : STRUCT  //! <Type Public="true" Name="t_s_SPIWriteVars"/>
	    sWriteAccess : t_s_SPI_AccessControl;
	    pWriteData : ^t_SPI_PDO_DataWrite;
	    sReadAccess : t_s_SPI_AccessControl;
	    pReadData : ^t_SPI_PDO_DataRead;
	    sReadStateAccess : t_s_SPI_AccessControl;
	    pReadStateData : ^USINT;
	    pSectorMemoryRead : ^USINT;
	    pSectorMemoryWrite : ^USINT;
	    pSectorMemoryVerify : ^USINT;
	    pUserWriteData : ^USINT;
	    udUserWriteLength : UDINT;
	    usUserSectorNumber : USINT;
	    usUserPageNumber : USINT;
	    usUserPageOffset : USINT;
	    eSPIWriteSSW : t_e_SPIWriteSSW;
	    eSPIWriteSSWError : t_e_SPIWriteSSW;
	    uiCurrentPage : UINT;
	    udCurrentSectorMemoryOffset : UDINT;
	    usRetryCnt : USINT;
	    bsFinished : BSINT
	    [
	      1 Finished,
	      2 Error,
	      3 Bit3,
	      4 Bit4,
	      5 Bit5,
	      6 Bit6,
	      7 Bit7,
	      8 Bit8,
	    ];
	    usCycleWaitCnt : USINT;
	    udTimeout : UDINT;
	    NotReadyCnt : DINT;
	    usFinishedWriteLog : USINT;
	    usWriteMode : usWriteModes;
	    udMemoryToAllocate : UDINT;
	    uiLastPage : UINT;
	    udUserDataOffset : DINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_SPI_List : STRUCT
	    BaseAdress : STRUCT
	      PageOffset : HSINT;  //! <Type Comment="= offset in page" Name="t_SPI_List.BaseAdress.PageOffset"/>
	      PageAdress0 : HSINT;  //! <Type Comment="= page number" Name="t_SPI_List.BaseAdress.PageAdress0"/>
	      PageAdress1 : HSINT;  //! <Type Comment="= sector number" Name="t_SPI_List.BaseAdress.PageAdress1"/>
	    END_STRUCT;
	    ID : HSINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_SPI_DataObjectList : STRUCT
	    Identifier : HDINT;
	    Checksum : DINT;
	    ListVersion : DINT;
	    ListLength : UDINT;
	    IdentificationList : t_SPI_List;
	    FlashConfiguration : t_SPI_List;
	    OptionalListelements : ARRAY [0..SDIAS_SPI_NUMBER_OF_OPTIONAL_ELEMENTS] OF t_SPI_List;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_SPI_DataObjectListHeader : STRUCT
	    Identifier : DINT;
	    CheckSum : DINT;
	    ListVersion : DINT;
	    ListLength : DINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_SPI_IdentificationList : STRUCT
	    CheckSum : DINT;
	    ListVersion : DINT;
	    ListLength : UDINT;
	    VendorID : UDINT;
	    DeviceID : DINT;
	    Reserved : DINT;
	    HardwareVersion : HDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_SPI_ListHeader : STRUCT
	    CheckSum : UDINT;
	    ListVersion : DINT;
	    ListLength : UDINT;
	  END_STRUCT;
#pragma pack(pop)
#pragma pack(push, 1)
	  t_SPIAccessData : STRUCT
	    SPIIDHeader : t_SPI_ListHeader;
	    udReadOffset : UDINT;
	    udActWROffset : UDINT;
	    udReadData : UDINT;
	  END_STRUCT;
#pragma pack(pop)
	END_TYPE
  //Servers:
	ClassState 	: SvrChCmd_t_e_VaranErrors;
	DeviceID 	: SvrCh_UDINT;
	FPGAVersion 	: SvrCh_HDINT;
	HwVersion 	: SvrCh_HDINT;
	SerialNo 	: SvrChCmd_UDINT;
	RetryCounter 	: SvrCh_UDINT;
	LEDControl 	: SvrCh_DINT;
	SDOState 	: SvrChCmd_iprStates;
  //Clients:
	SdiasIn 	: CltChCmd_DINT;
	Place 	: CltCh_UDINT;
	Required 	: CltCh_DINT;
	ToStdLib 	: CltChCmd__StdLib;
	SerialNoStr 	: CltChCmd_String;
  //Variables:
		eInitState 	: t_e_VaranErrors;			//! <Variable Comment="Actual State of Initialisation of Module." Name="eInitState"/>
		eLastErrorCommand 	: t_SDIAS_SDO_Cmd;			//! <Variable Comment="Variable stores in case of an Error the SDO-Command." Name="eLastErrorCommand"/>
		eLastErrorInfo 	: t_s_ErrorInfo;			//! <Variable Comment="Variable stores in case of an Error the Informations about the SDO." Name="eLastErrorInfo"/>
		pHardwareTree 	: ^LSL_HARDWARETREE;			//! <Variable Comment="Pointer to OS to access Hardwaretree-Functions." Name="pHardwareTree"/>
		pObjectPath 	: ^USINT;			//! <Variable Comment="Pointer to Objectpath of Module." Name="pObjectPath"/>
		udActiveSDOCnt 	: UDINT;			//! <Variable Comment="Number of active SDOs." Name="udActiveSDOCnt"/>
		udBusTime 	: UDINT;			//! <Variable Comment="Used Cycletime [ns]" Name="udBusTime"/>
		usInitCounter 	: USINT;			//! <Variable Comment="Variable indicated the actual Number of the Init-Run." Name="usInitCounter"/>
		DefaultAccesses 	: t_SDIAS_DefaultAccesses;
		udSDOTimeoutSPI 	: UDINT;
		eLastErrorInfoSPI 	: t_s_ErrorInfoSPI;
		eResponseStateSPI 	: t_ResponseInfo;
		eGetSPIDataSSW 	: t_e_Get_SPI_Data_SSW;
		eGetObjectListSSW 	: t_GetObjectList_SSW;
		eSPISSW 	: t_e_SPI_SSW;
		aSPIReceive : ARRAY [0..SDIAS_MAX_SDO_SPI_READ_LENGTH] OF CHAR;

		bSDOResponseHandle 	: DINT;
		sSPIData 	: t_SPIAccessData;
		bd_SingleLog 	: t_b_SingleLog;
		sSPIVars 	: t_s_SPIWriteVars;
		pSPIDataObjectList 	: ^t_SPI_DataObjectList;
		bIsConnectedViaVARAN 	: BOOL;
		sSPIAccess 	: t_s_SPIProtect;
  //Functions:
				//! <Function Comment="Constructor of Class." Name="SdiasBase"/>
	FUNCTION SdiasBase
		VAR_OUTPUT
			ret_code 	: ConfStates;
		END_VAR;
				//! <Function Comment="Init-Methode of Class.&#13;&#10;&#13;&#10;This Methode needs to get called. In case the Methode gets overwritten, a call needs to bet installed." Name="Init"/>
	FUNCTION VIRTUAL GLOBAL Init;
	
	FUNCTION AddDynRdAccess
		VAR_INPUT
			hOffset 	: HINT;
			uLength 	: UINT;
			bCtrlAcc0 	: BOOL := false;
			ppData 	: pVoid;
			ppStateByte 	: pVoid;
			ppControlByte 	: pVoid;
			pAccessHandle 	: ^UDINT;
			WaitForAccessHandle 	: UDINT;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION AddDynWrAccess
		VAR_INPUT
			hOffset 	: HINT;
			uLength 	: UINT;
			bCtrlAcc0 	: BOOL := false;
			ppData 	: pVoid;
			ppStateByte 	: pVoid;
			ppControlByte 	: pVoid;
			pAccessHandle 	: ^UDINT;
			WaitForAccessHandle 	: UDINT;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
				//! <Function Comment="Methode to add a Read-Access to Module." Name="AddRdAccess"/>
	FUNCTION AddRdAccess
		VAR_INPUT
			hOffset 	: HINT;			//! <Variable Comment="Address-Offset in Module." Name="AddRdAccess.hOffset"/>
			uLength 	: UINT;			//! <Variable Comment="Length of Data." Name="AddRdAccess.uLength"/>
			bCtrlAcc0 	: BOOL := false;			//! <Variable Comment="0 ... access to memory area&#13;&#10;1 ... access to ctrl area" Name="AddRdAccess.bCtrlAcc0"/>
			ppData 	: pVoid;			//! <Variable Comment="Pointer to the Memory for the Read-Data." Name="AddRdAccess.ppData"/>
			ppStateByte 	: pVoid;			//! <Variable Comment="Pointer to the StateWord of the Read-Data." Name="AddRdAccess.ppStateByte"/>
			ppControlByte 	: pVoid;			//! <Variable Comment="Pointer to the ControlWord of the Read-Data." Name="AddRdAccess.ppControlByte"/>
			pAccessHandle 	: ^UDINT := NIL;
			WaitForAccessHandle 	: UDINT := 0;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
				//! <Function Comment="Methode to add a Write-Access to Module." Name="AddWrAccess"/>
	FUNCTION AddWrAccess
		VAR_INPUT
			hOffset 	: HINT;			//! <Variable Comment="Address-Offset in Module." Name="AddWrAccess.hOffset"/>
			uLength 	: UINT;			//! <Variable Comment="Length of Data." Name="AddWrAccess.uLength"/>
			bCtrlAcc0 	: BOOL := false;			//! <Variable Comment="0 ... access to memory area&#13;&#10;1 ... access to ctrl area" Name="AddWrAccess.bCtrlAcc0"/>
			ppData 	: pVoid;			//! <Variable Comment="Pointer to the Memory of the Write-Data." Name="AddWrAccess.ppData"/>
			ppStateByte 	: pVoid;			//! <Variable Comment="Pointer to the StateWord of the Write-Data." Name="AddWrAccess.ppStateByte"/>
			ppControlByte 	: pVoid;			//! <Variable Comment="Pointer to the ControlWord of the Write-Data." Name="AddWrAccess.ppControlByte"/>
			pAccessHandle 	: ^UDINT := NIL;
			WaitForAccessHandle 	: UDINT := 0;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION ChangeDynAccess
		VAR_INPUT
			AccessHandle 	: UDINT;
			NewOffset 	: HDINT;
			NewLength 	: UDINT;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
				//! <Function Comment="Methode to execute a Read-SDO to Module." Name="StartReadSDO"/>
	FUNCTION StartReadSDO
		VAR_INPUT
			hOffset 	: HINT;			//! <Variable Comment="Address-Offset in Module." Name="StartReadSDO.hOffset"/>
			usLength 	: USINT;			//! <Variable Comment="Length of Data." Name="StartReadSDO.usLength"/>
			eCommand 	: t_SDIAS_SDO_Cmd;			//! <Variable Comment="Type of SDO-Command." Name="StartReadSDO.eCommand"/>
		END_VAR
		VAR_OUTPUT
			eRetcode 	: iprStates;			//! <Variable Comment="Indicates if the call of the Methode was successful." Name="StartReadSDO.eRetcode"/>
		END_VAR;
				//! <Function Comment="Methode to execute a Read-SPI-SDO to Module." Name="StartReadSDOSPI"/>
	FUNCTION StartReadSDOSPI
		VAR_INPUT
			PageAdress 	: HINT;
			usPageOffset 	: HSINT;
			usLength 	: USINT;
			eCommand 	: t_SDIAS_SDO_Cmd;
		END_VAR
		VAR_OUTPUT
			eRetcode 	: iprStates;
		END_VAR;
				//! <Function Comment="Methode to execute a Write-SDO to Module." Name="StartWriteSDO"/>
	FUNCTION StartWriteSDO
		VAR_INPUT
			hOffset 	: HINT;			//! <Variable Comment="Address-Offset in Module." Name="StartWriteSDO.hOffset"/>
			usLength 	: USINT;			//! <Variable Comment="Length of Data." Name="StartWriteSDO.usLength"/>
			pWriteBuffer 	: ^USINT;			//! <Variable Comment="Pointer of Data to Write." Name="StartWriteSDO.pWriteBuffer"/>
			eCommand 	: t_SDIAS_SDO_Cmd;			//! <Variable Comment="Type of SDO-Command." Name="StartWriteSDO.eCommand"/>
		END_VAR
		VAR_OUTPUT
			dRetcode 	: iprStates;			//! <Variable Comment="Indicates if the call of the Methode was successful." Name="StartWriteSDO.dRetcode"/>
		END_VAR;
				//! <Function Comment="Realtime Update Methode which runs in PreScan." Name="UpdateRt"/>
	FUNCTION VIRTUAL GLOBAL UpdateRt;
				//! <Function Comment="Realtime Update Methode which runs in PostScan." Name="UpdateRtPostScan"/>
	FUNCTION VIRTUAL GLOBAL UpdateRtPostScan;
	
	FUNCTION VIRTUAL GLOBAL UpdateRtPreOp
		VAR_INPUT
			callinfo 	: t_SDIAS_UpdateRTPreOp;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
				//! <Function Comment="Cyclic Update Methode which runs in PostScan." Name="UpdateCy"/>
	FUNCTION VIRTUAL GLOBAL UpdateCy;
	
	FUNCTION VIRTUAL GLOBAL AddAccesses
		VAR_OUTPUT
			eModuleInitState 	: iprStates;
		END_VAR;
				//! <Function Comment="Methode is called if this Module gets connected." Name="ConnectEvent"/>
	FUNCTION VIRTUAL GLOBAL ConnectEvent;
				//! <Function Comment="Methode is called if this Module gets disconnected." Name="DisconnectEvent"/>
	FUNCTION VIRTUAL GLOBAL DisconnectEvent;
				//! <Function Comment="Methode should be used to run a Statemachine to Initialize the Module." Name="InitModule"/>
	FUNCTION VIRTUAL GLOBAL InitModule
		VAR_OUTPUT
			eModuleInitState 	: iprStates;			//! <Variable Comment="Return BUSY until the Initialisation is finished." Name="InitModule.eModuleInitState"/>
		END_VAR;
				//! <Function Comment="This Methode should be used, to check the DeviceID of the Module." Name="CheckDeviceID"/>
	FUNCTION VIRTUAL GLOBAL CheckDeviceID
		VAR_INPUT
			udID2Check 	: UDINT;			//! <Variable Comment="Device ID of connected Module." Name="CheckDeviceID.udID2Check"/>
		END_VAR
		VAR_OUTPUT
			bIsOK 	: BOOL;			//! <Variable Comment="FALSE = Wrong DeviceID for Module&#13;&#10;TRUE  = DeviceID of Module is correct" Name="CheckDeviceID.bIsOK"/>
		END_VAR;
				//! <Function Comment="Methode to execute a Read-DA to Module." Name="ReadDirect"/>
	FUNCTION ReadDirect
		VAR_INPUT
			hOffset 	: HINT;			//! <Variable Comment="Address-Offset in Module." Name="ReadDirect.hOffset"/>
			usLength 	: USINT;			//! <Variable Comment="Length of Data." Name="ReadDirect.usLength"/>
			pData 	: pVoid;			//! <Variable Comment="Pointer to Memory for the Read-Data." Name="ReadDirect.pData"/>
		END_VAR;
				//! <Function Comment="Methode to execute a Write-DA to Module." Name="WriteDirect"/>
	FUNCTION WriteDirect
		VAR_INPUT
			hOffset 	: HINT;			//! <Variable Comment="Address-Offset in Module." Name="WriteDirect.hOffset"/>
			usLength 	: USINT;			//! <Variable Comment="Length of Data." Name="WriteDirect.usLength"/>
			pData 	: pVoid;			//! <Variable Comment="Pointer of Data to Write." Name="WriteDirect.pData"/>
		END_VAR;
				//! <Function Comment="Methode to receive the Response in case of SDO-Communication." Name="ReceiveSDOResponse"/>
	FUNCTION VIRTUAL ReceiveSDOResponse
		VAR_INPUT
			eCommand 	: t_SDIAS_SDO_Cmd;			//! <Variable Comment="Type of SDO-Command" Name="ReceiveSDOResponse.eCommand"/>
			hOffset 	: HINT;			//! <Variable Comment="Address-Offset in Module." Name="ReceiveSDOResponse.hOffset"/>
			usLength 	: USINT;			//! <Variable Comment="Length of Data." Name="ReceiveSDOResponse.usLength"/>
			eResult 	: iprStates;			//! <Variable Comment="State of SDO-Communication." Name="ReceiveSDOResponse.eResult"/>
			pResponseBuffer 	: ^USINT;			//! <Variable Comment="Pointer of ResponseBuffer, where the SDO-Data is located." Name="ReceiveSDOResponse.pResponseBuffer"/>
			ResponseLength 	: USINT;			//! <Variable Comment="Size of the ResponseBuffer." Name="ReceiveSDOResponse.ResponseLength"/>
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;			//! <Variable Comment="Indicates if the Methode was successful." Name="ReceiveSDOResponse.ret_code"/>
		END_VAR;
				//! <Function Comment="Methode can be used for a CRC16 Calculation." Name="CheckSum_16"/>
	FUNCTION GLOBAL CheckSum_16
		VAR_INPUT
			pData 	: ^UINT;			//! <Variable Comment="Pointer to Data for the Calculation." Name="CheckSum_16.pData"/>
			uiDataLength 	: UINT;			//! <Variable Comment="Length of the Data for the Calculation." Name="CheckSum_16.uiDataLength"/>
			uiCRCStart 	: UINT;			//! <Variable Comment="Start-Polynom for the Calculation." Name="CheckSum_16.uiCRCStart"/>
		END_VAR
		VAR_OUTPUT
			uiCRCCalculation 	: UINT;			//! <Variable Comment="Result of the Calculation." Name="CheckSum_16.uiCRCCalculation"/>
		END_VAR;
				//! <Function Comment="Methode to get this-Pointer of Object in HardwareTree." Name="GetObjectThisp"/>
	FUNCTION VIRTUAL GetObjectThisp
		VAR_INPUT
			pObjectPath 	: ^USINT;			//! <Variable Comment="Objectpath of the Module." Name="GetObjectThisp.pObjectPath"/>
		END_VAR
		VAR_OUTPUT
			pThis 	: UDINT;			//! <Variable Comment="This-Pointer of Object." Name="GetObjectThisp.pThis"/>
		END_VAR;
				//! <Function Comment="Methode to set this-Pointer of Object in HardwareTree." Name="SetObjectThisp"/>
	FUNCTION VIRTUAL SetObjectThisp
		VAR_INPUT
			pObjectPath 	: ^USINT;			//! <Variable Comment="Objectpath of the Module." Name="SetObjectThisp.pObjectPath"/>
			pThis 	: UDINT;			//! <Variable Comment="This-Pointer of Object." Name="SetObjectThisp.pThis"/>
		END_VAR
		VAR_OUTPUT
			dRetcode 	: DINT;			//! <Variable Comment="Indicates if the call of the Methode was successful." Name="SetObjectThisp.dRetcode"/>
		END_VAR;
				//! <Function Comment="Call this Methode to get the Pointer to the Objectpath of this Module." Name="GetPointer2ObjectPath"/>
	FUNCTION VIRTUAL GLOBAL GetPointer2ObjectPath
		VAR_INPUT
			pThisPointerOverride 	: pVirtualBase := NIL;
		END_VAR
		VAR_OUTPUT
			p_us_ObjectPath 	: ^USINT;			//! <Variable Comment="Pointer to Objectpath of the Module." Name="GetPointer2ObjectPath.p_us_ObjectPath"/>
		END_VAR;
	
	FUNCTION VIRTUAL GetTaskCfg
		VAR_OUTPUT
			TaskCfg 	: BDINT;			//! <Variable Comment="Select which tasks will be available for this module:&#13;&#10;Bit1..Realtime PreScan&#13;&#10;Bit2..Realtime PostScan&#13;&#10;Bit3..Cyclic&#13;&#10;&#13;&#10;e.g. if only realtime postscan is needed:&#13;&#10;2#010" Name="GetTaskCfg.TaskCfg"/>
		END_VAR;
	
	FUNCTION StartSDOIntelligent
		VAR_INPUT
			pSDOBuffer 	: ^USINT;
			usLength 	: USINT;
			eCommand 	: t_SDIAS_SDO_Cmd;
		END_VAR
		VAR_OUTPUT
			eRetcode 	: iprStates;
		END_VAR;
	
	FUNCTION ReadSPIData
		VAR_INPUT
			h_PageAdress 	: HINT;
			hs_PageOffset 	: HSINT;
			us_Length 	: USINT;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
				//! <Function Comment="Writes the SPI DataObjectList to the private Var a_DataObjectList." Name="GetDataObjectList"/>
	FUNCTION GetDataObjectList
		VAR_INPUT
			pDataObjectList 	: ^t_SPI_DataObjectList := NIL;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION GetSPIDataFromID
		VAR_INPUT
			usListID 	: USINT;
			pData 	: ^CHAR;
			RequDataLength 	: UDINT;
			pIDHeader 	: ^t_SPI_ListHeader;
			pReadLen 	: ^UDINT := NIL;
			udReadOffset 	: UDINT := 16#00;
			bIDFound 	: ^BOOL := nil;
			pDataObjectList 	: ^t_SPI_DataObjectList := NIL;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION GetBaseAdressFromID
		VAR_INPUT
			usListID 	: DINT;
			pListElement 	: ^t_SPI_List;
			pDataObjectList 	: ^t_SPI_DataObjectList := NIL;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION LockSPIAccess
		VAR_INPUT
			udAccessID 	: UDINT;
		END_VAR
		VAR_OUTPUT
			dRetCode 	: iprStates;
		END_VAR;
	
	FUNCTION UnlockSPIAccess
		VAR_INPUT
			udAccessID 	: UDINT;
		END_VAR
		VAR_OUTPUT
			dRetCode 	: iprStates;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL GetDeviceID
		VAR_OUTPUT
			output 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL GetFWVersion
		VAR_INPUT
			pData 	: ^CHAR;
			Length 	: UDINT;
		END_VAR;
				//! <Function Comment="Methode can be used for calculation the spi checksum.&#13;&#10;The checksum is a simple adding of each byte.&#13;&#10;Checksum Calculation:&#13;&#10;Data: 16#12345678&#13;&#10;Checksum: 16#12 + 16#34 + 16#56 + 16#78 = 16#114" Name="CheckSum_SPI"/>
	FUNCTION GLOBAL CheckSum_SPI
		VAR_INPUT
			pData 	: ^USINT;			//! <Variable Comment="Pointer to Data for the Calculation." Name="CheckSum_SPI.pData"/>
			uiDataLength 	: UINT;			//! <Variable Comment="Length of the Data for the Calculation." Name="CheckSum_SPI.uiDataLength"/>
			udCRCStart 	: UDINT := 16#0000;			//! <Variable Comment="Start-Polynom for the Calculation." Name="CheckSum_SPI.udCRCStart"/>
		END_VAR
		VAR_OUTPUT
			udCheckSum 	: UDINT;			//! <Variable Comment="calculated checksum from pData" Name="CheckSum_SPI.udCheckSum"/>
		END_VAR;
				//! <Function Comment="Returns the hardware time stamp.&#13;&#10;This time stamp corresponds to the Rt start point" Name="GetHWTimeStamp"/>
	FUNCTION VIRTUAL GLOBAL GetHWTimeStamp
		VAR_OUTPUT
			HWTimeStamp 	: UINT;			//! <Variable Comment="Hardware Timestamp:&#13;&#10;0 - 65535 [µs]&#13;&#10;Constantly 0 Error, Timestamp could not be determined." Name="GetHWTimeStamp.HWTimeStamp"/>
		END_VAR;
				//! <Function Comment="Methode to set a Text with Parameters as Log-Message." Name="LogValue"/>
	FUNCTION VIRTUAL LogValue
		VAR_INPUT
			pString 	: ^CHAR;			//! <Variable Comment="Pointer to Log-Message." Name="LogValue.pString"/>
			udValue 	: UDINT;			//! <Variable Comment="ParameterValue 1 which is added to Log-Message.&#13;&#10;&#13;&#10;16#FFFFFFFF =&gt; Value is not used." Name="LogValue.udValue"/>
			udValue2 	: UDINT := 16#FFFFFFFF;			//! <Variable Comment="ParameterValue 2 which is added to Log-Message.&#13;&#10;&#13;&#10;16#FFFFFFFF =&gt; Value is not used." Name="LogValue.udValue2"/>
			udValue3 	: UDINT := 16#FFFFFFFF;			//! <Variable Comment="ParameterValue 3 which is added to Log-Message.&#13;&#10;&#13;&#10;16#FFFFFFFF =&gt; Value is not used." Name="LogValue.udValue3"/>
		END_VAR;
				//! <Function Comment="Methode to set directly a Text as Log-Message." Name="LogError"/>
	FUNCTION VIRTUAL LogError
		VAR_INPUT
			pMsg 	: ^CHAR;			//! <Variable Comment="Pointer to Log-Message." Name="LogError.pMsg"/>
		END_VAR;
	
	FUNCTION VIRTUAL MyTrace
		VAR_INPUT
			pFormat 	: ^CHAR;
			pTxt0 	: ^CHAR;
			pTxt1 	: ^CHAR;
			pTxt2 	: ^CHAR;
		END_VAR;
				//! <Function Comment="Methode to add dataobjects for spi write access. Is only needed for function WriteSPIData()." Name="SPIWriteAddAccesses"/>
	FUNCTION VIRTUAL SPIWriteAddAccesses
		VAR_OUTPUT
			eModuleInitState 	: iprStates;
		END_VAR;
				//! <Function Comment="Methode to get the state of spi write." Name="SPIWriteGetState"/>
	FUNCTION VIRTUAL SPIWriteGetState
		VAR_OUTPUT
			retcode 	: iprStates;
		END_VAR;
				//! <Function Comment="Methode to write data into spi flash of modul." Name="WriteSPIData"/>
	FUNCTION VIRTUAL WriteSPIData
		VAR_INPUT
			usSectorNumber 	: USINT;			//! <Variable Comment="Sector number in which the data should be written. One sector has a size of 64 kByte." Name="WriteSPIData.usSectorNumber"/>
			usPageNumber 	: USINT;			//! <Variable Comment="Pagenumber in which the data should be written. One sector has 256 pages." Name="WriteSPIData.usPageNumber"/>
			usPageOffset 	: USINT;			//! <Variable Comment="Page offset in which the data should be written. One page has a size of 256 Byte." Name="WriteSPIData.usPageOffset"/>
			udLength 	: UDINT;			//! <Variable Comment="Gives the length of write data." Name="WriteSPIData.udLength"/>
			pData 	: ^USINT;			//! <Variable Comment="Pointer to the write data." Name="WriteSPIData.pData"/>
			usWriteMode 	: usWriteModes := 0x00;			//! <Variable Comment="defines the write mode for spi data&#13;&#10; 0 .. read sector - erase sector - write sector&#13;&#10; 1 .. read page - erase sector - write page&#13;&#10; 2 .. erase sector - write sector&#13;&#10; 3 .. erase sector - write page" Name="WriteSPIData.usWriteMode"/>
		END_VAR
		VAR_OUTPUT
			retcode 	: DINT;			//! <Variable Comment="The return value of the function.&#13;&#10; 0 .. ok&#13;&#10;-1 ..  pointer to write data is not valid&#13;&#10;-2 .. the last write access is not finished&#13;&#10;-3 .. length of write data is zero&#13;&#10;-4 .. length of write data is to long&#13;&#10;-5 .. no grant for spi access&#13;&#10;-6 .. the given write mode is not supported&#13;&#10;" Name="WriteSPIData.retcode"/>
		END_VAR;
	
	FUNCTION VIRTUAL SPIWriteUpdateRt;
	
	FUNCTION VIRTUAL SPIWriteUpdateCy;
	
	FUNCTION VIRTUAL GLOBAL ClassState::NewInst
		VAR_INPUT
			pPara 	: ^CmdStruct;
			pResult 	: ^results;
		END_VAR
		VAR_OUTPUT
			ret_code 	: iprStates;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL SerialNo::Read
		VAR_OUTPUT
			output (EAX) 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL SerialNo::Write
		VAR_INPUT
			input (EAX) 	: UDINT;
		END_VAR
		VAR_OUTPUT
			result (EAX) 	: UDINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL LEDControl::Write
		VAR_INPUT
			input (EAX) 	: DINT;
		END_VAR
		VAR_OUTPUT
			result (EAX) 	: DINT;
		END_VAR;
	
	FUNCTION VIRTUAL GLOBAL SDOState::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 using String
#pragma usingLtd _StdLib


//}}LSL_DECLARATION


FUNCTION GLOBAL TAB SdiasBase::@CT_
0$UINT,
2#0100000000000000$UINT, //TY_SDIASBASE
1$UINT, 60$UINT, (SIZEOF(::SdiasBase))$UINT, 
8$UINT, 5$UINT, 0$UINT, 
TO_UDINT(3175101883), "SdiasBase", //Class
TO_UDINT(0), 0, 0$UINT, 0$UINT, //Baseclass
//Servers:
(::SdiasBase.ClassState.pMeth)$UINT, _CH_CMD$UINT, 2#0000000000000000$UINT, TO_UDINT(1096059024), "ClassState", 
(::SdiasBase.DeviceID.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(4261957967), "DeviceID", 
(::SdiasBase.FPGAVersion.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(2929703442), "FPGAVersion", 
(::SdiasBase.HwVersion.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(3247816137), "HwVersion", 
(::SdiasBase.SerialNo.pMeth)$UINT, _CH_SVR_OBJ$UINT, 2#0000000001000000$UINT, TO_UDINT(1856804640), "SerialNo", 
(::SdiasBase.RetryCounter.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(3383993698), "RetryCounter", 
(::SdiasBase.LEDControl.pMeth)$UINT, _CH_SVR$UINT, 2#0000000000000000$UINT, TO_UDINT(288098344), "LEDControl", 
(::SdiasBase.SDOState.pMeth)$UINT, _CH_CMD$UINT, 2#0000000000000000$UINT, TO_UDINT(1646536137), "SDOState", 
//Clients:
(::SdiasBase.SdiasIn.pCh)$UINT, _CH_CLT_CMD$UINT, 2#0000000000000010$UINT, TO_UDINT(3019270524), "SdiasIn", 
(::SdiasBase.Place.pCh)$UINT, _CH_CLT_DATA$UINT, 2#0000000000000000$UINT, TO_UDINT(2202636669), "Place", 
(::SdiasBase.Required.pCh)$UINT, _CH_CLT_DATA$UINT, 2#0000000000000010$UINT, TO_UDINT(648558917), "Required", 
(::SdiasBase.ToStdLib.pCh)$UINT, _CH_CLT_OBJ$UINT, 2#0000000000000000$UINT, TO_UDINT(2501598121), "ToStdLib", TO_UDINT(3689364044), "_StdLib", 2$UINT, 0$UINT, 
(::SdiasBase.SerialNoStr.pCh)$UINT, _CH_CLT_OBJ$UINT, 2#0000000000000010$UINT, TO_UDINT(321148131), "SerialNoStr", TO_UDINT(1850111279), "String", 1$UINT, 11$UINT, 
END_FUNCTION


#define USER_CNT_SdiasBase 25

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	IF ClassState.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;
	_memcpy((#vmt.CmdTable)$^USINT, SerialNo.pMeth, SIZEOF(CMDMETH));
	vmt.CmdTable.nCmds		:= nSTDCMD;
	vmt.CmdTable.Read		:= #SerialNo::Read();
	vmt.CmdTable.Write		:= #SerialNo::Write();
	SerialNo.pMeth		:= StoreCmd (pCmd := #vmt.CmdTable, SHARED);

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

	IF SDOState.pMeth THEN
		ret_code	:= C_OK;
	ELSE
		ret_code	:= C_OUTOF_NEAR;
		RETURN;
	END_IF;
	ret_code	:= SdiasBase();

END_FUNCTION

#pragma usingLtd HwcLogging
#pragma usingLtd SigCLib

//{{LSL_IMPLEMENTATION
VAR_PRIVATE
  p_this                : ^void;
  a_SerialNo			      : ARRAY[0..43] OF CHAR;
  a_VendorName          : ARRAY[0..99] OF CHAR;
  a_IdentificationList  : ARRAY [0..99] OF CHAR;
  a_DeviceName          : ARRAY[0..99] OF CHAR;
  a_OrderingNo          : ARRAY[0..99] OF CHAR;
  a_FWVersion           : ARRAY[0..43] OF CHAR;
  a_DataObjectList      : SdiasBase::t_SPI_DataObjectList;      //DataObjectList
  a_HWTEntryStr         : ARRAY[0.. VARAN_MAX_DEPTH+1] OF CHAR; //+1 because lenght is 2 byte
  
  bDiagnosisDataAvailableFlags : BSINT
  [
    1 HWVersion,
    2 VendorName,
    3 DeviceName,
    4 OrderingNo,    
  ];
  
  mt_api : ^LSL_MT_TYPE;
  pSdoCreationMutex : pVoid;
  
END_VAR

FUNCTION VIRTUAL GLOBAL SdiasBase::Init
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
    TempStr  : ARRAY [0..31] OF CHAR;
    dRetCode : iprStates;
  END_VAR

#ifdef DEACTIVATED_LSL
  IF Place.Read() = DEACTIVATED_LSL THEN
    Place := DEACTIVATED_LSL;
    ClassState := _NoHardware;
    return;
  END_IF;
#endif

  usInitCounter += 1;  

  if usInitCounter = 1 then

    if mt_api = NIL then
      if (OS_CILGet("LSL_MULTITASK", #mt_api$void)) THEN
        LogError("@02C7 (SdiasBase::Init) Failed to get MULTITASK OS-Interface for Mutex usage");   
      end_if;
    end_if;
  
    // if we don't have a mutex yet => create it
    if mt_api & (pSdoCreationMutex = NIL) then

      // install mutex for accessing the SDOs (receiving is handled in cyclic and can therefore interrupt adding SDOs in background)
      _strcpy(dest:=#TempStr[0], src:="SdiasSDOs");
      MyPara.uiCmd := SDIAS_CMD_CREATE_MUTEX;
      MyPara.aPara[0] := (#TempStr)$DINT;
      MyPara.aPara[1] := this$DINT;
      SdiasIn.NewInst(pPara:=#MyPara, pResult:=#MyResult);
      
      // check retcode
      if MyResult.aData[0] = 0 then
        pSdoCreationMutex := MyResult.aData[4]$^void;
      end_if;

    end_if;
  
    Place         := Place.Read();
    Required      := required.Read();

    MyPara.uiCmd := SDIAS_CMD_INIT_STATE_LOGIN;
    MyPara.aPara[0] := (#eInitState)$DINT;
    MyPara.aPara[1] := Place$DINT;
    MyPara.aPara[2] := this$DINT;
    MyPara.aPara[3] := (GetTaskCfg())$DINT;
    MyPara.aPara[4] := (#DefaultAccesses)$DINT;
    MyPara.aPara[5] := Required;
    if SdiasIn.NewInst(#MyPara, #MyResult) <> READY then
      LogValue("@02C8 (SdiasBase::Init) Failed to login pointer to init state for SDIAS module at Place 0x{0}", Place);
    end_if;
    
    MyPara.uiCmd     := SDIAS_CMD_IS_VARAN_AVAILABLE;      
    dRetCode := SdiasIn.NewInst(pPara:=#MyPara, pResult:=#MyResult);
    
    if (dRetCode = READY) & (MyResult.aData[0]$UDINT = 0) then
      bIsConnectedViaVARAN := MyResult.aData[4]$BOOL;
    else  
      LogError("@03FA (SdiasBase::Init) Failed to get connection type of sdias bus (local or varan).");
    end_if;        
  
  end_if;
  if _FirstScan = TRUE then
    // Called in last init so that not default calls will not be overwritten by this call.
    GetPointer2ObjectPath(pThisPointerOverride:=nil);    
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::UpdateRt

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::UpdateRtPostScan

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::UpdateCy

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::ClassState::NewInst
	VAR_INPUT
		pPara 	: ^CmdStruct;
		pResult 	: ^results;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
	  ud_StrSize : UDINT;
    ud_MaxStrSize : UDINT;
    sSPIIDHeader  : t_SPI_ListHeader;
  END_VAR
  
  ud_StrSize    := 0;
  ud_MaxStrSize := 0;
  sSPIIDHeader.ListLength  := 0;

	ret_code := READY;
  
	CASE pPara^.uiCmd OF
//**********************************************************************************************************************************************************
		SDIAS_CMD_DEVICE_INFO:
      // save device ID and check it
      DeviceID := pPara^.aPara[0]$UINT;
      if CheckDeviceID(udID2Check:=DeviceID) = FALSE then
        ret_code := ERROR;
      end_if;
      
      // save serial number
      SerialNoStr.WriteDataOff(udLen:=_StrLen(src:=pPara^.aPara[1]$^CHAR), udOff:=0, pData:=pPara^.aPara[1]$^CHAR);
      
      // save the Bus Time
      udBusTime := pPara^.aPara[2]$UDINT;
      
      // show the FPGA Version
      FPGAVersion := pPara^.aPara[3]$HSINT;

//**********************************************************************************************************************************************************
    SDIAS_CMD_DEVICE_INFO2:
      // check the size of the parameters
      if pPara^.aPara[0] >= sizeof(HDINT) then
        HwVersion := pPara^.aPara[1]$HDINT;
      end_if;

//**********************************************************************************************************************************************************
		SDIAS_CMD_INIT_ACCESSES:
      // if a module needs accesses beyond the ones in the info block, they should be defined in the derivation of the AddAccesses()-Method
      ret_code := AddAccesses();

//*********************************************************************************************************************************************************
    SDIAS_CMD_DATA_POINTER_AVAILABLE: // the pointer to the data are available now => use in derivation if needed

//**********************************************************************************************************************************************************
    SDIAS_CLT_CMD_LOG_MESSAGE:
      // check for valid pointer to message
      if pPara^.aPara[0] then
        LogError(pMsg:=pPara^.aPara[0]$^CHAR);
      end_if;

//**********************************************************************************************************************************************************
    SDIAS_CLT_CMD_LOG_VALUE:
      // check for valid pointer to message
      if pPara^.aPara[0] then
        LogValue(pPara^.aPara[0]$^CHAR, udValue:=pPara^.aPara[1]$UDINT, udValue2:=pPara^.aPara[2]$UDINT, udValue3:=pPara^.aPara[3]$UDINT);
      end_if;

//**********************************************************************************************************************************************************
    SDIAS_CLT_CMD_CREATE_MUTEX:
      pPara^.uiCmd := SDIAS_CMD_CREATE_MUTEX;
      ret_code := SdiasIn.NewInst(pPara, pResult);

//**********************************************************************************************************************************************************
    SDIAS_CLT_CMD_IS_VARAN_AVAILABLE:
      pPara^.uiCmd := SDIAS_CMD_IS_VARAN_AVAILABLE;
      ret_code := SdiasIn.NewInst(pPara, pResult);
      
//**********************************************************************************************************************************************************
    CMD_GET_HARDWARE_DIAGNOSIS:
    
      pResult^.aData$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR;
      ret_code := ERROR;
      
      //Return State and Device ID and set retcode ----------------------------------------------------------
      pResult^.aData$t_HWCGetHardwareDiagnosisOut.ClassState := ClassState;
      pResult^.aData$t_HWCGetHardwareDiagnosisOut.ud_DeviceID := GetDeviceID();
      
      GetFWVersion(pData:=#a_FWVersion[0],sizeof(a_FWVersion));
      pResult^.aData$t_HWCGetHardwareDiagnosisOut.pStr_FWVersion := #a_FWVersion[0];
            
      if ClassState = _NoHardware then
        pResult^.aData$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_NO_HARDWARE;
        return;
      elsif ClassState = _WrongHardware then
        pResult^.aData$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_WRONG_HARDWARE;
        return;
      elsif ClassState = _NoCalibDataFound then
        pResult^.aData$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_NO_CALIB_DATA_FOUND;
        return;
      elsif ClassState <> _ClassOk then
        pResult^.aData$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_NO_HARDWARE;
        return;
      end_if;
      //------------------------------------------------------------------------------------------------------
          
      if this <> p_this then
        //Reset statemachine
        eSPISSW := _GetVendorName;
        //Save This pointer
        p_this := this;
        
        // Reset Flags
        bDiagnosisDataAvailableFlags.HWVersion   := FALSE;
        bDiagnosisDataAvailableFlags.VendorName  := FALSE;
        bDiagnosisDataAvailableFlags.DeviceName  := FALSE;
        bDiagnosisDataAvailableFlags.OrderingNo  := FALSE;
      end_if;
      
      case eSPISSW of
      
//----------------------------------------------------------------------------------------------------------------------------------------------
        _GetVendorName:        
        
            //protect spi access to calls from other instances
            ret_code := LockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE);
            
            if ret_code = READY then
              ret_code := GetSPIDataFromID(usListID:=SDIAS_SPI_VENDOR_NAME_ID, pData:=#a_VendorName[0], RequDataLength:=sizeof(a_VendorName), pIDHeader:= #sSPIIDHeader);
            end_if;
            
            if ret_code = READY then
              //release lock of spi access
              UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
                            
              // Null Termination ----------------------------------------
              ud_StrSize    := sSPIIDHeader.ListLength;
              ud_MaxStrSize := sizeof(a_VendorName);  
              
              pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_VendorName  := (#a_VendorName)$^CHAR; // Will be overwritten if this pointer is not equal
              
              (pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_VendorName + ud_MaxStrSize)^ := 0;
              if ud_StrSize < ud_MaxStrSize then
                (pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_VendorName + ud_StrSize)^ := 0;  
              end_if;
              
              bDiagnosisDataAvailableFlags.HWVersion := TRUE;
                            
              eSPISSW := _GetDeviceID_HWVersion;
              ret_code := BUSY;
            elsif ret_code = ERROR then
              //release lock of spi access
              UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
              
              pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_SDIAS_SPI_ACCESS_FAILED;
              //set SSW to SPI Data Ready, because we return the values which are already in the HWK
              eSPISSW := _SPIDataReady;
              ret_code := BUSY;
            end_if;
//----------------------------------------------------------------------------------------------------------------------------------------------
        _GetDeviceID_HWVersion:
        
            //protect spi access to calls from other instances
            ret_code := LockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE);
            
            if ret_code = READY then
              ret_code := GetSPIDataFromID(usListID:=SDIAS_SPI_IDENTIFICATION_LIST_ID, pData:=(#a_IdentificationList)$^CHAR, RequDataLength:=sizeof(a_IdentificationList), pIDHeader:= #sSPIIDHeader);
            end_if;
            
            if ret_code = READY then
              //release lock of spi access
              UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
              
              if a_IdentificationList[0]$^void then
                
                pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.hd_HWVersion :=  (#a_IdentificationList + SDIAS_SPI_HARDWARE_VERSION_OFFSET)^$HDINT; 
                pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.ud_VendorID :=  (#a_IdentificationList + SDIAS_SPI_VENDOR_ID_OFFSET)^$HDINT; 
                
                bDiagnosisDataAvailableFlags.VendorName := TRUE;
                
              else
                pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.hd_HWVersion := 16#FFFFFFFF;
                pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.ud_VendorID  := 16#FFFFFFFF;
                
              end_if;
              
              eSPISSW := _GetDeviceName;
              ret_code := BUSY;
            elsif ret_code = ERROR then
              //release lock of spi access
              UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
              pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_SDIAS_SPI_ACCESS_FAILED;
            end_if;
//----------------------------------------------------------------------------------------------------------------------------------------------
        _GetDeviceName:
        
          pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_DeviceName := (#a_DeviceName+ sizeof(t_SPI_ListHeader))$^CHAR; // Will be overwritten if this pointer is not equal 
          
          //protect spi access to calls from other instances
          ret_code := LockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE);
          
          if ret_code = READY then
            ret_code := GetSPIDataFromID(usListID:=SDIAS_SPI_DEVICE_NAME_ID, pData:=(#a_DeviceName)$^CHAR, RequDataLength:=sizeof(a_DeviceName), pIDHeader:= #sSPIIDHeader);
          end_if;
          
          if ret_code = READY then
            //release lock of spi access
            UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
            
            // Null Termination ----------------------------------------
            ud_StrSize    := sSPIIDHeader.ListLength;
            ud_MaxStrSize := sizeof(a_DeviceName)(* - sizeof(t_SPI_ListHeader)*);  
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_DeviceName  := (#a_DeviceName)$^CHAR; // Will be overwritten if this pointer is not equal
            (pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_DeviceName + ud_MaxStrSize)^ := 0;
            if ud_StrSize < ud_MaxStrSize then
              (pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_DeviceName + ud_StrSize)^ := 0;  
            end_if;
            
            bDiagnosisDataAvailableFlags.DeviceName := TRUE;
            
            eSPISSW := _GetOrderingNo;
            ret_code := BUSY;
          elsif ret_code = ERROR then
            //release lock of spi access
            UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_SDIAS_SPI_ACCESS_FAILED;
          end_if;
//----------------------------------------------------------------------------------------------------------------------------------------------
        _GetOrderingNo:
        
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_OrderingNo := (#a_OrderingNo)$^CHAR; // Will be overwritten if this pointer is not equal    

            //protect spi access to calls from other instances
            ret_code := LockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE);
            
            if ret_code = READY then
              ret_code := GetSPIDataFromID(usListID:=SDIAS_SPI_ORDERING_NUMBER_ID, pData:=(#a_OrderingNo)$^CHAR, RequDataLength:=sizeof(a_OrderingNo), pIDHeader := #sSPIIDHeader);
            end_if;
            
            if ret_code = READY then
              //release lock of spi access
              UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
                            
              // Null Termination ----------------------------------------
              ud_StrSize    := sSPIIDHeader.ListLength;
              ud_MaxStrSize := sizeof(a_OrderingNo)(* - sizeof(t_SPI_ListHeader)*);  
              pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_OrderingNo  := (#a_OrderingNo)$^CHAR; // Will be overwritten if this pointer is not equal         
              (pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_OrderingNo + ud_MaxStrSize)^ := 0;
              if ud_StrSize < ud_MaxStrSize then
                (pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_OrderingNo + ud_StrSize)^ := 0;  
              end_if;
              
              bDiagnosisDataAvailableFlags.OrderingNo := TRUE;

              eSPISSW := _SPIDataReady;
              ret_code := BUSY;
            elsif ret_code = ERROR then
              //release lock of spi access
              UnlockSPIAccess(udAccessID:=SDIAS_SPI_ACCESS_ID_HW_TREE); 
              pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_ERROR_SDIAS_SPI_ACCESS_FAILED;
            end_if;        
//----------------------------------------------------------------------------------------------------------------------------------------------
        _SPIDataReady:          
          
          //Don't reset statemachine because if we have the same this pointer we don't have to make spi Accesses again
          //eSPISSW := _GetVendorName;
          
          //Data which is already in HWK available
          pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.ud_DeviceID := DeviceID;
          pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.ud_Place := Place;
          
          // Get Serialno ---------------------------------------------------------------------------------------------
          ud_StrSize := SerialNoStr.GetLength() ;              
          pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_SerialNo     := NIL;
          
          if ud_StrSize then

            if (ud_StrSize < sizeof(a_SerialNo)) then // < to account for 0 termination
              // Copy complete string
              SerialNoStr.GetDataAt(pData:=#a_SerialNo[0], udSize:=ud_StrSize, udAt:=0);
              a_SerialNo[ud_StrSize] := 0; // 0 termination
            else
              // Copy part string
              SerialNoStr.GetDataAt(pData:=#a_SerialNo[0], udSize:=sizeof(a_SerialNo)-1, udAt:=0);
              a_SerialNo[sizeof(a_SerialNo)-1] := 0; // 0 termination                
            end_if; 
            
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_SerialNo     := #a_SerialNo[0];
          end_if;   
          
          // Get FPGA Version -----------------------------------------------------------------------------------------
          pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.hd_FPGAVersion   := FPGAVersion;
          
          if bDiagnosisDataAvailableFlags.VendorName then
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_VendorName  := (#a_VendorName)$^CHAR;            
          end_if;
          
          if bDiagnosisDataAvailableFlags.HWVersion then
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.hd_HWVersion     := (#a_IdentificationList + SDIAS_SPI_HARDWARE_VERSION_OFFSET)^$HDINT; 
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.ud_VendorID      := (#a_IdentificationList + SDIAS_SPI_VENDOR_ID_OFFSET)^$HDINT; 
          end_if;
          
          if bDiagnosisDataAvailableFlags.DeviceName then
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_DeviceName  := (#a_DeviceName)$^CHAR;
          end_if;          
          
          if bDiagnosisDataAvailableFlags.OrderingNo then
            pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.pStr_OrderingNo  := (#a_OrderingNo)$^CHAR;
          end_if;
          
          ret_code := READY;
          pResult^.aData[0]$t_HWCGetHardwareDiagnosisOut.retcode := _HWC_RETVAL_OK;
          
      end_case;
            
//**********************************************************************************************************************************************************
    CMD_GET_HARDWARE_TREE_ENTRY :
      
      //Set Errors
      ret_code := ERROR;
      pResult^.aData$t_HWCGetHardwareTreeEntryOut.retcode := _HWC_RETVAL_ERROR;
      pResult^.aData[0]$t_HWCGetHardwareTreeEntryOut.pStr_NextHardewareTreeEntry := NIL;

      if pObjectPath then
        //Copy Length of p_objectpath (is on 1st byte) on first byte of our array, length is 2 byte so set the 2nd byte manual
        a_HWTEntryStr[0] := pObjectPath^$USINT;
        a_HWTEntryStr[1] := 0;
        
        //a_HWTEntryStr + 2 because first 2 bytes are lenght, ObjectPath 1st byte is length, copy as long as the object path is
        _memcpy(ptr1:=#a_HWTEntryStr+2, ptr2:=pObjectPath+1, cntr:=a_HWTEntryStr[0]);   
        
        //Return Hardware Tree Entry and set return codes
        pResult^.aData[0]$t_HWCGetHardwareTreeEntryOut.pStr_NextHardewareTreeEntry := #a_HWTEntryStr[0];
        ret_code := READY;
        pResult^.aData[0]$t_HWCGetHardwareTreeEntryOut.retcode := _HWC_RETVAL_OK_HWTSTR_RETURNED;
        
        // Compare Module string with passed string
        if pPara^.aPara[0]$t_HWCGetHardwareTreeEntryIn.pStr_LastHardewareTreeEntry then 
        
          if pPara^.aPara[0]$t_HWCGetHardwareDiagnosisIn.pStr_HardewareTreeEntry$^USINT^ =
             pResult^.aData[0]$t_HWCGetHardwareTreeEntryOut.pStr_NextHardewareTreeEntry$^USINT^ then // Compare Length
             
            if _memcmp( ptr1:= pPara^.aPara[0]$t_HWCGetHardwareDiagnosisIn.pStr_HardewareTreeEntry // Compare Data
                      , ptr2:= pResult^.aData[0]$t_HWCGetHardwareTreeEntryOut.pStr_NextHardewareTreeEntry
                      , cntr:= pResult^.aData[0]$t_HWCGetHardwareTreeEntryOut.pStr_NextHardewareTreeEntry$^USINT^ + 2) = 0 then   // + 2 for 2 Byte Length
                      
              pResult^.aData$t_HWCGetHardwareTreeEntryOut.retcode :=  _HWC_RETVAL_OK_MODULE_FOUND; 
            end_if;
          end_if;
        end_if;
        
      end_if;
//**********************************************************************************************************************************************************
    SDIAS_CLT_GET_ANALOG_DATA_BUFFER:
      // command is only supported in some modules
      ret_code := ERROR;

//**********************************************************************************************************************************************************
    SDIAS_CLT_GET_INFO_PLACE:
      // return the place
      ret_code := READY;
      pResult^.uiLng := sizeof(UINT);
      pResult^.aData[0]$UINT := Place$UINT;

  ELSE
    // we forward this message to the SDIAS Manager
    ret_code := SdiasIn.NewInst(pPara, pResult);
	END_CASE;

END_FUNCTION


FUNCTION SdiasBase::AddRdAccess
	VAR_INPUT
		hOffset 	: HINT;
		uLength 	: UINT;
		bCtrlAcc0 	: BOOL;(* := false *)
		ppData 	: pVoid;
		ppStateByte 	: pVoid;
		ppControlByte 	: pVoid;
		pAccessHandle 	: ^UDINT;
		WaitForAccessHandle 	: UDINT;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR
  
  MyPara.uiCmd := SDIAS_CMD_ADD_RD_ACCESS;
  MyPara.aPara[0] := this$DINT;
  MyPara.aPara[1] := Place$DINT;
  MyPara.aPara[2] := Required;
  MyPara.aPara[3] := hOffset;
  MyPara.aPara[4] := uLength;
  MyPara.aPara[5] := ppData$DINT;
  MyPara.aPara[6] := ppStateByte$DINT;
  MyPara.aPara[7] := ppControlByte$DINT;
  MyPara.aPara[8] := pAccessHandle$DINT;
  MyPara.aPara[9] := WaitForAccessHandle$DINT;
  MyPara.aPara[10]:= to_dint(bCtrlAcc0);

  ret_code := SdiasIn.NewInst(#MyPara, #MyResult);

  if ret_code <> READY then
    LogValue("@02C9 (SdiasBase::AddRdAccess) Failed to add read access for SDIAS module at Place 0x{0}, offset {1}", Place, to_udint(hOffset));
  end_if;

END_FUNCTION


FUNCTION SdiasBase::AddWrAccess
	VAR_INPUT
		hOffset 	: HINT;
		uLength 	: UINT;
		bCtrlAcc0 	: BOOL;(* := false *)
		ppData 	: pVoid;
		ppStateByte 	: pVoid;
		ppControlByte 	: pVoid;
		pAccessHandle 	: ^UDINT;
		WaitForAccessHandle 	: UDINT;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR
  
  MyPara.uiCmd := SDIAS_CMD_ADD_WR_ACCESS;
  MyPara.aPara[0] := this$DINT;
  MyPara.aPara[1] := Place$DINT;
  MyPara.aPara[2] := Required;
  MyPara.aPara[3] := hOffset;
  MyPara.aPara[4] := uLength;
  MyPara.aPara[5] := ppData$DINT;
  MyPara.aPara[6] := ppStateByte$DINT;
  MyPara.aPara[7] := ppControlByte$DINT;
  MyPara.aPara[8] := pAccessHandle$DINT;
  MyPara.aPara[9] := WaitForAccessHandle$DINT;
  MyPara.aPara[10]:= to_dint(bCtrlAcc0);

  ret_code := SdiasIn.NewInst(#MyPara, #MyResult);

  if ret_code <> READY then
    LogValue("@02CA (SdiasBase::AddWrAccess) Failed to add write access for SDIAS module at Place 0x{0}, offset {1}", Place, to_udint(hOffset));
  end_if;

END_FUNCTION

FUNCTION VIRTUAL GLOBAL SdiasBase::ConnectEvent

  // overwrite this, if initmodule is used, so _NotInitialized can be shown during initialisation
  eInitState := _ClassOk;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::DisconnectEvent

  // at the disconnect we can use directly the server
  ClassState := eInitState := _NoHardware;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::InitModule
	VAR_OUTPUT
		eModuleInitState 	: iprStates;
	END_VAR
  
  // should be overwritten by derivation to initialize the module via service accesses in a state machine if necessary (return BUSY while initializing)
  eModuleInitState := READY;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::SerialNo::Read
	VAR_OUTPUT
		output (EAX) 	: UDINT;
	END_VAR

	SerialNo := SerialNoStr.Data.Read();
	output := SerialNo;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::SerialNo::Write
	VAR_INPUT
		input (EAX) 	: UDINT;
	END_VAR
	VAR_OUTPUT
		result (EAX) 	: UDINT;
	END_VAR

	SerialNo := input;
 	result := (SerialNoStr.Data.Write(SerialNo))$UDINT;

END_FUNCTION


FUNCTION SdiasBase::SdiasBase
	VAR_OUTPUT
		ret_code 	: ConfStates;
	END_VAR

  ClassState := eInitState := _NotInitialized;

#ifdef HWC_LogInit
  HWC_LogInit();
#endif

  // intialize pointers
  mt_api            := NIL;
  pSdoCreationMutex := NIL;

  //get interface for hardwaretree functions
  if ( OS_CILGET(INTERFACE_HARDWARETREE, #pHardwareTree)) then
    pHardwareTree := NIL;
    
    LogError("@02CB (SdiasBase::SdiasBase) Hardwaretree-Interface not found. A newer OS is required for Safety-Modules");
  end_if;

	ret_code := C_OK;

END_FUNCTION

#pragma warning(disable: 73)
FUNCTION VIRTUAL GLOBAL SdiasBase::CheckDeviceID
	VAR_INPUT
		udID2Check 	: UDINT;
	END_VAR
	VAR_OUTPUT
		bIsOK 	: BOOL;
	END_VAR

  bIsOK := TRUE;

END_FUNCTION
#pragma warning(default: 73)


FUNCTION VIRTUAL GLOBAL SdiasBase::LEDControl::Write
	VAR_INPUT
		input (EAX) 	: DINT;
	END_VAR
	VAR_OUTPUT
		result (EAX) 	: DINT;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR

#ifdef DEACTIVATED_LSL
  if Place.Read() = DEACTIVATED_LSL then
    result := LEDControl;
    return;
  end_if;
#endif
  
  // only 2 bits are used for LED control
  input := input AND 2#11;
  
  if input <> LEDControl then
    MyPara.uiCmd := SDIAS_CMD_CHANGE_LED_STATE;
    MyPara.aPara[0] := this$DINT;
    MyPara.aPara[1] := Place$DINT;
    MyPara.aPara[2] := input;
    
    if SdiasIn.NewInst(#MyPara, #MyResult) <> READY then
      if bd_SingleLog.LedControlFailed = FALSE then
        bd_SingleLog.LedControlFailed := TRUE;
        LogValue("@02CC (SdiasBase::LEDControl::Write) [SL] Failed to change LED state for SDIAS module at Place 0x{0}", Place);
      end_if;
    end_if;
  end_if;
	LEDControl := input;
 	result := LEDControl;

END_FUNCTION


FUNCTION SdiasBase::StartReadSDO
	VAR_INPUT
		hOffset 	: HINT;
		usLength 	: USINT;
		eCommand 	: t_SDIAS_SDO_Cmd;
	END_VAR
	VAR_OUTPUT
		eRetcode 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
    RdWrHead : t_SDIAS_SDO_RdWr_Head;
  END_VAR

  if usLength > SDIAS_MAX_SDO_READ_LENGTH then
    eRetcode := ERROR;
    return;
  end_if;

  RdWrHead.Offset := hOffset;
  RdWrHead.Length := usLength;
  
  MyPara.uiCmd := SDIAS_CMD_SDO_SEND_REQUEST;
  MyPara.aPara[0] := (#SDOState.pMeth)$DINT;
  MyPara.aPara[1] := to_dint(eCommand);
  MyPara.aPara[2] := Place$DINT;
  MyPara.aPara[3] := sizeof(hOffset) + sizeof(usLength); // length of command
  MyPara.aPara[4] := (#RdWrHead)$DINT;

  if pSdoCreationMutex then
    OS_MT_WAIT(pSdoCreationMutex);
  end_if;

  eRetcode := SdiasIn.NewInst(#MyPara, #MyResult);

  if eRetcode = READY then
    udActiveSDOCnt += 1;
    SDOState := BUSY;
  else
    eLastErrorInfo.Offset := hOffset;
    eLastErrorInfo.Length := usLength;
    SDOState := ERROR;
  end_if;
  
  if pSdoCreationMutex then
    OS_MT_SIGNAL(pSdoCreationMutex);
  end_if;

END_FUNCTION

FUNCTION SdiasBase::StartReadSDOSPI
	VAR_INPUT
		PageAdress  : HINT;
    usPageOffset 	: HSINT;
		usLength 	: USINT;
		eCommand 	: t_SDIAS_SDO_Cmd;
	END_VAR
	VAR_OUTPUT
		eRetcode 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
    RdSPIHead : t_SDIAS_SDO_Rd_SPI_Head;
  END_VAR
  
  // 4Byte header
  if usLength > SDIAS_MAX_SDO_SPI_READ_LENGTH then
    eRetcode := ERROR;
    return;
  end_if;
  
  RdSPIHead.PageAdress := PageAdress;              
  RdSPIHead.PageOffset := usPageOffset;
  RdSPIHead.Length := usLength;
  
  MyPara.uiCmd := SDIAS_CMD_SDO_SEND_REQUEST;
  MyPara.aPara[0] := (#SDOState.pMeth)$DINT;
  MyPara.aPara[1] := to_dint(eCommand);
  MyPara.aPara[2] := Place$DINT;
  MyPara.aPara[3] := sizeof(PageAdress) + sizeof(usPageOffset) + sizeof(usLength); // length of command
  MyPara.aPara[4] := (#RdSPIHead)$DINT;

  if pSdoCreationMutex then
    OS_MT_WAIT(pSdoCreationMutex);
  end_if;
  
  eRetcode := SdiasIn.NewInst(#MyPara, #MyResult);

  if eRetcode = READY then
    udActiveSDOCnt += 1;
    SDOState := BUSY;
  else
    eLastErrorInfoSPI.PageAdress := PageAdress;
    eLastErrorInfoSPI.PageOffset := usPageOffset;
    eLastErrorInfoSPI.Length := usLength;
    SDOState := ERROR;
  end_if;
  
  if pSdoCreationMutex then
    OS_MT_SIGNAL(pSdoCreationMutex);
  end_if;

END_FUNCTION


FUNCTION SdiasBase::StartWriteSDO
	VAR_INPUT
		hOffset 	: HINT;
		usLength 	: USINT;
		pWriteBuffer 	: ^USINT;
		eCommand 	: t_SDIAS_SDO_Cmd;
	END_VAR
	VAR_OUTPUT
		dRetcode 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
    TempSendBuf: ARRAY [0..127] OF USINT;
    RdWrHead : t_SDIAS_SDO_RdWr_Head;
  END_VAR
  
  if usLength > SDIAS_MAX_SDO_WRITE_LENGTH then
    dRetcode := ERROR;
    return;
  end_if;
  
  RdWrHead.Offset := hOffset;
  RdWrHead.Length := usLength;
  TempSendBuf[0]$t_SDIAS_SDO_RdWr_Head := RdWrHead;
  _memcpy(ptr1:=#TempSendBuf[3], ptr2:=pWriteBuffer, cntr:=usLength);
  
  MyPara.uiCmd := SDIAS_CMD_SDO_SEND_REQUEST;
  MyPara.aPara[0] := (#SDOState.pMeth)$DINT;
  MyPara.aPara[1] := to_dint(eCommand);
  MyPara.aPara[2] := Place$DINT;
  MyPara.aPara[3] := sizeof(hOffset) + sizeof(usLength) + usLength; // length of command
  MyPara.aPara[4] := (#TempSendBuf)$DINT;

  if pSdoCreationMutex then
    OS_MT_WAIT(pSdoCreationMutex);
  end_if;
  
  dRetcode := SdiasIn.NewInst(#MyPara, #MyResult);

  if dRetcode = READY then
    udActiveSDOCnt += 1;
    SDOState := BUSY;
  else
    eLastErrorInfo.Offset := hOffset;
    eLastErrorInfo.Length := usLength;
    SDOState := ERROR;
  end_if;
  
  if pSdoCreationMutex then
    OS_MT_SIGNAL(pSdoCreationMutex);
  end_if;

END_FUNCTION


FUNCTION SdiasBase::StartSDOIntelligent
	VAR_INPUT
		pSDOBuffer 	: ^USINT;
		usLength 	: USINT;
		eCommand 	: t_SDIAS_SDO_Cmd;
	END_VAR
	VAR_OUTPUT
		eRetcode 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR
  
  if usLength > 36 then
    eRetcode := ERROR;
    return;
  end_if;

  MyPara.uiCmd := SDIAS_CMD_SDO_SEND_REQUEST;
  MyPara.aPara[0] := (#SDOState.pMeth)$DINT;
  MyPara.aPara[1] := to_dint(eCommand);
  MyPara.aPara[2] := Place$DINT;
  MyPara.aPara[3] := usLength; // length of command
  MyPara.aPara[4] := (pSDOBuffer)$DINT;

  if pSdoCreationMutex then
    OS_MT_WAIT(pSdoCreationMutex);
  end_if;
  
  eRetcode := SdiasIn.NewInst(#MyPara, #MyResult);

  if eRetcode = READY then
    udActiveSDOCnt += 1;
    SDOState := BUSY;
  else
    eLastErrorInfo.Offset := 0;
    eLastErrorInfo.Length := usLength;
    SDOState := ERROR;
  end_if;
  
  if pSdoCreationMutex then
    OS_MT_SIGNAL(pSdoCreationMutex);
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::SDOState::NewInst
	VAR_INPUT
		pPara 	: ^CmdStruct;
		pResult 	: ^results;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR

	ret_code := ERROR;
  
	CASE pPara^.uiCmd OF
//**********************************************************************************************************************************************************
		SDIAS_CMD_SDO_RESPONSE:
      
      if pSdoCreationMutex then
        OS_MT_WAIT(pSdoCreationMutex);
      end_if;

      // check if we're waiting for a response
      if udActiveSDOCnt then
        ret_code := ReceiveSDOResponse(eCommand:=pPara^.aPara[0]$t_SDIAS_SDO_Cmd,
                                       hOffset:=pPara^.aPara[1]$HINT,
                                       usLength:=pPara^.aPara[2]$USINT, 
                                       eResult:=pPara^.aPara[3]$iprStates, 
                                       pResponseBuffer:=pPara^.aPara[4]$^USINT, 
                                       ResponseLength:=pPara^.aPara[5]$USINT
                                       );
      end_if;

      if pSdoCreationMutex then
        OS_MT_SIGNAL(pSdoCreationMutex);
      end_if;

//**********************************************************************************************************************************************************
	END_CASE;

END_FUNCTION


FUNCTION SdiasBase::ReadDirect
	VAR_INPUT
		hOffset 	: HINT;
		usLength 	: USINT;
		pData 	: pVoid;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR

  MyPara.uiCmd := SDIAS_CMD_DA_RD_ACCESS;
  MyPara.aPara[0] := hOffset;
  MyPara.aPara[1] := usLength;
  MyPara.aPara[2] := pData$DINT;
  
  if SdiasIn.NewInst(#MyPara, #MyResult) <> READY then
    LogValue("@02CD (SdiasBase::ReadDirect) Direct read access failed for SDIAS module at Place 0x{0}", Place);
  end_if;

END_FUNCTION


FUNCTION SdiasBase::WriteDirect
	VAR_INPUT
		hOffset 	: HINT;
		usLength 	: USINT;
		pData 	: pVoid;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR

  MyPara.uiCmd := SDIAS_CMD_DA_WR_ACCESS;
  MyPara.aPara[0] := hOffset;
  MyPara.aPara[1] := usLength;
  MyPara.aPara[2] := pData$DINT;
  
  if SdiasIn.NewInst(#MyPara, #MyResult) <> READY then
    LogValue("@02CE (SdiasBase::WriteDirect) Direct write access failed for SDIAS module at Place 0x{0}", Place);
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SdiasBase::ReceiveSDOResponse
	VAR_INPUT
		eCommand 	: t_SDIAS_SDO_Cmd;
		hOffset 	: HINT;
		usLength 	: USINT;
		eResult 	: iprStates;
		pResponseBuffer 	: ^USINT;
		ResponseLength 	: USINT;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR

  udActiveSDOCnt -= 1;
  // reset SDOResponseHandle
  bSDOResponseHandle := FALSE;

  // if it succeeded, decrement the number of open requests
  if eResult = READY then
    if udActiveSDOCnt = 0 then
      // if the last one has succeeded, show it on the state server
      SDOState := READY;
    end_if;

    case eCommand of
//**********************************************************************************************************************************************************
      SDIAS_SDO_CMD_SPI_READ:
        
        hOffset := pResponseBuffer^$t_SDIAS_SDO_Rd_SPI_Head.PageAdress;       
        usLength := pResponseBuffer^$t_SDIAS_SDO_Rd_SPI_Head.Length;
        
        case hOffset of
        
          SDIAS_SPI_DATA_OBJECT_LIST_PAGE_ADDRESS:
          
            if eResponseStateSPI = wait then
              eResponseStateSPI := valid;
              
              _memcpy(  ptr1:=pSPIDataObjectList+pResponseBuffer^$t_SDIAS_SDO_Rd_SPI_Head.PageOffset
                      , ptr2:=(pResponseBuffer + sizeof(t_SDIAS_SDO_Rd_SPI_Head))
                      , cntr:=usLength );
                          
            end_if;            
            
        //Other adresses  
        else 
          
            if eResponseStateSPI = wait then
              eResponseStateSPI := valid;
                                          
              _memcpy(  ptr1:=#aSPIReceive
                      , ptr2:=(pResponseBuffer + sizeof(t_SDIAS_SDO_Rd_SPI_Head))
                      , cntr:=usLength);   
                                 
            end_if;        
            
        end_case;
        
        bSDOResponseHandle := TRUE;
           
//**********************************************************************************************************************************************************
    end_case;

  
//*****************************************************************************

  else
    // an error occured while performing the operation
    
    // save the error information
    eLastErrorInfo.Offset := hOffset;
    eLastErrorInfo.Length := usLength;
    eLastErrorCommand := eCommand;
    
    SDOState := ERROR;
  end_if;
  
  ret_code := READY;
  
END_FUNCTION

#pragma warning(disable: 73);
FUNCTION VIRTUAL SdiasBase::LogError
	VAR_INPUT
		pMsg 	: ^CHAR;
	END_VAR

#ifdef HWC_LogError
  HWC_LogError(this, pMsg);
#endif

END_FUNCTION


FUNCTION VIRTUAL SdiasBase::LogValue
	VAR_INPUT
		pString 	: ^CHAR;
		udValue 	: UDINT;
		udValue2 	: UDINT;(* := 16#FFFFFFFF *)
		udValue3 	: UDINT;(* := 16#FFFFFFFF *)
	END_VAR

#ifdef HWC_LogValue3
  HWC_LogValue3(this, pString, udValue, udValue2, udValue3);
#endif
  
END_FUNCTION


FUNCTION VIRTUAL SdiasBase::MyTrace
	VAR_INPUT
		pFormat 	: ^CHAR;
		pTxt0 	: ^CHAR;
		pTxt1 	: ^CHAR;
		pTxt2 	: ^CHAR;
	END_VAR

  TRACE("(SdiasBase::MyTrace) Old logging function call detected. Please update hardware class library to v01.01.070 or higher");

END_FUNCTION
#pragma warning(default: 73);

FUNCTION GLOBAL SdiasBase::CheckSum_16
	VAR_INPUT
		pData 	: ^UINT;
		uiDataLength 	: UINT;
		uiCRCStart 	: UINT;
	END_VAR
	VAR_OUTPUT
		uiCRCCalculation 	: UINT;
	END_VAR
  VAR
  	TmpCrc : UINT;
    idxI   : UINT;
    Carry  : USINT;
  END_VAR
  
	TmpCrc := uiCrcStart;			

  for idxI := 0 to (uiDataLength - 1) by 2 do
		Carry := ((TmpCrc SHR 15) AND 0x01)$USINT;
		TmpCrc := ((TmpCrc SHL 1) AND 0xFFFE);
		TmpCrc += (pData + idxI)^;
		TmpCrc += Carry;
  end_for;

	uiCrcCalculation := TmpCrc;
  
END_FUNCTION


FUNCTION GLOBAL SdiasBase::CheckSum_SPI
	VAR_INPUT
		pData 	: ^USINT;
		uiDataLength 	: UINT;
		udCRCStart 	: UDINT;(* := 16#0000 *)
	END_VAR
	VAR_OUTPUT
		udCheckSum 	: UDINT;
	END_VAR
  VAR
  	TmpCrc  : UDINT;
    Carry   : USINT;  
    idxI    : UINT;  
  END_VAR
  
	TmpCrc := udCRCStart;
  
  for idxI := 0 to (uiDataLength - 1) by 1 do
    Carry   := (pData + idxI)^$HSINT;
		TmpCrc += Carry;
  end_for;
  
  udCheckSum := TmpCrc;
  
END_FUNCTION


FUNCTION VIRTUAL SdiasBase::GetObjectThisp
	VAR_INPUT
		pObjectPath 	: ^USINT;
	END_VAR
	VAR_OUTPUT
		pThis 	: UDINT;
	END_VAR
  
  if ( pHardwareTree <> NIL ) then
    pThis := HWT_iGetObjectThisp(0,pObjectPath);
  else
    pThis := 0;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SdiasBase::SetObjectThisp
	VAR_INPUT
		pObjectPath 	: ^USINT;
		pThis 	: UDINT;
	END_VAR
	VAR_OUTPUT
		dRetcode 	: DINT;
	END_VAR
  
  if ( pHardwareTree <> NIL ) then
    dRetcode := HWT_iSetObjectThisp(0,pObjectPath,pThis);
  else
    dRetcode := 0;
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::GetPointer2ObjectPath
	VAR_INPUT
		pThisPointerOverride 	: pVirtualBase;(* := NIL *)
	END_VAR
	VAR_OUTPUT
		p_us_ObjectPath 	: ^USINT;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
    p_us_PreviousPath : ^USINT;
  END_VAR

  if ( pObjectPath = NIL ) then
  
    if ( Place <= SDIAS_MAX_PLACE_NR ) then
    
      MyPara.uiCmd := SDIAS_CMD_GET_HW_PATH;      
      if SdiasIn.NewInst(#MyPara, #MyResult) <> READY then
        if bd_SingleLog.GetHwPathFailed = FALSE then
          bd_SingleLog.GetHwPathFailed := TRUE;
          LogError("@02CF (SdiasBase::GetPointer2ObjectPath) [SL] Failed to get hardware path of preceding module");
        end_if;
      else
        p_us_PreviousPath := MyResult.aData[4]$^USINT;

        //alloc memory
        pObjectPath := ( ToStdLib.Malloc( size := p_us_PreviousPath^ + 3 ) )$^USINT;
        
        ToStdLib.MemCpy ( dest 		:= pObjectPath
                        , source	:= p_us_PreviousPath
                        , size		:= p_us_PreviousPath^ + 1
                        );

        // increase length
        pObjectPath^$UINT += 2;
        
        //insert escape sequence for SDIAS
        ( pObjectPath + pObjectPath^ - 1)^ := HWT_ESCAPE_SDIAS;

        //insert SDIAS place
        ( pObjectPath + pObjectPath^)^ := Place$USINT;

        if pThisPointerOverride then
          //register object path in OS
          SetObjectThisp(pObjectPath:=pObjectPath, pthis:=pThisPointerOverride$UDINT);
        else
          //register object path in OS
          SetObjectThisp(pObjectPath:=pObjectPath, pthis:=this$UDINT);
        end_if;
      end_if;
    else
      p_us_ObjectPath := NIL;
    end_if;    
  end_if;
  
  p_us_ObjectPath := pObjectPath;
  
END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::AddAccesses
	VAR_OUTPUT
		eModuleInitState 	: iprStates;
	END_VAR
  
  // overload this if the module needs special accesses (not covered by default accesses according to info block)
  eModuleInitState := READY;

END_FUNCTION


FUNCTION VIRTUAL SdiasBase::GetTaskCfg
	VAR_OUTPUT
		TaskCfg 	: BDINT;
	END_VAR

  // define in derivation which tasks are needed

// like this
//  TaskCfg := SDIAS_TASK_MASK_RT_PRE OR SDIAS_TASK_MASK_RT_POST OR SDIAS_TASK_MASK_CY;

  TaskCfg := 0;

END_FUNCTION


FUNCTION SdiasBase::ReadSPIData
	VAR_INPUT
		h_PageAdress 	: HINT;
		hs_PageOffset 	: HSINT;
		us_Length 	: USINT;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
    TempRet_code  : iprStates;
  END_VAR
  
    //only access spi if it is locked or lock check is not activated
    if sSPIAccess.eSPIAccessProtect | sSPIAccess.bActivateProtectionCheck = FALSE then
    
      //initialize return value
      ret_code := BUSY;
      
      case eResponseStateSPI of
        
//*****************************************************************************************************************************************************
        idle:
          // set it to wait already, because maybe this is called in background and processed in cyclic before returning out of the method => to handle the response correctly
          udSDOTimeoutSPI   := ops.tAbsolute;
          eResponseStateSPI := wait; 

          TempRet_code  := StartReadSDOSPI(PageAdress:= h_PageAdress , usPageOffset:=hs_PageOffset, usLength:=us_Length, eCommand:=SDIAS_SDO_CMD_SPI_READ );

          if TempRet_code <> READY then
            // if not ready => get back to the current step
            eResponseStateSPI := idle;

            if TempRet_code = ERROR then
              ret_code := ERROR;
              LogError("@02D0 (SdiasBase::ReadSPIData) Failed to add Read SPI SDO at HWT Diagnosis");
            end_if;
          end_if; 
        
//*****************************************************************************************************************************************************
        wait:
          // wait for response
          if ((ops.tAbsolute - udSDOTimeoutSPI) > SDIAS_SDO_TIMEOUT * 2) then
            LogError("@02D1 (SdiasBase::ReadSPIData) SDO Response timeout read state at HWT Diagnosis");
            ret_code := ERROR;
          end_if;
        
//*****************************************************************************************************************************************************
        valid: 
          ret_code := READY;
          eResponseStateSPI := idle;
    
      end_case;
    else  
      //spi access is not locked (biSPIAccessProtect)
      ret_code := ERROR;      
    end_if;
      
END_FUNCTION


FUNCTION SdiasBase::GetDataObjectList
	VAR_INPUT
    pDataObjectList	: ^t_SPI_DataObjectList;(* := NIL *)
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
    SPI_Ret_Code : iprStates;
    ListLength : UDINT;
    tempvar : HDINT;
  END_VAR
  
    ret_code := BUSY;
     
    if pDataObjectList then
      pSPIDataObjectList := pDataObjectList;
    else
      pSPIDataObjectList := #a_DataObjectList;
    end_if;
  
    case eGetObjectListSSW of
    //*****************************************************************************
      _GetObjList:
        //Get Object List, Data in method ReceiveSDOResponse
        SPI_Ret_code := ReadSPIData(  h_PageAdress  :=  SDIAS_SPI_DATA_OBJECT_LIST_PAGE_ADDRESS
                                    , hs_PageOffset :=  SDIAS_SPI_DATA_OBJECT_LIST_PAGE_OFFSET
                                    , us_Length     :=  SDIAS_MAX_SDO_SPI_READ_LENGTH); 
        tempvar := pSPIDataObjectList^$t_SPI_DataObjectList.Identifier; 
        
        if SPI_ret_code = BUSY then
          ret_code := BUSY;
        elsif SPI_Ret_Code = ERROR then
          ret_code := ERROR;
        elsif SPI_Ret_Code = READY then                                    
          if pSPIDataObjectList^$t_SPI_DataObjectList.Identifier <> SDIAS_SPI_DATA_OBJECT_LIST_IDENTIFIER then
            LogError("@02D2 (SDIASBase::GetDataObjectList) Wrong Identifier from data object list at HWT Diagnosis");
            ret_code := ERROR;
            return;
          end_if;
          
          ListLength := pSPIDataObjectList^$t_SPI_DataObjectList.ListLength;
                      
          //if Listlength +16 (HeaderDataobject list , 4byte identifier,4bytechecksum, 4byte list version and 4byte list
          if (ListLength + sizeof(t_SPI_DataObjectListHeader)) > SDIAS_MAX_SDO_SPI_READ_LENGTH then            
            eGetObjectListSSW := _GetOptionalLists;
          else
            ret_code := READY;
            eGetObjectListSSW := _GetObjList;
          end_if;          
        end_if;
                
    //*****************************************************************************
      _GetOptionalLists:
              
        //Get Object List, Data in method ReceiveSDOResponse
        SPI_Ret_code := ReadSPIData(  h_PageAdress  :=  SDIAS_SPI_DATA_OBJECT_LIST_PAGE_ADDRESS
                                    , hs_PageOffset :=  SDIAS_SPI_DATA_OBJECT_LIST_PAGE_OFFSET + SDIAS_SPI_HEADER_BEFORE_OPTIONAL_ELEMENTS
                                    , us_Length     :=  SDIAS_MAX_SDO_SPI_READ_LENGTH);

        if SPI_ret_code = BUSY then
          ret_code := BUSY;
        elsif SPI_Ret_Code = ERROR then
          ret_code := ERROR;
        elsif SPI_Ret_Code = READY then
            ret_code := READY;
            eGetObjectListSSW := _GetObjList;
        end_if;          
    //*****************************************************************************          
    
    end_case;
  
END_FUNCTION


FUNCTION SdiasBase::GetSPIDataFromID
	VAR_INPUT
		usListID 	: USINT;
		pData 	: ^CHAR;
		RequDataLength 	: UDINT;
		pIDHeader 	: ^t_SPI_ListHeader;
		pReadLen 	: ^UDINT;(* := NIL *)
		udReadOffset 	: UDINT;(* := 16#00 *)
		bIDFound 	: ^BOOL;(* := nil *)
		pDataObjectList 	: ^t_SPI_DataObjectList;(* := NIL *)
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR  
  VAR
  	TempRetCode : iprStates;
    hs_PageOffset : HSINT;
    h_PageAdress : HINT;
    i :   USINT;
    bTempIDFound : BOOL;
    udActReadLength : UDINT;
    udActRequestLength : UDINT;
  END_VAR
  
  //Set Errors
  ret_code := ERROR;   
  
  case eGetSPIDataSSW of
    //*****************************************************************************
    _GetDataObjectList:      
      
      //Get Data Object List
      TempRetCode := GetDataObjectList(pDataObjectList);   
        
      if TempRetCode = READY then
        ret_code := BUSY;
        eGetSPIDataSSW := _CheckID;
      elsif TempRetCode = ERROR then
        ret_code := ERROR;
      else
        ret_code := BUSY;
      end_if;
      

    //*****************************************************************************
    _CheckID:  

      if bIDFound = Nil then
        bIDFound := #bTempIDFound;
      end_if;    
      bIDFound^ := FALSE;    
            
      //Check if ID is ID of Identification list
      if pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList.ID = usListID then
        bIDFound^ := TRUE;
        hs_PageOffset := pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList.BaseAdress.PageOffset; 
        // PageAdress0 is casted to hint because PageAdress0 and PageAdress1 is needed for h_Pageadress
        h_PageAdress  := pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList.BaseAdress.PageAdress0$HINT;        
        
      elsif pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration.ID = usListID then
        bIDFound^ := TRUE;
        hs_PageOffset := pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration.BaseAdress.PageOffset; 
        // PageAdress0 is casted to hint because PageAdress0 and PageAdress1 is needed for h_Pageadress
        h_PageAdress  := pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration.BaseAdress.PageAdress0$HINT;
        
      else
        //Get Right Offset and Page adress for input ID
        for i:= 0 to (pSPIDataObjectList^$t_SPI_DataObjectList.ListLength / sizeof(t_SPI_List)) do 
        udActReadLength := (pSPIDataObjectList^$t_SPI_DataObjectList.ListLength / sizeof(t_SPI_List));
        hs_PageOffset := pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].ID ;
          if pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].ID = usListID then
            bIDFound^ := TRUE;
            hs_PageOffset := pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].BaseAdress.PageOffset; 
            // PageAdress0 is casted to hint because PageAdress0 and PageAdress1 is needed for h_Pageadress
            h_PageAdress  := pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].BaseAdress.PageAdress0$HINT;
            exit;
          end_if;
        end_for;
      end_if;
      
      //Requested ID not found
      if bIDFound^ = FALSE then        
        //Reset SSW and set error code ready that next id can be read out
        eGetSPIDataSSW :=_GetDataObjectList;
        ret_code := READY;  
        return;
      else
        
        //Read Out List Data of right ID to get length for 2nd access
        TempRetCode := ReadSPIData(  h_PageAdress  :=  h_PageAdress
                                   , hs_PageOffset :=  hs_PageOffset
                                   , us_Length     :=  sizeof(t_SPI_ListHeader)(* + sizeof(t_SDIAS_SDO_Rd_SPI_Head)*));
      
      end_if;
           
      if TempRetCode = READY then
        sSPIData.SPIIDHeader  := aSPIReceive[0]$t_SPI_ListHeader;
        pIDHeader^            := sSPIData.SPIIDHeader;        //return ID Header     
        sSPIData.udReadOffset := udReadOffset ;
        sSPIData.udReadData   := 0;
        sSPIData.udActWROffset := 0;
        ret_code       := BUSY;
        eGetSPIDataSSW := _GetData;
       
      elsif TempRetCode = ERROR then
        ret_code := ERROR;
      else
        ret_code := BUSY;
      end_if;
      
    //***********************************************************************************************
    _GetData:
      if bIDFound = Nil then
        bIDFound := #bTempIDFound;
      end_if;
      
      pIDHeader^      := sSPIData.SPIIDHeader;    //return ID Header
      udActReadLength := sSPIData.SPIIDHeader.ListLength - sSPIData.udReadOffset; //get actual read length 
      
      //if listlength is shorter then requested length read only listlength
      if (RequDataLength >= (sSPIData.SPIIDHeader.ListLength-udReadOffset)) then
        udActRequestLength :=  sSPIData.SPIIDHeader.ListLength-udReadOffset;
      else
        udActRequestLength := RequDataLength;
      end_if;      
      
      //if udActReadLength is more then requested length set udActReadLength to requested length
      if (udActReadLength > (udActRequestLength-sSPIData.udActWROffset)) then
        udActReadLength := (udActRequestLength-sSPIData.udActWROffset);
      end_if;
        
      //Check if we have to do more than one SPI accesses to get the whole data
      if udActReadLength > SDIAS_MAX_SDO_SPI_READ_LENGTH then
        udActReadLength := SDIAS_MAX_SDO_SPI_READ_LENGTH;    
      end_if; 
   
      bIDFound^ := FALSE; 
      
      //Check if ID is ID of Identification list
      if pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList.ID = usListID then
        bIDFound^ := TRUE;
        hs_PageOffset := pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList.BaseAdress.PageOffset;
        // PageAdress0 is casted to hint because PageAdress0 and PageAdress1 is needed for h_Pageadress
        h_PageAdress  := pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList.BaseAdress.PageAdress0$HINT;
          
      elsif pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration.ID = usListID then
        bIDFound^ := TRUE;
        hs_PageOffset := pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration.BaseAdress.PageOffset;
        // PageAdress0 is casted to hint because PageAdress0 and PageAdress1 is needed for h_Pageadress
        h_PageAdress  := pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration.BaseAdress.PageAdress0$HINT;
        
      else      
        //Get Right Offset and Page adress for input ID
        for i:= 0 to (pSPIDataObjectList^$t_SPI_DataObjectList.ListLength / sizeof(t_SPI_List)) do 
          if pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].ID = usListID then
            bIDFound^ := TRUE;
            hs_PageOffset := pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].BaseAdress.PageOffset;
            // PageAdress0 is casted to hint because PageAdress0 and PageAdress1 is needed for h_Pageadress
            h_PageAdress  := pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].BaseAdress.PageAdress0$HINT;
            exit;
          end_if;
        end_for;
      end_if;
      
      //Requested ID not found
      if bIDFound^ = FALSE then      
        //Reset SSW and set error code ready that next id can be read out
        eGetSPIDataSSW :=_GetDataObjectList;
        ret_code := READY;        
        return;   
      else        
      
        TempRetCode := ReadSPIData(   h_PageAdress  :=  h_PageAdress
                                    , hs_PageOffset :=  hs_PageOffset + sSPIData.udReadOffset$USINT + sizeof(t_SPI_ListHeader) 
                                    , us_Length     :=  udActReadLength$USINT);
      
      end_if;   

      if TempRetCode = READY then 
        sSPIData.udReadData += udActReadLength;
        _memcpy(ptr1:=pData+sSPIData.udActWROffset, ptr2:=#aSPIReceive[0], cntr:=udActReadLength); 
        if sSPIData.udReadData >= udActRequestLength then
          //check if pointer is valid
          if pReadLen <> NIL then
            pReadLen^ := sSPIData.udReadData;
          end_if;   
          //Set Retcodes and reset SSW 
          ret_code := READY;
          eGetSPIDataSSW := _GetDataObjectList;
        else
          sSPIData.udReadOffset += udActReadLength;
          sSPIData.udActWROffset += udActReadLength;
          ret_code := BUSY;          
        end_if;
       
      elsif TempRetCode = BUSY then
        ret_code := BUSY;
      else
        ret_code := ERROR;
      end_if;
                       
  end_case;
            
END_FUNCTION


FUNCTION SdiasBase::GetBaseAdressFromID
	VAR_INPUT
		usListID 	: DINT;
		pListElement 	: ^t_SPI_List;
		pDataObjectList 	: ^t_SPI_DataObjectList;(* := NIL *)
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
  	TempRetCode     : iprStates;
    i               :   USINT;
  END_VAR
  
  //Set Errors
  ret_code := ERROR;   
  
  TempRetCode := GetDataObjectList(pDataObjectList);
  
  //Get Data Object List
  if TempRetCode = READY then
      
    //Check if ID is ID of Identification list
    if pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList.ID = usListID then
      pListElement^  := pSPIDataObjectList^$t_SPI_DataObjectList.IdentificationList; 
      ret_code       := READY;
      
    elsif pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration.ID = usListID then
      pListElement^  := pSPIDataObjectList^$t_SPI_DataObjectList.FlashConfiguration; 
      ret_code       := READY;
      
    else
      //Get Right Offset and Page adress for input ID
      for i:= 0 to (pSPIDataObjectList^$t_SPI_DataObjectList.ListLength / sizeof(t_SPI_List)) do 
        if pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i].ID = usListID then
          pListElement^  := pSPIDataObjectList^$t_SPI_DataObjectList.OptionalListElements[i];
          ret_code       := READY;
          exit;
        end_if;
      end_for;
    end_if;
    
  elsif TempRetCode = ERROR then
    ret_code := ERROR;
  else
    ret_code := BUSY;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::GetDeviceID
	VAR_OUTPUT
		output 	: UDINT;
	END_VAR
  
  //Overwrite in derived Classes
  output := HWC_INVALID_DEVICE_ID;

END_FUNCTION


FUNCTION VIRTUAL SdiasBase::GetFWVersion
	VAR_INPUT
		pData 	: ^CHAR;
		Length 	: UDINT;
	END_VAR
  
  //Overwrite in derived Classes
  if pData then
    if Length >= 1 then
      pData^ := 0;    // Set first byte  to 0 termination = invalid string
    end_if;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::GetHWTimeStamp
	VAR_OUTPUT
		HWTimeStamp 	: UINT;
	END_VAR
  VAR
    MyPara   : CmdStruct;
    MyResult : results;
    retval   : iprStates;
  END_VAR
  
  HWTimeStamp := 0;
  
  MyPara.uiCmd := CMD_GET_HW_TIMESTAMP;
  MyPara.aPara[0]$t_GetHwTimestampCmd.udVersion    := 1;
  MyPara.aPara[0]$t_GetHwTimestampCmd.udCoreSelect := 0;
  
  retval := SdiasIn.NewInst(#MyPara, #MyResult);
  if retval = READY then
    if MyResult.uiLng >= sizeof(t_GetHwTimestampResult) then
      HWTimeStamp := MyResult.aData[0]$t_GetHwTimestampResult.uiTimeStamp;
    end_if;  
  end_if;

END_FUNCTION


FUNCTION VIRTUAL GLOBAL SdiasBase::UpdateRtPreOp
#pragma warning(disable:0073);
	VAR_INPUT
		callinfo 	: t_SDIAS_UpdateRTPreOp;
	END_VAR
#pragma warning(default:0073);  
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  
  // Overerload in derived Classes
//  case callinfo of
//  	t_SDIAS_UpdateRTPreOp::SDIAS_RTPREOP_CHECKSYNC:
//  	t_SDIAS_UpdateRTPreOp::SDIAS_RTPREOP_CHECKDATAVALID:
//  end_case;
  
  ret_code := READY;
  
END_FUNCTION
FUNCTION SdiasBase::AddDynRdAccess
	VAR_INPUT
		hOffset 	: HINT;
		uLength 	: UINT;
		bCtrlAcc0 	: BOOL;(* := false *)
		ppData 	: pVoid;
		ppStateByte 	: pVoid;
		ppControlByte 	: pVoid;
		pAccessHandle 	: ^UDINT;
		WaitForAccessHandle 	: UDINT;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR
  
  MyPara.uiCmd := SDIAS_CMD_ADD_DYN_RD_ACCESS;
  MyPara.aPara[0] := this$DINT;
  MyPara.aPara[1] := Place$DINT;
  MyPara.aPara[2] := Required;
  MyPara.aPara[3] := hOffset;
  MyPara.aPara[4] := uLength;
  MyPara.aPara[5] := ppData$DINT;
  MyPara.aPara[6] := ppStateByte$DINT;
  MyPara.aPara[7] := ppControlByte$DINT;
  MyPara.aPara[8] := pAccessHandle$DINT;
  MyPara.aPara[9] := WaitForAccessHandle$DINT;
  MyPara.aPara[10]:= to_dint(bCtrlAcc0);

  ret_code := SdiasIn.NewInst(#MyPara, #MyResult);

  if ret_code <> READY then
    LogValue("@02C9 (SdiasBase::AddDynRdAccess) Failed to add read access for SDIAS module at Place 0x{0}, offset {1}", Place, to_udint(hOffset));
  end_if;

END_FUNCTION


FUNCTION SdiasBase::AddDynWrAccess
	VAR_INPUT
		hOffset 	: HINT;
		uLength 	: UINT;
		bCtrlAcc0 	: BOOL;(* := false *)
		ppData 	: pVoid;
		ppStateByte 	: pVoid;
		ppControlByte 	: pVoid;
		pAccessHandle 	: ^UDINT;
		WaitForAccessHandle 	: UDINT;
	END_VAR
	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR
  
  MyPara.uiCmd := SDIAS_CMD_ADD_DYN_WR_ACCESS;
  MyPara.aPara[0] := this$DINT;
  MyPara.aPara[1] := Place$DINT;
  MyPara.aPara[2] := Required;
  MyPara.aPara[3] := hOffset;
  MyPara.aPara[4] := uLength;
  MyPara.aPara[5] := ppData$DINT;
  MyPara.aPara[6] := ppStateByte$DINT;
  MyPara.aPara[7] := ppControlByte$DINT;
  MyPara.aPara[8] := pAccessHandle$DINT;
  MyPara.aPara[9] := WaitForAccessHandle$DINT;
  MyPara.aPara[10]:= to_dint(bCtrlAcc0);

  ret_code := SdiasIn.NewInst(#MyPara, #MyResult);

  if ret_code <> READY then
    LogValue("@02CA (SdiasBase::AddDynWrAccess) Failed to add write access for SDIAS module at Place 0x{0}, offset {1}", Place, to_udint(hOffset));
  end_if;

END_FUNCTION


FUNCTION SdiasBase::ChangeDynAccess
	VAR_INPUT
		AccessHandle 	: UDINT;
		NewOffset 	: HDINT;
		NewLength 	: UDINT;
	END_VAR
 	VAR_OUTPUT
		ret_code 	: iprStates;
	END_VAR
  VAR
  	MyPara   : CmdStruct;
    MyResult : results;
  END_VAR
  
  MyPara.uiCmd := SDIAS_CMD_CHANGE_DYN_ACCESS;
  MyPara.aPara[0] := AccessHandle$DINT;
  MyPara.aPara[1] := NewOffset$DINT;
  MyPara.aPara[2] := NewLength$DINT;
  ret_code := SdiasIn.NewInst(#MyPara, #MyResult);

  if ret_code <> READY then
    LogValue("@03F6 (SdiasBase::ChangeDynAccess) Failed to change access for SDIAS module at Place 0x{0}", Place);
  end_if;
  
END_FUNCTION


FUNCTION VIRTUAL SdiasBase::SPIWriteAddAccesses
	VAR_OUTPUT
		eModuleInitState 	: iprStates;
	END_VAR
  
  //This method has to be called in the derivated class if the class wants to use this feature

  //Add dynamic write access for write SPI Data
  if AddDynWrAccess(hOffset             := SDIAS_SPI_MASTER_ADRESS_TRANSMIT_BUFFER0
                 , uLength              := sizeof(t_SPI_PDO_DataWrite)
                 , bCtrlAcc0            := 1                                //Control Write Access               
                 , ppData               := #sSPIVars.pWriteData
                 , ppStateByte          := #sSPIVars.sWriteAccess.pStateByte
                 , ppControlByte        := #sSPIVars.sWriteAccess.pControlByte
                 , pAccessHandle        := #sSPIVars.sWriteAccess.udAccessHandle
                 , WaitForAccessHandle  := 0
                 ) <> READY then              
               
    LogError(pMsg:="@02ED (SdiasBase::SPIWriteAddAccesses) Adding dynamic write access for SPI Write functionality failed!");
    eModuleInitState := ERROR;
    return;
  end_if;  
  
  //Add dynamic read access for reading SPI Data
  if AddDynRdAccess(hOffset             := SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER0
                 , uLength              := sizeof(t_SPI_PDO_DataRead)
                 , bCtrlAcc0            := 1                                //Control Read Access
                 , ppData               := #sSPIVars.pReadData
                 , ppStateByte          := #sSPIVars.sReadAccess.pStateByte
                 , ppControlByte        := #sSPIVars.sReadAccess.pControlByte
                 , pAccessHandle        := #sSPIVars.sReadAccess.udAccessHandle
                 , WaitForAccessHandle  := 0
                 ) <> READY then
               
    LogError(pMsg:="@02EE (SdiasBase::SPIWriteAddAccesses) Adding dynamic read access for SPI Write functionality failed!");
    eModuleInitState := ERROR;
    return;
  end_if;  

  //Add dynamic read access for reading status of SPI Master
  if AddRdAccess(hOffset              := SDIAS_SPI_MASTER_ADRESS_STATUS_REGISTER
               , uLength              := 1
               , bCtrlAcc0            := 1                                //Control Read Access
               , ppData               := #sSPIVars.pReadStateData
               , ppStateByte          := #sSPIVars.sReadStateAccess.pStateByte
               , ppControlByte        := #sSPIVars.sReadStateAccess.pControlByte
               , pAccessHandle        := #sSPIVars.sReadStateAccess.udAccessHandle
               , WaitForAccessHandle  := 0
               ) <> READY then
               
    LogError(pMsg:="@02EE (SdiasBase::SPIWriteAddAccesses) Adding dynamic read access for SPI Write functionality failed!");
    eModuleInitState := ERROR;
    return;
  end_if;
  
  //if access are ok
  eModuleInitState := READY;
  
END_FUNCTION


FUNCTION VIRTUAL SdiasBase::SPIWriteGetState
	VAR_OUTPUT
		retcode	: iprStates;
	END_VAR
  
  //initialize return code with busy
  retcode := BUSY;
  
  if sSPIVars.bsFinished.Error then
    //error occured while write spi data
    retcode := ERROR;
  elsif sSPIVars.bsFinished.Finished then
    //write spi has finished
    retcode := READY;
  end_if;

END_FUNCTION


FUNCTION VIRTUAL SdiasBase::WriteSPIData
	VAR_INPUT
		usSectorNumber 	: USINT;
		usPageNumber 	: USINT;
		usPageOffset 	: USINT;
		udLength 	: UDINT;
		pData 	: ^USINT;
		usWriteMode 	: usWriteModes;(* := 0x00 *)
	END_VAR
	VAR_OUTPUT
		retcode 	: DINT;
	END_VAR
  
  retcode := 0;
    
  //Check for valid data
  if pData = nil then
    retcode := -1;
    return;
  end_if;
  
  //Check if last access finished or it was finished with error
  if sSPIVars.eSPIWriteSSW <> _SPIWrite_Idle then
    retcode := -2;
    return;
  end_if;
  
  //Check for valid length
  if udLength = 0 then
    retcode := -3;
    return;
  end_if;
    
  //Check if user write data is within the sector size
  if (((usPageNumber * SDIAS_SPI_PAGE_SIZE) + usPageOffset + udLength) > SDIAS_SPI_SECTOR_SIZE) then
    retcode := -4;
    return;
  end_if;
      
  //only access spi if it is locked
  if sSPIAccess.eSPIAccessProtect = _NotLocked then
    retcode := -5;  
    return;
  end_if;  
  
  sSPIVars.udUserWriteLength  := udLength;
  sSPIVars.usUserSectorNumber := usSectorNumber;
  sSPIVars.usUserPageNumber   := usPageNumber;
  sSPIVars.usUserPageOffset   := usPageOffset;  
  sSPIVars.pUserWriteData     := pData;  
  sSPIVars.usWriteMode        := usWriteMode;
  
  //reset error ssw
  sSPIVars.eSPIWriteSSWError := t_e_SPIWriteSSW::_SPIWrite_Idle;  
  sSPIVars.bsFinished        := 0;  
  
  case sSPIVars.usWriteMode of      
    0,2: 
      //read/write full sector
      sSPIVars.udMemoryToAllocate          := SDIAS_SPI_SECTOR_SIZE;
      sSPIVars.sReadAccess.usAccessLength  := SDIAS_MAX_PDO_SIZE;
      sSPIVars.sWriteAccess.usAccessLength := SDIAS_MAX_PDO_SIZE; 
      sSPIVars.uiCurrentPage               := 0;
      sSPIVars.uiLastPage                  := SDIAS_SPI_PAGE_SIZE;
      sSPIVars.udUserDataOffset            := (sSPIVars.usUserPageNumber * SDIAS_SPI_PAGE_SIZE) + sSPIVars.usUserPageOffset;
    1,3: 
      //read/write only required pages
      sSPIVars.udMemoryToAllocate          := ((sSPIVars.udUserWriteLength+SDIAS_SPI_PAGE_SIZE)/SDIAS_SPI_PAGE_SIZE)*SDIAS_SPI_PAGE_SIZE;
      sSPIVars.sReadAccess.usAccessLength  := SDIAS_MAX_PDO_SIZE/4;
      sSPIVars.sWriteAccess.usAccessLength := SDIAS_MAX_PDO_SIZE/4; 
      sSPIVars.uiCurrentPage               := sSPIVars.usUserPageNumber;
      sSPIVars.uiLastPage                  := sSPIVars.uiCurrentPage + (sSPIVars.udUserWriteLength/SDIAS_SPI_PAGE_SIZE)$UINT + 1;
      sSPIVars.udUserDataOffset            := sSPIVars.usUserPageOffset;
  else
    retcode := -6;  
    return;  
  end_case;
  
  //allocate memory at SPIWriteUpdateCy()
  sSPIVars.eSPIWriteSSW   := t_e_SPIWriteSSW::_AllocateMemory;
  
END_FUNCTION


FUNCTION VIRTUAL SdiasBase::SPIWriteUpdateRt
    

  case sSPIVars.eSPIWriteSSW of
   
//*****************************************************************************
    t_e_SPIWriteSSW::_SPIWrite_Idle:    
      //Wait for start
      //SSW will be set in method SPIWriteStart
    
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
//START REQUEST
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
  	t_e_SPIWriteSSW::_SetRequest:
      
      //Start/Stop data objects (is needed for the next steps)
      if sSPIVars.sWriteAccess.pControlByte then
        sSPIVars.sWriteAccess.pControlByte^.EnableDO := TRUE;
      else
        //error at start dataobject for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
        return;
      end_if;

      if sSPIVars.sReadAccess.pControlByte then
        sSPIVars.sReadAccess.pControlByte^.EnableDO := TRUE;
      else
        //error at start dataobject for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest; 
        return;
      end_if;

      if sSPIVars.sReadStateAccess.pControlByte then
        sSPIVars.sReadStateAccess.pControlByte^.EnableDO := TRUE;
      else
        //error at start dataobject for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
        return;
      end_if;
            
      //Change write DO length
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_REQUEST_REGISTER
                   , NewLength    :=  1
                   );
    
      //Set Software request for SPI Master
      sSPIVars.pWriteData^[0] := SDIAS_SPI_MASTER_SOFTWARE_REQUEST;
      
      //Set length and offset for read object
      ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_GRANT_REGISTER
                   , NewLength    :=  1
                   ); 
             
      sSPIVars.udTimeout    := ops.tAbsolute;
      sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadGrant;
      
//*****************************************************************************
  	t_e_SPIWriteSSW::_ReadGrant:
      
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      //Check for grant
      if sSPIVars.pReadData^[0] = SDIAS_SPI_MASTER_SOFTWARE_REQUEST then
      
        //We got a grant, carry on
        //check if read of sector - page is needed
        case sSPIVars.usWriteMode of      
          0,1: 
            sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadSectorData_SetPageAddress;
          2,3:
            //read of sector / page is not needed -> erase sector
            sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_EraseSector_SetPageAddress;
        end_case;
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadSectorData_SetPageAddress;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //error at getting grant for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;
      
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
//READ SECTOR DATA
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
  	t_e_SPIWriteSSW::_ReadSectorData_SetPageAddress:
   
      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PAGE_ADRESS
                   , NewLength    :=  2
                   );
                   
      //Due to the reason that we have to read the whole sector, just use the sector number from the user write address
      sSPIVars.pWriteData^[0]$UINT := (sSPIVars.usUserSectorNumber SHL 8) + sSPIVars.uiCurrentPage; //Byte 1 .. Pagenumber, Byte 2 .. Sectornumber
      
      sSPIVars.udTimeout    := ops.tAbsolute;
      sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadSectorData_Wait4SetPageAddress;
                   
//*****************************************************************************
  	t_e_SPIWriteSSW::_ReadSectorData_Wait4SetPageAddress:
      
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY  then
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadSectorData_SetReadPageCommand;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //timeout while waiting for spi master state ready
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;

//*****************************************************************************
	  t_e_SPIWriteSSW::_ReadSectorData_SetReadPageCommand:
  
      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PREDEFINED_COMMAND_REGISTER
                   , NewLength    :=  1
                   );    
             
      sSPIVars.sReadAccess.usAccessOffset   := 0;   
      sSPIVars.sWriteAccess.usAccessOffset  := 0;   
      
      sSPIVars.pWriteData^$USINT := SDIAS_SPI_MASTER_COMMAND_PAGE_READ;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER0
                   , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                   );  
      sSPIVars.udTimeout    := ops.tAbsolute;       
      sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadSectorData_Wait4SetReadPageCommand;
                   
//*****************************************************************************
  	t_e_SPIWriteSSW::_ReadSectorData_Wait4SetReadPageCommand:
   
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY  then
        sSPIVars.usCycleWaitCnt := 1;
        sSPIVars.eSPIWriteSSW   := t_e_SPIWriteSSW::_ReadSectorData_ReadReceiveBufferPart1;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //timeout while waiting for spi master state ready
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;
    
//*****************************************************************************
  	t_e_SPIWriteSSW::_ReadSectorData_ReadReceiveBufferPart1:
    
      //Check if the module is connected via VARAN (behind a VI02x), so we have to wait an additional cycle until we can access the data      
      if bIsConnectedViaVARAN then
        //ChangeDynAccess was done in last cycle, so in this cycle the access will be changed and in the next cycle we can access the data
        if sSPIVars.usCycleWaitCnt = 0 then
          sSPIVars.usCycleWaitCnt += 1;
          return;
        end_if;
        sSPIVars.usCycleWaitCnt := 0;  
      end_if;

      //Read the first part of the receive buffer
      //Receive buffer is 256 byte huge but our read DO can only read maximal 128 Byte with one access -> split it into more accesses
            
      //Part 1: Copy Byte 0 to 127 from receive buffer
      _memcpy(ptr1:=sSPIVars.pSectorMemoryRead + sSPIVars.udCurrentSectorMemoryOffset + sSPIVars.sReadAccess.usAccessOffset, ptr2:= sSPIVars.pReadData, cntr:=sSPIVars.sReadAccess.usAccessLength);
      //Also copy it to write buffer
      _memcpy(ptr1:=sSPIVars.pSectorMemoryWrite + sSPIVars.udCurrentSectorMemoryOffset + sSPIVars.sReadAccess.usAccessOffset, ptr2:= sSPIVars.pReadData, cntr:=sSPIVars.sReadAccess.usAccessLength);
      
      sSPIVars.sReadAccess.usAccessOffset += sSPIVars.sReadAccess.usAccessLength;
      
      if sSPIVars.sReadAccess.usAccessOffset < SDIAS_MAX_PDO_SIZE then
      
        ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                     , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER0+sSPIVars.sReadAccess.usAccessOffset
                     , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                     );               
               
      else
        //Increment offset to copy data
        sSPIVars.udCurrentSectorMemoryOffset += sizeof(t_SPI_PDO_DataRead);
        
        ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                     , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER1
                     , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                     );
               
        sSPIVars.sReadAccess.usAccessOffset := 0;
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadSectorData_ReadReceiveBufferPart2;      
      end_if;

       
//*****************************************************************************
  	t_e_SPIWriteSSW::_ReadSectorData_ReadReceiveBufferPart2:

      //Check if the module is connected via VARAN (behind a VI02x), so we have to wait an additional cycle until we can access the data      
      if bIsConnectedViaVARAN then
        //ChangeDynAccess was done in last cycle, so in this cycle the access will be changed and in the next cycle we can access the data
        if sSPIVars.usCycleWaitCnt = 0 then
          sSPIVars.usCycleWaitCnt += 1;
          return;
        end_if;
        sSPIVars.usCycleWaitCnt := 0;  
      end_if;

      //Part 2: Copy Byte 128 to 255 from receive buffer
      _memcpy(ptr1:=sSPIVars.pSectorMemoryRead + sSPIVars.udCurrentSectorMemoryOffset+ sSPIVars.sReadAccess.usAccessOffset, ptr2:= sSPIVars.pReadData, cntr:=sSPIVars.sReadAccess.usAccessLength);
      
      //Also copy it to write buffer
      _memcpy(ptr1:=sSPIVars.pSectorMemoryWrite + sSPIVars.udCurrentSectorMemoryOffset + sSPIVars.sReadAccess.usAccessOffset, ptr2:= sSPIVars.pReadData, cntr:=sSPIVars.sReadAccess.usAccessLength);
      
      
      sSPIVars.sReadAccess.usAccessOffset += sSPIVars.sReadAccess.usAccessLength;
      
      if sSPIVars.sReadAccess.usAccessOffset < SDIAS_MAX_PDO_SIZE then
      
        ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                     , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER1+sSPIVars.sReadAccess.usAccessOffset
                     , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                     );               
               
      else        
        //Increment offset to copy data
        sSPIVars.udCurrentSectorMemoryOffset += sizeof(t_SPI_PDO_DataRead);
        
        //Increment every page we read
        sSPIVars.uiCurrentPage               += 1;
        sSPIVars.sReadAccess.usAccessOffset  := 0;

        //Check if we have read the last page
        if sSPIVars.uiCurrentPage = sSPIVars.uiLastPage then
        
          sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_EraseSector_SetPageAddress;
          
          //Stop ReadDo (is not needed for the next steps, will be enabled again if needed)
          sSPIVars.sReadAccess.pControlByte^.EnableDO := FALSE;          
          
        else
          sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ReadSectorData_SetPageAddress;
        end_if;   
      end_if;
      

//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
//ERASE SECTOR 
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
	t_e_SPIWriteSSW::_EraseSector_SetPageAddress:
  

      //Reset our page/sector counters
      case sSPIVars.usWriteMode of      
        0,2: 
          sSPIVars.uiCurrentPage := 0;
        1,3:
          sSPIVars.uiCurrentPage := sSPIVars.usUserPageNumber;
      end_case;
      sSPIVars.udCurrentSectorMemoryOffset := 0;
      
      //copy user data into memory write buffer        
      _memcpy(ptr1 := sSPIVars.pSectorMemoryWrite + sSPIVars.udUserDataOffset
            , ptr2 := sSPIVars.pUserWriteData
            , cntr := sSPIVars.udUserWriteLength);

      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PAGE_ADRESS
                   , NewLength    :=  2
                   );    
      
      sSPIVars.udTimeout           := ops.tAbsolute;
      sSPIVars.pWriteData^[0]$UINT := (sSPIVars.usUserSectorNumber SHL 8);
      sSPIVars.eSPIWriteSSW        := t_e_SPIWriteSSW::_EraseSector_Wait4SetPageAddress;
  
//*****************************************************************************
	t_e_SPIWriteSSW::_EraseSector_Wait4SetPageAddress:
             
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY  then
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_EraseSector_SetEraseSectorCommand;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //timeout while waiting for spi master state ready
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;

//*****************************************************************************
  	t_e_SPIWriteSSW::_EraseSector_SetEraseSectorCommand:
    
      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PREDEFINED_COMMAND_REGISTER
                   , NewLength    :=  1
                   );    
      
      sSPIVars.udTimeout         := ops.tAbsolute;
      sSPIVars.pWriteData^$USINT := SDIAS_SPI_MASTER_COMMAND_SECTOR_ERASE;
      sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_EraseSector_Wait4SetEraseSectorCommand;
          
//*****************************************************************************
  	t_e_SPIWriteSSW::_EraseSector_Wait4SetEraseSectorCommand:
    
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY  then
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_WriteSectorData_SetPageAddress;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //timeout while waiting for spi master state ready
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;
  
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
//WRITE SECTOR DATA
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
    t_e_SPIWriteSSW::_WriteSectorData_SetPageAddress:

      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      //Wait until last write/erase sector has finished  
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PAGE_ADRESS
                   , NewLength    :=  2
                   );
                   
      //Due to the reason that we have to write the whole sector, just use the sector number from the user write address
      sSPIVars.pWriteData^[0]$UINT := (sSPIVars.usUserSectorNumber SHL 8) + sSPIVars.uiCurrentPage;
      sSPIVars.udTimeout           := ops.tAbsolute;
      sSPIVars.eSPIWriteSSW        := t_e_SPIWriteSSW::_WriteSectorData_Wait4SetPageAddress;

//*****************************************************************************
    t_e_SPIWriteSSW::_WriteSectorData_Wait4SetPageAddress:
      
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY  then
        sSPIVars.sWriteAccess.usAccessOffset := 0;
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_WriteSectorData_WriteTransmitBufferPart1;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //timeout while waiting for spi master state ready
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;
  
//*****************************************************************************
    t_e_SPIWriteSSW::_WriteSectorData_WriteTransmitBufferPart1:
  
      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      //Write whole sector to SPI Flash
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_TRANSMIT_BUFFER0 + sSPIVars.sWriteAccess.usAccessOffset
                   , NewLength    :=  sSPIVars.sWriteAccess.usAccessLength
                   );    
        
      //Copy sector write memory to Dataobject data
      _memcpy(ptr1:= sSPIVars.pWriteData
            , ptr2:= sSPIVars.pSectorMemoryWrite + sSPIVars.udCurrentSectorMemoryOffset + sSPIVars.sWriteAccess.usAccessOffset
            , cntr:= sSPIVars.sWriteAccess.usAccessLength
            );
            
      sSPIVars.sWriteAccess.usAccessOffset += sSPIVars.sWriteAccess.usAccessLength;
      
      if sSPIVars.sWriteAccess.usAccessOffset >= SDIAS_MAX_PDO_SIZE then
        sSPIVars.sWriteAccess.usAccessOffset := 0;
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_WriteSectorData_WriteTransmitBufferPart2;      
      end_if;

//*****************************************************************************
    t_e_SPIWriteSSW::_WriteSectorData_WriteTransmitBufferPart2:
    
      //Write whole sector to SPI Flash
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_TRANSMIT_BUFFER1 + sSPIVars.sWriteAccess.usAccessOffset
                   , NewLength    :=  sSPIVars.sWriteAccess.usAccessLength
                   );    
        
      //Copy sector write memory to Dataobject data
      _memcpy(ptr1:= sSPIVars.pWriteData
            , ptr2:= sSPIVars.pSectorMemoryWrite + sSPIVars.udCurrentSectorMemoryOffset + SDIAS_MAX_PDO_SIZE + sSPIVars.sWriteAccess.usAccessOffset
            , cntr:= sSPIVars.sWriteAccess.usAccessLength
            );
            
      sSPIVars.sWriteAccess.usAccessOffset += sSPIVars.sWriteAccess.usAccessLength;
      
      if sSPIVars.sWriteAccess.usAccessOffset >= SDIAS_MAX_PDO_SIZE then
        sSPIVars.sWriteAccess.usAccessOffset := 0;
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_WriteSectorData_SetWritePageCommand;
      end_if;  

//*****************************************************************************
    t_e_SPIWriteSSW::_WriteSectorData_SetWritePageCommand:
    
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PREDEFINED_COMMAND_REGISTER
                   , NewLength    :=  1
                   );    
      sSPIVars.pWriteData^$USINT := SDIAS_SPI_MASTER_COMMAND_PAGE_PROGRAM;
      
      sSPIVars.udTimeout    := ops.tAbsolute;
      sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_WriteSectorData_Wait4SetWritePageCommand;
      
//*****************************************************************************
    t_e_SPIWriteSSW::_WriteSectorData_Wait4SetWritePageCommand:
            
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY then
          
        //Increment page counter if program was OK
        sSPIVars.uiCurrentPage += 1; 
        //Increment our offset
        sSPIVars.udCurrentSectorMemoryOffset += SDIAS_SPI_PAGE_SIZE;
        //reset retry counter
        sSPIVars.usRetryCnt   := 0;
        
        //Check if finished
        if sSPIVars.uiCurrentPage = sSPIVars.uiLastPage then
          sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_VerifySectorData_SetPageAddress;
          
          //Reset our page/sector counters
          case sSPIVars.usWriteMode of      
            0,2: 
              sSPIVars.uiCurrentPage := 0;
            1,3:
              sSPIVars.uiCurrentPage := sSPIVars.usUserPageNumber;
          end_case;
          
          sSPIVars.udCurrentSectorMemoryOffset := 0;
                                    
        else
          sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_WriteSectorData_SetPageAddress;
        end_if;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //timeout while waiting for spi master state ready
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_WriteSectorData_SetPageAddress;
        sSPIVars.usRetryCnt   += 1;
        if sSPIVars.usRetryCnt > SDIAS_SPI_WRITE_MAX_RETRIES then
          //reset retry counter
          sSPIVars.usRetryCnt        := 0;
          //timeout while waiting for spi master state ready
          sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
          sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
        end_if;
        sSPIVars.NotReadyCnt += 1;
      end_if;
  
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
//VERIFY SECTOR DATA
//**********************************************************************************************************************************************************
//**********************************************************************************************************************************************************
    t_e_SPIWriteSSW::_VerifySectorData_SetPageAddress:

      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PAGE_ADRESS
                   , NewLength    :=  2
                   );
                   
      //Due to the reason that we have to read the whole sector, just use the sector number from the user write address
      sSPIVars.pWriteData^[0]$UINT := (sSPIVars.usUserSectorNumber SHL 8) + sSPIVars.uiCurrentPage;
      sSPIVars.udTimeout    := ops.tAbsolute;
      sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_VerifySectorData_Wait4SetPageAddress;

//*****************************************************************************
    t_e_SPIWriteSSW::_VerifySectorData_Wait4SetPageAddress:

      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY  then
        sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_VerifySectorData_SetReadPageCommand;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //error at getting grant for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;
  
//*****************************************************************************
    t_e_SPIWriteSSW::_VerifySectorData_SetReadPageCommand:
    
      //Start write object (is needed for the next steps)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := TRUE;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_PREDEFINED_COMMAND_REGISTER
                   , NewLength    :=  1
                   );    
             
      sSPIVars.pWriteData^$USINT := SDIAS_SPI_MASTER_COMMAND_PAGE_READ;
      
      //Start ReadDo (is needed for the next steps)
      sSPIVars.sReadAccess.pControlByte^.EnableDO := TRUE;
      
      ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER0
                   , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                   );                
      
      sSPIVars.udTimeout    := ops.tAbsolute;
      sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_VerifySectorData_Wait4SetReadPageCommand;

//*****************************************************************************
    t_e_SPIWriteSSW::_VerifySectorData_Wait4SetReadPageCommand:
  
      //Stop write object (is not needed for the next steps, will be enabled again if needed)
      sSPIVars.sWriteAccess.pControlByte^.EnableDO  := FALSE;
      
      if sSPIVars.pReadStateData^$USINT = SDIAS_SPI_MASTER_STATE_READY  then
        sSPIVars.sReadAccess.usAccessOffset := 0;
        sSPIVars.usCycleWaitCnt := 1;
        sSPIVars.eSPIWriteSSW   := t_e_SPIWriteSSW::_VerifySectorData_ReadReceiveBufferPart1;
      elsif (ops.tAbsolute-sSPIVars.udTimeout) >= SDIAS_SPI_TIMEOUT then
        //error at getting grant for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
      end_if;
      
//*****************************************************************************
  	t_e_SPIWriteSSW::_VerifySectorData_ReadReceiveBufferPart1:

      //Check if the module is connected via VARAN (behind a VI02x), so we have to wait an additional cycle until we can access the data      
      if bIsConnectedViaVARAN then
        //ChangeDynAccess was done in last cycle, so in this cycle the access will be changed and in the next cycle we can access the data
        if sSPIVars.usCycleWaitCnt = 0 then
          sSPIVars.usCycleWaitCnt += 1;
          return;
        end_if;
        sSPIVars.usCycleWaitCnt := 0;  
      end_if; 
      
      //Read the first part of the receive buffer
      //Receive buffer is 256 byte huge but our read DO can only read 128 Byte with one access -> split it into two accesses
            
      //Part 1: Copy Byte 0 to 127 from receive buffer
      _memcpy(ptr1:=sSPIVars.pSectorMemoryVerify + sSPIVars.udCurrentSectorMemoryOffset + sSPIVars.sReadAccess.usAccessOffset, ptr2:= sSPIVars.pReadData, cntr:=sSPIVars.sReadAccess.usAccessLength);
     
      sSPIVars.sReadAccess.usAccessOffset += sSPIVars.sReadAccess.usAccessLength;
      
      if sSPIVars.sReadAccess.usAccessOffset < SDIAS_MAX_PDO_SIZE then
      
        ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                     , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER0+sSPIVars.sReadAccess.usAccessOffset
                     , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                     );               
               
      else
        //Increment offset to copy data
        sSPIVars.udCurrentSectorMemoryOffset += sizeof(t_SPI_PDO_DataRead);
        
        ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                     , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER1
                     , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                     );       
        sSPIVars.sReadAccess.usAccessOffset := 0;
        sSPIVars.usCycleWaitCnt := 0;
        sSPIVars.eSPIWriteSSW   := t_e_SPIWriteSSW::_VerifySectorData_ReadReceiveBufferPart2;    
      end_if;      
       
//*****************************************************************************
  	t_e_SPIWriteSSW::_VerifySectorData_ReadReceiveBufferPart2:
       
      //Check if the module is connected via VARAN (behind a VI02x), so we have to wait an additional cycle until we can access the data      
      if bIsConnectedViaVARAN then
        //ChangeDynAccess was done in last cycle, so in this cycle the access will be changed and in the next cycle we can access the data
        if sSPIVars.usCycleWaitCnt = 0 then
          sSPIVars.usCycleWaitCnt += 1;
          return;
        end_if;
        sSPIVars.usCycleWaitCnt := 0;  
      end_if;
             
      //Part 2: Copy Byte 128 to 255 from receive buffer
      _memcpy(ptr1:=sSPIVars.pSectorMemoryVerify + sSPIVars.udCurrentSectorMemoryOffset+ sSPIVars.sReadAccess.usAccessOffset, ptr2:= sSPIVars.pReadData, cntr:=sSPIVars.sReadAccess.usAccessLength);
      
      
      sSPIVars.sReadAccess.usAccessOffset += sSPIVars.sReadAccess.usAccessLength;
      
      if sSPIVars.sReadAccess.usAccessOffset < SDIAS_MAX_PDO_SIZE then
      
        ChangeDynAccess(AccessHandle :=  sSPIVars.sReadAccess.udAccessHandle
                     , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_RECEIVE_BUFFER1+sSPIVars.sReadAccess.usAccessOffset
                     , NewLength    :=  sSPIVars.sReadAccess.usAccessLength
                     );               
               
      else        
        //next step at SPIWriteUpdateCy() 
        sSPIVars.eSPIWriteSSW := _VerifySectorData_CompareData;
      end_if;      
        
//*****************************************************************************
    t_e_SPIWriteSSW::_VerifySectorData_CompareData:
    //at SPIWriteUpdateCy() 
    
//**********************************************************************************************************************************************************
//FINISH SPI WRITE
//**********************************************************************************************************************************************************
  	t_e_SPIWriteSSW::_ClearRequest:
    
      //Start write object (is needed for the next steps)
      if sSPIVars.sWriteAccess.pControlByte then
        sSPIVars.sWriteAccess.pControlByte^.EnableDO := TRUE;
      else
        //error at start dataobject for spi access
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_DisableDataObjects;
        return;
      end_if;
      
      //Change write DO length
      ChangeDynAccess(AccessHandle :=  sSPIVars.sWriteAccess.udAccessHandle
                   , NewOffset    :=  SDIAS_SPI_MASTER_ADRESS_CLEAR_REQUEST_REGISTER
                   , NewLength    :=  1
                   );
    
      //Clear Software request for SPI Master
      sSPIVars.pWriteData^[0] := SDIAS_SPI_MASTER_SOFTWARE_REQUEST;
      
      sSPIVars.eSPIWriteSSW := _DisableDataObjects;      
      
//*****************************************************************************
  	t_e_SPIWriteSSW::_DisableDataObjects:    
      //Disable Data objects because they are not used anymore (will be enabled with next start of SPI write access)
      if sSPIVars.sWriteAccess.pControlByte then
        sSPIVars.sWriteAccess.pControlByte^.EnableDO := FALSE;
      end_if;
      if sSPIVars.sReadAccess.pControlByte then
        sSPIVars.sReadAccess.pControlByte^.EnableDO := FALSE;
      end_if;
      if sSPIVars.sReadStateAccess.pControlByte then
        sSPIVars.sReadStateAccess.pControlByte^.EnableDO := FALSE;
      end_if;
      //free memory at SPIWriteUpdateCy()
      sSPIVars.eSPIWriteSSW := _FreeMemory;      
      
//*****************************************************************************
  	t_e_SPIWriteSSW::_FinishSPIWrite:
      
      //Reset SSW
      sSPIVars.eSPIWriteSSW := _SPIWrite_Idle;        
      //spi write has finished
      sSPIVars.bsFinished.Finished := 1;
      //check if an error occured while spi write
      if sSPIVars.eSPIWriteSSWError <> sSPIVars.eSPIWriteSSW then
        //error occurred while spi access - act error step is saved at sSPIVars.eSPIWriteSSWError 
        sSPIVars.bsFinished.Error := 1;
      end_if;


//*****************************************************************************
  end_case;
  
END_FUNCTION


FUNCTION VIRTUAL SdiasBase::SPIWriteUpdateCy


  case sSPIVars.eSPIWriteSSW of
//*****************************************************************************
    t_e_SPIWriteSSW::_SPIWrite_Idle:    
      //Wait for start
      //SSW will be set in method WriteSPIData()     
    
//**********************************************************************************************************************************************************
  	t_e_SPIWriteSSW::_AllocateMemory:
    
      //check pointer, if it's not nil it must be freed
      if sSPIVars.pSectorMemoryRead then
        ToStdLib.Free(mptr:=sSPIVars.pSectorMemoryRead);
        sSPIVars.pSectorMemoryRead := NIL;
      end_if;      
      //Allocate memory for read sector (1 sector has 64kByte)
      sSPIVars.pSectorMemoryRead$^void    := ToStdLib.Malloc(size:=sSPIVars.udMemoryToAllocate);      
      if sSPIVars.pSectorMemoryRead = NIL then
        LogError(pMsg:="@02EF (SdiasBase::SPIWriteUpdateCy) Failed to allocate memory for sSPIVars.pSectorMemoryRead.");
        //error at allocate memory for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;      
        //next step at SPIWriteUpdateRt()
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
        return;
      end_if;

      //check pointer, if it's not nil it must be freed
      if sSPIVars.pSectorMemoryWrite then
        ToStdLib.Free(mptr:=sSPIVars.pSectorMemoryWrite);
        sSPIVars.pSectorMemoryWrite := NIL;
      end_if;
      //Allocate memory for write sector (1 sector has 64kByte)
      sSPIVars.pSectorMemoryWrite$^void   := ToStdLib.Malloc(size:=sSPIVars.udMemoryToAllocate);      
      if sSPIVars.pSectorMemoryWrite = NIL then
        LogError(pMsg:="@02F0 (SdiasBase::SPIWriteUpdateCy) Failed to allocate memory for sSPIVars.pSectorMemoryWrite.");
        //error at allocate memory for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;     
        //next step at SPIWriteUpdateRt()
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
        return;
      end_if;
      
      //check pointer, if it's not nil it must be freed
      if sSPIVars.pSectorMemoryVerify then
        ToStdLib.Free(mptr:=sSPIVars.pSectorMemoryVerify);
        sSPIVars.pSectorMemoryVerify := NIL;
      end_if;
      //Allocate memory for verify sector (1 sector has 64kByte)
      sSPIVars.pSectorMemoryVerify$^void  := ToStdLib.Malloc(size:=sSPIVars.udMemoryToAllocate);

      if sSPIVars.pSectorMemoryVerify = NIL then
        LogError(pMsg:="@02F1 (SdiasBase::SPIWriteUpdateCy) Failed to allocate memory for sSPIVars.pSectorMemoryVerify.");  
        //error at allocate memory for spi access
        sSPIVars.eSPIWriteSSWError := sSPIVars.eSPIWriteSSW;     
        //next step at SPIWriteUpdateRt()
        sSPIVars.eSPIWriteSSW      := t_e_SPIWriteSSW::_ClearRequest;
        return;
      end_if;

      //Clear the memory
      _memset(sSPIVars.pSectorMemoryRead,   16#FF, sSPIVars.udMemoryToAllocate);
      _memset(sSPIVars.pSectorMemoryWrite,  16#FF, sSPIVars.udMemoryToAllocate);
      _memset(sSPIVars.pSectorMemoryVerify, 16#FF, sSPIVars.udMemoryToAllocate);
      
      //next step at SPIWriteUpdateRt()
      sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_SetRequest;    
    
//**********************************************************************************************************************************************************
//other steps are at SPIWriteUpdateRt()
//**********************************************************************************************************************************************************
        
//*****************************************************************************
    t_e_SPIWriteSSW::_VerifySectorData_CompareData:
      
      //Check if data is the same
      if _memcmp(ptr1:= sSPIVars.pSectorMemoryVerify + sSPIVars.udCurrentSectorMemoryOffset - sizeof(t_SPI_PDO_DataRead), 
                 ptr2:= sSPIVars.pSectorMemoryWrite + sSPIVars.udCurrentSectorMemoryOffset - sizeof(t_SPI_PDO_DataRead), 
                 cntr:=SDIAS_SPI_PAGE_SIZE) <> 0 then
              
        //Error        
        sSPIVars.usRetryCnt += 1;
        
        //Reset our page/sector counters
        case sSPIVars.usWriteMode of      
          0,2: 
            sSPIVars.uiCurrentPage := 0;
          1,3:
            sSPIVars.uiCurrentPage := sSPIVars.usUserPageNumber;
        end_case;
        sSPIVars.udCurrentSectorMemoryOffset := 0;
        
        if sSPIVars.usRetryCnt > SDIAS_SPI_WRITE_MAX_RETRIES then
          sSPIVars.usRetryCnt   :=  0;
          //next step at SPIWriteUpdateRt() 
          sSPIVars.eSPIWriteSSW := _ClearRequest;                
        else
          sSPIVars.bsFinished   := 0;  
          //next step at SPIWriteUpdateRt()       
          sSPIVars.eSPIWriteSSW := _ReadSectorData_SetPageAddress;        
        end_if;
      else 
        //Increment offset to copy data
        sSPIVars.udCurrentSectorMemoryOffset += sizeof(t_SPI_PDO_DataRead);
        
        //Increment every page we read
        sSPIVars.uiCurrentPage               += 1;         

        //Check if we have read the last page
        if sSPIVars.uiCurrentPage = sSPIVars.uiLastPage then        
          //Data is equal, finish SPI communication
          //next step at SPIWriteUpdateRt() 
          sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_ClearRequest;          
        else
          //next step at SPIWriteUpdateRt() 
          sSPIVars.eSPIWriteSSW := t_e_SPIWriteSSW::_VerifySectorData_SetPageAddress;
        end_if;  
      end_if;
    
//**********************************************************************************************************************************************************
  	t_e_SPIWriteSSW::_FreeMemory:
      //Free memory
      if sSPIVars.pSectorMemoryRead then
        ToStdLib.Free(mptr:=sSPIVars.pSectorMemoryRead); 
        sSPIVars.pSectorMemoryRead := NIL;   
      end_if;
      if sSPIVars.pSectorMemoryWrite then
        ToStdLib.Free(mptr:=sSPIVars.pSectorMemoryWrite); 
        sSPIVars.pSectorMemoryWrite := NIL;   
      end_if;
      if sSPIVars.pSectorMemoryVerify then
        ToStdLib.Free(mptr:=sSPIVars.pSectorMemoryVerify);
        sSPIVars.pSectorMemoryVerify := NIL;    
      end_if;
      
      sSPIVars.uiCurrentPage                := 0;
      sSPIVars.udCurrentSectorMemoryOffset  := 0;
       //next step at SPIWriteUpdateRt() 
      sSPIVars.eSPIWriteSSW := _FinishSPIWrite;   
      
    
//**********************************************************************************************************************************************************
  	t_e_SPIWriteSSW::_FinishSPIWrite:
    //at SPIWriteUpdateRt()
    
  end_case;
    

END_FUNCTION


FUNCTION SdiasBase::LockSPIAccess
	VAR_INPUT
		udAccessID 	: UDINT;
	END_VAR
	VAR_OUTPUT
		dRetCode 	: iprStates;
	END_VAR
  VAR
  	udTempVar : UDINT;
  END_VAR  
  
  udTempVar := sigclib_atomic_cmpxchgU32(pValue:=#sSPIAccess.eSPIAccessProtect$UDINT, cmpVal:=_NotLocked, newVal:=udAccessID);  

  if (udTempVar = 0) | (udTempVar = udAccessID) then
    //access to spi interface is granted
    sSPIAccess.udSPILastLockTime := ops.tAbsolute;
    dRetCode := READY;  
  else
    //spi access is locked by another instance
    dRetCode := BUSY;
    if (ops.tAbsolute-sSPIAccess.udSPILastLockTime) > (SDIAS_SPI_LOCK_TIMEOUT*udBusTime*SDIAS_MAX_PDO_SIZE/(1000*sSPIVars.sWriteAccess.usAccessLength)) then
      //reset all accesses
      sSPIAccess.eSPIAccessProtect := _NotLocked;      
      //reset spi SSW 
      eGetSPIDataSSW := _GetDataObjectList;
      eGetObjectListSSW := _GetObjList;
    end_if;
  end_if;  
  
END_FUNCTION


FUNCTION SdiasBase::UnlockSPIAccess
  VAR_INPUT
    udAccessID 	: UDINT;
  END_VAR
  VAR_OUTPUT
    dRetCode 	: iprStates;
  END_VAR
  
  //reset lock bit if compare value is ok
  sigclib_atomic_cmpxchgU32(pValue:=#sSPIAccess.eSPIAccessProtect$UDINT, cmpVal:=udAccessID, newVal:=_NotLocked);
  dRetCode := READY;

END_FUNCTION