diff --git a/commonLibs/attachmentSupport/stimulusProducer.cpp b/commonLibs/attachmentSupport/stimulusProducer.cpp index fb5d959..530e0c7 100644 --- a/commonLibs/attachmentSupport/stimulusProducer.cpp +++ b/commonLibs/attachmentSupport/stimulusProducer.cpp @@ -29,6 +29,28 @@ std::shared_ptr StimulusProducer::getAttachedStimulusBuffer( return nullptr; } +bool StimulusProducer::hasBufferWithQualeIfaceApi( + const std::string& qualeIfaceApi) const +{ + for (const auto& buffer : attachedStimulusBuffers) + { + if (!buffer || !buffer->deviceAttachmentSpec) + { + throw std::runtime_error( + "StimulusProducer::hasBufferWithQualeIfaceApi: encountered " + "null buffer or null deviceAttachmentSpec in " + "attachedStimulusBuffers (should never happen)"); + } + + if (buffer->deviceAttachmentSpec->qualeIfaceApi != qualeIfaceApi) + { continue; } + + return true; + } + + return false; +} + void StimulusProducer::stop() { { diff --git a/include/user/stimulusBuffer.h b/include/user/stimulusBuffer.h index c821b5d..d8639e4 100644 --- a/include/user/stimulusBuffer.h +++ b/include/user/stimulusBuffer.h @@ -39,7 +39,7 @@ public: ringBufferConstraints) {} - ~StimulusBuffer() = default; + virtual ~StimulusBuffer() = default; // Non-copyable, movable StimulusBuffer(const StimulusBuffer&) = delete; diff --git a/include/user/stimulusProducer.h b/include/user/stimulusProducer.h index 0fff2f0..b503d84 100644 --- a/include/user/stimulusProducer.h +++ b/include/user/stimulusProducer.h @@ -69,13 +69,16 @@ public: void allowNextStimulusFrame() { frameAssemblyRateLimiter.release(); } - std::shared_ptr getAttachedStimulusBuffer( + virtual std::shared_ptr getAttachedStimulusBuffer( const std::shared_ptr& spec) const; virtual std::shared_ptr getOrCreateAttachedStimulusBuffer( const std::shared_ptr& deviceAttachmentSpec, int histbuffMs) = 0; + // Check if any attached buffer has the specified qualeIfaceApi + bool hasBufferWithQualeIfaceApi(const std::string& qualeIfaceApi) const; + protected: SpinLock frameAssemblyRateLimiter; diff --git a/stimBuffApis/livoxGen1/livoxGen1.cpp b/stimBuffApis/livoxGen1/livoxGen1.cpp index 862fbee..8e4d86d 100644 --- a/stimBuffApis/livoxGen1/livoxGen1.cpp +++ b/stimBuffApis/livoxGen1/livoxGen1.cpp @@ -30,6 +30,8 @@ static SmoThreadingModelDesc smoThreadingModelDesc; // Local collection of stimulus producers static std::vector> attachedStimulusProducers; +static bool isSupportedQualeIfaceApi(const std::string& qualeIfaceApi); + // Check if a StimulusProducer matches the requested stim feature static bool isProducerForStimFeature( const std::shared_ptr& stimProducer, @@ -192,6 +194,17 @@ private: return false; } + // Check for duplicate qualeIfaceApi + const std::string& qualeIfaceApi = context->spec->qualeIfaceApi; + if (context->stimProducer->hasBufferWithQualeIfaceApi(qualeIfaceApi)) + { + std::cerr << __func__ << ": Buffer with qualeIfaceApi '" + << qualeIfaceApi << "' already exists for this producer. " + "Each producer can only have one buffer per qualeIfaceApi." + << std::endl; + return false; + } + // Parse histbuffMs int histbuffMs = parseHistbuffMs(context->spec); @@ -203,7 +216,7 @@ private: std::cerr << __func__ << ": Failed to create StimBuffer: " << e.what() << ". Producer is committed, DeviceReattacher will retry." << std::endl; - // Return false so DeviceReattacher can retry later + // Return false so caller can handle error callback return false; } @@ -604,6 +617,13 @@ static const StimBuffApiDesc livoxGen1ApiDesc = { } }; +static bool isSupportedQualeIfaceApi(const std::string& qualeIfaceApi) +{ + // Check if this is a supported (implemented) qualeIfaceApi + return qualeIfaceApi == "mesh" || qualeIfaceApi == "pcloudIntensity" || + qualeIfaceApi == "pcloudAmbience"; +} + // Callback function implementations extern "C" int livoxGen1_initializeInd(void) { @@ -713,6 +733,29 @@ extern "C" void livoxGen1_attachDeviceReq( "not available"); } + // Validate qualeIfaceApi + const std::string& qualeIfaceApi = desc->qualeIfaceApi; + if (qualeIfaceApi == "gyro" || qualeIfaceApi == "accel") + { + // These are for ImuStimulusProducer (not yet implemented) + std::cerr << __func__ << ": qualeIfaceApi '" << qualeIfaceApi + << "' requires ImuStimulusProducer which is not yet implemented" + << std::endl; + cb.callbackFn(false, desc); + return; + } + + if (!isSupportedQualeIfaceApi(qualeIfaceApi)) + { + // Unknown qualeIfaceApi + std::cerr << __func__ << ": Unsupported qualeIfaceApi '" + << qualeIfaceApi << "' for LivoxGen1. " + "Supported values: mesh, pcloudIntensity, pcloudAmbience" + << std::endl; + cb.callbackFn(false, desc); + return; + } + auto request = std::make_shared(desc, cb); // Case 1: Check if StimBuffer already exists @@ -740,7 +783,23 @@ extern "C" void livoxGen1_attachDeviceReq( } else { - // StimProducer exists, StimBuffer doesn't + /** EXPLANATION: + * StimProducer exists, StimBuffer doesn't (DASpec doesn't match) + * Check if producer already has a buffer with the requested + * qualeIfaceApi but different DASpec - this is not allowed. + */ + if (stimProducer->hasBufferWithQualeIfaceApi(desc->qualeIfaceApi)) + { + std::cerr << __func__ << ": Producer already has a buffer with " + "qualeIfaceApi '" << desc->qualeIfaceApi + << "' but with a different DeviceAttachmentSpec. " + "A single LivoxGen1 device cannot support multiple DASpecs " + "with the same qualeIfaceApi." << std::endl; + + cb.callbackFn(false, desc); + return; + } + request->stimProducer = stimProducer; // Ensure StimBuffer is attached and enable pcloud data if needed request->attachDeviceReq4_doCreateStimBuff_maybeDirectlyCalled( diff --git a/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp b/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp index ad0bc78..e09baa5 100644 --- a/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp +++ b/stimBuffApis/livoxGen1/pcloudStimulusProducer.cpp @@ -106,6 +106,40 @@ void produceStimFrameAck(void) { } +std::shared_ptr +PcloudStimulusProducer::getAttachedStimulusBuffer( + const std::shared_ptr& spec) const +{ + // Call base class implementation + auto buffer = StimulusProducer::getAttachedStimulusBuffer(spec); + if (!buffer) + { + return nullptr; + } + + // Optionally validate/upcast the buffer type matches expected type + // based on qualeIfaceApi (for type safety) + const std::string& qualeIfaceApi = spec->qualeIfaceApi; + if (qualeIfaceApi == "mesh") + { + if (std::dynamic_pointer_cast(buffer)) + { return buffer; } + } + else if (qualeIfaceApi == "pcloudIntensity") + { + if (std::dynamic_pointer_cast(buffer)) + { return buffer; } + } + else if (qualeIfaceApi == "pcloudAmbience") + { + if (std::dynamic_pointer_cast(buffer)) + { return buffer; } + } + + // Type mismatch - return nullptr + return nullptr; +} + std::shared_ptr PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer( const std::shared_ptr& deviceAttachmentSpec, @@ -117,15 +151,43 @@ PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer( if (existingBuffer) { return existingBuffer; } - // Create new MeshStimulusBuffer (for now, always use XYZ type) - auto buffer = std::make_shared( - *this, deviceAttachmentSpec, histbuffMs, openClInputConstraints); + // Parse qualeIfaceApi to determine buffer type + const std::string& qualeIfaceApi = deviceAttachmentSpec->qualeIfaceApi; - // Add to collection - attachedStimulusBuffers.push_back(buffer); - // Update specialized member - meshStimulusBuffer = buffer; - return buffer; + if (qualeIfaceApi == "mesh") + { + auto meshBuffer = std::make_shared( + *this, deviceAttachmentSpec, histbuffMs, openClInputConstraints); + + meshStimulusBuffer = meshBuffer; + attachedStimulusBuffers.push_back(meshBuffer); + return meshBuffer; + } + else if (qualeIfaceApi == "pcloudIntensity") + { + auto intensityBuffer = std::make_shared( + *this, deviceAttachmentSpec, histbuffMs, openClInputConstraints); + + intensityStimulusBuffer = intensityBuffer; + attachedStimulusBuffers.push_back(intensityBuffer); + return intensityBuffer; + } + else if (qualeIfaceApi == "pcloudAmbience") + { + auto ambienceBuffer = std::make_shared( + *this, deviceAttachmentSpec, histbuffMs, openClInputConstraints); + + ambienceStimulusBuffer = ambienceBuffer; + attachedStimulusBuffers.push_back(ambienceBuffer); + return ambienceBuffer; + } + else + { + throw std::runtime_error( + "Unsupported qualeIfaceApi: '" + qualeIfaceApi + "' for " + "PcloudStimulusProducer. " + "Supported values: mesh, pcloudIntensity, pcloudAmbience"); + } } void PcloudStimulusProducer::stimFrameProductionTimesliceInd() diff --git a/stimBuffApis/livoxGen1/pcloudStimulusProducer.h b/stimBuffApis/livoxGen1/pcloudStimulusProducer.h index 2504cab..161432b 100644 --- a/stimBuffApis/livoxGen1/pcloudStimulusProducer.h +++ b/stimBuffApis/livoxGen1/pcloudStimulusProducer.h @@ -67,6 +67,10 @@ public: &deviceAttachmentSpec, int histbuffMs) override; + std::shared_ptr getAttachedStimulusBuffer( + const std::shared_ptr& spec) + const override; + protected: void stimFrameProductionTimesliceInd() override;