use crate::engine::schema_data;
use crate::engine::types::{
BitLookupTable, FieldInfo, FieldRef, IndirectLookupTable, LookupFieldTypeTable,
LookupFieldTypeValue, LookupTable, PgnInfo,
};
pub struct PgnDatabase {
pub schema_version: &'static str,
pub version: &'static str,
pub schema_hash: u64,
units: Units,
protocol: crate::engine::BusProtocol,
pgns: &'static [PgnInfo],
pgn_index: &'static [(u32, &'static [u32])],
lookups: &'static [LookupTable],
bit_lookups: &'static [BitLookupTable],
indirect_lookups: &'static [IndirectLookupTable],
field_type_lookups: &'static [LookupFieldTypeTable],
pub(crate) dispatch: fn(u32, &[u8]) -> Option<usize>,
pub(crate) catchall: fn(u32) -> Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Units {
#[default]
Si,
Metric,
}
macro_rules! embedded_db {
($flavor:ident, $pgns:ident, $ft_lookups:ident, $units:expr, $protocol:expr) => {
PgnDatabase {
schema_version: schema_data::SCHEMA_VERSION,
version: schema_data::VERSION,
schema_hash: schema_data::SCHEMA_HASH,
units: $units,
protocol: $protocol,
pgns: crate::engine::$flavor::$pgns,
pgn_index: crate::engine::$flavor::PGN_INDEX,
lookups: crate::engine::$flavor::LOOKUPS,
bit_lookups: crate::engine::$flavor::BIT_LOOKUPS,
indirect_lookups: crate::engine::$flavor::INDIRECT_LOOKUPS,
field_type_lookups: crate::engine::$flavor::$ft_lookups,
dispatch: crate::engine::$flavor::dispatch,
catchall: crate::engine::$flavor::find_catchall,
}
};
}
static EMBEDDED_SI: PgnDatabase = embedded_db!(
schema_data,
PGNS_SI,
FIELD_TYPE_LOOKUPS_SI,
Units::Si,
crate::engine::BusProtocol::Nmea2000
);
static EMBEDDED_METRIC: PgnDatabase = embedded_db!(
schema_data,
PGNS_METRIC,
FIELD_TYPE_LOOKUPS_METRIC,
Units::Metric,
crate::engine::BusProtocol::Nmea2000
);
static EMBEDDED_J1939_SI: PgnDatabase = embedded_db!(
schema_data_j1939,
PGNS_SI,
FIELD_TYPE_LOOKUPS_SI,
Units::Si,
crate::engine::BusProtocol::J1939
);
static EMBEDDED_J1939_METRIC: PgnDatabase = embedded_db!(
schema_data_j1939,
PGNS_METRIC,
FIELD_TYPE_LOOKUPS_METRIC,
Units::Metric,
crate::engine::BusProtocol::J1939
);
static EMBEDDED_QUICK_SI: PgnDatabase = embedded_db!(
schema_data_quick,
PGNS_SI,
FIELD_TYPE_LOOKUPS_SI,
Units::Si,
crate::engine::BusProtocol::Quick
);
static EMBEDDED_QUICK_METRIC: PgnDatabase = embedded_db!(
schema_data_quick,
PGNS_METRIC,
FIELD_TYPE_LOOKUPS_METRIC,
Units::Metric,
crate::engine::BusProtocol::Quick
);
impl PgnDatabase {
#[inline]
pub fn embedded(units: Units) -> &'static Self {
match units {
Units::Si => &EMBEDDED_SI,
Units::Metric => &EMBEDDED_METRIC,
}
}
#[inline]
pub fn embedded_j1939(units: Units) -> &'static Self {
match units {
Units::Si => &EMBEDDED_J1939_SI,
Units::Metric => &EMBEDDED_J1939_METRIC,
}
}
#[inline]
pub fn embedded_quick(units: Units) -> &'static Self {
match units {
Units::Si => &EMBEDDED_QUICK_SI,
Units::Metric => &EMBEDDED_QUICK_METRIC,
}
}
#[inline]
pub fn units(&self) -> Units {
self.units
}
#[inline]
pub fn is_j1939(&self) -> bool {
self.protocol == crate::engine::BusProtocol::J1939
}
#[inline]
pub fn protocol(&self) -> crate::engine::BusProtocol {
self.protocol
}
pub fn pgn_count(&self) -> usize {
self.pgns.len()
}
pub fn pgns(&self) -> std::slice::Iter<'static, PgnInfo> {
let pgns: &'static [PgnInfo] = self.pgns;
pgns.iter()
}
pub(crate) fn pgn_slice(&self) -> &'static [PgnInfo] {
self.pgns
}
pub fn pgn_variants(&self, pgn: u32) -> PgnVariants {
let index: &'static [(u32, &'static [u32])] = self.pgn_index;
let pgns: &'static [PgnInfo] = self.pgns;
let slice: &'static [u32] = match index.binary_search_by_key(&pgn, |&(p, _)| p) {
Ok(i) => index[i].1,
Err(_) => &[],
};
PgnVariants {
inner: slice.iter(),
pgns,
}
}
pub fn first_pgn(&self, pgn: u32) -> Option<&'static PgnInfo> {
match self.pgn_index.binary_search_by_key(&pgn, |&(p, _)| p) {
Ok(i) => self.pgn_index[i].1.first().map(|&j| &self.pgns[j as usize]),
Err(_) => None,
}
}
pub(crate) fn specific_variants(&self, pgn: u32) -> impl Iterator<Item = &'static PgnInfo> {
self.pgn_variants(pgn).filter(|p| p.fallback != Some(true))
}
pub fn pgn_by_id(&self, id: &str) -> Option<&'static PgnInfo> {
self.pgns.iter().find(|p| p.id == id)
}
pub fn encode(
&'static self,
pgn_id: &str,
) -> Result<crate::engine::encode::PgnBuilder, crate::engine::encode::EncodeError> {
let pgn = self
.pgn_by_id(pgn_id)
.ok_or_else(|| crate::engine::encode::EncodeError::NoSuchPgnId(pgn_id.to_string()))?;
Ok(crate::engine::encode::PgnBuilder::for_pgn(self, pgn))
}
pub fn encode_for(&'static self, pgn: &'static PgnInfo) -> crate::engine::encode::PgnBuilder {
let resolved = self.pgn_by_id(pgn.id).unwrap_or(pgn);
crate::engine::encode::PgnBuilder::for_pgn(self, resolved)
}
pub fn encode_by_pgn(
&'static self,
pgn: u32,
) -> Result<crate::engine::encode::PgnBuilder, crate::engine::encode::EncodeError> {
let mut variants = self.pgn_variants(pgn);
let first = variants
.next()
.ok_or(crate::engine::encode::EncodeError::NoSuchPgn(pgn))?;
let extra = variants.count();
if extra > 0 {
return Err(crate::engine::encode::EncodeError::AmbiguousPgn {
pgn,
variants: extra + 1,
});
}
Ok(crate::engine::encode::PgnBuilder::for_pgn(self, first))
}
pub fn field(&self, pgn_id: &str, field_id: &str) -> Option<FieldRef> {
let info = self.pgn_by_id(pgn_id)?;
let f = info.fields.iter().find(|f| f.id == field_id)?;
Some(FieldRef {
pgn: info,
field: f,
})
}
pub fn fallback_pgn(&self, pgn: u32) -> Option<&'static PgnInfo> {
Some(&self.pgns[(self.catchall)(pgn)?])
}
pub fn lookup(&self, name: &str) -> Option<&'static LookupTable> {
match self.lookups.binary_search_by(|t| t.name.cmp(name)) {
Ok(i) => Some(&self.lookups[i]),
Err(_) => None,
}
}
pub fn bit_lookup(&self, name: &str) -> Option<&'static BitLookupTable> {
match self.bit_lookups.binary_search_by(|t| t.name.cmp(name)) {
Ok(i) => Some(&self.bit_lookups[i]),
Err(_) => None,
}
}
pub fn indirect_lookup(&self, name: &str, value1: u64, value2: u64) -> Option<&'static str> {
let table = match self.indirect_lookups.binary_search_by(|t| t.name.cmp(name)) {
Ok(i) => &self.indirect_lookups[i],
Err(_) => return None,
};
table.get(value1, value2).map(|v| v.name)
}
pub fn field_type_lookup(&self, name: &str, key: u64) -> Option<&'static LookupFieldTypeValue> {
let table = match self
.field_type_lookups
.binary_search_by(|t| t.name.cmp(name))
{
Ok(i) => &self.field_type_lookups[i],
Err(_) => return None,
};
table.get(key)
}
pub fn field_type_lookup_by_name(
&self,
name: &str,
value_name: &str,
) -> Option<&'static LookupFieldTypeValue> {
let table = match self
.field_type_lookups
.binary_search_by(|t| t.name.cmp(name))
{
Ok(i) => &self.field_type_lookups[i],
Err(_) => return None,
};
table.values.iter().find(|v| v.name == value_name)
}
}
pub struct PgnVariants {
inner: std::slice::Iter<'static, u32>,
pgns: &'static [PgnInfo],
}
impl Iterator for PgnVariants {
type Item = &'static PgnInfo;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|&i| &self.pgns[i as usize])
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl ExactSizeIterator for PgnVariants {}
#[allow(dead_code)]
fn _api_anchor(_: &FieldInfo) {}