示例#1
0
def test_McCommunicationProcess__sends_handshake_every_5_seconds__and_includes_correct_timestamp__and_processes_response(
    four_board_mc_comm_process,
    mantarray_mc_simulator_no_beacon,
    mocker,
):
    mc_process = four_board_mc_comm_process["mc_process"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]

    spied_write = mocker.spy(simulator, "write")

    expected_durs = [
        0,
        MICRO_TO_BASE_CONVERSION * SERIAL_COMM_HANDSHAKE_PERIOD_SECONDS,
    ]
    mocker.patch.object(mc_comm,
                        "get_serial_comm_timestamp",
                        autospec=True,
                        side_effect=expected_durs)
    mocker.patch.object(
        mc_comm,
        "_get_secs_since_last_handshake",
        autospec=True,
        side_effect=[
            0,
            SERIAL_COMM_HANDSHAKE_PERIOD_SECONDS,
            SERIAL_COMM_HANDSHAKE_PERIOD_SECONDS - 1,
            1,
        ],
    )

    set_connection_and_register_simulator(four_board_mc_comm_process,
                                          mantarray_mc_simulator_no_beacon)
    # send handshake
    invoke_process_run_and_check_errors(mc_process)
    expected_handshake_1 = create_data_packet(
        expected_durs[0],
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_HANDSHAKE_PACKET_TYPE,
        bytes(0),
    )
    assert spied_write.call_args[0][0] == expected_handshake_1
    # process handshake on simulator
    invoke_process_run_and_check_errors(simulator)
    # process handshake response
    invoke_process_run_and_check_errors(mc_process)
    # assert handshake response was read
    assert simulator.in_waiting == 0
    # repeat, 5 seconds since previous beacon
    invoke_process_run_and_check_errors(mc_process)
    expected_handshake_2 = create_data_packet(
        expected_durs[1],
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_HANDSHAKE_PACKET_TYPE,
        bytes(0),
    )
    assert spied_write.call_args[0][0] == expected_handshake_2
    invoke_process_run_and_check_errors(simulator)
    invoke_process_run_and_check_errors(mc_process)
    assert simulator.in_waiting == 0
示例#2
0
def test_McCommunicationProcess__raises_error_if_unrecognized_packet_type_sent_from_instrument(
        four_board_mc_comm_process, mantarray_mc_simulator_no_beacon, mocker,
        patch_print):
    mc_process = four_board_mc_comm_process["mc_process"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]

    dummy_timestamp = 0
    test_packet_type = 254
    test_packet = create_data_packet(
        dummy_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        test_packet_type,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        {
            "command": "add_read_bytes",
            "read_bytes": test_packet,
        },
        testing_queue,
    )
    invoke_process_run_and_check_errors(simulator)

    board_idx = 0
    mc_process.set_board_connection(board_idx, simulator)
    with pytest.raises(UnrecognizedSerialCommPacketTypeError) as exc_info:
        invoke_process_run_and_check_errors(mc_process)
    assert str(SERIAL_COMM_MAIN_MODULE_ID) in str(exc_info.value)
    assert str(test_packet_type) in str(exc_info.value)
示例#3
0
def test_McCommunicationProcess__includes_correct_timestamp_in_packets_sent_to_instrument(
        four_board_mc_comm_process, mantarray_mc_simulator_no_beacon, mocker):
    mc_process = four_board_mc_comm_process["mc_process"]
    board_queues = four_board_mc_comm_process["board_queues"]
    input_queue = board_queues[0][0]

    expected_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    mocker.patch.object(
        mc_comm,
        "get_serial_comm_timestamp",
        autospec=True,
        return_value=expected_timestamp,
    )

    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    spied_write = mocker.spy(simulator, "write")

    set_connection_and_register_simulator(four_board_mc_comm_process,
                                          mantarray_mc_simulator_no_beacon)
    test_command = {
        "communication_type": "metadata_comm",
        "command": "get_metadata",
    }
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        copy.deepcopy(test_command), input_queue)
    # run mc_process one iteration to send the command
    invoke_process_run_and_check_errors(mc_process)

    expected_data_packet = create_data_packet(
        expected_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_GET_METADATA_PACKET_TYPE,
    )
    spied_write.assert_called_with(expected_data_packet)
示例#4
0
def test_McCommunicationProcess_register_magic_word__registers_magic_word_in_serial_comm_from_board__when_first_packet_is_not_truncated__and_handles_reads_correctly_afterward(
        four_board_mc_comm_process, mantarray_mc_simulator_no_beacon, mocker):
    mc_process = four_board_mc_comm_process["mc_process"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]

    board_idx = 0
    timestamp = 0
    mc_process.set_board_connection(board_idx, simulator)
    assert mc_process.is_registered_with_serial_comm(board_idx) is False
    test_bytes = create_data_packet(
        timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STATUS_BEACON_PACKET_TYPE,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    test_item = {
        "command": "add_read_bytes",
        "read_bytes": [test_bytes, test_bytes]
    }
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        test_item, testing_queue)
    invoke_process_run_and_check_errors(simulator)

    invoke_process_run_and_check_errors(mc_process)
    assert mc_process.is_registered_with_serial_comm(board_idx) is True
    # make sure no errors reading next packet
    invoke_process_run_and_check_errors(mc_process)
    # make sure no errors when no more bytes available
    invoke_process_run_and_check_errors(mc_process)
