/*
Copyright by 3S Smart Software Solutions GmbH, 87439 Kempten, Germany
Author: A. Fauter
*/

#define MY_IODRV_NAME "RTIOdrv"

//structure to pass information to the callback-function.
typedef struct _tagCallbackInfo
{
		//the security-members are made to check, whether the caller is a legal caller to the driver.
	unsigned long ulSecurityId1; // this member must be set to 0x436F4465 means CoDe
	unsigned long ulSecurityId2; // this member must be set to 0x53797320 means Sys<space>

	unsigned long ulCommand;
	unsigned long ulSubCommand;

	unsigned long ulCmdParam;
	void* pAdditionalCmdData; //may point to a caller-supplied, command-specific structure.
}CallbackInfo;
unsigned long ntCallback(CallbackInfo* pcbi, void* pPar);


//let's say 5 devices are enough for a driver.
#define DRV_MAX_DEVICES 5
//let's further say 5 interrupt-service-routines (or different service-contexts) are enough for one driver.
#define MAX_DRV_INTERRUPT_ROUTINES 5

/* Functions for Init/Exit the device. */
typedef unsigned long (*PF_INIT)(unsigned long ulPar);
typedef unsigned long (*PF_EXIT)(void);

/* Function for start/stop communication */
typedef unsigned long (*PF_START_DEV)(void);
typedef unsigned long (*PF_STOP_DEV)(void);

/* Functions for accessing drivers configuration data. */
typedef unsigned long (*PF_GET_ID)(void* pData);
typedef unsigned long (*PF_GET_FLAGS)(unsigned long ulDeviceNr);
typedef void (*PF_GET_IORANGE)(unsigned long* pulOffset, unsigned long* pulSize);
	/* The Offset will normally be returned zero. The driver does not know, where his IOs are in the processimage.*/
	/* A driver only knows about the size of his IOs.*/

/* Functions for configuration of connected modules and the device itself.*/
typedef char (*PF_START_CONFIGURE_IONET)(unsigned long ulDeviceNr);
typedef char (*PF_DONE_CONFIGURE_IONET)(unsigned long ulDeviceNr);
typedef char (*PF_CONFIGURE_MODULE)(int iModule, void* pModConfData, unsigned long ulDeviceNr);
typedef char (*PF_CONFIGURE_DEVICE)(void* pBusDesc, unsigned long ulDeviceNr);

/* Functions for IOupdate.*/
typedef char (*PF_START_WRITE_OUTPUTS)(unsigned long ulTaskNr, unsigned long ulCycleTime, int iDevice);
typedef char (*PF_WRITE_OUTPUTS)(unsigned long ulOffset, unsigned long ulSize, unsigned char* pucData, unsigned long ulTaskNr, int iDevice);
typedef char (*PF_DONE_WRITE_OUTPUTS)(unsigned long ulTaskNr, unsigned long ulCycleTime, int iDevice);
typedef char (*PF_WRITE_MODULE_OUTPUTS)(int iModule, unsigned char* pucData, int iDevice);
typedef char (*PF_START_READ_INPUTS)(unsigned long ulTaskNr, unsigned long ulCycleTime, int iDevice);
typedef char (*PF_READ_INPUTS)(unsigned long ulOffset, unsigned long ulSize, unsigned char* pucData,  unsigned long ulTaskNr, int iDevice);
typedef char (*PF_DONE_READ_INPUTS)(unsigned long ulTaskNr, unsigned long ulCycleTime, int iDevice);
typedef char (*PF_READ_MODULE_INPUTS)(int iModule, unsigned char* pucData, int iDevice);
typedef char (*PF_INITIATE_BUS_CYCLE)(int iDevice);
typedef char (*PF_PLC_STATUS_CHANGES)(char cNewState);

/*Functions for IOdrivers, which are actually handlers for hardware-specific things.*/
typedef unsigned long (*PF_ENTER_NMI_ROUTINE)(unsigned long ulPar);
typedef void (*PF_LEAVE_NMI_ROUTINE)(void);
typedef int (*PF_START_INTERRUPT)(int iPar);
typedef int (*PF_STOP_INTERRUPT)(int iPar);

/*Functions for handling evtl. retain-data on an IOdevice.*/
typedef unsigned long (*PF_RETAIN_SAVE)(unsigned short sSegment, unsigned char *pbyData, unsigned long ulSize, int iDevice);
typedef unsigned long (*PF_RETAIN_RESTORE)(unsigned short sSegment, unsigned char *pbyData, unsigned long ulSize, int iDevice);

/*Function-type for handling interupts*/
typedef BOOLEAN (*PF_INTERRUPT_ROUTINE)(PKINTERRUPT pvIntVec, PVOID pContext);

/*Function to get a cyclic call in an IOdrv.*/
typedef unsigned long (*PF_CYCLIC_CALL)(__int64* liCurTime);

/*Function to get an external referenz table from the IODrv*/
typedef void* (*PF_GET_EXTREFTABLE)(void);

/*Functions from Cst-interface of the PLC.*/
typedef unsigned long (*PF_PRECOMPUTE_SERVICE)(unsigned char* pbyReceive, unsigned long ulRecvLen, unsigned char* pbySend);

typedef unsigned long (*PF_POSTCOMPUTE_SERVICE)(unsigned char* pbyReceive, unsigned long ulRecvLen, unsigned char* pbySend, unsigned long ulSendLen);

/*TargetID is IODrv-dependent!*/
typedef char (PF_GETTARGETIDS)(unsigned long *pulIOTrgId, unsigned long *pulIOHookId);


/* Subcommands for PLC-Browser-Commands */
#define PLC_SUB_COMMAND			0x0000
#define PLC_SUB_SENDNEXT		0x0001
#define PLC_SUB_ABBORT			0x0002
#define PLC_SUB_FOLLOWINGS		0x0003
#define PLC_SUB_LAST			0x0004
/* Hook to implement own PLC-Browser commands */
/* define your own command IDs here: starting with 0x50 !! */
#define BROWSERCMD_NOCMD		0x00
#define BROWSERCMD_USERCMD0		0x50
#define BROWSERCMD_USERTEST		0x51
unsigned int (*PF_SRV_BROWSERCMD)(char* pszCommand, char* pszAnswer, unsigned int iSubCommand, unsigned int iBlockNr, char* pcActiveBrowserCommand);

unsigned int (*PF_ADD_BROWSERHELP)(char* pszAnswer, unsigned short usCurNr);

