///|
priv struct Fields {
tags : Array[Tag]
mut pos : Int32
}
///|
fn Fields::new(tags : Array[Tag]) -> Fields {
{ tags, pos: 0, }
}
///|
fn Fields::next(self : Fields) -> Tag raise BacnetError {
if self.pos >= self.tags.length() {
raise Invalid("missing service field", self.pos)
}
let tag = self.tags[self.pos]
self.pos += 1
tag
}
///|
fn Fields::context(self : Fields, number : Int32) -> Bytes raise BacnetError {
match self.next() {
Context(n, bytes) if n == number => bytes
_ => raise Invalid("context field order or type", self.pos - 1)
}
}
///|
fn Fields::optional(self : Fields, number : Int32) -> Bytes? {
if self.pos < self.tags.length() &&
self.tags[self.pos] is Context(n, bytes) &&
n == number {
self.pos += 1
Some(bytes)
} else {
None
}
}
///|
fn Fields::uint(self : Fields, number : Int32) -> UInt32 raise BacnetError {
read_unsigned(self.context(number))
}
///|
fn Fields::optional_uint(
self : Fields,
number : Int32,
) -> UInt32? raise BacnetError {
match self.optional(number) {
Some(b) => Some(read_unsigned(b))
None => None
}
}
///|
fn Fields::object(self : Fields, number : Int32) -> ObjectId raise BacnetError {
let bytes = self.context(number)
if bytes.length() != 4 {
raise Invalid("context object ID width", self.pos - 1)
}
ObjectId::from_number(read_unsigned(bytes))
}
///|
fn Fields::children(
self : Fields,
number : Int32,
) -> Array[Tag] raise BacnetError {
match self.next() {
Constructed(n, tags) if n == number => tags
_ => raise Invalid("expected constructed field", self.pos - 1)
}
}
///|
fn Fields::done(self : Fields) -> Unit raise BacnetError {
if self.pos != self.tags.length() {
raise Invalid("unexpected service field", self.pos)
}
}
///|
fn context_uint(number : Int32, value : UInt32) -> Tag raise BacnetError {
Context(number, unsigned_bytes(value))
}
///|
fn context_object(number : Int32, value : ObjectId) -> Tag raise BacnetError {
let w = Writer::new(4)
w.uint(value.number(), 4)
Context(number, w.finish())
}
///|
fn context_bool(number : Int32, value : Bool) -> Tag {
Context(number, if value { b"\x01" } else { b"\x00" })
}
///|
fn read_context_bool(bytes : Bytes) -> Bool raise BacnetError {
if bytes.length() != 1 || bytes[0].to_int() > 1 {
raise Invalid("context boolean", 0)
}
bytes[0].to_int() == 1
}