示例#5
0
def test_McCommunicationProcess_register_magic_word__registers_magic_word_in_serial_comm_from_board__when_first_packet_is_truncated_to_more_than_8_bytes(
        four_board_mc_comm_process, mantarray_mc_simulator_no_beacon, mocker):
    mc_process = four_board_mc_comm_process["mc_process"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]

    board_idx = 0
    mc_process.set_board_connection(board_idx, simulator)
    assert mc_process.is_registered_with_serial_comm(board_idx) is False
    test_bytes = SERIAL_COMM_MAGIC_WORD_BYTES[3:] + bytes(8)
    dummy_timestamp = 0
    test_bytes += create_data_packet(
        dummy_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STATUS_BEACON_PACKET_TYPE,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    test_item = {"command": "add_read_bytes", "read_bytes": test_bytes}
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        test_item, testing_queue)
    invoke_process_run_and_check_errors(simulator)

    invoke_process_run_and_check_errors(mc_process)
    assert mc_process.is_registered_with_serial_comm(board_idx) is True
    # make sure no errors in next iteration
    invoke_process_run_and_check_errors(mc_process)
示例#6
0
def test_MantarrayMcSimulator__processes_start_stimulation_command__before_protocols_have_been_set(
    mantarray_mc_simulator_no_beacon,
):
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    set_simulator_idle_ready(mantarray_mc_simulator_no_beacon)

    expected_stim_running_statuses = {
        convert_module_id_to_well_name(module_id): False for module_id in range(1, 25)
    }

    # send start stim command
    expected_pc_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    start_stimulation_command = create_data_packet(
        expected_pc_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_START_STIM_PACKET_TYPE,
    )
    simulator.write(start_stimulation_command)

    invoke_process_run_and_check_errors(simulator)
    # assert that stimulation was not started on any wells
    assert simulator.get_stim_running_statuses() == expected_stim_running_statuses
    # assert command response is correct
    expected_size = get_full_packet_size_from_packet_body_size(SERIAL_COMM_TIMESTAMP_LENGTH_BYTES + 1)
    stim_command_response = simulator.read(size=expected_size)
    assert_serial_packet_is_expected(
        stim_command_response,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE,
        additional_bytes=convert_to_timestamp_bytes(expected_pc_timestamp)
        + bytes([SERIAL_COMM_COMMAND_FAILURE_BYTE]),
    )
示例#7
0
def test_handle_data_packets__parses_single_stim_data_packet_with_a_single_status_correctly(
):
    base_global_time = randint(0, 100)
    test_time_index = random_time_index()
    test_well_idx = randint(0, 23)
    test_subprotocol_idx = randint(0, 5)

    stim_packet_body = (
        bytes([1])  # num status updates in packet
        + bytes([STIM_WELL_IDX_TO_MODULE_ID[test_well_idx]]) +
        bytes([StimStatuses.ACTIVE]) +
        test_time_index.to_bytes(8, byteorder="little") +
        bytes([test_subprotocol_idx]))
    test_data_packet = create_data_packet(
        random_timestamp(),
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STIM_STATUS_PACKET_TYPE,
        stim_packet_body,
    )

    parsed_data_dict = handle_data_packets(bytearray(test_data_packet), [],
                                           base_global_time)
    actual_stim_data = parsed_data_dict["stim_data"]
    assert list(actual_stim_data.keys()) == [test_well_idx]
    assert actual_stim_data[test_well_idx].dtype == np.int64
    np.testing.assert_array_equal(actual_stim_data[test_well_idx],
                                  [[test_time_index], [test_subprotocol_idx]])

    # make sure no magnetometer data was returned
    assert not any(parsed_data_dict["magnetometer_data"].values())
示例#8
0
def test_McCommunicationProcess__raises_error_when_receiving_untracked_command_response_from_instrument(
    four_board_mc_comm_process_no_handshake,
    mantarray_mc_simulator_no_beacon,
    mocker,
    patch_print,
):
    mc_process = four_board_mc_comm_process_no_handshake["mc_process"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]

    test_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    test_timestamp_bytes = bytes(
        8)  # 8 arbitrary bytes in place of timestamp of command sent from PC
    test_command_response = create_data_packet(
        test_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE,
        test_timestamp_bytes,
    )
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        {
            "command": "add_read_bytes",
            "read_bytes": test_command_response
        },
        testing_queue,
    )
    invoke_process_run_and_check_errors(simulator)

    mc_process.set_board_connection(0, simulator)
    with pytest.raises(SerialCommUntrackedCommandResponseError) as exc_info:
        invoke_process_run_and_check_errors(mc_process)
    assert str(SERIAL_COMM_MAIN_MODULE_ID) in str(exc_info.value)
    assert str(SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE) in str(exc_info.value)
    assert str(test_timestamp_bytes) in str(exc_info.value)
示例#9
0
def test_McCommunicationProcess__raises_error_if_unrecognized_module_id_sent_from_instrument(
        four_board_mc_comm_process, mantarray_mc_simulator_no_beacon, mocker,
        patch_print):
    mc_process = four_board_mc_comm_process["mc_process"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]

    dummy_timestamp = 0
    dummy_packet_type = 1
    test_module_id = 254
    test_packet = create_data_packet(
        dummy_timestamp,
        test_module_id,
        dummy_packet_type,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        {
            "command": "add_read_bytes",
            "read_bytes": test_packet,
        },
        testing_queue,
    )
    invoke_process_run_and_check_errors(simulator)

    board_idx = 0
    mc_process.set_board_connection(board_idx, simulator)
    with pytest.raises(UnrecognizedSerialCommModuleIdError,
                       match=str(test_module_id)):
        invoke_process_run_and_check_errors(mc_process)
