MX_EXPORT mx_status_type mxd_uglide_soft_abort( MX_MOTOR *motor ) { static const char fname[] = "mxd_uglide_soft_abort()"; MX_UGLIDE_MOTOR *uglide_motor; MX_UGLIDE *uglide; mx_status_type mx_status; mx_status = mxd_uglide_get_pointers( motor, &uglide_motor, &uglide, fname ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; MX_DEBUG( 2,("%s invoked for motor '%s'.", fname, motor->record->name)); /* Send the single character 's' to the controller. */ #if UGLIDE_DEBUG MX_DEBUG(-2,("%s: sending soft abort character 's' to '%s'", fname, uglide->record->name )); #endif mx_status = mx_rs232_putline( uglide->rs232_record, "s", NULL, 0 ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Wait a moment for the move to stop and then discard all * existing responses from the controller. */ mx_msleep(500); mx_status = mx_rs232_discard_unread_input( uglide->rs232_record, UGLIDE_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Tell the controller record that the last command executed was * a position report. */ uglide->last_response_code = MXF_UGLIDE_POSITION_REPORT; /* Refresh the position of the motors. */ mx_status = mxi_uglide_command( uglide, "q", UGLIDE_DEBUG ); return mx_status; }
MX_EXPORT mx_status_type mxd_src_mono_open( MX_RECORD *record ) { static const char fname[] = "mxd_src_mono_open()"; MX_MOTOR *motor = NULL; MX_SRC_MONO *src_mono = NULL; MX_RS232 *rs232 = NULL; mx_status_type mx_status; motor = (MX_MOTOR *) record->record_class_struct; mx_status = mxd_src_mono_get_pointers(motor, &src_mono, &rs232, fname); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Verify the RS-232 port parameters. */ #if 0 mx_status = mx_rs232_verify_configuration( src_mono->rs232_record, 9600, 8, 'N', 1, 'N', 0x0d0a, 0x0d, rs232->timeout ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; #endif /* Discard any existing bytes in the serial port. */ (void) mx_rs232_discard_unwritten_output( src_mono->rs232_record, MXD_SRC_MONO_DEBUG ); mx_status = mx_rs232_discard_unread_input( src_mono->rs232_record, MXD_SRC_MONO_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Verify that the connection is alive by asking for the * current position. */ src_mono->state = MXS_SRC_MONO_IDLE; mx_status = mxd_src_mono_get_extended_status( motor ); return mx_status; }
MX_EXPORT mx_status_type mxi_compumotor_close( MX_RECORD *record ) { static const char fname[] = "mxi_compumotor_close()"; MX_COMPUMOTOR_INTERFACE *compumotor_interface; mx_status_type mx_status; if ( record == (MX_RECORD *) NULL ) { return mx_error( MXE_NULL_ARGUMENT, fname, "MX_RECORD pointer passed is NULL."); } compumotor_interface = (MX_COMPUMOTOR_INTERFACE *) (record->record_type_struct); if ( compumotor_interface == (MX_COMPUMOTOR_INTERFACE *) NULL ) { return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "MX_COMPUMOTOR_INTERFACE pointer for record '%s' is NULL.", record->name); } /* Get rid of any remaining characters in the input and output * buffers and do it quietly. */ (void) mx_rs232_discard_unwritten_output( compumotor_interface->rs232_record, FALSE ); mx_status = mx_rs232_discard_unread_input( compumotor_interface->rs232_record, FALSE ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* If a shutdown program is defined, then run it. */ if ( strlen( compumotor_interface->shutdown_program ) > 0 ) { mx_status = mxi_compumotor_run_program( compumotor_interface, "shutdown", compumotor_interface->shutdown_program ); } return mx_status; }
MX_EXPORT mx_status_type mxd_icplus_resynchronize( MX_RECORD *record ) { static const char fname[] = "mxd_icplus_resynchronize()"; MX_ICPLUS *icplus; char command[40]; mx_status_type mx_status; icplus = NULL; mx_status = mxd_icplus_get_pointers( record, NULL, &icplus, fname ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; MX_DEBUG( 2,("%s invoked for record '%s'.", fname, record->name)); 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; /* Reset the IC PLUS. */ snprintf( command, sizeof(command), "*RST%ld", icplus->address ); mx_status = mxd_icplus_command( icplus, command, NULL, 0, MXD_ICPLUS_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; MX_DEBUG( 2,("%s complete.", fname)); return MX_SUCCESSFUL_RESULT; }
MX_EXPORT mx_status_type mxi_isobus_open( MX_RECORD *record ) { static const char fname[] = "mxi_isobus_open()"; MX_ISOBUS *isobus; MX_RECORD *interface_record; unsigned long isobus_flags, read_terminator; mx_status_type mx_status; if ( record == (MX_RECORD *) NULL ) { return mx_error( MXE_NULL_ARGUMENT, fname, "MX_RECORD pointer passed is NULL."); } isobus = (MX_ISOBUS *) record->record_type_struct; if ( isobus == (MX_ISOBUS *) NULL ) { return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "MX_ISOBUS pointer for record '%s' is NULL.", record->name); } isobus_flags = isobus->isobus_flags; #if MXI_ISOBUS_DEBUG MX_DEBUG(-2,("%s invoked for record '%s', isobus_flags = %#lx.", fname, record->name, isobus_flags )); #endif interface_record = isobus->isobus_interface.record; switch( interface_record->mx_class ) { case MXI_RS232: /* Verify that the RS-232 port has the right settings. */ if ( isobus_flags & MXF_ISOBUS_READ_TERMINATOR_IS_LINEFEED ) { read_terminator = MX_LF; } else { read_terminator = MX_CR; } mx_status = mx_rs232_verify_configuration( interface_record, 9600, 8, 'N', 1, 'N', read_terminator, 0x0d ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Reinitialize the serial port. */ mx_status = mx_resynchronize_record( interface_record ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; mx_msleep(1000); /* Discard any characters waiting to be sent or received. */ mx_status = mx_rs232_discard_unwritten_output( interface_record, MXI_ISOBUS_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; mx_status = mx_rs232_discard_unread_input( interface_record, MXI_ISOBUS_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; break; case MXI_GPIB: /* GPIB does not require any initialization. */ break; default: return mx_error( MXE_TYPE_MISMATCH, fname, "Only RS-232 and GPIB interfaces are supported for " "ISOBUS interface '%s'. Interface record '%s' is " "of unsupported type '%s'.", record->name, interface_record->name, mx_get_driver_name( interface_record ) ); break; } return MX_SUCCESSFUL_RESULT; }
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; }
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, &lifier, &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; }
MX_EXPORT mx_status_type mxd_sim980_open( MX_RECORD *record ) { static const char fname[] = "mxd_sim980_open()"; MX_ANALOG_INPUT *ainput; MX_SIM980 *sim980 = NULL; char response[100]; char copy_of_response[100]; int argc, num_items; char **argv; mx_status_type mx_status; if ( record == (MX_RECORD *) NULL ) { return mx_error( MXE_NULL_ARGUMENT, fname, "MX_RECORD pointer passed was NULL." ); } ainput = (MX_ANALOG_INPUT *) record->record_class_struct; mx_status = mxd_sim980_get_pointers( ainput, &sim980, fname ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Discard any outstanding characters. */ mx_status = mx_rs232_discard_unwritten_output( sim980->port_record, MXD_SIM980_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; mx_status = mx_rs232_discard_unread_input( sim980->port_record, MXD_SIM980_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Reset the communication interface by sending a break signal. */ #if MXD_SIM980_DEBUG MX_DEBUG(-2,("%s: sending a break signal to '%s'.", fname, record->name )); #endif mx_status = mx_rs232_send_break( sim980->port_record ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Verify that we are connected to a SIM980 analog PID controller. */ mx_status = mxd_sim980_command( sim980, "*IDN?", response, sizeof(response), MXD_SIM980_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; strlcpy( copy_of_response, response, sizeof(copy_of_response) ); mx_string_split( copy_of_response, ",", &argc, &argv ); if ( argc != 4 ) { free( argv ); return mx_error( MXE_DEVICE_IO_ERROR, fname, "Did not find 4 tokens in the response '%s' to " "the *IDN? command sent to '%s'.", response, record->name ); } if ( strcmp( argv[0], "Stanford_Research_Systems" ) != 0 ) { free( argv ); return mx_error( MXE_ILLEGAL_ARGUMENT, fname, "Controller '%s' is not a Stanford Research Systems device. " "The response to '*IDN?' was '%s'.", record->name, response ); } if ( strcmp( argv[1], "SIM980" ) != 0 ) { free( argv ); return mx_error( MXE_ILLEGAL_ARGUMENT, fname, "Device '%s' is not a SIM980 analog PID controller. " "The response to '*IDN?' was '%s'.", record->name, response ); } /* Get the version number. */ num_items = sscanf( argv[3], "ver%lf", &(sim980->version) ); if ( num_items != 1 ) { mx_status = mx_error( MXE_DEVICE_IO_ERROR, fname, "Did not find the SIM980 version number in the token '%s' " "contained in the response '%s' to '*IDN?' by controller '%s'.", argv[3], response, record->name ); free( argv ); return mx_status; } return mx_status; }
MX_EXPORT mx_status_type mxi_keithley2000_open( MX_RECORD *record ) { static const char fname[] = "mxi_keithley2000_open()"; static char idcode[] = "KEITHLEY INSTRUMENTS INC.,MODEL 20"; MX_KEITHLEY2000 *keithley2000 = NULL; MX_INTERFACE *interface = NULL; char response[160]; mx_status_type mx_status; mx_status = mxi_keithley2000_get_pointers( record, &keithley2000, &interface, fname ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; switch ( interface->record->mx_class ) { case MXI_RS232: mx_status = mx_rs232_discard_unread_input( interface->record, KEITHLEY2000_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; break; case MXI_GPIB: mx_status = mx_gpib_open_device( interface->record, interface->address ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; mx_status = mx_gpib_selective_device_clear( interface->record, interface->address ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; #if 0 mx_status = mx_gpib_remote_enable( interface->record, interface->address ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; #endif break; default: return mx_error( MXE_TYPE_MISMATCH, fname, "Interface '%s' for Keithley 2000 record '%s' " "is not an RS-232 or GPIB record.", interface->record->name, record->name ); } /**** Find out what kind of controller this is. ****/ /* Need to avoid mxi_keithley_command() at this stage, * since that function automatically calls *STB?, which * we probably do not want when we are trying to establish * what kind of module this is. */ mx_status = mxi_keithley_putline( record, interface, "*IDN?", KEITHLEY2000_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; mx_status = mxi_keithley_getline( record, interface, response, sizeof(response), KEITHLEY2000_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; MX_DEBUG( 2,("%s: *IDN? response for record '%s' is '%s'", fname, record->name, response)); if ( strncmp( response, idcode, strlen(idcode) ) != 0 ) { return mx_error( MXE_TYPE_MISMATCH, fname, "The controller '%s' is not a Keithley 2000 series multimeter. " "Its response to an identification query command '*IDN?' was '%s'.", record->name, response ); } /* Clear the 2000 Error Queue. */ mx_status = mxi_keithley_command( record, interface, "SYST:CLE", NULL, 0, KEITHLEY2000_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; keithley2000->last_measurement_type = MXT_KEITHLEY2000_UNKNOWN; return mx_status; }
MX_EXPORT mx_status_type mxi_numato_gpio_open( MX_RECORD *record ) { static const char fname[] = "mxi_numato_gpio_open()"; MX_NUMATO_GPIO *numato_gpio = NULL; unsigned long flags; mx_bool_type debug_rs232; mx_status_type mx_status; #if MXI_NUMATO_GPIO_DEBUG MX_DEBUG(-2,("%s invoked for record '%s'.", fname, record->name )); #endif numato_gpio = (MX_NUMATO_GPIO *) record->record_type_struct; if ( numato_gpio == (MX_NUMATO_GPIO *) NULL ) { return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "MX_NUMATO_GPIO pointer for record '%s' is NULL.", record->name); } flags = numato_gpio->numato_gpio_flags; if ( flags & MXF_NUMATO_GPIO_DEBUG_RS232 ) { debug_rs232 = TRUE; } else { debug_rs232 = FALSE; } /* Make sure that the RS232 line terminators are set correctly. The * write terminator must be set to 0x0d, while the read terminators * must be set to 0x3e0d (or '>\r'). */ #if 0 { MX_RS232 *rs232 = (MX_RS232 *) numato_gpio->rs232_record->record_class_struct; rs232->read_terminators = 0x3e0d; rs232->write_terminators = 0x0d; mx_status = mx_rs232_convert_terminator_characters( numato_gpio->rs232_record ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; } #endif /* Send a <CR> to make sure that any partial commands or junk data * have been discarded. */ mx_status = mx_rs232_putline( numato_gpio->rs232_record, "", NULL, debug_rs232 ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; mx_msleep(1000); /* Discard any leftover bytes in the serial port. */ mx_status = mx_rs232_discard_unwritten_output( numato_gpio->rs232_record, debug_rs232 ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; mx_status = mx_rs232_discard_unread_input( numato_gpio->rs232_record, debug_rs232 ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Read the version number of the Numato firmware. */ mx_breakpoint(); mx_status = mxi_numato_gpio_command( numato_gpio, "ver", numato_gpio->version, sizeof(numato_gpio->version), debug_rs232 ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Read the id number of the Numato device. */ mx_status = mxi_numato_gpio_command( numato_gpio, "id", numato_gpio->id, sizeof(numato_gpio->id), debug_rs232 ); return mx_status; }
MX_EXPORT mx_status_type mxi_spellman_df3_open( MX_RECORD *record ) { static const char fname[] = "mxi_spellman_df3_open()"; MX_SPELLMAN_DF3 *spellman_df3; char response[40]; int num_items; unsigned long software_version; mx_status_type mx_status; if ( record == (MX_RECORD *) NULL ) { return mx_error( MXE_NULL_ARGUMENT, fname, "The MX_RECORD pointer passed was NULL."); } spellman_df3 = (MX_SPELLMAN_DF3 *) record->record_type_struct; if ( spellman_df3 == (MX_SPELLMAN_DF3 *) NULL ) { return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "The MX_SPELLMAN_DF3 pointer for record '%s' is NULL.", record->name); } /* Compute the query command interval in clock ticks. */ if ( spellman_df3->query_interval > 0 ) { spellman_df3->ticks_per_query = mx_convert_seconds_to_clock_ticks( spellman_df3->query_interval ); spellman_df3->next_query_tick = mx_current_clock_tick(); } /* Clear out any existing trash from the RS-232 line. */ mx_status = mx_rs232_discard_unread_input( spellman_df3->rs232_record, MXI_SPELLMAN_DF3_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Verify that the Spellman power supply is present by asking * for its software version. */ mx_status = mxi_spellman_df3_command( spellman_df3, "V", response, sizeof(response), MXI_SPELLMAN_DF3_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; num_items = sscanf( response, "B%lu", &software_version ); if ( num_items != 1 ) { return mx_error( MXE_INTERFACE_IO_ERROR, fname, "The response '%s' to the 'V' command for " "Spellman DF3/FF3 '%s' was not recognizable.", response, record->name ); } #if MXI_SPELLMAN_DF3_DEBUG MX_DEBUG(-2,("%s: Spellman DF3/FF3 '%s' software version = %lu", fname, record->name, software_version )); #endif /* Initialize the monitor arrays by doing a query command. */ mx_status = mxi_spellman_df3_query_command( spellman_df3 ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Copy the voltage and current monitor values to the control array. * * NOTE: The copied values must be rescaled since control values * run from 0 to 0xFFF, while monitor values run from 0 to 0x3FF. * This basically means that the monitor values must be multiplied * by 4. */ spellman_df3->analog_control[MXF_SPELLMAN_DF3_VOLTAGE_CONTROL] = 4 * spellman_df3->analog_monitor[MXF_SPELLMAN_DF3_VOLTAGE_MONITOR]; spellman_df3->analog_control[MXF_SPELLMAN_DF3_CURRENT_CONTROL] = 4 * spellman_df3->analog_monitor[MXF_SPELLMAN_DF3_CURRENT_MONITOR]; /* Copy in the default power and filament current limits. */ spellman_df3->analog_control[MXF_SPELLMAN_DF3_POWER_LIMIT] = (int) spellman_df3->default_power_limit; spellman_df3->analog_control[MXF_SPELLMAN_DF3_FILAMENT_CURRENT_LIMIT] = (int) spellman_df3->default_filament_current_limit; /* Set all of the digital control fields to 0. */ memset( spellman_df3->digital_control, 0, sizeof(spellman_df3->digital_control) ); return MX_SUCCESSFUL_RESULT; }
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; }
MX_EXPORT mx_status_type mxi_compumotor_resynchronize( MX_RECORD *record ) { static const char fname[] = "mxi_compumotor_resynchronize()"; MX_COMPUMOTOR_INTERFACE *compumotor_interface = NULL; char command[80], response[80]; char version_string[80], type_string[80]; long i, j; int num_items, command_flags; mx_status_type mx_status; mx_status = mxi_compumotor_get_pointers( record, &compumotor_interface, fname ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* Throw away any pending input and output on the * Compumotor RS-232 port. */ mx_status = mx_rs232_discard_unwritten_output( compumotor_interface->rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG ); switch( mx_status.code ) { case MXE_SUCCESS: case MXE_UNSUPPORTED: break; /* Continue on. */ default: return mx_status; } mx_status = mx_rs232_discard_unread_input( compumotor_interface->rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG); /* Verify that each of the controllers are there by asking them * for their revision number. */ command_flags = MXI_COMPUMOTOR_INTERFACE_DEBUG | MXF_COMPUMOTOR_NO_RECURSION; for ( i = 0; i < compumotor_interface->num_controllers; i++ ) { snprintf( command, sizeof(command), "%ld_!TREV", compumotor_interface->controller_number[i] ); mx_status = mxi_compumotor_command( compumotor_interface, command, response, sizeof response, command_flags ); switch( mx_status.code ) { case MXE_SUCCESS: break; case MXE_NOT_READY: case MXE_INTERFACE_IO_ERROR: return mx_error( MXE_INTERFACE_IO_ERROR, fname, "Cannot communicate with Compumotor controller %ld on RS-232 port '%s'. " "Is it turned on?", i+1, compumotor_interface->rs232_record->name ); break; default: return mx_status; break; } /* Attempt to determine the model of each type of * controller. The effectiveness of this logic is * limited by the fact that I only have access to * 6K and Zeta 6104 controllers. (W. Lavender) */ compumotor_interface->controller_type[i] = MXT_COMPUMOTOR_UNKNOWN; num_items = sscanf( response, "%s %s", version_string, type_string ); if ( num_items == 1 ) { compumotor_interface->controller_type[i] = MXT_COMPUMOTOR_6000_SERIES; } else if ( num_items == 2 ) { if ( strcmp( type_string, "6K" ) == 0 ) { compumotor_interface->controller_type[i] = MXT_COMPUMOTOR_6K; } else if ( strcmp( type_string, "ZETA6000" ) == 0 ) { compumotor_interface->controller_type[i] = MXT_COMPUMOTOR_ZETA_6000; } else if ( strcmp( type_string, "6104" ) == 0 ) { compumotor_interface->controller_type[i] = MXT_COMPUMOTOR_ZETA_6000; } else { compumotor_interface->controller_type[i] = MXT_COMPUMOTOR_6000_SERIES; } } if ( compumotor_interface->controller_type[i] == MXT_COMPUMOTOR_UNKNOWN ) { (void) mx_error( MXE_UNPARSEABLE_STRING, fname, "Compumotor interface '%s' had an unrecognized response to " "the TREV command. TREV response = '%s'", record->name, response ); } } /* Try to enable all of the axes for each controller. */ for ( i = 0; i < compumotor_interface->num_controllers; i++ ) { snprintf( command, sizeof(command), "%ld_!DRIVE", compumotor_interface->controller_number[i] ); for ( j = 0; j < compumotor_interface->num_axes[i]; j++ ) { strlcat( command, "1", sizeof(command) ); } (void) mxi_compumotor_command( compumotor_interface, command, NULL, 0, command_flags ); } /* Discard any unread output from the controller. */ mx_msleep(100); mx_status = mx_rs232_discard_unread_input( compumotor_interface->rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG ); return mx_status; }
MX_EXPORT mx_status_type mxi_compumotor_open( MX_RECORD *record ) { static const char fname[] = "mxi_compumotor_open()"; MX_COMPUMOTOR_INTERFACE *compumotor_interface; MX_RECORD *rs232_record; MX_RS232 *rs232; long i; unsigned long interface_flags; mx_status_type mx_status; if ( record == (MX_RECORD *) NULL ) { return mx_error( MXE_NULL_ARGUMENT, fname, "MX_RECORD pointer passed is NULL."); } compumotor_interface = (MX_COMPUMOTOR_INTERFACE *) (record->record_type_struct); if ( compumotor_interface == (MX_COMPUMOTOR_INTERFACE *) NULL ) { return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "MX_COMPUMOTOR_INTERFACE pointer for record '%s' is NULL.", record->name); } /* Are the line terminators set correctly? */ rs232_record = compumotor_interface->rs232_record; if ( rs232_record == (MX_RECORD *) NULL ) { return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "rs232_record pointer for Compumotor interface '%s' is NULL.", record->name ); } rs232 = (MX_RS232 *) rs232_record->record_class_struct; if ( rs232 == (MX_RS232 *) NULL ) { return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "MX_RS232 pointer for RS-232 record '%s' is NULL.", compumotor_interface->rs232_record->name ); } #if 0 if ( (rs232->read_terminators != 0x0d0a) || (rs232->write_terminators != 0x0d0a) ) { return mx_error( MXE_ILLEGAL_ARGUMENT, fname, "The Compumotor interface '%s' requires that the line terminators " "of RS-232 record '%s' be a carriage return followed by a line feed. " "Instead saw read terminator %#x and write terminator %#x.", record->name, compumotor_interface->rs232_record->name, rs232->read_terminators, rs232->write_terminators ); } #endif /* If requested, attempt to automatically configure the parameter * settings needed for correct communication handshaking with the * Compumotor controller. The most important ones are * EOT13,10,0 * ERRLVL1 * ECHO1 * MA11111111 */ interface_flags = compumotor_interface->interface_flags; if ( interface_flags & MXF_COMPUMOTOR_AUTO_COMMUNICATION_CONFIG ) { #if MXI_COMPUMOTOR_INTERFACE_DEBUG MX_DEBUG(-2, ("%s: Attempting automatic communication config for controller '%s'.", fname, record->name)); #endif /* Since we do not necessarily have correct handshaking set up * yet, we send the first three commands in the blind. */ (void) mx_rs232_discard_unwritten_output( rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG ); (void) mx_rs232_discard_unread_input( rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG ); mx_status = mx_rs232_putline( rs232_record, "!EOT13,10,0", NULL, MXI_COMPUMOTOR_INTERFACE_DEBUG ); mx_status = mx_rs232_putline( rs232_record, "!ERRLVL1", NULL, MXI_COMPUMOTOR_INTERFACE_DEBUG ); mx_status = mx_rs232_putline( rs232_record, "!ECHO1", NULL, MXI_COMPUMOTOR_INTERFACE_DEBUG ); mx_status = mx_rs232_putline( rs232_record, "!MA11111111", NULL, MXI_COMPUMOTOR_INTERFACE_DEBUG ); if ( interface_flags & MXF_COMPUMOTOR_KILL_ON_STARTUP ) { mx_status = mx_rs232_putline( rs232_record, "!K", NULL, MXI_COMPUMOTOR_INTERFACE_DEBUG ); } /* If all went well, the controllers have been configured * to handshake correctly with the MX driver. We discard * any responses from the controller so far. */ mx_msleep(100); mx_status = mx_rs232_discard_unread_input( rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; } /* If requested, send an '!ADDR1' command to automatically configure * unit addresses. This will not do the right thing for an RS-485 * multi-drop configuration, so it must be optional. */ if ( compumotor_interface->interface_flags & MXF_COMPUMOTOR_AUTO_ADDRESS_CONFIG ) { /* If we are using autoaddressing, the controllers must be * numbered from 1 to num_controllers in the controller_number * array for consistency. If they are not, generate an * error message. */ for ( i = 0; i < compumotor_interface->num_controllers; i++ ) { if ( compumotor_interface->controller_number[i] != i+1 ) { return mx_error( MXE_ILLEGAL_ARGUMENT, fname, "If MXF_COMPUMOTOR_AUTO_ADDRESS_CONFIG is set for record '%s', then the " "controller addresses in the database must be in order from 1 to %ld.", compumotor_interface->record->name, compumotor_interface->num_controllers ); } } /* Send the automatic configuration command. */ (void) mx_rs232_discard_unwritten_output( rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG ); mx_status = mx_rs232_putline( rs232_record, "!ADDR1", NULL, MXI_COMPUMOTOR_INTERFACE_DEBUG ); switch( mx_status.code ) { case MXE_SUCCESS: break; case MXE_NOT_READY: case MXE_INTERFACE_IO_ERROR: return mx_error( MXE_INTERFACE_IO_ERROR, fname, "Cannot set addresses for Compumotor interface '%s' on RS-232 port '%s'. " "Is it turned on?", record->name, compumotor_interface->rs232_record->name ); default: return mx_status; } } /* Synchronize with the controllers. */ mx_status = mxi_compumotor_resynchronize( record ); if ( mx_status.code != MXE_SUCCESS ) return mx_status; /* If a startup program is defined, then run it. */ if ( strlen( compumotor_interface->startup_program ) > 0 ) { mx_status = mxi_compumotor_run_program( compumotor_interface, "startup", compumotor_interface->startup_program ); } return mx_status; }
static mx_status_type mxi_compumotor_run_program( MX_COMPUMOTOR_INTERFACE *compumotor_interface, char *program_type, char *program_filename ) { static const char fname[] = "mxi_compumotor_run_program()"; int saved_errno; FILE *program_file; char program_line[200]; char *ptr; long i, length; mx_bool_type suppress_comments; mx_status_type mx_status; if ( compumotor_interface->interface_flags & MXF_COMPUMOTOR_SUPPRESS_COMMENTS ) { suppress_comments = TRUE; } else { suppress_comments = FALSE; } /* Try to open the startup program file. */ program_file = mx_cfn_fopen( MX_CFN_CONFIG, program_filename, "r" ); if ( program_file == ( FILE * ) NULL ) { saved_errno = errno; return mx_error( MXE_FILE_IO_ERROR, fname, "The attempt by record '%s' to open %s program " "file '%s' failed. Errno = %d, error message = '%s'.", compumotor_interface->record->name, program_type, program_filename, saved_errno, strerror(saved_errno) ); } /* Read the startup program one line at a time * and send it to the Compumotor controller. */ while (1) { mx_fgets( program_line, sizeof(program_line), program_file ); if ( feof(program_file) ) { fclose( program_file ); program_file = NULL; break; /* Exit the while() loop. */ } else if ( ferror(program_file) ) { saved_errno = errno; fclose( program_file ); return mx_error( MXE_FILE_IO_ERROR, fname, "An error occurred while reading from " "%s program file '%s' for record '%s'. " "Errno = %d, error message = '%s'.", program_type, program_filename, compumotor_interface->record->name, saved_errno, strerror(saved_errno) ); } if ( suppress_comments ) { /* Look for the comment character ';'. */ ptr = strchr( program_line, ';' ); if ( ptr != NULL ) { *ptr = '\0'; } /* Suppress trailing spaces or tabs. */ length = strlen( program_line ); for ( i = length-1; i >= 0; i-- ) { if ( program_line[i] == ' ' ) { program_line[i] = '\0'; } else if ( program_line[i] == '\t' ) { program_line[i] = '\0'; } else { break; /* Exit the for() loop. */ } } if ( strlen( program_line ) == 0 ) { /* If we have an empty line, do not send it * to the controller and go back to the top * of the while() loop. */ continue; } } mx_status = mxi_compumotor_command( compumotor_interface, program_line, NULL, 0, MXI_COMPUMOTOR_INTERFACE_DEBUG ); if ( mx_status.code != MXE_SUCCESS ) { fclose( program_file ); return mx_status; } } /* This should never happen, but we check anyway. */ if ( program_file != NULL ) { fclose( program_file ); return mx_error( MXE_CORRUPT_DATA_STRUCTURE, fname, "The FILE pointer for file '%s' used by record '%s' " "was not NULL at a time when the file should " "already be closed.", program_filename, compumotor_interface->record->name ); } /* If the commands we sent generated any output, * we throw the output away now. */ mx_msleep(100); mx_status = mx_rs232_discard_unread_input( compumotor_interface->rs232_record, MXI_COMPUMOTOR_INTERFACE_DEBUG ); return mx_status; }