Files
sics/ascon.i

200 lines
8.0 KiB
OpenEdge ABL

#ifndef ASCON_I
#define ASCON_I
#include <sys/time.h>
#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 ("<host>:<port>" [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