11
22#include < string.h>
33#include < cstdlib>
4+ #include < cstdint>
45#include < sstream>
56
67#include < iocsh.h>
@@ -260,7 +261,7 @@ asynStatus SPiiPlusComm::writeReadErrorMessage(char* errNoReply)
260261 std::stringstream val_convert;
261262 std::stringstream local_cmd;
262263 char inString[MAX_CONTROLLER_STRING_SIZE ];
263- int errNo;
264+ int errNo = 0 ;
264265
265266 /* errNoReply is of the form ?#### */
266267
@@ -298,6 +299,41 @@ asynStatus SPiiPlusComm::writeReadErrorMessage(char* errNoReply)
298299 return status;
299300}
300301
302+ // A separate method to read error messages from binary comm methods is needed to avoid deadlocks
303+ asynStatus SPiiPlusComm::writeReadBinaryErrorMessage (int errNo)
304+ {
305+ static const char *functionName = " writeReadBinaryErrorMessage" ;
306+ std::stringstream local_cmd;
307+ char inString[MAX_CONTROLLER_STRING_SIZE ];
308+
309+ std::fill (inString, inString + 256 , ' \0 ' );
310+
311+ // The command to query the error message is ??####
312+ local_cmd << " ??" << errNo;
313+
314+ asynPrint (pasynUserSelf, ASYN_TRACEIO_DRIVER , " %s:%s: output = %s\n " , driverName, functionName, local_cmd.str ().c_str ());
315+
316+ size_t response;
317+ // The function calling binaryErrorCheck already has the lock, so no locking is needed here
318+ asynStatus status = writeReadController (local_cmd.str ().c_str (), inString, 256 , &response, -1 );
319+
320+ asynPrint (pasynUserSelf, ASYN_TRACEIO_DRIVER , " %s:%s: input = %s\n " , driverName, functionName, inString);
321+ asynPrint (pasynUserSelf, ASYN_TRACEIO_DRIVER , " %s:%s: status = %i\n " , driverName, functionName, status);
322+
323+ if (inString[0 ] != ' ?' )
324+ {
325+ asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: ERROR #%i: %s\n " , driverName, functionName, errNo, inString);
326+ }
327+ else {
328+ // We should never get here unless a controller returns an error for which it doesn't have an error message defined
329+ asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: ERROR #%i\n " , driverName, functionName, errNo);
330+
331+ status = asynError;
332+ }
333+
334+ return status;
335+ }
336+
301337// NOTE: readBytes the number of data bytes that were read, excluding the command header and suffix
302338// NOTE: there is no error checking on inBytes and outBytes
303339// FYI: motor/motorApp/MotorSrc/asynMotorController.h:#define MAX_CONTROLLER_STRING_SIZE 256
@@ -307,6 +343,7 @@ asynStatus SPiiPlusComm::writeReadBinary(char *output, int outBytes, char *input
307343 char * packetBuffer;
308344 size_t nwrite, nread;
309345 int eomReason;
346+ int errNo = 0 ;
310347 asynStatus status;
311348 static const char *functionName = " writeReadBinary" ;
312349
@@ -333,17 +370,18 @@ asynStatus SPiiPlusComm::writeReadBinary(char *output, int outBytes, char *input
333370 // The reply from the controller has a 4-byte header and a 1-byte suffix
334371 status = pasynOctetSyncIO->read (pasynUserComm_, packetBuffer, inBytes, SPIIPLUS_ARRAY_TIMEOUT , &nread, &eomReason);
335372
336- asynPrint (pasynUserSelf, ASYN_TRACEIO_DRIVER , " %s:%s: input bytes = %i\n " , driverName, functionName, inBytes);
373+ asynPrint (pasynUserSelf, ASYN_TRACEIO_DRIVER , " %s:%s: input bytes = %i\n " , driverName, functionName, inBytes);
337374 asynPrint (pasynUserSelf, ASYN_TRACEIO_DRIVER , " %s:%s: status = %i\n " , driverName, functionName, status);
338375
339376 if (status == asynSuccess)
340377 {
341378 // Check for an error reply
342- status = binaryErrorCheck (packetBuffer);
343- if (status == asynError )
379+ errNo = binaryErrorCheck (packetBuffer, nread );
380+ if (errNo != 0 )
344381 {
345382 *sliceAvailable = false ;
346383 asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Binary read failed (controller)\n " , driverName, functionName);
384+ status = asynError;
347385 }
348386 else
349387 {
@@ -371,6 +409,16 @@ asynStatus SPiiPlusComm::writeReadBinary(char *output, int outBytes, char *input
371409 {
372410 *sliceAvailable = false ;
373411 asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Binary read failed (asyn): status=%i, nread=%li\n " , driverName, functionName, status, nread);
412+
413+ // If fewer bytes were read and there was an asyn timeout, there might still be an error message
414+ if (nread > 0 )
415+ {
416+ errNo = binaryErrorCheck (packetBuffer, nread);
417+ if (errNo != 0 )
418+ {
419+ status = asynError;
420+ }
421+ }
374422 }
375423
376424 // Restore the EOS characters
@@ -382,37 +430,93 @@ asynStatus SPiiPlusComm::writeReadBinary(char *output, int outBytes, char *input
382430
383431 unlock ();
384432
433+ if (errNo != 0 )
434+ {
435+ // Print the human-readable error string; humans don't like searching pdfs for error descriptions
436+ writeReadBinaryErrorMessage (errNo);
437+ }
438+
385439 asynPrint (pasynUserSelf, ASYN_TRACE_FLOW , " %s:%s: end\n " , driverName, functionName);
386440
387441 return status;
388442 }
389443
390- asynStatus SPiiPlusComm::binaryErrorCheck (char *buffer)
444+ // Return value is 0 if there is no error and the error number if an error is detected
445+ int SPiiPlusComm::binaryErrorCheck (char *buffer, int readBytes)
391446{
392- asynStatus status=asynSuccess;
393447 std::stringstream val_convert;
394- int errNo;
448+ int errNo = 0 ;
449+ int idx;
450+ uint8_t replyStart, replyEnd, cmdId, bodyLenLsb, bodyLenMsb, bodyStart, bodyEnd;
451+ int bodyLength;
452+ uint8_t errorStr[5 ] = {0 , 0 , 0 , 0 , 0 };
453+ bool errNoIsValid = true ;
395454 static const char *functionName = " binaryErrorCheck" ;
396455
397- // If the first character of the data is a question mark, the error number follows it
398- if ((buffer[4 ] == 0x3f ) && (buffer[9 ] == 0x0d ))
456+ /*
457+ * This was the original expected error response (11 bytes), but I can't find it anywhere in the documentation now.
458+ * Error response: [E3][XX][06][00]?####[0D][E6]
459+ *
460+ * This is documented error response (10 bytes) that is present in many verions of the low level host communication user guide.
461+ * Error response: [E3][XX]6?####[0D][E6]
462+ */
463+
464+ if (readBytes == 11 )
399465 {
400- /*
401- * Error response: [E3][XX][06][00]?####[0D][E6]
402- */
403-
404- // replace the carriage return with a null byte
405- buffer[9 ] = 0 ;
466+ replyStart = buffer[0 ];
467+ cmdId = buffer[1 ];
468+ bodyLenLsb = buffer[2 ];
469+ bodyLenMsb = buffer[3 ];
470+ // Only the least two significant bits of the most signficant body-length byte are the most significant bits of the body length
471+ bodyLength = ((int )bodyLenMsb << 8 ) | (int )bodyLenLsb;
472+ bodyStart = buffer[4 ];
473+ bodyEnd = buffer[9 ];
474+ replyEnd = buffer[10 ];
406475
407- // convert the error number bytes into an int
408- val_convert << buffer+5 ;
409- val_convert >> errNo;
410-
411- asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Binary command error #%i\n " , driverName, functionName, errNo);
412- status = asynError;
476+ if ((replyStart == 0xe3 ) && (bodyLength == 6 ) && (replyEnd == 0xe6 ))
477+ {
478+ // '?' is 0x3f
479+ if ((bodyStart == 0x3f ) && (bodyEnd == 0x0d ))
480+ {
481+ for (idx=0 ; idx<4 ; idx++)
482+ {
483+ /*
484+ * The error number starts at index = 4 in the error reply
485+ * Confirm the error number has valid characters (digits 0-9)
486+ * '0' is 48; '9' is 57
487+ */
488+ if ((buffer[5 +idx] < 48 ) && (buffer[5 +idx] > 57 ))
489+ {
490+ errNoIsValid = false ;
491+ break ;
492+ }
493+ else
494+ {
495+ errorStr[idx] = buffer[5 +idx];
496+ }
497+ }
498+
499+ if (errNoIsValid)
500+ {
501+ // The error string is valid and can be converted into an int and reported on the IOC's shell
502+ val_convert << errorStr;
503+ val_convert >> errNo;
504+
505+ asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Binary command error %i for command id %x\n " , driverName, functionName, errNo, cmdId);
506+ }
507+ }
508+ else
509+ {
510+ asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Incorrect error body start/end: bodyStart = %x, bodyEnd = %x\n " , driverName, functionName, bodyStart, bodyEnd);
511+ }
512+ }
513+ else
514+ {
515+ asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Incorrect error reply prefix/suffix: replyStart = %x, bodyLength = %i, replyEnd = %x\n " , driverName, functionName, replyStart, bodyLength, replyEnd);
516+ }
413517 }
414518
415- return status ;
519+ return errNo ;
416520}
417521
418522asynStatus SPiiPlusComm::isVariableDefined (bool *isDefined, const char *var)
@@ -453,6 +557,7 @@ asynStatus SPiiPlusComm::writeReadAckBinary(char *output, int outBytes, char *in
453557 size_t nwrite, nread, extraRead;
454558 int eomReason;
455559 int commandID;
560+ int errNo = 0 ;
456561 asynStatus status;
457562 static const char *functionName = " writeReadAckBinary" ;
458563
@@ -492,11 +597,12 @@ asynStatus SPiiPlusComm::writeReadAckBinary(char *output, int outBytes, char *in
492597
493598 asynPrint (pasynUserSelf, ASYN_TRACEIO_DRIVER , " %s:%s: status = %i; extraRead = %li, eomReason = %i\n " , driverName, functionName, status, extraRead, eomReason);
494599
495- // Check for an error reply -- this overwrites the buffer if an error occurs
496- status = binaryErrorCheck (input);
497- if (status == asynError )
600+ // Check for an error reply
601+ errNo = binaryErrorCheck (input, nread+extraRead );
602+ if (errNo != 0 )
498603 {
499604 asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Binary read failed (controller)\n " , driverName, functionName);
605+ status = asynError;
500606 }
501607
502608 }
@@ -509,6 +615,7 @@ asynStatus SPiiPlusComm::writeReadAckBinary(char *output, int outBytes, char *in
509615 else
510616 {
511617 asynPrint (pasynUserSelf, ASYN_TRACE_ERROR , " %s:%s: Command ID mismatch: write ID = %i, read ID = %i\n " , driverName, functionName, commandID, input[1 ]);
618+ // Should status be set to asynError here?
512619 }
513620 }
514621 }
@@ -523,6 +630,12 @@ asynStatus SPiiPlusComm::writeReadAckBinary(char *output, int outBytes, char *in
523630
524631 unlock ();
525632
633+ if (errNo > 0 )
634+ {
635+ // Print the human-readable error string; humans don't like searching pdfs for error descriptions
636+ writeReadBinaryErrorMessage (errNo);
637+ }
638+
526639 asynPrint (pasynUserSelf, ASYN_TRACE_FLOW , " %s:%s: end\n " , driverName, functionName);
527640
528641 return status;
0 commit comments