Skip to main content

rustbac_core/services/
acknowledge_alarm.rs

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}