1use crate::apdu::ConfirmedRequestHeader;
2use crate::encoding::{primitives::encode_ctx_object_id, writer::Writer};
3use crate::types::ObjectId;
4use crate::EncodeError;
5
6#[cfg(feature = "alloc")]
7use crate::encoding::{
8 primitives::decode_unsigned,
9 reader::Reader,
10 tag::{AppTag, Tag},
11};
12#[cfg(feature = "alloc")]
13use crate::types::BitString;
14#[cfg(feature = "alloc")]
15use crate::DecodeError;
16#[cfg(feature = "alloc")]
17use alloc::vec::Vec;
18
19pub const SERVICE_GET_EVENT_INFORMATION: u8 = 0x1D;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct GetEventInformationRequest {
23 pub last_received_object_id: Option<ObjectId>,
24 pub invoke_id: u8,
25}
26
27impl GetEventInformationRequest {
28 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
29 ConfirmedRequestHeader {
30 segmented: false,
31 more_follows: false,
32 segmented_response_accepted: true,
33 max_segments: 0,
34 max_apdu: 5,
35 invoke_id: self.invoke_id,
36 sequence_number: None,
37 proposed_window_size: None,
38 service_choice: SERVICE_GET_EVENT_INFORMATION,
39 }
40 .encode(w)?;
41
42 if let Some(object_id) = self.last_received_object_id {
43 encode_ctx_object_id(w, 0, object_id.raw())?;
44 }
45 Ok(())
46 }
47}
48
49#[cfg(feature = "alloc")]
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub struct EventSummaryItem<'a> {
52 pub object_id: ObjectId,
53 pub event_state: u32,
54 pub acknowledged_transitions: BitString<'a>,
55 pub notify_type: u32,
56 pub event_enable: BitString<'a>,
57 pub event_priorities: [u32; 3],
58}
59
60#[cfg(feature = "alloc")]
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct GetEventInformationAck<'a> {
63 pub summaries: Vec<EventSummaryItem<'a>>,
64 pub more_events: bool,
65}
66
67#[cfg(feature = "alloc")]
68impl<'a> GetEventInformationAck<'a> {
69 pub fn decode_after_header(r: &mut Reader<'a>) -> Result<Self, DecodeError> {
70 if Tag::decode(r)? != (Tag::Opening { tag_num: 0 }) {
71 return Err(DecodeError::InvalidTag);
72 }
73
74 let mut summaries = Vec::new();
75 loop {
76 let tag = Tag::decode(r)?;
77 if tag == (Tag::Closing { tag_num: 0 }) {
78 break;
79 }
80 let object_id = decode_object_id_from_tag(r, tag)?;
81 let event_state = decode_expected_context_unsigned(r, 1)?;
82 let acknowledged_transitions = decode_expected_context_bit_string(r, 2)?;
83
84 match Tag::decode(r)? {
85 Tag::Opening { tag_num: 3 } => skip_constructed(r, 3)?,
86 _ => return Err(DecodeError::InvalidTag),
87 }
88
89 let notify_type = decode_expected_context_unsigned(r, 4)?;
90 let event_enable = decode_expected_context_bit_string(r, 5)?;
91
92 match Tag::decode(r)? {
93 Tag::Opening { tag_num: 6 } => {}
94 _ => return Err(DecodeError::InvalidTag),
95 }
96 let mut priorities = [0u32; 3];
97 for slot in &mut priorities {
98 let priority_tag = Tag::decode(r)?;
99 *slot = decode_unsigned_from_tag(r, priority_tag)?;
100 }
101 if Tag::decode(r)? != (Tag::Closing { tag_num: 6 }) {
102 return Err(DecodeError::InvalidTag);
103 }
104
105 summaries.push(EventSummaryItem {
106 object_id,
107 event_state,
108 acknowledged_transitions,
109 notify_type,
110 event_enable,
111 event_priorities: priorities,
112 });
113 }
114
115 let more_events = match Tag::decode(r)? {
116 Tag::Context { tag_num: 1, len: 0 } => true,
117 Tag::Context { tag_num: 1, len } => decode_unsigned(r, len as usize)? != 0,
118 _ => return Err(DecodeError::InvalidTag),
119 };
120
121 Ok(Self {
122 summaries,
123 more_events,
124 })
125 }
126}
127
128#[cfg(feature = "alloc")]
129fn decode_object_id_from_tag(r: &mut Reader<'_>, tag: Tag) -> Result<ObjectId, DecodeError> {
130 match tag {
131 Tag::Context { tag_num: 0, len } => {
132 if len != 4 {
133 return Err(DecodeError::InvalidLength);
134 }
135 Ok(ObjectId::from_raw(decode_unsigned(r, len as usize)?))
136 }
137 _ => Err(DecodeError::InvalidTag),
138 }
139}
140
141#[cfg(feature = "alloc")]
142fn decode_expected_context_unsigned(r: &mut Reader<'_>, tag_num: u8) -> Result<u32, DecodeError> {
143 match Tag::decode(r)? {
144 Tag::Context {
145 tag_num: found,
146 len,
147 } if found == tag_num => decode_unsigned(r, len as usize),
148 _ => Err(DecodeError::InvalidTag),
149 }
150}
151
152#[cfg(feature = "alloc")]
153fn decode_unsigned_from_tag(r: &mut Reader<'_>, tag: Tag) -> Result<u32, DecodeError> {
154 match tag {
155 Tag::Application {
156 tag: AppTag::UnsignedInt,
157 len,
158 } => decode_unsigned(r, len as usize),
159 Tag::Context { len, .. } => decode_unsigned(r, len as usize),
160 _ => Err(DecodeError::InvalidTag),
161 }
162}
163
164#[cfg(feature = "alloc")]
165fn decode_expected_context_bit_string<'a>(
166 r: &mut Reader<'a>,
167 tag_num: u8,
168) -> Result<BitString<'a>, DecodeError> {
169 match Tag::decode(r)? {
170 Tag::Context {
171 tag_num: found,
172 len,
173 } if found == tag_num => {
174 if len == 0 {
175 return Err(DecodeError::InvalidLength);
176 }
177 let raw = r.read_exact(len as usize)?;
178 if raw[0] > 7 {
179 return Err(DecodeError::InvalidValue);
180 }
181 Ok(BitString {
182 unused_bits: raw[0],
183 data: &raw[1..],
184 })
185 }
186 _ => Err(DecodeError::InvalidTag),
187 }
188}
189
190#[cfg(feature = "alloc")]
191fn skip_constructed(r: &mut Reader<'_>, tag_num: u8) -> Result<(), DecodeError> {
192 loop {
193 let tag = Tag::decode(r)?;
194 match tag {
195 Tag::Closing { tag_num: closing } if closing == tag_num => return Ok(()),
196 Tag::Opening { tag_num: nested } => skip_constructed(r, nested)?,
197 Tag::Application { len, .. } | Tag::Context { len, .. } => {
198 r.read_exact(len as usize)?;
199 }
200 Tag::Closing { .. } => return Err(DecodeError::InvalidTag),
201 }
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 #[cfg(feature = "alloc")]
208 use super::GetEventInformationAck;
209 use super::{GetEventInformationRequest, SERVICE_GET_EVENT_INFORMATION};
210 #[cfg(feature = "alloc")]
211 use crate::apdu::ComplexAckHeader;
212 use crate::apdu::ConfirmedRequestHeader;
213 #[cfg(feature = "alloc")]
214 use crate::encoding::primitives::{encode_ctx_object_id, encode_ctx_unsigned};
215 #[cfg(feature = "alloc")]
216 use crate::encoding::tag::Tag;
217 use crate::encoding::{reader::Reader, writer::Writer};
218 #[cfg(feature = "alloc")]
219 use crate::types::{ObjectId, ObjectType};
220
221 #[test]
222 fn encode_get_event_information_request() {
223 let req = GetEventInformationRequest {
224 last_received_object_id: None,
225 invoke_id: 9,
226 };
227 let mut buf = [0u8; 32];
228 let mut w = Writer::new(&mut buf);
229 req.encode(&mut w).unwrap();
230
231 let mut r = Reader::new(w.as_written());
232 let hdr = ConfirmedRequestHeader::decode(&mut r).unwrap();
233 assert_eq!(hdr.service_choice, SERVICE_GET_EVENT_INFORMATION);
234 }
235
236 #[cfg(feature = "alloc")]
237 #[test]
238 fn decode_get_event_information_ack() {
239 let mut buf = [0u8; 256];
240 let mut w = Writer::new(&mut buf);
241 ComplexAckHeader {
242 segmented: false,
243 more_follows: false,
244 invoke_id: 9,
245 sequence_number: None,
246 proposed_window_size: None,
247 service_choice: SERVICE_GET_EVENT_INFORMATION,
248 }
249 .encode(&mut w)
250 .unwrap();
251 Tag::Opening { tag_num: 0 }.encode(&mut w).unwrap();
252 encode_ctx_object_id(&mut w, 0, ObjectId::new(ObjectType::AnalogInput, 1).raw()).unwrap();
253 encode_ctx_unsigned(&mut w, 1, 2).unwrap();
254 Tag::Context { tag_num: 2, len: 2 }.encode(&mut w).unwrap();
255 w.write_u8(5).unwrap();
256 w.write_u8(0b1110_0000).unwrap();
257 Tag::Opening { tag_num: 3 }.encode(&mut w).unwrap();
258 Tag::Opening { tag_num: 0 }.encode(&mut w).unwrap();
259 encode_ctx_unsigned(&mut w, 1, 42).unwrap();
260 Tag::Closing { tag_num: 0 }.encode(&mut w).unwrap();
261 Tag::Closing { tag_num: 3 }.encode(&mut w).unwrap();
262 encode_ctx_unsigned(&mut w, 4, 0).unwrap();
263 Tag::Context { tag_num: 5, len: 2 }.encode(&mut w).unwrap();
264 w.write_u8(5).unwrap();
265 w.write_u8(0b1110_0000).unwrap();
266 Tag::Opening { tag_num: 6 }.encode(&mut w).unwrap();
267 encode_ctx_unsigned(&mut w, 0, 1).unwrap();
268 encode_ctx_unsigned(&mut w, 1, 2).unwrap();
269 encode_ctx_unsigned(&mut w, 2, 3).unwrap();
270 Tag::Closing { tag_num: 6 }.encode(&mut w).unwrap();
271 Tag::Closing { tag_num: 0 }.encode(&mut w).unwrap();
272 Tag::Context { tag_num: 1, len: 1 }.encode(&mut w).unwrap();
273 w.write_u8(0).unwrap();
274
275 let mut r = Reader::new(w.as_written());
276 let _ack_hdr = ComplexAckHeader::decode(&mut r).unwrap();
277 let ack = GetEventInformationAck::decode_after_header(&mut r).unwrap();
278 assert_eq!(ack.summaries.len(), 1);
279 assert!(!ack.more_events);
280 }
281}