示例#10
0
def test_MantarrayMcSimulator__processes_set_stimulation_protocol_command__when_stimulation_not_running_on_any_wells(
    mantarray_mc_simulator_no_beacon,
):
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    set_simulator_idle_ready(mantarray_mc_simulator_no_beacon)

    test_protocol_ids = ("A", "B", "E", None)
    stim_info_dict = {
        "protocols": [
            {
                "protocol_id": protocol_id,
                "stimulation_type": choice(["C", "V"]),
                "run_until_stopped": choice([True, False]),
                "subprotocols": [
                    choice([get_random_subprotocol(), get_null_subprotocol(600)])
                    for _ in range(randint(1, 3))
                ],
            }
            for protocol_id in test_protocol_ids[:-1]
        ],
        "protocol_assignments": {
            GENERIC_24_WELL_DEFINITION.get_well_name_from_well_index(well_idx): choice(test_protocol_ids)
            for well_idx in range(24)
        },
    }

    expected_pc_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    set_protocols_command = create_data_packet(
        expected_pc_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_SET_STIM_PROTOCOL_PACKET_TYPE,
        convert_stim_dict_to_bytes(stim_info_dict),
    )
    simulator.write(set_protocols_command)

    invoke_process_run_and_check_errors(simulator)
    # assert that stim info was stored
    actual = simulator.get_stim_info()
    for protocol_idx in range(len(test_protocol_ids) - 1):
        del stim_info_dict["protocols"][protocol_idx][
            "protocol_id"
        ]  # the actual protocol ID letter is not included
        assert actual["protocols"][protocol_idx] == stim_info_dict["protocols"][protocol_idx], protocol_idx
    assert actual["protocol_assignments"] == {  # indices of the protocol are used instead
        well_name: (None if protocol_id is None else test_protocol_ids.index(protocol_id))
        for well_name, protocol_id in stim_info_dict["protocol_assignments"].items()
    }
    # assert command response is correct
    stim_command_response = simulator.read(
        size=get_full_packet_size_from_packet_body_size(SERIAL_COMM_TIMESTAMP_LENGTH_BYTES + 1)
    )
    assert_serial_packet_is_expected(
        stim_command_response,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE,
        additional_bytes=convert_to_timestamp_bytes(expected_pc_timestamp)
        + bytes([SERIAL_COMM_COMMAND_SUCCESS_BYTE]),
    )
示例#11
0
def test_MantarrayMcSimulator__processes_start_stimulation_command__after_protocols_have_been_set(
    mantarray_mc_simulator_no_beacon, mocker
):
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    set_simulator_idle_ready(mantarray_mc_simulator_no_beacon)

    spied_global_timer = mocker.spy(simulator, "_get_global_timer")
    # mock so no protocol status packets are sent
    mocker.patch.object(mc_simulator, "_get_us_since_subprotocol_start", autospec=True, return_value=0)

    # set single arbitrary protocol applied to wells randomly
    stim_info = simulator.get_stim_info()
    stim_info["protocols"] = [
        {
            "protocol_id": "A",
            "stimulation_type": "C",
            "run_until_stopped": True,
            "subprotocols": [get_random_subprotocol()],
        }
    ]
    stim_info["protocol_assignments"] = {
        GENERIC_24_WELL_DEFINITION.get_well_name_from_well_index(well_idx): choice(["A", None])
        if well_idx
        else "A"
        for well_idx in range(24)
    }
    expected_stim_running_statuses = {
        well_name: bool(protocol_id) for well_name, protocol_id in stim_info["protocol_assignments"].items()
    }

    for response_byte_value in (
        SERIAL_COMM_COMMAND_SUCCESS_BYTE,
        SERIAL_COMM_COMMAND_FAILURE_BYTE,
    ):
        # send start stim command
        expected_pc_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
        start_stimulation_command = create_data_packet(
            expected_pc_timestamp,
            SERIAL_COMM_MAIN_MODULE_ID,
            SERIAL_COMM_START_STIM_PACKET_TYPE,
        )
        simulator.write(start_stimulation_command)

        invoke_process_run_and_check_errors(simulator)
        # assert that stimulation was started on wells that were assigned a protocol
        assert simulator.get_stim_running_statuses() == expected_stim_running_statuses
        # assert command response is correct
        additional_bytes = convert_to_timestamp_bytes(expected_pc_timestamp) + bytes([response_byte_value])
        if not response_byte_value:
            additional_bytes += spied_global_timer.spy_return.to_bytes(8, byteorder="little")
        expected_size = get_full_packet_size_from_packet_body_size(len(additional_bytes))
        stim_command_response = simulator.read(size=expected_size)
        assert_serial_packet_is_expected(
            stim_command_response,
            SERIAL_COMM_MAIN_MODULE_ID,
            SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE,
            additional_bytes=additional_bytes,
        )
示例#12
0
def test_McCommunicationProcess__raises_error_if_checksum_in_data_packet_sent_from_mantarray_is_invalid(
    is_command_awaiting,
    four_board_mc_comm_process_no_handshake,
    mantarray_mc_simulator_no_beacon,
    mocker,
    patch_print,
):
    mc_process = four_board_mc_comm_process_no_handshake["mc_process"]
    input_queue = four_board_mc_comm_process_no_handshake["board_queues"][0][0]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]
    set_connection_and_register_simulator(
        four_board_mc_comm_process_no_handshake,
        mantarray_mc_simulator_no_beacon)

    if is_command_awaiting:
        test_prev_command = {
            "communication_type": "metadata_comm",
            "command": "get_metadata"
        }
        put_object_into_queue_and_raise_error_if_eventually_still_empty(
            test_prev_command, input_queue)
        invoke_process_run_and_check_errors(mc_process)
    else:
        test_prev_command = None

    # add packet with bad checksum to be sent from simulator
    dummy_timestamp = 0
    test_bytes = create_data_packet(
        dummy_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STATUS_BEACON_PACKET_TYPE,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    # set checksum bytes to an arbitrary incorrect value
    bad_checksum = 1234
    bad_checksum_bytes = bad_checksum.to_bytes(
        SERIAL_COMM_CHECKSUM_LENGTH_BYTES, byteorder="little")
    test_bytes = test_bytes[:-SERIAL_COMM_CHECKSUM_LENGTH_BYTES] + bad_checksum_bytes
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        {
            "command": "add_read_bytes",
            "read_bytes": test_bytes,
        },
        testing_queue,
    )
    invoke_process_run_and_check_errors(simulator)

    with pytest.raises(
            SerialCommIncorrectChecksumFromInstrumentError) as exc_info:
        invoke_process_run_and_check_errors(mc_process)
    expected_checksum = int.from_bytes(
        test_bytes[-SERIAL_COMM_CHECKSUM_LENGTH_BYTES:], byteorder="little")
    assert str(bad_checksum) in exc_info.value.args[0]
    assert str(expected_checksum) in exc_info.value.args[0]
    assert str(test_bytes) in exc_info.value.args[0]
    assert f"Previous Command: {test_prev_command}" in exc_info.value.args[0]
示例#13
0
def test_McCommunicationProcess_register_magic_word__registers_with_magic_word_in_serial_comm_from_board__when_first_packet_is_truncated_to_less_than_8_bytes__and_calls_read_with_correct_size__and_calls_sleep_correctly(
        four_board_mc_comm_process, mantarray_mc_simulator_no_beacon, mocker):
    # mock sleep to speed up the test
    mocked_sleep = mocker.patch.object(mc_comm, "sleep", autospec=True)

    mc_process = four_board_mc_comm_process["mc_process"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]

    # Arbitrarily slice the magic word across multiple reads and add empty reads to simulate no bytes being available to read
    test_read_values = [SERIAL_COMM_MAGIC_WORD_BYTES[:4]]
    test_read_values.extend([
        bytes(0) for _ in range(SERIAL_COMM_REGISTRATION_TIMEOUT_SECONDS - 1)
    ])
    test_read_values.append(SERIAL_COMM_MAGIC_WORD_BYTES[4:])
    # add a real data packet after but remove magic word
    dummy_timestamp = 0
    test_packet = create_data_packet(
        dummy_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STATUS_BEACON_PACKET_TYPE,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    packet_length_bytes = test_packet[len(SERIAL_COMM_MAGIC_WORD_BYTES
                                          ):len(SERIAL_COMM_MAGIC_WORD_BYTES) +
                                      SERIAL_COMM_PACKET_INFO_LENGTH_BYTES]
    test_read_values.append(packet_length_bytes)
    test_read_values.append(test_packet[len(SERIAL_COMM_MAGIC_WORD_BYTES) +
                                        SERIAL_COMM_PACKET_INFO_LENGTH_BYTES:])
    mocked_read = mocker.patch.object(simulator,
                                      "read",
                                      autospec=True,
                                      side_effect=test_read_values)

    board_idx = 0
    mc_process.set_board_connection(board_idx, simulator)
    assert mc_process.is_registered_with_serial_comm(board_idx) is False
    invoke_process_run_and_check_errors(mc_process)
    assert mc_process.is_registered_with_serial_comm(board_idx) is True

    # Assert it reads once initially then once per second until status beacon period is reached (a new packet should be available by then). Tanner (3/16/21): changed == to >= in the next line because others parts of mc_comm may call read after the magic word is registered
    assert len(mocked_read.call_args_list
               ) >= SERIAL_COMM_REGISTRATION_TIMEOUT_SECONDS + 1
    assert mocked_read.call_args_list[0] == mocker.call(size=8)
    assert mocked_read.call_args_list[1] == mocker.call(size=4)
    assert mocked_read.call_args_list[2] == mocker.call(size=4)
    assert mocked_read.call_args_list[3] == mocker.call(size=4)
    assert mocked_read.call_args_list[4] == mocker.call(size=4)

    # Assert sleep is called with correct value and correct number of times
    expected_sleep_secs = 1
    for sleep_call_num in range(SERIAL_COMM_REGISTRATION_TIMEOUT_SECONDS - 1):
        sleep_iter_call = mocked_sleep.call_args_list[sleep_call_num][0][0]
        assert (sleep_call_num, sleep_iter_call) == (
            sleep_call_num,
            expected_sleep_secs,
        )
示例#14
0
def test_MantarrayMcSimulator__processes_set_stimulation_protocol_command__when_an_incorrect_amount_of_module_assignments_are_given(
    mantarray_mc_simulator_no_beacon, test_num_module_assignments, test_description
):
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    set_simulator_idle_ready(mantarray_mc_simulator_no_beacon)

    stim_info_dict = {
        "protocols": [
            {
                "protocol_id": "V",
                "stimulation_type": choice(["C", "V"]),
                "run_until_stopped": choice([True, False]),
                "subprotocols": [get_random_subprotocol()],
            }
        ],
        "protocol_assignments": {
            GENERIC_24_WELL_DEFINITION.get_well_name_from_well_index(well_idx): "V" for well_idx in range(24)
        },
    }
    stim_info_bytes = convert_stim_dict_to_bytes(stim_info_dict)
    # add or remove an assignment
    if test_num_module_assignments > 24:
        stim_info_bytes += bytes([0])
    else:
        stim_info_bytes = stim_info_bytes[:-1]

    expected_pc_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    set_protocols_command = create_data_packet(
        expected_pc_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_SET_STIM_PROTOCOL_PACKET_TYPE,
        stim_info_bytes,
    )
    simulator.write(set_protocols_command)

    invoke_process_run_and_check_errors(simulator)
    # assert stim info was not updated
    assert simulator.get_stim_info() == {}
    # assert command response is correct
    stim_command_response = simulator.read(
        size=get_full_packet_size_from_packet_body_size(SERIAL_COMM_TIMESTAMP_LENGTH_BYTES + 1)
    )
    assert_serial_packet_is_expected(
        stim_command_response,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE,
        additional_bytes=convert_to_timestamp_bytes(expected_pc_timestamp)
        + bytes([SERIAL_COMM_COMMAND_FAILURE_BYTE]),
    )
示例#15
0
def test_McCommunicationProcess__handles_plate_event(
    test_barcode,
    expected_valid_flag,
    test_description,
    four_board_mc_comm_process_no_handshake,
    mantarray_mc_simulator_no_beacon,
):
    mc_process = four_board_mc_comm_process_no_handshake["mc_process"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]
    set_connection_and_register_simulator(
        four_board_mc_comm_process_no_handshake,
        mantarray_mc_simulator_no_beacon)
    to_main_queue = four_board_mc_comm_process_no_handshake["board_queues"][0][
        1]

    # send plate event packet from simulator
    was_plate_placed_byte = expected_valid_flag is not None
    plate_event_packet = create_data_packet(
        randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE),
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_PLATE_EVENT_PACKET_TYPE,
        bytes([was_plate_placed_byte]) + bytes(test_barcode, encoding="ascii"),
    )
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        {
            "command": "add_read_bytes",
            "read_bytes": plate_event_packet
        },
        testing_queue,
    )
    invoke_process_run_and_check_errors(simulator)
    # process plate event packet and send barcode comm to main
    invoke_process_run_and_check_errors(mc_process)
    confirm_queue_is_eventually_of_size(to_main_queue, 1)
    # check that comm was sent correctly
    expected_barcode_comm = {
        "communication_type": "barcode_comm",
        "board_idx": 0,
        "barcode": test_barcode,
    }
    if expected_valid_flag is not None:
        expected_barcode_comm["valid"] = expected_valid_flag
    actual = to_main_queue.get(timeout=QUEUE_CHECK_TIMEOUT_SECONDS)
    assert actual == expected_barcode_comm
