///|
pub(all) struct GsvAssembly {
talker_value : String
signal_id_value : String?
total_messages_value : Int
total_satellites_value : Int
observations_value : Array[SatelliteObservation]
source_record_indexes_value : Array[Int]
} derive(Eq, Debug)
///|
struct PendingGsvAssembly {
talker_value : String
signal_id_value : String?
total_messages_value : Int
mut next_message_value : Int
total_satellites_value : Int
observations_value : Array[SatelliteObservation]
source_record_indexes_value : Array[Int]
}
///|
pub struct GsvAssembler {
pending_value : Array[PendingGsvAssembly]
}
///|
fn gsv_assembly_error(record_index : Int, message : String) -> NmeaError {
NmeaError::from_diagnostic(
Diagnostic::new(
GsvAssemblyInvalid,
Error,
message,
SourceRef::sentence(record_index),
),
)
}
///|
pub fn GsvAssembler::new() -> GsvAssembler {
{ pending_value: [] }
}
///|
pub fn GsvAssembler::pending_count(self : GsvAssembler) -> Int {
self.pending_value.length()
}
///|
fn GsvAssembler::find_pending(
self : GsvAssembler,
talker : String,
signal_id : String?,
) -> Int? {
for index = 0; index < self.pending_value.length(); index = index + 1 {
let pending = self.pending_value[index]
if pending.talker_value == talker && pending.signal_id_value == signal_id {
return Some(index)
}
}
None
}
///|
fn pending_from_first(
record : GsvRecord,
record_index : Int,
) -> PendingGsvAssembly {
{
talker_value: record.talker_value,
signal_id_value: record.signal_id_value,
total_messages_value: record.total_messages_value,
next_message_value: 2,
total_satellites_value: record.total_satellites_value,
observations_value: record.observations_value.copy(),
source_record_indexes_value: [record_index],
}
}
///|
fn complete_gsv(
pending : PendingGsvAssembly,
record_index : Int,
) -> Result[GsvAssembly, NmeaError] {
if pending.observations_value.length() != pending.total_satellites_value {
return Err(
gsv_assembly_error(
record_index, "assembled observations do not match announced satellite total",
),
)
}
Ok({
talker_value: pending.talker_value,
signal_id_value: pending.signal_id_value,
total_messages_value: pending.total_messages_value,
total_satellites_value: pending.total_satellites_value,
observations_value: pending.observations_value,
source_record_indexes_value: pending.source_record_indexes_value,
})
}
///|
pub fn GsvAssembler::push(
self : GsvAssembler,
record : GsvRecord,
record_index~ : Int,
) -> Result[GsvAssembly?, NmeaError] {
let existing = self.find_pending(record.talker_value, record.signal_id_value)
if record.message_number_value == 1 {
let interrupted = match existing {
Some(index) => {
self.pending_value.remove(index) |> ignore
true
}
None => false
}
let pending = pending_from_first(record, record_index)
if pending.observations_value.length() > pending.total_satellites_value {
return Err(
gsv_assembly_error(
record_index, "fragment observations exceed announced satellite total",
),
)
}
if pending.total_messages_value == 1 {
return match complete_gsv(pending, record_index) {
Ok(assembly) => Ok(Some(assembly))
Err(error) => Err(error)
}
}
self.pending_value.push(pending)
if interrupted {
Err(
gsv_assembly_error(
record_index, "a new first fragment interrupted an incomplete sequence",
),
)
} else {
Ok(None)
}
} else {
let index = match existing {
Some(value) => value
None =>
return Err(
gsv_assembly_error(
record_index, "GSV sequence does not begin with the first fragment",
),
)
}
let pending = self.pending_value.remove(index)
if record.total_messages_value != pending.total_messages_value ||
record.total_satellites_value != pending.total_satellites_value {
return Err(
gsv_assembly_error(
record_index, "GSV sequence metadata changed between fragments",
),
)
}
if record.message_number_value != pending.next_message_value {
return Err(
gsv_assembly_error(
record_index, "GSV fragment is duplicate, missing, or out of order",
),
)
}
for observation in record.observations_value {
pending.observations_value.push(observation)
}
pending.source_record_indexes_value.push(record_index)
if pending.observations_value.length() > pending.total_satellites_value {
return Err(
gsv_assembly_error(
record_index, "fragment observations exceed announced satellite total",
),
)
}
if record.message_number_value == pending.total_messages_value {
match complete_gsv(pending, record_index) {
Ok(assembly) => Ok(Some(assembly))
Err(error) => Err(error)
}
} else {
pending.next_message_value += 1
self.pending_value.push(pending)
Ok(None)
}
}
}
///|
pub fn GsvAssembly::talker(self : GsvAssembly) -> String {
self.talker_value
}
///|
pub fn GsvAssembly::signal_id(self : GsvAssembly) -> String? {
self.signal_id_value
}
///|
pub fn GsvAssembly::total_satellites(self : GsvAssembly) -> Int {
self.total_satellites_value
}
///|
pub fn GsvAssembly::total_messages(self : GsvAssembly) -> Int {
self.total_messages_value
}
///|
pub fn GsvAssembly::observations(
self : GsvAssembly,
) -> Array[SatelliteObservation] {
self.observations_value.copy()
}
///|
pub fn GsvAssembly::source_record_indexes(self : GsvAssembly) -> Array[Int] {
self.source_record_indexes_value.copy()
}