///|
priv struct PropertyEntry {
id : UInt32
mut values : Array[Tag]
writable : Bool
array : Bool
}
///|
priv struct DeviceObject {
id : ObjectId
properties : Array[PropertyEntry]
mut command : PriorityArray?
}
///|
pub struct Device {
priv id : ObjectId
priv vendor : UInt32
priv limits : Limits
priv objects : Array[DeviceObject]
priv mut stored_bytes : Int32
}
///|
pub fn Device::new(
instance : Int32,
name : String,
vendor : UInt32,
limits~ : Limits = Limits::default(),
) -> Device raise BacnetError {
limits.validate()
if instance == 4194303 {
raise Invalid("wildcard device instance", 0)
}
if vendor > 65535U {
raise Invalid("vendor identifier", 0)
}
let id = ObjectId::new(8, instance)
let device : Device = { id, vendor, limits, objects: [], stored_bytes: 0, }
device.add_object(id, name)
device.define_property(id, 120U, [Application(Unsigned(vendor))])
device.define_property(id, 62U, [Application(Unsigned(1476U))])
device.define_property(id, 107U, [Application(Enumerated(3U))])
device
}
///|
pub fn Device::identifier(self : Device) -> ObjectId {
self.id
}
///|
pub fn Device::resource_limits(self : Device) -> Limits {
self.limits
}
///|
fn Device::find(self : Device, id : ObjectId) -> DeviceObject raise BacnetError {
for object in self.objects {
if object.id == id {
return object
}
}
raise Property(1U, 31U)
}
///|
fn DeviceObject::find(
self : DeviceObject,
id : UInt32,
) -> PropertyEntry raise BacnetError {
for property in self.properties {
if property.id == id {
return property
}
}
raise Property(2U, 32U)
}
///|
pub fn Device::add_object(
self : Device,
id : ObjectId,
name : String,
) -> Unit raise BacnetError {
if self.objects.length() >= self.limits.max_objects {
raise Limit("object count")
}
if self.limits.max_properties < (if id == self.id { 5 } else { 4 }) {
raise Limit("mandatory object properties")
}
if id.kind == 8 && id != self.id {
raise State("one device object per registry")
}
for object in self.objects {
if object.id == id {
raise State("duplicate object identifier")
}
if object.find(77U).values is [Application(Text(existing))] &&
existing == name {
raise State("duplicate object name")
}
}
if name.is_empty() {
raise Invalid("empty object name", 0)
}
let properties = [
{
id: 75U,
values: [Application(Object(id))],
writable: false,
array: false,
},
{
id: 77U,
values: [Application(Text(name))],
writable: false,
array: false,
},
{
id: 79U,
values: [Application(Enumerated(id.kind.reinterpret_as_uint()))],
writable: false,
array: false,
},
]
let mut added = 0
for p in properties {
added += encode_tags(p.values, limits=self.limits).length()
}
if added > self.limits.max_bytes - self.stored_bytes {
raise Limit("aggregate stored property bytes")
}
self.objects.push({ id, properties, command: None, })
self.stored_bytes += added
}
///|
pub fn Device::define_property(
self : Device,
id : ObjectId,
property : UInt32,
value : Array[Tag],
writable~ : Bool = false,
array~ : Bool = false,
) -> Unit raise BacnetError {
let object = self.find(id)
let computed = 1 +
(if id == self.id { 1 } else { 0 }) +
(if object.command is Some(_) { 2 } else { 0 })
if object.properties.length() + computed >= self.limits.max_properties {
raise Limit("property count")
}
if property == 76U || property == 87U || property == 104U || property == 371U {
raise State("property is managed by registry")
}
for entry in object.properties {
if entry.id == property {
raise State("duplicate property")
}
}
if !array && value.length() != 1 {
raise Invalid("scalar property requires one value", 0)
}
if writable && (array || !(value is [Application(_)])) {
raise Unsupported("writable compound or array property")
}
let wire = encode_tags(value, limits=self.limits)
if wire.length() > self.limits.max_bytes - self.stored_bytes {
raise Limit("aggregate stored property bytes")
}
let frozen = decode_tags(wire, limits=self.limits)
object.properties.push({ id: property, values: frozen, writable, array, })
self.stored_bytes += wire.length()
}
///|
fn indexed(
value : Array[Tag],
array : Bool,
index : UInt32?,
) -> Array[Tag] raise BacnetError {
match index {
None => value.copy()
Some(0U) if array =>
[Application(Unsigned(value.length().reinterpret_as_uint()))]
Some(i) if array => {
if i > value.length().reinterpret_as_uint() {
raise Property(2U, 42U)
}
[value[i.reinterpret_as_int() - 1]]
}
Some(_) => raise Property(2U, 50U)
}
}
///|
pub fn Device::read(
self : Device,
reference : PropertyRef,
) -> Array[Tag] raise BacnetError {
let object = self.find(reference.object)
let (value, array) = match reference.property {
76U if reference.object == self.id =>
(self.objects.map(fn(o) { Application(Object(o.id)) }), true)
371U =>
(
self
.property_ids(reference.object)
.map(fn(p) { Application(Enumerated(p)) }),
true,
)
87U =>
match object.command {
Some(c) => (c.values(), true)
None => raise Property(2U, 32U)
}
104U =>
match object.command {
Some(c) => ([Application(c.default_value())], false)
None => raise Property(2U, 32U)
}
85U if object.command is Some(c) => ([Application(c.effective())], false)
_ => {
let p = object.find(reference.property)
(p.values, p.array)
}
}
clone_tags(indexed(value, array, reference.index), self.limits)
}
///|
pub fn Device::property_ids(
self : Device,
id : ObjectId,
) -> Array[UInt32] raise BacnetError {
let object = self.find(id)
let result = object.properties.map(fn(p) { p.id })
if id == self.id {
result.push(76U)
}
if object.command is Some(_) {
result.push(87U)
result.push(104U)
}
// Property_List excludes the three mandatory identification properties and itself.
result.filter(fn(p) { p != 75U && p != 77U && p != 79U })
}
///|
pub fn Device::all_property_ids(
self : Device,
id : ObjectId,
) -> Array[UInt32] raise BacnetError {
let result = [75U, 77U, 79U, 371U]
for p in self.property_ids(id) {
result.push(p)
}
result
}