unsigned long (*PF_CST_PROCESSHOOK)(unsigned long ulHook, unsigned long ulAdditionalInfo);
/*Defines for processhook(only the ones mentioned here are implemented):*/
#define PH_BEFORE_RESET					5
/*	Is called before reset.
	ulAdditionalInfo: reset type (RTS_RESET_COLD, RTS_RESET_HARD, RTS_RESET_WARM) */

#define PH_AFTER_RESET					6
/*	Is called after reset,
	ulAdditionalInfo: reset type (RTS_RESET_COLD, RTS_RESET_HARD, RTS_RESET_WARM) */

#define PH_AFTER_PROGRAM_DOWNLOAD		4
/*	Is called after program dowenload:
	ulAdditionalInfo:  1 = After Download, 0 = After Online-Change */

typedef struct tagExtRef
{
	char	szName[32]			ALIGNATTRIB;
	void	(*pFunction)()		ALIGNATTRIB;
}ExtRef;


//following types are for callbacks from the driver into the runtime-api (taken from RtsApi.h).
typedef void			(*PFRTSSTOPALLTASKS)(void);
typedef void			(*PFRTSSTARTALLTASKS)(void);
typedef unsigned char	(*PFRTSGETPLCSTATE)(void);
typedef unsigned char	(*PFRTSPROGRAMLOADED)(void);
typedef unsigned char*	(*PFRTSIOGETINPUTMEMORY)(void);
typedef unsigned char*	(*PFRTSIOGETOUTPUTMEMORY)(void);
typedef unsigned long	(*PFRTSIOGETINPUTSIZE)(void);
typedef unsigned long	(*PFRTSIOGETOUTPUTSIZE)(void);
typedef void            (*PFRTSPROGRAMRESET)(unsigned char byResetMode);
typedef unsigned long   (*PFDATAMANIPULATION)(unsigned long ulMode, unsigned long ulPar);

/*The following modes are valid for the generic call to datamanipulation:*/
/*The returnvalue with DTAMAN_MODE_GETLASTERROR will be a pointer to an RTS_LAST_ERROR-structure.*/
#define DTAMAN_MODE_GETLASTERROR    (3UL)
#define DTMAN_MODE_MAP_KERNELMEMORY_TO_USERSPACE (4UL)
#define DTMAN_MODE_GETSYSTEMTIMEFROMBOOT (5UL)
#define DTMAN_MODE_GETAPICTICKCORRECTION	(6UL)
#define DTMAN_MODE_GETRETAINADDRESS		(7UL)
#define DTMAN_MODE_GETADDRESS	(8UL)
#define DTMAN_MODE_GETSEGSIZE	(9UL)
#define DTMAN_MODE_GETIDENTITY	(10UL)


//If caller wants to map memory, he passes a pointer to the following structure in ulPar:
#pragma pack(1)
typedef struct _tagKernelMapInfo
{
	void* pKernel;
	unsigned long ulSize;
	unsigned long ulPHandle;
	int iSuccess; //Output of the function: 0 is success, all other values are errors.
}KernelMapInfo;

/* The following lines ensure that the stringlength is a multiple of 4
   for natural alignment! */
#define LZS_TEXT_SIZE	80
#define LZS_MAX_TEXT_SIZE	((LZS_TEXT_SIZE>>2)<<2)
typedef struct
{
	unsigned long ulError ;
	unsigned long ulTimeStamp ;
	unsigned long ulParameter ;
	char szText[LZS_MAX_TEXT_SIZE] ;
} RTS_LAST_ERROR;

/*structure for extended task-create-options, usable by IODrivers (extern created tasks). Also defined in IODrvInterface.h (toolkit)*/
typedef struct _tagExtTaskCreateInfo
{
    void* pArg;
    unsigned long ulStackSize;
}ExtTaskCreateInfo;
#pragma pack()

typedef struct tagProjectInfo
{
	unsigned long	dwDate			ALIGNATTRIB;
	char*			pszProject		ALIGNATTRIB;
	char*			pszTitle		ALIGNATTRIB;
	char*			pszVersion		ALIGNATTRIB;
	char*			pszAuthor		ALIGNATTRIB;
	char*			pszDescription	ALIGNATTRIB;
}ProjectInfo;

typedef ProjectInfo* (*PFRTSGETPROJECTINFO)(void);

//definitions for task-flags, which are made to describe some taskstates.
#define TSK_APIFLAG_TASKISSUSPENDED         0x8
typedef int (*PFRTSCREATETASK)(int iPriority, unsigned long ulCycleTime, int (*pfEntry)(void*), void* pArg, unsigned long ulFlags);
typedef int (*PFRTSDELETETASK)(int iPriority);
#define WAIT_INFINITE (0xffffffff)
typedef int (*PFRTSWAITFOROBJECT)(int iWaitObjectNr, unsigned long ulMaxTime);
typedef int (*PFRTSSETOBJECT)(int iWaitObjectNr);
typedef int (*PFRTSSLEEP)(unsigned long ulSleepTime);
typedef int (*PFRTSRESUME)(int iPriority);
typedef int (*PFRTSSUSPEND)(int iPriority);

/*File-access from realtimetasks (asynchronuous)*/
#define OF_ATTRIB_READ 0x1UL
#define OF_ATTRIB_WRITE 0x2UL
#define OF_ATTRIB_APPEND 0x4UL

/*Resultcodes for asynchronuous jobs.*/
typedef enum _FCTJOB_ResultCode
{
	NoResult = 0,
	NotRegistered,
	Processing,
	Done,
	DoneWithError1,
	DoneWithError2,
    DoneWithError3
}FCTJOB_ResultCode;
typedef int (*PFASYNCHFILEOPEN)(void** pHandle,unsigned long ulAttributes,char* pszName);
typedef int (*PFASYNCHFILECLOSE)(void* pHandle);
typedef int (*PFASYNCHFILEREAD)(void* pHandle, char* pBuffer, unsigned long ulSize);
typedef int (*PFASYNCHFILEWRITE)(void* pHandle, char* pBuffer, unsigned long ulSize);

typedef union EventValuetag
{
	char			b;
	unsigned char	uc;
	short			s;
	unsigned short	us;
	long			l;
	unsigned long	ul;
	float			f;
	double			d;
}EventValue;

#pragma pack(1)

