feat: The rest of the owl

This commit is contained in:
Baud 2024-05-25 17:44:14 +01:00
parent 8cce21193a
commit be96c86f74
9 changed files with 275 additions and 117 deletions

View File

@ -44,6 +44,10 @@ fn main() {
panic!("Running `trunk build` failed, reason {err}"); panic!("Running `trunk build` failed, reason {err}");
} }
if let Ok(trunk_output_string) = String::from_utf8(trunk_build_output.stdout) {
println!("{trunk_output_string}");
}
let dist_paths = fs::read_dir("./frontend/dist").expect("Could not read dist directory"); let dist_paths = fs::read_dir("./frontend/dist").expect("Could not read dist directory");
let paths: Vec<Result<DirEntry, Error>> = dist_paths.collect(); let paths: Vec<Result<DirEntry, Error>> = dist_paths.collect();
let paths: Vec<(String, PathBuf)> = paths let paths: Vec<(String, PathBuf)> = paths

View File

@ -1,17 +1,16 @@
use std::{ use std::{
collections::VecDeque, collections::{HashMap, VecDeque},
fmt::Display,
mem, mem,
net::{IpAddr, Ipv4Addr}, net::{IpAddr, Ipv4Addr},
}; };
use egui::{Button, Color32, RichText, Sense, Stroke, Vec2}; use egui::{Button, Color32, RichText, Rounding, Sense, Stroke, Vec2};
use ewebsock::{Options, WsReceiver, WsSender}; use ewebsock::{Options, WsReceiver, WsSender};
use wasm_timer::Instant; use wasm_timer::Instant;
use crate::websocket::{ use crate::websocket::{
AddHyperdeckRequest, ClientRequest, HyperdeckConnectionState, RemoveHyperdeckRequest, AddHyperdeckRequest, ClientRequest, HyperdeckConnectionState, HyperdeckRecordBay,
ServerEvent, RecordingState, RemoveHyperdeckRequest, ServerEvent,
}; };
pub struct HyperdeckMonitorApp { pub struct HyperdeckMonitorApp {
@ -37,28 +36,69 @@ impl Default for HyperdeckMonitorApp {
new_hyperdeck_name: "".to_owned(), new_hyperdeck_name: "".to_owned(),
new_hyperdeck_port: 9993.to_string(), new_hyperdeck_port: 9993.to_string(),
hyperdecks: vec![ hyperdecks: vec![
// Hyperdeck { Hyperdeck {
// id: "test-1".to_string(), id: "test-1".to_string(),
// name: "Test Hyperdeck 1".to_string(), name: "Description: Connected Hyperdeck - Not Recording - Not Much Time"
// ip: IpAddr::V4(Ipv4Addr::new(192, 168, 10, 1)), .to_string(),
// status: HyperdeckStatus::Connected, ip: IpAddr::V4(Ipv4Addr::new(192, 168, 10, 1)),
// recording_bays: vec![HyperdeckRecordBay { status: HyperdeckStatus::Connected,
// status: RecordingStatus::NotRecording, recording_status: RecordingState::NotRecording,
// storage_capacity_mb: 500_000, slots: vec![(
// recording_time_remaining: TimeRemaining(60), 0usize,
// }], HyperdeckRecordBay {
// }, recording_time_remaining: 60,
// Hyperdeck { },
// id: "test-2".to_string(), )]
// name: "Test Hyperdeck 2".to_string(), .into_iter()
// ip: IpAddr::V4(Ipv4Addr::new(192, 168, 10, 2)), .collect::<HashMap<usize, HyperdeckRecordBay>>(),
// status: HyperdeckStatus::Disconnected, },
// recording_bays: vec![HyperdeckRecordBay { Hyperdeck {
// status: RecordingStatus::NotRecording, id: "test-2".to_string(),
// storage_capacity_mb: 500_000, name: "Description: Connected Hyperdeck - Recording - Not Much Time"
// recording_time_remaining: TimeRemaining(3600 * 5), // 5 Hours .to_string(),
// }], ip: IpAddr::V4(Ipv4Addr::new(192, 168, 10, 2)),
// }, status: HyperdeckStatus::Connected,
recording_status: RecordingState::Recording,
slots: vec![(
0usize,
HyperdeckRecordBay {
recording_time_remaining: 60,
},
)]
.into_iter()
.collect::<HashMap<usize, HyperdeckRecordBay>>(),
},
Hyperdeck {
id: "test-3".to_string(),
name: "Description: Connected Hyperdeck - Recording - Plenty of Time"
.to_string(),
ip: IpAddr::V4(Ipv4Addr::new(192, 168, 10, 3)),
status: HyperdeckStatus::Connected,
recording_status: RecordingState::Recording,
slots: vec![(
0usize,
HyperdeckRecordBay {
recording_time_remaining: 60 * 30,
},
)]
.into_iter()
.collect::<HashMap<usize, HyperdeckRecordBay>>(),
},
Hyperdeck {
id: "test-4".to_string(),
name: "Description: Disconnected Hyperdeck".to_string(),
ip: IpAddr::V4(Ipv4Addr::new(192, 168, 10, 4)),
status: HyperdeckStatus::Disconnected,
recording_status: RecordingState::NotRecording,
slots: vec![(
0usize,
HyperdeckRecordBay {
recording_time_remaining: 3600 * 5, // 5 Hours
},
)]
.into_iter()
.collect::<HashMap<usize, HyperdeckRecordBay>>(),
},
], ],
websocket_message_queue: VecDeque::new(), websocket_message_queue: VecDeque::new(),
ws_sender, ws_sender,
@ -87,18 +127,14 @@ impl eframe::App for HyperdeckMonitorApp {
name: hyperdeck.name, name: hyperdeck.name,
ip: hyperdeck.ip.parse().unwrap(), ip: hyperdeck.ip.parse().unwrap(),
status: hyperdeck.connection_state.into(), status: hyperdeck.connection_state.into(),
recording_bays: vec![], recording_status: hyperdeck.recording_status,
slots: hyperdeck.slots,
}) })
} }
} }
} }
} }
} }
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
egui::menu::bar(ui, |ui| {
egui::widgets::global_dark_light_mode_buttons(ui);
});
});
egui::CentralPanel::default().show(ctx, |ui| { egui::CentralPanel::default().show(ctx, |ui| {
add_hyperdeck_panel( add_hyperdeck_panel(
@ -118,11 +154,6 @@ impl eframe::App for HyperdeckMonitorApp {
&mut self.websocket_message_queue, &mut self.websocket_message_queue,
); );
}); });
ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| {
connection_status(ui);
egui::warn_if_debug_build(ui);
});
}); });
if self.last_blink_change.elapsed().as_secs() >= 1 { if self.last_blink_change.elapsed().as_secs() >= 1 {
@ -195,27 +226,41 @@ fn hyperdeck_list(
)); ));
} }
}); });
if !hyperdeck.recording_bays.is_empty() if matches!(hyperdeck.status, HyperdeckStatus::Connected) {
&& matches!(hyperdeck.status, HyperdeckStatus::Connected) ui.horizontal(|ui| {
{ match hyperdeck.recording_status {
let recording_bays_text: RichText = "Recording Bays".into(); RecordingState::Recording => {
ui.label(recording_bays_text.size(16.0).strong()); let (response, painter) =
for (index, bay) in hyperdeck.recording_bays.iter().enumerate() { ui.allocate_painter(Vec2 { x: 16.0, y: 16.0 }, Sense::hover());
ui.horizontal(|ui| { let rect = response.rect;
let bay_label: RichText = format!("Bay {}", index + 1).into(); painter.rect(
ui.label(bay_label.strong()); rect,
match bay.status { Rounding::ZERO,
RecordingStatus::Recording => ui.label("Recording"), Color32::from_rgb(255, 255, 255),
RecordingStatus::NotRecording => ui.label("Not Recording"), Stroke::NONE,
}; );
ui.label(format!( let recording_text: RichText = "[Recording]".into();
"Total Storage Capacity: {}GB", ui.label(
bay.storage_capacity_mb / 1000, recording_text
)); .color(Color32::from_rgb(255, 255, 255))
let time_remaining_text: RichText = .strong(),
format!("Time remaining: {}", bay.recording_time_remaining).into(); );
}
RecordingState::NotRecording => {
ui.label("[Not Recording]");
}
};
});
if bay.recording_time_remaining.0 > 15 * 60 || !blink { for (index, slot) in hyperdeck.slots.iter() {
ui.horizontal(|ui| {
let slot_label: RichText = format!("Slot {}", index + 1).into();
ui.label(slot_label.strong());
let time_remaining_text: RichText =
format!("Time remaining: {}", slot.recording_time_remaining).into();
if slot.recording_time_remaining > 15 * 60 || !blink {
ui.label(time_remaining_text); ui.label(time_remaining_text);
} else { } else {
ui.label(time_remaining_text.color(Color32::RED)); ui.label(time_remaining_text.color(Color32::RED));
@ -228,20 +273,14 @@ fn hyperdeck_list(
} }
} }
fn connection_status(ui: &mut egui::Ui) {
ui.horizontal(|ui| {
// TODO: Make it real
ui.label("Connected");
});
}
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
struct Hyperdeck { struct Hyperdeck {
id: String, id: String,
name: String, name: String,
ip: IpAddr, ip: IpAddr,
status: HyperdeckStatus, status: HyperdeckStatus,
recording_bays: Vec<HyperdeckRecordBay>, recording_status: RecordingState,
slots: HashMap<usize, HyperdeckRecordBay>,
} }
#[derive(serde::Deserialize, serde::Serialize)] #[derive(serde::Deserialize, serde::Serialize)]
@ -258,28 +297,3 @@ impl From<HyperdeckConnectionState> for HyperdeckStatus {
} }
} }
} }
#[derive(serde::Deserialize, serde::Serialize)]
struct HyperdeckRecordBay {
status: RecordingStatus,
/// Storage capacity in MB.
storage_capacity_mb: u64,
/// Recording time available in seconds.
recording_time_remaining: TimeRemaining,
}
#[derive(serde::Deserialize, serde::Serialize)]
struct TimeRemaining(u64);
impl Display for TimeRemaining {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let time = hrtime::from_sec_padded(self.0);
write!(f, "{time}")
}
}
#[derive(serde::Deserialize, serde::Serialize)]
enum RecordingStatus {
Recording,
NotRecording,
}

View File

@ -1,5 +1,6 @@
import { Hyperdeck, Commands } from 'hyperdeck-connection'; import { Hyperdeck, Commands } from 'hyperdeck-connection';
import WebSocket from 'ws'; import WebSocket from 'ws';
import { v4 as uuidv4 } from 'uuid';
interface WrappedHyperdeck { interface WrappedHyperdeck {
ip: String, ip: String,
@ -26,8 +27,11 @@ type WebSocketMessage = {
const wss = new WebSocket.Server({ port: 7867 }); const wss = new WebSocket.Server({ port: 7867 });
wss.on('connection', function connection(ws) { const connected_clients: Map<string, WebSocket> = new Map();
ws.on('message', function message(data) {
wss.on('connection', (ws) => {
const clientId = uuidv4();
ws.on('message', (data) => {
try { try {
const message = JSON.parse(data.toString()) as Partial<WebSocketMessage>; const message = JSON.parse(data.toString()) as Partial<WebSocketMessage>;
handle_message(message) handle_message(message)
@ -35,11 +39,16 @@ wss.on('connection', function connection(ws) {
return; return;
} }
}); });
ws.on('close', () => {
connected_clients.delete(clientId)
})
ws.send(JSON.stringify({ ws.send(JSON.stringify({
event: "log", event: "log",
message: "Hello" message: "Hello"
})); }));
connected_clients.set(clientId, ws);
}); });
function exhaustiveMatch(_never: never) { function exhaustiveMatch(_never: never) {
@ -68,24 +77,64 @@ function handle_message(message: Partial<WebSocketMessage>) {
hyperdeck: newHyperdeck hyperdeck: newHyperdeck
}); });
newHyperdeck.on('connected', (info) => { newHyperdeck.on('connected', (_info) => {
console.log(JSON.stringify(info)) notifyClients({
event: "hyperdeck_connected",
id: message.id
})
newHyperdeck.sendCommand(new Commands.TransportInfoCommand()).then((transportInfo) => { setInterval(() => {
console.log(JSON.stringify(transportInfo)) newHyperdeck.sendCommand(new Commands.TransportInfoCommand()).then((transportInfo) => {
notifyClients({
event: "record_state",
hyperdeckId: message.id,
state: transportInfo.status,
})
})
})
newHyperdeck.sendCommand(new Commands.DeviceInfoCommand()).then((info) => {
for (let index = 0; index < info.slots; index++) {
setInterval(() => {
newHyperdeck.sendCommand(new Commands.SlotInfoCommand(index)).then((slot) => {
notifyClients({
event: "record_time_remaining",
hyperdeckId: message.id,
slotId: slot.slotId,
remaining: slot.recordingTime
})
})
})
}
}) })
}) })
newHyperdeck.on('notify.slot', function (state) { newHyperdeck.on('notify.slot', function (slot) {
console.log(JSON.stringify(state)) // catch the slot state change. notifyClients({
event: "record_time_remaining",
hyperdeckId: message.id,
slotId: slot.slotId,
remaining: slot.recordingTime
})
}) })
newHyperdeck.on('notify.transport', function (state) { newHyperdeck.on('notify.transport', function (state) {
console.log(JSON.stringify(state)) // catch the transport state change. notifyClients({
event: "record_state",
hyperdeckId: message.id,
state: state.status
})
}) })
newHyperdeck.on('error', (err) => { newHyperdeck.on('error', (err) => {
console.log('Hyperdeck error', JSON.stringify(err)) console.log('Hyperdeck error', JSON.stringify(err))
}) })
newHyperdeck.on('disconnected', () => {
notifyClients({
event: "hyperdeck_disconnected",
id: message.id
})
})
newHyperdeck.connect(message.ip, message.port) newHyperdeck.connect(message.ip, message.port)
break; break;
@ -105,3 +154,9 @@ function handle_message(message: Partial<WebSocketMessage>) {
exhaustiveMatch(message) exhaustiveMatch(message)
} }
} }
function notifyClients(message: object) {
connected_clients.forEach((client) => {
client.send(JSON.stringify(message))
})
}

