Skip to content

logging: add logging mechanism, hooks to log uncaught crashes, and an example use of the logging tools - #1

Merged
ianohara merged 7 commits into
masterfrom
ian-logging-tools
Oct 25, 2024
Merged

logging: add logging mechanism, hooks to log uncaught crashes, and an example use of the logging tools#1
ianohara merged 7 commits into
masterfrom
ian-logging-tools

Conversation

@ianohara

Copy link
Copy Markdown
Collaborator

This adds logging tools in the squid.logging module (along with the squid package), and two helpers there for getting loggers and setting the squid logger logging level. This allows us to have a dedicated hierarchy of loggers that we can configure while leaving all the loggers setup by other libraries alone.

It also adds an example usage in the control._def.py module. We can start converting individual files after this.

It also lays the groundwork for better crash reports by allowing us to hook up handlers for uncaught exceptions. By default these just print to console, but we want to be able to hook up a FileHandler to our logger and have the uncaught exceptions go there as well. Actually creating the FileHandler and attaching it to the root squid logger is to come.

NOTE: This was originally hongquanli#114 but has been moved here since this is going to be the main development repository moving forward.

Tested by:

  1. Attempting to run main_hcs.py, and seeing log messages
  2. I also added a random raise ValueError("test test") in main_hcs.py for a different run to make sure that the handlers work as expected
  3. And via python -m pytest.

@ianohara
ianohara merged commit 2afb80a into master Oct 25, 2024
hongquanli pushed a commit that referenced this pull request Feb 25, 2025
hongquanli added a commit that referenced this pull request Jan 28, 2026
Address code review issues #1 and #2:

1. Add port index validation (0-15) to Microcontroller methods:
   - set_port_intensity, turn_on_port, turn_off_port, set_port_illumination
   - Raises ValueError for out-of-range ports, TypeError for non-integers