示例#16
0
def test_MantarrayMcSimulator__processes_testing_commands_during_reboot(
        mantarray_mc_simulator_no_beacon, mocker):
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]
    mocker.patch.object(
        mc_simulator,
        "_get_secs_since_reboot_command",
        autospec=True,
        return_value=AVERAGE_MC_REBOOT_DURATION_SECONDS - 1,
    )
    spied_get_absolute_timer = mocker.spy(simulator, "_get_absolute_timer")

    # send reboot command
    test_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    simulator.write(
        create_data_packet(
            test_timestamp,
            SERIAL_COMM_MAIN_MODULE_ID,
            SERIAL_COMM_SIMPLE_COMMAND_PACKET_TYPE,
            bytes([SERIAL_COMM_REBOOT_COMMAND_BYTE]),
        ))
    invoke_process_run_and_check_errors(simulator)

    # remove reboot response packet
    invoke_process_run_and_check_errors(simulator)
    reboot_response_size = get_full_packet_size_from_packet_body_size(
        SERIAL_COMM_TIMESTAMP_LENGTH_BYTES)
    reboot_response = simulator.read(size=reboot_response_size)
    assert_serial_packet_is_expected(
        reboot_response,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE,
        additional_bytes=convert_to_timestamp_bytes(test_timestamp),
        timestamp=spied_get_absolute_timer.spy_return,
    )

    # add read bytes as test command
    expected_item = b"the_item"
    test_dict = {"command": "add_read_bytes", "read_bytes": expected_item}
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        test_dict, testing_queue)
    invoke_process_run_and_check_errors(simulator)
    actual = simulator.read(size=len(expected_item))
    assert actual == expected_item
