Skip to content

Commit

Permalink
Release v1.2.3 (#640)
Browse files Browse the repository at this point in the history
- Add post stiffness display under plate barcode display.
- Add biphasic stim check.
- Update itles of live view screens.
- Handle network errors when logging in.
- Fix dropdown overflow styling.
- Barcode change warning now displays in the correct position.
- Barcode change while stimulating now correctly requires another stim configuration check before restarting stimulation.
- Can no longer manually edit barcodes while stimulating.
  • Loading branch information
tannermpeterson committed Apr 30, 2024
1 parent 2903c00 commit 4cbbeee
Show file tree
Hide file tree
Showing 19 changed files with 286 additions and 151 deletions.
26 changes: 24 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,12 +1,34 @@
Changelog for Mantarray Desktop App
===================================

1.2.2 (unreleased)
1.2.3 (unreleased)
------------------

Added:
^^^^^^
- Ability to view changelog.
- Post stiffness display under plate barcode display.
- Biphasic stim check.

Changed:
^^^^^^^^
- Titles of live view screens.

Fixed:
^^^^^^
- Handle network errors when logging in.
- Dropdown overflow styling.
- Barcode change warning now displays in the correct position.
- Barcode change while stimulating now correctly requires another stim configuration check before restarting stimulation.
- Can no longer manually edit barcodes while stimulating.


1.2.2 (2024-02-06)
------------------

Added:
^^^^^^
- User-defined metadata can be included in recording files.
- Ability to view MA controller changelog.

Changed:
^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions controller/src/mantarray_desktop_app/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ class StimulatorCircuitStatuses(IntEnum):
OPEN = 1
SHORT = 2
ERROR = 3
NOT_CHECKED = 4


class StimProtocolStatuses(IntEnum):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,10 +517,12 @@ def _check_and_handle_instrument_comm_to_main_queue(self) -> None:
elif command == "start_stim_checks":
key = "stimulator_circuit_statuses"
stimulator_circuit_statuses = communication[key]
self._values_to_share_to_server[key] = stimulator_circuit_statuses
self._queue_websocket_message(
{"data_type": key, "data_json": json.dumps(stimulator_circuit_statuses)}
)
status_combined = {
well_idx: list(StimulatorCircuitStatuses)[max(statuses.values()) + 1].name.lower()
for well_idx, statuses in stimulator_circuit_statuses.items()
}
self._values_to_share_to_server[key] = status_combined
self._queue_websocket_message({"data_type": key, "data_json": json.dumps(status_combined)})
elif communication_type == "board_connection_status_change":
board_idx = communication["board_index"]
self._values_to_share_to_server["in_simulation_mode"] = not communication["is_connected"]
Expand Down
2 changes: 2 additions & 0 deletions controller/src/mantarray_desktop_app/main_process/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,8 @@ def login_user() -> Response:
auth_response = validate_user_credentials(request.args)
except LoginFailedError as e:
return Response(json.dumps(str(e)), status=f"401 {repr(e)}")
except requests.exceptions.ConnectionError: # pragma: no cover
return Response(json.dumps({"err": "network"}), mimetype="application/json")

queue_command_to_main(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ def _process_main_module_command(self, comm_from_pc: bytes) -> None:
# Tanner (4/8/22): currently assuming that stim checks will take a negligible amount of time
for module_readings in self._adc_readings:
status = convert_adc_readings_to_circuit_status(*module_readings)
response_body += struct.pack("<HHB", *module_readings, status)
response_body += struct.pack("<HHB", *module_readings, status) * 2
elif packet_type == SERIAL_COMM_SET_SAMPLING_PERIOD_PACKET_TYPE:
response_body += self._update_sampling_period(comm_from_pc)
elif packet_type == SERIAL_COMM_START_DATA_STREAMING_PACKET_TYPE:
Expand Down
21 changes: 13 additions & 8 deletions controller/src/mantarray_desktop_app/sub_processes/mc_comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@
from ..constants import SERIAL_COMM_STOP_STIM_PACKET_TYPE
from ..constants import STIM_COMPLETE_SUBPROTOCOL_IDX
from ..constants import STIM_MODULE_ID_TO_WELL_IDX
from ..constants import StimulatorCircuitStatuses
from ..constants import STM_VID
from ..exceptions import FirmwareAndSoftwareNotCompatibleError
from ..exceptions import FirmwareGoingDormantError
Expand Down Expand Up @@ -851,16 +850,22 @@ def _process_comm_from_instrument(self, packet_type: int, packet_payload: bytes)
elif prev_command["command"] == "start_stim_checks":
stimulator_check_dict = convert_stimulator_check_bytes_to_dict(response_data)

stimulator_circuit_statuses: Dict[int, str] = {}
adc_readings: Dict[int, Tuple[int, int]] = {}

for module_id, (adc8, adc9, status_int) in enumerate(zip(*stimulator_check_dict.values())):
stimulator_circuit_statuses: Dict[int, Dict[str, str]] = {}
adc_readings: Dict[int, Dict[str, Tuple[int, int]]] = {}

for module_id, (
adc8_pos,
adc9_pos,
status_int_pos,
adc8_neg,
adc9_neg,
status_int_neg,
) in enumerate(zip(*stimulator_check_dict.values())):
well_idx = STIM_MODULE_ID_TO_WELL_IDX[module_id]
if well_idx not in prev_command["well_indices"]:
continue
status_str = list(StimulatorCircuitStatuses)[status_int + 1].name.lower()
stimulator_circuit_statuses[well_idx] = status_str
adc_readings[well_idx] = (adc8, adc9)
stimulator_circuit_statuses[well_idx] = {"pos": status_int_pos, "neg": status_int_neg}
adc_readings[well_idx] = {"pos": (adc8_pos, adc9_pos), "neg": (adc8_neg, adc9_neg)}

prev_command["stimulator_circuit_statuses"] = stimulator_circuit_statuses
prev_command["adc_readings"] = adc_readings
Expand Down
18 changes: 6 additions & 12 deletions controller/src/mantarray_desktop_app/utils/serial_comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,7 @@ def _get_checksum_bytes(packet: bytes) -> bytes:
return crc32(packet).to_bytes(SERIAL_COMM_CHECKSUM_LENGTH_BYTES, byteorder="little")


def create_data_packet(
timestamp: int,
packet_type: int,
packet_payload: bytes = bytes(0),
) -> bytes:
def create_data_packet(timestamp: int, packet_type: int, packet_payload: bytes = bytes(0)) -> bytes:
"""Create a data packet to send to the PC."""
packet_base = convert_to_timestamp_bytes(timestamp) + bytes([packet_type])
packet_remainder_size = len(packet_base) + len(packet_payload) + SERIAL_COMM_CHECKSUM_LENGTH_BYTES
Expand All @@ -104,10 +100,7 @@ def create_data_packet(

def validate_checksum(comm_from_pc: bytes) -> bool:
expected_checksum = crc32(comm_from_pc[:-SERIAL_COMM_CHECKSUM_LENGTH_BYTES])
actual_checksum = int.from_bytes(
comm_from_pc[-SERIAL_COMM_CHECKSUM_LENGTH_BYTES:],
byteorder="little",
)
actual_checksum = int.from_bytes(comm_from_pc[-SERIAL_COMM_CHECKSUM_LENGTH_BYTES:], byteorder="little")
return actual_checksum == expected_checksum


Expand Down Expand Up @@ -225,15 +218,16 @@ def get_serial_comm_timestamp() -> int:


def convert_stimulator_check_bytes_to_dict(stimulator_check_bytes: bytes) -> Dict[str, List[int]]:
stimulator_checks_as_ints = struct.unpack("<" + "HHB" * 24, stimulator_check_bytes)
stimulator_checks_as_ints = struct.unpack("<" + "HHB" * 24 * 2, stimulator_check_bytes)
# convert to lists of adc8, adc9, and status where the index of each list is the module id. Only creating an array here to reshape easily
stimulator_checks_list = (
np.array(stimulator_checks_as_ints, copy=False)
.reshape((3, len(stimulator_checks_as_ints) // 3), order="F")
.reshape((6, len(stimulator_checks_as_ints) // 6), order="F")
.tolist()
)
stimulator_checks_dict = {
key: stimulator_checks_list[i] for i, key in enumerate(["adc8", "adc9", "status"])
key: stimulator_checks_list[i]
for i, key in enumerate(["adc8_pos", "adc9_pos", "status_pos", "adc8_neg", "adc9_neg", "status_neg"])
}
return stimulator_checks_dict

Expand Down
54 changes: 9 additions & 45 deletions controller/tests/mc_comm/test_stimulation.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from mantarray_desktop_app.constants import SERIAL_COMM_CHECKSUM_LENGTH_BYTES
from mantarray_desktop_app.constants import SERIAL_COMM_PAYLOAD_INDEX
from mantarray_desktop_app.constants import STIM_MODULE_ID_TO_WELL_IDX
from mantarray_desktop_app.constants import StimulatorCircuitStatuses
from mantarray_desktop_app.simulators import mc_simulator
from mantarray_desktop_app.utils.serial_comm import convert_adc_readings_to_circuit_status
from mantarray_desktop_app.utils.stimulation import get_subprotocol_dur_us
Expand Down Expand Up @@ -52,11 +51,7 @@
]


def set_stimulation_protocols(
mc_fixture,
simulator,
stim_info,
):
def set_stimulation_protocols(mc_fixture, simulator, stim_info):
mc_process = mc_fixture["mc_process"]
from_main_queue = mc_fixture["board_queues"][0][0]
to_main_queue = mc_fixture["board_queues"][0][1]
Expand Down Expand Up @@ -132,12 +127,11 @@ def test_McCommunicationProcess__processes_start_stim_checks_command__and_sends_
for well_idx in test_well_indices:
well_readings = adc_readings[well_idx]
status_int = convert_adc_readings_to_circuit_status(*well_readings)
status = list(StimulatorCircuitStatuses)[status_int + 1].name.lower()
stimulator_circuit_statuses[well_idx] = status
stimulator_circuit_statuses[well_idx] = {"pos": status_int, "neg": status_int}
assert msg_to_main["stimulator_circuit_statuses"] == stimulator_circuit_statuses

assert msg_to_main["adc_readings"] == {
well_idx: adc_reading
well_idx: {"pos": adc_reading, "neg": adc_reading}
for well_idx, adc_reading in enumerate(adc_readings)
if well_idx in test_well_indices
}
Expand Down Expand Up @@ -304,13 +298,7 @@ def test_McCommunicationProcess__handles_stimulation_status_comm_from_instrument
"protocol_id": "A",
"stimulation_type": "C",
"run_until_stopped": False,
"subprotocols": [
{
"type": "loop",
"num_iterations": 1,
"subprotocols": [test_subprotocol],
}
],
"subprotocols": [{"type": "loop", "num_iterations": 1, "subprotocols": [test_subprotocol]}],
}
],
"protocol_assignments": {
Expand Down Expand Up @@ -389,11 +377,7 @@ def test_McCommunicationProcess__handles_stimulation_status_comm_from_instrument
"stimulation_type": "V",
"run_until_stopped": False,
"subprotocols": [
{
"type": "loop",
"num_iterations": 1,
"subprotocols": [test_subprotocol] * 2,
}
{"type": "loop", "num_iterations": 1, "subprotocols": [test_subprotocol] * 2}
],
}
],
Expand Down Expand Up @@ -518,13 +502,7 @@ def test_McCommunicationProcess__handles_stimulation_status_comm_from_instrument
"protocol_id": "A",
"stimulation_type": "C",
"run_until_stopped": False,
"subprotocols": [
{
"type": "loop",
"num_iterations": 1,
"subprotocols": [test_subprotocol],
}
],
"subprotocols": [{"type": "loop", "num_iterations": 1, "subprotocols": [test_subprotocol]}],
}
],
"protocol_assignments": {
Expand Down Expand Up @@ -640,11 +618,7 @@ def test_McCommunicationProcess__protocols_can_be_updated_and_stimulation_can_be
"stimulation_type": "C",
"run_until_stopped": False,
"subprotocols": [
{
"type": "loop",
"num_iterations": 1,
"subprotocols": [test_first_subprotocol],
}
{"type": "loop", "num_iterations": 1, "subprotocols": [test_first_subprotocol]}
],
}
],
Expand Down Expand Up @@ -721,24 +695,14 @@ def test_McCommunicationProcess__stim_packets_sent_to_file_writer_after_restarti
"protocol_id": "A",
"stimulation_type": "C",
"run_until_stopped": True,
"subprotocols": [
{
"type": "loop",
"num_iterations": 1,
"subprotocols": [test_subprotocol],
}
],
"subprotocols": [{"type": "loop", "num_iterations": 1, "subprotocols": [test_subprotocol]}],
},
{
"protocol_id": "B",
"stimulation_type": "C",
"run_until_stopped": True,
"subprotocols": [
{
"type": "loop",
"num_iterations": 1,
"subprotocols": [get_random_stim_pulse()],
}
{"type": "loop", "num_iterations": 1, "subprotocols": [get_random_stim_pulse()]}
],
},
],
Expand Down
18 changes: 14 additions & 4 deletions controller/tests/process_monitor/test_process_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1935,8 +1935,18 @@ def test_MantarrayProcessesMonitor__passes_stim_status_check_results_from_mc_com
queue_to_server_ws = test_process_manager.queue_container.to_websocket

