1use std::string::FromUtf8Error;
2
3use prost::DecodeError;
4use srad_types::{
5 payload::Payload,
6 topic::{state_host_topic, NodeMessage as NodeMessageType, NodeTopic, QoS},
7};
8use thiserror::Error;
9
10#[derive(Error, Debug, PartialEq)]
15pub enum MessageError {
16 #[error("There was an error decoding the payload: {0}")]
17 DecodePayloadError(DecodeError),
18 #[error("The topic was invalid")]
19 InvalidSparkplugTopic,
20 #[error("Topic parts utf8 decode error: {0}")]
21 TopicUtf8Error(FromUtf8Error),
22 #[error("Unable to decode state message as json: {0}")]
23 StatePayloadJsonDecodeError(String),
24}
25
26impl From<FromUtf8Error> for MessageError {
27 fn from(e: FromUtf8Error) -> Self {
28 MessageError::TopicUtf8Error(e)
29 }
30}
31
32#[derive(Debug, PartialEq)]
34pub enum MessageKind {
35 Birth,
36 Death,
37 Cmd,
38 Data,
39 Other(String),
40}
41
42#[derive(Debug, PartialEq)]
44pub struct Message {
45 pub payload: Payload,
46 pub kind: MessageKind,
47}
48
49#[derive(Debug, Clone, PartialEq)]
51pub enum StatePayload {
52 Online { timestamp: u64 },
53 Offline { timestamp: u64 },
54}
55
56impl StatePayload {
57 pub fn get_publish_quality_retain(&self) -> (QoS, bool) {
59 match self {
60 StatePayload::Online { timestamp: _ } => (QoS::AtLeastOnce, true),
61 StatePayload::Offline { timestamp: _ } => (QoS::AtLeastOnce, true),
62 }
63 }
64}
65
66impl From<StatePayload> for Vec<u8> {
67 fn from(value: StatePayload) -> Self {
68 match value {
69 StatePayload::Online { timestamp } => {
70 format!("{{\"online\" : true, \"timestamp\" : {timestamp}}}").into()
71 }
72 StatePayload::Offline { timestamp } => {
73 format!("{{\"online\" : false, \"timestamp\" : {timestamp}}}").into()
74 }
75 }
76 }
77}
78
79#[derive(Debug, PartialEq)]
81pub struct NodeMessage {
82 pub group_id: String,
84 pub node_id: String,
86 pub message: Message,
88}
89
90#[derive(Debug, PartialEq)]
92pub struct DeviceMessage {
93 pub group_id: String,
95 pub node_id: String,
97 pub device_id: String,
99 pub message: Message,
101}
102
103#[derive(Debug, PartialEq)]
105pub enum Event {
106 Offline,
107 Online,
108 Node(NodeMessage),
109 Device(DeviceMessage),
110 State {
111 host_id: String,
112 payload: StatePayload,
113 },
114 InvalidPublish {
115 reason: MessageError,
116 topic: Vec<u8>,
117 payload: Vec<u8>,
118 },
119}
120
121#[derive(Debug, Clone, PartialEq)]
123pub struct LastWill {
124 pub topic: String,
125 pub retain: bool,
126 pub qos: QoS,
127 pub payload: Vec<u8>,
128}
129
130impl LastWill {
131 pub fn new_node(group: &str, node_id: &str, payload: Payload) -> Self {
132 let topic = NodeTopic::new(group, NodeMessageType::NDeath, node_id);
133 let (qos, retain) = topic.get_publish_quality_retain();
134 Self {
135 retain,
136 qos,
137 payload: payload.into(),
138 topic: topic.topic,
139 }
140 }
141
142 pub fn new_app(host_id: &str, timestamp: u64) -> Self {
143 Self {
144 topic: state_host_topic(host_id),
145 retain: true,
146 qos: QoS::AtLeastOnce,
147 payload: StatePayload::Offline { timestamp }.into(),
148 }
149 }
150}