typedef struct tagTRACE_VAR
{
    unsigned char	*pbyVarAddress	ALIGNATTRIB;
    unsigned long	ulOffset		ALIGNATTRIB;
    unsigned long	ulSize			ALIGNATTRIB;
}TRACE_VAR;

typedef struct tagTRACE_DEF
{
    unsigned short	wRising					ALIGNATTRIB;
    unsigned short	wNumofVar				ALIGNATTRIB;
    unsigned short	wNumofScan				ALIGNATTRIB;
    unsigned short	wScanSize				ALIGNATTRIB;
    unsigned short	wScanTime				ALIGNATTRIB;
    unsigned short	wAfterEvent				ALIGNATTRIB;
    TRACE_VAR		TraceEvent				ALIGNATTRIB;
    TRACE_VAR		TraceVar[20]	        ALIGNATTRIB;
	unsigned short	wEventType				ALIGNATTRIB;
	double			dTriggerLevel			ALIGNATTRIB;
	EventValue		evLevel					ALIGNATTRIB;
	int     	tcEvent						ALIGNATTRIB;//enum!
	unsigned long	ulBufferSize			ALIGNATTRIB;
}TRACE_DEF;

typedef struct TRACE_DATATag
{
	char			bActive			ALIGNATTRIB;
	TRACE_DEF		Definition		ALIGNATTRIB;
	unsigned char*	pbyBuffer		ALIGNATTRIB;
	char			bEvent			ALIGNATTRIB;
	char			bNoEvent		ALIGNATTRIB;
	unsigned long	ulBufferIndex	ALIGNATTRIB;
	short			iNumAfterEvent	ALIGNATTRIB;
	unsigned long	dwLastTime		ALIGNATTRIB;
	EventValue		evOld			ALIGNATTRIB;
	unsigned short	wNumofScans		ALIGNATTRIB;
} TRACE_DATA;

#define MAX_BLOCKSIZE 220
typedef struct _tagDataBlockHeader
{
    unsigned long ulAddrSrc;
    unsigned long ulAddrDst;
    unsigned short wMemberSrc;
    unsigned short wMemberDst;
    unsigned long ulBlockNr;
    unsigned long ulTotalBlocks;
    unsigned short wServiceId;
    unsigned short wBlockLen;
    /*This is for managing the communication-buffer-blocks.*/
    unsigned short wComBufferNr;
    unsigned short wTotalComBuffers;
    /*End managing communcationbufferblocks.*/
    unsigned short wCRC; // Checksum over ulAddrSrc to wBlockLen, calculated in 2byte-windows.
}DataBlockHeader;

typedef struct _tagDataBlock
{
    DataBlockHeader bh;
    char acData[MAX_BLOCKSIZE - sizeof(DataBlockHeader)];
}DataBlock;

#pragma pack()

/********** Server specific **********/

/********** Types **********/

#pragma pack(1)

typedef struct MONITORING_DATATag
{
	char*	pcBuffer	ALIGNATTRIB;
	char	bDefined	ALIGNATTRIB;
} MONITORING_DATA;

typedef struct FORCE_DATATag
{
	char*	pcBuffer	ALIGNATTRIB;	/* List with force variables and the values  */
	char	bDefined	ALIGNATTRIB;	/* Forceing activated */
	char	bExtended	ALIGNATTRIB;	/* Use extended variable definition (32 bit offset) */
} FORCE_DATA;

typedef struct _tagINTERRUPT_DESCext
{
    /*If the driver connects via NT (IOConnectInterrupt), it stores the IntVec received from NT here, to be called with the 
    right parameters in case of a bluescreen. Attention: Any IO-driver that uses HW-interrupts must call
    KeRegisterBugCheckCallback, or in case of a bluescreen the system will hang if there's an interrupt for this driver.*/
    PKINTERRUPT pvIntVec;
    PVOID   pContext;
    int iIntVec;
    /*If bConnectNow is true, the interrupt will be connected at once by the runtime to the specified driver-routine.
    But the driver itself must make sure, the interrupt is enabled. This is normally done by the OS.
    If bConnectNow is false, the interrupt will be connected in case of a bluescreen, to make it possible for the 
    IO-driver to work after a crash. NT must not be called in this case.*/
    char bConnectNow;

}INTERRUPT_DESC;

#define RIM_REQTYPE_NO_REQUEST                  (0x0UL)
#define RIM_REQTYPE_GETIDTINDEX_OF_HWVECTOR     (0x1UL)
#define RIM_REQTYPE_GETINTERRUPTMASK            (0x2UL)
typedef struct _RIM_Additional_Info
{
    unsigned long ulRequestType;
    unsigned long ulInput;
    unsigned long ulRetVal;
}RIM_Additional_Info;

typedef struct _GetIDdata
{
    int iNumDevices;
    int iNumInterruptRoutines;
    INTERRUPT_DESC idIntDesc[MAX_DRV_INTERRUPT_ROUTINES];
    PF_INTERRUPT_ROUTINE apfIntRoutine[MAX_DRV_INTERRUPT_ROUTINES];
	unsigned long ulAdditional1; //Here the kernel passes the value for g_ulMicroSecClocks, the number of CPUclockticks per us.
}GetIDdata;

#define MAX_APPLTYPES					5
typedef struct APPL_DATAtag
{
	/* Global client parameters */
	char			bValid			ALIGNATTRIB;
	unsigned long	ulApplId		ALIGNATTRIB;
	unsigned long	ulLastTime		ALIGNATTRIB;
	char			bLogin			ALIGNATTRIB;
	unsigned long	dwType			ALIGNATTRIB;
	unsigned long	dwAccessRights	ALIGNATTRIB;
	void*			pFileDownload	ALIGNATTRIB;
	void*			pFileUpload		ALIGNATTRIB;
	unsigned long	ulApplications	ALIGNATTRIB;
	unsigned long	ulApplActive[MAX_APPLTYPES]		ALIGNATTRIB;
	unsigned long   ulCS			ALIGNATTRIB;

	/* Monitoring data */
	MONITORING_DATA*	pVarList	ALIGNATTRIB;		

	/* Force data */
	FORCE_DATA*			pForceList	ALIGNATTRIB;

	/* Trace data */
	TRACE_DATA*			pTrace		ALIGNATTRIB;
} APPL_DATA, *PAPPL_DATA;