示例#17
0
def test_handle_data_packets__parses_single_stim_data_packet_with_multiple_statuses_correctly(
):
    base_global_time = randint(0, 100)
    test_time_indices = [
        random_time_index(),
        random_time_index(),
        random_time_index()
    ]
    test_well_idx = randint(0, 23)
    test_subprotocol_indices = [randint(0, 5), randint(0, 5), 0]
    test_statuses = [
        StimStatuses.ACTIVE, StimStatuses.NULL, StimStatuses.RESTARTING
    ]

    stim_packet_body = bytes([3])  # num status updates in packet
    for packet_idx in range(3):
        stim_packet_body += (
            bytes([STIM_WELL_IDX_TO_MODULE_ID[test_well_idx]]) +
            bytes([test_statuses[packet_idx]]) +
            test_time_indices[packet_idx].to_bytes(8, byteorder="little") +
            bytes([test_subprotocol_indices[packet_idx]]))
    test_data_packet = create_data_packet(
        random_timestamp(),
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STIM_STATUS_PACKET_TYPE,
        stim_packet_body,
    )

    parsed_data_dict = handle_data_packets(
        bytearray(test_data_packet),
        [1, 2, 3, 4],
        base_global_time  # arbitrary channel list,
    )
    actual_stim_data = parsed_data_dict["stim_data"]
    assert list(actual_stim_data.keys()) == [test_well_idx]
    np.testing.assert_array_equal(
        actual_stim_data[test_well_idx],
        # removing last item in these lists since restarting status info is not needed
        [np.array(test_time_indices[:-1]), test_subprotocol_indices[:-1]],
    )
示例#18
0
def test_create_data_packet__creates_data_packet_bytes_correctly():
    test_timestamp = 100
    test_module_id = 0
    test_packet_type = 1
    test_data = bytes([1, 5, 3])

    expected_data_packet_bytes = SERIAL_COMM_MAGIC_WORD_BYTES
    expected_data_packet_bytes += (17).to_bytes(2, byteorder="little")
    expected_data_packet_bytes += test_timestamp.to_bytes(
        SERIAL_COMM_TIMESTAMP_LENGTH_BYTES, byteorder="little")
    expected_data_packet_bytes += bytes([test_module_id, test_packet_type])
    expected_data_packet_bytes += test_data
    expected_data_packet_bytes += crc32(expected_data_packet_bytes).to_bytes(
        SERIAL_COMM_CHECKSUM_LENGTH_BYTES, byteorder="little")

    actual = create_data_packet(
        test_timestamp,
        test_module_id,
        test_packet_type,
        test_data,
    )
    assert actual == expected_data_packet_bytes
示例#19
0
def test_MantarrayMcSimulator__automatically_sends_plate_barcode_after_time_is_synced(
    mantarray_mc_simulator_no_beacon, ):
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]

    # put simulator in time sync ready status
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        {
            "command": "set_status_code",
            "status_code": SERIAL_COMM_TIME_SYNC_READY_CODE
        },
        testing_queue,
    )
    invoke_process_run_and_check_errors(simulator)
    # send set time command
    test_pc_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    test_set_time_command = create_data_packet(
        test_pc_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_SIMPLE_COMMAND_PACKET_TYPE,
        bytes([SERIAL_COMM_SET_TIME_COMMAND_BYTE]) +
        convert_to_timestamp_bytes(test_pc_timestamp),
    )
    simulator.write(test_set_time_command)
    # process set time command and remove response
    invoke_process_run_and_check_errors(simulator)
    simulator.read(size=get_full_packet_size_from_packet_body_size(
        SERIAL_COMM_TIMESTAMP_LENGTH_BYTES))
    barcode_packet_size = get_full_packet_size_from_packet_body_size(
        len(MantarrayMcSimulator.default_barcode))
    # assert barcode packet sent correctly
    barcode_packet = simulator.read(size=barcode_packet_size)
    assert_serial_packet_is_expected(
        barcode_packet,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_BARCODE_FOUND_PACKET_TYPE,
        bytes(MantarrayMcSimulator.default_barcode, encoding="ascii"),
    )
示例#20
0
def test_MantarrayMcSimulator__raises_error_when_magnetometer_config_command_received_with_invalid_sampling_period(
        mantarray_mc_simulator_no_beacon, mocker, patch_print):
    set_simulator_idle_ready(mantarray_mc_simulator_no_beacon)
    simulator = mantarray_mc_simulator_no_beacon["simulator"]

    bad_sampling_period = 1001
    magnetometer_config_bytes = create_magnetometer_config_bytes(
        MantarrayMcSimulator.default_24_well_magnetometer_config)
    # send command with invalid sampling period
    dummy_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
    change_config_command = create_data_packet(
        dummy_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_SIMPLE_COMMAND_PACKET_TYPE,
        bytes([SERIAL_COMM_MAGNETOMETER_CONFIG_COMMAND_BYTE]) +
        bad_sampling_period.to_bytes(2, byteorder="little") +
        magnetometer_config_bytes,
    )
    simulator.write(change_config_command)
    # process command and raise error with given sampling period
    with pytest.raises(SerialCommInvalidSamplingPeriodError,
                       match=str(bad_sampling_period)):
        invoke_process_run_and_check_errors(simulator)
示例#21
0
def test_McCommunicationProcess__raises_error_if_length_of_additional_bytes_read_is_smaller_than_size_specified_in_packet_header(
        four_board_mc_comm_process_no_handshake,
        mantarray_mc_simulator_no_beacon, mocker, patch_print):
    mc_process = four_board_mc_comm_process_no_handshake["mc_process"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]

    # create valid packet
    dummy_timestamp = 0
    test_bytes = create_data_packet(
        dummy_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STATUS_BEACON_PACKET_TYPE,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    # cut off checksum bytes so that the remaining packet size is less than the specified packet length
    truncated_test_bytes = test_bytes[:-SERIAL_COMM_CHECKSUM_LENGTH_BYTES]
    test_item = {
        "command": "add_read_bytes",
        "read_bytes": truncated_test_bytes
    }
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        test_item, testing_queue)
    invoke_process_run_and_check_errors(simulator)

    board_idx = 0
    mc_process.set_board_connection(board_idx, simulator)

    num_bytes_not_counted_in_packet_len = len(SERIAL_COMM_MAGIC_WORD_BYTES) + 2
    with pytest.raises(
            SerialCommNotEnoughAdditionalBytesReadError) as exc_info:
        invoke_process_run_and_check_errors(mc_process)
    assert f"Expected Size: {len(test_bytes) - num_bytes_not_counted_in_packet_len}" in exc_info.value.args[
        0]
    assert (
        f"Actual Size: {len(truncated_test_bytes) - num_bytes_not_counted_in_packet_len}"
        in exc_info.value.args[0])