13
package-lock.json generated
View File

@ -9,6 +9,7 @@
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"hyperdeck-connection": "^2.0.1", "hyperdeck-connection": "^2.0.1",
"uuid": "^9.0.1",
"ws": "^8.17.0" "ws": "^8.17.0"
}, },
"devDependencies": { "devDependencies": {
@ -75,6 +76,18 @@
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true "dev": true
}, },
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/ws": { "node_modules/ws": {
"version": "8.17.0", "version": "8.17.0",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",

View File

@ -7,6 +7,7 @@
}, },
"dependencies": { "dependencies": {
"hyperdeck-connection": "^2.0.1", "hyperdeck-connection": "^2.0.1",
"uuid": "^9.0.1",
"ws": "^8.17.0" "ws": "^8.17.0"
}, },
"devDependencies": { "devDependencies": {

View File

@ -4,7 +4,6 @@ use axum::response::Html;
use axum::Json; use axum::Json;
use axum::{ use axum::{
body::Bytes, body::Bytes,
extract::Path,
http::{header, HeaderValue, Method}, http::{header, HeaderValue, Method},
response::IntoResponse, response::IntoResponse,
routing::get, routing::get,
@ -66,7 +65,7 @@ pub async fn initialize_api(
let clients = state_clients.lock().await; let clients = state_clients.lock().await;
let state_json = serde_json::to_string(&ServerEvent::HyperdeckMonitorState( let state_json = serde_json::to_string(&ServerEvent::HyperdeckMonitorState(
hyperdeck_monitor_state.into(), hyperdeck_monitor_state,
)) ))
.unwrap(); .unwrap();
for (_, client) in clients.iter() { for (_, client) in clients.iter() {

View File

@ -40,6 +40,9 @@ pub struct HyperdeckState {
pub ip: String, pub ip: String,
pub port: u16, pub port: u16,
pub connection_state: HyperdeckConnectionState, pub connection_state: HyperdeckConnectionState,
pub recording_status: RecordingState,
// HashMap to allow for sparse entries.
pub slots: HashMap<usize, HyperdeckRecordBay>,
} }
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
@ -47,3 +50,15 @@ pub enum HyperdeckConnectionState {
Connected, Connected,
Disconnected, Disconnected,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RecordingState {
Recording,
NotRecording,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HyperdeckRecordBay {
/// Recording time available in seconds.
pub recording_time_remaining: u64,
}

View File

@ -32,7 +32,7 @@ pub async fn client_connection(
let current_state = state.read().await.clone(); let current_state = state.read().await.clone();
let state_json = let state_json =
serde_json::to_string(&ServerEvent::HyperdeckMonitorState(current_state.into())).unwrap(); serde_json::to_string(&ServerEvent::HyperdeckMonitorState(current_state)).unwrap();
client_sender.send(Message::Text(state_json.clone())).ok(); client_sender.send(Message::Text(state_json.clone())).ok();
client.sender = Some(client_sender); client.sender = Some(client_sender);

View File

@ -1,8 +1,8 @@
use std::{process::Stdio, time::Duration}; use std::{collections::HashMap, process::Stdio, time::Duration};
use api::message::{ use api::message::{
AddHyperdeckRequest, ClientRequest, HyperdeckConnectionState, HyperdeckMonitorState, AddHyperdeckRequest, ClientRequest, HyperdeckConnectionState, HyperdeckMonitorState,
HyperdeckState, RemoveHyperdeckRequest, HyperdeckRecordBay, HyperdeckState, RecordingState, RemoveHyperdeckRequest,
}; };
use color_eyre::Report; use color_eyre::Report;
use futures_util::{ use futures_util::{
@ -31,9 +31,8 @@ async fn main() {
let (node_ws_message_tx, node_ws_message_rx) = tokio::sync::mpsc::unbounded_channel(); let (node_ws_message_tx, node_ws_message_rx) = tokio::sync::mpsc::unbounded_channel();
let (node_commands_tx, node_commands_rx) = tokio::sync::mpsc::unbounded_channel(); let (node_commands_tx, node_commands_rx) = tokio::sync::mpsc::unbounded_channel();
let state = AppState::default();
let node_ws_communication = let node_ws_communication =
talk_to_node_ws(state, node_ws_message_tx, node_commands_rx, cancel.clone()).fuse(); talk_to_node_ws(node_ws_message_tx, node_commands_rx, cancel.clone()).fuse();
let (state_tx, state_rx) = tokio::sync::broadcast::channel(1); let (state_tx, state_rx) = tokio::sync::broadcast::channel(1);
let (client_request_tx, client_request_rx) = tokio::sync::mpsc::unbounded_channel(); let (client_request_tx, client_request_rx) = tokio::sync::mpsc::unbounded_channel();
@ -67,7 +66,7 @@ async fn main() {
async fn run( async fn run(
mut node_commands_tx: tokio::sync::mpsc::UnboundedSender<NodeWsCommand>, mut node_commands_tx: tokio::sync::mpsc::UnboundedSender<NodeWsCommand>,
mut node_ws_message_rx: tokio::sync::mpsc::UnboundedReceiver<NodeWsMessageReceived>, mut node_ws_message_rx: tokio::sync::mpsc::UnboundedReceiver<NodeWsMessageReceived>,
mut state_tx: tokio::sync::broadcast::Sender<HyperdeckMonitorState>, state_tx: tokio::sync::broadcast::Sender<HyperdeckMonitorState>,
mut client_request_rx: tokio::sync::mpsc::UnboundedReceiver<ClientRequest>, mut client_request_rx: tokio::sync::mpsc::UnboundedReceiver<ClientRequest>,
cancel: CancellationToken, cancel: CancellationToken,
) { ) {
@ -100,7 +99,7 @@ async fn run(
async fn handle_message_from_node( async fn handle_message_from_node(
msg: NodeWsMessageReceived, msg: NodeWsMessageReceived,
node_commands_tx: &mut tokio::sync::mpsc::UnboundedSender<NodeWsCommand>, _node_commands_tx: &mut tokio::sync::mpsc::UnboundedSender<NodeWsCommand>,
state: &mut HyperdeckMonitorState, state: &mut HyperdeckMonitorState,
) -> bool { ) -> bool {
match msg { match msg {
@ -120,6 +119,38 @@ async fn handle_message_from_node(
}); });
true true
} }
NodeWsMessageReceived::RecordState {
hyperdeck_id,
status,
} => {
if let Some(hyperdeck) = state.hyperdecks.get_mut(&hyperdeck_id) {
hyperdeck.recording_status = if matches!(status, TransportStatus::Record) {
RecordingState::Recording
} else {
RecordingState::NotRecording
};
true
} else {
false
}
}
NodeWsMessageReceived::RecordTimeRemaining {
hyperdeck_id,
slot_id,
remaining,
} => {
if let Some(hyperdeck) = state.hyperdecks.get_mut(&hyperdeck_id) {
hyperdeck
.slots
.entry(slot_id)
.or_insert(HyperdeckRecordBay {
recording_time_remaining: remaining,
});
true
} else {
false
}
}
} }
} }
@ -139,6 +170,8 @@ async fn handle_message_from_client(
ip: ip.clone(), ip: ip.clone(),
port, port,
connection_state: api::message::HyperdeckConnectionState::Disconnected, connection_state: api::message::HyperdeckConnectionState::Disconnected,
recording_status: api::message::RecordingState::NotRecording,
slots: HashMap::new(),
}, },
); );
let _ = node_commands_tx.send(NodeWsCommand::AddHyperdeck(AddHyperdeckCommand { let _ = node_commands_tx.send(NodeWsCommand::AddHyperdeck(AddHyperdeckCommand {
@ -210,9 +243,6 @@ async fn run_node_process(cancel: CancellationToken) {
} }
} }
#[derive(Default)]
struct AppState {}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type")] #[serde(tag = "type")]
enum NodeWsCommand { enum NodeWsCommand {
@ -223,12 +253,40 @@ enum NodeWsCommand {
} }
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "snake_case")]
#[serde(tag = "event")] #[serde(tag = "event")]
enum NodeWsMessageReceived { enum NodeWsMessageReceived {
Log { message: String }, Log {
HyperdeckConnected { id: String }, message: String,
HypderdeckDisconnected { id: String }, },
HyperdeckConnected {
id: String,
},
HypderdeckDisconnected {
id: String,
},
RecordState {
hyperdeck_id: String,
status: TransportStatus,
},
RecordTimeRemaining {
hyperdeck_id: String,
slot_id: usize,
remaining: u64,
},
}
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
enum TransportStatus {
Preview,
Stopped,
Play,
Forward,
Rewind,
Jog,
Shuttle,
Record,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -246,7 +304,6 @@ struct RemoveHyperdeckCommand {
} }
async fn talk_to_node_ws( async fn talk_to_node_ws(
state: AppState,
ws_message_tx: tokio::sync::mpsc::UnboundedSender<NodeWsMessageReceived>, ws_message_tx: tokio::sync::mpsc::UnboundedSender<NodeWsMessageReceived>,
commands_rx: tokio::sync::mpsc::UnboundedReceiver<NodeWsCommand>, commands_rx: tokio::sync::mpsc::UnboundedReceiver<NodeWsCommand>,
cancel: CancellationToken, cancel: CancellationToken,