typedef struct CLIENT_DATAtag
{	
	/* Application management */
	char			bValid					ALIGNATTRIB;
	unsigned long	ulApplications			ALIGNATTRIB;
	APPL_DATA**		ppAppl					ALIGNATTRIB;
	unsigned long   ulCS					ALIGNATTRIB;
	unsigned long	ulForceListsActive		ALIGNATTRIB;

	/* Communication data */
	char			bOpen					ALIGNATTRIB;
	unsigned short	usIndex					ALIGNATTRIB;
	unsigned long	ulTask					ALIGNATTRIB;	/* Task Id of client communication task */
	unsigned long	ulHandle				ALIGNATTRIB;
	unsigned short	byProtocol				ALIGNATTRIB;
	char			bSendMessage			ALIGNATTRIB;
	char*			pcSendBuffer			ALIGNATTRIB;
	unsigned long	ulSendSize				ALIGNATTRIB;
	unsigned long	ulSendBufferSize		ALIGNATTRIB;
	char*			pcReceiveBuffer			ALIGNATTRIB;
	unsigned long	ulReceiveSize			ALIGNATTRIB;
	unsigned long	ulReceiveBufferSize		ALIGNATTRIB;
	unsigned long	ulDestination			ALIGNATTRIB;	/* Destination Id for routing protocols */
	unsigned long	ulSource				ALIGNATTRIB;	/* Source Id for routing protocols */
	
	/* Data for blocking data transfer from comm driver */
	char			bBlocking				ALIGNATTRIB;
	unsigned char	byService				ALIGNATTRIB;
	char			cSubService				ALIGNATTRIB;
	unsigned long	ulBlockNumber			ALIGNATTRIB;
	char			bLastBlock				ALIGNATTRIB;
	unsigned long	ulReceiveIndex			ALIGNATTRIB;
	unsigned long	ulReceiveMessageSize	ALIGNATTRIB;
} CLIENT_DATA, *PCLIENT_DATA;
#define PLC_COM_BUFFER_SIZE 0x40000
#pragma pack()

typedef void (*PFSRVCOMPUTESERVICE)(CLIENT_DATA *pClient, unsigned long ulApplId);
typedef char (*PFSRVADDCLIENT)(CLIENT_DATA **ppClient);
typedef void (*PFSRVDELETECLIENT)(CLIENT_DATA *pClient, char bFree);

typedef void (*PFCYCLICINTERRUPTHANDLER)(void);

typedef unsigned long (*PFCSCREATE)(void);
typedef void (*PFCSENTER)(unsigned long ulCS);
typedef void (*PFCSLEAVE)(unsigned long ulCS);
typedef void (*PFCSDELETE)(unsigned long ulCS);

typedef unsigned short (*PF_READINTERRUPTMASK)(unsigned long* pulPar);
typedef unsigned short (*PF_WRITEINTERRUPTMASK)(unsigned short usMask);

typedef struct _tagRtsApiInterface
{
    unsigned long ulVersion;
    PFRTSSTOPALLTASKS  pfRtsStopAllTasks;
    PFRTSSTARTALLTASKS pfRtsStartAllTasks;
    PFRTSGETPLCSTATE   pfRtsGetPlcState;
    PFRTSPROGRAMLOADED pfRtsProgramLoaded;
    PFRTSIOGETINPUTMEMORY pfRtsGetInputMemory;
    PFRTSIOGETOUTPUTMEMORY pfRtsGetOutputMemory;
    PFRTSIOGETINPUTSIZE pfRtsGetInputSize;
    PFRTSIOGETOUTPUTSIZE pfRtsGetOutputSize;
    PFRTSCREATETASK pfRtsCreateTask;
    PFRTSDELETETASK pfRtsDeleteTask;
    PFRTSWAITFOROBJECT pfRtsWaitForObject;
    PFRTSSETOBJECT pfRtsSetObject;
    PFRTSSLEEP pfRtsSleep;
    PFASYNCHFILEOPEN pfAsynchFileOpen;
    PFASYNCHFILECLOSE pfAsynchFileClose;
    PFASYNCHFILEREAD pfAsynchFileRead;
    PFASYNCHFILEWRITE pfAsynchFileWrite;
    PFRTSRESUME pfRtsResume;
    PFRTSSUSPEND pfRtsSuspend;
    PFRTSPROGRAMRESET pfRtsProgramReset;
    PFDATAMANIPULATION pfRtsDataManipulation;
    PFRTSGETPROJECTINFO pfRtsGetProjectInfo;
    PFSRVCOMPUTESERVICE pfSrvComputeService;
    PFSRVADDCLIENT pfSrvAddClient;
    PFSRVDELETECLIENT pfSrvDeleteClient;
	PFCYCLICINTERRUPTHANDLER pfCyclicInterruptHandler;
    PFCSCREATE pfCsCreate;
    PFCSENTER pfCsEnter;
    PFCSLEAVE pfCsLeave;
    PFCSDELETE pfCsDelete;
}RtsApiInterface;

//with this constant, it is possible to give IOdriver-created tasks a lower priority than certain IEC-tasks.
#define IECTASK_BASE_PRIORITY		0x20

//end callback into rts-api.

//defines for the standard-commands, the driver should support.
#define DRV_CMD_INVALID_CMD 0
#define DRV_CMD_GET_FUNCTION_POINTER_LIST 1
#define DRV_CMD_VERSIONCHECK  2

