use super::*;
pub fn handle_get_event_information(
db: &ObjectDatabase,
service_data: &[u8],
buf: &mut BytesMut,
) -> Result<(), Error> {
const MAX_SUMMARIES: usize = 25;
let request = GetEventInformationRequest::decode(service_data)?;
let mut summaries = Vec::new();
let mut skipping = request.last_received_object_identifier.is_some();
let mut more_events = false;
for (oid, object) in db.iter_objects() {
if skipping {
if Some(oid) == request.last_received_object_identifier {
skipping = false;
}
continue;
}
if let Ok(PropertyValue::Enumerated(state)) =
object.read_property(PropertyIdentifier::EVENT_STATE, None)
{
if state != bacnet_types::enums::EventState::NORMAL.to_raw() {
if summaries.len() >= MAX_SUMMARIES {
more_events = true;
break;
}
let notification_class = object
.read_property(PropertyIdentifier::NOTIFICATION_CLASS, None)
.ok()
.and_then(|v| match v {
PropertyValue::Unsigned(n) => Some(n as u32),
_ => None,
})
.unwrap_or(0);
let event_enable = object
.read_property(PropertyIdentifier::EVENT_ENABLE, None)
.ok()
.and_then(|v| match v {
PropertyValue::BitString { data, .. } => data.first().map(|b| b >> 5),
_ => None,
})
.unwrap_or(0x07);
let notify_type = object
.read_property(PropertyIdentifier::NOTIFY_TYPE, None)
.ok()
.and_then(|v| match v {
PropertyValue::Enumerated(n) => Some(n),
_ => None,
})
.unwrap_or(0);
let event_priorities =
ObjectIdentifier::new(ObjectType::NOTIFICATION_CLASS, notification_class)
.ok()
.and_then(|nc_oid| db.get(&nc_oid))
.and_then(|nc_obj| {
nc_obj
.read_property(PropertyIdentifier::PRIORITY, None)
.ok()
})
.and_then(|v| match v {
PropertyValue::OctetString(bytes) if bytes.len() == 3 => {
Some([bytes[0] as u32, bytes[1] as u32, bytes[2] as u32])
}
_ => None,
})
.unwrap_or([0, 0, 0]);
let acked = object
.read_property(PropertyIdentifier::ACKED_TRANSITIONS, None)
.ok()
.and_then(|v| match v {
PropertyValue::BitString { data, .. } => data.first().map(|b| b >> 5),
_ => None,
})
.unwrap_or(0x07);
let event_timestamps = object
.read_property(PropertyIdentifier::EVENT_TIME_STAMPS, None)
.ok()
.and_then(|v| match v {
PropertyValue::List(items) if items.len() == 3 => None,
_ => None,
})
.unwrap_or([
BACnetTimeStamp::SequenceNumber(0),
BACnetTimeStamp::SequenceNumber(0),
BACnetTimeStamp::SequenceNumber(0),
]);
summaries.push(EventSummary {
object_identifier: oid,
event_state: state,
acknowledged_transitions: acked,
event_timestamps,
notify_type,
event_enable,
event_priorities,
notification_class,
});
}
}
}
let ack = GetEventInformationAck {
list_of_event_summaries: summaries,
more_events,
};
ack.encode(buf);
Ok(())
}
pub fn handle_acknowledge_alarm(db: &mut ObjectDatabase, service_data: &[u8]) -> Result<(), Error> {
let request = AcknowledgeAlarmRequest::decode(service_data)?;
let transition_bit: u8 = match EventState::from_raw(request.event_state_acknowledged) {
s if s == EventState::NORMAL => 0x04, s if s == EventState::FAULT => 0x02, _ => 0x01, };
let object = db
.get_mut(&request.event_object_identifier)
.ok_or(Error::Protocol {
class: ErrorClass::OBJECT.to_raw() as u32,
code: ErrorCode::UNKNOWN_OBJECT.to_raw() as u32,
})?;
object.acknowledge_alarm(transition_bit)?;
Ok(())
}
pub fn handle_get_enrollment_summary(
db: &ObjectDatabase,
service_data: &[u8],
buf: &mut BytesMut,
) -> Result<(), Error> {
use bacnet_services::enrollment_summary::{
EnrollmentSummaryEntry, GetEnrollmentSummaryAck, GetEnrollmentSummaryRequest,
};
let request = GetEnrollmentSummaryRequest::decode(service_data)?;
let mut entries = Vec::new();
for (_oid, object) in db.iter_objects() {
let oid = object.object_identifier();
let event_state = object
.read_property(PropertyIdentifier::EVENT_STATE, None)
.ok()
.and_then(|v| match v {
PropertyValue::Enumerated(e) => Some(e),
_ => None,
})
.unwrap_or(0);
if let Some(filter_state) = request.event_state_filter {
if event_state != filter_state.to_raw() {
continue;
}
}
let notification_class = object
.read_property(PropertyIdentifier::NOTIFICATION_CLASS, None)
.ok()
.and_then(|v| match v {
PropertyValue::Unsigned(n) => Some(n as u16),
_ => None,
})
.unwrap_or(0);
if let Some(nc_filter) = request.notification_class_filter {
if notification_class != nc_filter {
continue;
}
}
if let Some(ref pf) = request.priority_filter {
let priority = ObjectIdentifier::new(
bacnet_types::enums::ObjectType::NOTIFICATION_CLASS,
notification_class as u32,
)
.ok()
.and_then(|nc_oid| db.get(&nc_oid))
.and_then(|nc_obj| {
nc_obj
.read_property(PropertyIdentifier::PRIORITY, Some(1))
.ok()
.and_then(|v| match v {
PropertyValue::Unsigned(p) => Some(p as u8),
_ => None,
})
})
.unwrap_or(0);
if priority < pf.min_priority || priority > pf.max_priority {
continue;
}
}
if event_state == 0 && request.event_state_filter.is_none() {
continue;
}
entries.push(EnrollmentSummaryEntry {
object_identifier: oid,
event_type: bacnet_types::enums::EventType::CHANGE_OF_STATE,
event_state: bacnet_types::enums::EventState::from_raw(event_state),
priority: 0,
notification_class,
});
}
let ack = GetEnrollmentSummaryAck { entries };
ack.encode(buf);
Ok(())
}
pub fn handle_text_message(
service_data: &[u8],
) -> Result<bacnet_services::text_message::TextMessageRequest, Error> {
bacnet_services::text_message::TextMessageRequest::decode(service_data)
}
pub fn handle_life_safety_operation(service_data: &[u8]) -> Result<(), Error> {
let _request = bacnet_services::life_safety::LifeSafetyOperationRequest::decode(service_data)?;
Ok(())
}
pub fn handle_get_alarm_summary(db: &ObjectDatabase, buf: &mut BytesMut) -> Result<(), Error> {
use bacnet_services::alarm_summary::{AlarmSummaryEntry, GetAlarmSummaryAck};
let mut entries = Vec::new();
for (_oid, object) in db.iter_objects() {
let oid = object.object_identifier();
let event_state = object
.read_property(PropertyIdentifier::EVENT_STATE, None)
.ok()
.and_then(|v| match v {
PropertyValue::Enumerated(e) => Some(e),
_ => None,
})
.unwrap_or(0);
if event_state != 0 {
let acked = object
.read_property(PropertyIdentifier::ACKED_TRANSITIONS, None)
.ok()
.and_then(|v| match v {
PropertyValue::BitString {
unused_bits, data, ..
} => Some((unused_bits, data)),
_ => None,
})
.unwrap_or((5, vec![0xE0]));
entries.push(AlarmSummaryEntry {
object_identifier: oid,
alarm_state: bacnet_types::enums::EventState::from_raw(event_state),
acknowledged_transitions: acked,
});
}
}
let ack = GetAlarmSummaryAck { entries };
ack.encode(buf);
Ok(())
}