# List of functions, structures and constants

#

## Constants

    /*
        This is a wrapper for the serial or ethernet interface. This is here to make porting easier.
    */
    typedef struct {
        int rfd;
        int wfd;
    } _daveOSserialType;
    typedef struct {
        HANDLE rfd;
        HANDLE wfd;
    } _daveOSserialType;

### some frequently used ASCII control codes:

    DLE
    ETX
    STX
    SYN

### Protocol types to be used with newInterface:

Name | Meaning
--- | ---
daveProtoMPI | MPI for S7 300/400
daveProtoMPI2 | MPI for S7 300/400, "Andrew's version"
daveProtoMPI3 | MPI for S7 300/400, The version Step7 uses. Not yet implemented.
daveProtoPPI | PPI for S7 200
daveProtoISOTCP | ISO over TCP
daveProtoISOTCP243 | ISO over TCP with CP243
daveProtoIBH | MPI with IBH NetLink MPI to ethernet gateway

### ProfiBus/MPI speed constants to be used with newInterface:

daveSpeed9k
daveSpeed19k
daveSpeed187k
daveSpeed500k
daveSpeed1500k
daveSpeed45k
daveSpeed93k

### Some S7 Communication function codes (yet unused ones may be incorrect).

These codes are used as the first byte of parameters in a PDU.

Name | Meaning
--- | ---
daveFuncOpenS7Connection | connect to a PLC, negotiate PDU length
daveFuncRead | marks a read requeast
daveFuncWrite | marks a write requeast
daveFuncStartUpload | initiates the transmission of a code block from PLC to programmer
daveFuncUpload | continues the transmission of a part of a code block from PLC to programmer
daveFuncEndUpload ends the transmission of a part of a code block from PLC to programmer

### S7 specific constants:

#### Block Type codes

S7 specific constants:

####

###

###

###

###

##

###

###

###

###

###

##

###

###

###

###

###

###

###

###

###

###

###

###

##

##

###

###

###

##

##

##

###

###

##