typedef enum _tagFctIndices
{
	DRV_FCT_INIT = 1,
	DRV_FCT_EXIT,
	DRV_FCT_GET_ID,
	DRV_FCT_GET_FLAGS,
	DRV_FCT_GET_IORANGE,
	DRV_FCT_CONFIGURE_IONET,
	DRV_FCT_CONFIGURE_MODULE,
	DRV_FCT_CONFIGURE_DEVICE,
	DRV_FCT_START_WRITE_OUTPUTS,
	DRV_FCT_WRITE_OUTPUTS,
	DRV_FCT_DONE_WRITE_OUTPUTS,
	DRV_FCT_START_READ_INPUTS,
	DRV_FCT_READ_INPUTS,
	DRV_FCT_DONE_READ_INPUTS,
	DRV_FCT_PLC_STATUS_CHANGES,
	DRV_FCT_START_CONFIGURE_IONET,
	DRV_FCT_DONE_CONFIGURE_IONET,
    DRV_FCT_ENTER_NMI_ROUTINE,
    DRV_FCT_LEAVE_NMI_ROUTINE,
    DRV_FCT_START_INTERRUPT,
    DRV_FCT_STOP_INTERRUPT,
    DRV_FCT_RETAIN_SAVE,
    DRV_FCT_RETAIN_RESTORE,
    DRV_FCT_CYCLIC_CALL,
    DRV_GET_EXTREFTABLE,
    DRV_FCT_BUSDIAG_GETBUSSTATE,
    DRV_FCT_BUSDIAG_GETSTATE,
    DRV_FCT_BUSDIAG_SETBUSSTATE,
    DRV_FCT_BUSDIAG_SETSTATE,
    DRV_FCT_AFTERDOWNLOAD,
    DRV_FCT_PLCRESETHOOK,
    DRV_FCT_PRECOMPUTE_SERVICE,
    DRV_FCT_POSTCOMPUTE_SERVICE,
    DRV_FCT_SRV_BROWSERCMD,
    DRV_FCT_ADD_BROWSERHELP,
    DRV_FCT_CST_PROCESSHOOK,
    DRV_FCT_GETTARGETIDS,
	DRV_FCT_READINTERRUPTMASK,
	DRV_FCT_WRITEINTERRUPTMASK,
    		/*Add new indices here.*/
	DRV_FCT_MAXINDEX /*Must be the last enum-member*/
}FctIndices;


typedef enum _tagRtsApiFctIndices
{
    API_FCT_ULVERSION = 0,
    API_FCT_STOP_ALL_TASKS,
    API_FCT_START_ALL_TASKS,
    API_FCT_GET_PLC_STATE,
    API_FCT_PROGRAM_LOADED,
    API_FCT_GET_INPUT_MEMORY,
    API_FCT_GET_OUTPUT_MEMORY,
    API_FCT_GET_INPUTSIZE,
    API_FCT_GET_OUTPUTSIZE,
    API_FCT_CREATE_TASK,
    API_FCT_DELETE_TASK,
    API_FCT_SLEEP,
    API_FCT_WAIT_FOR_OBJECT,
    API_FCT_SET_OBJECT,
    API_FCT_OPENFILE,
    API_FCT_CLOSEFILE,
    API_FCT_READFILE,
    API_FCT_WRITEFILE,
    API_FCT_RESUME,
    API_FCT_SUSPEND,
    API_FCT_PRGRESET,
    API_FCT_DATAMANIPULATION,
    API_FCT_GETPROJECTINFO,
    API_FCT_PFSRVCOMPUTESERVICE,
    API_FCT_PFSRVADDCLIENT,
    API_FCT_PFSRVDELETECLIENT,
	API_FCT_PFCYCLICINTERRUPTHANDLER,
    API_FCT_PFCSENTER,
    API_FCT_PFCSLEAVE,
    API_FCT_PFCSCREATE,
    API_FCT_PFCSDELETE,

        /*insert new indices here, after talking to 3S*/
    API_FCT_MAXINDEX
}RtsApiFctIndices;


typedef struct _tagIORange
{
	unsigned long ulOffset;
	unsigned long ulSize;
}IORange;


#define DRV_CAP_FLAG_WANTS_CONFIG	0x1
#define DRV_CAP_FLAG_IS_PBDEVICE	0x2
#define DRV_CAP_FLAG_IS_CANDEVICE	0x4
#define DRV_CAP_FLAG_IS_ASIDEVICE	0x8
#define DRV_CAP_FLAG_IS_TCPIPDEVICE	0x10
#define DRV_CAP_FLAG_IS_ARCNETDEVICE	0x20
#define DRV_CAP_FLAG_IS_IBSDEVICE	0x40
#define DRV_CAP_FLAG_IS_CONFCONFDEV 0x80

#define DRV_CAP_FLAG_HAS_RETAINDATA_AREA 0x100

/*These flags (combination) are possible return-values for the function IOdrvEnterNMIServiceRoutine.*/
#define IODRV_NMIFLAG_SYNCHRONIZE_HIGHPRIOTASK  (0x1UL)
#define IODRV_NMIFLAG_SCHEDULE_NT_ALWAYS        (0x2UL)
#define IODRV_NMIFLAG_SCHEUDLE_SCHEDULERTASKS   (0x4UL)

//structures used to pass CANnode-descriptions to the external drivers.
#pragma pack(1)

typedef struct _CanSDOwithData
{
	unsigned short	wIndex;
	unsigned char	bySubIndex;
	unsigned char	bySize;
	char* pData;   /*this structure is used for sending SDO-data from PLC to IODrivers, so the first 3 members must look like CAN_SDO !*/
}CAN_SDO_withData;

typedef struct CanKanal
{
	unsigned char	byBitOffsetInPDO;
	unsigned short	wRefid;
	unsigned long	ulBitOffset;
	unsigned char	byLen;
}CAN_KANAL;

typedef struct CanPDOwithChannel
{
	unsigned char	byDirection;
	unsigned long	ulCOBID;
	unsigned char	byCMS;
	unsigned short	wInhibitTime;
	unsigned char	byTransType;
	unsigned char	byNumOfChannels;
	CAN_KANAL* pChannel;
}CAN_PDOwithChannel;                                   

typedef struct CANM_ND_PRM_HEADERtag {
  unsigned short  usNode_Para_Len;  /* length of whole parameter data set+2 */
  struct
  {
    unsigned char bReserved    : 7;
    unsigned char bActive      : 1;
  } tNd_Flag;
  unsigned short  usAddTypeInfo;    /* corresponding module type OBJ1000H */
  unsigned short  usDevProfNum;     /* corresponding profile number OBJ1000H*/
  unsigned short  usCobIDEmcy;      /* EMCY-COB-ID */
  unsigned short  usCobIDGuard;     /* NDGRD-COB-ID */
  unsigned short  usGuardTime;      /* guard-time in multiples of 1msec */
  unsigned char   bLifeTime;        /* multiplcator for the guard-time */
  unsigned char   abOctet_String[2];
} CANM_ND_PRM_HEADER;

typedef struct _tagCANnode
{
	CANM_ND_PRM_HEADER	cnpHeader; 
	unsigned short	   	nCanSdos; /*determines how much CAN_SDO-structures are stored at pCanSdo*/
	CAN_SDO_withData*	pCanSdo;	
	unsigned short	   	nCanPdos; /*determines how much CAN_PDO-structures are stored at pCanPdo*/
	CAN_PDOwithChannel*	pCanPdo;	
}CANnode;

