#ifndef ASCON_I #define ASCON_I #include #include "ascon.h" #include "dynstring.h" /** \file * \brief Asynchronous connection handling for devices controlled over tcp-ip * connections. Interface for the implementation of custom protocols. * * For the implementation of a custom protocol, you have to implement * the handler function and the init function, declare the protocol * of type AsconProtocol and call AsconInsertProtocol on startup. * The handler and init functions are normally a wrapper around AsconStdHandler * and AsconStdInit * * The functions with fd as the first argument are utility functions with * may be used in handler wrapper functions. * On error, the return value may be one of the defined macros ASCON_xxx, * and errno will give more details about the error. */ /** * The state of the connection. */ typedef enum { AsconNotConnected, /**< unconnected, not to be connected automatically */ AsconConnectStart, /**< after initialisation or after AsconFailed */ AsconConnecting, /**< after AsconConnectStart or AsconConnecting */ AsconConnectDone, /**< after AsconConnecting */ AsconWriteStart, /**< set by the AsconWrite function */ AsconWriting, /**< after AsconWriteStart or AsconWriting */ AsconWriteDone, /**< after AsconWriting */ AsconReadStart, /**< after AsconWriteDone */ AsconReading, /**< after AsconReadStart or AsconReading */ AsconReadDone, /**< after AsconReading */ AsconReadDoneReconnect, /**< after AsconReading, read success, but need to reconnect */ AsconIdle, /**< after AsconWriteDone, AsconReadDone, AsconTimeout, AsconIdle */ AsconFailed, /**< after any state */ AsconTimeout, /**< after AsconReading */ AsconMaxState /**< number of states */ } AsconState; /** \brief the task handler function prototype * * custom handlers must have this prototype */ typedef int (* AsconHandler)(Ascon *connection); /** Ascon struct * all fields are public, allowing access by handler wrappers * the fields marked with (std) are used by the standard handler * they may get other meanings in a custom handler */ struct Ascon { AsconState state; /**< the current state */ int fd; /**< socket */ int conState; /**< 1: connection refused, 0: else */ int readState; /**< (std) last char was CR */ pDynString rdBuffer; /**< read buffer */ pDynString wrBuffer; /**< write buffer */ int wrPos; /**< write buffer position */ double timeout; /**< read timeout (sec) */ char *sendTerminator; /**< terminator for sending messages */ char *replyTerminator; /**< (std) terminator list for reply. NULL is the special case CR, LF or CR/LF */ char *hostport; /**< host:port to connect */ char ip[16]; /**< the ip address (dotted numbers) */ pDynString errmsg; /**< error message */ double start; /**< unix time when read was started */ int noResponse; /**< no response expected */ int responseValid; /**< a valid response is ready */ AsconHandler handler; /**< handler function */ double reconnectInterval; /**< reconnect interval */ double lastReconnect; /**< last reconnect try */ char lastChar; /**< last char read */ char *separator; /**< (std) separator for multiline responses */ int lineCount; /**< number of lines expected (counting down) */ int compositeTerminator; /**< the terminator contains several chars */ void *private; /**< private data of protocol */ void (*killPrivate)(void *); /**< kill function for private */ }; #define ASCON_SELECT_ERROR -1 #define ASCON_RECV_ERROR -2 #define ASCON_SEND_ERROR -3 #define ASCON_DISCONNECTED -4 /** \brief the basic handler routine. * \param a the connection * \return when the state before the call was not AsconReading, the return * value should be 1 * when the state was AsconReading, a 1 indicates that a character was read * and the character is stored in the lastChar field. A 0 indicates * that no character was read. And this 0 has to be given, otherwise AsconTask * goes into an endless loop till timeout is hit! * * * In most cases a custom handler may be a wrapper around AsconBaseHandler * or around AsconStdHandler */ int AsconBaseHandler(Ascon *a); /** \brief the standard handler routine. * \param a the connection * \return see AsconBaseHandler * * features: see description of AsconStdInit */ int AsconStdHandler(Ascon *a); /** \brief initialize a standard connection * \param a the connection * \param con A connection to print errors too. * \param argc number of arguments * \param argv arguments (":" [sendTerminator] [timeout] [replyTerminators] [separator]) * sendTerminator is a character or string sent at the end of a command * timeout is in seconds * replyTerminators is a string, meant as a list of terminator characters * if no replyTerminator is given, or if it is empty, CR, LF or CR/LF all are detected * as terminators. If the terminator is CR, LF or CR/LF, it is removed from the result, * all other terminators are kept in the result. * is the first and the last character are single quotes (') is is treated as a composite * terminator and not as a list of single character terminators * separator is used for multiline responses. If this parameter * is given (and not empty) a command may be followed by a line count in curly brackets, * indicating that a multiline response is expected. All the lines of the response are * then returned, separated with "separator" * * In many cases a custom init function may be a wrapper around AsconStdInit */ int AsconStdInit(Ascon *a, SConnection *con, int argc, char *argv[]); /** The Ascon Protocol */ typedef struct AsconProtocol { struct AsconProtocol *next; char *name; AsconHandler handler; int (*init)(Ascon *s, SConnection *con, int argc, char *argv[]); } AsconProtocol; /** \brief Insert a new protocol into the protocol list * protocol the protocol (must be allocated by the caller, may be statically) */ void AsconInsertProtocol(AsconProtocol *protocol); /** \brief close the connection and free internal used memory * \param a the connection to be closed * remark: the connection struct itself has to be freed manually */ void AsconClose(Ascon *a); /** \brief swallow garbage (utility function) * \param fd the socket * \return >=0: number of chars swallowed, else error */ int AsconReadGarbage(int fd); /** \brief check if a connection has succeded (utility function) * \param fd the socket * \return 1: connection succesful, 0: connection in progress, <0: error */ int AsconConnectSuccess(int fd); /** \brief read one character, if available (utility function) * \param fd the socket * \param chr the result * \return 1: succes, 0: no data available, <0: error */ int AsconReadChar(int fd, char *chr); /** \brief non blocking write (utility function) * \param fd the socket * \param data the data (not nul-terminated, may contain nul) * \param length the length of the data * \return >0: number of written chars,0: write not yet possible, <0: error */ int AsconWriteChars(int fd, char *data, int length); /** \brief store an error * \param a The asynchronous I/O structure to store the * error with * \param msg The error message * \param errorno The error number or 0 for a custom error */ void AsconError(Ascon *a, char *msg, int errorno); int AsconInterpreteArgs(int argc, char *argv[], int parc, char *parn[], char *pars[]); /** * Treat hex strings as terminators right. Note that this * is limited to single character terminators. */ void AsconCheckTerminators(Ascon *a); #endif