use super::*;
use crate::common::{decode_context, decode_context_bool, decode_context_u32};
use bacnet_encoding::constructed::validate_tlv_sequence;
#[derive(Debug, Clone)]
pub struct EventNotificationRequest {
pub process_identifier: u32,
pub initiating_device_identifier: ObjectIdentifier,
pub event_object_identifier: ObjectIdentifier,
pub timestamp: BACnetTimeStamp,
pub notification_class: u32,
pub priority: u8,
pub event_type: u32,
pub message_text: Option<String>,
pub notify_type: u32,
pub ack_required: bool,
pub from_state: u32,
pub to_state: u32,
pub event_values: Option<NotificationParameters>,
}
impl EventNotificationRequest {
pub fn encode(&self, buf: &mut BytesMut) -> Result<(), Error> {
let mut encoded = BytesMut::new();
self.encode_into(&mut encoded)?;
validate_tlv_sequence(&encoded, "EventNotification")
.map_err(|error| Error::Encoding(error.to_string()))?;
buf.extend_from_slice(&encoded);
Ok(())
}
fn encode_into(&self, buf: &mut BytesMut) -> Result<(), Error> {
primitives::encode_ctx_unsigned(buf, 0, self.process_identifier as u64);
primitives::encode_ctx_object_id(buf, 1, &self.initiating_device_identifier);
primitives::encode_ctx_object_id(buf, 2, &self.event_object_identifier);
primitives::encode_timestamp(buf, 3, &self.timestamp)?;
primitives::encode_ctx_unsigned(buf, 4, self.notification_class as u64);
primitives::encode_ctx_unsigned(buf, 5, self.priority as u64);
primitives::encode_ctx_enumerated(buf, 6, self.event_type);
if let Some(ref text) = self.message_text {
primitives::encode_ctx_character_string(buf, 7, text)?;
}
primitives::encode_ctx_enumerated(buf, 8, self.notify_type);
if self.notify_type != 2 {
primitives::encode_ctx_boolean(buf, 9, self.ack_required);
}
if self.notify_type != 2 {
primitives::encode_ctx_enumerated(buf, 10, self.from_state);
}
primitives::encode_ctx_enumerated(buf, 11, self.to_state);
if self.notify_type != 2 {
if let Some(ref params) = self.event_values {
tags::encode_opening_tag(buf, 12);
params.encode(buf)?;
tags::encode_closing_tag(buf, 12);
}
}
Ok(())
}
pub fn decode(data: &[u8]) -> Result<Self, Error> {
validate_tlv_sequence(data, "EventNotification")?;
let (process_identifier, mut offset) =
decode_context_u32(data, 0, 0, "EventNotification processIdentifier")?;
let (content, new_offset) = decode_context(
data,
offset,
1,
"EventNotification initiatingDeviceIdentifier",
)?;
let initiating_device_identifier = ObjectIdentifier::decode(content)?;
offset = new_offset;
let (content, new_offset) =
decode_context(data, offset, 2, "EventNotification eventObjectIdentifier")?;
let event_object_identifier = ObjectIdentifier::decode(content)?;
offset = new_offset;
let (timestamp, new_offset) = primitives::decode_timestamp(data, offset, 3)?;
offset = new_offset;
let (notification_class, new_offset) =
decode_context_u32(data, offset, 4, "EventNotification notificationClass")?;
offset = new_offset;
let priority_offset = offset;
let (content, new_offset) = decode_context(data, offset, 5, "EventNotification priority")?;
let priority = primitives::decode_unsigned(content)?;
let priority = u8::try_from(priority).map_err(|_| {
Error::decoding(priority_offset, "EventNotification priority exceeds u8")
})?;
offset = new_offset;
let (event_type, new_offset) =
decode_context_u32(data, offset, 6, "EventNotification eventType")?;
offset = new_offset;
let mut message_text = None;
if offset < data.len() {
let (peek, _) = tags::decode_tag(data, offset)?;
if peek.is_context(7) {
let (content, new_offset) =
decode_context(data, offset, 7, "EventNotification messageText")?;
message_text = Some(primitives::decode_character_string(content)?);
offset = new_offset;
}
}
let (notify_type, new_offset) =
decode_context_u32(data, offset, 8, "EventNotification notifyType")?;
offset = new_offset;
let mut ack_required = false;
if offset < data.len() {
let (peek, _) = tags::decode_tag(data, offset)?;
if peek.is_context(9) {
(ack_required, offset) =
decode_context_bool(data, offset, 9, "EventNotification ackRequired")?;
}
}
let mut from_state = 0;
if offset < data.len() {
let (peek, _) = tags::decode_tag(data, offset)?;
if peek.is_context(10) {
(from_state, offset) =
decode_context_u32(data, offset, 10, "EventNotification fromState")?;
} else if notify_type != 2 {
return Err(Error::decoding(
offset,
"EventNotification expected fromState",
));
}
} else if notify_type != 2 {
return Err(Error::decoding(
offset,
"EventNotification missing fromState",
));
}
let (to_state, new_offset) =
decode_context_u32(data, offset, 11, "EventNotification toState")?;
offset = new_offset;
let mut event_values = None;
if offset < data.len() {
let (opening, inner_start) = tags::decode_tag(data, offset)?;
if !opening.is_opening_tag(12) {
return Err(Error::decoding(
offset,
"EventNotification expected opening tag 12 for eventValues",
));
}
let closing_offset = data.len().checked_sub(1).ok_or_else(|| {
Error::decoding(offset, "EventNotification missing closing tag 12")
})?;
if closing_offset < inner_start {
return Err(Error::decoding(
inner_start,
"EventNotification empty eventValues",
));
}
let (closing, next) = tags::decode_tag(data, closing_offset)?;
if !closing.is_closing_tag(12) || next != data.len() {
return Err(Error::decoding(
closing_offset,
"EventNotification expected closing tag 12 after eventValues",
));
}
event_values = Some(NotificationParameters::decode_bounded(
data,
inner_start,
closing_offset,
)?);
offset = next;
}
let _ = offset;
Ok(Self {
process_identifier,
initiating_device_identifier,
event_object_identifier,
timestamp,
notification_class,
priority,
event_type,
message_text,
notify_type,
ack_required,
from_state,
to_state,
event_values,
})
}
}