///|
pub fn Device::add_point(
self : Device,
id : ObjectId,
name : String,
domain : CommandDomain,
initial : Value,
commandable~ : Bool = false,
units~ : UInt32 = 95U,
) -> Unit raise BacnetError {
domain_validate(domain, initial)
let valid_kind = match domain {
Analog => id.kind == 0 || id.kind == 1 || id.kind == 2
Binary => id.kind == 3 || id.kind == 4 || id.kind == 5
MultiState(_) => id.kind == 13 || id.kind == 14 || id.kind == 19
}
if !valid_kind {
raise Invalid("object type and point domain mismatch", 0)
}
if commandable && (id.kind == 0 || id.kind == 3 || id.kind == 13) {
raise Invalid("input point cannot be commandable", 0)
}
if !commandable && (id.kind == 1 || id.kind == 4 || id.kind == 14) {
raise Invalid("output object requires command priorities", 0)
}
// Construct a complete candidate before changing the live registry.
let temp : Device = {
id: self.id,
vendor: self.vendor,
limits: self.limits,
objects: [],
stored_bytes: 0,
}
temp.add_object(id, name)
temp.define_property(id, 85U, [Application(initial)], writable=commandable)
temp.define_property(id, 111U, [Application(Bits(4, b"\x00"))])
temp.define_property(id, 81U, [Application(Boolean(false))])
match domain {
Analog => {
temp.define_property(id, 117U, [Application(Enumerated(units))])
temp.define_property(id, 22U, [Application(Real(0.0F))])
}
MultiState(count) =>
temp.define_property(id, 74U, [Application(Unsigned(count))])
Binary => ()
}
if commandable {
if temp.objects[0].properties.length() + 3 > self.limits.max_properties {
raise Limit("command property count")
}
temp.objects[0].command = Some(PriorityArray::new(domain, initial))
temp.stored_bytes += 85
}
if temp.stored_bytes > self.limits.max_bytes - self.stored_bytes {
raise Limit("aggregate point bytes")
}
self.add_object(id, name)
let candidate = temp.objects[0]
let target = self.objects[self.objects.length() - 1]
for i = 3; i < candidate.properties.length(); i = i + 1 {
target.properties.push(candidate.properties[i])
}
target.command = candidate.command
let mut identification_bytes = 0
for i = 0; i < 3; i = i + 1 {
identification_bytes += encode_tags(
candidate.properties[i].values,
limits=self.limits,
).length()
}
self.stored_bytes += temp.stored_bytes - identification_bytes
}
///|
fn Device::replace_scalar(
self : Device,
property : PropertyEntry,
value : Value,
) -> Unit raise BacnetError {
let old = match property.values[:] {
[Application(old)] => old
_ => raise Property(2U, 9U)
}
let (old_kind, _) = application_body(old, self.limits)
let (new_kind, _) = application_body(value, self.limits)
if old_kind != new_kind {
raise Property(2U, 9U)
}
let candidate = [Application(value)]
let old_size = encode_tags(property.values, limits=self.limits).length()
let wire = encode_tags(candidate, limits=self.limits)
if wire.length() > self.limits.max_bytes - (self.stored_bytes - old_size) {
raise Limit("aggregate replacement bytes")
}
let snapshot = decode_tags(wire, limits=self.limits)
property.values = snapshot
self.stored_bytes = self.stored_bytes - old_size + wire.length()
}
///|
fn Device::point_domain(
_self : Device,
object : DeviceObject,
) -> CommandDomain raise BacnetError {
match object.id.kind {
0 | 1 | 2 => Analog
3 | 4 | 5 => Binary
13 | 14 | 19 =>
match object.find(74U).values[:] {
[Application(Unsigned(count))] => MultiState(count)
_ => raise State("invalid configured state count")
}
_ => raise Unsupported("object type does not have point semantics")
}
}
///|
pub fn Device::update(
self : Device,
id : ObjectId,
property : UInt32,
value : Value,
) -> Unit raise BacnetError {
let object = self.find(id)
if property == 75U || property == 77U || property == 79U || property == 74U {
raise State("immutable object identity or domain")
}
if property == 85U {
if object.command is Some(_) {
raise State("commandable value must be written through a priority")
}
domain_validate(self.point_domain(object), value)
}
if property == 111U {
let out_of_service = match object.find(81U).values[:] {
[Application(Boolean(v))] => v
_ => raise State("point Out_Of_Service schema")
}
match value {
Bits(4, data) if data.length() == 1 &&
(data[0].to_int() & 15) == 0 &&
((data[0].to_int() & 16) != 0) == out_of_service => ()
_ => raise Property(2U, 37U)
}
}
if property == 81U {
let enabled = match value {
Boolean(v) => v
_ => raise Property(2U, 9U)
}
let status = match object.find(111U).values[:] {
[Application(Bits(4, data))] if data.length() == 1 => data[0].to_int()
_ => raise State("point Status_Flags schema")
}
self.replace_scalar(object.find(81U), value)
let updated = if enabled { status | 16 } else { status & 0xef }
self.replace_scalar(
object.find(111U),
Bits(4, Bytes::from_array([updated.to_byte()])),
)
return
}
if property == 22U {
match value {
Real(v) if v >= 0.0F &&
(v.reinterpret_as_uint() & 0x7f800000U) != 0x7f800000U => ()
_ => raise Property(2U, 37U)
}
}
self.replace_scalar(object.find(property), value)
}
///|
pub fn Device::write(
self : Device,
request : WriteProperty,
) -> Unit raise BacnetError {
let object = self.find(request.reference.object)
if request.reference.index is Some(_) {
ignore(self.read(request.reference))
raise Property(2U, 40U)
}
let value = match request.value[:] {
[Application(value)] => value
_ => raise Property(2U, 9U)
}
if request.reference.property == 85U && object.command is Some(command) {
let priority = request.priority.unwrap_or(16)
command.write(priority, value)
return
}
if request.priority is Some(_) {
raise Property(2U, 40U)
}
let property = object.find(request.reference.property)
if !property.writable {
raise Property(2U, 40U)
}
self.replace_scalar(property, value)
}
///|
pub fn Device::set_relinquish_default(
self : Device,
id : ObjectId,
value : Value,
) -> Unit raise BacnetError {
match self.find(id).command {
Some(command) => command.set_default(value)
None => raise Property(2U, 32U)
}
}
///|
pub fn Device::active_priority(
self : Device,
id : ObjectId,
) -> Int32? raise BacnetError {
match self.find(id).command {
Some(command) => command.active_priority()
None => raise Property(2U, 32U)
}
}
///|
pub fn Device::announcement(self : Device) -> IAm {
{ device: self.id, vendor: self.vendor, segmentation: 3U, max_apdu: 1476U, }
}