#define SCPI_CORE_PRIVATE // access to 'scpi_core_private.h' #include #include #include #include "app/scpi/scpi_core.h" #include "app/scpi/scpi_core_private.h" #include "app/scpi/scpi_commands.h" #include "app/scpi/scpi_parser.h" #include "app/scpi/scpi_numeric.h" //---------------------------------------------------------------- // Refer to: // [1] IEEE 488.2 Standard, revision IEEE Std 488.2-1987 (1992) // "IEEE Standard Codes, Formats, Protocols, and Common Commands for Use With IEEE Std 488.1-1987, IEEE // Standard Digital Interface for Programmable Instrumentation" // [2] SCPI Specification, revision 1999.0 // "Standard Commands for Programmable Instruments (SCPI), VERSION 1999.0, May 1999" //---------------------------------------------------------------- // @processNumericArgument // Numeric argument validation and conversion function. // Processes specified SCPI Numeric Parameter (Numeric Program Data) with the most length SCPI_MAX_NUMBER_LENGTH. // References: // "7.7.2 ", [1] // "7.7.2.2 Encoding Syntax", [1] // "7.7.4 ", [1] // "7.7.4.2 Encoding Syntax", [1] // Parameters: // @pObj - numeric conversion context; // @argToken - numeric argument token to process; // @entityType - SCPI entity type [eScpiEntityType_t] (see @getParsedEntityDetails, 'peEntityType' argument) // @range - allowed values range structure, optional // Return: // - true: parameter processed successfully, the result is stored inside numeric conversion context // - false: paramenter processing failed. bool processNumericArgument( sProcessNumericEntry_t * pObj, const sStrToken_t * argToken, uint8_t entityType, const sNumericRange_t * range ) { bool status = false; assert( pObj ); assert( argToken ); // keep at least one free character left in the end of the buffer for null-character if( ((ptrdiff_t)argToken->stail - (ptrdiff_t)argToken->shead) >= SCPI_MAX_NUMBER_LENGTH ) { pObj->valueEntry.error = ScpiNumericError_DEVICE_RANGE; // too long number return false; } pObj->valueEntry.error = ScpiNumericError; // fill buffer with zeros, the last character must be '\0' memset( pObj->numberBuffer, 0, sizeof(pObj->numberBuffer) ); // copy number to the null-terminated buffer strncpy( pObj->numberBuffer, argToken->shead, ((ptrdiff_t)argToken->stail - (ptrdiff_t)argToken->shead) ); // fill the @valueType to specify the value type to be accessed in @pObj->Value pObj->valueEntry.valueType = entityType; int base = 0; // forward set to invalid char * pEnd = NULL; switch( entityType ) { // --- Float ---- case eScpiEntityTypeDemicalFloatNumber: // Demical Number (float, only mantissa) case eScpiEntityTypeDemicalExpNumber: // Demical Number (scientific, mantissa+exponent) { // check range type if( ! SCPI_NUMERIC_LIMIT_TYPE_IS_FLOAT(range->type) ) { // invalid range: user limit is set in integer limits // user expects that the value is integer, but it is not pObj->valueEntry.error = ScpiNumericError_USER_TYPE; (void)status; // false } else { pObj->valueEntry.Value.demicalFloat = strtold( pObj->numberBuffer, &pEnd ); // check for conversion error if( errno != 0 /*ERANGE*/ ) { // invalid float conversion: ERANGE? pObj->valueEntry.error = ScpiNumericError_DEVICE_RANGE; (void)status; // false } // check for limits else if( NULL != range ) // user limit is set { status = ( range->min.f <= pObj->valueEntry.Value.demicalFloat && range->max.f >= pObj->valueEntry.Value.demicalFloat ); if( !status ) pObj->valueEntry.error = ScpiNumericError_USER_RANGE; else pObj->valueEntry.error = ScpiNumericSuccess; } else { status = true; pObj->valueEntry.error = ScpiNumericSuccess; } } } break; // --- Integer --- case eScpiEntityTypeHexNumber: // Hexademical Number if( 0 == base ) base = 16; case eScpiEntityTypeOctNumber: // Octal Number if( 0 == base ) base = 8; case eScpiEntityTypeBinNumber: // Binary Number if( 0 == base ) base = 2; case eScpiEntityTypeDemicalIntegerNumber: // Integer number only, extension for Demical Number if( 0 == base ) base = 10; { pObj->valueEntry.Value.demicalInteger = strtoll( pObj->numberBuffer, &pEnd, base ); // check for conversion error if( errno != 0 /*ERANGE*/ ) { // invalid float conversion: ERANGE? pObj->valueEntry.error = ScpiNumericError_DEVICE_RANGE; (void)status; // false } // check for limits else if( NULL != range ) // user limit is set { // check if the float range is set, but actually integer received if( SCPI_NUMERIC_LIMIT_TYPE_IS_FLOAT(range->type) ) { // yep: change processed value type to float (integer promotion to float) // convert value pObj->valueEntry.Value.demicalFloat = pObj->valueEntry.Value.demicalInteger; // {type cast} // change type pObj->valueEntry.valueType = eScpiEntityTypeDemicalExpNumber; // check limits status = ( range->min.f <= pObj->valueEntry.Value.demicalFloat && range->max.f >= pObj->valueEntry.Value.demicalFloat ); if( !status ) pObj->valueEntry.error = ScpiNumericError_USER_RANGE; else pObj->valueEntry.error = ScpiNumericSuccess; } else if( SCPI_NUMERIC_LIMIT_TYPE_IS_INTEGER(range->type) ) { // no: integer limit for integer value status = ( range->min.i <= pObj->valueEntry.Value.demicalInteger && range->max.i >= pObj->valueEntry.Value.demicalInteger ); if( !status ) pObj->valueEntry.error = ScpiNumericError_USER_RANGE; else pObj->valueEntry.error = ScpiNumericSuccess; } else { (void)status; (void)pObj->valueEntry.error; // false, ScpiNumericError } } else { status = true; pObj->valueEntry.error = ScpiNumericSuccess; } } break; default: (void)status; (void)pObj->valueEntry.error; // false, ScpiNumericError; } if( status ) pObj->valueEntry.error = ScpiNumericSuccess; return status; }