diff --git a/controller/src/controller/subsystems/instrument_comm.py b/controller/src/controller/subsystems/instrument_comm.py index 1708c73..1f686a7 100644 --- a/controller/src/controller/subsystems/instrument_comm.py +++ b/controller/src/controller/subsystems/instrument_comm.py @@ -35,6 +35,7 @@ from ..constants import STIM_FINAL_SEXTANT from ..constants import STIM_MODULE_ID_TO_WELL_IDX from ..constants import StimScheduleType +from ..constants import StimulationStates from ..constants import STM_VID from ..exceptions import FirmwareGoingDormantError from ..exceptions import IncorrectInstrumentConnectedError @@ -729,7 +730,15 @@ async def _process_command_response(self, packet_type: int, response_data: bytes f"Instrument running in {'offline' if self._system_in_offline_mode else 'online'} mode at time of connection" ) case "end_offline_mode": - prev_command_info |= parse_end_offline_mode_bytes(response_data) + end_offline_mode_info = parse_end_offline_mode_bytes(response_data) + logger.info(f"Offline mode info: {end_offline_mode_info}") + prev_command_info |= end_offline_mode_info + # update which protocols are running since they may have stopped while in offline mode + self._protocols_running = { + protocol_idx + for protocol_idx, state in enumerate(prev_command_info["stimulation_protocol_statuses"]) + if state == StimulationStates.RUNNING + } # need to get sub wells before sending response send_response = False await self._send_data_packet(SerialCommPacketTypes.GET_SUB_WELLS) diff --git a/virtual-instrument/src/virtual_instrument/virtual_instrument.py b/virtual-instrument/src/virtual_instrument/virtual_instrument.py index b108ff1..a74d198 100644 --- a/virtual-instrument/src/virtual_instrument/virtual_instrument.py +++ b/virtual-instrument/src/virtual_instrument/virtual_instrument.py @@ -76,6 +76,11 @@ from .exceptions import UnrecognizedSerialCommPacketTypeError from .stimulation import StimulationProtocolManager +logging.basicConfig(format="%(asctime)s.%(msecs)03d -- %(message)s", datefmt="%H:%M:%S", level=logging.INFO) + + +logger = logging.getLogger(__name__) + MAGIC_WORD_LEN = len(SERIAL_COMM_MAGIC_WORD_BYTES) AVERAGE_MC_REBOOT_DURATION_SECONDS = MAX_MC_REBOOT_DURATION_SECONDS / 2 @@ -348,7 +353,7 @@ def _get_timestamp(self) -> int: return self._get_absolute_timer() def _send_data_packet( - self, packet_type: int, data_to_send: bytes = bytes(0), truncate: bool = False + self, packet_type: SerialCommPacketTypes, data_to_send: bytes = bytes(0), truncate: bool = False ) -> None: timestamp = self._get_timestamp() data_packet = create_data_packet(timestamp, packet_type, data_to_send) @@ -357,7 +362,7 @@ def _send_data_packet( 0, len(data_packet) - 1 ) data_packet = data_packet[trunc_index:] - print("SEND:", packet_type) # allow-print + logger.info(f"SEND: {packet_type}") if self.conn: self.conn.sendall(data_packet) @@ -406,7 +411,7 @@ def _check_socket(self): except BlockingIOError: return - print(f"CONNECTION MADE: {addr}") # allow-print + logger.info(f"CONNECTION MADE: {addr}") self.conn.setblocking(False) def _handle_comm_from_controller(self) -> None: @@ -421,6 +426,8 @@ def _handle_comm_from_controller(self) -> None: if self._connection_status != InstrumentConnectionStatuses.OFFLINE: raise + logger.info("DISCONNECT") + self.conn.close() self.conn = None return @@ -472,7 +479,7 @@ def _process_main_module_command(self, comm_from_controller: bytes) -> None: response_body = bytes(0) packet_type = comm_from_controller[SERIAL_COMM_PACKET_TYPE_INDEX] - print("RECV:", packet_type) # allow-print + logger.info(f"RECV: {packet_type}") if packet_type == SerialCommPacketTypes.REBOOT: self._reboot_time_secs = perf_counter() elif packet_type == SerialCommPacketTypes.HANDSHAKE: @@ -718,6 +725,9 @@ def _update_sampling_period(self, comm_from_controller: bytes) -> bytes: return update_status_byte def _handle_status_beacon(self) -> None: + if self._connection_status == InstrumentConnectionStatuses.OFFLINE: + return + if self._time_of_last_status_beacon_secs is None: self._send_status_beacon(truncate=self._time_of_last_handshake_secs is None) return @@ -730,7 +740,7 @@ def _send_status_beacon(self, truncate: bool = False) -> None: self._send_data_packet(SerialCommPacketTypes.STATUS_BEACON, bytes(self._status_codes), truncate) def _handle_barcode(self) -> None: - if self._ready_to_send_barcode: + if self._ready_to_send_barcode and self._connection_status != InstrumentConnectionStatuses.OFFLINE: self._send_data_packet( SerialCommPacketTypes.BARCODE_FOUND, bytes(self.default_plate_barcode, encoding="ascii") ) @@ -766,6 +776,7 @@ def _handle_magnetometer_data_packet(self) -> None: Since this process iterates once per 10 ms, it is possible that more than one data packet must be sent. """ + if self._timepoint_of_last_data_packet_us is None: # making mypy happy raise NotImplementedError("_timepoint_of_last_data_packet_us should never be None here") us_since_last_data_packet = _get_us_since_last_data_packet(self._timepoint_of_last_data_packet_us) @@ -785,7 +796,8 @@ def _handle_magnetometer_data_packet(self) -> None: # increment values self._time_index_us += self._sampling_period_us self._simulated_data_index = (self._simulated_data_index + 1) % simulated_data_len - # self._output_queue.put_nowait(data_packet_bytes) + # if self._connection_status != InstrumentConnectionStatuses.OFFLINE: + # self._output_queue.put_nowait(data_packet_bytes) # update timepoint self._timepoint_of_last_data_packet_us += num_packets_to_send * self._sampling_period_us @@ -863,7 +875,7 @@ def _handle_stimulation_packets(self) -> None: dur_since_subprotocol_start -= curr_subprotocol_duration_us curr_subprotocol_duration_us = get_subprotocol_dur_us(curr_subprotocol) - if num_status_updates > 0: + if num_status_updates > 0 and self._connection_status != InstrumentConnectionStatuses.OFFLINE: packet_bytes = bytes([num_status_updates]) + packet_bytes self._send_data_packet(SerialCommPacketTypes.STIM_STATUS, packet_bytes) @@ -878,7 +890,7 @@ def _handle_stimulation_packets(self) -> None: self._is_stimulating = False def _send_stim_sextant_status_update(self, sextant_num: int) -> None: - print("STIM SEXTANT:", sextant_num) # allow-print + logger.info(f"STIM SEXTANT: {sextant_num}") self._send_data_packet(SerialCommPacketTypes.STIM_SEXTANT_STATUS, bytes([sextant_num])) def _drain_all_queues(self) -> dict[str, Any]: