-
Notifications
You must be signed in to change notification settings - Fork 128
Evident IX85 - DeviceAdapters for the XY stage (COM) and Hub (both through COM and SDK) #780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Implements device adapter for the Evident IX85 microscope system with support for: - Focus drive (Z-stage) with position and speed control - Nosepiece (objective turret) with 6 positions - Magnification changer (1x, 1.6x, 2x) - Light path selector - Condenser turret - DIA and EPI shutters - Mirror units (filter cube turrets) - Polarizer, DIC prism, EPI ND filter - Correction collar Key features: - Hub-based architecture with automatic device discovery - C++17 std::thread for monitoring thread (not MMDeviceThreadBase) - Thread-safe state management using std::mutex - Active notifications for real-time position updates - Serial communication via USB VCOM (115200 baud, even parity) Files added: - DeviceAdapters/EvidentIX85/EvidentProtocol.h - Protocol definitions - DeviceAdapters/EvidentIX85/EvidentModel.h/.cpp - State management - DeviceAdapters/EvidentIX85/EvidentHub.h/.cpp - Hub with monitoring - DeviceAdapters/EvidentIX85/EvidentIX85.h/.cpp - Device implementations - DeviceAdapters/EvidentIX85/EvidentIX85.vcxproj - VS project (C++17) - DeviceAdapters/EvidentIX85/Makefile.am - Unix build config Build system integration: - Added to micromanager.sln - Added to DeviceAdapters/configure.ac Also updated CLAUDE.md with repository documentation. 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix include: change <lock_guard> to <mutex> in EvidentModel.cpp - Add missing error codes: ERR_PORT_NOT_SET and ERR_PORT_CHANGE_FORBIDDEN - Add error text messages for new error codes 🤖 Generated with Claude Code (https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
SharedRuntime.
Magnification device is working and updating its position automatically.
- Updated Focus notification handler to compare current position with target - When positions match, set Busy state to false in the model - Applied same pattern to Nosepiece device - This ensures Busy() returns false once movement completes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Problem: During device initialization, when enabling notifications, the microscope sends immediate notification messages (e.g., NCA 1 after enabling magnification notifications). These notifications could be read by the command thread when expecting command responses, causing ERR_NEGATIVE_ACK (10102) errors. Solution: Modified GetResponse() to skip over notification messages and only return command responses. Notifications start with 'N' prefix (NFP, NCA, NOB, etc.) and are now filtered out during command-response sequences. The monitoring thread will handle these notifications asynchronously. Changes: - GetResponse() now identifies notification tags and skips them - Logs skipped notifications for debugging - Continues reading until actual command response is found - Monitoring thread code simplified for clarity This ensures clean command-response sequences during initialization even when notifications are enabled. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Problem: GetResponse() was incorrectly filtering out command acknowledgments like "NCA +" thinking they were notifications, causing timeout errors (10101) when waiting for responses. Root cause: Notification enable commands (e.g., "NCA 1") receive TWO responses: 1. "NCA +" - acknowledgment that notifications are enabled 2. "NCA 1" - immediate notification of current state The previous code skipped both, causing timeouts. Solution: Added check to distinguish between: - Acknowledgments: "NCA +", "NFP +" (should be returned) - Notifications: "NCA 1", "NFP 3110" (should be skipped) Now only skips notifications that contain data, not ack responses. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Major architectural change: Only the monitoring thread now reads from the serial port. All messages are routed through this thread to either: - Command responses -> Passed to waiting command thread via condition variable - Notifications -> Processed directly and update model state Benefits: - Eliminates all race conditions between threads - No messages are lost or skipped - All notifications are processed regardless of timing - Cleaner separation of concerns Implementation: - Added condition_variable for command-response communication - MonitorThreadFunc() is now sole serial port reader - GetResponse() waits on condition variable instead of reading - ProcessNotification() handles all notification types - IsNotificationTag() distinguishes notifications from responses This fixes the timeout issue where notifications arriving during command execution were causing state management problems. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Problem: ParseIntParameter and ParseLongParameter would crash with C++ exceptions when encountering non-numeric strings like "+", "!", or empty strings. std::stoi/std::stol throw exceptions on invalid input. Solution: Added comprehensive defensive checks: - Handle empty strings -> return -1 - Handle "X" or "x" (unknown) -> return -1 - Handle "+" or "!" (acknowledgments) -> return -1 - Wrap std::stoi/std::stol in try-catch blocks - Catch std::invalid_argument (not a number) - Catch std::out_of_range (number too large) - Return -1 for all error cases This prevents application crashes when the microscope returns unexpected response formats or when parsing acknowledgment messages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Problem: IsNotificationTag() was identifying "NCA +" as a notification
because the tag "NCA" matches CMD_MAGNIFICATION_NOTIFY. But "NCA +" is
actually a command acknowledgment (response to "NCA 1" enable command),
not a notification. This caused the monitoring thread to process it as
a notification instead of passing it to the waiting command thread,
resulting in timeout errors (10101).
Root cause: The function only checked the tag, not the message content.
Both acknowledgments ("NCA +") and notifications ("NCA 1") have the
same tag "NCA".
Solution: Changed IsNotificationTag() to:
1. Take the full message instead of just the tag
2. Check if it's a known notification tag
3. Verify it's NOT an acknowledgment (+ or !)
4. Only return true for actual data notifications
Now:
- "NCA +" -> IsNotificationTag = false -> Passed to command thread ✓
- "NCA 1" -> IsNotificationTag = true -> Processed as notification ✓
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Problem: Focus device's Busy() function would return true indefinitely after movement because the exact position comparison (pos == targetPos) would almost never succeed. Mechanical focus drives have settling tolerance, backlash, and encoder quantization that prevent landing exactly on the requested position. Solution: Added tolerance-based comparison for continuous position devices: 1. Added FOCUS_POSITION_TOLERANCE constant (10 steps = 100nm) 2. Created IsAtTargetPosition() helper function 3. Updated ProcessNotification() to use tolerance comparison 4. Added debug logging when target is reached Now the focus is considered "at position" when within ±100nm of target, which is well within mechanical precision and allows Busy state to clear properly. For discrete state devices (Nosepiece), exact equality is still used since positions are discrete and well-defined. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Problem: Notifications could arrive before the command thread set the target position and busy state, causing race conditions: Timeline of the bug: 1. Send "FG 196000" command 2. Notification "NFP 196000" arrives (but target not set yet) 3. Acknowledgment "FG +" arrives 4. Set target=196000 and busy=true 5. No more notifications → busy stays true forever The notification arrived BEFORE the target was set, so it couldn't clear the busy state. Then after setting busy=true, no more notifications arrive to clear it. Solution: Set target position and busy state BEFORE sending command: 1. Set target=196000 and busy=true 2. Send "FG 196000" command 3. Notification "NFP 196000" arrives → clears busy (matches target) 4. Acknowledgment "FG +" arrives → command succeeded 5. Return success If command fails, clear busy state before returning error. Applied to both Focus and Nosepiece devices. This allows notifications to arrive in any order relative to acknowledgments and still work correctly. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Changed EvidentMagnification from CStateDeviceBase to CMagnifierBase for better semantic correctness as a magnification changer device. Changes: - Inherit from CMagnifierBase<EvidentMagnification> - Implemented GetMagnification() returning actual magnification value - Changed property from "State" to "Magnification" (MM::g_Keyword_Magnification) - Property values are now 1.0, 1.6, 2.0 instead of 0, 1, 2 - Added static magnifications_[3] array with actual values - Removed GetNumberOfPositions() (not needed for CMagnifierBase) - Updated OnState() to OnMagnification() - Updated notification handler to use Magnification property Benefits: - More semantically correct device type - Properties are actual magnification values, not abstract states - Better integration with Micro-Manager's magnification system - Consistent with other magnification changer implementations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Changes made to make the code build and run correctly: 1. Added custom Magnification property constant - MM::g_Keyword_Magnification doesn't exist in MMDevice API - Defined g_Keyword_Magnification = "Magnification" in EvidentIX85.cpp - Added extern declaration in EvidentHub.cpp 2. Corrected device type registration - Changed from MM::StateDevice to MM::MagnifierDevice - Properly identifies device as magnification changer 3. Fixed GetMagnification() signature - Removed 'const' qualifier (was causing issues) - GetHub() is not const, so method can't be const 4. Updated all property references - Use g_Keyword_Magnification consistently - Applied in Initialize() and notification handler These changes ensure proper compilation and correct runtime behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Added GetCoreCallback()->OnMagnifierChanged() when magnification position changes. This is a specialized callback that notifies the Micro-Manager core when the magnification changer position changes. The callback is called in addition to OnPropertyChanged to ensure: 1. Property value updates correctly 2. Core is notified of magnification system change 3. Dependent calculations (pixel size, etc.) can be updated Called in ProcessNotification() when magnification notification arrives from the microscope. Fix: OnMagnifierChanged() requires the device parameter to identify which magnifier changed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Problem: During shutdown, each command was timing out (2 seconds each), making device unload take 8+ seconds. The issue was that StopMonitoring() was called BEFORE sending shutdown commands. Timeline of the bug: 1. StopMonitoring() called → monitoring thread stopped 2. Send "NFP 0" command → waits for response 3. No monitoring thread to read response → timeout (2s) 4. Send "NOB 0" command → timeout (2s) 5. Send "NCA 0" command → timeout (2s) 6. Send "L 0" command → timeout (2s) 7. Total: 8 seconds of timeouts Root cause: GetResponse() waits on a condition variable that's signaled by the monitoring thread. With the thread stopped, responses are never read from the serial port and never delivered to GetResponse(). Solution: Reordered shutdown sequence: 1. Send all shutdown commands (disable notifications, local mode) 2. Commands complete quickly (responses read by monitoring thread) 3. THEN stop monitoring thread 4. Total: milliseconds instead of 8 seconds Shutdown is now instant instead of 8+ seconds. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add a property (enabled by default) to the Nosepiece device that protects expensive objective lenses by lowering the focus to zero before changing nosepiece positions, then restoring the original focus position after the change completes. Implementation details: - Added SafeNosepieceChange property (Disabled/Enabled, default=Enabled) - Added SafeNosepieceChange() helper method that orchestrates the sequence: 1. Save current focus position 2. Move focus to zero 3. Wait for focus to complete (with 10s timeout) 4. Change nosepiece position 5. Wait for nosepiece to complete (with 10s timeout) 6. Restore original focus position - Gracefully handles missing Focus device (falls back to regular change) - Includes error recovery: attempts to restore focus even if nosepiece change fails - Modified OnState() to use SafeNosepieceChange when property is enabled 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implemented full functionality for all remaining IX85 devices: State Devices (with notifications): - EvidentCondenserTurret: Condenser turret control with position tracking - EvidentMirrorUnit1: Filter cube turret with notifications - EvidentPolarizer: Polarizer position control with notifications State Devices (query-based): - EvidentLightPath: Light path selector (Left/Bi50/Bi100/Right) - EvidentDICPrism: DIC prism position control - EvidentEPIND: EPI ND filter position control Shutter Devices: - EvidentDIAShutter: Transmitted light shutter (open/closed) - EvidentEPIShutter1: Reflected light shutter (open/closed) Generic Devices: - EvidentCorrectionCollar: Objective correction collar (0-100 range) Implementation patterns: - Devices with notifications: Set target/busy BEFORE sending command - Devices without notifications: Direct query and command execution - All devices check for hardware presence during initialization - Proper error handling with busy state cleanup on failures - Follow established patterns from Focus, Nosepiece, and Magnification 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The MirrorUnit1 device was incorrectly using CMD_MIRROR_UNIT_NOTIFY1 (NMUINIT1) which is an initialization notification, not a position change notification. This caused invalid position errors during property queries. Changed MirrorUnit1 to use query-based position tracking like DICPrism and EPIND: - Removed notification-based position tracking - Query hardware position in OnState BeforeGet handler - Busy() now returns false (instantaneous changes) - Removed EnableNotifications call from Initialize and Shutdown - Added comment explaining why notifications aren't used This matches the behavior of other query-based state devices in the adapter. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixed the root cause of the "Invalid state (position) requested" error. Several Query functions were not calling SetNumPositions(), causing numPos_ to remain 0 in device Initialize() functions, which resulted in invalid property limits. Changes: - Added constants to EvidentProtocol.h: * CONDENSER_TURRET_MAX_POS = 6 * MIRROR_UNIT_MAX_POS = 6 * POLARIZER_MAX_POS = 6 * DIC_PRISM_MAX_POS = 6 * EPIND_MAX_POS = 6 - Fixed Query functions in EvidentHub.cpp to call SetNumPositions(): * QueryCondenserTurret() * QueryPolarizer() * QueryDICPrism() * QueryMirrorUnit1() (original reported issue) * QueryMirrorUnit2() * QueryEPIND() Now all multi-position devices properly initialize their position limits, preventing out-of-bounds errors during property access. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixed initialization failure of CondenserTurret and other devices. The issue was attempting to send notification tags (NTR, NFP, NOB, etc.) as commands to the microscope, which are invalid. Notification tags are what the microscope sends TO us automatically when positions change - they are not commands we send TO the microscope. Changes: - Removed all EnableNotification() calls from device Initialize() and Shutdown() methods - Removed EnableNotifications() member functions from: * EvidentFocus * EvidentNosepiece * EvidentMagnification * EvidentCondenserTurret * EvidentPolarizer - Removed EnableNotifications() declarations from header file - Added comments explaining that notifications are automatic The monitoring thread already correctly receives and processes these notifications (NFP, NOB, NCA, NTR, NPO) when the microscope sends them. Fixes error where "NTR 1" command was being sent and microscope responded with "x" (command not recognized). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
… command
Restored EnableNotifications functionality which is required for devices
to receive position update notifications from the microscope.
The key fix: CondenserTurret now uses CMD_CONDENSER_TURRET ("TR") instead
of CMD_CONDENSER_TURRET_NOTIFY ("NTR") to enable notifications.
Changes:
- Restored EnableNotifications() calls in Initialize() and Shutdown() for:
* EvidentFocus (uses NFP)
* EvidentNosepiece (uses NOB)
* EvidentMagnification (uses NCA)
* EvidentCondenserTurret (uses TR - FIXED from NTR)
* EvidentPolarizer (uses NPO)
- Restored EnableNotifications() member functions in all affected devices
- Restored declarations in header file
Without these notification enable commands, the devices don't receive
position updates from the microscope when users manually change positions.
Fixes CondenserTurret initialization failure where "NTR 1" was being sent
(which is invalid) - now correctly sends "TR 1" to enable notifications.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed timeout issue when changing CondenserTurret position. The problem was that the Busy flag was never cleared because CondenserTurret does not send notifications (NTR) when movement completes. Analysis: - Unlike other devices (Focus, Nosepiece, etc.) that send notifications (NFP, NOB) upon movement completion, CondenserTurret does not send NTR - The "TR +" acknowledgment is only returned AFTER the movement completes - This appears to be a firmware oversight in the IX85 Solution: - Clear Busy flag immediately after receiving "TR +" positive acknowledgment - Update position in model when ack is received - Removed SetTargetPosition call (not needed without notifications) The positive ack itself indicates completion, so no separate notification is needed. This matches the actual firmware behavior. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Extends default timeout to 5 seconds (which is needed for devices that takes a long time to move such as the Condenser).
Hardware testing revealed several firmware quirks and bugs that needed
fixing:
Device Detection:
- Fix DIAShutter detection: was calling QueryDIAAperture instead of
QueryDIAShutter, causing device not to be marked as present
- Work around firmware bug in Polarizer and DICPrism: first query
returns "X" even when device is present. Now sends test command
(PO 0 or DIC 0) to verify presence by checking for positive ack
Polarizer Fixes:
- Fix notification command: use CMD_POLARIZER ("PO") instead of
CMD_POLARIZER_NOTIFY ("NPO") to enable notifications
- Change from 6 positions to 2 positions with labels "Out" and "In"
- Fix indexing: Polarizer uses 0-based indexing (PO 0, PO 1) unlike
other devices that use 1-based indexing
- Fix timeout: Polarizer doesn't send NPO notifications, only returns
"PO +" after movement completes, so clear Busy flag immediately
Focus Drive Fixes:
- Fix Busy flag not clearing when commanded to move to current position:
firmware doesn't send NFP notifications if position doesn't change,
so now check if already at target and clear Busy immediately
- Fix SafeNosepieceChange timeout when focus already at zero: apply
same fix when moving to zero and when restoring original position
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…ion properties Replace individual device queries with V command-based detection which is more reliable and avoids firmware bugs where some queries return "X" on first attempt. The V command takes unit numbers 1-18 and returns firmware version if device is present or error if absent. This approach properly handles compound units (V7 contains Polarizer/CondenserTurret/DIAShutter, V8 contains DICPrism). Add firmware version storage to MicroscopeModel and expose versions as read-only properties in device property browser for 10 devices: Focus, Nosepiece, LightPath, CondenserTurret, DIAShutter, Polarizer, EPIShutter1, MirrorUnit1, DICPrism, EPIND. Simplify QueryPolarizer and QueryDICPrism by removing workaround test commands since V7/V8 detection now confirms device presence before these queries are called. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…dicator support Add support for MirrorUnit2 and EPIShutter2 devices following the same pattern as MirrorUnit1 and EPIShutter1. Both devices detected via V11/V12 commands and include firmware version properties. Implement Manual Control Unit (MCU) indicator support for visual feedback on the microscope's control panel when in external control mode (L1): - I1 indicator displays nosepiece position (1-6) or "---" when undetermined - I2 indicator displays mirror unit position (1-6) or "---" when undetermined - Added 7-segment display hex codes (0xEE-0x6F) for digits 0-9 and dash (0x01) - Indicators update automatically via notification system (I1) and after position changes (I2) - I1/I2 responses consumed by monitoring thread to avoid command/response sync issues MCU detected via V13 command. Indicators initialized on startup and update during operation without requiring changes to device implementations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…lbacks Encoder Support: - Fixed E1 (nosepiece) encoder acknowledgment handling to prevent spurious moves - Implemented E2 (mirror unit) encoder with position wrapping and indicator updates - Added OnPropertyChanged callbacks for encoder-driven position changes MCU Indicators: - Implemented I4 indicator for LightPath position display - Maps 4 positions to I4 values: Left Port→1, 50/50→2, Binocular 100%→4, Right Port→0 - Implemented I5 indicator for EPIShutter1 state display - Closed→I5 1, Open→I5 2 - All indicators use fire-and-forget pattern to avoid deadlock MCU Focus Control: - Enable jog (focus) control dials on initialization with JG 1 command - Disable on shutdown with JG 0 command Stage Position Callbacks: - Added OnStagePositionChanged callback for NFP (focus position) notifications - Enables core to track focus position changes from manual jog dials and other sources - Position properly converted from 10nm units to micrometers Device Registration: - Registered Focus, Nosepiece, MirrorUnit1, LightPath, and EPIShutter1 with hub - Enables proper callback routing for property and position change notifications 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add three jog control properties to Focus device: - Jog Direction (Default/Reverse) via JGDR command - Jog Fine Sensitivity (1-100) via JGSF command - Jog Coarse Sensitivity (1-100) via JGSC command - Fix DIA brightness property and I3 indicator behavior: - Brightness property now always shows remembered value (not 0 when closed) - I3 indicator now always shows remembered brightness - Both stay in sync when adjusting brightness with shutter closed - E3 encoder updates both property and I3 when shutter closed - Actual lamp brightness only changes when logical shutter is open 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add three autofocus workflow modes controlled by "AF-Workflow-Mode" property: - Measure-Offset: User focuses manually, AF 1 runs, offset calculated and stored, then returns to original position - Find-Focus-With-Offset: AF 3 runs to find focus, then applies stored offset to Focus Drive and stops - Continuous-Focus: Starts AF 2 for continuous focus tracking Add "Measured-Focus-Offset-um" property to display and set the Z-offset value. Property updates automatically when offset is measured and triggers OnPropertyChanged callbacks to notify core of changes. Remove redundant "Tracking Mode" property - workflow modes now handle all autofocus mode selection. Code simplified to always use AF mode 2 as default. Fix AF mode 1 auto-stop detection to recognize success when position changes and correct offset sign calculation to properly represent correction from ZDC's focus position to user's desired focus position. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implemented a new device adapter for one-time objective configuration: - Created lens database with 102 Evident objectives (EvidentLensDatabase.h) - New EvidentObjectiveSetup device with per-position configuration - Auto-detects installed objectives via GOB queries - Allows database override selection with filtering by magnification/immersion - Per-position Send-To-SDK buttons for individual objective configuration - Sends objective info to SDK using S_OB command - Supports clearing positions with NONE command - Dynamic property updates after sending objectives 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…onEx. Replaced 48 individual position-specific handler functions with 8 parameterized handlers using CPropertyActionEx. This reduces code duplication and improves maintainability. Changes: - Replaced OnPos1-6DetectedName/Specs/DatabaseSelection/SendToSDK with parameterized OnPos* handlers - Replaced OnPos1-6Special* handlers with parameterized OnPosSpecial* handlers - Updated property creation to use CPropertyActionEx instead of switch statements - Net reduction of ~200 lines of code 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…e measured first, because we now have the offset as a property with a default value of zero. It can for instance be set as a pre-initialization property.
Ensure that OnPropertyChanged is called whenever the autofocus status changes, so that the MMCore and applications are properly notified of status changes. Changes: - UpdateAFStatus(): Now calls OnPropertyChanged when status changes (triggered by NAFST notifications from microscope) - StopAF(): Calls OnPropertyChanged when AF is stopped - IsContinuousFocusLocked(): Calls OnPropertyChanged when querying updates the status All callbacks check if the status actually changed before notifying to avoid unnecessary property change events. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
ports are offered as options in the HCW.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds comprehensive device adapter support for the Evident IX85 microscope system, implementing three separate adapters to support different communication methods and hardware configurations:
- EvidentIX85XYStage: COM-based XY stage controller for the IX5-SSA hardware
- EvidentIX85Win: SDK-based adapter providing full microscope control (focus, nosepiece, shutters, filters, etc.)
- EvidentIX85: COM-based adapter for general microscope control
The implementation includes protocol definitions, thread-safe state management, device detection, and proper resource handling. Additionally, a HamamatsuHamU adapter reference and documentation (CLAUDE.md) are added.
Reviewed changes
Copilot reviewed 37 out of 39 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| micromanager.sln | Adds four new project entries: HamamatsuHamU, EvidentIX85, EvidentIX85Win, and EvidentIX85XYStage with build configurations |
| DeviceAdapters/configure.ac | Adds EvidentIX85 to autotools build system |
| DeviceAdapters/PVCAM/StreamWriter.cpp | Contains extraneous blank line additions |
| DeviceAdapters/EvidentIX85XYStage/* | Complete XY stage adapter implementation with protocol, model, and device classes |
| DeviceAdapters/EvidentIX85Win/* | Full SDK-based microscope adapter with hub, multiple device types, lens database, and objective setup |
| DeviceAdapters/EvidentIX85/* | COM-based microscope adapter with hub and device implementations |
| CLAUDE.md | Repository documentation for Claude AI assistance |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
|
|
||
| // Load the DLL | ||
| dllHandle_ = LoadLibraryExA(dllFullPath.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH); |
Copilot
AI
Dec 1, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Untrusted DLL load via LoadLibraryExA with a relative filename and LOAD_WITH_ALTERED_SEARCH_PATH enables DLL hijacking and arbitrary code execution. An attacker who can place a crafted msl_pm_ix85.dll in the working directory, application directory, or a searched path can get it loaded instead of the intended vendor DLL. To fix: resolve and validate an absolute, trusted path to the SDK DLL (e.g., program files directory), call SetDefaultDllDirectories(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS) and load with LoadLibraryExW(absPath, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR), or use AddDllDirectory to a trusted directory; avoid passing just a filename and avoid LOAD_WITH_ALTERED_SEARCH_PATH. Optionally verify the DLL’s signature before use.
… current directory. This should match instructions on the website about downloading and installting the SDK zip file.
marktsuchida
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome! I took a quick look focusing on organization and added some comments.
| @@ -200,6 +200,7 @@ int StreamWriter::Start() | |||
| camera_->LogAdapterMessage(std::string("Started streaming to '") + path_ + "'", false); | |||
|
|
|||
| return DEVICE_OK; | |||
|
|
|||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to remove changes to PVCAM from this PR branch.
| @@ -0,0 +1,12 @@ | |||
|
|
|||
| AM_CXXFLAGS = $(MMDEVAPI_CXXFLAGS) $(BOOST_CPPFLAGS) -std=c++17 | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Boost flags are not needed because Boost is not used (I think?).
| catch(...) | ||
| { | ||
| LogMessage("Exception in DetectDevice!",false); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What part of the above code is expected to throw an exception?
| { | ||
| detectedObjectives_[idx].na = std::stod(params[2]); | ||
| } | ||
| catch (...) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's catch exceptions by type: catch (const std::logic_error&). Ditto for the 2 other catch (...) below in this file.
(Unlikely here, but catch (...) can obscure unexpected problems (e.g., out of memory) and complicate debugging.)
(Will need #include <stdexcept>.)
| #include "DeviceBase.h" | ||
| #include "EvidentModelWin.h" | ||
| #include "EvidentProtocolWin.h" | ||
| #include "EvidentIX85Win.h" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This include is circular; one direction should be removed.
| }; | ||
|
|
||
| // Complete lens database from SDK LensInfo.unit file | ||
| static const LensInfo LENS_DATABASE[] = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this gets included in more than one .cpp file, the big static array will be duplicated. Probably won't actually get duplicated if not accessed, but I think it's better to put this (and the implementations of the functions that access it) into a .cpp file.
…ect position after one-shot autofocus.
1b62656 to
c647311
Compare
…mand 20 more times at 50 ms intervals.:
asynchronously. This mechanism shoudl be used from with the Notification processing code, otherwise there will either be deadlock and timeout, and/or the command responses will not be processed leading to chaose down the line. Tested this code with 528 channel switches, autofocus and z-stacks, and no issues observed,
…. Will try relative paths again later.
No description provided.