2. Add wait_till_operation_is_completed() to IlluminationController.set_port_intensity()
   - Makes behavior consistent with turn_on_port() and turn_off_port()
   - Prevents race conditions when setting intensity before turning on

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
hongquanli added a commit that referenced this pull request Mar 24, 2026
- Use SCREW_PITCH_W_MM and config bounds for step_size calculation
  instead of hardcoded 1.0/8 (comment #1)
- Add assert not t.is_alive() after join(timeout=...) to fail
  deterministically on deadlock (comment #2)
- Assert call_order sequence is non-interleaving, accounting for the
  offset move within _home_wheel_unlocked (comment #3)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Alpaca233 added a commit that referenced this pull request Apr 27, 2026
…hanges

Bug: clicking Start Acquisition a second time made the plate view show wells
at a different grid position than before. Root cause: the channel layer's
canvas was allocated by _create_channel_layer on the first tile of run #1
and reused as-is for run #2, but a new run can produce a different
well_slot_shape (max-extent over scanned wells) or scan a different subset
of wells — both shift where new tiles compute their slot offsets, while the
canvas geometry from run #1 has not moved.

Fix: setPlateLayout now wipes existing channel canvases to fresh zero-filled
plate-sized arrays whenever the new slot dimensions don't match the current
canvas, OR the set of wells in the new run differs from the previous one.
For re-runs of the *same* wells with the *same* extent, the canvas is
preserved (existing tiles stay visible until overwritten by new ones).

Plumbing changes:
- PlateViewInit gains a `well_ids: List[str]` field.
- multi_point_worker._emit_plate_layout populates it from
  scan_region_fov_coords_mm.
- gui_hcs.MultiPointController.plate_view_init Qt signal carries the new list.
- UnifiedMosaicWidget.setPlateLayout accepts well_ids, compares against the
  cached _plate_well_ids frozenset, and zeroes affected layers when the
  comparison shows coverage changed (or dims changed).
- Plate boundary layer is dropped so it gets redrawn at the new slot scale.

Tests: 1132 passed, 5 skipped, 1 xfailed. Smoke test clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
hongquanli added a commit that referenced this pull request May 18, 2026
…tbeat-skip, etc.

Addresses six issues from the copilot-pull-request-reviewer bot review:

#1 (cephla.py): Call acknowledge_aborted_command() after catching
CommandAborted (and after the inner resend failure if it was also a
CommandAborted), so the next send_command doesn't log the spurious
"Last command aborted and not cleared before new command sent!"
warning. The inner ack is gated on isinstance(e2, CommandAborted) to
avoid the "ack with nothing to ack" path on TimeoutError.

#2 (cephla.py): Drop the redundant `target_usteps = ...` recompute
after _home_wheel. config and target_pos haven't changed and
_target_pos_to_usteps doesn't depend on current_pos.

#3 (cephla.py): Fix _home_wheel docstring — wheel is driven to
config.min_index (typically slot 1), not "slot 0".

#5 (firmware/serial_communication.cpp): Skip the
`mcu_cmd_execution_status = COMPLETED_WITHOUT_ERRORS` reset when
processing a HEARTBEAT. The keepalive has no result to report, and
resetting would clobber a pending CMD_EXECUTION_ERROR from the
previous command if the broadcast hasn't fired yet. Eliminates the
narrow race where heartbeat traffic interleaves a failure broadcast.

#6 (firmware/stage_commands.cpp): Split mark_move_failed() into two
helpers — mark_move_failed() (for paths that already set
mcu_cmd_execution_in_progress = true) and report_move_error() (for
early-return paths that didn't). The !enabled branch in
dispatch_filterwheel_move now uses report_move_error() so it doesn't
spuriously unwind in_progress for an unrelated motion in flight on
another axis. Invariant: only the function that claimed in_progress
gets to clear it.

#7 (test_filter_wheel.py): Add `getattr(mc, move_rel_attr).assert_not_called()`
to both parametrized CommandAborted/TimeoutError tests, so the
absolute-MOVETO recovery path is enforced — fall-back to relative
MOVE would now be caught.
Alpaca233 added a commit that referenced this pull request May 26, 2026
Picks up the remaining cheap, reasonable fixes from PR #535 review:

#1 cv2.imwrite return value: cv2 returns False (without raising) on
missing dir, missing codec, or unsupported dtype. We now check the
return value, log a throttled WARNING per failure, increment a
_silent_write_failures counter (surfaced in the diagnostics summary),
and skip counter increment + CSV row write so no row references a
file that isn't on disk.

#4 channel-name path sanitization: get_image_filepath previously only
replaced spaces; now also strips '/', '\\', ':', '*', '?', '"', '<',
'>', '|'. Strict improvement for both the recording widget and the
multipoint pipeline.

#7 frames.csv open guarded: a PermissionError on the CSV file no
longer leaves the widget in a half-started state. On failure,
experiment_ID is cleared and the exception is re-raised so the
toggle_recording slot doesn't run streamHandler.start_recording().

#8 drop the dead image_format constructor parameter: process_queue
hasn't read it since the cv2.imwrite refactor — it derives the
extension from control._def.Acquisition.IMAGE_FORMAT via
get_image_filepath. No in-tree callers passed it.

#9 OSError auto-stop emits only once per recording: a saturated queue
hitting a persistent OSError (e.g., full disk) no longer fires 10
redundant stop_recording signals. _stop_requested_from_writer is
reset in start_new_experiment; subsequent errors during shutdown
log as WARNING instead of re-emitting.

#18 channel_provider returning None at start_new_experiment now logs
a distinct WARNING. Before, a user who launched the GUI and clicked
Record without first selecting a live channel got every frame
tagged 'live' with no warning (because the provider lambda itself
was non-None, just its return value was).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Alpaca233 added a commit that referenced this pull request May 30, 2026
Three fixes from the post-commit review of 3ece218:

1. Move set_exposure_time(self.get_exposure_time()) INSIDE the
   _pause_streaming with-block. It chains into _update_internal_settings
   → _calculate_strobe_info which itself writes PRECISE_FRAMERATE
   (camera_toupcam.py:130). With the call after the with-block, that
   write happened on a *live* stream — exactly the path the diff's own
   comment says is unreliable. Now strobe-info, ExpoTime, and the
   PRECISE_FRAMERATE write all run while paused.

2. Drop the now-redundant CONTINUOUS-specific MAX→PRECISE_FRAMERATE
   put. _calculate_strobe_info already does the same write, and with
   #1 it now does it inside the pause window.

3. Reset self._trigger_sent = False after the pause. stop_streaming()
   cancels any in-flight frame callback that would have cleared the
   flag, so a mode switch initiated shortly after send_trigger() would
   leave _trigger_sent=True until ~1.5×exposure+4s timeout — silently
   blocking the next trigger.

Also broadened the diagnostic log's except from HRESULTException to
Exception so a non-numeric get_Option return (TypeError on the format
spec) doesn't propagate out and break the mode switch at the last step.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant