예제 #1
0
MX_EXPORT mx_status_type
mxi_isobus_command( MX_ISOBUS *isobus,
		long isobus_address,
		char *command,
		char *response,
		size_t max_response_length,
		long maximum_retries,
		unsigned long isobus_flags )
{
	static const char fname[] = "mxi_isobus_command()";

	MX_RECORD *interface_record;
	long gpib_address;
	char local_command_buffer[100];
	char *command_ptr;
	size_t length;
	long i, j, rs232_retries;
	unsigned long wait_ms, num_input_bytes_available;
	mx_bool_type error_occurred;
	mx_status_type mx_status;

	if ( isobus == (MX_ISOBUS *) NULL ) {
		return mx_error( MXE_NULL_ARGUMENT, fname,
		"The MX_ISOBUS pointer passed was NULL." );
	}
	if ( command == (char *) NULL ) {
		return mx_error( MXE_NULL_ARGUMENT, fname,
		"The command pointer passed was NULL." );
	}

	interface_record = isobus->isobus_interface.record;

	if ( interface_record == (MX_RECORD *) NULL ) {
		return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname,
	    "The interface record pointer for ISOBUS interface '%s' is NULL.",
			isobus->record->name );
	}

	/* Format the command to be sent. */

	if ( isobus_address < 0 ) {
		command_ptr = command;
	} else {
		command_ptr = local_command_buffer;

		snprintf( local_command_buffer, sizeof(local_command_buffer),
			"@%ld%s", isobus_address, command );
	}

	if ( maximum_retries < 0 ) {
		maximum_retries = LONG_MAX;
	}

	error_occurred = FALSE;

	for ( i = 0; i <= maximum_retries; i++ ) {

		if ( i > 0 ) {
			mx_info( "ISOBUS interface '%s' command retry #%ld.",
				isobus->record->name, i );
		}

		/* Send the command and get the response. */

		if ( isobus_flags & MXF_ISOBUS_DEBUG ) {

			MX_DEBUG(-2,("%s: sending command '%s' to '%s'.",
			    fname, command_ptr, isobus->record->name));
		}

		error_occurred = FALSE;

		if ( interface_record->mx_class == MXI_RS232 ) {
			mx_status = mx_rs232_putline( interface_record,
						command_ptr, NULL, 0 );

			if ( mx_status.code != MXE_SUCCESS )
				return mx_status;

			if ( response != NULL ) {
				/* Wait for the response. */

				rs232_retries = 50;
				wait_ms = 100;

				for ( j = 0; j <= rs232_retries; j++ ) {

					/* See if the first character
					 * has arrived.
					 */

					mx_status =
					  mx_rs232_num_input_bytes_available(
						interface_record,
						&num_input_bytes_available );

					if ( mx_status.code != MXE_SUCCESS ) {
						/* Exit the for(j) loop. */

						break;  
					}

					if ( num_input_bytes_available > 0 ) {
						/* Exit the for(j) loop. */

						break;  
					}
				}

				if ( mx_status.code != MXE_SUCCESS ) {
					error_occurred = TRUE;
				} else {
					/* Read in the response. */

					mx_status = mx_rs232_getline(
						interface_record, response,
						max_response_length, NULL, 0);

					if ( mx_status.code != MXE_SUCCESS ) {
						error_occurred = TRUE;
					} else {
						/* Remove any trailing carriage
						 * return characters.
						 */

						length = strlen( response );

						if (length <
							max_response_length )
						{
							if ( response[length-1]
								== MX_CR )
							{
							    response[length-1]
								= '\0';
							}
						}
					}
				}
			}
		} else {	/* GPIB */

			gpib_address = isobus->isobus_interface.address;

			mx_status = mx_gpib_putline(
						interface_record, gpib_address,
						command_ptr, NULL, 0 );

			if ( mx_status.code != MXE_SUCCESS )
				return mx_status;

			if ( response != NULL ) {
				mx_status = mx_gpib_getline(
					interface_record, gpib_address,
					response, max_response_length, NULL, 0);

				if ( mx_status.code != MXE_SUCCESS ) {
					error_occurred = TRUE;
				}
			}
		}

		if ( error_occurred == FALSE ) {

			/* If the first character in the response is a
			 * question mark '?', then an error occurred.
			 */

			if ( response != NULL ) {
				if ( response[0] == '?' ) {

					mx_status = mx_error(
						MXE_DEVICE_ACTION_FAILED, fname,
			"The command '%s' to ISOBUS interface '%s' failed.  "
			"Controller error message = '%s'", command_ptr,
					isobus->record->name, response );

					error_occurred = TRUE;
				} else {
					if ( isobus_flags & MXF_ISOBUS_DEBUG )
					{
						MX_DEBUG(-2,("%s: received "
						"response '%s' from '%s'",
							fname, response,
							isobus->record->name ));
					}
				}
			}
		}

		if ( error_occurred == FALSE ) {
			break;		/* Exit the for() loop. */
		}
	}

	if ( error_occurred ) {
		return mx_error( MXE_TIMED_OUT, fname,
	"The command '%s' to ISOBUS interface '%s' is still failing "
	"after %ld retries.  Giving up...", command_ptr,
				isobus->record->name,
				maximum_retries );
	} else {
		return MX_SUCCESSFUL_RESULT;
	}
}
예제 #2
0
파일: d_icplus.c 프로젝트: nbeaver/mx-trunk
MX_EXPORT mx_status_type
mxd_icplus_command( MX_ICPLUS *icplus,
			char *command,
			char *response,
			size_t response_buffer_length,
			int debug_flag )
{
	static const char fname[] = "mxd_icplus_command()";

	char c;
	int i, max_attempts;
	unsigned long sleep_ms, num_input_bytes_available;
	mx_status_type mx_status, mx_status2;

	if ( icplus == (MX_ICPLUS *) NULL ) {
		return mx_error( MXE_NULL_ARGUMENT, fname,
		"NULL MX_ICPLUS pointer passed." );
	}
	if ( command == NULL ) {
		return mx_error( MXE_NULL_ARGUMENT, fname,
		"NULL command buffer pointer passed." );
	}

	if ( debug_flag ) {
		MX_DEBUG(-2,("%s: sending '%s' to '%s'",
				fname, command, icplus->record->name ));
	}

	/* Send the command string. */

	mx_status = mx_rs232_putline( icplus->rs232_record, command, NULL, 0 );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	/* The IC PLUS always sends an ACK character to acknowledge receipt
	 * of the LF terminator for the command that we just sent.  Even if
	 * we expect no other response, we must still read and discard this
	 * ACK character.
	 */

	mx_status = mx_rs232_getchar( icplus->rs232_record, &c, MXF_232_WAIT );

	if ( mx_status.code == MXE_NOT_READY ) {
		return mx_error( MXE_NOT_READY, fname,
		"No response received from %s amplifier '%s' "
		"for command '%s'.  Are you sure it is plugged in "
		"and turned on?",
			mx_get_driver_name( icplus->record ),
			icplus->record->name, command );
	}

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	if ( c != MX_ACK ) {
		(void) mx_rs232_discard_unread_input( icplus->rs232_record,
							MXD_ICPLUS_DEBUG );

		return mx_error( MXE_DEVICE_IO_ERROR, fname,
	"Did not receive an ACK acknowledgement character from "
	"%s interface '%s' in response to the command '%s'.  "
	"Instead, saw a %#x (%c) character.",
			mx_get_driver_name( icplus->record ),
			icplus->record->name, command, c, c );
	}

	/* If we expect a response, then read it in. */

	if ( response != NULL ) {
		mx_status = mx_rs232_getline( icplus->rs232_record,
					response, response_buffer_length,
					NULL, 0 );

		if ( debug_flag & (mx_status.code == MXE_SUCCESS) ) {
			MX_DEBUG(-2,("%s: received '%s' from '%s'",
				fname, response, icplus->record->name ));
		}
	} else {
		if ( debug_flag ) {
			MX_DEBUG(-2,("%s complete.", fname));
		}
	}

	/* If the IC PLUS echoes the command line back to us, then we must
	 * throw this away.
	 */

	if ( icplus->discard_echoed_command_line ) {
		max_attempts = 100;
		sleep_ms = 1;

		for ( i = 0; i < max_attempts; i++ ) {
			mx_status2 = mx_rs232_num_input_bytes_available(
						icplus->rs232_record,
						&num_input_bytes_available );

			if ( mx_status2.code != MXE_SUCCESS )
				break;

			if ( num_input_bytes_available > 0 )
				break;

			mx_msleep( sleep_ms );
		}

		if ( i >= max_attempts ) {
			mx_status = mx_error( MXE_TIMED_OUT, fname,
				"Timed out waiting for %s record '%s' to echo "
				"the command line '%s' back to us.",
					mx_get_driver_name( icplus->record ),
					icplus->record->name, command );
		}
		(void) mx_rs232_discard_unread_input(
					icplus->rs232_record, FALSE );
	}

	return mx_status;
}
예제 #3
0
MX_EXPORT mx_status_type
mxd_src_mono_get_extended_status( MX_MOTOR *motor )
{
	static const char fname[] = "mxd_src_mono_get_extended_status()";

	MX_SRC_MONO *src_mono = NULL;
	unsigned long num_input_bytes_available;
	double current_position;
	int num_items;
	char response[200];
	mx_status_type mx_status;

	mx_status = mxd_src_mono_get_pointers( motor, &src_mono, NULL, fname );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

#if MXD_SRC_MONO_DEBUG
	MX_DEBUG(-2,("%s invoked for motor '%s'.", fname, motor->record->name));
	MX_DEBUG(-2,("%s: Starting src_mono->state = %d",
		fname, src_mono->state ));
#endif

	motor->status = MXSF_MTR_ERROR;

	switch( src_mono->state ) {
	case MXS_SRC_MONO_ERROR:
		/* Currently, all we can do is close our eyes, be optimistic
		 * and hope that any existing errors have gone away.  We do
		 * generate a warning about that though.
		 */

		src_mono->state = MXS_SRC_MONO_IDLE;

		mx_warning( "Resetting SRC mono error for motor '%s'.",
			motor->record->name );
		break;

	case MXS_SRC_MONO_TIMED_OUT:
		/* Generate a warning that we are ignoring a timeout. */

		src_mono->state = MXS_SRC_MONO_IDLE;

		mx_warning( "Ignoring timeout for motor '%s'.",
			motor->record->name );
		break;

	case MXS_SRC_MONO_ACTIVE:
		/* A move is currently supposed to be in progress.
		 * Has the move completed yet?  We check for this
		 * by seeing whether or not the SRC computer has
		 * sent us a message.
		 */

		mx_status = mx_rs232_num_input_bytes_available(
						src_mono->rs232_record,
						&num_input_bytes_available );

		if ( mx_status.code != MXE_SUCCESS ) {
			src_mono->state = MXS_SRC_MONO_ERROR;
			break;
		}

		if ( num_input_bytes_available == 0 ) {
			/* We have not been sent any new characters, so
			 * the move must not yet be complete.
			 */

			break;
		}

		/* Apparently the move has completed.  We now verify this
		 * by looking for a scan complete message.
		 */

		mx_status = mx_rs232_getline( src_mono->rs232_record,
						response, sizeof(response),
						NULL, MXD_SRC_MONO_DEBUG );

		if ( mx_status.code == MXE_TIMED_OUT ) {
			src_mono->state = MXS_SRC_MONO_TIMED_OUT;
		} else
		if ( mx_status.code != MXE_SUCCESS ) {
			src_mono->state = MXS_SRC_MONO_ERROR;
		} else {
			if ( strcmp( response, "OK-Scan Completed!" ) == 0 ) {
				src_mono->state = MXS_SRC_MONO_IDLE;
			} else {
				mx_status = mx_error(MXE_DEVICE_IO_ERROR, fname,
				"Received unexpected response '%s' from "
				"SRC monochromator '%s'.",
					response, motor->record->name );
			}
		}
		break;

	case MXS_SRC_MONO_IDLE:
		/* If the mono is idle, then there is nothing that we
		 * need to do at this point in the function.
		 */

		break;

	default:
		return mx_error( MXE_ILLEGAL_ARGUMENT, fname,
		"SRC mono '%s' is in illegal state %d.  "
		"This should never happen, but if it does, try restarting MX.",
			motor->record->name, src_mono->state );
	}

	if ( src_mono->state == MXS_SRC_MONO_ACTIVE ) {
		motor->status = MXSF_MTR_IS_BUSY;

		return MX_SUCCESSFUL_RESULT;
	}

	/* We should only get here if the monochromator is now idle.  
	 * It should now be safe to ask for the current position.
	 */

	mx_status = mx_rs232_putline( src_mono->rs232_record,
				"ENERGY", NULL, MXD_SRC_MONO_DEBUG );

	if ( mx_status.code != MXE_SUCCESS ) {
		src_mono->state = MXS_SRC_MONO_ERROR;
		return mx_status;
	}

	/* Now read back the motor position. */

	mx_status = mx_rs232_getline( src_mono->rs232_record,
					response, sizeof(response),
					NULL, MXD_SRC_MONO_DEBUG );

	if ( mx_status.code == MXE_TIMED_OUT ) {
		src_mono->state = MXS_SRC_MONO_TIMED_OUT;
		return mx_status;
	} else
	if ( mx_status.code != MXE_SUCCESS ) {
		src_mono->state = MXS_SRC_MONO_ERROR;
		return mx_status;
	}

	num_items = sscanf( response, "%lg", &current_position );

	if ( num_items != 1 ) {
		src_mono->state = MXS_SRC_MONO_ERROR;
		return mx_error( MXE_DEVICE_IO_ERROR, fname,
		"Did not find the position for SRC mono '%s' in the "
		"response '%s' to the 'ENERGY' command.",
			motor->record->name, response );
	}

	motor->raw_position.analog = current_position;

	motor->status = 0;

	return MX_SUCCESSFUL_RESULT;
}
예제 #4
0
파일: d_icplus.c 프로젝트: nbeaver/mx-trunk
MX_EXPORT mx_status_type
mxd_icplus_open( MX_RECORD *record )
{
	static const char fname[] = "mxd_icplus_open()";

	MX_AMPLIFIER *amplifier;
	MX_ICPLUS *icplus;
	MX_RS232 *rs232;
	char command[40];
	char response[80];
	int timed_out;
	unsigned long i, max_attempts, wait_ms, num_input_bytes_available;
	mx_status_type mx_status;

	amplifier = NULL;
	icplus = NULL;

	mx_status = mxd_icplus_get_pointers( record,
					&amplifier, &icplus, fname );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	mx_status = mx_rs232_get_pointers( icplus->rs232_record,
						&rs232, NULL, fname );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	MX_DEBUG( 2,("%s invoked for record '%s'.", fname, record->name));

	/* The ICPLUS driver does not use the QBPM flags. */

	if ( record->mx_type == MXT_AMP_ICPLUS ) {
		icplus->qbpm_flags = 0;
	}

	/* See if the serial port is configured correctly. */

	if( record->mx_type == MXT_AMP_ICPLUS ) {
		mx_status = mx_rs232_verify_configuration( icplus->rs232_record,
					9600, 8, 'N', 1, 'N', 0x0a, 0x0a,
					rs232->timeout );
	} else {
		mx_status = mx_rs232_verify_configuration( icplus->rs232_record,
					19200, 8, 'N', 1, 'N', 0x0a, 0x0a,
					rs232->timeout );
	}

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	/* Throw away any leftover characters. */

	mx_status = mx_rs232_discard_unwritten_output( icplus->rs232_record,
							MXD_ICPLUS_DEBUG );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	mx_status = mx_rs232_discard_unread_input( icplus->rs232_record,
							MXD_ICPLUS_DEBUG );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	/* If the RS-232 port does not have a timeout specified, set
	 * the timeout to 1 second.
	 */

	if ( rs232->timeout < 0.0 ) {
		rs232->timeout = 1.0;

		MX_DEBUG( 2,("%s: forcing the timeout to 1 second.", fname));
	}

	/* See if the IC PLUS is available by trying to read
	 * the input current.
	 */

	if ( icplus->record->mx_type == MXT_AMP_ICPLUS ) {
		snprintf( command, sizeof(command),
				":READ%ld:CURR?", icplus->address );
	} else {
		snprintf( command, sizeof(command),
				":READ%ld:CURR1?", icplus->address );
	}

	wait_ms = 100;
	max_attempts = 5;
	timed_out = FALSE;

	for ( i = 0; i < max_attempts; i++ ) {
		mx_status = mxd_icplus_command( icplus, command,
					response, sizeof response,
					MXD_ICPLUS_DEBUG );

		switch( mx_status.code ) {
		case MXE_SUCCESS:
			timed_out = FALSE;
			break;
		case MXE_NOT_READY:
		case MXE_TIMED_OUT:
			timed_out = TRUE;
			break;
		default:
			return mx_status;
			break;
		}

		if ( timed_out == FALSE )
			break;			/* Exit the for() loop. */

		/* Resynchronize the serial port.  This will cause
		 * the serial port to be closed and then reopened.
		 */

#if MXD_ICPLUS_DEBUG
		MX_DEBUG(-2,("%s: resynchronizing the serial port.", fname));
#endif

		mx_status = mx_resynchronize_record( icplus->rs232_record );

		if ( mx_status.code != MXE_SUCCESS )
			return mx_status;

		mx_msleep( wait_ms );
	}

	/* If there are still characters available from the RS-232 port,
	 * then the serial port is echoing back part of the transmitted
	 * command.  This means that the RS-232 cable is incorrectly
	 * wired, but we will attempt to continue anyway.
	 */

	mx_status = mx_rs232_num_input_bytes_available( icplus->rs232_record,
						&num_input_bytes_available );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	if ( num_input_bytes_available > 0 ) {
		icplus->discard_echoed_command_line = TRUE;

		(void) mx_rs232_discard_unread_input( icplus->rs232_record,
						      	FALSE );

		mx_warning(
	"Some or all of the command string transmitted to '%s' device '%s' "
	"was echoed back to the serial port.  This means that the RS-232 "
	"cable is incorrectly wired, but we will attempt to continue by "
	"discarding the echoed characters.  However, this slows down the "
	"driver, so it would be better to fix the wiring.",
			mx_get_driver_name( icplus->record ), record->name );

	}

	/* Set the gain, offset, and peaking time. */

	mx_status = mx_amplifier_set_gain( record, amplifier->gain );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	mx_status = mx_amplifier_set_offset( record, amplifier->offset );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	mx_status = mx_amplifier_set_time_constant( record,
						amplifier->time_constant );

	if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

	/* If this is a QBPM controller, set the initial averaging. */

	if ( record->mx_type == MXT_AMP_QBPM ) {
		if ( icplus->default_averaging > 100 ) {
			return mx_error( MXE_WOULD_EXCEED_LIMIT, fname,
		"The requested averaging size of %ld for record '%s' is "
		"outside the allowed range of 1 to 100.",
				icplus->default_averaging, record->name );
		} else
		if ( icplus->default_averaging >= 1 ) {
			snprintf( command, sizeof(command),
					":READ%ld:AVGCURR %ld",
					icplus->address,
					icplus->default_averaging );
		} else
		if ( icplus->default_averaging > -1 ) {
			snprintf( command, sizeof(command),
					":READ%ld:SINGLE",
					icplus->address );
		} else
		if ( icplus->default_averaging >= -100 ) {
			snprintf( command, sizeof(command),
					":READ%ld:WDWCURR %ld",
					icplus->address,
					-(icplus->default_averaging) );
		} else {
			return mx_error( MXE_WOULD_EXCEED_LIMIT, fname,
		"The requested moving average size of %ld for record '%s' is "
		"outside the allowed range of -1 to -100.",
				icplus->default_averaging, record->name );
		}

		mx_status = mxd_icplus_command( icplus, command, NULL, 0,
							MXD_ICPLUS_DEBUG );
	}

	MX_DEBUG( 2,("%s complete.", fname));

	return MX_SUCCESSFUL_RESULT;
}
예제 #5
0
MX_EXPORT mx_status_type
mxi_compumotor_command( MX_COMPUMOTOR_INTERFACE *compumotor_interface,
		char *command, char *response, size_t response_buffer_length,
		int command_flags )
{
	static const char fname[] = "mxi_compumotor_command()";

	MX_RS232 *rs232;
	unsigned long interface_flags;
	unsigned long sleep_ms, num_bytes_available;
	long num_command_attempts;
	long i, max_response_attempts;
	size_t command_length, response_length;
	char c;
	char echoed_command_string[200];
	mx_bool_type debug_flag, debug_getchar_flag;
	mx_status_type mx_status;
#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING	
	MX_HRT_RS232_TIMING command_timing, response_timing;
#endif

	debug_flag = FALSE;

	MX_DEBUG(2,("%s invoked.", fname));

	if ( compumotor_interface == NULL ) {
		return mx_error( MXE_NULL_ARGUMENT, fname,
		"MX_COMPUMOTOR_INTERFACE pointer passed was NULL." );
	}
	if ( command == NULL ) {
		return mx_error( MXE_NULL_ARGUMENT, fname,
		"'command' buffer pointer passed was NULL.  No command sent.");
	}
	if ( compumotor_interface->rs232_record == (MX_RECORD *) NULL ) {
		return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname,
		"The rs232_record pointer for record '%s' is NULL.",
			compumotor_interface->record->name );
	}

	rs232 = (MX_RS232 *)
			compumotor_interface->rs232_record->record_class_struct;

	if ( rs232 == (MX_RS232 *) NULL ) {
		return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname,
		"The MX_RS232 pointer for record '%s' used by '%s' is NULL.",
			compumotor_interface->rs232_record->name,
			compumotor_interface->record->name );
	}

	interface_flags = compumotor_interface->interface_flags;

	if ( interface_flags & MXF_COMPUMOTOR_ECHO_ON ) {

		command_length = strlen( command );

		if ( command_length > ( sizeof(echoed_command_string) - 1 ) ) {
			return mx_error( MXE_WOULD_EXCEED_LIMIT, fname,
	"Compumotor interface record '%s' has ECHO set to 1.  When ECHO is "
	"set to 1, commands are limited to a maximum of %ld characters, but "
	"the command string passed is %ld characters long.  command = '%s'",
			compumotor_interface->record->name,
			(long) sizeof( echoed_command_string ) - 1L,
			(long) command_length, command );
		}
	}

	if ( command_flags & MXI_COMPUMOTOR_INTERFACE_DEBUG ) {
		debug_flag = TRUE;
	} else
	if ( interface_flags & MXF_COMPUMOTOR_DEBUG_SERIAL ) {
	} else {
		debug_flag = FALSE;
	}

	if ( command_flags & MXF_COMPUMOTOR_NO_RECURSION ) {
		num_command_attempts = 1;
	} else
	if ( interface_flags & MXF_COMPUMOTOR_AUTOMATIC_RESYNCHRONIZE ) {
		num_command_attempts = 2;
	} else {
		num_command_attempts = 1;
	}

	if ( interface_flags & MXF_COMPUMOTOR_DEBUG_SERIAL_GETCHAR ) {
		if ( interface_flags & MXF_COMPUMOTOR_DEBUG_SERIAL ) {
			debug_getchar_flag = TRUE;
		} else
		if ( rs232->rs232_flags & MXF_232_DEBUG_SERIAL ) {
			debug_getchar_flag = TRUE;
		} else
		if ( rs232->rs232_flags & MXF_232_DEBUG_SERIAL_HEX ) {
			debug_getchar_flag = TRUE;
		} else {
			debug_getchar_flag = FALSE;
		}
	} else {
		debug_getchar_flag = FALSE;
	}

	/* Send the command string. */

	if ( debug_flag ) {
		MX_DEBUG(-2,("%s: sending '%s' to '%s'",
			fname, command, compumotor_interface->record->name));
	}

	while ( num_command_attempts > 0 ) {

	    num_command_attempts--;

#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING	
	    MX_HRT_RS232_START_COMMAND( command_timing, 2 + strlen(command) );
#endif

	    mx_status = mx_rs232_putline( compumotor_interface->rs232_record,
					command, NULL, 0 );

#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING
	    MX_HRT_RS232_END_COMMAND( command_timing );
#endif

	    if ( mx_status.code != MXE_SUCCESS )
		return mx_status;

#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING
	MX_HRT_RS232_COMMAND_RESULTS( command_timing, command, fname );
#endif

	    if ( interface_flags & MXF_COMPUMOTOR_ECHO_ON ) {

		/* If the Compumotor is configured to echo commands,
		 * read the echoed command string.
		 */

#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING
		MX_HRT_RS232_START_RESPONSE( response_timing,
					compumotor_interface->rs232_record );
#endif

		mx_status = mx_rs232_getline(compumotor_interface->rs232_record,
					echoed_command_string,
					sizeof( echoed_command_string ) - 1L,
					NULL, 0 );

#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING
		MX_HRT_RS232_END_RESPONSE( response_timing,
					strlen(echoed_command_string) );

		MX_HRT_RS232_RESPONSE_RESULTS( response_timing,
					echoed_command_string, fname );
#endif

		if ( mx_status.code != MXE_SUCCESS )
			return mx_status;
	    }

	    /* Get the response, if one is expected. */

	    i = 0;

	    max_response_attempts = 1000;
	    sleep_ms = 1;

	    if ( response == NULL ) {

		/* If we are not checking for a response, then we have no
		 * way to tell whether or not the command succeeded.  That
		 * means that we have no justification for retrying a
		 * command, so we suppress command retries.
		 */

		num_command_attempts = 0;

	    } else {
		/* If we get here, a response is expected. */

#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING
#if 0
		MX_HRT_RS232_START_RESPONSE( response_timing,
					compumotor_interface->rs232_record );
#else
		MX_HRT_RS232_START_RESPONSE( response_timing, NULL );
#endif
#endif

		/* Any text sent by the Compumotor controller should
		 * be prefixed by an asterisk character, so to begin
		 * with, we try to read and discard characters until
		 * we see an asterisk.
		 */

		c = '\0';

		for ( i = 0; i < max_response_attempts; i++ ) {

			mx_status = mx_rs232_num_input_bytes_available(
					compumotor_interface->rs232_record,
					&num_bytes_available );

			if ( mx_status.code != MXE_SUCCESS )
				return mx_status;

			if ( num_bytes_available == 0 ) {
				/* Sleep and then go back to the top
				 * of the for() loop.
				 */

				mx_msleep(sleep_ms);
				continue;
			}

			mx_status = mx_rs232_getchar(
					compumotor_interface->rs232_record,
					&c, MXF_232_WAIT );

			if ( debug_getchar_flag ) {
				MX_DEBUG(-2,
				("%s: mx_rs232_getchar() = '%c' %#x",
					fname, c, c));
			}

			if ( mx_status.code != MXE_SUCCESS ) {
				response[0] = '\0';

				if ( debug_flag ) {
					MX_DEBUG(-2,
		("%s failed while waiting for an asterisk character from '%s'.",
				fname, compumotor_interface->record->name));
				}
				return mx_status;
			}

			/* Did we see the asterisk character? */

			if ( c != '*' ) {
				/* If not, sleep and then go back to
				 * the top of the for() loop.
				 */

				mx_msleep(sleep_ms);
				continue;	
			}

			/* Read in the Compumotor response. */

			mx_status = mx_rs232_getline(
					compumotor_interface->rs232_record,
					response, response_buffer_length,
					NULL, 0 );

			if ( mx_status.code == MXE_SUCCESS ) {
				break;		/* Exit the for() loop. */

			} else if ( mx_status.code != MXE_NOT_READY ) {
				MX_DEBUG(-2,
		("*** Exiting with status = %ld for Compumotor interface '%s'.",
			mx_status.code, compumotor_interface->record->name));

				return mx_status;
			}
			mx_msleep(sleep_ms);

		}   /* End of response attempt loop (i) */

		if ( i >= max_response_attempts ) {

		    if ( num_command_attempts > 0 ) {
			mx_warning( "Resynchronizing '%s'.",
					compumotor_interface->record->name );

			mx_status = mxi_compumotor_resynchronize(
					compumotor_interface->record );

			if ( mx_status.code != MXE_SUCCESS )
				return mx_status;

			mx_warning( "Resynchronization of '%s' complete.",
					compumotor_interface->record->name );
		    } else {
			mx_status = mx_rs232_discard_unread_input(
					compumotor_interface->rs232_record,
					debug_flag );

			if ( mx_status.code != MXE_SUCCESS ) {
				mx_error( MXE_INTERFACE_IO_ERROR, fname,
"Failed at attempt to discard unread characters in buffer for record '%s'",
					compumotor_interface->record->name );
			}

			return mx_error( MXE_TIMED_OUT, fname,
				"No response seen to '%s' command after "
				"%ld attempts to read from Compumotor "
				"interface '%s'.",
					command, max_response_attempts,
					compumotor_interface->record->name );
		    }
		} else {

		    /* Successfully got a response. */

		    num_command_attempts = 0;    /* No more attempts needed. */

		    /* Sometimes, the response string after the asterisk '*'
		     * character starts with a newline character.  If this
		     * has happened, strip off the leading newline.
		     */

		    if ( response[0] == MX_LF ) {
			response_length = strlen(response);

			memmove( response, response+1, response_length );
		    }

#if MXI_COMPUMOTOR_INTERFACE_DEBUG_TIMING
		    MX_HRT_RS232_END_RESPONSE( response_timing,
						strlen(response) );

		    MX_HRT_TIME_BETWEEN_MEASUREMENTS( command_timing,
						response_timing, fname );

		    MX_HRT_RS232_RESPONSE_RESULTS( response_timing,
						response, fname);
#endif

		    if ( debug_flag ) {
			MX_DEBUG(-2,("%s: received '%s' from '%s'",
				fname, response,
				compumotor_interface->record->name));
		    }
#if 0
		    {
			long j;

			response_length = strlen(response);

			for ( j = 0; j < response_length; j++ ) {
				MX_DEBUG(-2,("%s: response[%ld] = %#x '%c'",
				fname, j, response[j], response[j]));
			}
		    }
#endif
		}

	    } /* End of ( response != NULL ) block */

	} /* End of while() command attempt loop. */

	MX_DEBUG(2,("%s complete.", fname));

	return MX_SUCCESSFUL_RESULT;
}