MJL 16/3/07 Added transitional statuses, removed blocking code, improved performance under error conditions.
r1664 | mle | 2007-03-16 16:57:36 +1100 (Fri, 16 Mar 2007) | 2 lines
This commit is contained in:
committed by
Douglas Clowes
parent
f62af28efb
commit
4da6d932fb
@@ -21,7 +21,7 @@
|
|||||||
#include <ghttp.h>
|
#include <ghttp.h>
|
||||||
#include <stptok.h>
|
#include <stptok.h>
|
||||||
#include <countdriv.h>
|
#include <countdriv.h>
|
||||||
#include <zlib.h>
|
#include <nserver.h>
|
||||||
|
|
||||||
extern char *trim(char *);
|
extern char *trim(char *);
|
||||||
/*===================================================================
|
/*===================================================================
|
||||||
@@ -46,11 +46,9 @@ static char preset[] = {"/admin/presethm.egi"};
|
|||||||
#define SERVERERROR -706
|
#define SERVERERROR -706
|
||||||
#define BADSTATUS -707
|
#define BADSTATUS -707
|
||||||
#define BADAUTH -708
|
#define BADAUTH -708
|
||||||
#define IMMEDFAIL -709
|
|
||||||
/*=====================================================================
|
/*=====================================================================
|
||||||
our driver private data structure
|
our driver private data structure
|
||||||
======================================================================*/
|
======================================================================*/
|
||||||
#define MAXSTRLEN 512
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ghttp_request *syncRequest;
|
ghttp_request *syncRequest;
|
||||||
char hmAddress[512];
|
char hmAddress[512];
|
||||||
@@ -343,12 +341,25 @@ static int readStatus(pHistDriver pDriv){
|
|||||||
// This allows the status to be checked from all the start, stop, pause
|
// 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
|
// and continue callbacks, so we can wait for the server to actually
|
||||||
// change to the correct operating state before continuing.
|
// change to the correct operating state before continuing.
|
||||||
static int AnstoHttpStatus(pHistDriver self,SConnection *pCon){ // pCon=NULL allowed
|
//
|
||||||
|
// 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
|
||||||
|
//
|
||||||
|
static int AnstoHttpStatus_Base(pHistDriver self,SConnection *pCon,int *pextrastatus){ // pCon=NULL allowed
|
||||||
pAnstoHttp pPriv = NULL;
|
pAnstoHttp pPriv = NULL;
|
||||||
ghttp_status httpStatus;
|
ghttp_status httpStatus;
|
||||||
char daqStatus[20];
|
char daqStatus[20];
|
||||||
int status, len;
|
int status, len;
|
||||||
char *pPtr = NULL;
|
char *pPtr = NULL;
|
||||||
|
static int last_known_status=HWIdle; // assume idle initially
|
||||||
|
|
||||||
pPriv = (pAnstoHttp)self->pPriv;
|
pPriv = (pAnstoHttp)self->pPriv;
|
||||||
assert(pPriv != NULL);
|
assert(pPriv != NULL);
|
||||||
@@ -365,6 +376,7 @@ static int AnstoHttpStatus(pHistDriver self,SConnection *pCon){ // pCon=NULL all
|
|||||||
ghttp_set_sync(pPriv->syncRequest,ghttp_async);
|
ghttp_set_sync(pPriv->syncRequest,ghttp_async);
|
||||||
ghttp_prepare(pPriv->syncRequest);
|
ghttp_prepare(pPriv->syncRequest);
|
||||||
if(status != 1){
|
if(status != 1){
|
||||||
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
|
||||||
return HWFault;
|
return HWFault;
|
||||||
}
|
}
|
||||||
pPriv->asyncRunning = 1;
|
pPriv->asyncRunning = 1;
|
||||||
@@ -379,8 +391,9 @@ static int AnstoHttpStatus(pHistDriver self,SConnection *pCon){ // pCon=NULL all
|
|||||||
if(httpStatus != ghttp_done){
|
if(httpStatus != ghttp_done){
|
||||||
strncpy(pPriv->hmError,"Reconnect", 511);
|
strncpy(pPriv->hmError,"Reconnect", 511);
|
||||||
pPriv->errorCode = SERVERERROR;
|
pPriv->errorCode = SERVERERROR;
|
||||||
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
|
||||||
|
pPriv->asyncRunning = 0; // MJL bug fix 9/03/07
|
||||||
return HWFault;
|
return HWFault;
|
||||||
pPriv->asyncRunning = 0;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ghttp_not_done:
|
case ghttp_not_done:
|
||||||
@@ -392,6 +405,7 @@ static int AnstoHttpStatus(pHistDriver self,SConnection *pCon){ // pCon=NULL all
|
|||||||
pPriv->asyncRunning = 0;
|
pPriv->asyncRunning = 0;
|
||||||
status = anstoHttpCheckResponse(pPriv);
|
status = anstoHttpCheckResponse(pPriv);
|
||||||
if(status != 1){
|
if(status != 1){
|
||||||
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
|
||||||
return HWFault;
|
return HWFault;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -400,22 +414,53 @@ static int AnstoHttpStatus(pHistDriver self,SConnection *pCon){ // pCon=NULL all
|
|||||||
|
|
||||||
status = readStatus(self);
|
status = readStatus(self);
|
||||||
if(status != 1){
|
if(status != 1){
|
||||||
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
|
||||||
return HWFault;
|
return HWFault;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(StringDictGet(self->pOption,"daq",daqStatus,20) != 1){
|
if(StringDictGet(self->pOption,"daq",daqStatus,20) != 1){
|
||||||
pPriv->errorCode = BADSTATUS;
|
pPriv->errorCode = BADSTATUS;
|
||||||
strncpy(pPriv->hmError,"ERROR: status does not contain DAQ field",511);
|
strncpy(pPriv->hmError,"ERROR: status does not contain DAQ field",511);
|
||||||
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
|
||||||
return HWFault;
|
return HWFault;
|
||||||
}
|
}
|
||||||
///if (pCon) SCWrite(pCon,daqStatus,eError); // MJL DEBUG
|
///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.
|
||||||
if(strstr(daqStatus,"Started") != NULL){
|
if(strstr(daqStatus,"Started") != NULL){
|
||||||
return HWBusy;
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_STARTED;
|
||||||
|
return last_known_status=HWBusy;
|
||||||
} else if(strstr(daqStatus,"Paused") != NULL){
|
} else if(strstr(daqStatus,"Paused") != NULL){
|
||||||
return HWIdle;
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_PAUSED;
|
||||||
|
return last_known_status=HWIdle;
|
||||||
} else if(strstr(daqStatus,"Stopped") != NULL){
|
} else if(strstr(daqStatus,"Stopped") != NULL){
|
||||||
return HWIdle;
|
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_PAUSING;
|
||||||
|
return last_known_status; // return HWBusy only if it was started before
|
||||||
|
} else if(strstr(daqStatus,"Pausing") != NULL){
|
||||||
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_STOPPING;
|
||||||
|
return last_known_status; // return HWBusy if it was started before
|
||||||
|
// bad or missing status??
|
||||||
} else {
|
} else {
|
||||||
|
if (pextrastatus) *pextrastatus=ANSTO_HS_STATUS_INVALID;
|
||||||
pPriv->errorCode = BADSTATUS;
|
pPriv->errorCode = BADSTATUS;
|
||||||
snprintf(pPriv->hmError,511,"ERROR: invalid DAQ status %s",daqStatus);
|
snprintf(pPriv->hmError,511,"ERROR: invalid DAQ status %s",daqStatus);
|
||||||
return HWFault;
|
return HWFault;
|
||||||
@@ -423,19 +468,53 @@ static int AnstoHttpStatus(pHistDriver self,SConnection *pCon){ // pCon=NULL all
|
|||||||
|
|
||||||
return HWFault; // shouldn't get here
|
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 MAX_STATUS_READ_RETRIES 150
|
|
||||||
#define STATUS_READ_DELAY_US 200000
|
#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
|
static int AnstoHttpStatusWithRetries(pHistDriver self, int requiredstate,SConnection *pCon) // pCon=NULL allowed
|
||||||
{
|
{
|
||||||
int retries,retcode,initialentry=1;
|
int retries=0,initialentry=1,bad_status_requests=0;
|
||||||
for(retries=0;retries<MAX_STATUS_READ_RETRIES&&(initialentry||retcode!=requiredstate);retries++)
|
int retcode,runloop;
|
||||||
|
do
|
||||||
{
|
{
|
||||||
retcode=AnstoHttpStatus(self,pCon);
|
int extrastatus;
|
||||||
usleep(STATUS_READ_DELAY_US);
|
// Request current status from the histogram server.
|
||||||
initialentry=0;
|
// 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 (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();
|
||||||
|
if (pTasker)
|
||||||
|
TaskYield(pTasker);
|
||||||
|
} while(get_localtime_us()<(start_time_us+STATUS_READ_DELAY_USEC));
|
||||||
}
|
}
|
||||||
|
initialentry=0;
|
||||||
|
} while(runloop);
|
||||||
return retcode;
|
return retcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,7 +531,7 @@ static int AnstoHttpStart(pHistDriver self, SConnection *pCon){
|
|||||||
if(status != 1){
|
if(status != 1){
|
||||||
return HWFault;
|
return HWFault;
|
||||||
}
|
}
|
||||||
AnstoHttpStatusWithRetries(self,HWBusy,pCon);
|
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_STARTED,pCon);
|
||||||
return OKOK;
|
return OKOK;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------*/
|
||||||
@@ -468,7 +547,7 @@ static int AnstoHttpHalt(pHistDriver self){ // hmm, why isn't there a pCon like
|
|||||||
return HWFault;
|
return HWFault;
|
||||||
}
|
}
|
||||||
|
|
||||||
AnstoHttpStatusWithRetries(self,HWIdle,NULL); // no pCon available :(
|
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_STOPPED,NULL); // no pCon available :(
|
||||||
|
|
||||||
return OKOK;
|
return OKOK;
|
||||||
}
|
}
|
||||||
@@ -486,7 +565,7 @@ static int AnstoHttpPause(pHistDriver self,SConnection *pCon){
|
|||||||
}
|
}
|
||||||
pPriv->pause = 1;
|
pPriv->pause = 1;
|
||||||
|
|
||||||
AnstoHttpStatusWithRetries(self,HWIdle,pCon);
|
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_PAUSED,pCon);
|
||||||
|
|
||||||
return OKOK;
|
return OKOK;
|
||||||
}
|
}
|
||||||
@@ -498,12 +577,12 @@ static int AnstoHttpContinue(pHistDriver self, SConnection *pCon){
|
|||||||
pPriv = (pAnstoHttp)self->pPriv;
|
pPriv = (pAnstoHttp)self->pPriv;
|
||||||
assert(pPriv != NULL);
|
assert(pPriv != NULL);
|
||||||
|
|
||||||
status = anstoHttpGet(pPriv,continuedaq);
|
status = anstoHttpGet(pPriv,continuedaq); // which is the same as restarting
|
||||||
if(status != 1){
|
if(status != 1){
|
||||||
return HWFault;
|
return HWFault;
|
||||||
}
|
}
|
||||||
pPriv->pause = 0;
|
pPriv->pause = 0;
|
||||||
AnstoHttpStatusWithRetries(self,HWBusy,pCon);
|
AnstoHttpStatusWithRetries(self,ANSTO_HS_STATUS_STARTED,pCon);
|
||||||
return OKOK;
|
return OKOK;
|
||||||
}
|
}
|
||||||
/*---------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------*/
|
||||||
@@ -550,60 +629,7 @@ static int AnstoHttpGetData(pHistDriver self,SConnection *pCon){
|
|||||||
return OKOK;
|
return OKOK;
|
||||||
}
|
}
|
||||||
/*-------------------------------------------------------------------*/
|
/*-------------------------------------------------------------------*/
|
||||||
/** \brief Reads histogram data from a zipped file produced by the
|
static int AnstoHttpGetHistogram(pHistDriver self, SConnection *pCon,
|
||||||
* histogram memory server
|
|
||||||
*
|
|
||||||
* \param self (r) provides access to the histogram data structure
|
|
||||||
* \param bank number n maps to a HDS_Pn file.
|
|
||||||
*/
|
|
||||||
static int GetHMfromFile(pHistDriver self, SConnection *pCon,
|
|
||||||
int bank, int start, int end, HistInt *data){
|
|
||||||
HistInt *hmdata;
|
|
||||||
pAnstoHttp pPriv = NULL;
|
|
||||||
int status, len, i, size;
|
|
||||||
int errMsgLen;
|
|
||||||
FILE *fp;
|
|
||||||
|
|
||||||
char command[256];
|
|
||||||
char hmDataPath[MAXSTRLEN];
|
|
||||||
char DAQdir[MAXSTRLEN];
|
|
||||||
char DSfilepath[MAXSTRLEN];
|
|
||||||
char scanpoint[32];
|
|
||||||
memset(command, 0, sizeof(command));
|
|
||||||
memset(hmDataPath, 0, sizeof(hmDataPath));
|
|
||||||
memset(DAQdir, 0, sizeof(DAQdir));
|
|
||||||
memset(DSfilepath, 0, sizeof(DSfilepath));
|
|
||||||
memset(scanpoint, 0, sizeof(scanpoint));
|
|
||||||
|
|
||||||
pPriv = (pAnstoHttp)self->pPriv;
|
|
||||||
assert(pPriv != NULL);
|
|
||||||
errMsgLen = sizeof(pPriv->hmError);
|
|
||||||
if (bank < 0) {
|
|
||||||
snprintf(pPriv->hmError,errMsgLen,"Negative bank numbers are not supported when reading from the filesystem");
|
|
||||||
pPriv->errorCode = IMMEDFAIL;
|
|
||||||
return HWFault;
|
|
||||||
}
|
|
||||||
StringDictGet(self->pOption, "daq_dirname",DAQdir, sizeof(DAQdir)-1);
|
|
||||||
StringDictGet(self->pOption, "hmdatapath",hmDataPath, sizeof(hmDataPath)-1);
|
|
||||||
StringDictGet(self->pOption, "scanpoint",scanpoint, sizeof(scanpoint)-1);
|
|
||||||
snprintf(DSfilepath,sizeof(DSfilepath),"%s/%s/DATASET_%s/HDS_P%d",hmDataPath,DAQdir,scanpoint,bank);
|
|
||||||
|
|
||||||
if ((fp=gzopen(DSfilepath, "rb")) == NULL) {
|
|
||||||
snprintf(pPriv->hmError,errMsgLen,"Failed to open %s", DSfilepath);
|
|
||||||
pPriv->errorCode = IMMEDFAIL;
|
|
||||||
return HWFault;
|
|
||||||
}
|
|
||||||
size = end - start;
|
|
||||||
gzread(fp, data, size*sizeof(HistInt));
|
|
||||||
data+=size;
|
|
||||||
start+=size;
|
|
||||||
gzclose(fp);
|
|
||||||
return OKOK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**\brief Gets histogram data directly from the histogram memory http server.
|
|
||||||
*/
|
|
||||||
static int GetHMfromSocket(pHistDriver self, SConnection *pCon,
|
|
||||||
int bank, int start, int end, HistInt *data){
|
int bank, int start, int end, HistInt *data){
|
||||||
char command[256];
|
char command[256];
|
||||||
HistInt *hmdata;
|
HistInt *hmdata;
|
||||||
@@ -683,17 +709,6 @@ static int GetHMfromSocket(pHistDriver self, SConnection *pCon,
|
|||||||
}
|
}
|
||||||
return OKOK;
|
return OKOK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int AnstoHttpGetHistogram(pHistDriver self, SConnection *pCon,
|
|
||||||
int bank, int start, int end, HistInt *data){
|
|
||||||
char direct[2];
|
|
||||||
|
|
||||||
StringDictGet(self->pOption, "direct",direct, 2);
|
|
||||||
if (strcmp(direct,"1") == 0)
|
|
||||||
return GetHMfromSocket(self, pCon, bank, start, end, data);
|
|
||||||
else
|
|
||||||
return GetHMfromFile(self, pCon, bank, start, end, data);
|
|
||||||
}
|
|
||||||
/*--------------------------------------------------------------------*/
|
/*--------------------------------------------------------------------*/
|
||||||
static int AnstoHttpSetHistogram(pHistDriver self, SConnection *pCon,
|
static int AnstoHttpSetHistogram(pHistDriver self, SConnection *pCon,
|
||||||
int bank, int start, int end, HistInt *data){
|
int bank, int start, int end, HistInt *data){
|
||||||
@@ -758,8 +773,8 @@ pHistDriver CreateAnstoHttpDriver(pStringDict pOption){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* add our options */
|
/* add our options */
|
||||||
StringDictAddPair(pOption,"hmaddress","http://unknown.psi.ch:9090");
|
StringDictAddPair(pOption,"hmaddress","http://localhost:8080");
|
||||||
StringDictAddPair(pOption,"hmconfigurescript","hmconfigure");
|
StringDictAddPair(pOption,"hmconfigurescript","anstohm_full.xml");
|
||||||
|
|
||||||
/* initialise our private data structure */
|
/* initialise our private data structure */
|
||||||
pInternal = (pAnstoHttp)malloc(sizeof(anstoHttp));
|
pInternal = (pAnstoHttp)malloc(sizeof(anstoHttp));
|
||||||
@@ -770,17 +785,11 @@ pHistDriver CreateAnstoHttpDriver(pStringDict pOption){
|
|||||||
memset(pInternal,0,sizeof(anstoHttp));
|
memset(pInternal,0,sizeof(anstoHttp));
|
||||||
pNew->pPriv = pInternal;
|
pNew->pPriv = pInternal;
|
||||||
pInternal->syncRequest = ghttp_request_new();
|
pInternal->syncRequest = ghttp_request_new();
|
||||||
memset(pInternal->hmError,0,sizeof(pInternal->hmError));
|
|
||||||
if(pInternal->syncRequest == NULL){
|
if(pInternal->syncRequest == NULL){
|
||||||
free(pNew);
|
free(pNew);
|
||||||
free(pInternal);
|
free(pInternal);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
/* direct = 0 Get HM from data files
|
|
||||||
* direct = 1 Get HM from http server */
|
|
||||||
StringDictAddPair(pOption, "direct", "1");
|
|
||||||
StringDictAddPair(pOption, "hmdatapath", "../HMData");
|
|
||||||
StringDictAddPair(pOption, "scanpoint", "0");
|
|
||||||
|
|
||||||
|
|
||||||
/* configure all those functions */
|
/* configure all those functions */
|
||||||
|
|||||||
Reference in New Issue
Block a user