Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,6 @@ test-reports/
*.pb.cc
*.pb.h
bin

# AI
tmpclaude*
22 changes: 22 additions & 0 deletions DeviceAdapters/NIDAQ/NIAnalogOutputPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,28 @@ NIAnalogOutputPort::~NIAnalogOutputPort()


int NIAnalogOutputPort::Initialize()
{
// See NIDAQHub::Initialize() for why this wrapper exists.
try
{
return InitializeImpl();
}
catch (const std::exception& e)
{
LogMessage(std::string("EXCEPTION in NIAnalogOutputPort::Initialize (port ") +
niPort_ + "): " + typeid(e).name() + ": " + e.what());
return DEVICE_ERR;
}
catch (...)
{
LogMessage("Unknown (non-standard) C++ exception in "
"NIAnalogOutputPort::Initialize (port " + niPort_ + ")");
return DEVICE_ERR;
}
}


int NIAnalogOutputPort::InitializeImpl()
{
if (initialized_)
return DEVICE_OK;
Expand Down
84 changes: 63 additions & 21 deletions DeviceAdapters/NIDAQ/NIDAQ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,33 +67,52 @@ MODULE_API void InitializeModuleData()
}


// Returns true if name starts with prefix, and if so stores the remainder of
// name (the part after the prefix) in suffix.
//
// Using std::string::substr(pos) directly for this throws std::out_of_range
// when pos > size(). Such an exception escaping CreateDevice() propagates
// through MMCore and the JNI boundary and terminates the JVM, so avoid it.
static bool SplitDeviceNamePrefix(const char* name, const char* prefix,
std::string& suffix)
{
const size_t prefixLen = strlen(prefix);
const std::string nameStr(name);
if (nameStr.size() < prefixLen)
return false;
if (nameStr.compare(0, prefixLen, prefix) != 0)
return false;
suffix = nameStr.substr(prefixLen);
return true;
}


