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
This commit is contained in:
Ferdi Franceschini
2008-09-18 14:50:09 +10:00
committed by Douglas Clowes
parent 70f194fe85
commit 7919c7cd6a
2 changed files with 241 additions and 48 deletions

View File

@@ -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 "<cmd>-<resp>" 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;
}