/* This is a histogram memory driver for the 2005-6 version of the histogram memory software based on RTAI-Linux and an embedded WWW-server for communications. For all http work the ghttp library from the gnome project is used. This HM is meant to be used in conjunction with a counter module chained through the hmcontrol module. No need to handle counters here when hmcontrol can do the chaining. copyright: see file COPYRIGHT Mark Koennecke, January 2005 ----------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include extern char *trim(char *); /*=================================================================== The request strings to append to the computer address ====================================================================*/ static char startdaq[] = { "/admin/startdaq.egi" }; static char stopdaq[] = { "/admin/stopdaq.egi" }; static char pausedaq[] = { "/admin/pausedaq.egi" }; static char continuedaq[] = { "/admin/continuedaq.egi" }; static char statusdaq[] = { "/admin/textstatus.egi" }; static char gethm[] = { "/admin/readhmdata.egi" }; static char configure[] = { "/admin/configure.egi" }; static char preset[] = { "/admin/presethm.egi" }; static char subsample[] = { "/admin/processhmdata.egi" }; /*==================================================================== error codes ======================================================================*/ #define BADURL -701 #define HTTPERROR -702 #define NOBODY -703 #define BODYSHORT -704 #define NOTIMPLEMENTED -705 #define SERVERERROR -706 #define BADSTATUS -707 #define BADAUTH -708 #define HMNOMEMORY -709 #define BADSUBSAMPLE -710 /*===================================================================== our driver private data structure ======================================================================*/ typedef struct { ghttp_request *syncRequest; char hmAddress[512]; char userName[132]; char passWord[132]; char hmError[512]; int errorCode; int pause; int failCount; int asyncRunning; int tricsswap; } sinqHttp, *pSinqHttp; /*------------------------------------------------------------------*/ static int sinqHttpGetPrepare(pSinqHttp self, char *request) { char url[512]; ghttp_status httpStatus; if (self->asyncRunning) { while ((httpStatus = ghttp_process(self->syncRequest)) == ghttp_not_done) { } self->asyncRunning = 0; } self->errorCode = 0; ghttp_clean(self->syncRequest); memset(self->hmError, 0, 512 * sizeof(char)); snprintf(url, 511, "%s%s", self->hmAddress, request); ghttp_set_type(self->syncRequest, ghttp_type_get); ghttp_set_header(self->syncRequest, "connection", "keep-alive"); if (ghttp_set_uri(self->syncRequest, url) < 0) { self->errorCode = BADURL; return 0; } ghttp_set_authinfo(self->syncRequest, self->userName, self->passWord); ghttp_set_sync(self->syncRequest, ghttp_sync); return 1; } /*-------------------------------------------------------------------*/ static int sinqHttpCheckResponse(pSinqHttp self) { char *pPtr = NULL; int len; self->failCount = 0; pPtr = ghttp_get_body(self->syncRequest); if (pPtr == NULL) { return 1; } len = ghttp_get_body_len(self->syncRequest); if (len > 511) { len = 510; } if (strstr(pPtr, "ERROR") != NULL) { memset(self->hmError, 0, 512 * sizeof(char)); strlcpy(self->hmError, pPtr, sizeof(self->hmError)); self->errorCode = HTTPERROR; return 0; } else if (strstr(pPtr, "Authentication Error") != NULL) { memset(self->hmError, 0, 512 * sizeof(char)); strlcpy(self->hmError, pPtr, sizeof(self->hmError)); self->errorCode = BADAUTH; return 0; } return 1; } /*-------------------------------------------------------------------*/ static int sinqHttpGet(pSinqHttp self, char *request) { ghttp_status httpStatus; char *pPtr = NULL; if (!sinqHttpGetPrepare(self, request)) { return 0; } /* * try two times: a reconnect is no error */ ghttp_prepare(self->syncRequest); traceIO(self->hmAddress,"OUT:%s",request); httpStatus = ghttp_process(self->syncRequest); if (httpStatus != ghttp_done) { ghttp_close(self->syncRequest); sinqHttpGetPrepare(self, request); ghttp_prepare(self->syncRequest); httpStatus = ghttp_process(self->syncRequest); } if (httpStatus != ghttp_done) { strlcpy(self->hmError, "Reconnect", 511); self->errorCode = SERVERERROR; return 0; } else { traceIO(self->hmAddress,"IN:%s",request); return sinqHttpCheckResponse(self); } return 1; } /*====================================================================*/ static int SinqHttpConfigure(pHistDriver self, SConnection * pCon, pStringDict pOpt, SicsInterp * pSics) { char hmname[512], buffer[132]; char confCommand[512], url[512]; pSinqHttp pPriv = NULL; int status, iInit; float fVal; int i; char *confData = NULL; ghttp_status httpStatus; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); /* * The HM computer address */ if (StringDictGet(pOpt, "hmaddress", pPriv->hmAddress, 511) != 1) { SCWrite(pCon, "ERROR: required configuration parameter hmaddress not found", eError); return 0; } /* * looser credentials */ if (StringDictGet(pOpt, "username", pPriv->userName, 131) != 1) { SCWrite(pCon, "ERROR: required configuration parameter username not found", eError); return 0; } if (StringDictGet(pOpt, "password", pPriv->passWord, 131) != 1) { SCWrite(pCon, "ERROR: required configuration parameter password not found", eError); return 0; } /* actual configuration. Check for flag INIT in options. We do not need to configure, if the HM has configured itself already. What is does, these days. */ status = StringDictGetAsNumber(pOpt, "init", &fVal); iInit = 0; if (status == 1) { if (fVal > 0.9) { iInit = 1; } } status = StringDictGet(pOpt,"tricsswap", buffer, sizeof(buffer)); if(status == 1){ pPriv->tricsswap = atoi(buffer); } /* actually do configure */ if (iInit == 1) { for (i = 0; i < 2; i++) { memset(confCommand, 0, 512 * sizeof(char)); if (StringDictGet(pOpt, "hmconfigscript", confCommand, 511) != 1) { SCWrite(pCon, "ERROR: required parameter hmconfigscript not found!", eError); return 0; } status = Tcl_Eval(pSics->pTcl, confCommand); if (status != TCL_OK) { snprintf(confCommand, 511, "ERROR: Tcl reported %s while evaluating hmconfigscript", Tcl_GetStringResult(pSics->pTcl)); SCWrite(pCon, confCommand, eError); return 0; } else { /* uplod new configuration to HM */ ghttp_clean(pPriv->syncRequest); snprintf(url, 511, "%s%s", pPriv->hmAddress, configure); status = ghttp_set_uri(pPriv->syncRequest, url); if (status < 0) { SCWrite(pCon, "ERROR: invalid URI for HM request", eError); return 0; } status = ghttp_set_type(pPriv->syncRequest, ghttp_type_post); confData = (char *) Tcl_GetStringResult(pSics->pTcl); /* puts(confData); */ status = ghttp_set_body(pPriv->syncRequest, confData, strlen(confData)); ghttp_set_authinfo(pPriv->syncRequest, pPriv->userName, pPriv->passWord); ghttp_set_sync(pPriv->syncRequest, ghttp_sync); status = ghttp_prepare(pPriv->syncRequest); httpStatus = ghttp_process(pPriv->syncRequest); confData = (char *) ghttp_get_body(pPriv->syncRequest); if (httpStatus != ghttp_done) { /* we may need to reconnect..... */ if (i == 0) { ghttp_close(pPriv->syncRequest); continue; } /* * no we have a real error */ confData = (char *) ghttp_get_error(pPriv->syncRequest); snprintf(confCommand, 511, "ERROR: http error %s occurred", confData); SCWrite(pCon, confCommand, eError); return 0; } else if (confData != NULL) { if (strstr(confData, "ERROR") != NULL) { snprintf(confCommand, 511, "%s", confData); SCWrite(pCon, confCommand, eError); return 0; } if (strstr(confData, "Authentication Error") != NULL) { snprintf(confCommand, 511, "%s", confData); SCWrite(pCon, confCommand, eError); return 0; } } } } } return 1; } /*---------------------------------------------------------------------*/ static int SinqHttpPreset(pHistDriver self, SConnection * pCon, int val) { pSinqHttp pPriv = NULL; int status; char command[512]; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); snprintf(command, 512, "%s?value=%d", preset, val); status = sinqHttpGet(pPriv, command); if (status != 1) { return HWFault; } return 1; } /*--------------------------------------------------------------------*/ static int SinqHttpStart(pHistDriver self, SConnection * pCon) { pSinqHttp pPriv = NULL; int status; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); status = sinqHttpGet(pPriv, startdaq); if (status != 1) { return HWFault; } return 1; } /*---------------------------------------------------------------------*/ static int SinqHttpHalt(pHistDriver self) { pSinqHttp pPriv = NULL; int status; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); status = sinqHttpGet(pPriv, stopdaq); if (status != 1) { return HWFault; } return 1; } /*---------------------------------------------------------------------*/ static int SinqHttpPause(pHistDriver self, SConnection * pCon) { pSinqHttp pPriv = NULL; int status; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); status = sinqHttpGet(pPriv, pausedaq); if (status != 1) { return HWFault; } pPriv->pause = 1; return 1; } /*---------------------------------------------------------------------*/ static int SinqHttpContinue(pHistDriver self, SConnection * pCon) { pSinqHttp pPriv = NULL; int status; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); status = sinqHttpGet(pPriv, continuedaq); if (status != 1) { return HWFault; } pPriv->pause = 0; return 1; } /*--------------------------------------------------------------------*/ static int readStatus(pHistDriver pDriv) { char *pPtr = NULL, *pLinePtr; char line[132]; char name[80], value[80]; pSinqHttp self = NULL; char *daqPtr = NULL, *daqValPtr = NULL; self = (pSinqHttp) pDriv->pPriv; assert(self != NULL); pPtr = ghttp_get_body(self->syncRequest); if (pPtr == NULL) { strlcpy(self->hmError, "No body in status response", 131); self->errorCode = NOBODY; return 0; } pPtr = stptok(pPtr, line, 132, "\n"); while (pPtr != NULL) { pLinePtr = line; pLinePtr = stptok(pLinePtr, name, 80, ":"); pLinePtr = stptok(pLinePtr, value, 80, ":"); strtolower(name); if (StringDictExists(pDriv->pOption, trim(name)) == 1) { StringDictUpdate(pDriv->pOption, trim(name), trim(value)); } else { StringDictAddPair(pDriv->pOption, trim(name), trim(value)); } pPtr = stptok(pPtr, line, 131, "\n"); } return 1; } /*---------------------------------------------------------------------*/ static int SinqHttpStatus(pHistDriver self, SConnection * pCon) { pSinqHttp pPriv = NULL; ghttp_status httpStatus; char daqStatus[20]; int status, len; char *pPtr = NULL; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); if (pPriv->pause == 1) { return HWPause; } if (pPriv->asyncRunning == 0) { status = sinqHttpGetPrepare(pPriv, statusdaq); traceIO(pPriv->hmAddress,"OUT:%s", statusdaq); ghttp_set_sync(pPriv->syncRequest, ghttp_async); ghttp_prepare(pPriv->syncRequest); if (status != 1) { return HWFault; } pPriv->asyncRunning = 1; } httpStatus = ghttp_process(pPriv->syncRequest); switch (httpStatus) { case ghttp_error: ghttp_close(pPriv->syncRequest); sinqHttpGetPrepare(pPriv, statusdaq); ghttp_prepare(pPriv->syncRequest); httpStatus = ghttp_process(pPriv->syncRequest); if (httpStatus != ghttp_done) { strlcpy(pPriv->hmError, "Reconnect", 511); pPriv->errorCode = SERVERERROR; pPriv->asyncRunning = 0; return HWFault; } break; case ghttp_not_done: return HWBusy; break; case ghttp_done: pPriv->asyncRunning = 0; status = sinqHttpCheckResponse(pPriv); if (status != 1) { return HWFault; } traceIO(pPriv->hmAddress,"IN:%s", statusdaq); break; } status = readStatus(self); if (status != 1) { return HWFault; } if (StringDictGet(self->pOption, "daq", daqStatus, 20) != 1) { pPriv->errorCode = BADSTATUS; strlcpy(pPriv->hmError, "ERROR: status does not contain DAQ field", 511); return HWFault; } if (strstr(daqStatus, "1") != NULL) { return HWBusy; } else if (strstr(daqStatus, "0") != NULL) { return HWIdle; } else { pPriv->errorCode = BADSTATUS; snprintf(pPriv->hmError, 511, "ERROR: invalid DAQ status %s", daqStatus); return HWFault; } return HWIdle; } /*---------------------------------------------------------------------*/ static int SinqHttpError(pHistDriver self, int *code, char *error, int errLen) { pSinqHttp pPriv = NULL; int status; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); strlcpy(error, pPriv->hmError, errLen); *code = pPriv->errorCode; return 1; } /*--------------------------------------------------------------------*/ static int SinqHttpFixIt(pHistDriver self, int code) { pSinqHttp pPriv = NULL; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); /* * not much to fix here: ghttplib already tries hard to fix * connection problems... But abort any pending transactions... */ ghttp_close(pPriv->syncRequest); if (code == SERVERERROR) { pPriv->failCount++; if (pPriv->failCount > 2) { return COTERM; } else { return COREDO; } } return COTERM; } /*--------------------------------------------------------------------*/ static int SinqHttpGetData(pHistDriver self, SConnection * pCon) { /* * do noting, monitors are with the counter, histogram memory * caching and retrieval is handled via GetHistogram */ return 1; } /*------------------------------------------------------------------- * This is an ugly hack to swap TRICS data into the * correct order. */ static void swapTrics(HistInt *data, pHMdata hmdata, int length) { int x,y, xdim, ydim; HistInt *tmpData = NULL, val; /* * do nothing if no match */ xdim = hmdata->iDim[0]; ydim = hmdata->iDim[1]; if(xdim*ydim < length){ return; } tmpData = malloc(length*sizeof(HistInt)); if(tmpData == NULL){ return; } memcpy(tmpData, data, length*sizeof(HistInt)); for(y = 0; y < ydim; y++){ for(x = 0; x < xdim; x++){ val = tmpData[x*ydim + y]; data[y*xdim+x] = val; } } free(tmpData); } /*-------------------------------------------------------------------*/ static int SinqHttpGetHistogram(pHistDriver self, SConnection * pCon, int bank, int start, int end, HistInt * data) { char command[256]; HistInt *hmdata; pSinqHttp pPriv = NULL; int status, len, i; long dataSum = 0; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); snprintf(command, 255, "%s?bank=%d&start=%d&end=%d", gethm, bank, start, end); /* printf("SinqHttpGetHistogram:%d-%d\n", start,end); */ status = sinqHttpGet(pPriv, command); if (status != 1) { return HWFault; } len = ghttp_get_body_len(pPriv->syncRequest); if (len < (end - start) * sizeof(int)) { pPriv->errorCode = BODYSHORT; strlcpy(pPriv->hmError, "Not enough data received from HM", 511); return HWFault; } hmdata = (HistInt *) ghttp_get_body(pPriv->syncRequest); for (i = 0; i < (end - start); i++) { data[i] = ntohl(hmdata[i]); dataSum += data[i]; } /* printf("Read %ld counts from HM\n", dataSum); */ if(pPriv->tricsswap == 1){ swapTrics(data, self->data, end-start); } else if(pPriv->tricsswap == 2) { memcpy(data,hmdata,(end-start)*sizeof(HistInt)); } return 1; } /*-------------------------------------------------------------------*/ static HistInt *SinqHttpSubSample(pHistDriver self, SConnection * pCon, int bank, char *hmcommand) { char command[1024]; HistInt *hmdata = NULL; HistInt *resultdata = NULL; pSinqHttp pPriv = NULL; int status, len, i; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); snprintf(command, 1023, "%s?bank=%d&command=%s", subsample, bank, hmcommand); status = sinqHttpGet(pPriv, command); if (status != 1) { if(pCon != NULL){ SCPrintf(pCon,eError,"ERROR: subsampling failed with %s", pPriv->hmError); } return NULL; } len = ghttp_get_body_len(pPriv->syncRequest); if (len <= 0) { strlcpy(pPriv->hmError, "ERROR: no data returned from subsampling", 511); pPriv->errorCode = BADSUBSAMPLE; return NULL; } resultdata = malloc(len + 4); if (resultdata == NULL) { strlcpy(pPriv->hmError, "ERROR: failed to allocate buffer for subsampling results", 511); pPriv->errorCode = HMNOMEMORY; return NULL; } resultdata[0] = len / sizeof(HistInt); hmdata = (HistInt *) ghttp_get_body(pPriv->syncRequest); for (i = 0; i < len / sizeof(HistInt); i++) { resultdata[i + 1] = ntohl(hmdata[i]); } return resultdata; } /*--------------------------------------------------------------------*/ static int SinqHttpSetHistogram(pHistDriver self, SConnection * pCon, int bank, int start, int end, HistInt * data) { pSinqHttp pPriv = NULL; pPriv = (pSinqHttp) self->pPriv; assert(pPriv != NULL); pPriv->errorCode = NOTIMPLEMENTED; strlcpy(pPriv->hmError, "Not implemented", 511); return HWFault; } /*---------------------------------------------------------------------*/ static long SinqHttpGetMonitor(pHistDriver self, int i, SConnection * pCon) { return 0; } /*---------------------------------------------------------------------*/ static float SinqHttpGetTime(pHistDriver self, SConnection * pCon) { return -999.99; } /*---------------------------------------------------------------------*/ static int SinqHttpFreePrivate(pHistDriver self) { pSinqHttp pPriv = NULL; pPriv = (pSinqHttp) self->pPriv; if (pPriv == NULL) { return 1; } if (pPriv->syncRequest != NULL) { ghttp_request_destroy(pPriv->syncRequest); } free(pPriv); return 1; } /*-------------------------------------------------------------------*/ pHistDriver CreateSinqHttpDriver(pStringDict pOption) { pHistDriver pNew = NULL; pSinqHttp pInternal = NULL; /* create the general driver */ pNew = CreateHistDriver(pOption); if (!pNew) { return NULL; } /* add our options */ StringDictAddPair(pOption, "hmaddress", "http://unknown.psi.ch:9090"); StringDictAddPair(pOption, "hmconfigurescript", "hmconfigure"); /* initialise our private data structure */ pInternal = (pSinqHttp) malloc(sizeof(sinqHttp)); if (!pInternal) { free(pNew); return NULL; } memset(pInternal, 0, sizeof(sinqHttp)); pNew->pPriv = pInternal; pInternal->syncRequest = ghttp_request_new(); if (pInternal->syncRequest == NULL) { free(pNew); free(pInternal); return NULL; } /* configure all those functions */ pNew->Configure = SinqHttpConfigure; pNew->Start = SinqHttpStart; pNew->Halt = SinqHttpHalt; pNew->GetCountStatus = SinqHttpStatus; pNew->GetError = SinqHttpError; pNew->TryAndFixIt = SinqHttpFixIt; pNew->GetData = SinqHttpGetData; pNew->GetHistogram = SinqHttpGetHistogram; pNew->GetHistogram = SinqHttpGetHistogram; pNew->SubSample = SinqHttpSubSample; pNew->GetMonitor = SinqHttpGetMonitor; pNew->GetTime = SinqHttpGetTime; pNew->Preset = SinqHttpPreset; pNew->FreePrivate = SinqHttpFreePrivate; pNew->Pause = SinqHttpPause; pNew->Continue = SinqHttpContinue; return pNew; } /*-------------------------------------------------------------------------*/ int isSINQHTTP(pHistDriver self){ if(self->Start == SinqHttpStart){ return 1; } else { return 0; } }