diff --git a/machines/src/laser/act.rs b/machines/src/laser/act.rs index 501dafc2..95e1d266 100644 --- a/machines/src/laser/act.rs +++ b/machines/src/laser/act.rs @@ -41,7 +41,6 @@ impl MachineAct for LaserMachine { } fn act_machine_message(&mut self, msg: MachineMessage) { - tracing::info!("{:?}", msg); match msg { MachineMessage::SubscribeNamespace(namespace) => { self.namespace.namespace = Some(namespace); @@ -69,7 +68,8 @@ impl MachineAct for LaserMachine { /*Doesnt connect to any Machine so do nothing*/ { () - } + }, + MachineMessage::RequestValues(sender) => sender.send_blocking(crate::MachineValues { state: serde_json::Value::Null, live_values: serde_json::Value::Null }) } } } diff --git a/machines/src/lib.rs b/machines/src/lib.rs index 29d39a5e..3961941c 100644 --- a/machines/src/lib.rs +++ b/machines/src/lib.rs @@ -13,9 +13,8 @@ use machine_identification::{ use serde::Serialize; use smol::channel::{Receiver, Sender}; use socketioxide::extract::SocketRef; +use std::any::Any; use std::fmt::Debug; -use std::{any::Any, sync::Arc, time::Instant}; -pub mod analog_input_test_machine; pub mod aquapath1; #[cfg(not(feature = "mock-machine"))] pub mod buffer1; @@ -36,7 +35,6 @@ pub const MACHINE_WINDER_V1: u16 = 0x0002; pub const MACHINE_EXTRUDER_V1: u16 = 0x0004; pub const MACHINE_LASER_V1: u16 = 0x0006; pub const MACHINE_MOCK: u16 = 0x0007; -#[cfg(not(feature = "mock-machine"))] pub const MACHINE_BUFFER_V1: u16 = 0x0008; pub const MACHINE_AQUAPATH_V1: u16 = 0x0009; pub const MACHINE_WAGO_POWER_V1: u16 = 0x000A; @@ -276,6 +274,12 @@ pub trait MachineAct { fn act(&mut self, now: Instant); } +#[derive(Serialize, Debug, Clone)] +pub struct MachineValues { + pub state: serde_json::Value, + pub live_values: serde_json::Value, +} + // generic MachineMessage allows us to implement actions // to manage or mutate machines with simple messages sent to the Recv Channel of the given Machine, // which the machine itself will handle to avoid locking @@ -287,6 +291,7 @@ pub enum MachineMessage { HttpApiJsonRequest(serde_json::Value), ConnectToMachine(MachineConnection), DisconnectMachine(MachineConnection), + RequestValues(Sender), } pub trait MachineApi { diff --git a/machines/src/machine_identification.rs b/machines/src/machine_identification.rs index 7895ca7e..8bc7f690 100644 --- a/machines/src/machine_identification.rs +++ b/machines/src/machine_identification.rs @@ -41,6 +41,29 @@ impl MachineIdentification { pub const fn is_valid(&self) -> bool { self.vendor != 0 && self.machine != 0 } + + pub fn vendor_str(&self) -> String { + match self.vendor { + x if x == VENDOR_QITECH => "QiTech".to_string(), + _ => "N/A".to_string() + } + } + + pub fn slug(&self) -> String { + match self.machine { + x if x == MACHINE_WINDER_V1 => "winder_v1".to_string(), + x if x == MACHINE_EXTRUDER_V1 => "extruder_v1".to_string(), + x if x == MACHINE_LASER_V1 => "laser_v1".to_string(), + x if x == MACHINE_MOCK => "mock".to_string(), + x if x == MACHINE_AQUAPATH_V1 => "aquapath_v1".to_string(), + x if x == MACHINE_BUFFER_V1 => "buffer_v1".to_string(), + x if x == MACHINE_EXTRUDER_V2 => "extruder_v2".to_string(), + x if x == TEST_MACHINE => "test_machine".to_string(), + x if x == IP20_TEST_MACHINE => "ip20_test_machine".to_string(), + x if x == ANALOG_INPUT_TEST_MACHINE => "analog_input_test_machine".to_string(), + _ => "N/A".to_string() + } + } } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] @@ -149,6 +172,18 @@ use ethercat_hal::helpers::ethercrab_types::{ use ethercrab::MainDevice; use ethercrab::SubDeviceIdentity; +use crate::ANALOG_INPUT_TEST_MACHINE; +use crate::IP20_TEST_MACHINE; +use crate::MACHINE_AQUAPATH_V1; +use crate::MACHINE_BUFFER_V1; +use crate::MACHINE_EXTRUDER_V1; +use crate::MACHINE_EXTRUDER_V2; +use crate::MACHINE_LASER_V1; +use crate::MACHINE_MOCK; +use crate::MACHINE_WINDER_V1; +use crate::TEST_MACHINE; +use crate::VENDOR_QITECH; + #[derive(Debug)] pub struct MachineIdentificationAddresses { pub vendor_word: u16, diff --git a/server/src/app_state.rs b/server/src/app_state.rs index c6820a2f..37b1d467 100644 --- a/server/src/app_state.rs +++ b/server/src/app_state.rs @@ -3,11 +3,12 @@ use crate::rest::handlers::write_machine_device_identification::MachineDeviceInf use crate::socketio::main_namespace::MainNamespaceEvents; use crate::socketio::main_namespace::machines_event::{MachineObj, MachinesEventBuilder}; use crate::socketio::namespaces::Namespaces; +use anyhow::{Result, bail}; use control_core::socketio::event::GenericEvent; use ethercat_hal::devices::EthercatDevice; use ethercrab::SubDeviceRef; use ethercrab::{MainDevice, SubDeviceGroup, subdevice_group::Op}; -use machines::machine_identification::{DeviceIdentification, MachineIdentificationUnique}; +use machines::machine_identification::{self, DeviceIdentification, MachineIdentificationUnique}; use machines::serial::registry::SERIAL_DEVICE_REGISTRY; use machines::{Machine, MachineMessage}; use serde::{Deserialize, Serialize}; @@ -123,11 +124,27 @@ impl EthercatSetup { impl SharedState { pub async fn send_machines_event(&self) { - let event = MachinesEventBuilder().build(self.current_machines_meta.lock().await.clone()); + let event = MachinesEventBuilder().build(self.get_machines_meta().await); let main_namespace = &mut self.socketio_setup.namespaces.write().await.main_namespace; main_namespace.emit(MainNamespaceEvents::MachinesEvent(event)); } + pub async fn get_machines_meta(&self) -> Vec { + self.current_machines_meta.lock().await.clone() + } + + pub async fn message_machine(&self, machine_identification_unique: MachineIdentificationUnique, message: MachineMessage) -> Result<()> { + let machines = self.api_machines.lock().await; + let sender = machines.get(&machine_identification_unique); + + if let Some(sender) = sender { + sender.send(message).await?; + return Ok(()); + } + + bail!("Unknown machine!") + } + /// Removes a machine by its unique identifier pub async fn remove_machine(&self, machine_id: &MachineIdentificationUnique) { let mut current_machines = self.current_machines_meta.lock().await; diff --git a/server/src/rest/init.rs b/server/src/rest/init.rs index ba039b4b..0359f175 100644 --- a/server/src/rest/init.rs +++ b/server/src/rest/init.rs @@ -9,6 +9,7 @@ use tracing::Level; use super::handlers::machine_mutation::post_machine_mutate; use super::handlers::write_machine_device_identification::post_write_machine_device_identification; use crate::app_state::SharedState; +use crate::rest::rest_api::rest_api_router; use crate::socketio::init::init_socketio; use crate::rest::handlers::metrics::metrics_router; @@ -29,6 +30,7 @@ async fn init_api(app_state: Arc) -> Result<()> { ) .route("/api/v1/machine/mutate", post(post_machine_mutate)) .nest("/api/v1/metrics", metrics_router()) + .nest("/api/v2", rest_api_router()) .layer(socketio_layer) .layer(cors) .layer(trace_layer) @@ -53,7 +55,7 @@ pub fn start_api_thread(app_state: Arc) -> std::thread::JoinHandle< .build() .expect("Failed to create Tokio runtime"); - if let Err(err) = rt.block_on(init_api(app_state.clone())) { + if let Err(err) = rt.block_on(init_api(app_state)) { eprintln!("API server exited with error: {err:?}"); } }) diff --git a/server/src/rest/mod.rs b/server/src/rest/mod.rs index 735c4dc3..9002a98a 100644 --- a/server/src/rest/mod.rs +++ b/server/src/rest/mod.rs @@ -1,3 +1,4 @@ pub mod handlers; pub mod init; pub mod util; +pub mod rest_api; diff --git a/server/src/rest/rest_api.rs b/server/src/rest/rest_api.rs new file mode 100644 index 00000000..d300b77a --- /dev/null +++ b/server/src/rest/rest_api.rs @@ -0,0 +1,107 @@ +use std::sync::Arc; + +use axum::{Router, debug_handler, Json}; +use axum::extract::{Path, State}; +use axum::routing::get; +use machines::MachineMessage; +use machines::machine_identification::MachineIdentificationUnique; +use machines::winder2::Winder2; +use machines::winder2::api::{LiveValuesEvent, StateEvent, Winder2Events}; +use serde::{Deserialize, Serialize}; + +use crate::app_state::SharedState; +use crate::rest::util::ResponseUtil; + +#[derive(Deserialize, Serialize, Debug, PartialEq)] +struct MachineResponce { + legacy_id: MachineIdentificationUnique, + serial: u16, + vendor: String, + slug: String, + error: Option, +} + +impl From for MachineResponce { + + fn from(machine_identification_unique: MachineIdentificationUnique) -> Self { + let vendor = machine_identification_unique.machine_identification.vendor_str(); + let slug = machine_identification_unique.machine_identification.slug(); + let serial = machine_identification_unique.serial; + + MachineResponce { + legacy_id: machine_identification_unique, + serial, + vendor, + slug, + error: None, + } + } +} + +#[derive(Deserialize, Serialize, Debug, PartialEq)] +struct GetMachinesResponce { + machines: Vec +} + +#[debug_handler] +async fn get_machines_handler( + State(shared_state): State>, +) -> Json { + + let machines = shared_state.get_machines_meta().await.into_iter().map(|m| { + let vendor = m.machine_identification_unique.machine_identification.vendor_str(); + let slug = m.machine_identification_unique.machine_identification.slug(); + let serial = m.machine_identification_unique.serial; + + MachineResponce { + legacy_id: m.machine_identification_unique, + serial, + vendor, + slug, + error: m.error, + } + }) + .collect(); + + Json(GetMachinesResponce { + machines + }) +} + +#[derive(Deserialize, Serialize, Debug, PartialEq)] +struct GetMachineResponce { + machine: MachineResponce, + state: serde_json::Value, + live_values: serde_json::Value, +} + +#[debug_handler] +async fn get_winder_v1_handler( + Path(serial): Path, + State(shared_state): State>, +) -> Json { + + let id = MachineIdentificationUnique { + machine_identification: Winder2::MACHINE_IDENTIFICATION, + serial, + }; + + let (sender, receiver) = smol::channel::unbounded(); + shared_state.message_machine(machine_identification_unique, MachineMessage::RequestValues(sender)).await?; + + if let Ok(values) = receiver.recv().await { + return Json(GetMachineResponce { + machine: MachineResponce::from(id), + state: values.state, + live_values: values.live_values, + }); + } + + bail!("Could not get values from machine") +} + +pub fn rest_api_router() -> Router> { + Router::new() + .route("/machine", get(get_machines_handler)) + .route("/machine/winder_v1/{serial}", get(get_winder_v1_handler)) +}