semi_e37/generic.rs
1//! # GENERIC SERVICES
2//!
3//! Defines the full functionality of the [HSMS] protocol without modification
4//! by any subsidiary standards. This involves the sending of messages of
5//! particular types and at particular times as allowed by the protocol.
6//!
7//! ---------------------------------------------------------------------------
8//!
9//! To use the [Generic Services]:
10//!
11//! - Build [Message]s which use a [Message ID] and [Message Contents]:
12//! - [Data Message]
13//! - [Select.req]
14//! - [Select.rsp]
15//! - [Deselect.req]
16//! - [Deselect.rsp]
17//! - [Linktest.req]
18//! - [Linktest.rsp]
19//! - [Reject.req]
20//! - [Separate.req]
21//! - Create an [Client] by providing the [New Client] function with
22//! [Parameter Settings].
23//! - Manage the [Connection State] with the [Connect Procedure] and
24//! [Disconnect Procedure].
25//! - Manage the [Selection State] with the [Select Procedure],
26//! [Deselect Procedure], and [Separate Procedure].
27//! - Receive [Data Message]s with the hook provided by the
28//! [Connect Procedure].
29//! - Test connection integrity with the [Linktest Procedure].
30//! - Send [Data Message]s with the [Data Procedure].
31//! - Send [Reject.req] messages [Reject Procedure].
32//!
33//! [HSMS]: crate
34//! [Generic Services]: crate::generic
35//! [Client]: Client
36//! [New Client]: Client::new
37//! [Connect Procedure]: Client::connect
38//! [Disconnect Procedure]: Client::disconnect
39//! [Select Procedure]: Client::select
40//! [Deselect Procedure]: Client::deselect
41//! [Separate Procedure]: Client::separate
42//! [Linktest Procedure]: Client::linktest
43//! [Data Procedure]: Client::data
44//! [Reject Procedure]: Client::reject
45//! [Message]: Message
46//! [Message ID]: MessageID
47//! [Message Contents]: MessageContents
48//! [Data Message]: MessageContents::DataMessage
49//! [Select.req]: MessageContents::SelectRequest
50//! [Select.rsp]: MessageContents::SelectResponse
51//! [Deselect.req]: MessageContents::DeselectRequest
52//! [Deselect.rsp]: MessageContents::DeselectResponse
53//! [Linktest.req]: MessageContents::LinktestRequest
54//! [Linktest.rsp]: MessageContents::LinktestResponse
55//! [Reject.req]: MessageContents::RejectRequest
56//! [Separate.req]: MessageContents::SeparateRequest
57//! [Connection State]: crate::primitive::ConnectionState
58//! [Selection State]: SelectionState
59//! [Parameter Settings]: ParameterSettings
60
61use std::{
62 collections::HashMap,
63 io::{
64 Error,
65 ErrorKind,
66 },
67 net::SocketAddr,
68 ops::{
69 Deref,
70 DerefMut,
71 },
72 sync::{
73 atomic::Ordering::Relaxed,
74 Arc,
75 Mutex,
76 mpsc::{
77 channel,
78 Receiver,
79 Sender,
80 },
81 },
82 thread::{
83 self,
84 JoinHandle,
85 },
86 time::Duration,
87};
88use atomic::Atomic;
89use bytemuck::NoUninit;
90use oneshot::Sender as SendOnce;
91use crate::{
92 PresentationType,
93 primitive,
94};
95
96pub use crate::primitive::ConnectionMode;
97
98/// ## CLIENT
99///
100/// Encapsulates the full functionality of the [HSMS] protocol without
101/// reference to any subsidiary standards, known as the [Generic Services].
102///
103/// [HSMS]: crate
104/// [Generic Services]: crate::generic
105pub struct Client {
106 parameter_settings: ParameterSettings,
107 primitive_client: Arc<primitive::Client>,
108 selection_state: Atomic<SelectionState>,
109 selection_mutex: Mutex<()>,
110 outbox: Mutex<HashMap<u32, (MessageID, SendOnce<Option<Message>>)>>,
111 system: Mutex<u32>,
112}
113
114/// ## CONNECTION PROCEDURES
115/// **Based on SEMI E37-1109§6.3-6.5**
116///
117/// Encapsulates the parts of the [Client]'s functionality dealing with
118/// establishing and breaking a TCP/IP connection.
119///
120/// - [New Client]
121/// - [Connect Procedure]
122/// - [Disconnect Procedure]
123///
124/// [Client]: Client
125/// [New Client]: Client::new
126/// [Connect Procedure]: Client::connect
127/// [Disconnect Procedure]: Client::disconnect
128impl Client {
129 /// ### NEW CLIENT
130 ///
131 ///
132 /// Creates a [Client] in the [NOT CONNECTED] state, ready to initiate the
133 /// [Connect Procedure].
134 ///
135 /// [Client]: Client
136 /// [Connect Procedure]: Client::connect
137 /// [NOT CONNECTED]: primitive::ConnectionState::NotConnected
138 pub fn new(
139 parameter_settings: ParameterSettings
140 ) -> Arc<Self> {
141 Arc::new(Client {
142 parameter_settings,
143 primitive_client: primitive::Client::new(),
144 selection_state: Default::default(),
145 selection_mutex: Default::default(),
146 outbox: Default::default(),
147 system: Default::default(),
148 })
149 }
150
151 /// ### CONNECT PROCEDURE
152 /// **Based on SEMI E37-1109§6.3.4-6.3.7**
153 ///
154 /// Connects the [Client] to the Remote Entity.
155 ///
156 /// -------------------------------------------------------------------------
157 ///
158 /// The [Connection State] must be in the [NOT CONNECTED] state to use this
159 /// procedure.
160 ///
161 /// -------------------------------------------------------------------------
162 ///
163 /// The [Connect Procedure] has two different behaviors based on the
164 /// [Connection Mode] provided to it:
165 /// - [PASSIVE] - The socket address of the Local Entity must be provided,
166 /// and the [Client] listens for and accepts the [Connect Procedure] when
167 /// initiated by the Remote Entity.
168 /// - [ACTIVE] - The socket address of the Remote Entity must be provided,
169 /// and the [Client] initiates the [Connect Procedure] and waits up to the
170 /// time specified by [T5] for the Remote Entity to respond.
171 ///
172 /// -------------------------------------------------------------------------
173 ///
174 /// Upon completion of the [Connect Procedure], the [T8] parameter is set as
175 /// the TCP stream's read and write timeout, and the [CONNECTED] state is
176 /// entered.
177 ///
178 /// [Connection State]: primitive::ConnectionState
179 /// [NOT CONNECTED]: primitive::ConnectionState::NotConnected
180 /// [CONNECTED]: primitive::ConnectionState::Connected
181 /// [Connection Mode]: primitive::ConnectionMode
182 /// [PASSIVE]: primitive::ConnectionMode::Passive
183 /// [ACTIVE]: primitive::ConnectionMode::Active
184 /// [Client]: Client
185 /// [Connect Procedure]: Client::connect
186 /// [T5]: ParameterSettings::t5
187 /// [T8]: ParameterSettings::t8
188 pub fn connect(
189 self: &Arc<Self>,
190 entity: &str,
191 ) -> Result<(SocketAddr, Receiver<(MessageID, semi_e5::Message)>), Error> {
192 // Connect Primitive Client
193 let (socket, rx_receiver) = self.primitive_client.connect(entity, self.parameter_settings.connect_mode, self.parameter_settings.t5, self.parameter_settings.t8)?;
194 // Create Channel
195 let (data_sender, data_receiver) = channel::<(MessageID, semi_e5::Message)>();
196 // Start RX Thread
197 let clone: Arc<Client> = self.clone();
198 thread::spawn(move || {clone.receive(rx_receiver, data_sender)});
199 // Finish
200 Ok((socket, data_receiver))
201 }
202
203 /// ### DISCONNECT PROCEDURE
204 /// **Based on SEMI E37-1109§6.4-6.5**
205 ///
206 /// Disconnects the [Client] from the Remote Entity.
207 ///
208 /// -------------------------------------------------------------------------
209 ///
210 /// The [Connection State] must be in the [CONNECTED] state to use this
211 /// procedure.
212 ///
213 /// -------------------------------------------------------------------------
214 ///
215 /// Upon completion of the [Disconnect Procedure], the [NOT CONNECTED] state
216 /// is entered.
217 ///
218 /// [Connection State]: primitive::ConnectionState
219 /// [NOT CONNECTED]: primitive::ConnectionState::NotConnected
220 /// [CONNECTED]: primitive::ConnectionState::Connected
221 /// [Client]: Client
222 /// [Disconnect Procedure]: Client::disconnect
223 pub fn disconnect(
224 self: &Arc<Self>,
225 ) -> Result<(), Error> {
226 // TO: NOT CONNECTED
227 let result: Result<(), Error> = self.primitive_client.disconnect();
228 // TO: NOT SELECTED
229 let _guard = self.selection_mutex.lock().unwrap();
230 if let SelectionState::Selected = self.selection_state.load(Relaxed) {
231 self.selection_state.store(SelectionState::NotSelected, Relaxed);
232 }
233 // Finish
234 result
235 }
236}
237
238/// ## MESSAGE EXCHANGE PROCEDURES
239/// **Based on SEMI E37-1109§7**
240///
241/// Encapsulates the parts of the [Client]'s functionality dealing with
242/// exchanging [Message]s.
243///
244/// - [Data Procedure] - [Data Message]s
245/// - [Select Procedure] - [Select.req] and [Select.rsp]
246/// - [Deselect Procedure] - [Deselect.req] and [Deselect.rsp]
247/// - [Linktest Procedure] - [Linktest.req] and [Linktest.rsp]
248/// - [Separate Procedure] - [Separate.req]
249/// - [Reject Procedure] - [Reject.req]
250///
251/// [Message]: Message
252/// [Client]: Client
253/// [Select Procedure]: Client::select
254/// [Data Procedure]: Client::data
255/// [Deselect Procedure]: Client::deselect
256/// [Linktest Procedure]: Client::linktest
257/// [Separate Procedure]: Client::separate
258/// [Reject Procedure]: Client::reject
259/// [Data Message]: MessageContents::DataMessage
260/// [Select.req]: MessageContents::SelectRequest
261/// [Select.rsp]: MessageContents::SelectResponse
262/// [Deselect.req]: MessageContents::DeselectRequest
263/// [Deselect.rsp]: MessageContents::DeselectResponse
264/// [Linktest.req]: MessageContents::LinktestRequest
265/// [Linktest.rsp]: MessageContents::LinktestResponse
266/// [Reject.req]: MessageContents::RejectRequest
267/// [Separate.req]: MessageContents::SeparateRequest
268impl Client {
269 /// ### RECEIVE PROCEDURE
270 ///
271 /// An [Client] in the [CONNECTED] state will automatically receive
272 /// [Message]s and respond based on their [Message Contents] and the current
273 /// [Selection State].
274 ///
275 /// -------------------------------------------------------------------------
276 ///
277 /// #### [Data Message]
278 ///
279 /// - [NOT SELECTED] - The [Client] will respond by transmitting a
280 /// [Reject.req] message, rejecting the [HSMS Data Procedure] and
281 /// completing the [HSMS Reject Procedure].
282 /// - [SELECTED], Primary [Data Message] - The [Client] will send the
283 /// [Data Message] to the hook provided by the [Connect Procedure].
284 /// - [SELECTED], Response [Data Message] - The [Client] will respond by
285 /// correllating the message to a previously sent Primary [Data Message],
286 /// finishing a previously initiated [Data Procedure] if successful,
287 /// or if unsuccessful by transmitting a [Reject.req] message, rejecting
288 /// the [Data Procedure] and completing the [Reject Procedure].
289 ///
290 /// -------------------------------------------------------------------------
291 ///
292 /// #### [Select.req]:
293 ///
294 /// - [NOT SELECTED] - The [Client] will respond with a [Select.rsp]
295 /// accepting and completing the [Select Procedure].
296 /// - [SELECTED] - The [Client] will respond with a [Select.rsp] message
297 /// rejecting the [Select Procedure].
298 ///
299 /// -------------------------------------------------------------------------
300 ///
301 /// #### [Select.rsp]:
302 ///
303 /// - [NOT SELECTED] - The [Client] will complete the [Select Procedure].
304 /// - [SELECTED] - The [Client] will respond with a [Reject.req] message,
305 /// completing the [Reject Procedure].
306 ///
307 /// -------------------------------------------------------------------------
308 ///
309 /// #### [Deselect.req]:
310 ///
311 /// - Not yet implemented.
312 ///
313 /// -------------------------------------------------------------------------
314 ///
315 /// #### [Deselect.rsp]:
316 ///
317 /// - Not yet implemented.
318 ///
319 /// -------------------------------------------------------------------------
320 ///
321 /// #### [Linktest.req]:
322 ///
323 /// - The [Client] will respond with a [Linktest.rsp], completing the
324 /// [Linktest Procedure].
325 ///
326 /// -------------------------------------------------------------------------
327 ///
328 /// #### [Linktest.rsp]:
329 ///
330 /// - The [Client] will respond by correllating the message to a previously
331 /// sent [Linktest.req] message, finishing a previously initiated
332 /// [Linktest Procedure] if successful, or if unsuccessful by transmitting
333 /// a [Reject.req] message, completing the [Reject Procedure].
334 ///
335 /// -------------------------------------------------------------------------
336 ///
337 /// #### [Reject.req]:
338 ///
339 /// - Not yet implemented.
340 ///
341 /// -------------------------------------------------------------------------
342 ///
343 /// #### [Separate.req]:
344 ///
345 /// - [NOT SELECTED] - The [Client] will not do anything.
346 /// - [SELECTED] - The [Client] will complete the [Separate Procedure].
347 ///
348 /// -------------------------------------------------------------------------
349 ///
350 /// #### Unknown [Primitive Message]:
351 ///
352 /// - The [Client] will respond by transmitting a [Reject.req] message,
353 /// completing the [Reject Procedure].
354 ///
355 /// [Primitive Message]: primitive::Message
356 /// [Connection State]: primitive::ConnectionState
357 /// [NOT CONNECTED]: primitive::ConnectionState::NotConnected
358 /// [CONNECTED]: primitive::ConnectionState::Connected
359 /// [Message]: Message
360 /// [Message Contents]: MessageContents
361 /// [Data Message]: MessageContents::DataMessage
362 /// [Select.req]: MessageContents::SelectRequest
363 /// [Select.rsp]: MessageContents::SelectResponse
364 /// [Deselect.req]: MessageContents::DeselectRequest
365 /// [Deselect.rsp]: MessageContents::DeselectResponse
366 /// [Linktest.req]: MessageContents::LinktestRequest
367 /// [Linktest.rsp]: MessageContents::LinktestResponse
368 /// [Reject.req]: MessageContents::RejectRequest
369 /// [Separate.req]: MessageContents::SeparateRequest
370 /// [Client]: Client
371 /// [Connect Procedure]: Client::connect
372 /// [Select Procedure]: Client::select
373 /// [Data Procedure]: Client::data
374 /// [Deselect Procedure]: Client::deselect
375 /// [Linktest Procedure]: Client::linktest
376 /// [Separate Procedure]: Client::separate
377 /// [Reject Procedure]: Client::reject
378 /// [Selection State]: SelectionState
379 /// [NOT SELECTED]: SelectionState::NotSelected
380 /// [SELECTED]: SelectionState::Selected
381 /// [SELECT INITIATED]: SelectionState::SelectInitiated
382 /// [DESELECT INITIATED]: SelectionState::DeselectInitiated
383 fn receive(
384 self: &Arc<Self>,
385 rx_receiver: Receiver<primitive::Message>,
386 rx_sender: Sender<(MessageID, semi_e5::Message)>,
387 ) {
388 for primitive_message in rx_receiver {
389 let primitive_header = primitive_message.header;
390 match Message::try_from(primitive_message) {
391 Ok(rx_message) => match rx_message.contents {
392 // RX: Data Message
393 MessageContents::DataMessage(data) => {
394 match self.selection_state.load(Relaxed) {
395 // IS: SELECTED
396 SelectionState::Selected => {
397 // RX: Primary Data Message
398 if data.function % 2 == 1 {
399 // INBOX: New Transaction
400 if rx_sender.send((rx_message.id, data)).is_err() {break}
401 }
402 // RX: Response Data Message
403 else {
404 // OUTBOX: Find Transaction
405 let mut outbox = self.outbox.lock().unwrap();
406 let mut optional_transaction: Option<u32> = None;
407 for (outbox_id, (message_id, _)) in outbox.deref() {
408 if *message_id == rx_message.id {
409 optional_transaction = Some(*outbox_id);
410 break;
411 }
412 }
413 // OUTBOX: Transaction Found
414 if let Some(transaction) = optional_transaction {
415 // OUTBOX: Complete Transaction
416 let (_, sender) = outbox.deref_mut().remove(&transaction).unwrap();
417 sender.send(Some(Message{
418 id: rx_message.id,
419 contents: MessageContents::DataMessage(data),
420 })).unwrap();
421 }
422 // OUTBOX: Transaction Not Found
423 else {
424 // TX: Reject.req
425 if self.primitive_client.transmit(Message {
426 id: rx_message.id,
427 contents: MessageContents::RejectRequest(0, RejectReason::TransactionNotOpen as u8)
428 }.into()).is_err() {break}
429 }
430 }
431 },
432 // IS: NOT SELECTED
433 _ => {
434 // TX: Reject.req
435 if self.primitive_client.transmit(Message {
436 id: rx_message.id,
437 contents: MessageContents::RejectRequest(0, RejectReason::EntityNotSelected as u8)
438 }.into()).is_err() {break}
439 },
440 }
441 },
442 // RX: Select.req
443 MessageContents::SelectRequest => {
444 match self.selection_mutex.try_lock() {
445 Ok(_guard) => {
446 match self.selection_state.load(Relaxed) {
447 // IS: NOT SELECTED
448 SelectionState::NotSelected => {
449 // TX: Select.rsp Success
450 if self.primitive_client.transmit(Message {
451 id: rx_message.id,
452 contents: MessageContents::SelectResponse(SelectStatus::Success as u8),
453 }.into()).is_err() {break};
454 // TO: SELECTED
455 self.selection_state.store(SelectionState::Selected, Relaxed);
456 },
457 // IS: SELECTED
458 SelectionState::Selected => {
459 // TX: Select.rsp Already Active
460 if self.primitive_client.transmit(Message {
461 id: rx_message.id,
462 contents: MessageContents::SelectResponse(SelectStatus::AlreadyActive as u8),
463 }.into()).is_err() {break};
464 },
465 // IS: SELECT INITIATED
466 // TODO: Find way to reimplement this under the current scheme.
467 /*SelectionState::SelectInitiated(session_id) => {
468 // RX: Valid Simultaneous Select
469 if rx_message.id.session == *session_id {
470 // TX: Select.rsp Success
471 if self.primitive_client.transmit(HsmsMessage {
472 id: rx_message.id,
473 contents: HsmsMessageContents::SelectResponse(SelectStatus::Success as u8),
474 }.into()).is_err() {break};
475 }
476 // RX: Invalid Simultaneous Select
477 else {
478 // TX: Select.rsp Already Active
479 if self.primitive_client.transmit(HsmsMessage {
480 id: rx_message.id,
481 contents: HsmsMessageContents::SelectResponse(SelectStatus::AlreadyActive as u8),
482 }.into()).is_err() {break};
483 }
484 },*/
485 }
486 },
487 Err(_) => {
488 // Todo: probably appropriate to put something here, maybe to do with the simulatenous select procedure?
489 },
490 }
491 },
492 // RX: Select.rsp
493 MessageContents::SelectResponse(select_status) => {
494 // OUTBOX: Find Transaction
495 let mut outbox = self.outbox.lock().unwrap();
496 let mut optional_transaction: Option<u32> = None;
497 for (outbox_id, (message_id, _)) in outbox.deref() {
498 if *message_id == rx_message.id {
499 optional_transaction = Some(*outbox_id);
500 break;
501 }
502 }
503 // OUTBOX: Transaction Found
504 if let Some(transaction) = optional_transaction {
505 // OUTBOX: Complete Transaction
506 let (_, sender) = outbox.deref_mut().remove(&transaction).unwrap();
507 sender.send(Some(Message{
508 id: rx_message.id,
509 contents: MessageContents::SelectResponse(select_status),
510 })).unwrap();
511 }
512 // OUTBOX: Transaction Not Found
513 else {
514 // TX: Reject.req
515 if self.primitive_client.transmit(Message {
516 id: rx_message.id,
517 contents: MessageContents::RejectRequest(0, RejectReason::TransactionNotOpen as u8)
518 }.into()).is_err() {break}
519 }
520 },
521 // RX: Deselect.req
522 MessageContents::DeselectRequest => {
523 todo!()
524 },
525 // RX: Deselect.rsp
526 MessageContents::DeselectResponse(_deselect_status) => {
527 todo!()
528 },
529 // RX: Linktest.req
530 MessageContents::LinktestRequest => {
531 // TX: Linktest.rsp
532 if self.primitive_client.transmit(Message{
533 id: rx_message.id,
534 contents: MessageContents::LinktestResponse,
535 }.into()).is_err() {break};
536 },
537 // RX: Linktest.rsp
538 MessageContents::LinktestResponse => {
539 // OUTBOX: Find Transaction
540 let mut outbox = self.outbox.lock().unwrap();
541 let mut optional_transaction: Option<u32> = None;
542 for (outbox_id, (message_id, _)) in outbox.deref() {
543 if *message_id == rx_message.id {
544 optional_transaction = Some(*outbox_id);
545 break;
546 }
547 }
548 // OUTBOX: Transaction Found
549 if let Some(transaction) = optional_transaction {
550 // OUTBOX: Complete Transaction
551 let (_, sender) = outbox.deref_mut().remove(&transaction).unwrap();
552 sender.send(Some(rx_message)).unwrap();
553 }
554 // OUTBOX: Transaction Not Found
555 else {
556 // TX: Reject.req
557 if self.primitive_client.transmit(Message {
558 id: rx_message.id,
559 contents: MessageContents::RejectRequest(SessionType::LinktestRequest as u8, RejectReason::TransactionNotOpen as u8),
560 }.into()).is_err() {break}
561 }
562 },
563 // RX: Reject.req
564 MessageContents::RejectRequest(_message_type, _reason_code) => {
565 // OUTBOX: Find Transaction
566 let mut outbox = self.outbox.lock().unwrap();
567 let mut optional_transaction: Option<u32> = None;
568 for (outbox_id, (message_id, _)) in outbox.deref() {
569 if *message_id == rx_message.id {
570 optional_transaction = Some(*outbox_id);
571 break;
572 }
573 }
574 // OUTBOX: Transaction Found
575 if let Some(transaction) = optional_transaction {
576 // OUTBOX: Reject Transaction
577 let (_, sender) = outbox.deref_mut().remove(&transaction).unwrap();
578 sender.send(None).unwrap();
579 }
580 },
581 // RX: Separate.req
582 MessageContents::SeparateRequest => {
583 let _guard: std::sync::MutexGuard<'_, ()> = self.selection_mutex.lock().unwrap();
584 if let SelectionState::Selected = self.selection_state.load(Relaxed) {
585 self.selection_state.store(SelectionState::NotSelected, Relaxed);
586 }
587 },
588 },
589 Err(reject_reason) => {
590 // TX: Reject.req
591 if self.primitive_client.transmit(Message {
592 id: MessageID {
593 session: primitive_header.session_id,
594 system: primitive_header.system,
595 },
596 contents: MessageContents::RejectRequest(match reject_reason {
597 RejectReason::UnsupportedPresentationType => primitive_header.presentation_type,
598 _ => primitive_header.session_type,
599 }, reject_reason as u8),
600 }.into()).is_err() {break}
601 },
602 }
603 }
604 // OUTBOX: CLEAR
605 for (_, (_, sender)) in self.outbox.lock().unwrap().deref_mut().drain() {
606 let _ = sender.send(None);
607 }
608 }
609
610 /// ### TRANSMIT PROCEDURE
611 /// **Based on SEMI E37-1109§7.2**
612 ///
613 /// Serializes a [Message] and transmits it over the TCP/IP connection.
614 /// If a reply is expected, this function will then wait up to the time
615 /// specified for the requisite response [Message] to be recieved.
616 ///
617 /// -------------------------------------------------------------------------
618 ///
619 /// The [Connection State] must be in the [CONNECTED] state to use this
620 /// procedure.
621 ///
622 /// [Message]: Message
623 /// [Connection State]: primitive::ConnectionState
624 /// [NOT CONNECTED]: primitive::ConnectionState::NotConnected
625 /// [CONNECTED]: primitive::ConnectionState::Connected
626 fn transmit(
627 self: &Arc<Self>,
628 message: Message,
629 reply_expected: bool,
630 delay: Duration,
631 ) -> Result<Option<Message>, Error> {
632 let (receiver, system) = {
633 // OUTBOX: LOCK
634 let outbox_lock = if reply_expected {Some(self.deref().outbox.lock().unwrap())} else {None};
635 // TX
636 let message_id = message.id;
637 match self.primitive_client.transmit(message.into()) {
638 // TX: Success
639 Ok(()) => {
640 match outbox_lock {
641 // REPLY NOT EXPECTED: Finish
642 None => return Ok(None),
643 // REPLY EXPECTED
644 Some(mut outbox) => {
645 // OUTBOX: Create Transaction
646 let (sender, receiver) = oneshot::channel::<Option<Message>>();
647 let system = {
648 let mut system_guard = self.deref().system.lock().unwrap();
649 let system_counter = system_guard.deref_mut();
650 let system = *system_counter;
651 *system_counter += 1;
652 system
653 };
654 outbox.deref_mut().insert(system, (message_id, sender));
655 (receiver, system)
656 }
657 }
658 },
659 // TX: Failure
660 Err(error) => {
661 // TO: NOT CONNECTED, NOT SELECTED
662 let _ = self.disconnect();
663 return Err(error)
664 },
665 }
666 };
667 // RX
668 let rx_result = receiver.recv_timeout(delay);
669 // OUTBOX: Remove Transaction
670 let mut outbox = self.outbox.lock().unwrap();
671 outbox.deref_mut().remove(&system);
672 match rx_result {
673 // RX: Success
674 Ok(rx_message) => return Ok(rx_message),
675 // RX: Failure
676 Err(_e) => return Ok(None),
677 }
678 }
679
680 /// ### DATA PROCEDURE
681 /// **Based on SEMI E37-1109§7.5-7.6**
682 ///
683 /// Asks the [Client] to initiate the [Data Procedure] by transmitting a
684 /// [Data Message] and waiting for the corresponding response to be received
685 /// if it is necessary to do so.
686 ///
687 /// -------------------------------------------------------------------------
688 ///
689 /// The [Connection State] must be in the [CONNECTED] state and the
690 /// [Selection State] must be in the [SELECTED] state to use this procedure.
691 ///
692 /// When a Response [Data Message] is necessary, the [Client] will wait
693 /// to receive it for the amount of time specified by [T3] before it will
694 /// consider it a communications failure and initiate the
695 /// [Disconnect Procedure].
696 ///
697 /// -------------------------------------------------------------------------
698 ///
699 /// Although not done within this function, a [Client] in the [CONNECTED]
700 /// state will automatically respond to having received a [Data Message]
701 /// based on its contents and the current [Selection State]:
702 /// - [NOT SELECTED] - The [Client] will respond by transmitting a
703 /// [Reject.req] message, rejecting the [HSMS Data Procedure] and
704 /// completing the [HSMS Reject Procedure].
705 /// - [SELECTED], Primary [Data Message] - The [Client] will send the
706 /// [Data Message] to the hook provided by the [Connect Procedure].
707 /// - [SELECTED], Response [Data Message] - The [Client] will respond by
708 /// correllating the message to a previously sent Primary [Data Message],
709 /// finishing a previously initiated [Data Procedure] if successful,
710 /// or if unsuccessful by transmitting a [Reject.req] message, rejecting
711 /// the [Data Procedure] and completing the [Reject Procedure].
712 ///
713 /// [Connection State]: primitive::ConnectionState
714 /// [CONNECTED]: primitive::ConnectionState::Connected
715 /// [Selection State]: SelectionState
716 /// [NOT SELECTED]: SelectionState::NotSelected
717 /// [SELECTED]: SelectionState::Selected
718 /// [T3]: ParameterSettings::t3
719 /// [Client]: Client
720 /// [Connect Procedure]: Client::connect
721 /// [Disconnect Procedure]: Client::disconnect
722 /// [Data Procedure]: Client::data
723 /// [Reject Procedure]: Client::reject
724 /// [Data Message]: MessageContents::DataMessage
725 /// [Reject.req]: MessageContents::RejectRequest
726 pub fn data(
727 self: &Arc<Self>,
728 id: MessageID,
729 message: semi_e5::Message,
730 ) -> JoinHandle<Result<Option<semi_e5::Message>, Error>> {
731 let clone: Arc<Client> = self.clone();
732 let reply_expected: bool = message.function % 2 == 1 && message.w;
733 thread::spawn(move || {
734 match clone.selection_state.load(Relaxed) {
735 // IS: NOT SELECTED
736 SelectionState::NotSelected => return Err(Error::from(ErrorKind::AlreadyExists)),
737 // IS: SELECTED
738 SelectionState::Selected => {
739 // TX: Data Message
740 match clone.transmit(
741 Message {
742 id,
743 contents: MessageContents::DataMessage(message),
744 },
745 reply_expected,
746 clone.parameter_settings.t3,
747 )?{
748 // RX: Response
749 Some(rx_message) => {
750 match rx_message.contents {
751 // RX: Data
752 MessageContents::DataMessage(data_message) => return Ok(Some(data_message)),
753 // RX: Reject.req
754 MessageContents::RejectRequest(_type, _reason) => return Err(Error::from(ErrorKind::PermissionDenied)),
755 // RX: Unknown
756 _ => return Err(Error::from(ErrorKind::InvalidData)),
757 }
758 },
759 // RX: No Response
760 None => {
761 // REPLY EXPECTED
762 if reply_expected {
763 // TO: NOT CONNECTED
764 clone.disconnect()?;
765 Err(Error::from(ErrorKind::ConnectionAborted))
766 // TODO: HSMS-SS does NOT disconnect when the Data Procedure fails, may require this behavior to be optional.
767 }
768 // REPLY NOT EXPECTED
769 else {
770 return Ok(None);
771 }
772 },
773 }
774 },
775 }
776 })
777 }
778
779 /// ### SELECT PROCEDURE
780 /// **Based on SEMI E37-1109§7.3-7.4**
781 ///
782 /// Asks the [Client] to initiate the [Select Procedure] by transmitting a
783 /// [Select.req] message and waiting for the corresponding [Select.rsp]
784 /// message to be received.
785 ///
786 /// -------------------------------------------------------------------------
787 ///
788 /// The [Connection State] must be in the [CONNECTED] state and the
789 /// [Selection State] must be in the [NOT SELECTED] state to use this
790 /// procedure.
791 ///
792 /// The [Client] will wait to receive the [Select.rsp] for the amount
793 /// of time specified by [T6] before it will consider it a communications
794 /// failure and initiate the [Disconnect Procedure].
795 ///
796 /// -------------------------------------------------------------------------
797 ///
798 /// Although not done within this function, a [Client] in the [CONNECTED]
799 /// state will automatically respond to having received a [Select.req]
800 /// message based on its current [Selection State]:
801 /// - [NOT SELECTED] - The [Client] will respond with a [Select.rsp]
802 /// accepting and completing the [Select Procedure].
803 /// - [SELECTED] - The [Client] will respond with a [Select.rsp] message
804 /// rejecting the [Select Procedure].
805 ///
806 /// -------------------------------------------------------------------------
807 ///
808 /// Upon completion of the [Select Procedure], the [SELECTED] state
809 /// is entered.
810 ///
811 /// [Connection State]: primitive::ConnectionState
812 /// [CONNECTED]: primitive::ConnectionState::Connected
813 /// [Selection State]: SelectionState
814 /// [NOT SELECTED]: SelectionState::NotSelected
815 /// [SELECTED]: SelectionState::Selected
816 /// [T6]: ParameterSettings::t6
817 /// [Client]: Client
818 /// [Disconnect Procedure]: Client::disconnect
819 /// [Select Procedure]: Client::select
820 /// [Select.req]: MessageContents::SelectRequest
821 /// [Select.rsp]: MessageContents::SelectResponse
822 pub fn select(
823 self: &Arc<Self>,
824 id: MessageID,
825 ) -> JoinHandle<Result<(), Error>> {
826 let clone: Arc<Client> = self.clone();
827 thread::spawn(move || {
828 'disconnect: {
829 let _guard = clone.selection_mutex.lock();
830 match clone.selection_state.load(Relaxed) {
831 SelectionState::NotSelected => {
832 // TX: Select.req
833 match clone.transmit(
834 Message {
835 id,
836 contents: MessageContents::SelectRequest,
837 },
838 true,
839 clone.parameter_settings.t6,
840 )?{
841 // RX: Response
842 Some(rx_message) => {
843 match rx_message.contents {
844 // RX: Select.rsp
845 MessageContents::SelectResponse(select_status) => {
846 // RX: Select.rsp Success
847 if select_status == SelectStatus::Success as u8 {
848 // TO: SELECTED
849 clone.selection_state.store(SelectionState::Selected, Relaxed);
850 return Ok(())
851 }
852 // RX: Select.rsp Failure
853 else {
854 return Err(Error::from(ErrorKind::PermissionDenied))
855 }
856 },
857 // RX: Reject.req
858 MessageContents::RejectRequest(_type, _reason) => return Err(Error::from(ErrorKind::PermissionDenied)),
859 // RX: Unknown
860 _ => return Err(Error::from(ErrorKind::InvalidData)),
861 }
862 },
863 // RX: No Response
864 None => {
865 // TO: NOT CONNECTED, NOT SELECTED
866 break 'disconnect;
867 },
868 }
869 },
870 SelectionState::Selected => {
871 return Err(Error::from(ErrorKind::AlreadyExists))
872 },
873 }
874 }
875 clone.disconnect()?;
876 Err(Error::from(ErrorKind::ConnectionAborted))
877 })
878 }
879
880 /// ### DESELECT PROCEDURE (TODO)
881 /// **Based on SEMI E37-1109§7.7**
882 ///
883 /// Asks the [Client] to initiate the [Deselect Procedure] by transmitting a
884 /// [Deselect.req] message and waiting for the corresponding [Deselect.rsp]
885 /// message to be received.
886 ///
887 /// -------------------------------------------------------------------------
888 ///
889 /// The [Connection State] must be in the [CONNECTED] state and the
890 /// [Selection State] must be in the [SELECTED] state to use this procedure.
891 ///
892 /// The [Client] will wait to receive the [Deselect.rsp] for the amount of
893 /// time specified by [T6] before it will consider it a communications
894 /// failure and initiate the [Disconnect Procedure].
895 ///
896 /// -------------------------------------------------------------------------
897 ///
898 /// Although not done within this function, a [Client] in the [CONNECTED]
899 /// state will automatically respond to having received a [Deselect.req]
900 /// message based on its current [Selection State]:
901 /// - [NOT SELECTED] - The [Client] will respond with a [Deselect.rsp]
902 /// rejecting the [Deselect Procedure].
903 /// - [SELECTED] - The [Client] will respond with a [Deselect.rsp] accepting
904 /// and completing the [Deselect Procedure].
905 ///
906 /// -------------------------------------------------------------------------
907 ///
908 /// Upon completion of the [Deselect Procedure], the [NOT SELECTED] state is
909 /// entered.
910 ///
911 /// [Connection State]: primitive::ConnectionState
912 /// [CONNECTED]: primitive::ConnectionState::Connected
913 /// [Selection State]: SelectionState
914 /// [NOT SELECTED]: SelectionState::NotSelected
915 /// [SELECTED]: SelectionState::Selected
916 /// [T6]: ParameterSettings::t6
917 /// [Client]: Client
918 /// [Disconnect Procedure]: Client::disconnect
919 /// [Deselect Procedure]: Client::deselect
920 /// [Deselect.req]: MessageContents::DeselectRequest
921 /// [Deselect.rsp]: MessageContents::DeselectResponse
922 pub fn deselect(
923 self: &Arc<Self>,
924 ) -> Result<(), Error> {
925 todo!()
926 }
927
928 /// ### LINKTEST PROCEDURE
929 /// **Based on SEMI E37-1109§7.8**
930 ///
931 /// Asks the [Client] to initiate the [Linktest Procedure] by transmitting a
932 /// [Linktest.req] message and waiting for the corresponding [Linktest.rsp]
933 /// message to be received.
934 ///
935 /// -------------------------------------------------------------------------
936 ///
937 /// The [Connection State] must be in the [CONNECTED] state to use this
938 /// procedure.
939 ///
940 /// The [Client] will wait to receive the [Linktest.rsp] for the amount of
941 /// time specified by [T6] before it will consider it a communications
942 /// failure and initiate the [Disconnect Procedure].
943 ///
944 /// -------------------------------------------------------------------------
945 ///
946 /// Although not done within this function, a [Client] in the
947 /// [CONNECTED] state will automatically respond to having received a
948 /// [Linktest.req] message:
949 /// - The [Client] will respond with a [Linktest.rsp], completing the
950 /// [Linktest Procedure].
951 ///
952 /// [Connection State]: primitive::ConnectionState
953 /// [CONNECTED]: primitive::ConnectionState::Connected
954 /// [Selection State]: SelectionState
955 /// [NOT SELECTED]: SelectionState::NotSelected
956 /// [SELECTED]: SelectionState::Selected
957 /// [T6]: ParameterSettings::t6
958 /// [Client]: Client
959 /// [Disconnect Procedure]: Client::disconnect
960 /// [Linktest Procedure]: Client::linktest
961 /// [Linktest.req]: MessageContents::LinktestRequest
962 /// [Linktest.rsp]: MessageContents::LinktestResponse
963 pub fn linktest(
964 self: &Arc<Self>,
965 system: u32,
966 ) -> JoinHandle<Result<(), Error>> {
967 let clone: Arc<Client> = self.clone();
968 thread::spawn(move || {
969 // TX: Linktest.req
970 match clone.transmit(
971 Message {
972 id: MessageID {
973 session: 0xFFFF,
974 system,
975 },
976 contents: MessageContents::LinktestRequest,
977 },
978 true,
979 clone.parameter_settings.t6,
980 )?{
981 // RX: Response
982 Some(rx_message) => {
983 match rx_message.contents {
984 // RX: Linktest.rsp
985 MessageContents::LinktestResponse => Ok(()),
986 // RX: Reject.req
987 MessageContents::RejectRequest(_type, _reason) => Err(Error::from(ErrorKind::PermissionDenied)),
988 // RX: Unknown
989 _ => Err(Error::from(ErrorKind::InvalidData)),
990 }
991 },
992 // RX: No Response
993 None => {
994 // TO: NOT CONNECTED, NOT SELECTED
995 clone.disconnect()?;
996 Err(Error::from(ErrorKind::ConnectionAborted))
997 },
998 }
999 })
1000 }
1001
1002 /// ### SEPARATE PROCEDURE
1003 /// **Based on SEMI E37-1109§7.9**
1004 ///
1005 /// Asks the [Client] to initiate the [Separate Procedure] by transmitting a
1006 /// [Separate.req] message.
1007 ///
1008 /// -------------------------------------------------------------------------
1009 ///
1010 /// The [Connection State] must be in the [CONNECTED] state and the
1011 /// [Selection State] must be in the [SELECTED] state to use this procedure.
1012 ///
1013 /// -------------------------------------------------------------------------
1014 ///
1015 /// Although not done within this function, a [Client] in the [CONNECTED]
1016 /// state will automatically respond to having received a [Separate.req]
1017 /// message based on its current [Selection State]:
1018 /// - [NOT SELECTED] - The [Client] will not do anything.
1019 /// - [SELECTED] - The [Client] will complete the [Separate Procedure].
1020 ///
1021 /// -------------------------------------------------------------------------
1022 ///
1023 /// Upon completion of the [Separate Procedure], the [NOT SELECTED] state is
1024 /// entered.
1025 ///
1026 /// [Connection State]: primitive::ConnectionState
1027 /// [CONNECTED]: primitive::ConnectionState::Connected
1028 /// [Selection State]: SelectionState
1029 /// [NOT SELECTED]: SelectionState::NotSelected
1030 /// [SELECTED]: SelectionState::Selected
1031 /// [Client]: Client
1032 /// [Separate Procedure]: Client::separate
1033 /// [Separate.req]: MessageContents::SeparateRequest
1034 pub fn separate(
1035 self: &Arc<Self>,
1036 id: MessageID,
1037 ) -> JoinHandle<Result<(), Error>> {
1038 let clone: Arc<Client> = self.clone();
1039 thread::spawn(move || {
1040 let _guard = clone.selection_mutex.lock().unwrap();
1041 match clone.selection_state.load(Relaxed) {
1042 // IS: NOT SELECTED
1043 SelectionState::NotSelected => {
1044 Err(Error::from(ErrorKind::PermissionDenied))
1045 },
1046 // IS: SELECTED
1047 SelectionState::Selected => {
1048 // TX: Separate.req
1049 clone.transmit(
1050 Message {
1051 id,
1052 contents: MessageContents::SeparateRequest,
1053 },
1054 false,
1055 clone.parameter_settings.t6,
1056 )?;
1057 // TO: NOT SELECTED
1058 clone.selection_state.store(SelectionState::NotSelected, Relaxed);
1059 Ok(())
1060 },
1061 }
1062 })
1063 }
1064
1065 /// ### REJECT PROCEDURE (TODO)
1066 /// **Based on SEMI E37-1109§7.10**
1067 ///
1068 /// Asks the [Client] to initiate the [Reject Procedure] by transmitting a
1069 /// [Reject.req] message.
1070 ///
1071 /// -------------------------------------------------------------------------
1072 ///
1073 /// The [Connection State] must be in the [CONNECTED] state to use this
1074 /// procedure.
1075 ///
1076 /// -------------------------------------------------------------------------
1077 ///
1078 /// Although not done within this function, a [Client] in the [CONNECTED]
1079 /// state will automatically respond to having received a [Reject.req]:
1080 /// - Not yet implemented.
1081 ///
1082 /// [Connection State]: primitive::ConnectionState
1083 /// [CONNECTED]: primitive::ConnectionState::Connected
1084 /// [Selection State]: SelectionState
1085 /// [NOT SELECTED]: SelectionState::NotSelected
1086 /// [SELECTED]: SelectionState::Selected
1087 /// [Client]: Client
1088 /// [Reject Procedure]: Client::reject
1089 /// [Reject.req]: MessageContents::RejectRequest
1090 pub fn reject(
1091 self: &Arc<Self>,
1092 _reason: RejectReason,
1093 ) -> Result<(), Error> {
1094 todo!()
1095 }
1096}
1097
1098/// ## SELECTION STATE
1099/// **Based on SEMI E37-1109§5.5.2**
1100///
1101/// The [CONNECTED] state has two substates, [NOT SELECTED] and [SELECTED].
1102///
1103/// The [Client] moves between them based on whether it has established
1104/// a session with another entity according to the [Select Procedure],
1105/// [Deselect Procedure], and [Separate Procedure].
1106///
1107/// [CONNECTED]: primitive::ConnectionState::Connected
1108/// [NOT SELECTED]: SelectionState::NotSelected
1109/// [SELECTED]: SelectionState::Selected
1110/// [Client]: Client
1111/// [Select Procedure]: Client::select
1112/// [Deselect Procedure]: Client::deselect
1113/// [Separate Procedure]: Client::separate
1114#[derive(Clone, Copy, Debug, PartialEq, NoUninit)]
1115#[repr(u8)]
1116pub enum SelectionState {
1117 /// ### NOT SELECTED
1118 /// **Based on SEMI E37-1109§5.5.2.1**
1119 ///
1120 /// In this state, the [Client] is ready to initiate the [Select Procedure]
1121 /// but has either not yet done so, or has terminated a previous session.
1122 ///
1123 /// [Client]: Client
1124 /// [Select Procedure]: Client::select
1125 NotSelected,
1126
1127 /// ### SELECTED
1128 /// **Based on SEMI E37-1109§5.5.2.2**
1129 ///
1130 /// In this state, the [Client] has successfully initiated the
1131 /// [Select Procedure] and is able to send and receive [Data Message]s.
1132 ///
1133 /// [Client]: Client
1134 /// [Select Procedure]: Client::select
1135 /// [Data Message]: MessageContents::DataMessage
1136 Selected,
1137}
1138impl Default for SelectionState {
1139 /// ### DEFAULT SELECTION STATE
1140 /// **Based on SEMI E37-1109§5.4**
1141 ///
1142 /// Provides the [NOT SELECTED] state by default.
1143 ///
1144 /// [NOT SELECTED]: SelectionState::NotSelected
1145 fn default() -> Self {
1146 SelectionState::NotSelected
1147 }
1148}
1149
1150/// ## PARAMETER SETTINGS
1151/// **Based on SEMI E37-1109§10.2**
1152///
1153/// The required set of paramters which an [HSMS] implementation must provide,
1154/// and which the [Client] will abide by.
1155///
1156/// [HSMS]: crate
1157/// [Client]: Client
1158#[derive(Clone, Copy, Debug, PartialEq)]
1159pub struct ParameterSettings {
1160 /// ### CONNECT MODE
1161 ///
1162 /// Specifies the [Connection Mode] the [Client] will provide to
1163 /// the [Primitive Client] to use when performing the [Connect Procedure]:
1164 /// [PASSIVE] to wait for an incoming connection, or [ACTIVE] to initiate
1165 /// an outgoing connection.
1166 ///
1167 /// [Primitive Client]: primitive::Client
1168 /// [Client]: Client
1169 /// [Connect Procedure]: Client::connect
1170 /// [Connection Mode]: ConnectionMode
1171 /// [PASSIVE]: ConnectionMode::Passive
1172 /// [ACTIVE]: ConnectionMode::Active
1173 pub connect_mode: ConnectionMode,
1174
1175 /// ### T3: REPLY TIMEOUT
1176 ///
1177 /// The maximum amount of time that the [Client] will wait after sending
1178 /// a Primary [Data Message] to receive the appropriate Response
1179 /// [Data Message] before it must initiate the [Disconnect Procedure].
1180 ///
1181 /// [Client]: Client
1182 /// [Disconnect Procedure]: Client::disconnect
1183 /// [Data Message]: MessageContents::DataMessage
1184 pub t3: Duration,
1185
1186 /// ### T5: CONNECTION SEPARATION TIMEOUT
1187 ///
1188 /// The minimum amount of time that the [Client] must wait between successive
1189 /// attempts to initiate the [Connect Procedure] with a [Connect Mode] of
1190 /// [ACTIVE].
1191 ///
1192 /// [Client]: Client
1193 /// [Connect Procedure]: Client::connect
1194 /// [Connect Mode]: ParameterSettings::connect_mode
1195 /// [ACTIVE]: ConnectionMode::Active
1196 pub t5: Duration,
1197
1198 /// ### T6: CONTROL TRANSACTION TIMEOUT
1199 ///
1200 /// The maximum amount of time that the [Client] will wait after sending a
1201 /// [Select Request], [Deselect Request], or [Linktest Request] to receive
1202 /// the appropriate [Select Response], [Deselect Response], or
1203 /// [Linktest Response] before it must initiate the [Disconnect Procedure].
1204 ///
1205 /// [Client]: Client
1206 /// [Disconnect Procedure]: Client::disconnect
1207 /// [Select Request]: MessageContents::SelectRequest
1208 /// [Select Response]: MessageContents::SelectResponse
1209 /// [Deselect Request]: MessageContents::DeselectRequest
1210 /// [Deselect Response]: MessageContents::DeselectResponse
1211 /// [Linktest Request]: MessageContents::LinktestRequest
1212 /// [Linktest Response]: MessageContents::LinktestResponse
1213 pub t6: Duration,
1214
1215 /// ### T7: NOT SELECTED TIMEOUT
1216 ///
1217 /// The maximum amount of time that the [Client] will wait after being
1218 /// placed in the [NOT SELECTED] state before it must initiate the
1219 /// [Disconnect Procedure].
1220 ///
1221 /// [Client]: Client
1222 /// [Disconnect Procedure]: Client::disconnect
1223 /// [NOT SELECTED]: SelectionState::NotSelected
1224 pub t7: Duration,
1225
1226 /// ### T8: NETWORK INTERCHARACTER TIMEOUT
1227 ///
1228 /// The amount of time that the [Client] will provide to the
1229 /// [Primitive Client] to use as the maximum amount of time it may wait while
1230 /// sending or receiving data between successive characters in the same
1231 /// [Primitive Message] before it must initiate the [Disconnect Procedure].
1232 ///
1233 /// [Primitive Client]: primitive::Client
1234 /// [Disconnect Procedure]: primitive::Client::disconnect
1235 /// [Primitive Message]: primitive::Message
1236 /// [Client]: Client
1237 pub t8: Duration,
1238}
1239impl Default for ParameterSettings {
1240 /// ### DEFAULT PARAMETER SETTINGS
1241 /// **Based on SEMI E37-1109§10.2**
1242 ///
1243 /// Provides [Parameter Settings] with these values, with timeouts as shown
1244 /// in the 'typical values' column in Table 10.
1245 ///
1246 /// - [Connect Mode] of [PASSIVE]
1247 /// - [T3] of 45 seconds
1248 /// - [T5] of 10 seconds
1249 /// - [T6] of 5 seconds
1250 /// - [T7] of 10 seconds
1251 /// - [T8] of 5 seconds
1252 ///
1253 /// [Parameter Settings]: ParameterSettings
1254 /// [PASSIVE]: ConnectionMode::Passive
1255 /// [Connect Mode]: ParameterSettings::connect_mode
1256 /// [T3]: ParameterSettings::t3
1257 /// [T5]: ParameterSettings::t5
1258 /// [T6]: ParameterSettings::t6
1259 /// [T7]: ParameterSettings::t7
1260 /// [T8]: ParameterSettings::t8
1261 fn default() -> Self {
1262 Self {
1263 connect_mode: ConnectionMode::default(),
1264 t3: Duration::from_secs(45),
1265 t5: Duration::from_secs(10),
1266 t6: Duration::from_secs(5),
1267 t7: Duration::from_secs(10),
1268 t8: Duration::from_secs(5),
1269 }
1270 }
1271}
1272
1273/// ## MESSAGE
1274/// **Based on SEMI E37-1109§8.2-8.3**
1275///
1276/// Data using the structure defined by the [Generic Services], enforcing
1277/// compliance as determined by a [Presentation Type] of 0, broken down into
1278/// its [Message ID] and [Message Contents].
1279///
1280/// [Generic Services]: crate::generic
1281/// [Presentation Type]: PresentationType
1282/// [Message ID]: MessageID
1283/// [Message Contents]: MessageContents
1284#[derive(Clone, Debug)]
1285pub struct Message {
1286 pub id: MessageID,
1287 pub contents: MessageContents,
1288}
1289impl From<Message> for primitive::Message {
1290 /// ### PRIMITIVE MESSAGE FROM GENERIC MESSAGE
1291 ///
1292 /// Due to the fact that valid [Generic Message]s are a subset of valid
1293 /// [Primitive Message]s, this operation is infallible.
1294 ///
1295 /// [Generic Message]: Message
1296 /// [Primitive Message]: primitive::Message
1297 fn from(message: Message) -> Self {
1298 match message.contents {
1299 MessageContents::DataMessage(e5_message) => {
1300 primitive::Message {
1301 header: primitive::MessageHeader {
1302 session_id : message.id.session,
1303 byte_2 : ((e5_message.w as u8) << 7) | e5_message.stream,
1304 byte_3 : e5_message.function,
1305 presentation_type : PresentationType::SecsII as u8,
1306 session_type : SessionType::DataMessage as u8,
1307 system : message.id.system,
1308 },
1309 text: match e5_message.text {
1310 Some(item) => Vec::<u8>::from(item),
1311 None => vec![],
1312 },
1313 }
1314 },
1315 MessageContents::SelectRequest => {
1316 primitive::Message {
1317 header: primitive::MessageHeader {
1318 session_id : message.id.session,
1319 byte_2 : 0,
1320 byte_3 : 0,
1321 presentation_type : PresentationType::SecsII as u8,
1322 session_type : SessionType::SelectRequest as u8,
1323 system : message.id.system,
1324 },
1325 text: vec![],
1326 }
1327 },
1328 MessageContents::SelectResponse(select_status) => {
1329 primitive::Message {
1330 header: primitive::MessageHeader {
1331 session_id : message.id.session,
1332 byte_2 : 0,
1333 byte_3 : select_status,
1334 presentation_type : PresentationType::SecsII as u8,
1335 session_type : SessionType::SelectResponse as u8,
1336 system : message.id.system,
1337 },
1338 text: vec![],
1339 }
1340 },
1341 MessageContents::DeselectRequest => {
1342 primitive::Message {
1343 header: primitive::MessageHeader {
1344 session_id : message.id.session,
1345 byte_2 : 0,
1346 byte_3 : 0,
1347 presentation_type : PresentationType::SecsII as u8,
1348 session_type : SessionType::DeselectRequest as u8,
1349 system : message.id.system,
1350 },
1351 text: vec![],
1352 }
1353 },
1354 MessageContents::DeselectResponse(deselect_status) => {
1355 primitive::Message {
1356 header: primitive::MessageHeader {
1357 session_id : message.id.session,
1358 byte_2 : 0,
1359 byte_3 : deselect_status,
1360 presentation_type : PresentationType::SecsII as u8,
1361 session_type : SessionType::DeselectResponse as u8,
1362 system : message.id.system,
1363 },
1364 text: vec![],
1365 }
1366 },
1367 MessageContents::LinktestRequest => {
1368 primitive::Message {
1369 header: primitive::MessageHeader {
1370 session_id : 0xFFFF,
1371 byte_2 : 0,
1372 byte_3 : 0,
1373 presentation_type : PresentationType::SecsII as u8,
1374 session_type : SessionType::LinktestRequest as u8,
1375 system : message.id.system,
1376 },
1377 text: vec![],
1378 }
1379 },
1380 MessageContents::LinktestResponse => {
1381 primitive::Message {
1382 header: primitive::MessageHeader {
1383 session_id : 0xFFFF,
1384 byte_2 : 0,
1385 byte_3 : 0,
1386 presentation_type : PresentationType::SecsII as u8,
1387 session_type : SessionType::LinktestResponse as u8,
1388 system : message.id.system,
1389 },
1390 text: vec![],
1391 }
1392 },
1393 MessageContents::RejectRequest(message_type, reason_code) => {
1394 primitive::Message {
1395 header: primitive::MessageHeader {
1396 session_id : message.id.session,
1397 byte_2 : message_type,
1398 byte_3 : reason_code,
1399 presentation_type : PresentationType::SecsII as u8,
1400 session_type : SessionType::RejectRequest as u8,
1401 system : message.id.system,
1402 },
1403 text: vec![],
1404 }
1405 },
1406 MessageContents::SeparateRequest => {
1407 primitive::Message {
1408 header: primitive::MessageHeader {
1409 session_id : message.id.session,
1410 byte_2 : 0,
1411 byte_3 : 0,
1412 presentation_type : PresentationType::SecsII as u8,
1413 session_type : SessionType::SeparateRequest as u8,
1414 system : message.id.system,
1415 },
1416 text: vec![],
1417 }
1418 },
1419 }
1420 }
1421}
1422impl TryFrom<primitive::Message> for Message {
1423 type Error = RejectReason;
1424
1425 /// ## GENERIC MESSAGE FROM PRIMITIVE MESSAGE
1426 ///
1427 /// Due to the fact that valid [Generic Message]s are a subset of valid
1428 /// [Primitive Message]s, this operation is fallable when the
1429 /// [Primitive Message] is not a [Generic Message].
1430 ///
1431 /// [Generic Message]: Message
1432 /// [Primitive Message]: primitive::Message
1433 fn try_from(message: primitive::Message) -> Result<Self, Self::Error> {
1434 if message.header.presentation_type != 0 {return Err(RejectReason::UnsupportedPresentationType)}
1435 Ok(Message {
1436 id: MessageID {
1437 session: message.header.session_id,
1438 system: message.header.system,
1439 },
1440 contents: match message.header.session_type {
1441 0 => {
1442 MessageContents::DataMessage(semi_e5::Message{
1443 stream : message.header.byte_2 & 0b0111_1111,
1444 function : message.header.byte_3,
1445 w : message.header.byte_2 & 0b1000_0000 > 0,
1446 text : match semi_e5::Item::try_from(message.text) {
1447 // Valid Item
1448 Ok(text) => Some(text),
1449 // Invalid Item
1450 Err(error) => {
1451 match error {
1452 // Empty Text: Considered Valid Here
1453 semi_e5::Error::EmptyText => {None},
1454 // Other Error: Malformed Data
1455 _ => {return Err(RejectReason::MalformedData)}
1456 }
1457 },
1458 },
1459 })
1460 },
1461 1 => {
1462 if message.header.byte_2 != 0 {return Err(RejectReason::MalformedData)}
1463 if message.header.byte_3 != 0 {return Err(RejectReason::MalformedData)}
1464 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1465 MessageContents::SelectRequest
1466 },
1467 2 => {
1468 if message.header.byte_2 != 0 {return Err(RejectReason::MalformedData)}
1469 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1470 MessageContents::SelectResponse(message.header.byte_3)
1471 },
1472 3 => {
1473 if message.header.byte_2 != 0 {return Err(RejectReason::MalformedData)}
1474 if message.header.byte_3 != 0 {return Err(RejectReason::MalformedData)}
1475 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1476 MessageContents::DeselectRequest
1477 },
1478 4 => {
1479 if message.header.byte_2 != 0 {return Err(RejectReason::MalformedData)}
1480 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1481 MessageContents::DeselectResponse(message.header.byte_3)
1482 },
1483 5 => {
1484 if message.header.session_id != 0xFFFF {return Err(RejectReason::MalformedData)}
1485 if message.header.byte_2 != 0 {return Err(RejectReason::MalformedData)}
1486 if message.header.byte_3 != 0 {return Err(RejectReason::MalformedData)}
1487 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1488 MessageContents::LinktestRequest
1489 },
1490 6 => {
1491 if message.header.session_id != 0xFFFF {return Err(RejectReason::MalformedData)}
1492 if message.header.byte_2 != 0 {return Err(RejectReason::MalformedData)}
1493 if message.header.byte_3 != 0 {return Err(RejectReason::MalformedData)}
1494 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1495 MessageContents::LinktestResponse
1496 },
1497 7 => {
1498 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1499 MessageContents::RejectRequest(message.header.byte_2, message.header.byte_3)
1500 },
1501 9 => {
1502 if message.header.byte_2 != 0 {return Err(RejectReason::MalformedData)}
1503 if message.header.byte_3 != 0 {return Err(RejectReason::MalformedData)}
1504 if !message.text.is_empty() {return Err(RejectReason::MalformedData)}
1505 MessageContents::SeparateRequest
1506 },
1507 _ => {return Err(RejectReason::UnsupportedSessionType)}
1508 },
1509 })
1510 }
1511}
1512
1513/// ## MESSAGE ID
1514/// **Based on SEMI E37-1109§8.2**
1515///
1516/// The uniquely identifying components of a [Message] in forming a valid
1517/// transaction, including the [Session ID] and [System Bytes].
1518///
1519/// [Message]: Message
1520/// [Session ID]: MessageID::session
1521/// [System Bytes]: MessageID::system
1522#[derive(Clone, Copy, Debug, PartialEq)]
1523pub struct MessageID {
1524 /// ### SESSION ID
1525 /// **Based on SEMI E37-1109§8.2.6.1**
1526 ///
1527 /// Provides an association between [Message]s across multiple
1528 /// transactions, particularly to link the [Select Procedure] and
1529 /// [Deselect Procedure] to subsequent [Data Message]s.
1530 ///
1531 /// [Select Procedure]: Client::select
1532 /// [Deselect Procedure]: Client::deselect
1533 /// [Message]: Message
1534 /// [Data Message]: MessageContents::DataMessage
1535 pub session: u16,
1536
1537 /// ### SYSTEM BYTES
1538 /// **Based on SEMI E37-1109§8.2.6.7**
1539 ///
1540 /// Identifies a transaction uniquely among the set of open transactions.
1541 pub system: u32,
1542}
1543
1544/// ## MESSAGE CONTENTS
1545/// **Based on SEMI E37-1109§8.3.1-8.3.21**
1546///
1547/// The contents of a [Message], broken down by its [Session Type]:
1548///
1549/// - [SECS-II] formatted [Data Message]
1550/// - [Select.req]
1551/// - [Select.rsp]
1552/// - [Deselect.req]
1553/// - [Deselect.rsp]
1554/// - [Linktest.req]
1555/// - [Linktest.rsp]
1556/// - [Reject.req]
1557/// - [Separate.req]
1558///
1559/// [SECS-II]: semi_e5
1560/// [Message]: Message
1561/// [Session Type]: SessionType
1562/// [Data Message]: MessageContents::DataMessage
1563/// [Select.req]: MessageContents::SelectRequest
1564/// [Select.rsp]: MessageContents::SelectResponse
1565/// [Deselect.req]: MessageContents::DeselectRequest
1566/// [Deselect.rsp]: MessageContents::DeselectResponse
1567/// [Linktest.req]: MessageContents::LinktestRequest
1568/// [Linktest.rsp]: MessageContents::LinktestResponse
1569/// [Reject.req]: MessageContents::RejectRequest
1570/// [Separate.req]: MessageContents::SeparateRequest
1571#[repr(u8)]
1572#[derive(Clone, Debug)]
1573pub enum MessageContents {
1574 /// ## DATA MESSAGE
1575 /// **Based on SEMI E37-1109§8.3.1-8.3.3**
1576 ///
1577 /// A [Message] with a [Session Type] of 0, used by the initiator of or
1578 /// responding entity in the [Data Procedure] to send data.
1579 ///
1580 /// Contains [SECS-II] formatted data.
1581 ///
1582 /// [SECS-II]: semi_e5
1583 /// [Message]: Message
1584 /// [Session Type]: SessionType
1585 /// [Data Procedure]: Client::data
1586 DataMessage(semi_e5::Message) = SessionType::DataMessage as u8,
1587
1588 /// ## SELECT REQUEST
1589 /// **Based on SEMI E37-1109§8.3.4**
1590 ///
1591 /// A [Message] with a [Session Type] of 1, used by the initiator of the
1592 /// [Select Procedure] for establishing communications.
1593 ///
1594 /// [Message]: Message
1595 /// [Select Procedure]: Client::select
1596 /// [Session Type]: SessionType
1597 SelectRequest = SessionType::SelectRequest as u8,
1598
1599 /// ## SELECT RESPONSE
1600 /// **Based on SEMI E37-1109§8.3.5-8.3.7**
1601 ///
1602 /// A [Message] with a [Session Type] of 2, used by the responding
1603 /// entity in the [Select Procedure].
1604 ///
1605 /// Contains a [Select Status], indicating the success or failure mode of
1606 /// the [Select Procedure].
1607 ///
1608 /// [Message]: Message
1609 /// [Select Procedure]: Client::select
1610 /// [Session Type]: SessionType
1611 /// [Select Status]: SelectStatus
1612 SelectResponse(u8) = SessionType::SelectResponse as u8,
1613
1614 /// ## DESELECT REQUEST
1615 /// **Based on SEMI E37-1109§8.3.8-8.3.10**
1616 ///
1617 /// A [Message] with a [Session Type] of 3, used by the initiator of the
1618 /// [Deselect Procedure] for breaking communications.
1619 ///
1620 /// [Message]: Message
1621 /// [Deselect Procedure]: Client::deselect
1622 /// [Session Type]: SessionType
1623 DeselectRequest = SessionType::DeselectRequest as u8,
1624
1625 /// ## DESELECT RESPONSE
1626 /// **Based on SEMI E37-1109§8.3.11-8.3.13**
1627 ///
1628 /// An [Message] with a [Session Type] of 4, used by the responding entity
1629 /// in the [Deselect Procedure].
1630 ///
1631 /// Contains a [Deselect Status], indicating the success or failure mode of
1632 /// the [Deselect Procedure].
1633 ///
1634 /// [Message]: Message
1635 /// [Deselect Procedure]: Client::deselect
1636 /// [Session Type]: SessionType
1637 /// [Deselect Status]: DeselectStatus
1638 DeselectResponse(u8) = SessionType::DeselectResponse as u8,
1639
1640 /// ## LINKTEST REQUEST
1641 /// **Based on SEMI E37-1109§8.3.14-8.3.16**
1642 ///
1643 /// A [Message] with a [Session Type] of 5, used by the initiator of the
1644 /// [Linktest Procedure] for checking communications stability.
1645 ///
1646 /// [Message]: Message
1647 /// [Session Type]: SessionType
1648 /// [Linktest Procedure]: Client::linktest
1649 LinktestRequest = SessionType::LinktestRequest as u8,
1650
1651 /// ## LINKTEST RESPONSE
1652 /// **Based on SEMI E37-1109§8.3.17-8.3.19**
1653 ///
1654 /// A [Message] with a [Session Type] of 6, used by the responding entity
1655 /// in the [Linktest Procedure].
1656 ///
1657 /// [Message]: Message
1658 /// [Session Type]: SessionType
1659 /// [Linktest Procedure]: Client::linktest
1660 LinktestResponse = SessionType::LinktestResponse as u8,
1661
1662 /// ## REJECT REQUEST
1663 /// **Based on SEMI E37-1109§8.3.20-8.3.21**
1664 ///
1665 /// A [Message] with a [Session Type] of 7, used by the responding entity
1666 /// in the [Reject Procedure].
1667 ///
1668 /// Contains the [Presentation Type] or [Session Type] of the [Message] being
1669 /// rejected, and the [Reason Code] indicating why the message was rejected.
1670 ///
1671 /// [Message]: Message
1672 /// [Reject Procedure]: Client::reject
1673 /// [Presentation Type]: PresentationType
1674 /// [Session Type]: SessionType
1675 /// [Reason Code]: RejectReason
1676 RejectRequest(u8, u8) = SessionType::RejectRequest as u8,
1677
1678 /// ## SEPARATE REQUEST
1679 /// **Based on SEMI E37-1109§8.3.22**
1680 ///
1681 /// A [Message] with a [Session Type] of 9, used by the initiator of the
1682 /// [Separate Procedure] for breaking communications.
1683 ///
1684 /// [Message]: Message
1685 /// [Separate Procedure]: Client::separate
1686 /// [Session Type]: SessionType
1687 SeparateRequest = SessionType::SeparateRequest as u8,
1688}
1689
1690/// ## SESSION TYPE
1691/// **Based on SEMI E37-1109§8.2.6.5-8.2.6.6**
1692///
1693/// Defines the type of [Message] being sent.
1694///
1695/// Values 11-127 are reserved for Subsidiary Standards.
1696///
1697/// Values 8, 10, and 128-255 are reserved and may not be used.
1698///
1699/// [Message]: Message
1700#[repr(u8)]
1701#[derive(Clone, Copy, Debug, PartialEq)]
1702pub enum SessionType {
1703 /// ### DATA MESSAGE
1704 ///
1705 /// Denotes a [SECS-II] formatted [Data Message].
1706 ///
1707 /// [SECS-II]: semi_e5
1708 /// [Data Message]: MessageContents::DataMessage
1709 DataMessage = 0,
1710
1711 /// ### SELECT REQUEST
1712 ///
1713 /// Denotes a [Select.req] message.
1714 ///
1715 /// [Select.req]: MessageContents::SelectRequest
1716 SelectRequest = 1,
1717
1718 /// ### SELECT RESPONSE
1719 ///
1720 /// Denotes a [Select.rsp] message.
1721 ///
1722 /// [Select.rsp]: MessageContents::SelectResponse
1723 SelectResponse = 2,
1724
1725 /// ### DESELECT REQUEST
1726 ///
1727 /// Denotes a [Deselect.req] message.
1728 ///
1729 /// [Deselect.req]: MessageContents::DeselectRequest
1730 DeselectRequest = 3,
1731
1732 /// ### DESELECT RESPONSE
1733 ///
1734 /// Denotes a [Deselect.rsp] message.
1735 ///
1736 /// [Deselect.rsp]: MessageContents::DeselectResponse
1737 DeselectResponse = 4,
1738
1739 /// ### LINKTEST REQUEST
1740 ///
1741 /// Denotes a [Linktest.req] message.
1742 ///
1743 /// [Linktest.req]: MessageContents::LinktestRequest
1744 LinktestRequest = 5,
1745
1746 /// ### LINKTEST RESPONSE
1747 ///
1748 /// Denotes a [Linktest.rsp] message.
1749 ///
1750 /// [Linktest.rsp]: MessageContents::LinktestResponse
1751 LinktestResponse = 6,
1752
1753 /// ### REJECT REQUEST
1754 ///
1755 /// Denotes a [Reject.req] message.
1756 ///
1757 /// [Reject.req]: MessageContents::RejectRequest
1758 RejectRequest = 7,
1759
1760 /// ### SEPARATE REQUEST
1761 ///
1762 /// Denotes a [Separate.req] message.
1763 ///
1764 /// [Separate.req]: MessageContents::SeparateRequest
1765 SeparateRequest = 9,
1766}
1767
1768/// ## SELECT STATUS
1769/// **Based on SEMI E37-1109§8.3.7.2**
1770///
1771/// [Byte 3] of a [Deselect.rsp] message, used as the indication of success or
1772/// reason for failure of the [Select Procedure].
1773///
1774/// Values 4-127 are reserved for Subsidiary Standards.
1775///
1776/// Values 128-255 are reserved for the Local Entity.
1777///
1778/// [Byte 3]: primitive::MessageHeader::byte_3
1779/// [Deselect.rsp]: MessageContents::DeselectResponse
1780/// [Select Procedure]: Client::select
1781#[repr(u8)]
1782#[derive(Clone, Copy, Debug, PartialEq)]
1783pub enum SelectStatus {
1784 Success = 0,
1785 AlreadyActive = 1,
1786 NotReady = 2,
1787 Exhausted = 3,
1788}
1789
1790/// ## DESELECT STATUS
1791/// **Based on SEMI E37-1109§8.3.13.2**
1792///
1793/// [Byte 3] of a [Deselect.rsp] message, used as the indication of success or
1794/// reason for failure of the [Deselect Procedure].
1795///
1796/// Values 3-127 are reserved for Subsidiary Standards.
1797///
1798/// Values 128-255 are reserved for the Local Entity.
1799///
1800/// [Byte 3]: primitive::MessageHeader::byte_3
1801/// [Deselect.rsp]: MessageContents::DeselectResponse
1802/// [Deselect Procedure]: Client::deselect
1803#[repr(u8)]
1804#[derive(Clone, Copy, Debug, PartialEq)]
1805pub enum DeselectStatus {
1806 Success = 0,
1807 NotEstablished = 1,
1808 Busy = 2,
1809}
1810
1811/// ## REJECT REASON
1812/// **Based on SEMI E37-1109§8.3.21.3**
1813///
1814/// [Byte 3] of a [Reject.req] message, specifying the reason a message has
1815/// been rejected in the [Reject Procedure].
1816///
1817/// Values 4-127 are reserved for Subsidiary Standards.
1818///
1819/// Values 0, and 128-255 are reserved for the Local Entity.
1820///
1821/// [Byte 3]: primitive::MessageHeader::byte_3
1822/// [Reject.req]: MessageContents::RejectRequest
1823/// [Reject Procedure]: Client::reject
1824#[repr(u8)]
1825#[derive(Clone, Copy, Debug, PartialEq)]
1826pub enum RejectReason {
1827 /// ### MALFORMED DATA
1828 /// **Local Entity Specific Reason**
1829 ///
1830 /// A [Message] was recieved which was valid according to the
1831 /// [Primitive Services] but invalid according to the [Generic Services].
1832 ///
1833 /// [Message]: primitive::Message
1834 /// [Primitive Services]: primitive
1835 /// [Generic Services]: crate::generic
1836 MalformedData = 0,
1837
1838 /// ### SESSION TYPE NOT SUPPORTED
1839 ///
1840 /// A [Message] was received whose [Session Type] value is not allowed.
1841 ///
1842 /// [Message]: primitive::Message
1843 /// [Session Type]: SessionType
1844 UnsupportedSessionType = 1,
1845
1846 /// ### PRESENTATION TYPE NOT SUPPORTED
1847 ///
1848 /// A [Message] was received whose [Presentation Type] value is not allowed.
1849 ///
1850 /// [Message]: primitive::Message
1851 /// [Presentation Type]: crate::PresentationType
1852 UnsupportedPresentationType = 2,
1853
1854 /// ### TRANSACTION NOT OPEN
1855 ///
1856 /// A [Select.rsp], [Deselect.rsp], or [Linktest.rsp] was recieved when there
1857 /// was no outstanding [Select.req], [Deselect.req], or [Linktest.req] which
1858 /// corresponded to it.
1859 ///
1860 /// [Select.req]: MessageContents::SelectRequest
1861 /// [Select.rsp]: MessageContents::SelectResponse
1862 /// [Deselect.req]: MessageContents::DeselectRequest
1863 /// [Deselect.rsp]: MessageContents::DeselectResponse
1864 /// [Linktest.req]: MessageContents::LinktestRequest
1865 /// [Linktest.rsp]: MessageContents::LinktestResponse
1866 TransactionNotOpen = 3,
1867
1868 /// ### ENTITY NOT SELECTED
1869 ///
1870 /// A [Data Message] was recieved when not in the [SELECTED] state.
1871 ///
1872 /// [Data Message]: MessageContents::DataMessage
1873 /// [SELECTED]: SelectionState::Selected
1874 EntityNotSelected = 4,
1875}