From 7919c7cd6abc3ee2906c971772550439297d5c9a Mon Sep 17 00:00:00 2001 From: Ferdi Franceschini Date: Thu, 18 Sep 2008 14:50:09 +1000 Subject: [PATCH] Mark Lesha's fixes to the asyncqueue and voltage controller r2696 | ffr | 2008-09-18 14:50:09 +1000 (Thu, 18 Sep 2008) | 2 lines --- asyncqueue.c | 4 +- site_ansto/orhvps.c | 285 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 241 insertions(+), 48 deletions(-) diff --git a/asyncqueue.c b/asyncqueue.c index 7a5276d0..a392604e 100644 --- a/asyncqueue.c +++ b/asyncqueue.c @@ -793,7 +793,7 @@ int AsyncQueueAction(SConnection *pCon, SicsInterp *pSics, return OKOK; } else if (value == 1) { - self->timeout = true; + self->translate = true; return OKOK; } snprintf(line, 132, "Invalid argument: %s", argv[2]); @@ -802,7 +802,7 @@ int AsyncQueueAction(SConnection *pCon, SicsInterp *pSics, } } else { - snprintf(line, 132, "%s.timeout = %d", argv[0], self->timeout); + snprintf(line, 132, "%s.translate = %d", argv[0], self->translate); SCWrite(pCon, line, eStatus); return OKOK; } diff --git a/site_ansto/orhvps.c b/site_ansto/orhvps.c index 453691fd..2cf3ddfd 100644 --- a/site_ansto/orhvps.c +++ b/site_ansto/orhvps.c @@ -107,7 +107,29 @@ static int ORHV_SendReceive(pORHVPSDriv priv, int *rep_len) { int status; - status = AsyncUnitTransact(priv->asyncUnit, cmd, cmd_len, reply, rep_len); + if (!*cmd) + { + *reply='\0'; + *rep_len=0; + return FAILURE; + } + + // Ordela 21000 firmware appears to send NAK (15h) code at regular intervals + // when there is no comms (time out)? To prevent this from interfering with + // the transaction, perform a dummy transaction first. + // Reading the jumper settings should be harmless (but in case of true + // disconnect we are going to get N * timeout - should not normally occur) + + int max_rep_len=*rep_len; // Value of arguemnt on entry is the max receive buffer len + AsyncUnitTransact(priv->asyncUnit, "Jz", 2, reply, rep_len); + *rep_len=max_rep_len; + // Now do the real transaction. */ + int max_retries=1; // Retries should be built into the async queue object, so set to 1. In practice this works just fine. + do + { + *rep_len=max_rep_len; + status = AsyncUnitTransact(priv->asyncUnit, cmd, cmd_len, reply, rep_len); + } while(--max_retries&&status!=1); if (status != 1) { return FAILURE; @@ -680,7 +702,7 @@ static int ORHVPSSend(pEVDriver self, char *pCommand, char *pReply, int iLen) { cmd[idx] = '\0'; cmd_len = idx; rsp_len = CMDLEN; - ORHV_SendReceive(self->pPrivate, cmd, cmd_len, rsp, &rsp_len); + int orhvsr_status=ORHV_SendReceive(self->pPrivate, cmd, cmd_len, rsp, &rsp_len); idx = 0; for (i = 0; i < rsp_len && idx < iLen - 1; ++i) { if (rsp[i] < 32 || rsp[i] > 126) { @@ -934,10 +956,17 @@ pEVDriver CreateORHVPSDriver(int argc, char *argv[]) priv->fsm.state_name = state_name; priv->fsm.event_name = event_name; priv->name = strdup(argv[0]); - priv->fMax = 2400.0; + // MJL 9/08 Modified the defaults to cater for the current Ordela 21000N detector on SANS + // (max HV setting 2600V adjusted via pot). For the spare Ordela detector, the max voltage + // is pot-adjusted to 2400V as we have been advised that this is the max safe operating voltage. + // Since this driver uses the fUpper for scaling, in order to prevent any potential damage to the + // detectors we set the default to the highest of the two. (i.e. if .upper is not initialized + // in the SICS configuration, it is higher than or equal to the actual detector voltage maximum + // and this results in the set voltage being equal to or lower than expected - not higher). + priv->fMax = 2600.0; priv->fRate = 10.0; - priv->fLower = 0.0; - priv->fUpper = 1800.0; + priv->fLower = 0.0; // fLower is 'low' safe voltage for driving - nominally 800V - but leave default at 0V for safety + priv->fUpper = 2350.0; priv->iPeriod = get_period(priv->fMax, priv->fRate); priv->bRunFlag = false; @@ -986,7 +1015,11 @@ int ORHVPSWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, { return EVControlWrapper(pCon,pSics,pData,argc,argv); } - if (strcasecmp("send", argv[1]) == 0) { +// MJL 17/9/08 implement a special command mode to make life easier +// (otherwise there is a lot of tedious translation between hex/decimal and ASCII codes +// due to the inability of the code to distinguish between ASCII and returned setting values) + int is_cmd=(strcasecmp("cmd", argv[1]) == 0); + if (is_cmd || strcasecmp("send", argv[1]) == 0) { char cmd[CMDLEN]; int cmd_len; char rsp[CMDLEN]; @@ -996,51 +1029,211 @@ int ORHVPSWrapper(SConnection *pCon, SicsInterp *pSics, void *pData, /* Managers only */ if (!SCMatchRights(pCon, usMugger)) return 0; - cmd[idx] = '\0'; - for (i = 2; i < argc; ++i) { - int j, k; - if (i > 2 && idx < CMDLEN) - cmd[idx++] = ' '; - for (j = 0; argv[i][j]; ++j) { - if (argv[i][j] == '\\') { - k = 0; - if (isxdigit(argv[i][j+1]) && isxdigit(argv[i][j+2])) { - if (argv[i][j+1] >= '0' && argv[i][j+1] <= '9') - k = (argv[i][j+1] - '0') << 4; - else if (argv[i][j+1] >= 'a' && argv[i][j+1] <= 'f') - k = (argv[i][j+1] - 'a' + 10) << 4; - else if (argv[i][j+1] >= 'A' && argv[i][j+1] <= 'F') - k = (argv[i][j+1] - 'A' + 10) << 4; - if (argv[i][j+2] >= '0' && argv[i][j+2] <= '9') - k += (argv[i][j+2] - '0'); - else if (argv[i][j+2] >= 'a' && argv[i][j+2] <= 'f') - k += (argv[i][j+2] - 'a' + 10); - else if (argv[i][j+2] >= 'A' && argv[i][j+2] <= 'F') - k += (argv[i][j+2] - 'A' + 10); - j += 2; + + if (argc<3) + { + SCWrite(pCon, "ERROR: Not enough arguments supplied", eError); + return 0; + } + + char *pcmdrspfmt=argv[2]; + if (is_cmd) // "cmd" + { + // In command mode, the third argument is normally a single letter command + // and the remaining arguments are command parameters. For future expansion, + // we also allow the third argument to be an explicit command/response format. + // This is in format "-" where the command and response fields + // are format specifiers containing the required/expected ASCII letters + // in the command, and the format specifiers '%s' (string), + // '%d' (integer 0-255), , '%B' (a board name e.g. x0-y15), + // '%P' (a pot name e.g. x0-y191), or '%A' (ack code as 'ACK'/'NAK'). + // Formats '%c' (a character) and '%x' (char as 2 hex digits) could be + // added to the code if needed. + // Note '%P' translates to/from a two-byte board number plus pot number. + // Code '%A' only useable in response format. + if (strlen(argv[2])==1) + switch(*argv[2]) + { + // All the known commands for the Ordela 21000N, at 9/08. + // If more become available, add them here, or alternately + // supply an appropriate command format string explicitly. + case 'v': pcmdrspfmt="vz-%sz"; break; // Get firmware version + case 'p': pcmdrspfmt="p%P%dz-%A"; break; // Set a pot value + case 'P': pcmdrspfmt="P%Pz-P%P%dz"; break; // Get a pot value + case 'h': pcmdrspfmt="h%dz-%A"; break; // Set HV voltage (also settable directly via the orhvps object) + case 'H': pcmdrspfmt="Hz-H%dz"; break; // Get HV setting + case 'd': pcmdrspfmt="dz-%A"; break; // Enter diagnostic mode (disables coincidence detection etc.) + case 'D': pcmdrspfmt="Dz-D%dz"; break; // Check if in diagnostic mode + case 'n': pcmdrspfmt="nz-%A"; break; // Exit diagnostic mode + case 'b': pcmdrspfmt="b%Bz-%A"; break; // Disable a board + case 'B': pcmdrspfmt="B%Bz-B%B%dz"; break; // Query if board disabled + case 'l': pcmdrspfmt="l%Bz-%A"; break; // Re-enable a board + case 'J': pcmdrspfmt="Jz-J%dz"; break; // Check jumper settings + } + // Prepare the command string + char *pcmd=cmd; + int nfmtspec=0; + while(*pcmdrspfmt!='-') + { + if (*pcmdrspfmt=='\0') + { + SCWrite(pCon, "ERROR: Missing '-' separator in format string", eError); + return 0; } - } - else - k = argv[i][j]; - if (idx < CMDLEN) - cmd[idx++] = k; - } - if (idx < CMDLEN) - cmd[idx] = '\0'; - cmd_len = idx; + if (*pcmdrspfmt=='%') // format specifier + { + pcmdrspfmt++; + if (++nfmtspec>argc-3) + { + SCWrite(pCon, "ERROR: Not enough arguments supplied for cmd", eError); + return 0; + } + + int v1,v2; + switch(*pcmdrspfmt++) + { + case 's': // probably never used + pcmd+=sprintf(pcmd,"%s",argv[nfmtspec+2]); + break; + case 'd': + sscanf(argv[nfmtspec+2],"%d",&v1); + pcmd+=sprintf(pcmd,"%c",(char)v1); + break; + case 'B': + sscanf(argv[nfmtspec+2]+1,"%d",&v1); + v1+=(v1>=8)*8; // 0-15-> 0-7 and 16-23 + v1+=(*argv[nfmtspec+2]=='y')*8; // y at 8-15 and 24-31 + pcmd+=sprintf(pcmd,"%c",v1); + break; + case 'P': + sscanf(argv[nfmtspec+2]+1,"%d",&v1); + v2=v1&0xF; + v1>>=4; + v1+=(v1>=8)*8; + v1+=(*argv[nfmtspec+2]=='y')*8; + pcmd+=sprintf(pcmd,"%c%c",v1,v2); + break; + // case 'A': is NOT supported - responses only! + default: SCWrite(pCon, "ERROR: Unknown % specification in command format", eError); return 0; + } + } + else + *pcmd++=*pcmdrspfmt++; // Simple ASCII character that is part of command + } + pcmdrspfmt++; // skip over the '-' separator into the response format + cmd_len=pcmd-cmd; } + else // "send" + { + cmd[idx] = '\0'; + for (i = 2; i < argc; ++i) { + int j, k; + if (i > 2 && idx < CMDLEN) + cmd[idx++] = ' '; + for (j = 0; argv[i][j]; ++j) { + if (argv[i][j] == '\\') { + k = 0; + if (isxdigit(argv[i][j+1]) && isxdigit(argv[i][j+2])) { + if (argv[i][j+1] >= '0' && argv[i][j+1] <= '9') + k = (argv[i][j+1] - '0') << 4; + else if (argv[i][j+1] >= 'a' && argv[i][j+1] <= 'f') + k = (argv[i][j+1] - 'a' + 10) << 4; + else if (argv[i][j+1] >= 'A' && argv[i][j+1] <= 'F') + k = (argv[i][j+1] - 'A' + 10) << 4; + if (argv[i][j+2] >= '0' && argv[i][j+2] <= '9') + k += (argv[i][j+2] - '0'); + else if (argv[i][j+2] >= 'a' && argv[i][j+2] <= 'f') + k += (argv[i][j+2] - 'a' + 10); + else if (argv[i][j+2] >= 'A' && argv[i][j+2] <= 'F') + k += (argv[i][j+2] - 'A' + 10); + j += 2; + } + } + else + k = argv[i][j]; + if (idx < CMDLEN) + cmd[idx++] = k; + } + if (idx < CMDLEN) + cmd[idx] = '\0'; + cmd_len = idx; + } + } + rsp_len = CMDLEN; - ORHV_SendReceive(priv, cmd, cmd_len, rsp, &rsp_len); - idx = 0; - for (i = 0; i < rsp_len && idx < CMDLEN - 1; ++i) { - if (rsp[i] < 32 || rsp[i] > 126) { - idx+=sprintf(&cmd[idx], "%02Xh", rsp[i]); + int orhvsr_status=ORHV_SendReceive(priv, cmd, cmd_len, rsp, &rsp_len); + + int error_in_cmd_rsp_parse=0; + if (is_cmd) // "cmd" response + { + // Start parsing the response according to the format string. + // If we don't get what we expect, set error_in_cmd_rsp_parse + // so that the response is output in 'send' format. + // Only output the parameters in the response format, not ASCII's + char *pcmd=cmd; // reformat rsp string into cmd string + char *prsp=rsp; + int got_fmt_rsp=0; + while (*pcmdrspfmt&&!error_in_cmd_rsp_parse) + { + if (prsp-rsp>=rsp_len) + error_in_cmd_rsp_parse=1; // response is too short - no bytes left to parse + else if (*pcmdrspfmt=='%') // Parse byte(s) + { + pcmdrspfmt++; + if (got_fmt_rsp) + *pcmd++=' '; // separate response fields with spaces (Tcl list can separate these) + int slen; + switch(*pcmdrspfmt++) + { + case 's': + pcmd+=(slen=sprintf(pcmd,"%s",prsp)); + *--pcmd='\0'; // string should have been terminated (with a 'z') but that gets checked later + prsp+=slen-1; + break; + case 'd': + pcmd+=sprintf(pcmd,"%d",*prsp++); + break; + case 'B': + pcmd+=sprintf(pcmd,"%c%d",(prsp[0]&0x8)?'y':'x',(prsp[0]&0x7)+((prsp[0]&0x10)>>1)); + prsp++; + break; + case 'P': + if (rsp_len-(prsp-rsp)<2) + error_in_cmd_rsp_parse=1; + else + { + pcmd+=sprintf(pcmd,"%c%d",(prsp[0]&0x8)?'y':'x',(prsp[0]&0x7)*0x10+(prsp[0]&0x10)*0x8+prsp[1]); + prsp+=2; + } + break; + case 'A': + error_in_cmd_rsp_parse|=(prsp[0]!=0x06&&prsp[0]!=0x15); + pcmd+=sprintf(pcmd,"%s",(prsp[0]==0x06)?"ACK":"NAK"); + prsp++; + break; + default: SCWrite(pCon, "ERROR: Unknown % specification in response format", eError); return 0; + } + got_fmt_rsp=1; + } + else // expected ASCII characters in response have to match response format + error_in_cmd_rsp_parse|=(*prsp++!=*pcmdrspfmt++); } - else - cmd[idx++] = rsp[i]; } - if (idx < CMDLEN) - cmd[idx++] = '\0'; + if (!is_cmd||error_in_cmd_rsp_parse) // "send" response, or a "cmd" response that didn't match expected format + { + idx = 0; + if (error_in_cmd_rsp_parse) + idx=sprintf(cmd,"ERROR: Bad response format: "); + for (i = 0; i < rsp_len && idx < CMDLEN - 1; ++i) { + if (rsp[i] < 32 || rsp[i] > 126) { + idx+=sprintf(&cmd[idx], "%02Xh", rsp[i]); + } + else + cmd[idx++] = rsp[i]; + } + if (idx < CMDLEN) + cmd[idx++] = '\0'; + } SCWrite(pCon, cmd, eValue); return 1; }