Files
sics/site_ansto/hardsup/aqp_opalstatus.c

171 lines
5.1 KiB
C

/* @file aqp_opalstatus.c
* @brief
* Implements a simple protocol to GET reactor status using an HTTP/1.1 GET.
* It sets the UserAgent as SICS to avoid re-directs because we don't handle them.
* If the HTTP status is not OK or if the driver fails to find a line that
* contains the "Power:" field then it returns the unprocessed reply
* characters in the data buffer.
*
* The reply is expected to have a form similar to the following,
* HTTP/1.1 200 OK
* Date: Thu, 26 Feb 2015 01:27:30 GMT
* Server: Oracle-Application-Server-10g/10.1.3.5.0 Oracle-HTTP-Server
* Content-Location: /reactor.jsp
* Content-Type: text/plain; charset=UTF-8
* Set-Cookie: JSESSIONID=74646fa7cf3cdf53a371694a33ab760cd649631d758f416d916c6d1b496a477b.e34Pc3qNbh4RaO0Ma3yMbxaRaN0Ke0; path=/Bragg
* Connection: close
* Transfer-Encoding: chunked
*
* 4f
* Power: 19.381588; CNS Out: 25.76; TG123: 1; CG123: 1; TG4: Open; HB1: 0; HB2: 1
*
* 0
*
* It should have no more than 512 characters excluding the HTTP OK status.
* Each line is expected to be terminated with a \r\n.
* The reply is expected to begin with "HTTP/1.1 200 OK"
* A line is identified as the status message if and only if it includes
* "Power:" regardless of case.
* The reply is expected to end with 0\r\n\r\n
*
* Processing lines is simplified by discarding \r characters and then just
* checking for \n.
* NOTE: This implies that \n should not be used in the status line as a
* delimiter.
*/
#include "../made_config.h"
#include <sics.h>
#include <asyncqueue.h>
#define HTTP_OK "HTTP/1.1 200 OK"
#define HTTP11 " HTTP/1.1\r\n"
#if defined SICS_VERSION && defined SICS_REVISION
#define USERAGENT "User-Agent: SICS Version = " SICS_VERSION " Revision = " SICS_REVISION "\r\n"
#else
#define USERAGENT "User-Agent: SICS\r\n"
#endif
#define HOST "Host: neutron.ansto.gov.au\r\n"
/* Assume that the first field in the reactor status starts with "Power" */
#define PWR_FLD "Power"
#define PWR_FLD_LEN 5
enum replystates {START, CHK_PWR_FLD, MSG, END_MSG, DRAIN_REPLY};
static pAsyncProtocol OPAL_Protocol = NULL;
/* The message is assumed to start with "Power:" and terminated with crlf.
* by two more crlf pairs of characters
* Processing of line terminators is simplified by discarding cr chars */
static int OPAL_Rx(pAsyncProtocol p, pAsyncTxn txn, int ch) {
char *fldptr;
int ret = 1;
static int char_ctr = 0, newline_ctr = 0;
static enum replystates state = START;
if (char_ctr++ >= txn->inp_len) {
char_ctr = 0;
newline_ctr = 0;
state = START;
return AQU_POP_CMD;
}
if (ch == '\r') {
return ret;
}
switch (state) {
case START:
if (ch == '\n') {
/* check HTTP status is OK before proceeding */
if ( strncmp(txn->inp_buf, HTTP_OK, strlen(HTTP_OK) ) == 0 ) {
txn->inp_idx = 0;
state = CHK_PWR_FLD;
} else
ret = AQU_POP_CMD;
} else
txn->inp_buf[txn->inp_idx++] = ch;
break;
case CHK_PWR_FLD:
if (ch == '\n') {
txn->inp_idx = 0;
} else if (ch == ':') {
if ( txn->inp_idx >= PWR_FLD_LEN ) {
/* Check for "Power" field anywhere in status line */
fldptr = &txn->inp_buf[txn->inp_idx - PWR_FLD_LEN];
if ( strncasecmp(fldptr, PWR_FLD, PWR_FLD_LEN) == 0 ) {
state = MSG;
}
}
}
txn->inp_buf[txn->inp_idx++] = ch;
break;
case MSG:
if (ch == '\n') {
state = END_MSG;
} else {
txn->inp_buf[txn->inp_idx++] = ch;
}
break;
case END_MSG:
if (ch == '0') {
state = DRAIN_REPLY;
}
break;
case DRAIN_REPLY:
newline_ctr++;
if (newline_ctr >= 2)
ret = AQU_POP_CMD;
break;
}
if (ret == AQU_POP_CMD) {
char_ctr = 0;
newline_ctr = 0;
state = START;
}
return ret;
}
static int OPAL_Ev(pAsyncProtocol p, pAsyncTxn pTxn, int event) {
if (event == AQU_TIMEOUT) {
pTxn->txn_status = ATX_TIMEOUT;
return AQU_POP_CMD;
}
return AQU_POP_CMD;
}
/* Append HTTP/1.1 and Host: line to message */
static int OPAL_PrepareTxn(pAsyncProtocol p, pAsyncTxn txn, const char* cmd, int cmd_len, int rsp_len) {
int msglen;
msglen = cmd_len + strlen(HTTP11) + strlen(USERAGENT) + strlen(HOST) + 3;
txn->out_buf = (char*) malloc(msglen);
if (txn->out_buf == NULL) {
SICSLogWrite("ERROR: Out of memory in OPAL_PrepareTxn", eError);
return 0;
}
strcpy(txn->out_buf, cmd);
strcat(txn->out_buf, HTTP11);
strcat(txn->out_buf, USERAGENT);
strcat(txn->out_buf, HOST);
strcat(txn->out_buf, "\r\n");
txn->out_len = msglen;
txn->out_idx = 0;
if (txn->inp_buf != NULL) {
free(txn->inp_buf);
}
txn->inp_len = 512;
txn->inp_idx = 0;
txn->txn_state = 0;
txn->txn_status = 0;
return 1;
}
void OpalStatusInitProtocol(SicsInterp *pSics) {
if (OPAL_Protocol == NULL) {
OPAL_Protocol = AsyncProtocolCreate(pSics, "OPALSTAT", NULL, NULL);
OPAL_Protocol->sendCommand = NULL;
OPAL_Protocol->handleInput = OPAL_Rx;
OPAL_Protocol->killPrivate = NULL;
OPAL_Protocol->handleEvent = OPAL_Ev;
OPAL_Protocol->prepareTxn = OPAL_PrepareTxn;
}
}