typedef struct CanBus
{
	char			szName[30];
	unsigned short	wNodeIDMaster;
	unsigned char	byAutoStart;
	unsigned char	byBaudrate;
	unsigned long	ulCommCyclePeriod;
	unsigned long	ulSyncWindowsLen;
	unsigned long	ulSyncCobID;
	unsigned short	wDiagSegment;
	unsigned long	dwDiagOffset;
	unsigned char	byNumOfNodes;
	struct _tagCCModule* pCCModule;
}CAN_BUS;

#pragma pack()


#pragma pack(1)
/* --------------------------------- */
/* structure to set master parameter */
/* --------------------------------- */

/* master parameter 'DPM_Bus_Dp' data set
   described in norm DIN 19245-T3 page 98 */

#define DP_BAUD_96      0
#define DP_BAUD_19_2    1
#define DP_BAUD_93_75   2
#define DP_BAUD_187_5   3
#define DP_BAUD_500     4
#define DP_BAUD_1500    6
#define DP_BAUD_3000    7
#define DP_BAUD_6000    8
#define DP_BAUD_12000   9
//added form 3S:
#define DP_BAUD_31_25   10	
#define DP_BAUD_45_45   11

typedef struct DPM_BUS_DPtag {
   unsigned short       usBus_Para_Len;   /* length of BUS_DP */
   unsigned char        bFDL_Add;         /* master address */
   unsigned char        bBaudrate;        /* baudrate, see. #defines */

   unsigned short       usTSL;            /* slot-time */
   unsigned short       usMin_TSDR;       /* min. station delay responder */
   unsigned short       usMax_TSDR;       /* max. station delay responder */
   unsigned char        bTQUI;            /* quite time */
   unsigned char        bTSET;            /* setup time */
   unsigned long        ulTTR;            /* target rotation time */
   unsigned char        bG;               /* gap update */
   unsigned char        bHSA;             /* highest active station address */
   unsigned char        bMax_Retry_Limit; /* retries if error occurs */
   struct
   {
     unsigned char      bReserved          : 7;
     unsigned char      bError_Action_Flag : 1;
   } Bp_Flag;                             /* auto_clear on/off */
   unsigned short       usMin_Slave_Intervall; /* min. slave intervall time */
   unsigned short       usPoll_Timeout;        /* poll timeout */
   unsigned short       usData_Control_Time;   /* data control time */

   unsigned char        abOctet[6];
   unsigned short       usMaster_User_Data_Len;
   char                 achMaster_Class2_Name[32];
   unsigned char        abMaster_User_Data[1];
} DPM_BUS_DP;


/* -------------------------------- */
/* structures to set slave data set */
/* -------------------------------- */

/* parameter data header structure of a slave station,
   described in norm EN 50170 page 101 */

typedef struct DPM_SL_PRM_HEADERtag {
  unsigned short  usSlaveParaLen;    /* length of whole parameter data set */
  struct
  {
    unsigned char  biReserved1       : 1;
    unsigned char  biExtra_Alarm_SAP : 1;
    unsigned char  biDPV1_Data_Types : 1;
    unsigned char  biDPV1_Supported  : 1;
    unsigned char  biReserved2       : 1;
    unsigned char  biFail_Safe       : 1;
    unsigned char  biNew_Prm         : 1;
    unsigned char  biActive          : 1;
  } Sl_Flag;                          /* slave related flags */
  unsigned char    bSlave_Typ;         /* type of slave */

  unsigned char    bMax_Diag_Data_Len;
  unsigned char    bMax_Alarm_Len;
  unsigned char    bMax_Channel_Data_Length;
  unsigned char    bDiag_Upd_Delay;
  struct
  {
    unsigned char  biNA_to_Abort : 1;
    unsigned char  biIgnore_Aclr : 1;
    unsigned char  abiReserved   : 6;
  } Alarm_Mode;

  unsigned char    bAdd_Sl_Flag;
  unsigned char    abOctet_String[6];

} DPM_SL_PRM_HEADER;


/* parameter data 'Prm_Data' structure of a slave station,
   described in norm EN 50170 page 51 */

#define  MAX_USR_PRM_LEN  242

typedef struct DPM_SL_PRM_PRM_DATAtag {
  unsigned short  usPrmDataLen;       /* length of PRM_PRM_DATA */
  struct
    {
      unsigned char   bReserved     : 3;
      unsigned char   bWD_On        : 1;
      unsigned char   bFreeze_Req   : 1;
      unsigned char   bSync_Req     : 1;
      unsigned char   bUnLock_Req   : 1;
      unsigned char   bLock_Req     : 1;
    } Station_Status;             /* status of supported functions */
  unsigned char   bWD_Fact_1;     /* watchdog factor 1 */
  unsigned char   bWD_Fact_2;     /* watchdog factor 2 */
  unsigned char   bMin_Tsdr;      /* min. station delay reponder */
  unsigned short  usIdent_Number; /* ident number of the station, motorola format */
  unsigned char   bGroup_Ident;   /* fixed group number */
  unsigned char   abUsr_Prm_Data[MAX_USR_PRM_LEN];
} DPM_SL_PRM_PRM_DATA;


/* configuration data 'Cfg_Data' structure of a slave station,
   described in norm EN 50170 page 55 */
#define MAX_CFG_DATA_LEN 255

typedef struct DPM_SL_PRM_CFG_DATAtag {
  unsigned short  usCfg_Data_Len;  /* length of CFG_DATA */
  unsigned char   abCfg_Data[MAX_CFG_DATA_LEN]; /* configuration data of the
                                                   slave station */
} DPM_SL_PRM_CFG_DATA;


/* i/o offset table 'Add_Tab' structure of a slave station,
   described in norm EN 50170 page 103 */
#define MAX_EA_OFFSET_LEN 244

typedef struct DPM_SL_PRM_ADD_TABtag {
  unsigned short  usAdd_Tab_Len; /* length of ADD_TAB */
  unsigned char   bInput_Count;  /* counter of following input offsets */
  unsigned char   bOutput_Count; /* counter of following ouput offsets */
  unsigned short  ausEA_Offset[MAX_EA_OFFSET_LEN]; /* user defined offsets */
} DPM_SL_PRM_ADD_TAB;


/* extented data 'Slave_User_Data' structure of a slave station,
   described in norm EN 50170 page 103 */
#define  MAX_SL_PRM_LEN  100

