1use crate::apdu::ConfirmedRequestHeader;
2use crate::encoding::{
3 primitives::{encode_ctx_character_string, encode_ctx_object_id, encode_ctx_unsigned},
4 tag::{AppTag, Tag},
5 writer::Writer,
6};
7use crate::types::{Date, ObjectId, Time};
8use crate::EncodeError;
9
10pub const SERVICE_ACKNOWLEDGE_ALARM: u8 = 0x00;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13#[repr(u32)]
14pub enum EventState {
15 Normal = 0,
16 Fault = 1,
17 Offnormal = 2,
18 HighLimit = 3,
19 LowLimit = 4,
20 LifeSafetyAlarm = 5,
21}
22
23impl EventState {
24 pub const fn to_u32(self) -> u32 {
25 self as u32
26 }
27
28 pub const fn from_u32(value: u32) -> Option<Self> {
29 match value {
30 0 => Some(Self::Normal),
31 1 => Some(Self::Fault),
32 2 => Some(Self::Offnormal),
33 3 => Some(Self::HighLimit),
34 4 => Some(Self::LowLimit),
35 5 => Some(Self::LifeSafetyAlarm),
36 _ => None,
37 }
38 }
39}
40
41#[derive(Debug, Clone, Copy, PartialEq, Eq)]
42pub enum TimeStamp {
43 Time(Time),
44 SequenceNumber(u32),
45 DateTime { date: Date, time: Time },
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct AcknowledgeAlarmRequest<'a> {
50 pub acknowledging_process_id: u32,
51 pub event_object_id: ObjectId,
52 pub event_state_acknowledged: EventState,
53 pub event_time_stamp: TimeStamp,
54 pub acknowledgment_source: &'a str,
55 pub time_of_acknowledgment: TimeStamp,
56 pub invoke_id: u8,
57}
58
59impl<'a> AcknowledgeAlarmRequest<'a> {
60 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
61 ConfirmedRequestHeader {
62 segmented: false,
63 more_follows: false,
64 segmented_response_accepted: false,
65 max_segments: 0,
66 max_apdu: 5,
67 invoke_id: self.invoke_id,
68 sequence_number: None,
69 proposed_window_size: None,
70 service_choice: SERVICE_ACKNOWLEDGE_ALARM,
71 }
72 .encode(w)?;
73 encode_ctx_unsigned(w, 0, self.acknowledging_process_id)?;
74 encode_ctx_object_id(w, 1, self.event_object_id.raw())?;
75 encode_ctx_unsigned(w, 2, self.event_state_acknowledged.to_u32())?;
76
77 Tag::Opening { tag_num: 3 }.encode(w)?;
78 encode_timestamp(w, self.event_time_stamp)?;
79 Tag::Closing { tag_num: 3 }.encode(w)?;
80
81 encode_ctx_character_string(w, 4, self.acknowledgment_source)?;
82
83 Tag::Opening { tag_num: 5 }.encode(w)?;
84 encode_timestamp(w, self.time_of_acknowledgment)?;
85 Tag::Closing { tag_num: 5 }.encode(w)?;
86 Ok(())
87 }
88}
89
90fn encode_timestamp(w: &mut Writer<'_>, value: TimeStamp) -> Result<(), EncodeError> {
91 match value {
92 TimeStamp::Time(time) => {
93 Tag::Context { tag_num: 0, len: 4 }.encode(w)?;
94 w.write_all(&[time.hour, time.minute, time.second, time.hundredths])
95 }
96 TimeStamp::SequenceNumber(seq) => encode_ctx_unsigned(w, 1, seq),
97 TimeStamp::DateTime { date, time } => {
98 Tag::Opening { tag_num: 2 }.encode(w)?;
99 Tag::Application {
100 tag: AppTag::Date,
101 len: 4,
102 }
103 .encode(w)?;
104 w.write_all(&[date.year_since_1900, date.month, date.day, date.weekday])?;
105 Tag::Application {
106 tag: AppTag::Time,
107 len: 4,
108 }
109 .encode(w)?;
110 w.write_all(&[time.hour, time.minute, time.second, time.hundredths])?;
111 Tag::Closing { tag_num: 2 }.encode(w)
112 }
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::{AcknowledgeAlarmRequest, EventState, TimeStamp, SERVICE_ACKNOWLEDGE_ALARM};
119 use crate::apdu::ConfirmedRequestHeader;
120 use crate::encoding::{reader::Reader, writer::Writer};
121 use crate::types::{Date, ObjectId, ObjectType, Time};
122
123 #[test]
124 fn encode_acknowledge_alarm_request() {
125 let req = AcknowledgeAlarmRequest {
126 acknowledging_process_id: 12,
127 event_object_id: ObjectId::new(ObjectType::AnalogInput, 2),
128 event_state_acknowledged: EventState::Offnormal,
129 event_time_stamp: TimeStamp::SequenceNumber(42),
130 acknowledgment_source: "operator",
131 time_of_acknowledgment: TimeStamp::DateTime {
132 date: Date {
133 year_since_1900: 126,
134 month: 2,
135 day: 7,
136 weekday: 6,
137 },
138 time: Time {
139 hour: 10,
140 minute: 11,
141 second: 12,
142 hundredths: 13,
143 },
144 },
145 invoke_id: 9,
146 };
147 let mut buf = [0u8; 256];
148 let mut w = Writer::new(&mut buf);
149 req.encode(&mut w).unwrap();
150
151 let mut r = Reader::new(w.as_written());
152 let hdr = ConfirmedRequestHeader::decode(&mut r).unwrap();
153 assert_eq!(hdr.service_choice, SERVICE_ACKNOWLEDGE_ALARM);
154 assert_eq!(hdr.invoke_id, 9);
155 }
156}