test_wells = range(randint(0, 3), randint(20, 24))
possible_stim_statuses = [member.name.lower() for member in StimulatorCircuitStatuses]
stim_check_results = {well_idx: choice(possible_stim_statuses) for well_idx in test_wells}
stim_check_results = {
well_idx: {
"pos": choice(list(StimulatorCircuitStatuses)),
"neg": choice(list(StimulatorCircuitStatuses)),
}
for well_idx in test_wells
}

combined_results = {
well_idx: list(StimulatorCircuitStatuses)[max(res["pos"], res["neg"]) + 1].name.lower()
for well_idx, res in stim_check_results.items()
}

# values in this dict don't matter, just the keys
adc_readings = {well_idx: None for well_idx in test_wells}
Expand All @@ -1956,13 +1966,13 @@ def test_MantarrayProcessesMonitor__passes_stim_status_check_results_from_mc_com
copy.deepcopy(test_comm), instrument_comm_to_main
)
invoke_process_run_and_check_errors(monitor_thread)
assert shared_values_dict["stimulator_circuit_statuses"] == stim_check_results
assert shared_values_dict["stimulator_circuit_statuses"] == combined_results

confirm_queue_is_eventually_of_size(queue_to_server_ws, 1)
ws_message = queue_to_server_ws.get(timeout=QUEUE_CHECK_TIMEOUT_SECONDS)
assert ws_message == {
"data_type": "stimulator_circuit_statuses",
"data_json": json.dumps(stim_check_results),
"data_json": json.dumps(combined_results),
}

# convert all well idxs to well names
Expand Down

0 comments on commit 4cbbeee

Please sign in to comment.