Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 102 additions & 1 deletion MMCore/MMCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -817,6 +817,14 @@ void CMMCore::unloadDevice(const char* label///< the name of the device to unloa

mm::DeviceModuleLockGuard guard(pDevice);
LOG_DEBUG(coreLogger_) << "Will unload device " << label;

// Clean up initial state labels for this device if it exists
auto it = initialStateLabels_.find(label);
if (it != initialStateLabels_.end()) {
initialStateLabels_.erase(it);
LOG_DEBUG(coreLogger_) << "Cleaned up initial state labels for device " << label;
}

deviceManager_->UnloadDevice(pDevice);
LOG_DEBUG(coreLogger_) << "Did unload device " << label;

Expand Down Expand Up @@ -854,6 +862,10 @@ void CMMCore::unloadAllDevices() MMCORE_LEGACY_THROW(CMMError)
}

LOG_DEBUG(coreLogger_) << "Will unload all devices";

// Clear all initial state labels since all devices are being unloaded
initialStateLabels_.clear();

deviceManager_->UnloadAllDevices();
LOG_INFO(coreLogger_) << "Did unload all devices";

Expand Down Expand Up @@ -955,6 +967,9 @@ void CMMCore::initializeAllDevicesSerial() MMCORE_LEGACY_THROW(CMMError)
pDevice->Initialize();
LOG_INFO(coreLogger_) << "Did initialize device " << devices[i];

// Capture initial state labels for State devices
captureInitialStateLabels(pDevice, devices[i].c_str());

assignDefaultRole(pDevice);
}

Expand Down Expand Up @@ -1064,12 +1079,16 @@ void CMMCore::initializeAllDevicesParallel() MMCORE_LEGACY_THROW(CMMError)
* This helper function is executed by a single thread, allowing initializeAllDevices to operate multi-threaded.
* All devices are supposed to originate from the same device adapter
*/

int CMMCore::initializeVectorOfDevices(std::vector<std::pair<std::shared_ptr<DeviceInstance>, std::string>> devicesLabels) {
for (auto& deviceLabel : devicesLabels) {
mm::DeviceModuleLockGuard guard(deviceLabel.first);
LOG_INFO(coreLogger_) << "Will initialize device " << deviceLabel.second;
deviceLabel.first->Initialize();
LOG_INFO(coreLogger_) << "Did initialize device " << deviceLabel.second;

// Capture initial state labels for State devices
captureInitialStateLabels(deviceLabel.first, deviceLabel.second.c_str());
}
return DEVICE_OK;
}
Expand Down Expand Up @@ -1122,10 +1141,66 @@ void CMMCore::initializeDevice(const char* label ///< the device to initialize
pDevice->Initialize();
LOG_INFO(coreLogger_) << "Did initialize device " << label;

// Capture initial state labels for State devices
captureInitialStateLabels(pDevice, label);

updateCoreProperties();
}


/**
* Helper function to capture initial state labels for State devices after initialization.
* This function should be called after any device initialization to ensure state labels are captured.
*
* @param pDevice the initialized device instance
* @param label the device label
*/
void CMMCore::captureInitialStateLabels(std::shared_ptr<DeviceInstance> pDevice, const char* label)
{
// For State devices, capture initial position labels after initialization
if (pDevice->GetType() == MM::StateDevice)
{
try
{

std::shared_ptr<StateInstance> pStateDev =
deviceManager_->GetDeviceOfType<StateInstance>(label);

unsigned long numPositions = pStateDev->GetNumberOfPositions();
std::map<long, std::string> initialLabels;

for (unsigned long pos = 0; pos < numPositions; pos++)
{
try
{
std::string posLabel = pStateDev->GetPositionLabel(pos);
if (!posLabel.empty())
{
initialLabels[pos] = posLabel;
}
}
catch (const CMMError&)
{
// Label not defined for this position, skip
}
}

// Store the initial labels for this device
initialStateLabels_[label] = initialLabels;

LOG_DEBUG(coreLogger_) << "Captured " << initialLabels.size()
<< " initial state labels for device " << label;
}
catch (const CMMError& e)
{
// Log but don't fail initialization if we can't capture labels
LOG_WARNING(coreLogger_) << "Failed to capture initial state labels for device "
<< label << ": " << e.getMsg();
}
}
}


/**
* Queries the initialization state of the given device.
*
Expand Down Expand Up @@ -7118,6 +7193,8 @@ void CMMCore::loadSystemState(const char* fileName) MMCORE_LEGACY_THROW(CMMError
* Saves the current system configuration to a text file of the MM specific format.
* The configuration file records only the information essential to the hardware
* setup: devices, labels, pre-initialization properties, and configurations.
* For state devices, only position labels that have been modified from their
* initial values (captured during device initialization) are saved.
* The file format is the same as for the system state.
*/
void CMMCore::saveSystemConfiguration(const char* fileName) MMCORE_LEGACY_THROW(CMMError)
Expand Down Expand Up @@ -7224,6 +7301,11 @@ void CMMCore::saveSystemConfiguration(const char* fileName) MMCORE_LEGACY_THROW(
deviceManager_->GetDeviceOfType<StateInstance>(deviceLabels[i]);
mm::DeviceModuleLockGuard guard(pSD);
unsigned numPos = pSD->GetNumberOfPositions();

// Check if we have initial labels stored for this device
auto initialLabelsIt = initialStateLabels_.find(deviceLabels[i]);
bool hasInitialLabels = (initialLabelsIt != initialStateLabels_.end());

for (unsigned long j=0; j<numPos; j++)
{
std::string stateLabel;
Expand All @@ -7238,7 +7320,26 @@ void CMMCore::saveSystemConfiguration(const char* fileName) MMCORE_LEGACY_THROW(
}
if (!stateLabel.empty())
{
os << MM::g_CFGCommand_Label << ',' << deviceLabels[i] << ',' << j << ',' << stateLabel << '\n';
bool shouldSaveLabel = true;

// If we have initial labels, only save if the current label is different
if (hasInitialLabels)
{
auto& initialLabels = initialLabelsIt->second;
auto initialLabelIt = initialLabels.find(j);

if (initialLabelIt != initialLabels.end() &&
initialLabelIt->second == stateLabel)
{
// Current label matches initial label, don't save it
shouldSaveLabel = false;
}
}

if (shouldSaveLabel)
{
os << MM::g_CFGCommand_Label << ',' << deviceLabels[i] << ',' << j << ',' << stateLabel << '\n';
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions MMCore/MMCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,11 @@ class CMMCore
mutable MMThreadLock stateCacheLock_;
mutable Configuration stateCache_; // Synchronized by stateCacheLock_

// Storage for initial state labels after device initialization
std::map<std::string, std::map<long, std::string>> initialStateLabels_;

MMThreadLock* pPostedErrorsLock_;
mutable std::deque<std::pair< int, std::string> > postedErrors_;
// True while interpreting the config file (but not while rolling back on
// failure):
bool isLoadingSystemConfiguration_ = false;
Expand Down Expand Up @@ -759,4 +764,5 @@ class CMMCore
void initializeAllDevicesSerial() MMCORE_LEGACY_THROW(CMMError);
void initializeAllDevicesParallel() MMCORE_LEGACY_THROW(CMMError);
int initializeVectorOfDevices(std::vector<std::pair<std::shared_ptr<DeviceInstance>, std::string> > pDevices);
void captureInitialStateLabels(std::shared_ptr<DeviceInstance> pDevice, const char* label);
};