Files
sics/site_ansto/anstohttp.c
Ferdi Franceschini 39497e2810 SICS-638 Send a pause to the histmem when SICS receives an INT1712 interrupt.
This is done by mapping AnstoHttpPauseNoCon() to Halt in the histmem interface.
An ANSTO stop command astop has been added to send a Stop to the histmem which
is needed when uploading a new configuration.
2013-09-10 14:39:03 +10:00

1047 lines
36 KiB
C

/*
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 (original SINQ version)
Mark Lesha, 9 October 2006 (ANSTO version)
----------------------------------------------------------------------*/
#include <stdlib.h>
#include <assert.h>
#include <HistDriv.i>
#include <tcl.h>
#include <ghttp.h>
#include <stptok.h>
#include <countdriv.h>
#include <nserver.h>
#include <sys/time.h>
#include <HistMem.i>
extern char *trim(char *);
extern pICallBack statemon_cbinterface;
int ANSTO_HTTP_PAUSE = 0;
int AnstoHttpPause(pHistDriver self,SConnection *pCon);
/*===================================================================
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 vetodaq[] = {"/admin/guienablesoftveto.egi"};
static char novetodaq[] = {"/admin/guidisablesoftveto.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 dynamicsrvmodify[] = {"/admin/selectdynamicsrvmodify.egi?dynamicSRVmodifyparamname=%s&dynamicSRVmodifyparamvalue=%s"};
static char dynamicfatmodify[] = {"/admin/selectdynamicfatmodify.egi?dynamicFATmodifyparamname=%s&dynamicFATmodifyparamvalue=%s"};
static char dynamicxatmodify[] = {"/admin/selectdynamicxatmodify.egi?dynamicxATmodifyparamname=%s&dynamicxATmodifyparamvalue=%s"};
static char devName[32] = "histmem";
/*====================================================================
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
/*=====================================================================
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;
// Keep a local copy of the dictionary pointer in our internal data structure.
// We initialize this pointer with the one passed in when the object is created.
// This is used to allow certain functions (like AnstoHttpGetHistogram) to access
// the dictionary, even though a pointer to the dictionary isn't passed in.
// Hopefully this will not have any side effects.
pStringDict pOption;
pICallBack pCall;
}anstoHttp, *pAnstoHttp;
/*------------------------------------------------------------------*/
static int anstoHttpGetPrepare(pAnstoHttp 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 anstoHttpCheckResponse(pAnstoHttp self){
char *pPtr = NULL;
int len;
self->failCount = 0;
pPtr = ghttp_get_body(self->syncRequest);
if(pPtr == NULL) // MJL check ghttp_get_body for NULL return
return 1; // empty response is okay
len = ghttp_get_body_len(self->syncRequest);
if(len > 511){
len = 510;
}
if(strstr(pPtr,"ERROR") != NULL){
if(strstr(pPtr,"unknown caller") != NULL){
//ffr Ignore this error so we can use the guienablesoftveto
// and guidisablesoftveto controls.
return 1;
}
memset(self->hmError,0,512*sizeof(char));
strncpy(self->hmError,pPtr, len);
self->errorCode = HTTPERROR;
return 0;
} else if(strstr(pPtr,"Authentication Error") != NULL){
memset(self->hmError,0,512*sizeof(char));
strncpy(self->hmError,pPtr, len);
self->errorCode = BADAUTH;
return 0;
}
return 1;
}
/*-------------------------------------------------------------------*/
static int anstoHttpGet(pAnstoHttp self, char *request){
ghttp_status httpStatus;
if(!anstoHttpGetPrepare(self,request)){
return 0;
}
/*
* try two times: a reconnect is no error
*/
ghttp_prepare(self->syncRequest);
httpStatus = ghttp_process(self->syncRequest);
if(httpStatus != ghttp_done){
ghttp_close(self->syncRequest);
if (!anstoHttpGetPrepare(self,request))
return 0; // MJL return here if failed
ghttp_prepare(self->syncRequest); // MJL be sure to call ghttp_prepare before each ghttp_process
httpStatus = ghttp_process(self->syncRequest);
}
if(httpStatus != ghttp_done){
strncpy(self->hmError,"Reconnect", 511);
self->errorCode = SERVERERROR;
return 0;
} else {
return anstoHttpCheckResponse(self);
}
return 1;
}
/*====================================================================*/
static int AnstoHttpConfigure(pHistDriver self, SConnection *pCon,
pStringDict pOpt, SicsInterp *pSics){
char confCommand[512], url[512];
pAnstoHttp pPriv = NULL;
int status, iInit;
float fVal;
int i;
char *confData = NULL;
ghttp_status httpStatus;
pPriv = (pAnstoHttp)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;
}
}
/*
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);
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;
}
}
}
}
// MJL NOTE: Added extra init parameters here, these get committed
// regardless of whether we are doing the first init or not
// (i.e. will get committed even if init=1).
// Need to do init on the histogram object (e.g. 'hm init')
// in order to commit changed settings during operation.
// Specifically, this is important if FAT settings need to be
// modified during operation. To do this, the command
// 'hm configure FAT_Xxxx vvv' needs to be performed,
// where Xxxx is the name of the item in the FAT, and vvv is the desired value.
// If the requested FAT variable doesn't exist or cannot be modified
// during operation, the set fails but no indication is given.
//
// Now, to find out what entries are FAT_Xxxx format,
/// by traversing the dictionary list.
char pValue[256];
const char *pItem=NULL;
do {
pItem=StringDictGetNext(pOpt,pValue,256);
if (pItem)
{
// Make special http request to set the SRV or FAT parameter
char modify_http_request[1024]="";
if (strncasecmp(pItem,"SRV_",4)==0)
sprintf(modify_http_request,dynamicsrvmodify,pItem+4,pValue);
else if (strncasecmp(pItem,"FAT_",4)==0)
sprintf(modify_http_request,dynamicfatmodify,pItem+4,pValue);
// MJL 3/12 General (hierachical) xAT section attribute or table element e.g. CAT:MESYTEC_MPSD8_THRESHOLDS.0
else if (pItem[0]&&strncasecmp(pItem+1,"AT:",3)==0) // e.g. FAT: BAT: OAT: CAT: etc.
sprintf(modify_http_request,dynamicxatmodify,pItem,pValue); // preserve full hierachical prefix (don't strip)
// Try committing the setting to the histogram server
if (*modify_http_request)
{
// Send the request. When one doesn't work, drop out of the loop.
int status = anstoHttpGet(pPriv,modify_http_request);
if(status != 1)
return 0;
}
}
} while(pItem);
return 1;
}
/*--------------------------------------------------------------------*/
static int readStatus(pHistDriver pDriv){
char *pPtr = NULL, *pLinePtr;
char line[132];
char name[80], value[80];
pAnstoHttp self = NULL;
self = (pAnstoHttp)pDriv->pPriv;
assert(self != NULL);
pPtr = ghttp_get_body(self->syncRequest);
if(pPtr == NULL){
strncpy(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;
}
/*---------------------------------------------------------------------*/
// The status code now allows pCon==NULL to be passed in.
// This allows the status to be checked from all the start, stop, pause
// and continue callbacks, so we can wait for the server to actually
// change to the correct operating state before continuing.
//
// Define codes for more detailed status info
// (returned via pextrastatus instead of retcode which is just HWBusy or HWIdle).
// These are needed so we can tell when a start/stop/pause operation actually completes.
#define ANSTO_HS_STATUS_INVALID 0 // e.g. couldn't retrieve a status because server is down
#define ANSTO_HS_STATUS_STARTED 1
#define ANSTO_HS_STATUS_PAUSED 2
#define ANSTO_HS_STATUS_STOPPED 3
#define ANSTO_HS_STATUS_STARTING 4
#define ANSTO_HS_STATUS_PAUSING 5
#define ANSTO_HS_STATUS_STOPPING 6
#define ANSTO_HS_STATUS_UNKNOWN 7
#define ANSTO_HS_STATUS_REDO 8
//
static int AnstoHttpStatus_Base(pHistDriver self,SConnection *pCon,int *pextrastatus){ // pCon=NULL allowed
pAnstoHttp pPriv = NULL;
ghttp_status httpStatus;
char daqStatus[20];
int status;
pAnstoHttp pInternal = NULL;
static int last_known_status=HWIdle; // assume idle initially
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
// MJL for the ANSTO histogram server we STILL need status checking to occur
// even when in paused mode (our pause mode has a different functionality).
// So the code below is removed.
/* ffr reenabled because we now have AnstoHttpVeto()*/
if(pPriv->pause == 1){
return HWPause;
}
if(pPriv->asyncRunning == 0){
status = anstoHttpGetPrepare(pPriv,statusdaq);
ghttp_set_sync(pPriv->syncRequest,ghttp_async);
ghttp_prepare(pPriv->syncRequest);
if(status != 1){
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
return HWFault;
}
pPriv->asyncRunning = 1;
}
httpStatus = ghttp_process(pPriv->syncRequest);
switch(httpStatus){
case ghttp_error:
ghttp_close(pPriv->syncRequest);
pPriv->asyncRunning = 0; // MJL bug fix 9/03/07 moved by DFC
anstoHttpGetPrepare(pPriv,statusdaq);
ghttp_prepare(pPriv->syncRequest);
httpStatus = ghttp_process(pPriv->syncRequest);
if(httpStatus != ghttp_done){
strncpy(pPriv->hmError,"Reconnect", 511);
pPriv->errorCode = SERVERERROR;
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
return HWFault;
}
break;
case ghttp_not_done:
// MJL this is not a problem, the server is just slow.
// return HWRedo not HWBusy (original) or fault
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_REDO;
return HWRedo;
break;
case ghttp_done:
pPriv->asyncRunning = 0;
status = anstoHttpCheckResponse(pPriv);
if(status != 1){
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
return HWFault;
}
break;
}
status = readStatus(self);
if(status != 1){
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
return HWFault;
}
if(StringDictGet(self->pOption,"daq",daqStatus,20) != 1){
pPriv->errorCode = BADSTATUS;
strncpy(pPriv->hmError,"ERROR: status does not contain DAQ field",511);
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
return HWFault;
}
///if (pCon) SCWrite(pCon,daqStatus,eError); // MJL DEBUG
//
// Basically we just diagnose whether the DAQ is running or not,
// but also return more detailed status via pextrastatus if supplied.
pInternal = (pAnstoHttp) self->pPriv;
if(strstr(daqStatus,"Started") != NULL){
if (last_known_status != HWBusy)
InvokeCallBack(statemon_cbinterface, STSTART, devName);
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_STARTED;
return last_known_status=HWBusy;
} else if(strstr(daqStatus,"Paused") != NULL){
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_PAUSED;
return last_known_status=HWIdle;
} else if(strstr(daqStatus,"Stopped") != NULL){
InvokeCallBack(statemon_cbinterface, STEND, devName);
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_STOPPED;
return last_known_status=HWIdle;
//
// The following are extra statuses which may
// (in the next histogram server version) be reported
// by the server, when the DAQ state is changing.
// As some DAE types are slow to start up, this
// is a valuable feature.
//
// Try to distinguish the HWBusy and HWIdle states
// based on the last known status. For example,
// when Stopping from Paused state it should report
// HWBusy if it was in Started state to begin with,
// otherwise HWIdle if it was in Paused or Stopped states.
} else if(strstr(daqStatus,"Starting") != NULL){
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_STARTING;
return last_known_status; // if started already, is already HWBusy
} else if(strstr(daqStatus,"Stopping") != NULL){
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_STOPPING;
return last_known_status; // return HWBusy only if it was started before
} else if(strstr(daqStatus,"Pausing") != NULL){
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_PAUSING;
return last_known_status; // return HWBusy if it was started before
// bad or missing status??
} else {
pPriv->errorCode = BADSTATUS;
snprintf(pPriv->hmError,511,"ERROR: invalid DAQ status %s",daqStatus);
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
return HWFault;
}
return HWFault; // shouldn't get here
}
//
static int AnstoHttpStatus(pHistDriver self,SConnection *pCon){ // pCon=NULL allowed
return AnstoHttpStatus_Base(self,pCon,NULL); // pCon=NULL allowed
}
#define STATUS_READ_DELAY_USEC 200000 // re-check once per 0.2 second if possible
#define MAX_STATUS_READ_RETRIES 150 // wait up to 30 secs for status to change to desired state
#define MAX_BAD_STATUS_REQUESTS 3 // don't wait too long if too many bad status requests (probably server is down)
//
long long get_localtime_us()
// Get the time in us not in the US ;)
{
struct timeval tv;
gettimeofday(&tv,NULL);
return ((long long)tv.tv_sec)*1000000+tv.tv_usec;
}
//
static int AnstoHttpStatusWithRetries(pHistDriver self, int requiredstate,SConnection *pCon) // pCon=NULL allowed
{
int retries=0,initialentry=1,bad_status_requests=0;
int retcode,runloop;
do
{
int extrastatus=ANSTO_HS_STATUS_UNKNOWN;
// Request current status from the histogram server.
// The server usually responds within milliseconds.
retcode=AnstoHttpStatus_Base(self,pCon,&extrastatus);
retries++;
bad_status_requests+=(extrastatus==ANSTO_HS_STATUS_INVALID);
runloop=(retries<MAX_STATUS_READ_RETRIES)&&(initialentry||extrastatus!=requiredstate)
&&(bad_status_requests<MAX_BAD_STATUS_REQUESTS);
// Retry after a delay if status hasn't changed yet
// (DAE takes time to start up / shut down)
if (retcode != HWPause && runloop) // Only wait if we're going to loop, otherwise drop out immediately
{
long long start_time_us=get_localtime_us();
do // make sure the yield in this loop gets called at least once
{
// Service other SICS tasks while we wait.
pTaskMan pTasker = GetTasker();
/* Don't run the taskloop during initialisation */
if (pTasker && (pServ->dummyCon != NULL))
TaskYield(pTasker);
} while(get_localtime_us()<(start_time_us+STATUS_READ_DELAY_USEC));
}
initialentry=0;
} while(runloop);
return retcode;
}
/*--------------------------------------------------------------------*/
static int AnstoHttpStart(pHistDriver self, SConnection *pCon){
pAnstoHttp pPriv = NULL;
int status;
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
status = anstoHttpGet(pPriv,startdaq);
if(status != 1){
return HWFault;
}
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_STARTED,pCon);
return OKOK;
}
/*---------------------------------------------------------------------*/
static int AnstoHttpHalt(pHistDriver self){ // hmm, why isn't there a pCon like all the other ones...?
pAnstoHttp pPriv = NULL;
int status;
char checkstatus[20]="false";
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
status = anstoHttpGet(pPriv,stopdaq);
if(status != 1){
return HWFault;
}
//TODO check statuscheck flag
StringDictGet(self->pOption,"statuscheck",checkstatus,20);
if (strcmp(checkstatus,"true") == 0) {
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_STOPPED,NULL); // no pCon available :(
}
return OKOK;
}
/*---------------------------------------------------------------------*/
static int AnstoHttpVeto(pHistDriver self,SConnection *pCon)
{
pAnstoHttp pPriv = NULL;
int status;
char daqStatus[20];
if (ANSTO_HTTP_PAUSE) {
ANSTO_HTTP_PAUSE = 0;
return AnstoHttpPause(self, pCon);
}
StringDictGet(self->pOption,"daq",daqStatus,20);
if(strstr(daqStatus,"Stopped") != NULL){
return OKOK;
}
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
status = anstoHttpGet(pPriv,vetodaq);
if(status != 1){
return HWFault;
}
pPriv->pause = 1;
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_PAUSED,pCon);
InvokeCallBack(statemon_cbinterface, STPAUSE, devName);
return OKOK;
}
static int AnstoHttpNoVeto(pHistDriver self,SConnection *pCon)
{
pAnstoHttp pPriv = NULL;
int status;
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
status = anstoHttpGet(pPriv,novetodaq); // which is the same as restarting
if(status != 1){
return HWFault;
}
pPriv->pause = 0;
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_STARTED,pCon);
InvokeCallBack(statemon_cbinterface, STCONTINUE, devName);
return OKOK;
}
int AnstoHttpPause(pHistDriver self,SConnection *pCon){
pAnstoHttp pPriv = NULL;
int status;
/* ffr revert to R2.3
char daqStatus[20];
StringDictGet(self->pOption,"daq",daqStatus,20);
if(strstr(daqStatus,"Stopped") != NULL){
return OKOK;
}
*/
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
status = anstoHttpGet(pPriv,pausedaq);
if(status != 1){
return HWFault;
}
/*ffr AnstoHttpVeto now provides pause()
pPriv->pause = 1;
*/
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_PAUSED,pCon);
return OKOK;
}
static int AnstoHttpPauseNoCon(pHistDriver self) {
return AnstoHttpPause(self, NULL);
}
/*---------------------------------------------------------------------*/
static int AnstoHttpContinue(pHistDriver self, SConnection *pCon){
pAnstoHttp pPriv = NULL;
int status;
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
status = anstoHttpGet(pPriv,continuedaq); // which is the same as restarting
if(status != 1){
return HWFault;
}
pPriv->pause = 0;
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_STARTED,pCon);
return OKOK;
}
/*---------------------------------------------------------------------*/
static int AnstoHttpError(pHistDriver self, int *code,
char *error, int errLen){
pAnstoHttp pPriv = NULL;
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
strncpy(error,pPriv->hmError, errLen);
*code = pPriv->errorCode;
return OKOK;
}
/*--------------------------------------------------------------------*/
static int AnstoHttpFixIt(pHistDriver self, int code){
pAnstoHttp pPriv = NULL;
pPriv = (pAnstoHttp)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 AnstoHttpGetData(pHistDriver self,SConnection *pCon){
/*
* do noting, monitors are with the counter, histogram memory
* caching and retrieval is handled via GetHistogram
*/
return OKOK;
}
/*-------------------------------------------------------------------*/
static int AnstoHttpGetHistogram(pHistDriver self, SConnection *pCon,
int bank, int start, int end, HistInt *data){
char command[1024]; // can be quite long now, if read_data_xxx options are appended
HistInt *hmdata;
pAnstoHttp pPriv = NULL;
int status, len, i;
// For the ANSTO Histogram Server, the transfer size may be large
// and this can lead to transmission of the data from the server in chunks.
// This is apparent from the presence of 'xxxxx; chunk length nnnnnn' in the
// data when the transfer size is greater than 128K.
// e.g. a request for 64K elements is split into two chunks,
// and each has the header '20000; chunk length 131072'.
// However for some reason ghttp_get_body_len returns only the data size,
// even though there are header(s) added to the data.
// So, for the time being we play it safe and divide the transfer
// into chunks of less than 128KB.
//
// MJL 22/11/06: Found that AppWeb 2.1.0 supports HTTP 1.1 chunking,
// but this can be turned off by specifying HttpChunking off in the AppWeb
// config file, which is what we do currently, so the transfer size limitation code below
// is no longer needed. I've left it in with a much larger size restriction,
// just in case it's required later on for any reason (to improve transfer reliability?).
// Apparently the AppWeb 2.1.0 HTTP chunking feature is broken, because when the
// data is opened directly from the web server using an editor like KHexEdit, the open
// stalls for large data sizes unless chunking is disabled. This shows it is
// an AppWeb problem, not a ghttp problem.
// For AppWeb versions before 2.1.0, HTTP chunking is not available anyway.
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
// How this limit is set in the server is not obvious... anyway, it should not change.
// MJL 22/11/06: Turned HTTP 1.1 chunking off at the histogram server, so we no longer
// need a size restriction. Leave the code in just in case it's needed for other reasons,
// but set a larger maximum request size.
// MJL 19/3/07 Because some clients now perform requests without valid start and end parameters,
// the code below won't work properly in some cases. As a temporary fix, effectively disable it
// by setting MAX_HTTP_REQUEST_BYTES to a very large value (4GB = 1Gint).
// Note, actual transfer of such large data sizes in one hit may fail, not recommended.
//#define MAX_HTTP_REQUEST_BYTES 131072
//#define MAX_HTTP_REQUEST_BYTES 1048576
#define MAX_HTTP_REQUEST_BYTES (((long long)4096)*1048576) // max transfer size 4GB (but your mileage may vary)
// Do the HTTP data transfer in bite sized pieces
while(end>start)
{
int size=((end-start)>(MAX_HTTP_REQUEST_BYTES/sizeof(int)))
?(MAX_HTTP_REQUEST_BYTES/sizeof(int)):(end-start);
// Send traditional SICS bank,start,end parameters to the server
// bank is now ignored by the server though, and start and end
// may be overridden by supplementary settings (see below).
int ncommand=snprintf(command,1023,"%s?bank=%d&start=%d&end=%d",gethm,bank,
start,start+size);
// Now add specific items read_data_xxxx in the string dictionary
// to the HTTP request. These act to selectively override the
// default read-data configuration at the server, so we can select
// different dataypes and formats.
// We use the internally-stored dictionary pointer for this purpose,
// since one isn't provided in the argument list.
char *pcommand_build=command+ncommand;
char pValue[256];
const char *pItem=NULL;
do
{
pItem=StringDictGetNext(pPriv->pOption,pValue,256);
if (pItem)
{
if (strncasecmp(pItem,"read_data_",10)==0)
{
pcommand_build+=sprintf(pcommand_build,"&%s=%s",pItem,pValue);
}
}
} while(pItem);
// Send our request
status = anstoHttpGet(pPriv,command);
if(status != 1){
return HWFault;
}
len = ghttp_get_body_len(pPriv->syncRequest);
if(len < (int) (size*sizeof(int))){
pPriv->errorCode = BODYSHORT;
strncpy(pPriv->hmError,"Not enough data received from HM",511);
return HWFault;
}
hmdata = (HistInt *)ghttp_get_body(pPriv->syncRequest);
if(hmdata == NULL){ // MJL check ghttp_get_body for NULL return
pPriv->errorCode = NOBODY;
strncpy(pPriv->hmError,"No body in HM data response",511);
return HWFault;
}
// MJL removed the ntohl, our histogram server doesn't apply htonl,
// so the data arrives in LE format. (Would have to buffer data
// at the server otherwise...)
// Doubt this will raise any conpatibility issues.
for(i = 0; i < size; i++){
//data[i] = ntohl(hmdata[i]);
data[i] = hmdata[i];
}
data+=size;
start+=size;
}
return OKOK;
}
/*--------------------------------------------------------------------*/
static int AnstoHttpSetHistogram(pHistDriver self, SConnection *pCon,
int bank, int start, int end, HistInt *data){
pAnstoHttp pPriv = NULL;
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
pPriv->errorCode = NOTIMPLEMENTED;
strncpy(pPriv->hmError,"Not implemented",511);
return HWFault;
}
/*---------------------------------------------------------------------*/
static long AnstoHttpGetMonitor(pHistDriver self, int i,
SConnection *pCon){
return 0;
}
/*---------------------------------------------------------------------*/
static float AnstoHttpGetTime(pHistDriver self, SConnection *pCon){
return -999.99;
}
/*---------------------------------------------------------------------*/
static int AnstoHttpPreset(pHistDriver self, SConnection *pCon, int val){
pAnstoHttp pPriv = NULL;
int status;
char command[512];
pPriv = (pAnstoHttp)self->pPriv;
assert(pPriv != NULL);
snprintf(command,512,"%s?value=%d",preset,val);
status = anstoHttpGet(pPriv,command);
if(status != 1){
return HWFault;
}
return 1;
}
/*---------------------------------------------------------------------*/
static int AnstoHttpFreePrivate(pHistDriver self){
pAnstoHttp pPriv = NULL;
pPriv = (pAnstoHttp)self->pPriv;
if(pPriv == NULL){
return 1;
}
if(pPriv->syncRequest != NULL){
ghttp_request_destroy(pPriv->syncRequest);
}
if (pPriv->pCall != NULL) {
DeleteCallBackInterface(pPriv->pCall);
}
free(pPriv);
return 1;
}
/*-------------------------------------------------------------------*/
pHistDriver CreateAnstoHttpDriver(pStringDict pOption){
pHistDriver pNew = NULL;
pAnstoHttp pInternal = NULL;
pICallBack pCallNew = NULL;
/* create the general driver */
pNew = CreateHistDriver(pOption);
if(!pNew){
return NULL;
}
/* add our options */
StringDictAddPair(pOption,"hmaddress","http://localhost:8080");
StringDictAddPair(pOption,"hmconfigurescript","anstohm_full.xml");
StringDictAddPair(pOption,"statuscheck","false");
/* initialise our private data structure */
pInternal = (pAnstoHttp)malloc(sizeof(anstoHttp));
if(!pInternal){
free(pNew);
return NULL;
}
memset(pInternal,0,sizeof(anstoHttp));
pNew->pPriv = pInternal;
pInternal->syncRequest = ghttp_request_new();
if(pInternal->syncRequest == NULL){
free(pNew);
free(pInternal);
return NULL;
}
pCallNew = CreateCallBackInterface();
if (!pCallNew) {
free(pNew);
free(pInternal);
return NULL;
}
// Save a pointer to the string dictionary internally,
// for the use of those functions that require it and
// don't get a pOption passed in via the argument list.
pInternal->pOption=pOption;
pInternal->pCall = pCallNew;
/* configure all those functions */
pNew->Configure = AnstoHttpConfigure;
pNew->Start = AnstoHttpStart;
pNew->Halt = AnstoHttpPauseNoCon;
pNew->GetCountStatus = AnstoHttpStatus;
pNew->GetError = AnstoHttpError;
pNew->TryAndFixIt = AnstoHttpFixIt;
pNew->GetData = AnstoHttpGetData;
pNew->GetHistogram = AnstoHttpGetHistogram;
pNew->SetHistogram = AnstoHttpSetHistogram;
pNew->GetMonitor = AnstoHttpGetMonitor;
pNew->GetTime = AnstoHttpGetTime;
pNew->Preset = AnstoHttpPreset;
pNew->FreePrivate = AnstoHttpFreePrivate;
pNew->Pause = AnstoHttpVeto;
pNew->Continue = AnstoHttpContinue;
return pNew;
}
int HistAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[]);
int ANSTO_HistAction(SConnection *pCon, SicsInterp *pSics, void *pData, int argc, char *argv[])
{
pHistMem self = NULL;
/* check arguments */
char pBueffel[512];
if(argc < 2)
{
sprintf(pBueffel,"ERROR: no arguments specified to %s, shall porvide an argument to hmm.", argv[0]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
self = (pHistMem)pData;
if(strcmp(argv[1],"astop") == 0) {
if(!SCMatchRights(pCon,usUser)) {
return 0;
}
AnstoHttpHalt(self->pDriv);
SCSendOK(pCon);
return 1;
}
if(strcmp(argv[1],"pause") == 0) {
if(!SCMatchRights(pCon,usUser)) {
return 0;
}
AnstoHttpPause(self->pDriv,pCon);
SCSendOK(pCon);
return 1;
}
if(strcmp(argv[1],"veto") == 0) {
if(!SCMatchRights(pCon,usUser)) {
return 0;
}
AnstoHttpVeto(self->pDriv,pCon);
SCSendOK(pCon);
return 1;
}
if(strcmp(argv[1],"noveto") == 0) {
if(!SCMatchRights(pCon,usUser)) {
return 0;
}
AnstoHttpNoVeto(self->pDriv,pCon);
SCSendOK(pCon);
return 1;
}
return HistAction(pCon, pSics, pData, argc, argv);
}
int ANSTO_MakeHistMemory(SConnection *pCon, SicsInterp *pSics, void *pData,
int argc, char *argv[])
{
pHistMem pNew = NULL;
char pBueffel[512];
int iRet;
/* check no of arguments */
if(argc < 3)
{
sprintf(pBueffel,"ERROR: insufficient no of arguments to %s",argv[0]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
/* make new HM */
strtolower(argv[2]);
pNew = CreateHistMemory(argv[2]);
if(!pNew)
{
sprintf(pBueffel,"ERROR: failed to create Histogram Memory %s, driver %s may be invalid or no memory",
argv[1], argv[2]);
SCWrite(pCon,pBueffel,eError);
return 0;
}
StringDictAddPair(pNew->pDriv->pOption,"name",argv[1]);
/* install HM as command */
iRet = AddCommand(pSics,argv[1],ANSTO_HistAction,DeleteHistMemory,(void *)pNew);
if(!iRet)
{
sprintf(pBueffel,"ERROR: duplicate command %s not created",argv[1]);
SCWrite(pCon,pBueffel,eError);
DeleteHistMemory((void *)pNew);
return 0;
}
return 1;
}