/* * 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 { pObjectDescriptor pDes; char* pHost; int iPort; 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 int mcc_index = 0; static void 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; mkChannel* sock = self->controller->pSock; /* * Remove any old command timeout timer */ if (self->nw_tmr) NetWatchRemoveTimer(self->nw_tmr); /* * Discard any input before sending command */ while (NETAvailable(sock, 0)) { char reply[1]; int iRet; iRet = NETRead(sock, reply, 1, 0); if (iRet < 0) { /* TODO: EOF */ iRet = MC_Reconnect(self); if (iRet == 0) return 0; } } /* * Add a new command timeout timer */ if (myCmd->unit->timeout > 0) NetWatchRegisterTimer(&self->nw_tmr, myCmd->unit->timeout, CommandTimeout, self); else NetWatchRegisterTimer(&self->nw_tmr, 1000, CommandTimeout, self); return myCmd->tx(myCmd->cntx); } static int CommandTimeout(void* cntx, int mode) { pMultiChanController self = (pMultiChanController) cntx; self->nw_tmr = 0; StartCommand(self); return 1; } 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; } return 1; } 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); return 1; } 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 MultiChanReconnect(pMultiChan unit) { int iRet; assert(unit); assert(unit->mcc); 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); } int MultiChanWrite(pMultiChan unit, void* buffer, int buflen) { int iRet; mkChannel* sock; assert(unit); assert(unit->mcc); sock = MultiChanGetSocket(unit); iRet = NETWrite(sock, buffer, buflen); /* TODO handle errors */ if (iRet < 0) { /* TODO: EOF */ iRet = MC_Reconnect(unit->mcc); if (iRet == 0) return 0; } return 1; } void MultiChanSetNotify(pMultiChan unit, void* context, MCC_Notify notify) { assert(unit); unit->notify_func = notify; unit->notify_cntx = context; } int MultiChanGetTimeout(pMultiChan unit) { assert(unit); return unit->timeout; } int MultiChanSetTimeout(pMultiChan unit, int timeout) { int old_timeout; assert(unit); old_timeout = unit->timeout; unit->timeout = timeout; return old_timeout; } static int MC_Action(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { pMultiChanController self = (pMultiChanController) pData; /* TODO: handle private stuff */ return RS232Action(pCon, pSics, self->controller, argc, argv); } static pMultiChanController MC_Create(const char* host, int port) { int i; pMultiChanController self = NULL; prs232 ctlr = NULL; /* try to find the MCC with this name */ self = (pMultiChanController)FindCommandData(pServ->pSics, host, "MultiChan"); if (self == NULL) { ctlr = (prs232)FindCommandData(pServ->pSics, host, "RS232 Controller"); if(ctlr == NULL || ctlr->pSock == NULL) { return NULL; } 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)); if (self == NULL) return NULL; memset(self, 0, sizeof(MultiChanController)); self->controller = ctlr; self->pDes = CreateDescriptor("MultiChan"); mcc_array[mcc_index++] = self; } } else { for (i = 0; i < mcc_index; ++i) if (mcc_array[i] == self) { break; } if (i == mcc_index) mcc_array[mcc_index++] = self; } return self; } static int MC_Init(pMultiChanController self) { /* TODO: Init the controller */ if (self->nw_ctx == NULL) NetWatchRegisterCallback(&self->nw_ctx, self->controller->pSock->sockid, MyCallback, self); return 1; } static void MC_Kill(pMultiChanController self) { 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); return; } } int MultiChanFactory(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]) { pMultiChanController pNew = NULL; int iRet, status; char pError[256]; if(argc < 4) { SCWrite(pCon,"ERROR: insufficient no of arguments to MultiChanFactory", eError); return 0; } /* create data structure and open port */ pNew = MC_Create(argv[2], atoi(argv[3])); if(!pNew) { SCWrite(pCon,"ERROR: failed to create MultiChan in MultiChanFactory",eError); return 0; } status = MC_Init(pNew); if(status != 1) { sprintf(pError,"ERROR: failed to connect to %s at port %d", pNew->pHost, pNew->iPort); SCWrite(pCon,pError,eError); } /* create the command */ iRet = AddCommand(pSics, argv[1], MC_Action, MC_Kill, pNew); if(!iRet) { sprintf(pError,"ERROR: duplicate command %s not created", argv[1]); SCWrite(pCon,pError,eError); MC_Kill(pNew); return 0; } return 1; } int MultiChanCreate(const char* name, pMultiChan* handle) { int status; pMultiChanController self = NULL; pMultiChan unit = NULL; *handle = NULL; self = MC_Create(name, 0); if (self == NULL) return 0; status = MC_Init(self); unit = (pMultiChan) malloc(sizeof(MultiChan)); /* TODO: check malloc failure */ memset(unit, 0, sizeof(MultiChan)); ++self->unit_count; 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) { MC_Kill(self); } free(chan); return 1; }