lcameraDev: Add session mgr lib for libcamera device binding

This commit is contained in:
2026-06-13 12:02:04 -04:00
parent cc7f4fcd9b
commit 46f767f232
21 changed files with 1363 additions and 64 deletions
+171
View File
@@ -0,0 +1,171 @@
#include <selectorResolve.h>
#include <algorithm>
#include <cctype>
#include <optional>
#include <sstream>
#include <stdexcept>
namespace lcamera_dev {
namespace {
std::string toLowerAscii(const std::string& text)
{
std::string lowered = text;
for (char& ch : lowered) {
ch = static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
}
return lowered;
}
bool recordMatchesCriterion(
const CameraIdentityRecord& record, const SelectorCriterion& criterion)
{
switch (criterion.kind)
{
case SelectorCriterionKind::LibcameraId:
return record.id == criterion.value;
case SelectorCriterionKind::Index:
return false;
case SelectorCriterionKind::Model:
return record.model == criterion.value;
case SelectorCriterionKind::ModelSubstr:
return record.model.find(criterion.value) != std::string::npos;
case SelectorCriterionKind::Location:
return toLowerAscii(record.locationLabel)
== toLowerAscii(criterion.value);
}
return false;
}
int parseIndexCriterion(const SelectorCriterion& criterion)
{
try {
return std::stoi(criterion.value);
}
catch (const std::exception&) {
throw std::runtime_error("Invalid index: value in deviceSelector");
}
}
} // namespace
std::string formatCameraListForDiagnostics(
const std::vector<CameraIdentityRecord>& records)
{
std::ostringstream result;
result << "Known cameras:\n";
for (size_t i = 0; i < records.size(); ++i)
{
const CameraIdentityRecord& record = records[i];
result << " [" << i << "] id=" << record.id;
if (!record.model.empty()) {
result << " model=" << record.model;
}
if (!record.locationLabel.empty()) {
result << " location=" << record.locationLabel;
}
result << '\n';
}
return result.str();
}
CameraIdentityRecord resolveSelectorAgainstRecords(
const std::vector<SelectorCriterion>& criteria,
const std::vector<CameraIdentityRecord>& records)
{
std::optional<int> indexCriterion;
for (const SelectorCriterion& criterion : criteria)
{
if (criterion.kind != SelectorCriterionKind::Index) {
continue;
}
const int index = parseIndexCriterion(criterion);
if (index < 0
|| static_cast<size_t>(index) >= records.size())
{
throw std::runtime_error("index: selector out of range");
}
indexCriterion = index;
}
std::vector<const CameraIdentityRecord*> matches;
matches.reserve(records.size());
for (const CameraIdentityRecord& record : records)
{
bool matchesAll = true;
for (const SelectorCriterion& criterion : criteria)
{
if (criterion.kind == SelectorCriterionKind::Index) {
continue;
}
if (!recordMatchesCriterion(record, criterion))
{
matchesAll = false;
break;
}
}
if (!matchesAll) {
continue;
}
matches.push_back(&record);
}
if (indexCriterion.has_value())
{
const CameraIdentityRecord& indexedRecord =
records.at(static_cast<size_t>(*indexCriterion));
auto it = std::find_if(
matches.begin(), matches.end(),
[&indexedRecord](const CameraIdentityRecord* candidate) {
return candidate->id == indexedRecord.id;
});
if (it == matches.end())
{
throw std::runtime_error(
"index: criterion conflicts with other selector clauses");
}
if (matches.size() > 1)
{
throw std::runtime_error(
"Ambiguous deviceSelector: multiple cameras match\n"
+ formatCameraListForDiagnostics(records));
}
return indexedRecord;
}
if (matches.empty())
{
throw std::runtime_error(
"No camera matches deviceSelector\n"
+ formatCameraListForDiagnostics(records));
}
if (matches.size() > 1)
{
throw std::runtime_error(
"Ambiguous deviceSelector: multiple cameras match\n"
+ formatCameraListForDiagnostics(records));
}
return *matches.front();
}
} // namespace lcamera_dev