#ifndef SENSORDEVICESPEC_H #define SENSORDEVICESPEC_H #include #include #include #include #include #include #include namespace smo { namespace device { /* Carrier used by the DAP spec parser to pass one parenthesized segment * (e.g. postrin(interest-pc=85) or pcloudLightAmbience(...)) up the reduction * stack; the spec_body reduction classifies segments and populates the * DeviceAttachmentSpec. Defined here so both the parser header and consumers * of the generated header see the type. */ struct DapSegment { std::string name; std::vector> params; }; class DeviceAttachmentSpec { public: friend std::ostream& operator<<( std::ostream& os, const DeviceAttachmentSpec& spec) { os << spec.stringify(); return os; } bool operator==(const DeviceAttachmentSpec& other) const { return deviceIdentifier == other.deviceIdentifier && sensorType == other.sensorType && postrin == other.postrin && negtrin == other.negtrin && qualeIfaceApi == other.qualeIfaceApi && stimBuffApi == other.stimBuffApi && provider == other.provider && deviceSelector == other.deviceSelector; } public: std::string deviceIdentifier; char sensorType; /* postrin/negtrin hold the literal segment name ("postrin" / * "negtrin") when present, empty string when the DAP spec omits the * corresponding intrin specifier. Params vectors carry the params from * within the postrin(...)/negtrin(...) segment. */ std::string postrin; std::vector> postrinParams; std::string negtrin; std::vector> negtrinParams; std::string qualeIfaceApi; std::vector> qualeIfaceApiParams; std::string stimBuffApi; std::vector> stimBuffApiParams; std::string provider; std::vector> providerParams; std::string deviceSelector; static void stringifyParams( std::ostream& os, const std::vector>& params) { for (const auto& param : params) { os << param.first; if (!param.second.empty()) { os << "=" << param.second; } os << " "; } } std::string stringify() const { std::ostringstream os; os << "Device Identifier: " << deviceIdentifier << ", Sensor Type: " << sensorType; if (!postrin.empty()) { os << ", Postrin Params: ("; stringifyParams(os, postrinParams); os << ")"; } if (!negtrin.empty()) { os << ", Negtrin Params: ("; stringifyParams(os, negtrinParams); os << ")"; } os << ", QualeIface API: " << qualeIfaceApi << ", QualeIface API Params: ("; for (const auto& param : qualeIfaceApiParams) { os << param.first; if (!param.second.empty()) { os << "=" << param.second; } os << " "; } os << "), StimBuff API: " << stimBuffApi << ", StimBuff API Params: ("; for (const auto& param : stimBuffApiParams) { os << param.first; if (!param.second.empty()) { os << "=" << param.second; } os << " "; } os << "), Provider: " << provider << ", Provider Params: ("; for (const auto& param : providerParams) { os << param.first; if (!param.second.empty()) { os << "=" << param.second; } os << " "; } os << "), Device Selector: " << deviceSelector; return os.str(); } /** * @brief Parse a required integer parameter from a parameter list * @param params The parameter vector to search in * @param paramName The name of the parameter to parse * @return The parsed integer value * @throws std::runtime_error if parameter is not found or cannot be parsed */ static int parseRequiredParamAsInt( const std::vector>& params, const std::string& paramName ) { auto it = std::find_if( params.begin(), params.end(), [¶mName](const auto& param) { return param.first == paramName; } ); if (it == params.end()) { throw std::runtime_error( "No " + paramName + " specified in params"); } try { return std::stoi(it->second); } catch (const std::exception& e) { throw std::runtime_error( "Failed to parse '" + paramName + "' param value '" + it->second + "' as integer: " + e.what()); } } /** * @brief Parse an optional integer parameter from a parameter list * @param params The parameter vector to search in * @param paramName The name of the parameter to parse * @param defaultValue The default value to return if no parameter is found * @return The parsed integer value, or defaultValue if none found * @note The lattermost supplied matching param wins if multiple are present */ static int parseOptionalParamAsInt( const std::vector>& params, const std::string& paramName, int defaultValue ) { const std::string paramNames[] = {paramName}; return parseOptionalParamAsIntWithSynonyms( params, paramNames, defaultValue); } /** * @brief Parse an optional integer parameter from a parameter list using synonyms * @param params The parameter vector to search in * @param synonymNames The collection of synonymous parameter names to try * @param defaultValue The default value to return if no parameter is found * @return The parsed integer value, or defaultValue if none found * @note The lattermost supplied matching param wins if multiple are present */ template static int parseOptionalParamAsIntWithSynonyms( const std::vector>& params, const SynonymCollectionT& synonymNames, int defaultValue ) { // Loop through params in reverse order; lattermost supplied param wins. for (auto paramIt = params.rbegin(); paramIt != params.rend(); ++paramIt) { const auto& [paramName, paramValue] = *paramIt; auto synonymIt = std::find( std::begin(synonymNames), std::end(synonymNames), paramName); if (synonymIt == std::end(synonymNames)) { continue; } try { return std::stoi(paramValue); } catch (const std::exception& e) { throw std::runtime_error( "Failed to parse '" + paramName + "' param value '" + paramValue + "' as integer: " + e.what()); } } return defaultValue; } }; class InteroceptorDevAttachmentSpec : public DeviceAttachmentSpec { }; class ExtrospectorDevAttachmentSpec : public DeviceAttachmentSpec { }; } // namespace device } // namespace smo #endif // SENSORDEVICESPEC_H