예제 #1
0
    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
예제 #2
0
    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"))
예제 #3
0
    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"))
예제 #4
0
    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.
예제 #5
0
 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()
예제 #6
0
    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
예제 #7
0
        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()
예제 #8
0
    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.
예제 #9
0
 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"
예제 #10
0
    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.
예제 #11
0
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"
            )
예제 #12
0
    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.
예제 #13
0
    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.