def test_get_status(self): """Tests to make sure that the get_status() function operates properly.""" serial_mock = Mock() serial_mock.read_until = Mock(return_value=b"?1\r") serial_mock.write = Mock() l = Laser() l._ser = serial_mock # Inject our mock function and play it as if it's a serial.Serial object l.connected = True # Also needed for injection with self.assertRaises(LaserCommandError): l.get_status() serial_mock.write.assert_called_once_with(";LA:SS?\r".encode("ascii")) # Reset our read and write mocks serial_mock.read_until = Mock(return_value=b"3075\r") serial_mock.write = Mock() l._ser = serial_mock status = l.get_status() serial_mock.write.assert_called_once_with(";LA:SS?\r".encode("ascii")) assert status.laser_active assert status.laser_enabled assert status.ready_to_fire assert status.ready_to_enable assert not status.diode_external_trigger assert not status.high_power_mode assert not status.low_power_mode assert not status.resonator_over_temp assert not status.electrical_over_temp assert not status.external_interlock
def test_disarm_command(self): """Tests Laser.disarm(), should return True because we are feeding it a nominal response, and this should result in serial.write being called with the correct command""" serial_mock = Mock() serial_mock.read_until = Mock(return_value=b"OK\r") serial_mock.write = Mock() # NOTE: Major difference from pyserial class, does not return the number of bytes written. l = Laser() # Connect our laser to our mock serial l._ser = serial_mock l.connected = True # Now check the arm command assert l.disarm() == True serial_mock.write.assert_called_once_with(";LA:EN 0\r".encode("ascii"))
def test_get_repetition_rate_range(self): """Tests Laser.get_pulse_period_range""" serial_mock = Mock() serial_mock.read_until = Mock() test_range = [1.0, 5.0] serial_mock.read_until.side_effect = [str(test_range[0]).encode("ascii"), str(test_range[1]).encode("ascii")] serial_mock.write = Mock() l = Laser() l._ser = serial_mock l.connected = True minimum, maximum = l.get_repetition_rate_range() assert minimum == test_range[0] assert maximum == test_range[1] serial_mock.write.assert_any_call(";LA:RR:MIN?\r".encode("ascii")) serial_mock.write.assert_any_call(";LA:RR:MAX?\r".encode("ascii"))
def test_get_repetition_rate_range(self): """Tests Laser.get_pulse_period_range""" serial_mock = Mock() serial_mock.read_until = Mock() test_range = [1.0, 5.0] serial_mock.read_until.side_effect = [str(test_range[0]).encode("ascii"), str(test_range[1]).encode("ascii")] serial_mock.write = Mock() l = Laser() l._ser = serial_mock l.connected = True minimum, maximum = l.get_repetition_rate_range() assert minimum == test_range[0] assert maximum == test_range[1] serial_mock.write.assert_any_call(";LA:RR:MIN?\r".encode("ascii")) serial_mock.write.assert_called_once_with(";LA:DT 0\r".encode("ascii")) assert l.diodeTrigger == 1 # This value should have NOT changed since this command failed.
def test_not_connected(self): """Laser object should raise a ConnectionError if __send_command is sent without being connected to a serial device.""" l = Laser() assert l.connected == False assert l._ser == None with self.assertRaises(ConnectionError): l.is_armed() with self.assertRaises(ConnectionError): # try two different commands for good measure l.get_status()
def test_send_command(self): """Tests Laser._send_command, feeds in a mock serial object. Checking to make sure that write is called and that it returns the reponse we give it.""" serial_mock = Mock() serial_mock.read_until = Mock(return_value=b"OK\r") serial_mock.write = Mock() l = Laser() with self.assertRaises(ConnectionError): l._send_command("HELLO THERE") # This should throw an exception because we have not connected it to a serial object. l._ser = serial_mock l.connected = True assert l._send_command("HELLO WORLD") == b"OK\r" # Ensure that we are returning the serial response serial_mock.write.assert_called_once_with(";LA:HELLO WORLD\r".encode("ascii")) # Ensure that the correct command format is being used
self.assertEqual(laser._send_command("PE 0.01"), b"?5\r\n") self.assertEqual(laser.get_pulse_period(), 0.2) def test_resonator_temp(self): global laser t = laser.get_resonator_temp() print("Resonator temperature: " + str(t)) assert t > -1 assert t < 51 def test_resonator_temp_range(self): global laser pass # TODO: Implement this, it's not that important of a function, but still needed for 100% emulation if __name__ == "__main__": serial_port = "COM2" if len(sys.argv) > 1: serial_port = sys.argv[1] del sys.argv[1] print("Attempting to connect to serial port " + serial_port) serial_conn = Serial(serial_port, 115200) if not serial_conn: print("Failed to connect to serial port, exiting...") exit() laser = Laser() laser.connect(serial_conn) unittest.main()
def test_pulse_width_command(self): """Tests Laser.set_pulse_width, feeds in a mock serial object. Makes sure that the correct data is written and that the properties of the class are changed.""" serial_mock = Mock() serial_mock.read_until = Mock(return_value=b"ok\r\n") serial_mock.write = Mock() l = Laser() l._ser = serial_mock l.connected = True with self.assertRaises(ValueError): l.set_pulse_width(0) # Valid values positive, non-zero numbers with self.assertRaises(ValueError): l.set_pulse_width(-20) # Valid values positive numbers with self.assertRaises(ValueError): l.set_pulse_width("this is not an integer") assert l.set_pulse_width(0.1) serial_mock.write.assert_called_once_with( ";LA:DW 0.1\r".encode("ascii")) assert l.pulseWidth == 0.1 serial_mock.read_until = Mock( return_value=b"?1" ) # Make sure we return False is the laser returns an error serial_mock.write = Mock() with self.assertRaises(LaserCommandError): l.set_pulse_width(0.2) serial_mock.write.assert_called_once_with( ";LA:DW 0.2\r".encode("ascii")) assert l.pulseWidth == 0.1 # This value should have NOT changed since this command failed.
def test_device_address(self): """The only device address listed in the microjewel manual is 'LA'. Make sure this is set correctly in the code.""" l = Laser() assert l._device_address == "LA"
def test_diode_trigger_command(self): """Tests Laser.set_diode_trigger, feeds in a mock serial object. Makes sure that the correct data is written and that the properties of the class are changed.""" serial_mock = Mock() serial_mock.read_until = Mock(return_value=b"ok\r\n") serial_mock.write = Mock() l = Laser() l._ser = serial_mock l.connected = True with self.assertRaises(ValueError): l.set_diode_trigger(6) with self.assertRaises(ValueError): l.set_diode_trigger("this is not an integer") assert l.set_diode_trigger(1) serial_mock.write.assert_called_once_with(";LA:DT 1\r".encode("ascii")) assert l.diodeTrigger == 1 serial_mock.read_until = Mock( return_value=b"?1\r\n" ) # Make sure we return False is the laser returns an error serial_mock.write = Mock() with self.assertRaises(LaserCommandError): l.set_diode_trigger(0) serial_mock.write.assert_called_once_with(";LA:DT 0\r".encode("ascii")) assert l.diodeTrigger == 1 # This value should have NOT changed since this command failed.
def command_loop(): global running, spectrometer, laser, external_trigger_pin, laserSingleShot, sample_mode, integration_time # make the below global variables? currently moved to here since it seems unnecessary integration_time = 6000 # This is the default value the spectrometer is set to mode = "NORMAL" software_trigger_delay = 0 # delays laser firing time?? is this what it means? while running: c = input("?").strip( ) # Get a command from the user and remove any extra whitespace log_input("?" + c) parts = c.split( ) # split the command up into the command and any arguments if c == "help": # check to see what command we were given give_help() elif c == "spectrometer spectrum": if check_spectrometer(spectrometer): continue get_spectrum(spectrometer) elif c == "spectrometer dump_registers": if check_spectrometer(spectrometer): continue dump_settings_register(spectrometer) elif c == "spectrometer query_settings": if check_spectrometer(spectrometer): continue query_settings(spectrometer) elif parts[0:3] == ["spectrometer", "set", "trigger_delay"]: if check_spectrometer(spectrometer): continue if len(parts) < 4: print_cli( "!!! Invalid command: Set Trigger Delay command expects at least 1 argument." ) continue try: t = int(parts[3]) # t is the time in microseconds to delay set_trigger_delay(spectrometer, t) software_trigger_delay = t except ValueError: print_cli( "!!! Invalid argument: Set Trigger Delay command expected an integer." ) continue elif parts[0:3] == ["spectrometer", "set", "integration_time"]: if check_spectrometer(spectrometer): continue if len(parts) < 4: print_cli( "!!! Invalid command: Set Integration Time command expects at least 1 argument." ) continue try: t = int(parts[3]) set_integration_time(spectrometer, t) except ValueError: print_cli( "!!! Invalid argument: Set Integration Time command expected an integer!" ) continue except SeaBreezeError as e: print_cli("!!! " + str(e)) continue elif parts[0:3] == ["spectrometer", "set", "sample_mode"]: if check_spectrometer(spectrometer): continue if len(parts) < 4: print_cli( "!!! Invalid command: Set Sample Mode command expects at least 1 argument." ) continue if parts[3] == "NORMAL" or parts[3] == "EXT_LEVEL" or parts[ 3] == "EXT_SYNC" or parts[3] == "EXT_EDGE": set_sample_mode(spectrometer, parts[3]) mode = parts[3] else: print_cli( "!!! Invalid argument: Set Sample Mode command expected one of: NORMAL, EXT_SYNC, EXT_LEVEL, EXT_EDGE" ) continue elif c == "spectrometer get integration_time": if check_spectrometer(spectrometer): continue print_cli("Spectrometer integration time set to " + str(integration_time) + " microseconds.") continue elif c == "status": give_status(spectrometer, laser) continue elif parts[0:4] == ["set", "external_trigger_pin"]: if len(parts) < 3: print_cli( "!!! Invalid command: Set external trigger pin command expects at least 1 argument." ) continue try: pin = parts[2] if not pin.startswith("P8_") or pin.startswith("P9_"): raise ValueError("Invalid pin!") set_external_trigger_pin(spectrometer, pin) external_trigger_pin = pin except: cli_print( "!!! " + pin + " is not a valid pin name! Should follow format such as: P8_22 or P9_16 (these are examples)." ) continue elif c == "get external_trigger_pin": print_cli("External trigger pin is set to: " + external_trigger_pin) continue elif parts[0:3] == ["spectrometer", "connect"]: if len(parts) == 2: spectrometer = auto_connect_spectrometer() elif len(parts) == 3: spectrometer = connect_spectrometer(parts[2]) elif c == "laser connect": port = user_select_port() if not port: cli_print("!!! Aborting connect laser.") continue laser = Laser() print_cli("Connecting to laser...") laser.connect(port) print_cli("Refreshing settings...") laser.refresh_parameters() s = laser.get_status() if not s: cli_print("!!! Failed to connect to laser!") continue cli_print("Laser Status:") cli_print("ID: " + laser.get_laser_ID() + "\n") cli_print(str(s)) print_cli("Rep rate: " + str(laser.repRate) + "Hz") print_cli("Pulse width: " + str(laser.pulseWidth) + "s") print_cli("Pulse mode: " + str(laser.pulseMode)) print_cli("Burst count: " + str(laser.burstCount)) elif c == "laser arm": if check_laser(laser): continue try: if laser.arm(): print_cli("*** Laser ARMED") except LaserCommandError as e: print_cli("!!! Error encountered while arming laser: " + str(e)) continue elif c == "laser disarm": if check_laser(laser): continue try: if laser.disarm(): print_cli("*** Laser DISARMED") except LaserCommandError as e: print_cli("!!! Error encountered while disarming laser: " + str(e)) continue elif c == "laser status": if check_laser(laser): print_cli("Laser is not connected.") continue s = laser.get_status() print_cli(str(s)) print_cli("Rep rate: " + str(laser.repRate) + "Hz") print_cli("Pulse width: " + str(laser.pulseWidth) + "s") print_cli("Pulse mode: " + str(laser.pulseMode)) print_cli("Burst count: " + str(laser.burstCount)) elif c == "laser fire": if check_laser(laser): continue laser.fire() elif c == "laser stop": if check_laser(laser): continue laser.emergency_stop() elif parts[0:3] == [ "laser", "set", "rep_rate" ]: # TODO: Add check to see if this is within the repetition rate. if check_laser(laser): continue if len(parts) < 4: print_cli("!!! Set Laser Rep Rate expects a number argument!") continue try: rate = float(parts[3]) if rate < 0: raise ValueError("Repetition Rate must be positive!") laser.set_repetition_rate(rate) except ValueError: print_cli( "!!! Set Laser Rep Rate expects a positive float argument! You did not enter a float value!" ) continue except LaserCommandError as e: print_cli("!!! Error encountered while commanding laser! " + str(e)) continue elif parts[0:3] == ["laser", "get", "rep_rate"]: if check_laser(laser): continue try: r = laser.get_repetition_rate() print_cli("Laser repetition rate set to: " + str(r) + "Hz") except LaserCommandError as e: print_cli("!!! Error encountered while commanding laser! " + str(e)) continue elif parts[0:3] == ["laser", "get", "pulse_mode"]: if check_laser(laser): continue try: r = laser.get_pulse_mode() s = "UNKOWN" if r == 0: s = "CONTINUOUS" elif r == 1: s = "SINGLE" elif r == 2: s = "BURST" print_cli("Laser is set to fire in " + s + " mode.") except LaserCommandError as e: print_cli("!!! Error encountered while commanding laser! " + str(e)) continue elif parts[0:3] == ["laser", "set", "pulse_mode"]: if check_laser(laser): continue if len(parts) < 4: print_cli( "!!! Set Laser Pulse Mode expects one of the following arguments: CONTINUOUS, SINGLE, BURST!" ) continue else: if parts[3] == "CONTINUOUS": laser.set_pulse_mode(0) laserSingleShot = True elif parts[3] == "SINGLE": laser.set_pulse_mode(1) laserSingleShot = True elif parts[3] == "BURST": laser.set_pulse_mode(2) laserSingleShot = False elif parts[0:3] == ["laser", "set", "burst_count"]: if check_laser(laser): continue if len(parts) < 4: print_cli( "!!! Set Laser Burst Count expects an integer argument!") continue if laser.pulseMode != 2: print_cli( "!!! Please set Laser Pulse Mode to BURST before setting the burst count!" ) continue try: burst_count = int(parts[3]) if burst_count < 0: raise ValueError("Burst Count must be positive!") laser.set_burst_count(burst_count) except ValueError: print_cli( "!!! Set Laser Burst Count expects a positive integer argument! You did not enter an integer." ) continue elif parts[0:3] == ["laser", "get", "burst_count"]: if check_laser(laser): continue try: r = laser.get_burst_count() print_cli("Laser set to " + str(r) + " pulses per sample") except LaserCommandError as e: print_cli("!!! Error encountered while commanding laser! " + str(e)) elif parts[0:3] == ["laser", "set", "pulse_width"]: if check_laser(laser): continue if len(parts) < 4: print_cli( "!!! Set Laser Pulse Width expects a positive float argument!" ) continue try: width = float(parts[3]) laser.set_pulse_width(width) except ValueError: print_cli( "!!! Set Laser Pulse Width expects a float argument! You did not enter a float." ) continue except LaserCommandError as e: print_cli("!!! Error encountered while commanding laser! " + str(e)) continue elif parts[0:3] == ["laser", "get", "pulse_width"]: if check_laser(laser): continue try: r = laser.get_pulse_width() except LaserCommandError as e: print_cli("!!! Error while commanding laser: " + str(e)) continue if not r: print_cli( "!!! Error while querying the laser for pulse width!") continue print_cli("Laser pulse width is set to: " + str(r)) elif parts[0:3] == ["laser", "get", "fet_temp"]: if check_laser(laser): continue t = laser.get_fet_temp() print_cli("Laser FET temperature: " + str(t)) elif parts[0:3] == ["laser", "get", "shot_count"]: shot_count = laser.get_system_shot_count() print_cli("The laser shot count is at " + str(shot_count) + " shots.") elif parts[0:3] == ["laser", "get", "diode_current"]: diode_current = laser.get_diode_current() print_cli("The laser's diode current is " + str(diode_current) + " Amps") elif c == "do_libs_sample": if check_laser(laser) or check_spectrometer(spectrometer): continue try: do_sample(spectrometer, laser) except SeaBreezeError as e: print_cli("!!! " + str(e)) continue except LaserCommandError as e: print_cli("!!! Error while commanding laser! " + str(e)) elif c == "do_trigger": do_trigger(external_trigger_pin) print_cli("Triggered " + external_trigger_pin + ".") elif c == "exit" or c == "quit": if spectrometer: spectrometer.close() if laser: laser.disconnect() running = False else: print_cli( "!!! Invalid command. Enter the 'help' command for usage information" )
def test_diode_current_command(self): """Tests Laser.set_diode_current, feeds in a mock serial object. Makes sure that the correct data is written and that the properties of the class are changed.""" serial_mock = Mock() serial_mock.read_until = Mock(return_value=b"OK\r") serial_mock.write = Mock() l = Laser() l._ser = serial_mock l.connected = True with self.assertRaises(ValueError): l.set_diode_current(0) with self.assertRaises(ValueError): l.set_diode_current(-5) with self.assertRaises(ValueError): l.set_diode_current("this is not an integer") # Reset the energy mode to test that it gets switched to manual (0) l.energyMode = 2 assert l.set_diode_current(100) serial_mock.write.assert_called_once_with(";LA:DC 100\r".encode("ascii")) assert l.diodeCurrent == 100 assert l.energyMode == 0 serial_mock.read_until = Mock(return_value=b"?1") # Emulate the laser returning a failure code. serial_mock.write = Mock() # Reset the energy mode to test that it DOES NOT gets switched to manual (0) l.energyMode = 2 with self.assertRaises(LaserCommandError): l.set_diode_current(50) serial_mock.write.assert_called_once_with(";LA:DC 50\r".encode("ascii")) assert l.diodeCurrent == 100 # This value should have NOT changed since this command failed. assert l.energyMode == 2 # This also should not have changed because the command failed.
def test_energy_mode_command(self): """Tests Laser.set_energy_mode, feeds in a mock serial object. Makes sure that the correct data is written and that the properties of the class are changed.""" serial_mock = Mock() serial_mock.read_until = Mock(return_value=b"OK\r") serial_mock.write = Mock() l = Laser() l._ser = serial_mock l.connected = True with self.assertRaises(ValueError): l.set_energy_mode(4) # Valid values are 0 to 2 with self.assertRaises(ValueError): l.set_energy_mode("this is not an integer") assert l.set_energy_mode(1) serial_mock.write.assert_called_once_with(";LA:EM 1\r".encode("ascii")) assert l.energyMode == 1 serial_mock.read_until = Mock(return_value=b"?1") # Make sure we return False is the laser returns an error serial_mock.write = Mock() with self.assertRaises(LaserCommandError): l.set_energy_mode(2) serial_mock.write.assert_called_once_with(";LA:EM 2\r".encode("ascii")) serial_mock.write.assert_called_once_with(";LA:EM 2\r".encode("ascii")) assert l.energyMode == 1 # This value should have NOT changed since this command failed.