Match initial query with mqtt client
This commit is contained in:
6
Cargo.lock
generated
6
Cargo.lock
generated
@@ -3232,6 +3232,7 @@ dependencies = [
|
|||||||
"rand",
|
"rand",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"soon",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -3545,6 +3546,11 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "soon"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/connorslade/misc#0e48a0c5652e7f58481074690ded4e0b9aa24a9a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.9.8"
|
version = "0.9.8"
|
||||||
|
@@ -5,11 +5,12 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.86"
|
anyhow = "1.0.86"
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
|
||||||
serde_json = "1.0.117"
|
|
||||||
|
|
||||||
common = { path = "../common" }
|
|
||||||
bitflags = "2.5.0"
|
bitflags = "2.5.0"
|
||||||
|
chrono = "0.4.38"
|
||||||
parking_lot = "0.12.3"
|
parking_lot = "0.12.3"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
chrono = "0.4.38"
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
|
serde_json = "1.0.117"
|
||||||
|
soon = { git = "https://github.com/connorslade/misc" }
|
||||||
|
|
||||||
|
common = { path = "../common" }
|
||||||
|
50
remote_send/src/commands/mod.rs
Normal file
50
remote_send/src/commands/mod.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use chrono::Utc;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub struct Command<Data> {
|
||||||
|
pub cmd: u16,
|
||||||
|
pub data: Data,
|
||||||
|
pub from: u8,
|
||||||
|
#[serde(rename = "MainboardID")]
|
||||||
|
pub mainboard_id: String,
|
||||||
|
#[serde(rename = "RequestID")]
|
||||||
|
pub request_id: String,
|
||||||
|
#[serde(rename = "TimeStamp")]
|
||||||
|
pub time_stamp: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait CommandTrait: Serialize {
|
||||||
|
const CMD: u16;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Data> Command<Data> {
|
||||||
|
pub fn new(cmd: u16, data: Data, mainboard_id: String) -> Self {
|
||||||
|
let request_id = format!("{:x}", rand::random::<u128>());
|
||||||
|
let time_stamp = Utc::now().timestamp_millis() as u64;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
cmd,
|
||||||
|
data,
|
||||||
|
from: 0,
|
||||||
|
mainboard_id,
|
||||||
|
request_id,
|
||||||
|
time_stamp,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
|
pub struct StartPrinting {
|
||||||
|
pub filename: String,
|
||||||
|
pub start_layer: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
pub struct DisconnectCommand;
|
||||||
|
|
||||||
|
impl CommandTrait for DisconnectCommand {
|
||||||
|
const CMD: u16 = 64;
|
||||||
|
}
|
@@ -1,54 +1,18 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::Utc;
|
|
||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
|
|
||||||
|
pub mod commands;
|
||||||
pub mod mqtt;
|
pub mod mqtt;
|
||||||
|
pub mod mqtt_server;
|
||||||
pub mod status;
|
pub mod status;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
#[serde(rename_all = "PascalCase")]
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub struct Response<Data> {
|
pub struct Response<Data> {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub data: Data,
|
pub data: Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
#[serde(rename_all = "PascalCase")]
|
|
||||||
pub struct Command<Data> {
|
|
||||||
pub cmd: u16,
|
|
||||||
pub data: Data,
|
|
||||||
pub from: u8,
|
|
||||||
#[serde(rename = "MainboardID")]
|
|
||||||
pub mainboard_id: String,
|
|
||||||
#[serde(rename = "RequestID")]
|
|
||||||
pub request_id: String,
|
|
||||||
#[serde(rename = "TimeStamp")]
|
|
||||||
pub time_stamp: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize)]
|
|
||||||
#[serde(rename_all = "PascalCase")]
|
|
||||||
pub struct StartPrinting {
|
|
||||||
pub filename: String,
|
|
||||||
pub start_layer: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Data> Command<Data> {
|
|
||||||
pub fn new(cmd: u16, data: Data, mainboard_id: String) -> Self {
|
|
||||||
let request_id = format!("{:x}", rand::random::<u128>());
|
|
||||||
let time_stamp = Utc::now().timestamp_millis() as u64;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
cmd,
|
|
||||||
data,
|
|
||||||
from: 0,
|
|
||||||
mainboard_id,
|
|
||||||
request_id,
|
|
||||||
time_stamp,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Resolution {
|
pub struct Resolution {
|
||||||
pub x: u16,
|
pub x: u16,
|
||||||
|
@@ -1,120 +1,13 @@
|
|||||||
|
use std::{net::UdpSocket, thread};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::Serialize;
|
use remote_send::{mqtt::MqttServer, mqtt_server::Mqtt, status::FullStatusData, Response};
|
||||||
use std::net::UdpSocket;
|
|
||||||
|
|
||||||
use remote_send::{
|
|
||||||
mqtt::{
|
|
||||||
packets::{
|
|
||||||
connect::ConnectPacket,
|
|
||||||
connect_ack::{ConnectAckFlags, ConnectAckPacket, ConnectReturnCode},
|
|
||||||
publish::{PublishFlags, PublishPacket},
|
|
||||||
publish_ack::PublishAckPacket,
|
|
||||||
subscribe::SubscribePacket,
|
|
||||||
subscribe_ack::{SubscribeAckPacket, SubscribeReturnCode},
|
|
||||||
},
|
|
||||||
HandlerCtx, MqttHandler, MqttServer,
|
|
||||||
},
|
|
||||||
status::{Attributes, FullStatusData, StatusData},
|
|
||||||
Command, Response,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Mqtt {
|
|
||||||
// todo: must support multiple clients
|
|
||||||
status: Attributes,
|
|
||||||
id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MqttHandler for Mqtt {
|
|
||||||
fn on_connect(
|
|
||||||
&self,
|
|
||||||
ctx: &HandlerCtx<Self>,
|
|
||||||
_packet: ConnectPacket,
|
|
||||||
) -> Result<ConnectAckPacket> {
|
|
||||||
println!("Client `{}` connected", ctx.client_id);
|
|
||||||
|
|
||||||
Ok(ConnectAckPacket {
|
|
||||||
flags: ConnectAckFlags::empty(),
|
|
||||||
return_code: ConnectReturnCode::Accepted,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_subscribe(
|
|
||||||
&self,
|
|
||||||
ctx: &HandlerCtx<Self>,
|
|
||||||
packet: SubscribePacket,
|
|
||||||
) -> Result<SubscribeAckPacket> {
|
|
||||||
println!(
|
|
||||||
"Client `{}` subscribed to topics: {:?}",
|
|
||||||
ctx.client_id, packet.filters
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(SubscribeAckPacket {
|
|
||||||
packet_id: packet.packet_id,
|
|
||||||
return_codes: packet
|
|
||||||
.filters
|
|
||||||
.iter()
|
|
||||||
.map(|(_, qos)| SubscribeReturnCode::Success(*qos))
|
|
||||||
.collect::<Vec<_>>(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_publish(&self, ctx: &HandlerCtx<Self>, packet: PublishPacket) -> Result<()> {
|
|
||||||
println!(
|
|
||||||
"Client `{}` published to topic `{}`",
|
|
||||||
ctx.client_id, packet.topic
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(board_id) = packet.topic.strip_prefix("/sdcp/status/") {
|
|
||||||
let status = serde_json::from_slice::<Response<StatusData>>(&packet.data)?;
|
|
||||||
println!("Got status from `{}`", board_id);
|
|
||||||
println!("{:?}", status);
|
|
||||||
} else if let Some(board_id) = packet.topic.strip_prefix("/sdcp/response/") {
|
|
||||||
println!("Got command response from `{}`", board_id);
|
|
||||||
println!("{:?}", String::from_utf8_lossy(&packet.data));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_publish_ack(&self, _ctx: &HandlerCtx<Self>, _packet: PublishAckPacket) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_disconnect(&self, _ctx: &HandlerCtx<Self>) -> Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Mqtt {
|
|
||||||
fn send_command<Data: Serialize>(
|
|
||||||
&self,
|
|
||||||
ctx: &HandlerCtx<Self>,
|
|
||||||
cmd: u16,
|
|
||||||
command: Data,
|
|
||||||
) -> Result<()> {
|
|
||||||
let id = ctx.next_packet_id();
|
|
||||||
|
|
||||||
let data = Command::new(cmd, command, self.id.to_owned());
|
|
||||||
let data = serde_json::to_vec(&data).unwrap();
|
|
||||||
|
|
||||||
ctx.server
|
|
||||||
.send_packet(
|
|
||||||
ctx.client_id,
|
|
||||||
PublishPacket {
|
|
||||||
flags: PublishFlags::QOS1,
|
|
||||||
topic: format!("/sdcp/request/{}", self.status.mainboard_id),
|
|
||||||
packet_id: Some(id),
|
|
||||||
data,
|
|
||||||
}
|
|
||||||
.to_packet(),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
let mqtt = Mqtt::new();
|
||||||
|
let mqtt_inner = mqtt.clone();
|
||||||
|
MqttServer::new(mqtt).start_async()?;
|
||||||
|
|
||||||
let socket = UdpSocket::bind("0.0.0.0:3000")?;
|
let socket = UdpSocket::bind("0.0.0.0:3000")?;
|
||||||
|
|
||||||
let msg = b"M99999";
|
let msg = b"M99999";
|
||||||
@@ -129,15 +22,12 @@ fn main() -> Result<()> {
|
|||||||
"Got status from `{}`",
|
"Got status from `{}`",
|
||||||
response.data.attributes.machine_name
|
response.data.attributes.machine_name
|
||||||
);
|
);
|
||||||
|
mqtt_inner.add_future_client(response.data.attributes, response.id);
|
||||||
MqttServer::new(Mqtt {
|
|
||||||
status: response.data.attributes,
|
|
||||||
id: response.id,
|
|
||||||
})
|
|
||||||
.start_async()?;
|
|
||||||
|
|
||||||
let msg = b"M66666 1883";
|
let msg = b"M66666 1883";
|
||||||
socket.send_to(msg, "192.168.1.233:3000")?;
|
socket.send_to(msg, "192.168.1.233:3000")?;
|
||||||
|
|
||||||
Ok(())
|
loop {
|
||||||
|
thread::park()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
net::{TcpListener, TcpStream},
|
net::{TcpListener, TcpStream},
|
||||||
sync::{
|
sync::Arc,
|
||||||
atomic::{AtomicU16, Ordering},
|
|
||||||
Arc,
|
|
||||||
},
|
|
||||||
thread,
|
thread,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -20,6 +17,7 @@ use packets::{
|
|||||||
Packet,
|
Packet,
|
||||||
};
|
};
|
||||||
use parking_lot::{lock_api::MutexGuard, MappedMutexGuard, Mutex};
|
use parking_lot::{lock_api::MutexGuard, MappedMutexGuard, Mutex};
|
||||||
|
use soon::Soon;
|
||||||
|
|
||||||
mod misc;
|
mod misc;
|
||||||
pub mod packets;
|
pub mod packets;
|
||||||
@@ -27,38 +25,33 @@ pub mod packets;
|
|||||||
pub struct MqttServer<H: MqttHandler> {
|
pub struct MqttServer<H: MqttHandler> {
|
||||||
listeners: Mutex<Option<TcpListener>>,
|
listeners: Mutex<Option<TcpListener>>,
|
||||||
clients: Mutex<HashMap<u64, TcpStream>>,
|
clients: Mutex<HashMap<u64, TcpStream>>,
|
||||||
handler: H,
|
handler: Soon<H>,
|
||||||
}
|
|
||||||
|
|
||||||
pub struct HandlerCtx<Handler: MqttHandler> {
|
|
||||||
pub server: Arc<MqttServer<Handler>>,
|
|
||||||
pub client_id: u64,
|
|
||||||
next_packet_id: AtomicU16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait MqttHandler
|
pub trait MqttHandler
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
fn on_connect(&self, ctx: &HandlerCtx<Self>, packet: ConnectPacket)
|
fn init(&self, server: Arc<MqttServer<Self>>);
|
||||||
-> Result<ConnectAckPacket>;
|
fn on_connect(&self, client_id: u64, packet: ConnectPacket) -> Result<ConnectAckPacket>;
|
||||||
fn on_subscribe(
|
fn on_subscribe(&self, client_id: u64, packet: SubscribePacket) -> Result<SubscribeAckPacket>;
|
||||||
&self,
|
fn on_publish(&self, client_id: u64, packet: PublishPacket) -> Result<()>;
|
||||||
ctx: &HandlerCtx<Self>,
|
fn on_publish_ack(&self, client_id: u64, packet: PublishAckPacket) -> Result<()>;
|
||||||
packet: SubscribePacket,
|
fn on_disconnect(&self, client_id: u64) -> Result<()>;
|
||||||
) -> Result<SubscribeAckPacket>;
|
|
||||||
fn on_publish(&self, ctx: &HandlerCtx<Self>, packet: PublishPacket) -> Result<()>;
|
|
||||||
fn on_publish_ack(&self, ctx: &HandlerCtx<Self>, packet: PublishAckPacket) -> Result<()>;
|
|
||||||
fn on_disconnect(&self, ctx: &HandlerCtx<Self>) -> Result<()>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: MqttHandler + Send + Sync + 'static> MqttServer<H> {
|
impl<H: MqttHandler + Send + Sync + 'static> MqttServer<H> {
|
||||||
pub fn new(handler: H) -> Arc<Self> {
|
pub fn new(handler: H) -> Arc<Self> {
|
||||||
Arc::new(Self {
|
let this = Arc::new(Self {
|
||||||
listeners: Mutex::new(None),
|
listeners: Mutex::new(None),
|
||||||
clients: Mutex::new(HashMap::new()),
|
clients: Mutex::new(HashMap::new()),
|
||||||
handler,
|
handler: Soon::empty(),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
handler.init(this.clone());
|
||||||
|
this.handler.replace(handler);
|
||||||
|
|
||||||
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_async(self: Arc<Self>) -> Result<()> {
|
pub fn start_async(self: Arc<Self>) -> Result<()> {
|
||||||
@@ -102,22 +95,10 @@ impl<H: MqttHandler + Send + Sync + 'static> MqttServer<H> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<H: MqttHandler> HandlerCtx<H> {
|
|
||||||
pub fn next_packet_id(&self) -> u16 {
|
|
||||||
self.next_packet_id.fetch_add(1, Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handle_client<H>(server: Arc<MqttServer<H>>, client_id: u64, mut stream: TcpStream) -> Result<()>
|
fn handle_client<H>(server: Arc<MqttServer<H>>, client_id: u64, mut stream: TcpStream) -> Result<()>
|
||||||
where
|
where
|
||||||
H: MqttHandler + Send + Sync + 'static,
|
H: MqttHandler + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
let ctx = HandlerCtx {
|
|
||||||
server: server.clone(),
|
|
||||||
client_id,
|
|
||||||
next_packet_id: AtomicU16::new(0),
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let packet = Packet::read(&mut stream)?;
|
let packet = Packet::read(&mut stream)?;
|
||||||
|
|
||||||
@@ -127,7 +108,7 @@ where
|
|||||||
|
|
||||||
server
|
server
|
||||||
.handler
|
.handler
|
||||||
.on_connect(&ctx, packet)?
|
.on_connect(client_id, packet)?
|
||||||
.to_packet()
|
.to_packet()
|
||||||
.write(&mut stream)?;
|
.write(&mut stream)?;
|
||||||
}
|
}
|
||||||
@@ -136,7 +117,7 @@ where
|
|||||||
|
|
||||||
server
|
server
|
||||||
.handler
|
.handler
|
||||||
.on_subscribe(&ctx, packet)?
|
.on_subscribe(client_id, packet)?
|
||||||
.to_packet()
|
.to_packet()
|
||||||
.write(&mut stream)?;
|
.write(&mut stream)?;
|
||||||
}
|
}
|
||||||
@@ -147,7 +128,7 @@ where
|
|||||||
.contains(PublishFlags::QOS1)
|
.contains(PublishFlags::QOS1)
|
||||||
.then(|| packet.packet_id.unwrap());
|
.then(|| packet.packet_id.unwrap());
|
||||||
|
|
||||||
server.handler.on_publish(&ctx, packet)?;
|
server.handler.on_publish(client_id, packet)?;
|
||||||
|
|
||||||
if let Some(packet_id) = packet_id {
|
if let Some(packet_id) = packet_id {
|
||||||
PublishAckPacket { packet_id }
|
PublishAckPacket { packet_id }
|
||||||
@@ -157,10 +138,10 @@ where
|
|||||||
}
|
}
|
||||||
PublishAckPacket::PACKET_TYPE => {
|
PublishAckPacket::PACKET_TYPE => {
|
||||||
let packet = PublishAckPacket::from_packet(&packet)?;
|
let packet = PublishAckPacket::from_packet(&packet)?;
|
||||||
server.handler.on_publish_ack(&ctx, packet)?;
|
server.handler.on_publish_ack(client_id, packet)?;
|
||||||
}
|
}
|
||||||
0x0E => {
|
0x0E => {
|
||||||
server.handler.on_disconnect(&ctx)?;
|
server.handler.on_disconnect(client_id)?;
|
||||||
server.remove_client(client_id);
|
server.remove_client(client_id);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -9,6 +9,7 @@ pub struct SubscribeAckPacket {
|
|||||||
pub return_codes: Vec<SubscribeReturnCode>,
|
pub return_codes: Vec<SubscribeReturnCode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
pub enum SubscribeReturnCode {
|
pub enum SubscribeReturnCode {
|
||||||
Success(QoS),
|
Success(QoS),
|
||||||
Failure,
|
Failure,
|
||||||
|
222
remote_send/src/mqtt_server.rs
Normal file
222
remote_send/src/mqtt_server.rs
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
ops::Deref,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicU16, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
use parking_lot::RwLock;
|
||||||
|
use soon::Soon;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
commands::{Command, CommandTrait, DisconnectCommand},
|
||||||
|
mqtt::{
|
||||||
|
packets::{
|
||||||
|
connect::ConnectPacket,
|
||||||
|
connect_ack::{ConnectAckFlags, ConnectAckPacket, ConnectReturnCode},
|
||||||
|
publish::{PublishFlags, PublishPacket},
|
||||||
|
publish_ack::PublishAckPacket,
|
||||||
|
subscribe::SubscribePacket,
|
||||||
|
subscribe_ack::{SubscribeAckPacket, SubscribeReturnCode},
|
||||||
|
},
|
||||||
|
MqttHandler, MqttServer,
|
||||||
|
},
|
||||||
|
status::{Attributes, StatusData},
|
||||||
|
Response,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct MqttInner {
|
||||||
|
server: Soon<Arc<MqttServer<Mqtt>>>,
|
||||||
|
// mainboard_id -> MqttClient
|
||||||
|
clients: RwLock<HashMap<String, MqttClient>>,
|
||||||
|
// client_id -> mainboard_id
|
||||||
|
client_ids: RwLock<HashMap<u64, String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Mqtt {
|
||||||
|
inner: Arc<MqttInner>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MqttClient {
|
||||||
|
status: Attributes,
|
||||||
|
machine_id: String,
|
||||||
|
client_id: Option<u64>,
|
||||||
|
next_packet_id: AtomicU16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MqttHandler for Mqtt {
|
||||||
|
fn init(&self, server: Arc<MqttServer<Self>>) {
|
||||||
|
self.server.replace(server);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_connect(&self, client_id: u64, _packet: ConnectPacket) -> Result<ConnectAckPacket> {
|
||||||
|
println!("Client `{client_id}` connected");
|
||||||
|
|
||||||
|
Ok(ConnectAckPacket {
|
||||||
|
flags: ConnectAckFlags::empty(),
|
||||||
|
return_code: ConnectReturnCode::Accepted,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_subscribe(&self, client_id: u64, packet: SubscribePacket) -> Result<SubscribeAckPacket> {
|
||||||
|
println!(
|
||||||
|
"Client `{client_id}` subscribed to topics: {:?}",
|
||||||
|
packet.filters
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut return_codes = vec![SubscribeReturnCode::Failure; packet.filters.len()];
|
||||||
|
if let Some((idx, mainboard_id, qos)) =
|
||||||
|
packet
|
||||||
|
.filters
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.find_map(|(idx, (topic, qos))| {
|
||||||
|
topic.strip_prefix("/sdcp/request/").map(|x| (idx, x, qos))
|
||||||
|
})
|
||||||
|
{
|
||||||
|
if self.clients.read().get(mainboard_id).is_none() {
|
||||||
|
eprintln!("Client `{mainboard_id}` does not exist.");
|
||||||
|
return Ok(SubscribeAckPacket {
|
||||||
|
packet_id: packet.packet_id,
|
||||||
|
return_codes,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return_codes[idx] = SubscribeReturnCode::Success(*qos);
|
||||||
|
self.client_ids
|
||||||
|
.write()
|
||||||
|
.insert(client_id, mainboard_id.to_owned());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(SubscribeAckPacket {
|
||||||
|
packet_id: packet.packet_id,
|
||||||
|
return_codes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_publish(&self, client_id: u64, packet: PublishPacket) -> Result<()> {
|
||||||
|
println!("Client `{client_id}` published to topic `{}`", packet.topic);
|
||||||
|
|
||||||
|
if let Some(board_id) = packet.topic.strip_prefix("/sdcp/status/") {
|
||||||
|
let status = serde_json::from_slice::<Response<StatusData>>(&packet.data)?;
|
||||||
|
println!("Got status from `{}`", board_id);
|
||||||
|
println!("{:?}", status);
|
||||||
|
} else if let Some(board_id) = packet.topic.strip_prefix("/sdcp/response/") {
|
||||||
|
println!("Got command response from `{}`", board_id);
|
||||||
|
println!("{:?}", String::from_utf8_lossy(&packet.data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_publish_ack(&self, _client_id: u64, _packet: PublishAckPacket) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_disconnect(&self, client_id: u64) -> Result<()> {
|
||||||
|
let machine_id = self.client_ids.write().remove(&client_id);
|
||||||
|
if let Some(machine_id) = machine_id {
|
||||||
|
self.clients.write().remove(&machine_id);
|
||||||
|
println!("Client `{machine_id}` disconnected");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Mqtt {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
inner: Arc::new(MqttInner {
|
||||||
|
server: Soon::empty(),
|
||||||
|
clients: RwLock::new(HashMap::new()),
|
||||||
|
client_ids: RwLock::new(HashMap::new()),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send_command<Data: CommandTrait>(
|
||||||
|
&self,
|
||||||
|
mainboard_id: &str,
|
||||||
|
command: Data,
|
||||||
|
) -> Result<()> {
|
||||||
|
let clients = self.clients.read();
|
||||||
|
let client = clients.get(mainboard_id).unwrap();
|
||||||
|
let packet_id = client.next_id();
|
||||||
|
|
||||||
|
let Some(client_id) = client.client_id else {
|
||||||
|
eprintln!("Client `{mainboard_id}` is not connected. Command not sent.");
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
let data = Response {
|
||||||
|
data: Command::new(Data::CMD, command, client.machine_id.to_owned()),
|
||||||
|
id: String::new(),
|
||||||
|
};
|
||||||
|
let data = serde_json::to_vec(&data).unwrap();
|
||||||
|
|
||||||
|
self.server
|
||||||
|
.send_packet(
|
||||||
|
client_id,
|
||||||
|
PublishPacket {
|
||||||
|
flags: PublishFlags::QOS1,
|
||||||
|
topic: format!("/sdcp/request/{}", client.status.mainboard_id),
|
||||||
|
packet_id: Some(packet_id),
|
||||||
|
data,
|
||||||
|
}
|
||||||
|
.to_packet(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_future_client(&self, attributes: Attributes, machine_id: String) {
|
||||||
|
if self.clients.read().contains_key(&attributes.mainboard_id) {
|
||||||
|
println!("Client `{}` already exists.", attributes.mainboard_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mainboard_id = attributes.mainboard_id.clone();
|
||||||
|
let client = MqttClient {
|
||||||
|
status: attributes,
|
||||||
|
machine_id: machine_id.clone(),
|
||||||
|
client_id: None,
|
||||||
|
next_packet_id: AtomicU16::new(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut clients = self.clients.write();
|
||||||
|
clients.insert(mainboard_id, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MqttClient {
|
||||||
|
fn next_id(&self) -> u16 {
|
||||||
|
self.next_packet_id.fetch_add(1, Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Mqtt {
|
||||||
|
type Target = MqttInner;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for Mqtt {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
for mainboard_id in self.clients.read().keys() {
|
||||||
|
println!("Disconnecting `{mainboard_id}`");
|
||||||
|
let _ = self.send_command(mainboard_id, DisconnectCommand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user