typedef struct DPM_SL_PRM_USR_DATAtag {
  unsigned short usSlave_Usr_Data_Len; /* length of USR_DATA */
  unsigned char  abSlave_Usr_Data[MAX_SL_PRM_LEN]; /* user paramteres */
} DPM_SL_PRM_USR_DATA;


/* Structures for reading PB config from a byte stream*/

typedef struct PBSlaveHeaderTag
{
	unsigned char		bySlaveStationNumber		ALIGNATTRIB;
	unsigned short		usSlaveRefIdDiag			ALIGNATTRIB;
	unsigned long		unSlaveBitOffsetDiag		ALIGNATTRIB;
	unsigned short		usSlaveRefIdInput			ALIGNATTRIB;
	unsigned long		unSlaveBitOffsetInput		ALIGNATTRIB;
	unsigned long		unSlaveInputDataLength		ALIGNATTRIB;
	unsigned short		usSlaveRefIdOutput			ALIGNATTRIB;
	unsigned long		unSlaveBitOffsetOutput		ALIGNATTRIB;
	unsigned long		unSlaveOutputDataLength		ALIGNATTRIB;
}PBSlaveHeader;

typedef struct PBMasterDescTag
{
	unsigned short	usMasterRefIdDiag				ALIGNATTRIB;
	unsigned long	unMasterBitOffsetDiag			ALIGNATTRIB;
	unsigned char	byAutoStart						ALIGNATTRIB;
	DPM_BUS_DP		dpMaster						ALIGNATTRIB;
	unsigned char	bySlaves						ALIGNATTRIB;
	struct _tagCCModule* pCCModule;
}PBMasterDesc;

typedef struct PBConfigHeaderTag
{
	char			szConfigName[10]	ALIGNATTRIB;	/* Name of configuration */
	unsigned short	wVersion			ALIGNATTRIB;	/* Version number */
	unsigned long	dwLength			ALIGNATTRIB;	/* Length of configuration */
}PBConfigHeader;

typedef struct PBSlaveTag
{
	PBSlaveHeader			*pHeader;
	DPM_SL_PRM_HEADER		*pslHeader;
	DPM_SL_PRM_PRM_DATA		*pslPrmData;
	DPM_SL_PRM_CFG_DATA		*pslCfgData;
	DPM_SL_PRM_ADD_TAB		*pslAddTab;
	DPM_SL_PRM_USR_DATA		*pslUsrData;
}PBSlave;

typedef struct PBMasterTag
{
	PBMasterDesc	*pMasterDesc;
	PBSlave			*pSlaves;
}PBMaster;

/*The definitions for the external lib 'BusDiag.lib'.*/
#define BOOL char
#define INT short
#define DWORD unsigned long
#define BYTE unsigned char 

//Possible STATEs in the requests to IOdriver-diag-functions.
#define STATE_FIRSTLEVEL_NODESTATE 1

//Possible STATEs in the GETSTATE-type to request/return:
#define NDSTATE_INVALID_INPUTPARAM ((short)(-1))
#define NDSTATE_NOTENABLED              0
#define NDSTATE_GETDIAG_INFO            1

//Returnvalues from a request (in STATE):
#define NDSTATE_DIAGINFO_AVAILABLE      2
#define NDSTATE_DIAGINFO_NOTAVAILABLE   3

typedef struct
{
	char BOLDENABLE;
	char ENABLE;
	char* DRIVERNAME;
	short DEVICENUMBER;
	char READY;
	short STATE;
	unsigned char EXTENDEDINFO[130];
}
BUSDIAGLIB_GETBUSSTATEtyp;

typedef struct
{
	BOOL BOLDENABLE;
	BOOL ENABLE;
	char* DRIVERNAME;
	INT DEVICENUMBER;
	DWORD BUSMEMBERID;
	BOOL READY;
	INT STATE;
	BYTE EXTENDEDINFO[100];
}
BUSDIAGLIB_GETSTATEtyp;

typedef struct
{
	BOOL BOLDENABLE;
	BOOL ENABLE;
	char* DRIVERNAME;
	INT DEVICENUMBER;
	INT NEWSTATE;
	BOOL READY;
	INT STATE;
}
BUSDIAGLIB_SETBUSSTATEtyp;

typedef struct
{
	BOOL BOLDENABLE;
	BOOL ENABLE;
	char* DRIVERNAME;
	INT DEVICENUMBER;
	INT NEWSTATE;
	DWORD BUSMEMBERID;
	BOOL READY;
	INT STATE;
}
BUSDIAGLIB_SETSTATEtyp;

#pragma pack()              /* Byte Alignment   */

#pragma pack(8)

typedef struct
{
	char BOLDENABLE;
	char ENABLE;
	char* DRIVERNAME;
	short DEVICENUMBER;
	char READY;
	short STATE;
	unsigned char EXTENDEDINFO[130];
}
BUSDIAGLIB_GETBUSSTATEtyp8;

typedef struct
{
	BOOL BOLDENABLE;
	BOOL ENABLE;
	char* DRIVERNAME;
	INT DEVICENUMBER;
	DWORD BUSMEMBERID;
	BOOL READY;
	INT STATE;
	BYTE EXTENDEDINFO[100];
}
BUSDIAGLIB_GETSTATEtyp8;

typedef struct
{
	BOOL BOLDENABLE;
	BOOL ENABLE;
	char* DRIVERNAME;
	INT DEVICENUMBER;
	INT NEWSTATE;
	BOOL READY;
	INT STATE;
}
BUSDIAGLIB_SETBUSSTATEtyp8;

typedef struct
{
	BOOL BOLDENABLE;
	BOOL ENABLE;
	char* DRIVERNAME;
	INT DEVICENUMBER;
	INT NEWSTATE;
	DWORD BUSMEMBERID;
	BOOL READY;
	INT STATE;
}
BUSDIAGLIB_SETSTATEtyp8;

#pragma pack()
/*Now the definitions for some usefull structures from CommConfig follow.*/
#pragma pack(4)
typedef struct _tagCCParam
{
    unsigned long		ulParameterId;/*	Id of the parameter given in the configuration file (*.cfg)*/
    unsigned short		usParameterNumber;/*	Number of the parameter in the module*/
    unsigned char		byReadOnly;/*		1=TRUE, 0=FALSE*/
    unsigned short		usParameterType;/*		Type of the parameter (coded as CoDeSys "TypeClass")*/
    unsigned long		ulSize;/*			Size of the parameter in bytes*/
    unsigned char	    byValue[1];/*			Memory representation of the parameter value*/
}CCParam;

