From 9298401846acd69925430637dd15896a66585aef Mon Sep 17 00:00:00 2001 From: Douglas Clowes Date: Thu, 15 Feb 2007 07:53:09 +1100 Subject: [PATCH] Add MultiChannelController a controller with multiple channels or units on a single network connection. r1487 | dcl | 2007-02-15 07:53:09 +1100 (Thu, 15 Feb 2007) | 2 lines --- multichan.c | 275 ++++++++++++++++++++++++++++++++++++++++++++++++++++ multichan.h | 35 +++++++ 2 files changed, 310 insertions(+) create mode 100644 multichan.c create mode 100644 multichan.h diff --git a/multichan.c b/multichan.c new file mode 100644 index 00000000..0776261d --- /dev/null +++ b/multichan.c @@ -0,0 +1,275 @@ +/* + * M U L T I C H A N + * + * This module manages communications on a multi-channel controller. + * + * A multi-channel controller is one where multiple channels or units are on a + * single controller and share a single command channel. + * + * Douglas Clowes, February 2007 + * + */ + +#include +#include +#include +#include "network.h" +#include "multichan.h" +#include "nwatch.h" + +typedef struct __MultiChanController MultiChanController, *pMultiChanController; + +typedef struct __mcc_command MC_Cmd, *pMC_Cmd; + +struct __mcc_command { + pMC_Cmd next; + void* cntx; + MCC_Transmit tx; + MCC_Receive rx; + pMultiChan unit; +}; + +struct __MultiChan { + pMultiChan next; + pMultiChanController mcc; + MCC_Notify notify_func; + void* notify_cntx; + int timeout; +}; + +struct __MultiChanController { + int unit_count; /* number of units connected */ + pMultiChan units; /* head of unit chain */ + prs232 controller; /* associated RS232 controller object */ + pMC_Cmd command_head; /* first/next command in queue */ + pMC_Cmd command_tail; /* last command in queue */ + pNWContext nw_ctx; /* NetWait context handle */ + pNWTimer nw_tmr; /* NetWait timer handle */ +}; + +static pMultiChanController mcc_array[FD_SETSIZE]; +static mcc_index = 0; + +static int MC_Notify(pMultiChanController self, int event) +{ + pMultiChan unit; + for (unit = self->units; unit; unit = unit->next) + if (unit->notify_func != NULL) + unit->notify_func(unit, event); +} + +static int MC_Reconnect(pMultiChanController self) +{ + int iRet; + int sock; + int flag = 1; + + iRet = NETReconnect(self->controller->pSock); + if (iRet <= 0) { + MC_Notify(self, MCC_DISCONNECT); + return iRet; + } + sock = self->controller->pSock->sockid; + iRet = setsockopt(sock, /* socket */ + IPPROTO_TCP, /* set option at TCP level */ + TCP_NODELAY, /* name of option */ + (char *) &flag, /* the cast is historical cruft */ + sizeof(int)); /* length of option value */ + MC_Notify(self, MCC_RECONNECT); + return 1; +} + + +static int CommandTimeout(void* cntx, int mode); + +static int StartCommand(pMultiChanController self) +{ + pMC_Cmd myCmd = self->command_head; + + if (self->nw_tmr) + NetWatchRemoveTimer(self->nw_tmr); + if (myCmd->unit->timeout > 0) + NetWatchRegisterTimer(&self->nw_tmr, myCmd->unit->timeout * 1000, + CommandTimeout, self); + else + NetWatchRegisterTimer(&self->nw_tmr, 1000000, + CommandTimeout, self); + return myCmd->tx(myCmd->cntx); +} + +static int CommandTimeout(void* cntx, int mode) +{ + pMultiChanController self = (pMultiChanController) cntx; + self->nw_tmr = 0; + StartCommand(self); +} + +static int QueCommand(pMultiChanController self, pMC_Cmd cmd) +{ + /* + * If the command queue is empty, start transmission + */ + if (self->command_head == NULL) { + self->command_head = self->command_tail = cmd; + StartCommand(self); + } else { + self->command_tail->next = cmd; + self->command_tail = cmd; + } +} + +static int PopCommand(pMultiChanController self) +{ + pMC_Cmd myCmd = self->command_head; + if (self->nw_tmr) + NetWatchRemoveTimer(self->nw_tmr); + self->nw_tmr = 0; + /* + * If this is not the last in queue, start transmission + */ + if (myCmd->next) { + pMC_Cmd pNew = myCmd->next; + self->command_head = pNew; + StartCommand(self); + } + else + self->command_head = self->command_tail = NULL; + free(myCmd); +} + +static int MyCallback(void* context, int mode) +{ + pMultiChanController self = (pMultiChanController) context; + + if (mode & nwatch_read) { + int iRet; + char reply[1]; + + iRet = NETRead(self->controller->pSock, reply, 1, 0); + if (iRet < 0) { /* TODO: EOF */ + iRet = MC_Reconnect(self); + if (iRet <= 0) + return iRet; + /* restart the command */ + StartCommand(self); + return 1; + } + if (iRet == 0) { /* TODO: timeout or error */ + return 0; + } else { + pMC_Cmd myCmd = self->command_head; + if (myCmd) { + iRet = myCmd->rx(myCmd->cntx, reply[0]); + if (iRet < 0) /* TODO: error */ + ; + else if (iRet == 0) /* end of command */ + PopCommand(self); + } + } + } + return 1; +} + + +int MultiChanCreate(const char* name, pMultiChan* handle) +{ + int i; + prs232 ctlr = NULL; + pMultiChanController self = NULL; + pMultiChan unit = NULL; + + *handle = NULL; + + ctlr = (prs232)FindCommandData(pServ->pSics, name, "RS232 Controller"); + if(ctlr == NULL || ctlr->pSock == NULL) { + return 0; + } + for (i = 0; i < mcc_index; ++i) + if (mcc_array[i]->controller == ctlr) { + self = mcc_array[i]; + break; + } + if (self == NULL) { + self = (pMultiChanController) malloc(sizeof(MultiChanController)); + /* TODO check malloc */ + if (self == NULL) + return 0; + memset(self, 0, sizeof(MultiChanController)); + mcc_array[mcc_index++] = self; + self->controller = ctlr; + NetWatchRegisterCallback(&self->nw_ctx, + self->controller->pSock->sockid, + MyCallback, + self); + } + ++self->unit_count; + unit = (pMultiChan) malloc(sizeof(MultiChan)); + /* TODO: check malloc failure */ + memset(unit, 0, sizeof(MultiChan)); + unit->mcc = self; + unit->next = self->units; + self->units = unit; + *handle = unit; + return 1; +} + +int MultiChanDestroy(pMultiChan chan) +{ + assert(chan); + assert(chan->mcc); + pMultiChanController self = chan->mcc; + pMultiChan* pNxt = &self->units; + while (*pNxt) { + if (*pNxt == chan) { + *pNxt = (*pNxt)->next; + break; + } + pNxt = &(*pNxt)->next; + } + --self->unit_count; + if (self->unit_count <= 0) { + int i; + for (i = 0; i < mcc_index; ++i) + if (mcc_array[i] == self) { + --mcc_index; + if (mcc_index > 0) + mcc_array[i] = mcc_array[mcc_index]; + if (self->nw_ctx) + NetWatchRemoveCallback(self->nw_ctx); + if (self->nw_tmr) + NetWatchRemoveTimer(self->nw_tmr); + free(self); + } + } + free(chan); + return 1; +} + +int MultiChanReconnect(pMultiChan unit) +{ + int iRet; + iRet = MC_Reconnect(unit->mcc); + /* TODO: handle in-progress */ + return iRet; +} + +mkChannel* MultiChanGetSocket(pMultiChan unit) +{ + assert(unit); + assert(unit->mcc); + return unit->mcc->controller->pSock; +} + +int MultiChanEnque(pMultiChan unit, void* context, MCC_Transmit tx, MCC_Receive rx) +{ + pMC_Cmd myCmd = NULL; + + myCmd = (pMC_Cmd) malloc(sizeof(MC_Cmd)); + /* TODO: check malloc */ + memset(myCmd, 0, sizeof(MC_Cmd)); + myCmd->cntx = context; + myCmd->tx = tx; + myCmd->rx = rx; + myCmd->unit = unit; + return QueCommand(unit->mcc, myCmd); +} diff --git a/multichan.h b/multichan.h new file mode 100644 index 00000000..0d73d4fe --- /dev/null +++ b/multichan.h @@ -0,0 +1,35 @@ +/* + * M U L T I C H A N + * + * This module manages communications on a multi-channel controller. + * + * A multi-channel controller is one where multiple channels or units are on a + * single controller and share a single command channel. + * + * Douglas Clowes, February 2007 + * + */ +#ifndef SICSMULTICHAN +#define SICSMULTICHAN + +typedef struct __MultiChan MultiChan, *pMultiChan; + +int MultiChanCreate(const char* controller, pMultiChan* handle); +int MultiChanDestroy(pMultiChan handle); + +int MultiChanReconnect(pMultiChan handle); + +mkChannel* MultiChanGetSocket(pMultiChan handle); + +typedef (*MCC_Transmit)(void* context); +typedef (*MCC_Receive)(void* context, int ch); +int MultiChanEnque(pMultiChan unit, void* context, MCC_Transmit tx, MCC_Receive rx); + +typedef (*MCC_Notify)(void* context, int event); +void MultiChanSetNotify(pMultiChan unit, void* context, MCC_Notify notify); +#define MCC_DISCONNECT 101 +#define MCC_RECONNECT 102 + +int MultiChanGetTimeout(pMultiChan unit); +int MultiChanSetTimeout(pMultiChan unit, int timeout); +#endif /* SICSMULTICHAN */