###

    	daveBlockType_OB
    	daveBlockType_DB
    	daveBlockType_SDB
    	daveBlockType_FC
    	daveBlockType_SFC
    	daveBlockType_FB
    	daveBlockType_SFB
    Memory Area Codes
    Use these constants for parameter "area" in daveReadBytes and daveWriteBytes.

    NameMeaning
    daveSysInfo 0x3System info of 200 family
    daveSysFlagsSystem flags of 200 family
    daveAnaInanalog inputs of 200 family
    daveAnaOutanalog outputs of 200 family
    daveInputsInput image memory
    daveOutputsOutput image memory
    daveFlagsFlags (Merker) area
    daveDBData blocks in 300 and 400, V-Memory in 200
    daveCounterCounters in 300 and 400
    daveTimerTimers in 300 and 400

    Function Result Codes.
    Genarally, 0 means ok, >0 are results (also errors) reported by the PLC, <0 means error reported by library code.

    NameValueMeaning
    daveResOK0all ok
    daveResMultipleBitsNotSupported6CPU tells it does not support to read a bit block with a length other than 1 bit
    daveResItemNotAvailable2003means a a piece of data is not available in the CPU, e.g. when trying to read a non existing DB or bit bloc of length<>1. This code seems to be specific to 200 family.
    daveResItemNotAvailable10 means a a piece of data is not available in the CPU, e.g.  when trying to read a non existing DB
    daveAddressOutOfRange5means the data address is beyond the CPUs address range
    daveResCannotEvaluatePDU-123
    daveResCPUNoData-124
    daveUnknownError-125
    daveEmptyResultError-126
    daveEmptyResultSetError-127

    Error code to message string conversion:
    Call this function to get an explanation for error codes returned by other functions.

    	char *  daveStrerror(int code);

    Max number of bytes in a single message.
    An upper limit for MPI over serial is:

    8 byte transport header
    +2*240	max PDU len *2 if every character were a DLE
    +3		DLE,ETX and BCC
    = 491
        Later I saw some programs offering up to 960 bytes in PDU size negotiation

        Max number of bytes in a single message.
        An upper limit for MPI over serial is:
        8		transport header
        +2*960	max PDU len *2 if every character were a DLE
        +3		DLE,ETX and BCC
        = 1931

        For now, we take the rounded max of all this to determine our buffer size. This is ok
        for PC systems, where one k less or more doesn't matter.

    #define daveMaxRawLen 2048

    Some definitions for debugging:

    daveDebugRawReadShow the single bytes received
    daveDebugSpecialCharsShow when special chars are read
    daveDebugRawWriteShow the single bytes written
    daveDebugListReachablesShow the steps when determine devices in MPI net
    daveDebugInitAdapterShow the steps when Initilizing the MPI adapter
    daveDebugConnectShow the steps when connecting a PLC
    daveDebugPacket
    daveDebugByte
    daveDebugCompare
    daveDebugExchange
    daveDebugPDUdebug PDU handling
    daveDebugUploaddebug PDU loading program blocks from PLC
    daveDebugMPI
    daveDebugPrintErrorsPrint error messages
    daveDebugPassive
    daveDebugAllEnables all debug levels

    Global variables
    Current debug level:

    int daveDebug;

    Set the Current debug level:

    void setDebug(int nDebug);

    Some useful data types:

    #define uc unsigned char
    #define us unsigned short
    #define u32 unsigned int

    typedef struct _daveConnection  daveConnection;
    typedef struct _daveInterface  daveInterface;
    Helper struct to manage PDUs. This is NOT the part of the packet called PDU, but a set of pointers that ease access to the "private parts" of a PDU.

    typedef struct {
        uc * header;	/* pointer to start of PDU (PDU header) */
        uc * param;		/* pointer to start of parameters inside PDU */
        uc * data;		/* pointer to start of data inside PDU */
        uc * udata;		/* pointer to start of result data inside PDU */
        int hlen;		/* header length */
        int plen;		/* parameter length */
        int dlen;		/* data length */
        int udlen;		/* user or result data length */
    } PDU;

    Definitions of prototypes for the protocol specific functions. The library "switches" protocol by setting pointers to the protol specific implementations.

    typedef int (*_initAdapterFunc) ();
    typedef int (*_connectPLCFunc) ();
    typedef int (*_disconnectPLCFunc) ();
    typedef int (*_disconnectAdapterFunc) ();
    typedef int (*_exchangeFunc) (daveConnection *, PDU *);
    typedef int (*_receiveFunc) (daveConnection *, PDU *);
    typedef int (*_listReachablePartnersFunc) (daveInterface * di, char * buf);

    /+
        This groups an interface together with some information about it's properties
        in the library's context.
    */

    struct _daveInterface {
        _daveOSserialType fd; /* some handle for the serial interface */
        int users;		/* a counter used when multiple PLCs are accessed via */
    			/* the same serial interface and adapter. */
        int localMPI;	/* the adapter's MPI address */
        char * name;	/* just a name that can be used in programs dealing with multiple */
    			/* daveInterfaces */
        int timeout;	/* Timeout in microseconds used in transort. */
        int protocol;	/* The kind of transport protocol used on this interface. */
        int speed;		/* The MPI or Profibus speed */
        int ackPos;		/* position of some packet number that has to be repeated in ackknowledges */
        _initAdapterFunc initAdapter;		/* pointers to the protocol */
        _connectPLCFunc connectPLC;			/* specific implementations */
        _disconnectPLCFunc disconnectPLC;		/* of these functions */
        _disconnectAdapterFunc disconnectAdapter;
        _exchangeFunc exchange;
        _listReachablePartnersFunc listReachablePartners;
    };

    daveInterface *  daveNewInterface(_daveOSserialType nfd, char * nname, int localMPI, int protocol, int speed);

    /*
        A special header for MPI packets:
    */

    typedef struct {
        uc src_conn;
        uc dst_conn;
        uc MPI;
        uc localMPI;
        uc len;
        uc func;
        uc packetNumber;
    } MPIheader;

    /*
        This is the packet header used by IBH ethernet NetLink.
    */

    typedef struct {
        uc ch1;	// logical connection or channel ?
        uc ch2;	// logical connection or channel ?
        uc len;	// number of bytes counted from the ninth one.
        uc packetNumber;	// a counter, response packets refer to request packets
        us sFlags;		// my guess
        us rFlags;		// my interpretation
    } IBHpacket;

    /*
        This holds data for a PLC connection;
    */

    struct _daveConnection {
        daveInterface * iface; /* pointer to used interface */
        int MPIAdr;		/* The PLC's address */
        int messageNumber;  /* current message number */
        int needAckNumber;	/* message number we need ackknowledge for */
        int AnswLen;	/* length of last message */
        PDU rcvdPDU;
        MPIheader templ;	/* template of MPI Header, setup once, copied in and then modified */
        uc msgIn[daveMaxRawLen];
        uc msgOut[daveMaxRawLen];
        uc * resultPointer;	/* used to retrieve single values from the result byte array */
        uc * _resultPointer;
        uc	packetNumber;	/* packetNumber in transport layer */
        int PDUstartO;	/* position of PDU in outgoing messages. This is different for different transport methodes. */
        int PDUstartI;	/* position of PDU in incoming messages. This is different for different transport methodes. */
        int rack;		/* rack number for ISO over TCP */
        int slot;		/* slot number for ISO over TCP */
        int maxPDUlength;
        uc ackByte2;
    };

    /*
        Setup a new connection structure using an initialized
        daveInterface and PLC's MPI address.
    */

    daveConnection * daveNewConnection(daveInterface * di, int MPI,int rack, int slot);

    typedef struct {
        uc type[2];
        unsigned short count;
    } daveBlockTypeEntry;

    typedef struct {
        unsigned short number;
        uc type[2];
    } daveBlockEntry;

    typedef struct {
        uc type[2];
        uc x1[2];  /* 00 4A */
        uc w1[2];  /* some word var? */
        char pp[2]; /* allways 'pp' */
        uc x2[4];  /* 00 4A */
        unsigned short number; /* the block's number */
        uc x3[26];  /* ? */
        unsigned short length; /* the block's length */
        uc x4[16];
        uc name[8];
        uc x5[12];
    } daveBlockInfo;

    PDU handling functions:
        PDU is the central structure present in S7 communication.
        It is composed of a 10 or 12 byte header,a parameter block and a data block.
        When reading or writing values, the data field is itself composed of a data
        header followed by payload data

    typedef struct {
        uc P;	/* allways 0x32 */
        uc type;	/* Header type, one of 1,2,3 or 7. type 2 and 3 headers are two bytes longer. */
        uc a,b;	/* currently unknown. Maybe it can beused for long numbers? */
        us number;	/* A number. This can be used to make sure a received answer */
    		/* corresponds to the request with the same number. */
        us plen;	/* length of parameters which follow this header */
        us dlen;	/* length of data which follow the parameters */
        uc result[2]; /* only present in type 2 and 3 headers. This contains error information. */
    } PDUHeader;

    set up the PDU header. Needs valid header pointer in the struct p points to.

    void _daveInitPDUheader(PDU * p, int type);

    Add parameters after header
    Adjust pointer to data. needs valid header.

    void  _daveAddParam(PDU * p,uc * param,us len);

    add data after parameters
    Set dlen needs valid header,and valid parameters.

    void  _daveAddData(PDU * p,void * data,int len);

    Add values after value header in data
    Adjust dlen and data count. Needs valid header,parameters,data,dlen

    void  _daveAddValue(PDU * p,void * data,int len);

    Add data in user data.
    Add a user data header, if not yet present.

    void  _daveAddUserData(PDU * p, uc * da, int len);

    Build PDU for a read request

    void  _daveConstructReadRequest(PDU *p, int area, int DBnum, int start, int bytes);

        build PDU for a BIT read request

     void  _daveConstructBitReadRequest(PDU *p, int area, int DBnum, int start, int bytes);

        build the PDU for a write request

     void  _daveConstructWriteRequest(PDU *p, int area, int DBnum, int start, int bytes,void * values);

        build the PDU for a bit write request

     void  _daveConstructBitWriteRequest(PDU *p, int area, int DBnum, int start, int bytes,void * values);

        set up pointers to the fields of a received message

     int  _daveSetupReceivedPDU(daveConnection * dc,PDU * p);

        send PDU to PLC and retrieves the answer

     int  _daveExchange(daveConnection * dc,PDU *p);
    Utilities:
    Hex dump PDU:
     void  _daveDumpPDU(PDU * p);
    Compare blocks:
        This is an extended memory compare routine. It can handle don't care and stop flags
        in the sample data. A stop flag lets it return success, if there were no mismatches
        up to this point.
     int  _daveMemcmp(us * a, uc *b, size_t len);
    Hex dump:
    Writes the name followed by len bytes written in hex and a newline.
     void  _daveDump(char * name,uc*b,int len);
    names for Objects
     char *  daveBlockName(uc bn);
     char *  daveAreaName(uc n);

        Data conversion convenience functions:

     int  daveGetByte(daveConnection * dc);

     float  daveGetFloat(daveConnection * dc);

     int  daveGetInteger(daveConnection * dc);

     unsigned int  daveGetDWORD(daveConnection * dc);

     unsigned int  daveGetUnsignedInteger(daveConnection * dc);

     unsigned int  daveGetWORD(daveConnection * dc);

     int  daveGetByteat(daveConnection * dc, int pos);

     unsigned int  daveGetWORDat(daveConnection * dc, int pos);

     unsigned int  daveGetDWORDat(daveConnection * dc, int pos);

     float  daveGetFloatat(daveConnection * dc, int pos);

     float  toPLCfloat(float ff);

     short  daveSwapIed_16(short ff);

     int  daveSwapIed_32(int ff);
    Newer data conversion convenience functions:
        Newer conversion routines. As the terms WORD, INT, INTEGER etc have different meanings
        for users of different programming languages and compilers, I choose to provide a new
        set of conversion routines named according to the bit length of the value used. The 'U'
        or 'S' stands for unsigned or signed.
    Get a value from the position b points to
    B is typically a pointer to a buffer that has
        been filled with daveReadBytes:
     int  daveGetS8from(uc *b);
     int  daveGetU8from(uc *b);
     int  daveGetS16from(uc *b);
     int  daveGetU16from(uc *b);
     int  daveGetS32from(uc *b);
     unsigned int  daveGetU32from(uc *b);
     float  daveGetFloatfrom(uc *b);
    Get a value from the current position
         in the last result read on the connection dc.
        This will increment an internal pointer, so the next value is read from the position
        following this value.

     int  daveGetS8(daveConnection * dc);
     int  daveGetU8(daveConnection * dc);
     int  daveGetS16(daveConnection * dc);
     int  daveGetU16(daveConnection * dc);
     int  daveGetS32(daveConnection * dc);
     unsigned int  daveGetU32(daveConnection * dc);

        Get a value from a given position in the last result read on the connection dc.
     int  daveGetS8at(daveConnection * dc, int pos);
     int  daveGetU8at(daveConnection * dc, int pos);
     int  daveGetS16at(daveConnection * dc, int pos);
     int  daveGetU16at(daveConnection * dc, int pos);
     int  daveGetS32at(daveConnection * dc, int pos);
     unsigned int  daveGetU32at(daveConnection * dc, int pos);
    put one byte into buffer b:
     uc *  davePut8(uc *b,int v);
     uc *  davePut16(uc *b,int v);
     uc *  davePut32(uc *b,int v);
     uc *  davePutFloat(uc *b,float v);
     void  davePut8at(uc *b, int pos, int v);
     void  davePut16at(uc *b, int pos, int v);
     void  davePut32at(uc *b, int pos, int v);
     void  davePutFloatat(uc *b,int pos, float v);
    /**
        Timer and Counter conversion functions:
    **/
    /*
        get time in seconds from current read position:
    */
     float  daveGetSeconds(daveConnection * dc);
    /*
        get time in seconds from random position:
    */
     float  daveGetSecondsAt(daveConnection * dc, int pos);
    /*
        get counter value from current read position:
    */
     int  daveGetCounterValue(daveConnection * dc);
    /*
        get counter value from random read position:
    */
     int  daveGetCounterValueAt(daveConnection * dc,int pos);

    /*
        Functions to load blocks from PLC:
    */
     void  _daveConstructUpload(PDU *p,char blockType, int blockNr);

     void  _daveConstructDoUpload(PDU * p, int uploadID);

     void  _daveConstructEndUpload(PDU * p, int uploadID);
    /*
        Get the PLC's order code as ASCIIZ. Buf must provide space for
        21 characters at least.
    */

    #define daveOrderCodeSize 21
     int  daveGetOrderCode(daveConnection * dc,char * buf);
    /*
        connect to a PLC. returns 0 on success.
    */

     int  daveConnectPLC(daveConnection * dc);
    /*
        Read len bytes from the PLC. Start determines the first byte.
        Area denotes whether the data comes from FLAGS, DATA BLOCKS,
        INPUTS or OUTPUTS. The reading and writing of other data
        like timers and counters is not supported.
        DB is the number of the data block to be used. Set it to zero
        for other area types.
        Buffer is a pointer to a memory block provided by the calling
        program. If the pointer is not NULL, the result data will be copied thereto.
        Hence it must be big enough to take up the result.
        In any case, you can also retrieve the result data using the get macros
        on the connection pointer.

        FIXME:	Existence of DB is not checked.
    		There is no error message for nonexistent data blocks.
    		There is no check for max. message len or
    		automatic splitting into multiple messages.
    */

     int  daveReadBytes(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
    /*
        Write len bytes from buffer to the PLC.
        Start determines the first byte.
        Area denotes whether the data goes to FLAGS, DATA BLOCKS,
        INPUTS or OUTPUTS. The writing of other data
        like timers and counters is not supported.
        DB is the number of the data block to be used. Set it to zero
        for other area types.
        FIXME:	Existence of DB is not checked.
    		There is no error message for nonexistent data blocks.
    		There is no check for max. message len or
    		automatic splitting into multiple messages.
    */
     int  daveWriteBytes(daveConnection * dc,int area, int DB, int start, int len, void * buffer);

    /*
        Bit manipulation:
    */
     int  daveReadBits(daveConnection * dc, int area, int DB, int start, int len, void * buffer);
     int  daveWriteBits(daveConnection * dc,int area, int DB, int start, int len, void * buffer);
    /*
        PLC diagnostic and inventory functions:
    */
     int  daveReadSZL(daveConnection * dc, int ID, int index, void * buf);
     int  daveListBlocksOfType(daveConnection * dc,uc type,daveBlockEntry * buf);
     int  daveListBlocks(daveConnection * dc,daveBlockTypeEntry * buf);
    /*
        PLC program read functions:
    */
     int  initUpload(daveConnection * dc,char blockType, int blockNr, int * uploadID);
     int  doUpload(daveConnection*dc, int * more, uc**buffer, int*len, int uploadID);
     int  endUpload(daveConnection*dc, int uploadID);
    /*
        Multiple variable support:
    */
    typedef struct {
        int error;
        int length;
        uc * bytes;
    } daveResult;

    typedef struct {
        int numResults;
        daveResult * results;
    } daveResultSet;

    /* use this to initialize a multivariable read: */
     void  davePrepareReadRequest(daveConnection * dc, PDU *p);
    /* Adds a new variable to a prepared request: */
     void  daveAddVarToReadRequest(PDU *p, int area, int DBnum, int start, int bytes);
    /* Executes the complete request. */
     int  daveExecReadRequest(daveConnection * dc, PDU *p, daveResultSet * rl);
    /* Lets the functions daveGet work on the n-th result: */
     int  daveUseResult(daveConnection * dc, daveResultSet rl, int n);
    /* Frees the memory occupied by the result structure */
     void  daveFreeResults(daveResultSet * rl);

     int  daveInitAdapter(daveInterface * di);
     int  daveConnectPLC(daveConnection * dc);
     int  daveDisconnectPLC(daveConnection * dc);

     int  daveDisconnectAdapter(daveInterface * di);
     int  daveListReachablePartners(daveInterface * di,char * buf);

     int  _daveInitAdapterDummy(daveInterface * di);
     int  _daveConnectPLCDummy(daveConnection * dc);
     int  _daveDisconnectPLCDummy(daveConnection * dc);
     int  _daveDisconnectAdapterDummy(daveInterface * di);
     int  _daveExchangeDummy(daveConnection * dc,PDU * p1);
     int  _daveListReachablePartnersDummy(daveInterface * di,char * buf);

    /* MPI specific functions */

    #define daveMPIReachable 0x30
    #define daveMPIunused 0x10
    #define davePartnerListSize 126

     int  _daveListReachablePartnersMPI(daveInterface * di,char * buf);
     int  _daveInitAdapterMPI1(daveInterface * di);
     int  _daveInitAdapterMPI2(daveInterface * di);
     int  _daveConnectPLCMPI1(daveConnection * dc);
     int  _daveConnectPLCMPI2(daveConnection * dc);
     int  _daveDisconnectPLCMPI(daveConnection * dc);
     int  _daveDisconnectAdapterMPI(daveInterface * di);
     int  _daveExchangeMPI(daveConnection * dc,PDU * p1);

    /* ISO over TCP specific functions */
     int  _daveExchangeTCP(daveConnection * dc,PDU * p1);
     int  _daveConnectPLCTCP(daveConnection * dc);
    /*
        make internal PPI functions available for experimental use:
    */
     int  _daveExchangePPI(daveConnection * dc,PDU * p1);
     void  _daveSendYOURTURN(daveConnection * dc);
     void  _daveSendLength(daveInterface * di, int len);
     void  _daveSendIt(daveInterface * di, uc * b, int size);
     int  _daveReadChars(daveInterface * di,	uc *b, int tmo, int max);

    /*
        make internal MPI functions available for experimental use:
    */
     int  _daveReadMPI(daveInterface * di, uc *b);
     void  _daveSendSingle(daveInterface * di, uc c);
     int  _daveSendAck(daveConnection * dc, int nr);
     int  _daveGetAck(daveInterface*di, int nr);
     int  _daveSendDialog2(daveConnection * dc, int size);
     int  _daveSendWithCRC(daveInterface * di, uc *b, int size);
     int  _daveReadSingle(daveInterface * di);

     int  _daveReadOne(daveInterface * di, uc *b);

    typedef uc * (*userReadFunc) (int , int, int, int, int *);
    typedef void (*userWriteFunc) (int , int, int, int, int *,uc *);
    extern userReadFunc readCallBack;
    extern userWriteFunc writeCallBack;

    void _daveConstructReadResponse(PDU * p);
    void _daveConstructWriteResponse(PDU * p);
    void _daveConstructBadReadResponse(PDU * p);
    void _daveHandleRead(PDU * p1,PDU * p2);
    void _daveHandleWrite(PDU * p1,PDU * p2);
    //void _daveSendMPIAck2(daveConnection *dc);

    #endif /* _nodave */

    /*
        Changes:
        07/19/04 added the definition of daveExchange().
        09/09/04 applied patch for variable Profibus speed from Andrew Rostovtsew.
        09/09/04 applied patch from Bryan D. Payne to make this compile under Cygwin and/or newer gcc.
        12/09/04 added daveReadBits(), daveWriteBits()
        12/09/04 added some more comments.
        12/09/04 changed declaration of _daveMemcmp to use typed pointers.
    */