MODULE_API MM::Device* CreateDevice(const char* deviceName)
{
if (deviceName == 0)
return 0;

if (strcmp(deviceName, g_DeviceNameNIDAQHub) == 0)
{
return new NIDAQHub;
}
else if (std::string(deviceName).
substr(0, strlen(g_DeviceNameNIDAQAOPortPrefix)) ==
g_DeviceNameNIDAQAOPortPrefix)
try
{
return new NIAnalogOutputPort(std::string(deviceName).
substr(strlen(g_DeviceNameNIDAQAOPortPrefix)));
}
else if (std::string(deviceName).substr(0, strlen(g_DeviceNameNIDAQDOPortPrefix)) ==
g_DeviceNameNIDAQDOPortPrefix)
{
return new DigitalOutputPort(std::string(deviceName).
substr(strlen(g_DeviceNameNIDAQDOPortPrefix)));
}
else if (std::string(deviceName).substr(0, strlen(g_DeviceNameNIDAQAIPortPrefix)) ==
g_DeviceNameNIDAQAIPortPrefix)
{
return new NIAnalogInputPort(std::string(deviceName).
substr(strlen(g_DeviceNameNIDAQAIPortPrefix)));
if (strcmp(deviceName, g_DeviceNameNIDAQHub) == 0)
{
return new NIDAQHub;
}

std::string port;
if (SplitDeviceNamePrefix(deviceName, g_DeviceNameNIDAQAOPortPrefix, port))
return new NIAnalogOutputPort(port);
if (SplitDeviceNamePrefix(deviceName, g_DeviceNameNIDAQDOPortPrefix, port))
return new DigitalOutputPort(port);
if (SplitDeviceNamePrefix(deviceName, g_DeviceNameNIDAQAIPortPrefix, port))
return new NIAnalogInputPort(port);
}
catch (const std::exception&)
{
// No device object exists yet, so LogMessage() is not available here.
// Returning 0 makes MMCore report a normal "failed to instantiate
// device" error rather than letting the exception kill the process.
return 0;
}

return 0;
Expand Down Expand Up @@ -176,6 +195,29 @@ NIDAQHub::~NIDAQHub()


int NIDAQHub::Initialize()
{
// Wrapper: a C++ exception escaping Initialize() propagates through MMCore
// and the JNI boundary and terminates the JVM with no usable diagnostic.
// Catch it here, log what it was, and report a normal device error instead.
try
{
return InitializeImpl();
}
catch (const std::exception& e)
{
LogMessage(std::string("EXCEPTION in NIDAQHub::Initialize: ") +
typeid(e).name() + ": " + e.what());
return DEVICE_ERR;
}
catch (...)
{
LogMessage("Unknown (non-standard) C++ exception in NIDAQHub::Initialize");
return DEVICE_ERR;
}
}


int NIDAQHub::InitializeImpl()
{
if (initialized_)
return DEVICE_OK;
Expand Down
6 changes: 6 additions & 0 deletions DeviceAdapters/NIDAQ/NIDAQ.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include <boost/utility.hpp>

#include <string>
#include <typeinfo>
#include <vector>


Expand Down Expand Up @@ -219,7 +220,10 @@ class NIDAQHub : public HubBase<NIDAQHub>,
NIDAQHub();
virtual ~NIDAQHub();

// Initialize() is a thin exception-catching wrapper around InitializeImpl(),
// which holds the actual initialization logic.
virtual int Initialize();
int InitializeImpl();
virtual int Shutdown();

virtual void GetName(char* name) const;
Expand Down Expand Up @@ -347,7 +351,9 @@ class NIAnalogOutputPort : public CSignalIOBase<NIAnalogOutputPort>,
NIAnalogOutputPort(const std::string& port);
virtual ~NIAnalogOutputPort();

// See NIDAQHub::Initialize().
virtual int Initialize();
int InitializeImpl();
virtual int Shutdown();

virtual void GetName(char* name) const;
Expand Down
67 changes: 38 additions & 29 deletions DeviceAdapters/NIDAQ/NIDigitalOutputPort.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@
const int NO_INPUT_LINE = -1;


// Parse one sequence element into an unsigned long, rejecting anything that is
// not a complete valid number in [0, maxValue].
//
// std::stoul throws std::invalid_argument for non-numeric input and
// std::out_of_range on overflow. Such an exception escaping a device adapter
// propagates through MMCore and the JNI boundary and terminates the JVM, so it
// must be caught here and reported as an ordinary device error instead.
static bool ParseSequenceValue(const std::string& s, unsigned long maxValue,
unsigned long& result)
{
try
{
size_t pos = 0;
const unsigned long num = std::stoul(s, &pos, 0);
if (pos != s.size() || num > maxValue)
return false;
result = num;
return true;
}
catch (const std::exception&)
{
return false;
}
}


DigitalOutputPort::DigitalOutputPort(const std::string& port) :
ErrorTranslator(21000, 21999, &DigitalOutputPort::SetErrorText),
niPort_(port),
Expand Down Expand Up @@ -295,16 +321,10 @@ int DigitalOutputPort::OnState(MM::PropertyBase* pProp, MM::ActionType eAct)
sequence8_.clear();
for (unsigned int i = 0; i < sequence.size(); i++)
{
size_t pos;
unsigned long num = std::stoul(sequence[i], &pos, 0);

// Check if the entire string was used for conversion and if the number fits within uint8_t range
if (pos != sequence[i].size() || num > 255) {
// "Value out of range for uint8_t"
return ERR_SEQUENCE_INVALID_NUMBER;
}
uint8_t val = static_cast<uint8_t>(num);
sequence8_.push_back(val);
unsigned long num;
if (!ParseSequenceValue(sequence[i], 255, num))
return ERR_SEQUENCE_INVALID_NUMBER;
sequence8_.push_back(static_cast<uint8_t>(num));
}
GetHub()->getDOHub8()->RemoveDOPortFromSequencing(niPort_);
return GetHub()->getDOHub8()->AddDOPortToSequencing(niPort_, sequence8_);
Expand All @@ -314,16 +334,10 @@ int DigitalOutputPort::OnState(MM::PropertyBase* pProp, MM::ActionType eAct)
sequence16_.clear();
for (unsigned int i = 0; i < sequence.size(); i++)
{
size_t pos;
unsigned long num = std::stoul(sequence[i], &pos, 0);

// Check if the entire string was used for conversion and if the number fits within uint16_t range
if (pos != sequence[i].size() || num > 65535) {
// "Value out of range for uint16_t"
return ERR_SEQUENCE_INVALID_NUMBER;
}
uint16_t val = static_cast<uint16_t>(num);
sequence16_.push_back(val);
unsigned long num;
if (!ParseSequenceValue(sequence[i], 65535, num))
return ERR_SEQUENCE_INVALID_NUMBER;
sequence16_.push_back(static_cast<uint16_t>(num));
}
GetHub()->getDOHub16()->RemoveDOPortFromSequencing(niPort_);
return GetHub()->getDOHub16()->AddDOPortToSequencing(niPort_, sequence16_);
Expand All @@ -333,15 +347,10 @@ int DigitalOutputPort::OnState(MM::PropertyBase* pProp, MM::ActionType eAct)
sequence32_.clear();
for (unsigned int i = 0; i < sequence.size(); i++)
{
size_t pos;
unsigned long num = std::stoul(sequence[i], &pos, 0);

// Check if the entire string was used for conversion
if (pos != sequence[i].size()) {
return ERR_SEQUENCE_INVALID_NUMBER;
}
uint32_t val = static_cast<uint32_t>(num);
sequence32_.push_back(val);
unsigned long num;
if (!ParseSequenceValue(sequence[i], 0xFFFFFFFFUL, num))
return ERR_SEQUENCE_INVALID_NUMBER;
sequence32_.push_back(static_cast<uint32_t>(num));
}
GetHub()->getDOHub32()->RemoveDOPortFromSequencing(niPort_);
return GetHub()->getDOHub32()->AddDOPortToSequencing(niPort_, sequence32_);
Expand Down