示例#22
0
def test_McCommunicationProcess__raises_error_if_magic_word_is_incorrect_in_packet_after_previous_magic_word_has_been_registered(
    four_board_mc_comm_process,
    mantarray_mc_simulator_no_beacon,
    mocker,
    patch_print,
):
    mc_process = four_board_mc_comm_process["mc_process"]
    testing_queue = mantarray_mc_simulator_no_beacon["testing_queue"]
    simulator = mantarray_mc_simulator_no_beacon["simulator"]

    board_idx = 0
    dummy_timestamp = 0
    mc_process.set_board_connection(board_idx, simulator)
    assert mc_process.is_registered_with_serial_comm(board_idx) is False
    test_bytes_1 = create_data_packet(
        dummy_timestamp,
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STATUS_BEACON_PACKET_TYPE,
        DEFAULT_SIMULATOR_STATUS_CODE,
    )
    # Add arbitrary incorrect value into magic word slot
    bad_magic_word = b"NANOSURF"
    test_bytes_2 = bad_magic_word + test_bytes_1[
        len(SERIAL_COMM_MAGIC_WORD_BYTES):]
    test_item = {
        "command": "add_read_bytes",
        "read_bytes": [test_bytes_1, test_bytes_2],
    }
    put_object_into_queue_and_raise_error_if_eventually_still_empty(
        test_item, testing_queue)
    invoke_process_run_and_check_errors(simulator)

    with pytest.raises(SerialCommIncorrectMagicWordFromMantarrayError,
                       match=str(bad_magic_word)):
        # First iteration registers magic word, next iteration receive incorrect magic word
        invoke_process_run_and_check_errors(mc_process, num_iterations=2)
示例#23
0
from mantarray_desktop_app import SERIAL_COMM_MAX_TIMESTAMP_VALUE
from mantarray_desktop_app.constants import GENERIC_24_WELL_DEFINITION
import pytest
from stdlib_utils import drain_queue
from stdlib_utils import invoke_process_run_and_check_errors
from stdlib_utils import put_object_into_queue_and_raise_error_if_eventually_still_empty
from stdlib_utils import QUEUE_CHECK_TIMEOUT_SECONDS
from stdlib_utils import TestingQueue

STATUS_BEACON_SIZE_BYTES = 28
HANDSHAKE_RESPONSE_SIZE_BYTES = 36

TEST_HANDSHAKE_TIMESTAMP = 12345
TEST_HANDSHAKE = create_data_packet(
    TEST_HANDSHAKE_TIMESTAMP,
    SERIAL_COMM_MAIN_MODULE_ID,
    SERIAL_COMM_HANDSHAKE_PACKET_TYPE,
    bytes(0),
)

DEFAULT_SIMULATOR_STATUS_CODE = convert_to_status_code_bytes(
    SERIAL_COMM_BOOT_UP_CODE)


def random_time_index():
    return randint(100, 0xFFFFFFFFFF)


def random_time_offset():
    return randint(0, 0xFFFF)

