diff --git a/DeviceAdapters/EvidentIX85Win/EvidentHubWin.cpp b/DeviceAdapters/EvidentIX85Win/EvidentHubWin.cpp index 2acf57eb7..4e90cf4c6 100644 --- a/DeviceAdapters/EvidentIX85Win/EvidentHubWin.cpp +++ b/DeviceAdapters/EvidentIX85Win/EvidentHubWin.cpp @@ -977,6 +977,19 @@ int EvidentHubWin::ExecuteCommand(const std::string& command, std::string& respo return ret; } +void EvidentHubWin::ExecuteCommandFireAndForget(const std::string& command, + std::function callback) +{ + CommandTask task(command); + task.completionCallback = std::move(callback); + + { + std::lock_guard lock(queueMutex_); + commandQueue_.push(std::move(task)); + } + queueCV_.notify_one(); +} + void EvidentHubWin::CommandWorkerThread() { while (workerRunning_) @@ -1008,16 +1021,25 @@ void EvidentHubWin::CommandWorkerThread() // Execute command (outside queue lock to allow new submissions) std::string response; + int ret = DEVICE_ERR; try { - int ret = ExecuteCommandInternal(task.command, response); - task.responsePromise.set_value(std::make_pair(ret, response)); + ret = ExecuteCommandInternal(task.command, response); } catch (...) { - task.responsePromise.set_exception(std::current_exception()); + if (task.completionCallback) + task.completionCallback(DEVICE_ERR, ""); + else + task.responsePromise.set_exception(std::current_exception()); + continue; } + + if (task.completionCallback) + task.completionCallback(ret, response); + else + task.responsePromise.set_value(std::make_pair(ret, response)); } } @@ -1259,8 +1281,9 @@ int EvidentHubWin::DoDeviceDetection() model_.SetDevicePresent(DeviceType_OffsetLens, true); availableDevices_.push_back(DeviceType_OffsetLens); detectedDevicesByName_.push_back(g_OffsetLensDeviceName); + model_.SetLimits(DeviceType_OffsetLens, OFFSET_LENS_MIN_POS, OFFSET_LENS_MAX_POS); - // Query initial offset lens position + // Query initial offset lens position and hardware range QueryOffsetLens(); } @@ -1874,12 +1897,81 @@ int EvidentHubWin::QueryOffsetLens() { int pos = ParseIntParameter(params[0]); model_.SetPosition(DeviceType_OffsetLens, pos); + + long nosepiecePos = model_.GetPosition(DeviceType_Nosepiece); + if (nosepiecePos >= 1 && nosepiecePos <= NOSEPIECE_MAX_POS) + QueryOffsetLensRange(static_cast(nosepiecePos)); + else + model_.SetLimits(DeviceType_OffsetLens, OFFSET_LENS_MIN_POS, OFFSET_LENS_MAX_POS); + return DEVICE_OK; } return ERR_DEVICE_NOT_AVAILABLE; } +int EvidentHubWin::QueryOffsetLensRange(int nosepiecePos) +{ + std::string cmd = BuildCommand(CMD_OFFSET_LENS_RANGE, nosepiecePos); + std::string response; + int ret = ExecuteCommand(cmd, response); + if (ret != DEVICE_OK) + { + model_.SetLimits(DeviceType_OffsetLens, OFFSET_LENS_MIN_POS, OFFSET_LENS_MAX_POS); + return ret; + } + + ApplyOffsetLensRange(nosepiecePos, ret, response); + return DEVICE_OK; +} + +void EvidentHubWin::QueryOffsetLensRangeAsync(int nosepiecePos) +{ + std::string cmd = BuildCommand(CMD_OFFSET_LENS_RANGE, nosepiecePos); + ExecuteCommandFireAndForget(cmd, + [this, nosepiecePos](int ret, const std::string& response) + { + ApplyOffsetLensRange(nosepiecePos, ret, response); + }); +} + +void EvidentHubWin::ApplyOffsetLensRange(int nosepiecePos, int ret, const std::string& response) +{ + if (ret != DEVICE_OK) + { + model_.SetLimits(DeviceType_OffsetLens, OFFSET_LENS_MIN_POS, OFFSET_LENS_MAX_POS); + return; + } + + std::vector params = ParseParameters(response); + if (params.size() >= 2) + { + long lower = ParseLongParameter(params[0]); + long upper = ParseLongParameter(params[1]); + + if (lower >= OFFSET_LENS_MIN_POS && upper <= OFFSET_LENS_MAX_POS && lower <= upper) + { + model_.SetLimits(DeviceType_OffsetLens, lower, upper); + std::ostringstream msg; + msg << "Offset lens range for nosepiece " << nosepiecePos + << ": " << lower << " to " << upper << " steps"; + LogMessage(msg.str().c_str(), true); + return; + } + + std::ostringstream msg; + msg << "Invalid GABRANGE response for nosepiece " << nosepiecePos + << ": lower=" << lower << " upper=" << upper << "; using defaults"; + LogMessage(msg.str().c_str(), false); + } + else + { + LogMessage("GABRANGE response missing parameters; using defaults", false); + } + + model_.SetLimits(DeviceType_OffsetLens, OFFSET_LENS_MIN_POS, OFFSET_LENS_MAX_POS); +} + int EvidentHubWin::UpdateNosepieceIndicator(int position) { // Check if MCU is present @@ -2173,6 +2265,10 @@ void EvidentHubWin::ProcessNotification(const std::string& message) { model_.SetPosition(DeviceType_Nosepiece, pos); + // Refresh offset lens range for the new objective (async — must not block callback thread) + if (model_.IsDevicePresent(DeviceType_OffsetLens) && pos >= 1 && pos <= NOSEPIECE_MAX_POS) + QueryOffsetLensRangeAsync(pos); + // Note: MCU indicator I1 is updated automatically by SDK when using OBSEQ // Check if we've reached the target position diff --git a/DeviceAdapters/EvidentIX85Win/EvidentHubWin.h b/DeviceAdapters/EvidentIX85Win/EvidentHubWin.h index 6d6cb7973..4f1d70006 100644 --- a/DeviceAdapters/EvidentIX85Win/EvidentHubWin.h +++ b/DeviceAdapters/EvidentIX85Win/EvidentHubWin.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -42,6 +43,7 @@ struct CommandTask { std::string command; std::promise> responsePromise; + std::function completionCallback; // optional fire-and-forget path CommandTask(std::string cmd) : command(std::move(cmd)) {} @@ -84,6 +86,8 @@ class EvidentHubWin : public HubBase // Command execution (thread-safe with worker thread queue) std::future> ExecuteCommandAsync(const std::string& command); int ExecuteCommand(const std::string& command, std::string& response); + void ExecuteCommandFireAndForget(const std::string& command, + std::function callback); int SendCommand(const std::string& command); int GetResponse(std::string& response, long timeoutMs = -1); @@ -149,6 +153,9 @@ class EvidentHubWin : public HubBase int QueryCorrectionCollar(); int QueryManualControl(); int QueryOffsetLens(); + int QueryOffsetLensRange(int nosepiecePos); // blocking — call only from non-callback context + void QueryOffsetLensRangeAsync(int nosepiecePos); // non-blocking — safe from SDK callback thread + void ApplyOffsetLensRange(int nosepiecePos, int ret, const std::string& response); // Manual Control Unit (MCU) helpers int UpdateNosepieceIndicator(int position); diff --git a/DeviceAdapters/EvidentIX85Win/EvidentIX85Win.cpp b/DeviceAdapters/EvidentIX85Win/EvidentIX85Win.cpp index dbcce95e6..575506a47 100644 --- a/DeviceAdapters/EvidentIX85Win/EvidentIX85Win.cpp +++ b/DeviceAdapters/EvidentIX85Win/EvidentIX85Win.cpp @@ -5056,9 +5056,11 @@ int EvidentOffsetLens::SetPositionUm(double pos) // Convert μm to steps long steps = static_cast(pos / stepSizeUm_); - // Clamp to limits - if (steps < OFFSET_LENS_MIN_POS) steps = OFFSET_LENS_MIN_POS; - if (steps > OFFSET_LENS_MAX_POS) steps = OFFSET_LENS_MAX_POS; + // Clamp to hardware-reported limits for the current objective + long minSteps, maxSteps; + hub->GetModel()->GetLimits(EvidentIX85Win::DeviceType_OffsetLens, minSteps, maxSteps); + if (steps < minSteps) steps = minSteps; + if (steps > maxSteps) steps = maxSteps; hub->GetModel()->SetBusy(EvidentIX85Win::DeviceType_OffsetLens, true); @@ -5117,8 +5119,19 @@ int EvidentOffsetLens::SetOrigin() int EvidentOffsetLens::GetLimits(double& lower, double& upper) { - lower = OFFSET_LENS_MIN_POS * stepSizeUm_; - upper = OFFSET_LENS_MAX_POS * stepSizeUm_; + EvidentHubWin* hub = GetHub(); + if (hub) + { + long minSteps, maxSteps; + hub->GetModel()->GetLimits(EvidentIX85Win::DeviceType_OffsetLens, minSteps, maxSteps); + lower = minSteps * stepSizeUm_; + upper = maxSteps * stepSizeUm_; + } + else + { + lower = OFFSET_LENS_MIN_POS * stepSizeUm_; + upper = OFFSET_LENS_MAX_POS * stepSizeUm_; + } return DEVICE_OK; } diff --git a/DeviceAdapters/EvidentIX85Win/EvidentProtocolWin.h b/DeviceAdapters/EvidentIX85Win/EvidentProtocolWin.h index 7d6e300be..846bb99fa 100644 --- a/DeviceAdapters/EvidentIX85Win/EvidentProtocolWin.h +++ b/DeviceAdapters/EvidentIX85Win/EvidentProtocolWin.h @@ -152,7 +152,7 @@ const char* const CMD_OFFSET_LENS_MOVE = "ABM"; const char* const CMD_OFFSET_LENS_STOP = "ABSTP"; const char* const CMD_OFFSET_LENS_POSITION = "ABP"; const char* const CMD_OFFSET_LENS_NOTIFY = "NABP"; -const char* const CMD_OFFSET_LENS_RANGE = "ABRANGE"; +const char* const CMD_OFFSET_LENS_RANGE = "GABRANGE"; const char* const CMD_OFFSET_LENS_LIMIT = "ABLMT"; const char* const CMD_OFFSET_LENS_LOST_MOTION = "ABLM"; const char* const CMD_OFFSET_LENS_BASE_POSITION = "ABBP";