StimBuff: Make virtual so we can dynamic_cast in getOrCreateStimBuff

This commit is contained in:
2025-11-16 02:23:53 -04:00
parent addd2e275d
commit c5ed453bb4
6 changed files with 162 additions and 12 deletions
@@ -29,6 +29,28 @@ std::shared_ptr<StimulusBuffer> StimulusProducer::getAttachedStimulusBuffer(
return nullptr; 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() void StimulusProducer::stop()
{ {
{ {
+1 -1
View File
@@ -39,7 +39,7 @@ public:
ringBufferConstraints) ringBufferConstraints)
{} {}
~StimulusBuffer() = default; virtual ~StimulusBuffer() = default;
// Non-copyable, movable // Non-copyable, movable
StimulusBuffer(const StimulusBuffer&) = delete; StimulusBuffer(const StimulusBuffer&) = delete;
+4 -1
View File
@@ -69,13 +69,16 @@ public:
void allowNextStimulusFrame() void allowNextStimulusFrame()
{ frameAssemblyRateLimiter.release(); } { frameAssemblyRateLimiter.release(); }
std::shared_ptr<StimulusBuffer> getAttachedStimulusBuffer( virtual std::shared_ptr<StimulusBuffer> getAttachedStimulusBuffer(
const std::shared_ptr<device::DeviceAttachmentSpec>& spec) const; const std::shared_ptr<device::DeviceAttachmentSpec>& spec) const;
virtual std::shared_ptr<StimulusBuffer> getOrCreateAttachedStimulusBuffer( virtual std::shared_ptr<StimulusBuffer> getOrCreateAttachedStimulusBuffer(
const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec, const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec,
int histbuffMs) = 0; int histbuffMs) = 0;
// Check if any attached buffer has the specified qualeIfaceApi
bool hasBufferWithQualeIfaceApi(const std::string& qualeIfaceApi) const;
protected: protected:
SpinLock frameAssemblyRateLimiter; SpinLock frameAssemblyRateLimiter;
+61 -2
View File
@@ -30,6 +30,8 @@ static SmoThreadingModelDesc smoThreadingModelDesc;
// Local collection of stimulus producers // Local collection of stimulus producers
static std::vector<std::shared_ptr<StimulusProducer>> attachedStimulusProducers; static std::vector<std::shared_ptr<StimulusProducer>> attachedStimulusProducers;
static bool isSupportedQualeIfaceApi(const std::string& qualeIfaceApi);
// Check if a StimulusProducer matches the requested stim feature // Check if a StimulusProducer matches the requested stim feature
static bool isProducerForStimFeature( static bool isProducerForStimFeature(
const std::shared_ptr<StimulusProducer>& stimProducer, const std::shared_ptr<StimulusProducer>& stimProducer,
@@ -192,6 +194,17 @@ private:
return false; 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 // Parse histbuffMs
int histbuffMs = parseHistbuffMs(context->spec); int histbuffMs = parseHistbuffMs(context->spec);
@@ -203,7 +216,7 @@ private:
std::cerr << __func__ << ": Failed to create StimBuffer: " std::cerr << __func__ << ": Failed to create StimBuffer: "
<< e.what() << ". Producer is committed, DeviceReattacher will retry." << e.what() << ". Producer is committed, DeviceReattacher will retry."
<< std::endl; << std::endl;
// Return false so DeviceReattacher can retry later // Return false so caller can handle error callback
return false; 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 // Callback function implementations
extern "C" int livoxGen1_initializeInd(void) extern "C" int livoxGen1_initializeInd(void)
{ {
@@ -713,6 +733,29 @@ extern "C" void livoxGen1_attachDeviceReq(
"not available"); "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<AttachDeviceReq>(desc, cb); auto request = std::make_shared<AttachDeviceReq>(desc, cb);
// Case 1: Check if StimBuffer already exists // Case 1: Check if StimBuffer already exists
@@ -740,7 +783,23 @@ extern "C" void livoxGen1_attachDeviceReq(
} }
else 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; request->stimProducer = stimProducer;
// Ensure StimBuffer is attached and enable pcloud data if needed // Ensure StimBuffer is attached and enable pcloud data if needed
request->attachDeviceReq4_doCreateStimBuff_maybeDirectlyCalled( request->attachDeviceReq4_doCreateStimBuff_maybeDirectlyCalled(
@@ -106,6 +106,40 @@ void produceStimFrameAck(void)
{ {
} }
std::shared_ptr<StimulusBuffer>
PcloudStimulusProducer::getAttachedStimulusBuffer(
const std::shared_ptr<device::DeviceAttachmentSpec>& 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<MeshStimulusBuffer>(buffer))
{ return buffer; }
}
else if (qualeIfaceApi == "pcloudIntensity")
{
if (std::dynamic_pointer_cast<PcloudIntensityStimulusBuffer>(buffer))
{ return buffer; }
}
else if (qualeIfaceApi == "pcloudAmbience")
{
if (std::dynamic_pointer_cast<PcloudAmbienceStimulusBuffer>(buffer))
{ return buffer; }
}
// Type mismatch - return nullptr
return nullptr;
}
std::shared_ptr<StimulusBuffer> std::shared_ptr<StimulusBuffer>
PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer( PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer(
const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec, const std::shared_ptr<device::DeviceAttachmentSpec>& deviceAttachmentSpec,
@@ -117,15 +151,43 @@ PcloudStimulusProducer::getOrCreateAttachedStimulusBuffer(
if (existingBuffer) if (existingBuffer)
{ return existingBuffer; } { return existingBuffer; }
// Create new MeshStimulusBuffer (for now, always use XYZ type) // Parse qualeIfaceApi to determine buffer type
auto buffer = std::make_shared<MeshStimulusBuffer>( const std::string& qualeIfaceApi = deviceAttachmentSpec->qualeIfaceApi;
*this, deviceAttachmentSpec, histbuffMs, openClInputConstraints);
// Add to collection if (qualeIfaceApi == "mesh")
attachedStimulusBuffers.push_back(buffer); {
// Update specialized member auto meshBuffer = std::make_shared<MeshStimulusBuffer>(
meshStimulusBuffer = buffer; *this, deviceAttachmentSpec, histbuffMs, openClInputConstraints);
return buffer;
meshStimulusBuffer = meshBuffer;
attachedStimulusBuffers.push_back(meshBuffer);
return meshBuffer;
}
else if (qualeIfaceApi == "pcloudIntensity")
{
auto intensityBuffer = std::make_shared<PcloudIntensityStimulusBuffer>(
*this, deviceAttachmentSpec, histbuffMs, openClInputConstraints);
intensityStimulusBuffer = intensityBuffer;
attachedStimulusBuffers.push_back(intensityBuffer);
return intensityBuffer;
}
else if (qualeIfaceApi == "pcloudAmbience")
{
auto ambienceBuffer = std::make_shared<PcloudAmbienceStimulusBuffer>(
*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() void PcloudStimulusProducer::stimFrameProductionTimesliceInd()
@@ -67,6 +67,10 @@ public:
&deviceAttachmentSpec, &deviceAttachmentSpec,
int histbuffMs) override; int histbuffMs) override;
std::shared_ptr<StimulusBuffer> getAttachedStimulusBuffer(
const std::shared_ptr<device::DeviceAttachmentSpec>& spec)
const override;
protected: protected:
void stimFrameProductionTimesliceInd() override; void stimFrameProductionTimesliceInd() override;