typedef struct _tagCCChannel
{
    unsigned char	ucEntryTag;/*	'C' = Channel*/
    signed long		ulChannelId;/*	Id of the channel given in the configuration file*/
    unsigned short	usChannelNumber;/*	Number of the channel in the parent module*/
    unsigned long		ulRefId;/*	Direction of the channel (1=input, 2=output,
						    3=input and output)*/
    unsigned short		usChannelType;/*		Type of the channel (coded as CoDeSys "TypeClass")*/
    unsigned long		ulBitOffset;/*		Offset of the channel in in-/output area*/
    unsigned short		usParameterCount;/*	Number of parameters*/
    CCParam** ppccpParams; /*<PARAMETER[1..usParameterCount]> pointer to an array of pointers to CCParam-structures.*/
}CCChannel;
#define MODULETYPE_3SMODULE         0
#define MODULETYPE_DPMASTER         1
#define MODULETYPE_DPSLAVE          2
#define MODULETYPE_CANMASTER        3
#define MODULETYPE_CANSLAVE         4
#define MODULETYPE_DPSINGLESLAVE    5
typedef struct _tagCCModule
{
    unsigned char	ucEntryTag;/*		'M' = Module*/
    unsigned long	ulModuleId;/*		Id of the module given in the configuration file (*.cfg)*/
    signed short	sModuleNumber;/*	Number of the module in the parent module (-1 if root)*/ 
    unsigned short	usModuleTag;/*		Describes the kind of the module
    (0=3S-Module, 1=DP-Master, 2=DP-Slave, 3=CAN-Master, 4=CAN-Slave, 5=DP-SingleSlave)*/
    unsigned char	byDeviceDriver;/*	The module needs a device driver (0=FALSE, 1=TRUE)*/
    unsigned long	ulNodeId; /*		NodeId of the module*/
    unsigned char	byDefinedWithStruct;/*	The module was defined with a structure (0=FALSE, 1=TRUE)*/
    unsigned long	ulBitOffsetInput;/*	Offset of the modules input area */
    unsigned long	ulBitSizeInput;/*	Size of the modules input area in bit*/
    unsigned long	ulBitOffsetOutput;/*Offset of the modules output area*/
    unsigned long	ulBitSizeOutput;/*  Size of the modules output area in bit*/
    unsigned long   ulRefIdDiag;   /*The modules common diagnosis area*/
    unsigned long	ulBitOffsetCommonDiag;/*Offset of the modules common diagnosis area*/
    unsigned long	ulBitSizeDiag;/*		Size of the modules diagnosis area in bit*/		
    unsigned short 	usParameterCount;/*	Number of parameters*/
    CCParam**       ppccpModuleParams;/* <PARAMETER[0..usParameterCount]> a pointer to an array of pointers to CCModuleParam-structures.(Definition above)*/ 
    unsigned long	ulSizeOfSpecificData; /*Size in bytes of the module specific data*/
    void* pModuleData; /*<MODULE_SPECIFIC_DATA> Here the data, according to usModuleTag is located:
                        pModuleData is possible to be a pointer to PBSlave,CANSlave,PBMaster,PBSlave,PBSingleSlave(exotic, used by Elau),
                        see definitions below.*/
    unsigned short	usChannelCount;/*		Number of configured channels*/
    unsigned short	usModuleCount;/*		Number of configured modules */

    /*Here the Channels and Modules of this Module in the configured order are located!
    (DP-Slaves are ordered by the stationnumber!)
    This means, its possible a CCModule structure is inserted here.*/
    CCChannel**       ppcccChannels; /*(Definition above)*/
    struct _tagCCModule** ppccmSubModules;
}CCModule;

#pragma pack()
/*End CommConfig-structures*/

/* Prototypes for BusDiagLib-calls. */
void IODrvLibGetBusState(BUSDIAGLIB_GETBUSSTATEtyp* pinst);
void IODrvLibSetBusState(BUSDIAGLIB_SETBUSSTATEtyp* pinst);

void IODrvLibGetState(BUSDIAGLIB_GETSTATEtyp* pinst);
void IODrvLibSetState(BUSDIAGLIB_SETSTATEtyp* pinst);

#undef BOOL
#undef INT
#undef DWORD
#undef BYTE

/*end of BusDiagLib interface.*/

/* Controller Status definitions */
#define RTS_VOID					((unsigned char)-1)
#define RTS_RUN						0
#define RTS_STOP_SRV				1
#define RTS_STOP_BP					2
#define RTS_RUN_STEP_IN				3
#define RTS_RUN_STEP_OVER			4
#define RTS_RUN_WATCHDOG			5

#define RTS_VOID_TEXT				"VOID"
#define RTS_RUN_TEXT				"RUN"
#define RTS_RUNP_TEXT				"RUN-P"
#define RTS_STOP_SRV_TEXT			"STOP"
#define RTS_STOP_BP_TEXT			"STOP-BP"
#define RTS_RUN_STEP_IN_TEXT		"RUN-StepIn"
#define RTS_RUN_STEP_OVER_TEXT		"RUN-StepOver"
#define RTS_RUN_WATCHDOG_TEXT		"RUN-Watchdog"

/* List of service replies */
#define RTS_OK						0		/* Service treated correctly */
#define DEB_BP_ALREADY_SET			10		/* Breakpoint already set */
#define DEB_BP_FULL					11		/* Maximal number of breakpoints reached */
#define DEB_BP_NOT_FOUND			12		/* No valid breakpoint */
#define SRV_NO_SERVICE				50		/* No valid service (or not implemented) */
#define SRV_NO_VARLIST				51		/* No varlist defined */
#define SRV_FB_UNDEFINED			52
#define SRV_DATA_OVERFLOW           53
#define SRV_NO_SOURCE_LOADED		54
#define SRV_RELOC_ERROR				60
#define SRV_CODE_SIZE_ERROR			61
#define SRV_DATA_SIZE_ERROR			62
#define SRV_RETAIN_SIZE_EXCEEDED	63
#define SRV_ERROR_ONLCHANGE_NOT_SUPPORTED	64
#define SRV_RESOLVE_REFERENCE_ERROR	65
#define SRV_CHECKSUM_ERROR			66
#define SRV_NO_ACCESS_RIGHT			67
#define SRV_NUM_OF_POUS_MISMATCH	68