示例#24
0
def test_MantarrayMcSimulator__processes_stop_stimulation_command(mantarray_mc_simulator_no_beacon, mocker):
    simulator = mantarray_mc_simulator_no_beacon["simulator"]
    set_simulator_idle_ready(mantarray_mc_simulator_no_beacon)

    spied_global_timer = mocker.spy(simulator, "_get_global_timer")

    # set single arbitrary protocol applied to wells randomly
    stim_info = simulator.get_stim_info()
    stim_info["protocols"] = [
        {
            "protocol_id": "B",
            "stimulation_type": "V",
            "run_until_stopped": True,
            "subprotocols": [get_random_subprotocol()],
        }
    ]
    stim_info["protocol_assignments"] = {
        GENERIC_24_WELL_DEFINITION.get_well_name_from_well_index(well_idx): choice(["B", None])
        if well_idx
        else "B"
        for well_idx in range(24)
    }
    initial_stim_running_statuses = {
        well_name: bool(protocol_id) for well_name, protocol_id in stim_info["protocol_assignments"].items()
    }
    simulator.get_stim_running_statuses().update(initial_stim_running_statuses)

    for response_byte_value in (
        SERIAL_COMM_COMMAND_SUCCESS_BYTE,
        SERIAL_COMM_COMMAND_FAILURE_BYTE,
    ):
        # send start stim command
        expected_pc_timestamp = randint(0, SERIAL_COMM_MAX_TIMESTAMP_VALUE)
        stop_stimulation_command = create_data_packet(
            expected_pc_timestamp,
            SERIAL_COMM_MAIN_MODULE_ID,
            SERIAL_COMM_STOP_STIM_PACKET_TYPE,
        )
        simulator.write(stop_stimulation_command)
        invoke_process_run_and_check_errors(simulator)

        # make sure finished status updates are sent if command succeeded
        if response_byte_value == SERIAL_COMM_COMMAND_SUCCESS_BYTE:
            status_update_bytes = bytes([sum(initial_stim_running_statuses.values())])
            for well_name in simulator.get_stim_running_statuses().keys():
                if not initial_stim_running_statuses[well_name]:
                    continue
                well_idx = GENERIC_24_WELL_DEFINITION.get_well_index_from_well_name(well_name)
                status_update_bytes += (
                    bytes([STIM_WELL_IDX_TO_MODULE_ID[well_idx]])
                    + bytes([StimStatuses.FINISHED])
                    + (spied_global_timer.spy_return).to_bytes(8, byteorder="little")
                    + bytes([STIM_COMPLETE_SUBPROTOCOL_IDX])
                )
            expected_size = get_full_packet_size_from_packet_body_size(len(status_update_bytes))
            stim_command_response = simulator.read(size=expected_size)
            assert_serial_packet_is_expected(
                stim_command_response,
                SERIAL_COMM_MAIN_MODULE_ID,
                SERIAL_COMM_STIM_STATUS_PACKET_TYPE,
                additional_bytes=status_update_bytes,
            )
        # assert that stimulation was stopped on all wells
        assert simulator.get_stim_running_statuses() == {
            convert_module_id_to_well_name(module_id): False for module_id in range(1, 25)
        }
        # assert command response is correct
        additional_bytes = convert_to_timestamp_bytes(expected_pc_timestamp) + bytes([response_byte_value])
        expected_size = get_full_packet_size_from_packet_body_size(len(additional_bytes))
        stim_command_response = simulator.read(size=expected_size)
        assert_serial_packet_is_expected(
            stim_command_response,
            SERIAL_COMM_MAIN_MODULE_ID,
            SERIAL_COMM_COMMAND_RESPONSE_PACKET_TYPE,
            additional_bytes=additional_bytes,
        )
示例#25
0
def test_handle_data_packets__parses_multiple_stim_data_packet_with_multiple_wells_and_statuses_correctly(
):
    base_global_time = randint(0, 100)
    test_well_indices = [randint(0, 11), randint(12, 23)]
    test_time_indices = [
        [random_time_index(), random_time_index()],
        [random_time_index(),
         random_time_index(),
         random_time_index()],
    ]
    test_subprotocol_indices = [
        [0, randint(1, 5)],
        [randint(0, 5),
         randint(0, 5), STIM_COMPLETE_SUBPROTOCOL_IDX],
    ]
    test_statuses = [
        [StimStatuses.RESTARTING, StimStatuses.NULL],
        [StimStatuses.ACTIVE, StimStatuses.NULL, StimStatuses.FINISHED],
    ]

    stim_packet_body_1 = (
        bytes([2])  # num status updates in packet
        + bytes([STIM_WELL_IDX_TO_MODULE_ID[test_well_indices[0]]]) +
        bytes([test_statuses[0][0]]) +
        test_time_indices[0][0].to_bytes(8, byteorder="little") +
        bytes([test_subprotocol_indices[0][0]]) +
        bytes([STIM_WELL_IDX_TO_MODULE_ID[test_well_indices[1]]]) +
        bytes([test_statuses[1][0]]) +
        test_time_indices[1][0].to_bytes(8, byteorder="little") +
        bytes([test_subprotocol_indices[1][0]]))
    stim_packet_body_2 = (
        bytes([3])  # num status updates in packet
        + bytes([STIM_WELL_IDX_TO_MODULE_ID[test_well_indices[1]]]) +
        bytes([test_statuses[1][1]]) +
        test_time_indices[1][1].to_bytes(8, byteorder="little") +
        bytes([test_subprotocol_indices[1][1]]) +
        bytes([STIM_WELL_IDX_TO_MODULE_ID[test_well_indices[0]]]) +
        bytes([test_statuses[0][1]]) +
        test_time_indices[0][1].to_bytes(8, byteorder="little") +
        bytes([test_subprotocol_indices[0][1]]) +
        bytes([STIM_WELL_IDX_TO_MODULE_ID[test_well_indices[1]]]) +
        bytes([test_statuses[1][2]]) +
        test_time_indices[1][2].to_bytes(8, byteorder="little") +
        bytes([test_subprotocol_indices[1][2]]))
    test_data_packet_1 = create_data_packet(
        random_timestamp(),
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STIM_STATUS_PACKET_TYPE,
        stim_packet_body_1,
    )
    test_data_packet_2 = create_data_packet(
        random_timestamp(),
        SERIAL_COMM_MAIN_MODULE_ID,
        SERIAL_COMM_STIM_STATUS_PACKET_TYPE,
        stim_packet_body_2,
    )

    parsed_data_dict = handle_data_packets(
        bytearray(test_data_packet_1 + test_data_packet_2), [],
        base_global_time)
    actual_stim_data = parsed_data_dict["stim_data"]
    assert sorted(list(actual_stim_data.keys())) == sorted(test_well_indices)
    np.testing.assert_array_equal(
        actual_stim_data[test_well_indices[0]],
        # removing first item in these lists since restarting status info is not needed
        [np.array(test_time_indices[0][1:]), test_subprotocol_indices[0][1:]],
    )
    np.testing.assert_array_equal(
        actual_stim_data[test_well_indices[1]],
        [np.array(test_time_indices[1]), test_subprotocol_indices[1]],
    )