1.1.0 released, new user UI and modified system architecture to improve performance
@ -1,10 +1,10 @@
|
||||
[package]
|
||||
name = "Agent"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
AgentLibrary = { path = "../AgentLibrary" }
|
||||
tokio = { version = "1.37.0", features = ["rt", "rt-multi-thread", "macros"] }
|
||||
tokio = { version = "1.41.0", features = ["full", "tracing"] }
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
[package]
|
||||
name = "AgentLibrary"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
toml = "0.8.19"
|
||||
image = "0.25.2"
|
||||
image = "0.25.4"
|
||||
chrono = "0.4.38"
|
||||
sysinfo = "0.31.4"
|
||||
async-ctrlc = "1.2.0"
|
||||
lazy_static = "1.4.0"
|
||||
serde_json = "1.0.128"
|
||||
lazy_static = "1.5.0"
|
||||
serde_json = "1.0.132"
|
||||
Macro = { path = "../Macro" }
|
||||
Common = { path = "../Common" }
|
||||
uuid = { version = "1.10.0", features = ["v4"] }
|
||||
tokio = { version = "1.40.0", features = ["full"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
uuid = { version = "1.11.0", features = ["v4"] }
|
||||
tokio = { version = "1.41.0", features = ["full"] }
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use tokio::select;
|
||||
use tokio::sync::oneshot;
|
||||
use crate::utils::logging::*;
|
||||
use crate::connection::channel::control_channel_receiver::ReceiverTX;
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::packet::PacketType;
|
||||
use crate::connection::socket::socket_stream::ReadHalf;
|
||||
use crate::connection::channel::control_channel_receiver::ReceiverTX;
|
||||
use crate::utils::logging::*;
|
||||
use tokio::select;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
pub struct ReceiveThread {
|
||||
socket_rx: ReadHalf,
|
||||
@ -29,21 +29,21 @@ impl ReceiveThread {
|
||||
if let Ok(packet) = packet {
|
||||
let packet_type = PacketType::parse_packet_type(&packet.clone_id_byte());
|
||||
let result = match packet_type {
|
||||
PacketType::AgentInformationAcknowledgePacket => self.receiver_tx.agent_information_acknowledge_packet.send(packet),
|
||||
PacketType::AgentInfoAckPacket => self.receiver_tx.agent_info_ack_packet.send(packet),
|
||||
PacketType::ControlPacket => self.receiver_tx.control_packet.send(packet),
|
||||
PacketType::DataChannelPortPacket => self.receiver_tx.data_channel_port_packet.send(packet),
|
||||
PacketType::PerformanceAcknowledgePacket => self.receiver_tx.performance_acknowledge_packet.send(packet),
|
||||
PacketType::PerformanceAckPacket => self.receiver_tx.performance_ack_packet.send(packet),
|
||||
_ => {
|
||||
logging_warning!("Receive Thread", "Receive unexpected packet");
|
||||
logging_warning!(NetworkEntry::UnexpectedPacket);
|
||||
Ok(())
|
||||
},
|
||||
};
|
||||
if result.is_err() {
|
||||
logging_notice!("Receive Thread", "Channel has been closed");
|
||||
logging_information!(NetworkEntry::ChannelClosed);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
logging_notice!("Receive Thread", "Management side disconnected");
|
||||
logging_information!(NetworkEntry::ManagementDisconnect);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,37 +1,35 @@
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
use crate::utils::logging::*;
|
||||
use crate::connection::channel::control_channel_receive_thread::ReceiveThread;
|
||||
use crate::connection::packet::base_packet::BasePacket;
|
||||
use crate::connection::socket::socket_stream::ReadHalf;
|
||||
use crate::connection::channel::control_channel_receive_thread::ReceiveThread;
|
||||
use crate::utils::create_unbounded_channels;
|
||||
use crate::utils::logging::*;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
pub struct ReceiverTX {
|
||||
pub agent_information_acknowledge_packet: mpsc::UnboundedSender<BasePacket>,
|
||||
pub agent_info_ack_packet: mpsc::UnboundedSender<BasePacket>,
|
||||
pub control_packet: mpsc::UnboundedSender<BasePacket>,
|
||||
pub data_channel_port_packet: mpsc::UnboundedSender<BasePacket>,
|
||||
pub performance_acknowledge_packet: mpsc::UnboundedSender<BasePacket>,
|
||||
pub performance_ack_packet: mpsc::UnboundedSender<BasePacket>,
|
||||
}
|
||||
|
||||
pub struct ControlChannelReceiver {
|
||||
stop_signal_tx: Option<oneshot::Sender<()>>,
|
||||
pub agent_information_acknowledge_packet: mpsc::UnboundedReceiver<BasePacket>,
|
||||
pub agent_info_ack_packet: mpsc::UnboundedReceiver<BasePacket>,
|
||||
pub control_packet: mpsc::UnboundedReceiver<BasePacket>,
|
||||
pub data_channel_port_packet: mpsc::UnboundedReceiver<BasePacket>,
|
||||
pub performance_acknowledge_packet: mpsc::UnboundedReceiver<BasePacket>,
|
||||
pub performance_ack_packet: mpsc::UnboundedReceiver<BasePacket>,
|
||||
}
|
||||
|
||||
impl ControlChannelReceiver {
|
||||
pub fn new(socket_rx: ReadHalf) -> Self {
|
||||
create_unbounded_channels!(4);
|
||||
let (stop_signal_tx, stop_signal_rx) = oneshot::channel();
|
||||
let (agent_information_acknowledge_packet_tx, agent_information_acknowledge_packet_rx) = mpsc::unbounded_channel();
|
||||
let (control_packet_tx, control_packet_rx) = mpsc::unbounded_channel();
|
||||
let (data_channel_port_packet_tx, data_channel_port_packet_rx) = mpsc::unbounded_channel();
|
||||
let (performance_acknowledge_packet_tx, performance_acknowledge_packet_rx) = mpsc::unbounded_channel();
|
||||
let receiver_tx = ReceiverTX {
|
||||
agent_information_acknowledge_packet: agent_information_acknowledge_packet_tx,
|
||||
control_packet: control_packet_tx,
|
||||
data_channel_port_packet: data_channel_port_packet_tx,
|
||||
performance_acknowledge_packet: performance_acknowledge_packet_tx,
|
||||
agent_info_ack_packet: channel_0_tx,
|
||||
control_packet: channel_1_tx,
|
||||
data_channel_port_packet: channel_2_tx,
|
||||
performance_ack_packet: channel_3_tx,
|
||||
};
|
||||
let mut receive_thread = ReceiveThread::new(socket_rx, receiver_tx, stop_signal_rx);
|
||||
tokio::spawn(async move {
|
||||
@ -39,27 +37,25 @@ impl ControlChannelReceiver {
|
||||
});
|
||||
Self {
|
||||
stop_signal_tx: Some(stop_signal_tx),
|
||||
agent_information_acknowledge_packet: agent_information_acknowledge_packet_rx,
|
||||
control_packet: control_packet_rx,
|
||||
data_channel_port_packet: data_channel_port_packet_rx,
|
||||
performance_acknowledge_packet: performance_acknowledge_packet_rx,
|
||||
agent_info_ack_packet: channel_0_rx,
|
||||
control_packet: channel_1_rx,
|
||||
data_channel_port_packet: channel_2_rx,
|
||||
performance_ack_packet: channel_3_rx,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn disconnect(&mut self) {
|
||||
self.agent_information_acknowledge_packet.close();
|
||||
self.agent_info_ack_packet.close();
|
||||
self.control_packet.close();
|
||||
self.data_channel_port_packet.close();
|
||||
self.performance_acknowledge_packet.close();
|
||||
self.performance_ack_packet.close();
|
||||
match self.stop_signal_tx.take() {
|
||||
Some(stop_signal) => {
|
||||
if stop_signal.send(()).is_ok() {
|
||||
logging_information!("Control Channel", "Successfully destroyed Receiver");
|
||||
} else {
|
||||
logging_error!("Control Channel", "Failed to destroy Receiver");
|
||||
if stop_signal.send(()).is_err() {
|
||||
logging_error!(NetworkEntry::DestroyInstanceError);
|
||||
}
|
||||
},
|
||||
None => logging_error!("Control Channel", "Failed to destroy Receiver"),
|
||||
}
|
||||
None => logging_error!(NetworkEntry::DestroyInstanceError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use crate::utils::logging::*;
|
||||
use crate::connection::channel::send_thread::SendThread;
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::socket::socket_stream::WriteHalf;
|
||||
use crate::connection::channel::send_thread::SendThread;
|
||||
use crate::utils::logging::*;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
pub type SenderTX = mpsc::UnboundedSender<Box<dyn Packet+Send>>;
|
||||
pub type SenderRX = mpsc::UnboundedReceiver<Box<dyn Packet+Send>>;
|
||||
pub type SenderTX = mpsc::UnboundedSender<Box<dyn Packet + Send>>;
|
||||
pub type SenderRX = mpsc::UnboundedReceiver<Box<dyn Packet + Send>>;
|
||||
|
||||
pub struct ControlChannelSender {
|
||||
sender_tx: SenderTX,
|
||||
@ -29,20 +29,18 @@ impl ControlChannelSender {
|
||||
pub async fn disconnect(&mut self) {
|
||||
match self.stop_signal_tx.take() {
|
||||
Some(stop_signal) => {
|
||||
if stop_signal.send(()).is_ok() {
|
||||
logging_information!("Control Channel", "Successfully destroyed Sender");
|
||||
} else {
|
||||
logging_error!("Control Channel", "Failed to destroy Sender");
|
||||
if stop_signal.send(()).is_err() {
|
||||
logging_error!(NetworkEntry::DestroyInstanceError);
|
||||
}
|
||||
},
|
||||
None => logging_error!("Control Channel", "Failed to destroy Sender"),
|
||||
}
|
||||
None => logging_error!(NetworkEntry::DestroyInstanceError),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send<T: Packet + Send + 'static>(&mut self, packet: T) {
|
||||
let packet: Box<dyn Packet + Send + 'static> = Box::new(packet);
|
||||
if self.sender_tx.send(packet).is_err() {
|
||||
logging_notice!("Control Channel", "Channel has been closed");
|
||||
logging_information!(NetworkEntry::ChannelClosed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use tokio::select;
|
||||
use tokio::sync::oneshot;
|
||||
use crate::utils::logging::*;
|
||||
use crate::connection::channel::data_channel_receiver::ReceiverTX;
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::packet::PacketType;
|
||||
use crate::connection::socket::socket_stream::ReadHalf;
|
||||
use crate::connection::channel::data_channel_receiver::ReceiverTX;
|
||||
use crate::utils::logging::*;
|
||||
use tokio::select;
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
pub struct ReceiveThread {
|
||||
socket_rx: ReadHalf,
|
||||
@ -31,22 +31,24 @@ impl ReceiveThread {
|
||||
let result = match packet_type {
|
||||
PacketType::AlivePacket => self.receiver_tx.alive_packet.send(packet),
|
||||
PacketType::FileBodyPacket => self.receiver_tx.file_body_packet.send(packet),
|
||||
PacketType::FileHeaderAckPacket => self.receiver_tx.file_header_ack_packet.send(packet),
|
||||
PacketType::FileHeaderPacket => self.receiver_tx.file_header_packet.send(packet),
|
||||
PacketType::FileTransferEndPacket => self.receiver_tx.file_transfer_end_packet.send(packet),
|
||||
PacketType::ResultAcknowledgePacket => self.receiver_tx.result_acknowledge_packet.send(packet),
|
||||
PacketType::FileTransferResultPacket => self.receiver_tx.file_transfer_result_packet.send(packet),
|
||||
PacketType::StillProcessPacket => self.receiver_tx.still_process_packet.send(packet),
|
||||
PacketType::TaskInfoPacket => self.receiver_tx.task_info_packet.send(packet),
|
||||
PacketType::TaskResultAckPacket => self.receiver_tx.task_result_ack_packet.send(packet),
|
||||
_ => {
|
||||
logging_warning!("Receive Thread", "Receive unexpected packet");
|
||||
logging_warning!(NetworkEntry::UnexpectedPacket);
|
||||
Ok(())
|
||||
},
|
||||
};
|
||||
if result.is_err() {
|
||||
logging_notice!("Receive Thread", "Channel has been closed");
|
||||
logging_information!(NetworkEntry::ChannelClosed);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
logging_notice!("Receive Thread", "Management side disconnected");
|
||||
logging_information!(NetworkEntry::ManagementDisconnect);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,49 +1,50 @@
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
use crate::utils::logging::*;
|
||||
use crate::connection::channel::data_channel_receive_thread::ReceiveThread;
|
||||
use crate::connection::packet::base_packet::BasePacket;
|
||||
use crate::connection::socket::socket_stream::ReadHalf;
|
||||
use crate::connection::channel::data_channel_receive_thread::ReceiveThread;
|
||||
use crate::utils::create_unbounded_channels;
|
||||
use crate::utils::logging::*;
|
||||
use tokio::sync::mpsc::{self, UnboundedReceiver, UnboundedSender};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
pub struct ReceiverTX {
|
||||
pub alive_packet: UnboundedSender<BasePacket>,
|
||||
pub file_body_packet: UnboundedSender<BasePacket>,
|
||||
pub file_header_ack_packet: UnboundedSender<BasePacket>,
|
||||
pub file_header_packet: UnboundedSender<BasePacket>,
|
||||
pub file_transfer_end_packet: UnboundedSender<BasePacket>,
|
||||
pub result_acknowledge_packet: UnboundedSender<BasePacket>,
|
||||
pub file_transfer_result_packet: UnboundedSender<BasePacket>,
|
||||
pub still_process_packet: UnboundedSender<BasePacket>,
|
||||
pub task_info_packet: UnboundedSender<BasePacket>,
|
||||
pub task_result_ack_packet: UnboundedSender<BasePacket>,
|
||||
}
|
||||
|
||||
pub struct DataChannelReceiver {
|
||||
stop_signal_tx: Option<oneshot::Sender<()>>,
|
||||
pub alive_packet: UnboundedReceiver<BasePacket>,
|
||||
pub file_body_packet: UnboundedReceiver<BasePacket>,
|
||||
pub file_header_ack_packet: UnboundedReceiver<BasePacket>,
|
||||
pub file_header_packet: UnboundedReceiver<BasePacket>,
|
||||
pub file_transfer_end_packet: UnboundedReceiver<BasePacket>,
|
||||
pub result_acknowledge_packet: UnboundedReceiver<BasePacket>,
|
||||
pub file_transfer_result_packet: UnboundedReceiver<BasePacket>,
|
||||
pub still_process_packet: UnboundedReceiver<BasePacket>,
|
||||
pub task_info_packet: UnboundedReceiver<BasePacket>,
|
||||
pub task_result_ack_packet: UnboundedReceiver<BasePacket>,
|
||||
}
|
||||
|
||||
impl DataChannelReceiver {
|
||||
pub fn new(socket_rx: ReadHalf) -> Self {
|
||||
create_unbounded_channels!(9);
|
||||
let (stop_signal_tx, stop_signal_rx) = oneshot::channel();
|
||||
let (alive_packet_tx, alive_packet_rx) = mpsc::unbounded_channel();
|
||||
let (file_body_packet_tx, file_body_packet_rx) = mpsc::unbounded_channel();
|
||||
let (file_header_packet_tx, file_header_packet_rx) = mpsc::unbounded_channel();
|
||||
let (file_transfer_end_packet_tx, file_transfer_end_packet_rx) = mpsc::unbounded_channel();
|
||||
let (result_acknowledge_packet_tx, result_acknowledge_packet_rx) = mpsc::unbounded_channel();
|
||||
let (still_process_packet_tx, still_process_packet_rx) = mpsc::unbounded_channel();
|
||||
let (task_info_packet_tx, task_info_packet_rx) = mpsc::unbounded_channel();
|
||||
let receiver_tx = ReceiverTX {
|
||||
alive_packet: alive_packet_tx,
|
||||
file_body_packet: file_body_packet_tx,
|
||||
file_header_packet: file_header_packet_tx,
|
||||
file_transfer_end_packet: file_transfer_end_packet_tx,
|
||||
result_acknowledge_packet: result_acknowledge_packet_tx,
|
||||
still_process_packet: still_process_packet_tx,
|
||||
task_info_packet: task_info_packet_tx,
|
||||
alive_packet: channel_0_tx,
|
||||
file_body_packet: channel_1_tx,
|
||||
file_header_ack_packet: channel_2_tx,
|
||||
file_header_packet: channel_3_tx,
|
||||
file_transfer_end_packet: channel_4_tx,
|
||||
file_transfer_result_packet: channel_5_tx,
|
||||
still_process_packet: channel_6_tx,
|
||||
task_info_packet: channel_7_tx,
|
||||
task_result_ack_packet: channel_8_tx,
|
||||
};
|
||||
let mut receive_thread = ReceiveThread::new(socket_rx, receiver_tx, stop_signal_rx);
|
||||
tokio::spawn(async move {
|
||||
@ -51,32 +52,35 @@ impl DataChannelReceiver {
|
||||
});
|
||||
Self {
|
||||
stop_signal_tx: Some(stop_signal_tx),
|
||||
alive_packet: alive_packet_rx,
|
||||
file_body_packet: file_body_packet_rx,
|
||||
file_header_packet: file_header_packet_rx,
|
||||
file_transfer_end_packet: file_transfer_end_packet_rx,
|
||||
result_acknowledge_packet: result_acknowledge_packet_rx,
|
||||
still_process_packet: still_process_packet_rx,
|
||||
task_info_packet: task_info_packet_rx,
|
||||
alive_packet: channel_0_rx,
|
||||
file_body_packet: channel_1_rx,
|
||||
file_header_ack_packet: channel_2_rx,
|
||||
file_header_packet: channel_3_rx,
|
||||
file_transfer_end_packet: channel_4_rx,
|
||||
file_transfer_result_packet: channel_5_rx,
|
||||
still_process_packet: channel_6_rx,
|
||||
task_info_packet: channel_7_rx,
|
||||
task_result_ack_packet: channel_8_rx,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn disconnect(&mut self) {
|
||||
self.alive_packet.close();
|
||||
self.file_body_packet.close();
|
||||
self.file_header_ack_packet.close();
|
||||
self.file_header_packet.close();
|
||||
self.file_transfer_end_packet.close();
|
||||
self.file_transfer_result_packet.close();
|
||||
self.still_process_packet.close();
|
||||
self.task_info_packet.close();
|
||||
self.task_result_ack_packet.close();
|
||||
match self.stop_signal_tx.take() {
|
||||
Some(stop_signal) => {
|
||||
if stop_signal.send(()).is_ok() {
|
||||
logging_information!("Data Channel", "Successfully destroyed the Receiver");
|
||||
} else {
|
||||
logging_error!("Data Channel", "Failed to destroy Receiver");
|
||||
if stop_signal.send(()).is_err() {
|
||||
logging_error!(NetworkEntry::DestroyInstanceError);
|
||||
}
|
||||
},
|
||||
None => logging_error!("Data Channel", "Failed to destroy Receiver"),
|
||||
}
|
||||
None => logging_error!(NetworkEntry::DestroyInstanceError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
use crate::utils::logging::*;
|
||||
use crate::connection::channel::send_thread::SendThread;
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::socket::socket_stream::WriteHalf;
|
||||
use crate::connection::channel::send_thread::SendThread;
|
||||
use crate::utils::logging::*;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
|
||||
pub type SenderTX = mpsc::UnboundedSender<Box<dyn Packet+Send>>;
|
||||
pub type SenderRX = mpsc::UnboundedReceiver<Box<dyn Packet+Send>>;
|
||||
pub type SenderTX = mpsc::UnboundedSender<Box<dyn Packet + Send>>;
|
||||
pub type SenderRX = mpsc::UnboundedReceiver<Box<dyn Packet + Send>>;
|
||||
|
||||
pub struct DataChannelSender {
|
||||
sender_tx: SenderTX,
|
||||
@ -29,20 +29,18 @@ impl DataChannelSender {
|
||||
pub async fn disconnect(&mut self) {
|
||||
match self.stop_signal_tx.take() {
|
||||
Some(stop_signal) => {
|
||||
if stop_signal.send(()).is_ok() {
|
||||
logging_information!("Data Channel", "Successfully destroyed the Sender")
|
||||
} else {
|
||||
logging_error!("Data Channel", "Failed to destroy Sender")
|
||||
if stop_signal.send(()).is_err() {
|
||||
logging_error!(NetworkEntry::DestroyInstanceError);
|
||||
}
|
||||
},
|
||||
None => logging_error!("Data Channel", "Failed to destroy Sender"),
|
||||
}
|
||||
None => logging_error!(NetworkEntry::DestroyInstanceError),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn send<T: Packet + Send + 'static>(&mut self, packet: T) {
|
||||
let packet: Box<dyn Packet + Send + 'static> = Box::new(packet);
|
||||
if self.sender_tx.send(packet).is_err() {
|
||||
logging_notice!("Data Channel", "Channel has been closed");
|
||||
logging_information!(NetworkEntry::ChannelClosed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,11 +6,11 @@ pub mod data_channel_receiver;
|
||||
pub mod data_channel_sender;
|
||||
pub mod send_thread;
|
||||
|
||||
use crate::connection::socket::socket_stream::SocketStream;
|
||||
use crate::connection::channel::data_channel_sender::DataChannelSender;
|
||||
use crate::connection::channel::data_channel_receiver::DataChannelReceiver;
|
||||
use crate::connection::channel::control_channel_sender::ControlChannelSender;
|
||||
use crate::connection::channel::control_channel_receiver::ControlChannelReceiver;
|
||||
use crate::connection::channel::control_channel_sender::ControlChannelSender;
|
||||
use crate::connection::channel::data_channel_receiver::DataChannelReceiver;
|
||||
use crate::connection::channel::data_channel_sender::DataChannelSender;
|
||||
use crate::connection::socket::socket_stream::SocketStream;
|
||||
|
||||
pub struct ControlChannel;
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::socket::socket_stream::WriteHalf;
|
||||
use crate::utils::logging::*;
|
||||
use tokio::select;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::sync::oneshot;
|
||||
use crate::utils::logging::*;
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::socket::socket_stream::WriteHalf;
|
||||
|
||||
type SenderRX = mpsc::UnboundedReceiver<Box<dyn Packet + Send>>;
|
||||
|
||||
@ -30,12 +30,12 @@ impl SendThread {
|
||||
match packet {
|
||||
Some(packet) => {
|
||||
if self.socket_tx.send_packet(packet).await.is_err() {
|
||||
logging_notice!("Send Thread", "Management side disconnected");
|
||||
logging_information!(NetworkEntry::ManagementDisconnect);
|
||||
break;
|
||||
}
|
||||
},
|
||||
None => {
|
||||
logging_notice!("Send Thread", "Channel has been closed");
|
||||
logging_information!(NetworkEntry::ChannelClosed);
|
||||
break;
|
||||
},
|
||||
}
|
||||
|
||||
10
AgentLibrary/src/connection/packet/agent_info_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithData;
|
||||
|
||||
#[derive(DefinePacketWithData)]
|
||||
pub struct AgentInfoPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct AgentInformationPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl AgentInformationPacket {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16 + data.len()),
|
||||
id: PacketType::AgentInformationPacket.as_byte(),
|
||||
data,
|
||||
packet_type: PacketType::AgentInformationPacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for AgentInformationPacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
10
AgentLibrary/src/connection/packet/alive_ack_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithoutData;
|
||||
|
||||
#[derive(DefinePacketWithoutData)]
|
||||
pub struct AliveAckPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct AliveAcknowledgePacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl AliveAcknowledgePacket {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16),
|
||||
id: PacketType::AliveAcknowledgePacket.as_byte(),
|
||||
data: Vec::new(),
|
||||
packet_type: PacketType::AliveAcknowledgePacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for AliveAcknowledgePacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
10
AgentLibrary/src/connection/packet/control_ack_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithoutData;
|
||||
|
||||
#[derive(DefinePacketWithoutData)]
|
||||
pub struct ControlAckPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct ControlAcknowledgePacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl ControlAcknowledgePacket {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16),
|
||||
id: PacketType::ControlAcknowledgePacket.as_byte(),
|
||||
data: Vec::new(),
|
||||
packet_type: PacketType::ControlAcknowledgePacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for ControlAcknowledgePacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct FileHeaderAcknowledgePacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl FileHeaderAcknowledgePacket {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16),
|
||||
id: PacketType::FileHeaderAcknowledgePacket.as_byte(),
|
||||
data: Vec::new(),
|
||||
packet_type: PacketType::FileHeaderAcknowledgePacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for FileHeaderAcknowledgePacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct FileTransferResultPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl FileTransferResultPacket {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16 + data.len()),
|
||||
id: PacketType::FileTransferResultPacket.as_byte(),
|
||||
data,
|
||||
packet_type: PacketType::FileTransferResultPacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for FileTransferResultPacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,9 @@
|
||||
pub mod agent_information_packet;
|
||||
pub mod alive_acknowledge_packet;
|
||||
pub mod control_acknowledge_packet;
|
||||
pub mod file_header_acknowledge_packet;
|
||||
pub mod file_transfer_result_packet;
|
||||
pub mod agent_info_packet;
|
||||
pub mod alive_ack_packet;
|
||||
pub mod control_ack_packet;
|
||||
pub mod performance_packet;
|
||||
pub mod result_packet;
|
||||
pub mod still_process_acknowledge_packet;
|
||||
pub mod task_info_acknowledge_packet;
|
||||
pub mod still_process_ack_packet;
|
||||
pub mod task_info_ack_packet;
|
||||
pub mod task_result_packet;
|
||||
|
||||
pub use Common::connection::packet::*;
|
||||
|
||||
@ -1,57 +1,10 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithData;
|
||||
|
||||
#[derive(DefinePacketWithData)]
|
||||
pub struct PerformancePacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl PerformancePacket {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16 + data.len()),
|
||||
id: PacketType::PerformancePacket.as_byte(),
|
||||
data,
|
||||
packet_type: PacketType::PerformancePacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for PerformancePacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct ResultPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl ResultPacket {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16 + data.len()),
|
||||
id: PacketType::ResultPacket.as_byte(),
|
||||
data,
|
||||
packet_type: PacketType::ResultPacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for ResultPacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithoutData;
|
||||
|
||||
#[derive(DefinePacketWithoutData)]
|
||||
pub struct StillProcessAckPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct StillProcessAcknowledgePacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl StillProcessAcknowledgePacket {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16),
|
||||
id: PacketType::StillProcessAcknowledgePacket.as_byte(),
|
||||
data: Vec::new(),
|
||||
packet_type: PacketType::StillProcessAcknowledgePacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for StillProcessAcknowledgePacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
10
AgentLibrary/src/connection/packet/task_info_ack_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithoutData;
|
||||
|
||||
#[derive(DefinePacketWithoutData)]
|
||||
pub struct TaskInfoAckPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
use crate::connection::packet::{Packet, PacketType, length_to_byte};
|
||||
|
||||
pub struct TaskInfoAcknowledgePacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
|
||||
impl TaskInfoAcknowledgePacket {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
length: length_to_byte(16),
|
||||
id: PacketType::TaskInfoAcknowledgePacket.as_byte(),
|
||||
data: Vec::new(),
|
||||
packet_type: PacketType::TaskInfoAcknowledgePacket
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Packet for TaskInfoAcknowledgePacket {
|
||||
fn as_length_byte(&self) -> &[u8] {
|
||||
&self.length
|
||||
}
|
||||
|
||||
fn as_id_byte(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn as_data_byte(&self) -> &[u8] {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn clone_length_byte(&self) -> Vec<u8> {
|
||||
self.length.clone()
|
||||
}
|
||||
|
||||
fn clone_id_byte(&self) -> Vec<u8> {
|
||||
self.id.clone()
|
||||
}
|
||||
|
||||
fn clone_data_byte(&self) -> Vec<u8> {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
fn data_to_string(&self) -> String {
|
||||
String::from_utf8_lossy(&*self.data.clone()).to_string()
|
||||
}
|
||||
|
||||
fn packet_type(&self) -> PacketType {
|
||||
self.packet_type
|
||||
}
|
||||
|
||||
fn equal(&self, packet_type: PacketType) -> bool {
|
||||
self.packet_type.eq(&packet_type)
|
||||
}
|
||||
}
|
||||
10
AgentLibrary/src/connection/packet/task_result_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithData;
|
||||
|
||||
#[derive(DefinePacketWithData)]
|
||||
pub struct TaskResultPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
use crate::connection::socket::socket_stream::SocketStream;
|
||||
use crate::utils::config::Config;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::TcpStream;
|
||||
use crate::utils::config::Config;
|
||||
use crate::connection::socket::socket_stream::SocketStream;
|
||||
|
||||
pub struct ManagementSocket;
|
||||
|
||||
|
||||
@ -1,42 +1,46 @@
|
||||
use std::mem;
|
||||
use uuid::Uuid;
|
||||
use tokio::select;
|
||||
use std::sync::Arc;
|
||||
use tokio::fs::File;
|
||||
use tokio::sync::RwLock;
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use tokio::time::{Instant, sleep};
|
||||
use crate::utils::logging::*;
|
||||
use crate::utils::config::Config;
|
||||
use crate::connection::channel::control_channel_receiver::ControlChannelReceiver;
|
||||
use crate::connection::channel::control_channel_sender::ControlChannelSender;
|
||||
use crate::connection::channel::data_channel_receiver::DataChannelReceiver;
|
||||
use crate::connection::channel::data_channel_sender::DataChannelSender;
|
||||
use crate::connection::channel::{ControlChannel, DataChannel};
|
||||
use crate::connection::packet::agent_info_packet::AgentInfoPacket;
|
||||
use crate::connection::packet::alive_ack_packet::AliveAckPacket;
|
||||
use crate::connection::packet::control_ack_packet::ControlAckPacket;
|
||||
use crate::connection::packet::file_body_packet::FileBodyPacket;
|
||||
use crate::connection::packet::file_header_ack_packet::FileHeaderAckPacket;
|
||||
use crate::connection::packet::file_header_packet::FileHeaderPacket;
|
||||
use crate::connection::packet::file_transfer_end_packet::FileTransferEndPacket;
|
||||
use crate::connection::packet::file_transfer_result_packet::FileTransferResultPacket;
|
||||
use crate::connection::packet::performance_packet::PerformancePacket;
|
||||
use crate::connection::packet::task_result_packet::TaskResultPacket;
|
||||
use crate::connection::packet::task_info_ack_packet::TaskInfoAckPacket;
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::socket::socket_stream::SocketStream;
|
||||
use crate::management::inference_manager::InferenceManager;
|
||||
use crate::management::monitor::Monitor;
|
||||
use crate::utils::clear_unbounded_channel;
|
||||
use crate::management::utils::task_info::TaskInfo;
|
||||
use crate::management::utils::model_type::ModelType;
|
||||
use crate::management::utils::agent_state::AgentState;
|
||||
use crate::management::utils::file_header::FileHeader;
|
||||
use crate::management::utils::task_result::TaskResult;
|
||||
use crate::management::utils::bounding_box::BoundingBox;
|
||||
use crate::connection::packet::result_packet::ResultPacket;
|
||||
use crate::management::calculate_manager::CalculateManager;
|
||||
use crate::connection::socket::socket_stream::SocketStream;
|
||||
use crate::connection::channel::{ControlChannel, DataChannel};
|
||||
use crate::connection::packet::performance_packet::PerformancePacket;
|
||||
use crate::management::utils::file_transfer_result::FileTransferResult;
|
||||
use crate::connection::channel::data_channel_sender::DataChannelSender;
|
||||
use crate::connection::channel::data_channel_receiver::DataChannelReceiver;
|
||||
use crate::connection::channel::control_channel_sender::ControlChannelSender;
|
||||
use crate::connection::packet::agent_information_packet::AgentInformationPacket;
|
||||
use crate::connection::packet::alive_acknowledge_packet::AliveAcknowledgePacket;
|
||||
use crate::connection::channel::control_channel_receiver::ControlChannelReceiver;
|
||||
use crate::connection::packet::control_acknowledge_packet::ControlAcknowledgePacket;
|
||||
use crate::connection::packet::file_transfer_result_packet::FileTransferResultPacket;
|
||||
use crate::connection::packet::task_info_acknowledge_packet::TaskInfoAcknowledgePacket;
|
||||
use crate::connection::packet::file_header_acknowledge_packet::FileHeaderAcknowledgePacket;
|
||||
use crate::management::utils::inference_argument::ModelType;
|
||||
use crate::management::utils::task_result::TaskResult;
|
||||
use crate::management::utils::task_info::TaskInfo;
|
||||
use crate::utils::clear_unbounded_channel;
|
||||
use crate::utils::config::Config;
|
||||
use crate::utils::logging::*;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::OsStr;
|
||||
use std::mem;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::select;
|
||||
use tokio::sync::RwLock;
|
||||
use tokio::time::{sleep, Instant};
|
||||
use uuid::Uuid;
|
||||
use crate::connection::packet::still_process_ack_packet::StillProcessAckPacket;
|
||||
|
||||
pub struct Agent {
|
||||
pub state: AgentState,
|
||||
@ -50,10 +54,10 @@ pub struct Agent {
|
||||
impl Agent {
|
||||
pub async fn new(socket_stream: SocketStream) -> Result<Self, LogEntry> {
|
||||
let config = Config::now().await;
|
||||
let (mut control_channel_sender, mut control_channel_receiver) = ControlChannel::new(socket_stream);
|
||||
let mut information_confirm = false;
|
||||
let information = serde_json::to_vec(&Monitor::get_system_info().await)
|
||||
.map_err(|err| error_entry!("Agent", "Unable to serialize data", format!("Err: {err}")))?;
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeSerializeError(err)))?;
|
||||
let (mut control_channel_sender, mut control_channel_receiver) = ControlChannel::new(socket_stream);
|
||||
let timer = Instant::now();
|
||||
let mut polling_times = 0_u32;
|
||||
let polling_interval = Duration::from_millis(config.polling_interval);
|
||||
@ -61,27 +65,25 @@ impl Agent {
|
||||
while timer.elapsed() <= timeout_duration {
|
||||
if timer.elapsed() > polling_times * polling_interval {
|
||||
if !information_confirm {
|
||||
control_channel_sender.send(AgentInformationPacket::new(information.clone())).await;
|
||||
control_channel_sender.send(AgentInfoPacket::new(information.clone())).await;
|
||||
} else {
|
||||
let performance = Monitor::get_performance().await;
|
||||
let performance_data = serde_json::to_vec(&performance)
|
||||
.map_err(|err| error_entry!("Agent", "Unable to serialize data", format!("Err: {err}")))?;
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeSerializeError(err)))?;
|
||||
control_channel_sender.send(PerformancePacket::new(performance_data)).await;
|
||||
}
|
||||
polling_times += 1;
|
||||
}
|
||||
select! {
|
||||
biased;
|
||||
packet = control_channel_receiver.agent_information_acknowledge_packet.recv() => {
|
||||
let _ = packet
|
||||
.ok_or(notice_entry!("Agent", "Channel has been closed"))?;
|
||||
packet = control_channel_receiver.agent_info_ack_packet.recv() => {
|
||||
let _ = packet.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
information_confirm = true;
|
||||
},
|
||||
packet = control_channel_receiver.performance_acknowledge_packet.recv() => {
|
||||
let _ = packet
|
||||
.ok_or(notice_entry!("Agent", "Channel has been closed"))?;
|
||||
}
|
||||
packet = control_channel_receiver.performance_ack_packet.recv() => {
|
||||
let _ = packet.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
if !information_confirm {
|
||||
Err(error_entry!("Agent", "Wrong packet delivery order"))?;
|
||||
Err(error_entry!(MiscEntry::WrongDeliverOrder))?;
|
||||
}
|
||||
let agent = Self {
|
||||
state: AgentState::None,
|
||||
@ -92,11 +94,11 @@ impl Agent {
|
||||
data_channel_receiver: None,
|
||||
};
|
||||
return Ok(agent);
|
||||
},
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
}
|
||||
Err(notice_entry!("Agent", "Control Channel timeout"))
|
||||
Err(information_entry!(NetworkEntry::ControlChannelTimeout))
|
||||
}
|
||||
|
||||
pub async fn run(agent: Arc<RwLock<Agent>>) {
|
||||
@ -110,6 +112,20 @@ impl Agent {
|
||||
});
|
||||
}
|
||||
|
||||
pub async fn terminate(agent: &Arc<RwLock<Agent>>) {
|
||||
logging_information!(SystemEntry::Terminating);
|
||||
let mut agent = agent.write().await;
|
||||
agent.control_channel_sender.disconnect().await;
|
||||
agent.control_channel_receiver.disconnect().await;
|
||||
if let Some(data_channel_sender) = &mut agent.data_channel_sender {
|
||||
data_channel_sender.disconnect().await;
|
||||
}
|
||||
if let Some(data_channel_receiver) = &mut agent.data_channel_receiver {
|
||||
data_channel_receiver.disconnect().await;
|
||||
}
|
||||
logging_information!(SystemEntry::TerminateComplete);
|
||||
}
|
||||
|
||||
async fn performance(agent: Arc<RwLock<Agent>>) {
|
||||
let config = Config::now().await;
|
||||
let mut polling_times = 0_u32;
|
||||
@ -122,31 +138,32 @@ impl Agent {
|
||||
return;
|
||||
}
|
||||
if timeout_timer.elapsed() > timeout_duration {
|
||||
logging_notice!("Agent", "Control Channel timeout");
|
||||
logging_information!(NetworkEntry::ControlChannelTimeout);
|
||||
break;
|
||||
}
|
||||
if polling_timer.elapsed() > polling_times * polling_interval {
|
||||
let performance = Monitor::get_performance().await;
|
||||
match serde_json::to_vec(&performance) {
|
||||
Ok(performance_data) => {
|
||||
agent.write().await.control_channel_sender.send(PerformancePacket::new(performance_data)).await;
|
||||
},
|
||||
Err(err) => logging_error!("Agent", "Unable to serialize data", format!("Err: {err}")),
|
||||
agent.write().await.control_channel_sender
|
||||
.send(PerformancePacket::new(performance_data)).await;
|
||||
}
|
||||
Err(err) => logging_error!(IOEntry::SerdeSerializeError(err))
|
||||
}
|
||||
polling_times += 1;
|
||||
}
|
||||
let mut agent = agent.write().await;
|
||||
select! {
|
||||
biased;
|
||||
reply = agent.control_channel_receiver.performance_acknowledge_packet.recv() => {
|
||||
reply = agent.control_channel_receiver.performance_ack_packet.recv() => {
|
||||
if reply.is_some() {
|
||||
clear_unbounded_channel(&mut agent.control_channel_receiver.performance_acknowledge_packet).await;
|
||||
clear_unbounded_channel(&mut agent.control_channel_receiver.performance_ack_packet).await;
|
||||
timeout_timer = Instant::now();
|
||||
} else {
|
||||
logging_notice!("Agent", "Channel has been closed");
|
||||
logging_information!(NetworkEntry::ChannelClosed);
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
}
|
||||
@ -155,22 +172,29 @@ impl Agent {
|
||||
|
||||
async fn management(agent: Arc<RwLock<Agent>>) {
|
||||
loop {
|
||||
Self::refresh_state(agent.clone()).await;
|
||||
Self::refresh_state(&agent).await;
|
||||
let state = agent.read().await.state;
|
||||
match state {
|
||||
AgentState::ProcessTask => Self::process_task(agent.clone()).await,
|
||||
AgentState::Idle(idle_time) => Self::idle(agent.clone(), Duration::from_secs(idle_time)).await,
|
||||
AgentState::CreateDataChannel => Self::create_data_channel(agent.clone()).await,
|
||||
let result = match state {
|
||||
AgentState::ProcessTask => Self::process_task(&agent).await,
|
||||
AgentState::Idle(idle_time) => Self::idle(&agent, Duration::from_secs(idle_time)).await,
|
||||
AgentState::CreateDataChannel => Self::create_data_channel(&agent).await,
|
||||
AgentState::Terminate => {
|
||||
Self::terminate(agent.clone()).await;
|
||||
Self::terminate(&agent).await;
|
||||
return;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => Ok(())
|
||||
};
|
||||
if let Err(entry) = result {
|
||||
logging_entry!(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn refresh_state(agent: Arc<RwLock<Agent>>) {
|
||||
async fn refresh_state(agent: &Arc<RwLock<Agent>>) {
|
||||
{
|
||||
let mut agent = agent.write().await;
|
||||
clear_unbounded_channel(&mut agent.control_channel_receiver.control_packet).await;
|
||||
}
|
||||
let config = Config::now().await;
|
||||
let timer = Instant::now();
|
||||
let timeout_duration = Duration::from_secs(config.control_channel_timeout);
|
||||
@ -184,51 +208,40 @@ impl Agent {
|
||||
packet = agent.control_channel_receiver.control_packet.recv() => {
|
||||
match packet {
|
||||
Some(packet) => {
|
||||
clear_unbounded_channel(&mut agent.control_channel_receiver.control_packet).await;
|
||||
match serde_json::from_slice::<AgentState>(packet.as_data_byte()) {
|
||||
Ok(state) => agent.state = state,
|
||||
Err(err) => {
|
||||
logging_error!("Agent", "Unable to parse packet data", format!("Err: {err}"));
|
||||
logging_error!(IOEntry::SerdeDeserializeError(err));
|
||||
continue;
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
logging_notice!("Agent", "Channel has been closed");
|
||||
logging_information!(NetworkEntry::ChannelClosed);
|
||||
agent.state = AgentState::Terminate;
|
||||
},
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue
|
||||
}
|
||||
agent.control_channel_sender.send(ControlAcknowledgePacket::new()).await;
|
||||
agent.control_channel_sender.send(ControlAckPacket::new()).await;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_task(agent: Arc<RwLock<Agent>>) {
|
||||
let result = match Self::receive_task(agent.clone()).await {
|
||||
Ok(task_info) => {
|
||||
match Self::inference_task(task_info).await {
|
||||
Ok(bounding_box) => Ok(bounding_box),
|
||||
Err(entry) => {
|
||||
logging_entry!(entry.clone());
|
||||
Err(entry.message)
|
||||
},
|
||||
}
|
||||
},
|
||||
Err(entry) => {
|
||||
logging_entry!(entry.clone());
|
||||
Err(entry.message)
|
||||
},
|
||||
};
|
||||
if let Err(entry) = Self::send_result(agent.clone(), result).await {
|
||||
logging_entry!(entry);
|
||||
}
|
||||
async fn process_task(agent: &Arc<RwLock<Agent>>) -> Result<(), LogEntry> {
|
||||
let task_info = Self::receive_task(agent).await?;
|
||||
let result = Self::waiting_inference(agent, &task_info).await
|
||||
.map_err(|err| err.message);
|
||||
let task_result = TaskResult::new(result);
|
||||
Self::notice_complete(agent, &task_result).await?;
|
||||
Self::transfer_result(agent, &task_info).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive_task(agent: Arc<RwLock<Agent>>) -> Result<TaskInfo, LogEntry> {
|
||||
let task_info = Self::receive_task_info(agent.clone()).await?;
|
||||
async fn receive_task(agent: &Arc<RwLock<Agent>>) -> Result<TaskInfo, LogEntry> {
|
||||
let task_info = Self::receive_task_info(agent).await?;
|
||||
let previous_task_uuid = agent.read().await.previous_task_uuid;
|
||||
let need_receive_model = if let Some(previous_task_uuid) = previous_task_uuid {
|
||||
previous_task_uuid != task_info.uuid
|
||||
@ -236,94 +249,98 @@ impl Agent {
|
||||
true
|
||||
};
|
||||
if need_receive_model {
|
||||
let model_folder = Path::new(".").join("SavedModel");
|
||||
Self::receive_file(agent.clone(), &model_folder).await?;
|
||||
let model_folder = PathBuf::from("./SavedModel");
|
||||
Self::receive_file(agent, &model_folder).await?;
|
||||
agent.write().await.previous_task_uuid = Some(task_info.uuid);
|
||||
}
|
||||
let image_folder = Path::new(".").join("SavedFile");
|
||||
Self::receive_file(agent.clone(), &image_folder).await?;
|
||||
let media_folder = PathBuf::from("./SavedFile");
|
||||
Self::receive_file(agent, &media_folder).await?;
|
||||
Ok(task_info)
|
||||
}
|
||||
|
||||
async fn receive_task_info(agent: Arc<RwLock<Agent>>) -> Result<TaskInfo, LogEntry> {
|
||||
async fn receive_task_info(agent: &Arc<RwLock<Agent>>) -> Result<TaskInfo, LogEntry> {
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.task_info_packet).await;
|
||||
}
|
||||
let config = Config::now().await;
|
||||
let timer = Instant::now();
|
||||
let timeout_duration = Duration::from_secs(config.data_channel_timeout);
|
||||
let task_info = loop {
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
Err(notice_entry!("Agent", "Terminate. Interrupt current operation"))?;
|
||||
Err(information_entry!(SystemEntry::Cancel))?;
|
||||
}
|
||||
if timer.elapsed() > timeout_duration {
|
||||
Err(notice_entry!("Agent", "Data Channel timeout"))?;
|
||||
Err(information_entry!(NetworkEntry::DataChannelTimeout))?;
|
||||
}
|
||||
if let Some(data_channel_receiver) = &mut agent.write().await.data_channel_receiver {
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
packet = data_channel_receiver.task_info_packet.recv() => {
|
||||
let packet = packet
|
||||
.ok_or(notice_entry!("Agent", "Channel has been closed"))?;
|
||||
clear_unbounded_channel(&mut data_channel_receiver.task_info_packet).await;
|
||||
.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
break serde_json::from_slice::<TaskInfo>(packet.as_data_byte())
|
||||
.map_err(|err| error_entry!("Agent", "Unable to parse packet data", format!("Err: {err}")))?;
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeDeserializeError(err)))?;
|
||||
},
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?
|
||||
}
|
||||
};
|
||||
if let Some(data_channel_sender) = &mut agent.write().await.data_channel_sender {
|
||||
data_channel_sender.send(TaskInfoAcknowledgePacket::new()).await;
|
||||
if let Some(data_channel_sender) = agent.write().await.data_channel_sender.as_mut() {
|
||||
data_channel_sender.send(TaskInfoAckPacket::new()).await;
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
return Ok(task_info);
|
||||
Ok(task_info)
|
||||
}
|
||||
|
||||
async fn receive_file(agent: Arc<RwLock<Agent>>, save_folder: &PathBuf) -> Result<(), LogEntry> {
|
||||
let file_header = Self::receive_file_header(agent.clone()).await?;
|
||||
let file_body = Self::receive_file_body(agent.clone(), &file_header).await?;
|
||||
async fn receive_file(agent: &Arc<RwLock<Agent>>, save_folder: &PathBuf) -> Result<(), LogEntry> {
|
||||
let file_header = Self::receive_file_header(agent).await?;
|
||||
let file_body = Self::receive_file_body(agent, &file_header).await?;
|
||||
Self::create_file(file_header, file_body, save_folder).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive_file_header(agent: Arc<RwLock<Agent>>) -> Result<FileHeader, LogEntry> {
|
||||
async fn receive_file_header(agent: &Arc<RwLock<Agent>>) -> Result<FileHeader, LogEntry> {
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_header_packet).await;
|
||||
}
|
||||
let config = Config::now().await;
|
||||
let timer = Instant::now();
|
||||
let timeout_duration = Duration::from_secs(config.data_channel_timeout);
|
||||
if let Some(data_channel_receiver) = &mut agent.write().await.data_channel_receiver {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_header_packet).await;
|
||||
}
|
||||
let file_header = loop {
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
Err(notice_entry!("Agent", "Terminate. Interrupt current operation"))?;
|
||||
Err(information_entry!(SystemEntry::Cancel))?;
|
||||
}
|
||||
if timer.elapsed() > timeout_duration {
|
||||
Err(notice_entry!("Agent", "Data Channel timeout"))?;
|
||||
Err(information_entry!(NetworkEntry::DataChannelTimeout))?;
|
||||
}
|
||||
if let Some(data_channel_receiver) = &mut agent.write().await.data_channel_receiver {
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
packet = data_channel_receiver.file_header_packet.recv() => {
|
||||
let packet = packet
|
||||
.ok_or(notice_entry!("Agent", "Channel has been closed"))?;
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_header_packet).await;
|
||||
.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
break serde_json::from_slice::<FileHeader>(packet.as_data_byte())
|
||||
.map_err(|err| error_entry!("Agent", "Unable to parse packet data", format!("Err: {err}")))?;
|
||||
},
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeDeserializeError(err)))?;
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue
|
||||
}
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
};
|
||||
if let Some(data_channel_sender) = &mut agent.write().await.data_channel_sender {
|
||||
data_channel_sender.send(FileHeaderAcknowledgePacket::new()).await;
|
||||
data_channel_sender.send(FileHeaderAckPacket::new()).await;
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
return Ok(file_header);
|
||||
Ok(file_header)
|
||||
}
|
||||
|
||||
async fn receive_file_body(agent: Arc<RwLock<Agent>>, file_header: &FileHeader) -> Result<Vec<Vec<u8>>, LogEntry> {
|
||||
async fn receive_file_body(agent: &Arc<RwLock<Agent>>, file_header: &FileHeader) -> Result<Vec<Vec<u8>>, LogEntry> {
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_body_packet).await;
|
||||
}
|
||||
let config = Config::now().await;
|
||||
let mut file_block: HashMap<usize, Vec<u8>> = HashMap::new();
|
||||
let mut missing_blocks = Vec::new();
|
||||
@ -331,178 +348,347 @@ impl Agent {
|
||||
let timeout_duration = Duration::from_secs(config.data_channel_timeout);
|
||||
loop {
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
Err(notice_entry!("Agent", "Terminate. Interrupt current operation"))?;
|
||||
Err(information_entry!(SystemEntry::Cancel))?;
|
||||
}
|
||||
if timer.elapsed() > timeout_duration {
|
||||
Err(notice_entry!("Agent", "Data Channel timeout"))?;
|
||||
Err(information_entry!(NetworkEntry::DataChannelTimeout))?;
|
||||
}
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
biased;
|
||||
packet = data_channel_receiver.file_body_packet.recv() => {
|
||||
let packet = &packet
|
||||
.ok_or(notice_entry!("Agent", "Channel has been closed"))?;
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_body_packet).await;
|
||||
timer = Instant::now();
|
||||
let (sequence_bytes, file_body) = packet.data.split_at(mem::size_of::<usize>());
|
||||
let packet = packet
|
||||
.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
let (sequence_bytes, file_body) = packet.data.split_at(size_of::<usize>());
|
||||
let sequence_bytes = sequence_bytes.try_into()
|
||||
.map_err(|err| error_entry!("Agent", "Unable to parse packet data", format!("Err: {err}")))?;
|
||||
.map_err(|_| error_entry!(MiscEntry::InvalidPacket))?;
|
||||
let sequence_number = usize::from_be_bytes(sequence_bytes);
|
||||
file_block.insert(sequence_number, Vec::from(file_body));
|
||||
continue;
|
||||
},
|
||||
packet = data_channel_receiver.file_transfer_end_packet.recv() => {
|
||||
let _ = &packet
|
||||
.ok_or(notice_entry!("Agent", "Channel has been closed"))?;
|
||||
timer = Instant::now();
|
||||
continue;
|
||||
}
|
||||
packet = data_channel_receiver.file_transfer_end_packet.recv() => {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_transfer_end_packet).await;
|
||||
let _ = packet
|
||||
.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
for sequence_number in 0..file_header.packet_count {
|
||||
if !file_block.contains_key(&sequence_number) {
|
||||
missing_blocks.push(sequence_number);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
timer = Instant::now();
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue
|
||||
}
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
if let Some(data_channel_sender) = agent.write().await.data_channel_sender.as_mut() {
|
||||
if missing_blocks.len() != 0_usize {
|
||||
let missing_blocks = mem::take(&mut missing_blocks);
|
||||
let result = FileTransferResult::new(Some(missing_blocks));
|
||||
let result_data = serde_json::to_vec(&result)
|
||||
.map_err(|err| error_entry!("Agent", "Unable to serialize data", format!("Err: {err}")))?;
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeSerializeError(err)))?;
|
||||
data_channel_sender.send(FileTransferResultPacket::new(result_data)).await;
|
||||
} else {
|
||||
let result = FileTransferResult::new(None);
|
||||
let result_data = serde_json::to_vec(&result)
|
||||
.map_err(|err| error_entry!("Agent", "Unable to serialize data", format!("Err: {err}")))?;
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeSerializeError(err)))?;
|
||||
data_channel_sender.send(FileTransferResultPacket::new(result_data)).await;
|
||||
let mut sorted_blocks: Vec<Vec<u8>> = Vec::with_capacity(file_header.packet_count);
|
||||
for index in 0..file_header.packet_count {
|
||||
if let Some(block) = file_block.remove(&index) {
|
||||
sorted_blocks.push(block);
|
||||
} else {
|
||||
Err(error_entry!("Agent", "Missing file block"))?
|
||||
Err(error_entry!(MiscEntry::MissingFileBlockError))?
|
||||
}
|
||||
}
|
||||
return Ok(sorted_blocks);
|
||||
};
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_file(file_header: FileHeader, file_body: Vec<Vec<u8>>, saved_folder: &PathBuf) -> Result<(), LogEntry> {
|
||||
let saved_path = saved_folder.join(file_header.filename);
|
||||
let saved_path = saved_folder.join(file_header.file_name);
|
||||
let mut file = File::create(&saved_path).await
|
||||
.map_err(|err| error_entry!("Agent", "Unable create file", format!("File: {}, Err: {}", saved_path.display(), err)))?;
|
||||
.map_err(|err| error_entry!(IOEntry::CreateFileError(saved_path.display(), err)))?;
|
||||
for chunk in file_body {
|
||||
file.write_all(&chunk).await
|
||||
.map_err(|err| error_entry!("Agent", "Unable to write to file", format!("File: {}, Err: {}", saved_path.display(), err)))?;
|
||||
.map_err(|err| error_entry!(IOEntry::WriteFileError(saved_path.display(), err)))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn inference_task(task_info: TaskInfo) -> Result<Vec<BoundingBox>, LogEntry> {
|
||||
let model_path = Path::new(".").join("SavedModel").join(task_info.model_filename);
|
||||
let image_path = Path::new(".").join("SavedFile").join(task_info.image_filename);
|
||||
return match task_info.model_type {
|
||||
ModelType::Ultralytics => CalculateManager::ultralytics_inference(model_path, image_path).await,
|
||||
ModelType::YOLOv4 => CalculateManager::yolov4_inference(model_path, image_path).await,
|
||||
ModelType::YOLOv7 => CalculateManager::yolov7_inference(model_path, image_path).await,
|
||||
};
|
||||
}
|
||||
|
||||
async fn send_result(agent: Arc<RwLock<Agent>>, result: Result<Vec<BoundingBox>, String>) -> Result<(), LogEntry> {
|
||||
async fn waiting_inference(agent: &Arc<RwLock<Agent>>, task_info: &TaskInfo) -> Result<(), LogEntry> {
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.still_process_packet).await;
|
||||
}
|
||||
let config = Config::now().await;
|
||||
let result = TaskResult::new(result);
|
||||
let result_data = serde_json::to_vec(&result)
|
||||
.map_err(|err| error_entry!("Agent", "Unable to serialize data", format!("Err: {err}")))?;
|
||||
let timer = Instant::now();
|
||||
let mut polling_times = 0_u32;
|
||||
let polling_interval = Duration::from_millis(config.polling_interval);
|
||||
let task_info = task_info.clone();
|
||||
let mut timer = Instant::now();
|
||||
let timeout_duration = Duration::from_secs(config.control_channel_timeout);
|
||||
let join_handle = tokio::spawn(Self::inference(task_info));
|
||||
loop {
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
Err(notice_entry!("Agent", "Terminate. Interrupt current operation"))?;
|
||||
Err(information_entry!(SystemEntry::Cancel))?;
|
||||
}
|
||||
if timer.elapsed() > timeout_duration {
|
||||
Err(notice_entry!("Agent", "Data Channel timeout"))?;
|
||||
Err(information_entry!(NetworkEntry::DataChannelTimeout))?;
|
||||
}
|
||||
if timer.elapsed() > polling_times * polling_interval {
|
||||
if let Some(data_channel_sender) = agent.write().await.data_channel_sender.as_mut() {
|
||||
data_channel_sender.send(ResultPacket::new(result_data.clone())).await;
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?;
|
||||
}
|
||||
polling_times += 1;
|
||||
if join_handle.is_finished() {
|
||||
break join_handle.await
|
||||
.map_err(|err| error_entry!(SystemEntry::TaskPanickedError(err)))?;
|
||||
}
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
let mut agent = agent.write().await;
|
||||
if let Some(data_channel_receiver) = agent.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
packet = data_channel_receiver.result_acknowledge_packet.recv() => {
|
||||
if let Some(_) = packet {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.result_acknowledge_packet).await;
|
||||
return Ok(());
|
||||
} else {
|
||||
Err(notice_entry!("Agent", "Channel has been closed"))?;
|
||||
}
|
||||
},
|
||||
packet = data_channel_receiver.still_process_packet.recv() => {
|
||||
let _ = packet.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
clear_unbounded_channel(&mut data_channel_receiver.still_process_packet).await;
|
||||
timer = Instant::now();
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
} else {
|
||||
Err(warning_entry!("Agent", "Data Channel is not ready"))?;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
if let Some(data_channel_sender) = agent.data_channel_sender.as_mut() {
|
||||
data_channel_sender.send(StillProcessAckPacket::new()).await;
|
||||
} else {
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn idle(agent: Arc<RwLock<Agent>>, idle_duration: Duration) {
|
||||
async fn inference(task_info: TaskInfo) -> Result<(), LogEntry> {
|
||||
let inference_argument = task_info.inference_argument;
|
||||
let model_path = PathBuf::from(format!("./SavedModel/{}", task_info.model_file_name));
|
||||
let media_path = PathBuf::from(format!("./SavedFile/{}", task_info.media_file_name));
|
||||
match (inference_argument.model_type, media_path.extension().and_then(OsStr::to_str)) {
|
||||
(ModelType::Ultralytics, Some("png") | Some("jpg") | Some("jpeg")) =>
|
||||
InferenceManager::ultralytics_inference_image(inference_argument, model_path, media_path).await,
|
||||
(ModelType::Ultralytics, Some("mp4")) =>
|
||||
InferenceManager::ultralytics_inference_video(inference_argument, model_path, media_path).await,
|
||||
(ModelType::YOLOv4, Some("png") | Some("jpg") | Some("jpeg")) =>
|
||||
InferenceManager::yolov4_inference_picture(inference_argument, model_path, media_path).await,
|
||||
(ModelType::YOLOv4, Some("mp4")) =>
|
||||
InferenceManager::yolov4_inference_video(inference_argument, model_path, media_path).await,
|
||||
(ModelType::YOLOv7, Some("png") | Some("jpg") | Some("jpeg")) =>
|
||||
InferenceManager::yolov7_inference_picture(inference_argument, model_path, media_path).await,
|
||||
(ModelType::YOLOv7, Some("mp4")) =>
|
||||
InferenceManager::yolov7_inference_video(inference_argument, model_path, media_path).await,
|
||||
_ => Err(error_entry!(TaskEntry::UnSupportFileType(task_info.uuid))),
|
||||
}
|
||||
}
|
||||
|
||||
async fn notice_complete(agent: &Arc<RwLock<Agent>>, task_result: &TaskResult) -> Result<(), LogEntry> {
|
||||
let config = Config::now().await;
|
||||
let task_result_data = serde_json::to_vec(task_result)
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeSerializeError(err)))?;
|
||||
let mut polling_times = 0_u32;
|
||||
let timer = Instant::now();
|
||||
let polling_interval = Duration::from_millis(config.polling_interval);
|
||||
let timeout_duration = Duration::from_secs(config.control_channel_timeout);
|
||||
loop {
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
logging_notice!("Agent", "Terminate. Interrupt current operation");
|
||||
return;
|
||||
Err(information_entry!(SystemEntry::Cancel))?;
|
||||
}
|
||||
if timer.elapsed() > idle_duration {
|
||||
return;
|
||||
if timer.elapsed() > timeout_duration {
|
||||
Err(information_entry!(NetworkEntry::DataChannelTimeout))?;
|
||||
}
|
||||
if timer.elapsed() > polling_times * polling_interval {
|
||||
let mut agent = agent.write().await;
|
||||
let data_channel_sender = agent.data_channel_sender.as_mut()
|
||||
.ok_or(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
data_channel_sender.send(TaskResultPacket::new(task_result_data.clone())).await;
|
||||
polling_times += 1;
|
||||
}
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
packet = data_channel_receiver.task_result_ack_packet.recv() => {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.task_result_ack_packet).await;
|
||||
if packet.is_some() {
|
||||
return Ok(())
|
||||
} else {
|
||||
Err(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
}
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue
|
||||
}
|
||||
} else {
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn transfer_result(agent: &Arc<RwLock<Agent>>, task_info: &TaskInfo) -> Result<(), LogEntry> {
|
||||
let file_name = task_info.media_file_name.clone();
|
||||
let file_path = PathBuf::from(format!("./Result/{}", file_name));
|
||||
let file_body = Self::read_file(agent, &file_path).await?;
|
||||
Self::transfer_file_header(agent, &file_name, file_body.len()).await?;
|
||||
Self::transfer_file_body(agent, file_body).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn read_file(agent: &Arc<RwLock<Agent>>, file_path: &PathBuf) -> Result<Vec<Vec<u8>>, LogEntry> {
|
||||
let mut sequence_number = 0_usize;
|
||||
let mut buffer = vec![0; 1_048_576];
|
||||
let mut packets = Vec::new();
|
||||
let mut file = File::open(file_path.clone()).await
|
||||
.map_err(|err| error_entry!(IOEntry::ReadFileError(file_path.display(), err)))?;
|
||||
while agent.read().await.state != AgentState::Terminate {
|
||||
let bytes_read = file.read(&mut buffer).await
|
||||
.map_err(|err| error_entry!(IOEntry::ReadFileError(file_path.display(), err)))?;
|
||||
if bytes_read == 0 {
|
||||
return Ok(packets);
|
||||
}
|
||||
let mut data = sequence_number.to_be_bytes().to_vec();
|
||||
data.extend_from_slice(&buffer[..bytes_read]);
|
||||
packets.push(data);
|
||||
sequence_number += 1;
|
||||
}
|
||||
Err(information_entry!(SystemEntry::Cancel))?
|
||||
}
|
||||
|
||||
async fn transfer_file_header(agent: &Arc<RwLock<Agent>>, file_name: &String, packet_count: usize) -> Result<(), LogEntry> {
|
||||
let config = Config::now().await;
|
||||
let file_header = FileHeader::new(file_name.clone(), packet_count);
|
||||
let file_header_data = serde_json::to_vec(&file_header)
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeSerializeError(err)))?;
|
||||
let timer = Instant::now();
|
||||
let mut polling_times = 0_u32;
|
||||
let polling_interval = Duration::from_millis(config.polling_interval);
|
||||
let timeout_duration = Duration::from_secs(config.control_channel_timeout);
|
||||
while agent.read().await.state != AgentState::Terminate {
|
||||
if timer.elapsed() > timeout_duration {
|
||||
Err(information_entry!(NetworkEntry::DataChannelTimeout))?;
|
||||
}
|
||||
if timer.elapsed() > polling_times * polling_interval {
|
||||
let mut agent = agent.write().await;
|
||||
let data_channel_sender = agent.data_channel_sender.as_mut()
|
||||
.ok_or(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
data_channel_sender.send(FileHeaderPacket::new(file_header_data.clone())).await;
|
||||
polling_times += 1;
|
||||
}
|
||||
let mut agent = agent.write().await;
|
||||
if let Some(data_channel_receiver) = agent.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
packet = data_channel_receiver.file_header_ack_packet.recv() => {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_header_ack_packet).await;
|
||||
if packet.is_some() {
|
||||
return Ok(())
|
||||
} else {
|
||||
Err(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
}
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue
|
||||
}
|
||||
} else {
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?
|
||||
}
|
||||
}
|
||||
Err(information_entry!(SystemEntry::Cancel))
|
||||
}
|
||||
|
||||
async fn transfer_file_body(agent: &Arc<RwLock<Agent>>, file_body: Vec<Vec<u8>>) -> Result<(), LogEntry> {
|
||||
let config = Config::now().await;
|
||||
let mut timer = Instant::now();
|
||||
let mut require_send: Vec<usize> = (0..file_body.len()).collect();
|
||||
let timeout_duration = Duration::from_secs(config.file_transfer_timeout);
|
||||
while agent.read().await.state != AgentState::Terminate {
|
||||
if timer.elapsed() > timeout_duration {
|
||||
agent.write().await.state = AgentState::CreateDataChannel;
|
||||
Err(information_entry!(NetworkEntry::DataChannelTimeout))?;
|
||||
}
|
||||
for chunk in &require_send {
|
||||
let data = file_body.get(*chunk)
|
||||
.ok_or(error_entry!(MiscEntry::MissingFileBlockError))?;
|
||||
let mut agent = agent.write().await;
|
||||
let data_channel_sender = agent.data_channel_sender.as_mut()
|
||||
.ok_or(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
data_channel_sender.send(FileBodyPacket::new(data.clone())).await;
|
||||
}
|
||||
if !require_send.is_empty() {
|
||||
let mut agent = agent.write().await;
|
||||
let data_channel_sender = agent.data_channel_sender.as_mut()
|
||||
.ok_or(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
data_channel_sender.send(FileTransferEndPacket::new()).await;
|
||||
require_send = Vec::new();
|
||||
timer = Instant::now();
|
||||
}
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
biased;
|
||||
packet = data_channel_receiver.file_transfer_result_packet.recv() => {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.file_transfer_result_packet).await;
|
||||
let packet = packet.ok_or(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
let file_transfer_result = serde_json::from_slice::<FileTransferResult>(packet.as_data_byte())
|
||||
.map_err(|err| error_entry!(IOEntry::SerdeDeserializeError(err)))?;
|
||||
timer = Instant::now();
|
||||
match file_transfer_result.into() {
|
||||
Some(missing_chunks) => require_send = missing_chunks,
|
||||
None => return Ok(()),
|
||||
}
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue
|
||||
}
|
||||
} else {
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?
|
||||
}
|
||||
}
|
||||
Err(information_entry!(SystemEntry::Cancel))?
|
||||
}
|
||||
|
||||
async fn idle(agent: &Arc<RwLock<Agent>>, idle_duration: Duration) -> Result<(), LogEntry> {
|
||||
if let Some(data_channel_receiver) = agent.write().await.data_channel_receiver.as_mut() {
|
||||
clear_unbounded_channel(&mut data_channel_receiver.alive_packet).await;
|
||||
}
|
||||
let config = Config::now().await;
|
||||
let timer = Instant::now();
|
||||
while timer.elapsed() <= idle_duration {
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
Err(information_entry!(SystemEntry::Cancel))?;
|
||||
}
|
||||
let mut agent = agent.write().await;
|
||||
if let Some(data_channel_receiver) = agent.data_channel_receiver.as_mut() {
|
||||
select! {
|
||||
biased;
|
||||
_ = data_channel_receiver.alive_packet.recv() => clear_unbounded_channel(&mut data_channel_receiver.alive_packet).await,
|
||||
_ = data_channel_receiver.alive_packet.recv() =>
|
||||
clear_unbounded_channel(&mut data_channel_receiver.alive_packet).await,
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
} else {
|
||||
logging_warning!("Agent", "Data Channel is not available.");
|
||||
return;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
if let Some(data_channel_sender) = agent.data_channel_sender.as_mut() {
|
||||
data_channel_sender.send(AliveAcknowledgePacket::new()).await;
|
||||
data_channel_sender.send(AliveAckPacket::new()).await;
|
||||
} else {
|
||||
logging_warning!("Agent", "Data Channel is not available.");
|
||||
return;
|
||||
Err(warning_entry!(NetworkEntry::DataChannelNotReady))?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_assignments)]
|
||||
async fn create_data_channel(agent: Arc<RwLock<Agent>>) {
|
||||
async fn create_data_channel(agent: &Arc<RwLock<Agent>>) -> Result<(), LogEntry> {
|
||||
{
|
||||
let mut agent = agent.write().await;
|
||||
clear_unbounded_channel(&mut agent.control_channel_receiver.data_channel_port_packet).await;
|
||||
}
|
||||
let config = Config::now().await;
|
||||
let mut port: Option<u16> = None;
|
||||
let timer = Instant::now();
|
||||
let timeout_duration = Duration::from_secs(config.control_channel_timeout);
|
||||
loop {
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
logging_notice!("Agent", "Terminate. Interrupt current operation");
|
||||
return;
|
||||
Err(information_entry!(SystemEntry::Cancel))?;
|
||||
}
|
||||
if timer.elapsed() > timeout_duration {
|
||||
agent.write().await.state = AgentState::Terminate;
|
||||
logging_notice!("Agent", "Control Channel timout.");
|
||||
return;
|
||||
Err(information_entry!(NetworkEntry::ControlChannelTimeout))?;
|
||||
}
|
||||
{
|
||||
let mut agent = agent.write().await;
|
||||
@ -515,53 +701,30 @@ impl Agent {
|
||||
if bytes.len() == 2 {
|
||||
port = Some(u16::from_be_bytes([bytes[0], bytes[1]]))
|
||||
} else {
|
||||
logging_error!("Agent", "Unable to parse packet data");
|
||||
logging_error!(MiscEntry::InvalidPacket);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
agent.state = AgentState::Terminate;
|
||||
logging_notice!("Agent", "Channel has been closed");
|
||||
return;
|
||||
Err(information_entry!(NetworkEntry::ChannelClosed))?;
|
||||
}
|
||||
},
|
||||
}
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
}
|
||||
if let Some(port) = port {
|
||||
let address = format!("{}:{}", config.management_address, port);
|
||||
match TcpStream::connect(&address).await {
|
||||
Ok(tcp_stream) => {
|
||||
let socket_stream = SocketStream::new(tcp_stream);
|
||||
let (data_channel_sender, data_channel_receiver) = DataChannel::new(socket_stream);
|
||||
let mut agent = agent.write().await;
|
||||
agent.data_channel_sender = Some(data_channel_sender);
|
||||
agent.data_channel_receiver = Some(data_channel_receiver);
|
||||
logging_information!("Agent", "Data Channel created successfully");
|
||||
return;
|
||||
},
|
||||
Err(err) => {
|
||||
logging_error!("Agent", "Unable to establish connection", format!("Err: {err}"));
|
||||
return;
|
||||
},
|
||||
}
|
||||
} else {
|
||||
logging_error!("Agent", "Unknown error");
|
||||
return;
|
||||
let tcp_stream = TcpStream::connect(&address).await
|
||||
.map_err(|err| error_entry!(NetworkEntry::EstablishConnectionError(err)))?;
|
||||
let socket_stream = SocketStream::new(tcp_stream);
|
||||
let (data_channel_sender, data_channel_receiver) = DataChannel::new(socket_stream);
|
||||
let mut agent = agent.write().await;
|
||||
agent.data_channel_sender = Some(data_channel_sender);
|
||||
agent.data_channel_receiver = Some(data_channel_receiver);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn terminate(agent: Arc<RwLock<Agent>>) {
|
||||
logging_information!("Agent", "Termination in progress");
|
||||
let mut agent = agent.write().await;
|
||||
agent.control_channel_sender.disconnect().await;
|
||||
agent.control_channel_receiver.disconnect().await;
|
||||
if let Some(data_channel_sender) = &mut agent.data_channel_sender {
|
||||
data_channel_sender.disconnect().await;
|
||||
}
|
||||
if let Some(data_channel_receiver) = &mut agent.data_channel_receiver {
|
||||
data_channel_receiver.disconnect().await;
|
||||
}
|
||||
logging_information!("Agent", "Termination complete");
|
||||
logging_information!(NetworkEntry::CreateDataChannelSuccess);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,43 +0,0 @@
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
use tokio::process::Command as AsyncCommand;
|
||||
use crate::utils::logging::*;
|
||||
use crate::management::utils::bounding_box::BoundingBox;
|
||||
|
||||
pub struct CalculateManager;
|
||||
|
||||
impl CalculateManager {
|
||||
pub async fn ultralytics_inference(model_path: PathBuf, image_path: PathBuf) -> Result<Vec<BoundingBox>, LogEntry> {
|
||||
#[cfg(target_os = "windows")]
|
||||
let python = "python";
|
||||
#[cfg(target_os = "linux")]
|
||||
let python = "python3";
|
||||
let process = AsyncCommand::new(python)
|
||||
.arg("Script/ultralytics/inference.py")
|
||||
.arg(model_path)
|
||||
.arg(image_path)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|err| error_entry!("Calculate Manager", "Unable to create process", format!("Err: {err}")))?;
|
||||
let output = process.wait_with_output().await
|
||||
.map_err(|err| error_entry!("Calculate Manager", "An error occurred during process execution", format!("Err: {err}")))?;
|
||||
if output.status.success() {
|
||||
let serialized_data = String::from_utf8_lossy(&output.stdout);
|
||||
let bounding_boxes: Vec<BoundingBox> = serde_json::from_str(&serialized_data)
|
||||
.map_err(|err| error_entry!("Calculate Manager", "Unable to deserialize data", format!("Err: {err}")))?;
|
||||
Ok(bounding_boxes)
|
||||
} else {
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
Err(error_entry!("Calculate Manager", "An error occurred during process execution", format!("Err: {err}")))?
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn yolov4_inference(_model_path: PathBuf, _image_path: PathBuf) -> Result<Vec<BoundingBox>, LogEntry> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
pub async fn yolov7_inference(_model_path: PathBuf, _image_path: PathBuf) -> Result<Vec<BoundingBox>, LogEntry> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,23 @@
|
||||
use tokio::fs;
|
||||
use tokio::fs::File;
|
||||
use crate::utils::logging::*;
|
||||
use crate::utils::logging::{LogLevel, Logger};
|
||||
use crate::utils::static_files::StaticFiles;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
use tokio::fs;
|
||||
use tokio::fs::File;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::process::Command as AsyncCommand;
|
||||
use crate::utils::logging::*;
|
||||
use crate::utils::static_files::StaticFiles;
|
||||
use crate::utils::logging::{Logger, LogLevel};
|
||||
|
||||
pub struct FileManager;
|
||||
|
||||
impl FileManager {
|
||||
pub async fn initialize() {
|
||||
logging_information!("File Manager", "Initializing");
|
||||
let folders = ["SavedModel", "SavedFile", "Script", "Script/ultralytics"];
|
||||
logging_information!(SystemEntry::Initializing);
|
||||
let folders = ["SavedModel", "SavedFile", "Result", "Script", "Script/ultralytics"];
|
||||
for &folder_name in &folders {
|
||||
match fs::create_dir(folder_name).await {
|
||||
Ok(_) => logging_information!("File Manager", format!("Create {} folder successfully", folder_name)),
|
||||
Err(err) => logging_error!("File Manager", format!("Cannot create {} folder", folder_name), format!("Err: {err}")),
|
||||
let path = PathBuf::from(folder_name);
|
||||
if let Err(err) = fs::create_dir(folder_name).await {
|
||||
logging_error!(IOEntry::CreateDirectoryError(path.display(), err));
|
||||
}
|
||||
}
|
||||
if let Err(entry) = Self::clone_repository().await {
|
||||
@ -26,19 +26,19 @@ impl FileManager {
|
||||
if let Err(entry) = Self::extract_embed_folders().await {
|
||||
logging_entry!(entry);
|
||||
}
|
||||
logging_information!("File Manager", "Initialization completed");
|
||||
logging_information!(SystemEntry::InitializeComplete);
|
||||
}
|
||||
|
||||
pub async fn cleanup() {
|
||||
logging_information!("File Manager", "Cleaning up");
|
||||
let folders = ["SavedModel", "SavedFile", "Script"];
|
||||
logging_information!(SystemEntry::Cleaning);
|
||||
let folders = ["SavedModel", "SavedFile", "Result", "Script"];
|
||||
for &folder_name in &folders {
|
||||
match fs::remove_dir_all(folder_name).await {
|
||||
Ok(_) => logging_information!("File Manager", format!("Delete {folder_name} folder successfully")),
|
||||
Err(err) => logging_error!("File Manager", format!("Failed to delete {folder_name} folder"), format!("Err: {err}")),
|
||||
let path = PathBuf::from(folder_name);
|
||||
if let Err(err) = fs::remove_dir_all(folder_name).await {
|
||||
logging_error!(IOEntry::DeleteDirectoryError(path.display(), err));
|
||||
}
|
||||
};
|
||||
logging_information!("File Manager", "Cleanup completed");
|
||||
logging_information!(SystemEntry::CleanComplete);
|
||||
}
|
||||
|
||||
pub async fn extract_embed_folders() -> Result<(), LogEntry> {
|
||||
@ -47,14 +47,14 @@ impl FileManager {
|
||||
if let Some(first_part) = file_path.iter().next().and_then(|s| s.to_str()) {
|
||||
if first_part.eq("script") {
|
||||
let relative_path = file_path.strip_prefix(first_part).unwrap_or(&file_path);
|
||||
let full_path = PathBuf::from("Script").join(relative_path);
|
||||
let full_path = PathBuf::from(format!("Script/{}", relative_path.display()));
|
||||
let file_data = &StaticFiles::get(file.as_ref())
|
||||
.ok_or(error_entry!("File Manager", "Unable to read file", format!("File: {}", full_path.display())))?
|
||||
.ok_or(error_entry!("Unable to read file", format!("File: {}", full_path.display())))?
|
||||
.data;
|
||||
let mut file = File::create(&full_path).await
|
||||
.map_err(|err| error_entry!("File Manager", "Unable to create file", format!("File: {}, Err: {}", full_path.display(), err)))?;
|
||||
.map_err(|err| error_entry!("Unable to create file", format!("File: {}, Err: {}", full_path.display(), err)))?;
|
||||
file.write_all(file_data).await
|
||||
.map_err(|err| error_entry!("File Manager", "Unable to write file", format!("File: {}, Err: {}", full_path.display(), err)))?;
|
||||
.map_err(|err| error_entry!("Unable to write file", format!("File: {}, Err: {}", full_path.display(), err)))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,16 +68,18 @@ impl FileManager {
|
||||
let mut cmd = AsyncCommand::new("cmd");
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut cmd = AsyncCommand::new("sh");
|
||||
let status = cmd
|
||||
let mut process = cmd
|
||||
.arg(if cfg!(target_os = "windows") { "/C" } else { "-c" })
|
||||
.arg(format!("cd Script/ && git clone {} --depth 1 && git clone {} --depth 1", yolov4_repository, yolov7_repository))
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.status()
|
||||
.await
|
||||
.map_err(|err| critical_entry!("File Manager", "Unable to create process", format!("Err: {err}")))?;
|
||||
.spawn()
|
||||
.map_err(|err| critical_entry!(SystemEntry::ChildProcessError(err.to_string())))?;
|
||||
let status = process.wait().await
|
||||
.map_err(|err| error_entry!(SystemEntry::ChildProcessError(err.to_string())))?;
|
||||
if !status.success() {
|
||||
Err(critical_entry!("File Manager", "An error occurred during process execution"))?
|
||||
let err = format!("Process exit with code: {}", status.code().unwrap_or(-1));
|
||||
Err(error_entry!(SystemEntry::ChildProcessError(err)))?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
98
AgentLibrary/src/management/inference_manager.rs
Normal file
@ -0,0 +1,98 @@
|
||||
use crate::management::utils::inference_argument::InferenceArgument;
|
||||
use crate::utils::logging::*;
|
||||
use std::path::PathBuf;
|
||||
use std::process::Stdio;
|
||||
use tokio::process::Command as AsyncCommand;
|
||||
|
||||
pub struct InferenceManager;
|
||||
|
||||
impl InferenceManager {
|
||||
pub async fn ultralytics_inference_image(inference_argument: InferenceArgument,
|
||||
model_path: PathBuf, image_path: PathBuf) -> Result<(), LogEntry>
|
||||
{
|
||||
#[cfg(target_os = "windows")]
|
||||
let python = "python";
|
||||
#[cfg(target_os = "linux")]
|
||||
let python = "python3";
|
||||
let save_folder = PathBuf::from("./Result");
|
||||
let mut process = AsyncCommand::new(python)
|
||||
.arg("Script/ultralytics/picture_inference.py")
|
||||
.arg(inference_argument.detect_mode.to_string())
|
||||
.arg(model_path)
|
||||
.arg(image_path)
|
||||
.arg(save_folder)
|
||||
.arg(inference_argument.imgsz.to_string())
|
||||
.arg(inference_argument.conf.to_string())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|err| error_entry!(SystemEntry::ChildProcessError(err.to_string())))?;
|
||||
let status = process.wait().await
|
||||
.map_err(|err| error_entry!(SystemEntry::ChildProcessError(err.to_string())))?;
|
||||
if !status.success() {
|
||||
let err = format!("Process exit with code: {}", status.code().unwrap_or(-1));
|
||||
Err(error_entry!(SystemEntry::ChildProcessError(err)))?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn ultralytics_inference_video(inference_argument: InferenceArgument,
|
||||
model_path: PathBuf, video_path: PathBuf) -> Result<(), LogEntry>
|
||||
{
|
||||
#[cfg(target_os = "windows")]
|
||||
let python = "python";
|
||||
#[cfg(target_os = "linux")]
|
||||
let python = "python3";
|
||||
let save_folder = PathBuf::from("./Result");
|
||||
let mut process = AsyncCommand::new(python)
|
||||
.arg("Script/ultralytics/video_inference.py")
|
||||
.arg(inference_argument.detect_mode.to_string())
|
||||
.arg(model_path)
|
||||
.arg(video_path)
|
||||
.arg(save_folder)
|
||||
.arg(inference_argument.imgsz.to_string())
|
||||
.arg(inference_argument.conf.to_string())
|
||||
.arg(inference_argument.batch.to_string())
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
.map_err(|err| error_entry!(SystemEntry::ChildProcessError(err.to_string())))?;
|
||||
let status = process.wait().await
|
||||
.map_err(|err| error_entry!(SystemEntry::ChildProcessError(err.to_string())))?;
|
||||
if !status.success() {
|
||||
let err = format!("Process exit with code: {}", status.code().unwrap_or(-1));
|
||||
Err(error_entry!(SystemEntry::ChildProcessError(err)))?
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub async fn yolov4_inference_picture(inference_argument: InferenceArgument,
|
||||
model_path: PathBuf, video_path: PathBuf) -> Result<(), LogEntry>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub async fn yolov4_inference_video(inference_argument: InferenceArgument,
|
||||
model_path: PathBuf, video_path: PathBuf) -> Result<(), LogEntry>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub async fn yolov7_inference_picture(inference_argument: InferenceArgument,
|
||||
model_path: PathBuf, video_path: PathBuf) -> Result<(), LogEntry>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
#[allow(unused_variables)]
|
||||
pub async fn yolov7_inference_video(inference_argument: InferenceArgument,
|
||||
model_path: PathBuf, video_path: PathBuf) -> Result<(), LogEntry>
|
||||
{
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1,17 +1,18 @@
|
||||
use tokio::select;
|
||||
use std::sync::Arc;
|
||||
use tokio::time::sleep;
|
||||
use async_ctrlc::CtrlC;
|
||||
use std::time::Duration;
|
||||
use lazy_static::lazy_static;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use crate::utils::logging::*;
|
||||
use crate::utils::config::Config;
|
||||
use crate::management::agent::Agent;
|
||||
use crate::management::monitor::Monitor;
|
||||
use crate::management::file_manager::FileManager;
|
||||
use crate::management::utils::agent_state::AgentState;
|
||||
use crate::connection::socket::management_socket::ManagementSocket;
|
||||
use crate::management::agent::Agent;
|
||||
use crate::management::file_manager::FileManager;
|
||||
use crate::management::monitor::Monitor;
|
||||
use crate::management::utils::agent_state::AgentState;
|
||||
use crate::utils::config::Config;
|
||||
use crate::utils::logging::*;
|
||||
use async_ctrlc::CtrlC;
|
||||
use lazy_static::lazy_static;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::select;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use tokio::time::sleep;
|
||||
use Common::utils::log_entry::system::SystemEntry;
|
||||
|
||||
lazy_static! {
|
||||
static ref MANAGEMENT: RwLock<Management> = RwLock::new(Management::new());
|
||||
@ -44,19 +45,19 @@ impl Management {
|
||||
tokio::spawn(async move {
|
||||
Self::hot_reload().await;
|
||||
});
|
||||
logging_information!("Management", "Online now");
|
||||
logging_information!(SystemEntry::Online);
|
||||
match CtrlC::new() {
|
||||
Ok(ctrlc) => ctrlc.await,
|
||||
Err(err) => logging_emergency!("Management", "Unable to create instance", format!("Err: {err}")),
|
||||
Err(err) => logging_emergency!("Unable to create instance", format!("Err: {err}")),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn terminate() {
|
||||
logging_information!("Management", "Termination in process");
|
||||
logging_information!(SystemEntry::Terminating);
|
||||
Self::instance_mut().await.terminate = true;
|
||||
Monitor::terminate().await;
|
||||
FileManager::cleanup().await;
|
||||
logging_information!("Management", "Termination complete");
|
||||
logging_information!(SystemEntry::TerminateComplete);
|
||||
}
|
||||
|
||||
pub async fn hot_reload() {
|
||||
@ -64,9 +65,8 @@ impl Management {
|
||||
while !Self::instance().await.terminate {
|
||||
let mut management = Self::instance_mut().await;
|
||||
if let Some(agent) = management.agent.clone() {
|
||||
match agent.read().await.state {
|
||||
AgentState::Terminate => management.agent = None,
|
||||
_ => sleep(Duration::from_millis(config.internal_timestamp)).await,
|
||||
if agent.read().await.state == AgentState::Terminate {
|
||||
management.agent = None;
|
||||
}
|
||||
} else {
|
||||
select! {
|
||||
@ -76,12 +76,12 @@ impl Management {
|
||||
let agent = Arc::new(RwLock::new(agent));
|
||||
Agent::run(agent.clone()).await;
|
||||
management.agent = Some(agent);
|
||||
logging_information!("Management", format!("Management {management_ip} is connected"));
|
||||
},
|
||||
Err(entry) => logging_entry!(entry),
|
||||
logging_information!(SystemEntry::ManagementConnect(management_ip));
|
||||
}
|
||||
Err(entry) => logging_entry!(entry)
|
||||
}
|
||||
},
|
||||
_ = sleep(Duration::from_millis(config.internal_timestamp)) => continue,
|
||||
}
|
||||
_ = sleep(Duration::from_secs(config.refresh_interval)) => continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
pub mod utils;
|
||||
pub mod agent;
|
||||
pub mod calculate_manager;
|
||||
pub mod file_manager;
|
||||
pub mod inference_manager;
|
||||
pub mod management;
|
||||
|
||||
pub use Common::management::*;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use std::fs;
|
||||
use tokio::sync::RwLock;
|
||||
use std::net::ToSocketAddrs;
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::net::ToSocketAddrs;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
lazy_static! {
|
||||
static ref CONFIG: RwLock<Config> = RwLock::new(Config::new());
|
||||
@ -19,11 +19,11 @@ pub struct Config {
|
||||
pub internal_timestamp: u64,
|
||||
pub management_address: String,
|
||||
pub management_port: u16,
|
||||
pub refresh_interval: u64,
|
||||
pub polling_interval: u64,
|
||||
pub control_channel_timeout: u64,
|
||||
pub data_channel_timeout: u64,
|
||||
pub file_transfer_timeout: u64,
|
||||
pub confidence_threshold: f64,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@ -50,9 +50,10 @@ impl Config {
|
||||
Config::validate_mini_second(config.internal_timestamp)
|
||||
&& Config::validate_full_address(&config.management_address, config.management_port)
|
||||
&& Config::validate_second(config.control_channel_timeout)
|
||||
&& Config::validate_mini_second(config.refresh_interval)
|
||||
&& Config::validate_second(config.polling_interval)
|
||||
&& Config::validate_second(config.data_channel_timeout)
|
||||
&& Config::validate_second(config.file_transfer_timeout)
|
||||
&& Config::validate_confidence(config.confidence_threshold)
|
||||
}
|
||||
|
||||
fn validate_mini_second(second: u64) -> bool {
|
||||
@ -66,8 +67,4 @@ impl Config {
|
||||
fn validate_full_address(address: &str, port: u16) -> bool {
|
||||
format!("{}:{}", address, port).to_socket_addrs().is_ok()
|
||||
}
|
||||
|
||||
fn validate_confidence(confidence_threshold: f64) -> bool {
|
||||
!confidence_threshold.is_nan() && confidence_threshold >= 0.0 && confidence_threshold <= 1.0
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
pub use crate::{logging_alert, logging_critical, logging_debug, logging_emergency, logging_entry, logging_error, logging_information, logging_warning};
|
||||
pub use Common::utils::log_entry::io::IOEntry;
|
||||
pub use Common::utils::log_entry::misc::MiscEntry;
|
||||
pub use Common::utils::log_entry::network::NetworkEntry;
|
||||
pub use Common::utils::log_entry::system::SystemEntry;
|
||||
pub use Common::utils::log_entry::task::TaskEntry;
|
||||
pub use Common::utils::logging::*;
|
||||
pub use Common::{debug_entry, information_entry, notice_entry, warning_entry, error_entry, critical_entry, alert_entry, emergency_entry};
|
||||
pub use crate::{logging_debug, logging_information, logging_notice, logging_warning, logging_error, logging_critical, logging_alert, logging_emergency, logging_entry};
|
||||
pub use Common::{alert_entry, critical_entry, debug_entry, emergency_entry, error_entry, information_entry, warning_entry};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use std::collections::VecDeque;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
lazy_static! {
|
||||
@ -11,14 +15,14 @@ lazy_static! {
|
||||
}
|
||||
|
||||
pub struct Logger {
|
||||
system_log: VecDeque<LogEntry>,
|
||||
system_log: Vec<LogEntry>,
|
||||
}
|
||||
|
||||
impl Logger {
|
||||
fn new() -> Self {
|
||||
let mut system_log = VecDeque::new();
|
||||
let mut system_log = Vec::new();
|
||||
let log_entry = information_entry!("Logger", "Online now");
|
||||
system_log.push_back(log_entry);
|
||||
system_log.push(log_entry);
|
||||
Self {
|
||||
system_log,
|
||||
}
|
||||
@ -36,101 +40,91 @@ impl Logger {
|
||||
let log_entry = LogEntry::new(level, position, message, debug_info);
|
||||
Self::logging_console(log_entry.clone());
|
||||
let mut logger = Self::instance_mut().await;
|
||||
logger.system_log.push_back(log_entry);
|
||||
logger.system_log.push(log_entry);
|
||||
}
|
||||
|
||||
pub async fn add_system_log_entry(log_entry: LogEntry) {
|
||||
Self::logging_console(log_entry.clone());
|
||||
let mut logger = Self::instance_mut().await;
|
||||
logger.system_log.push_back(log_entry);
|
||||
logger.system_log.push(log_entry);
|
||||
}
|
||||
|
||||
pub fn logging_console(log_entry: LogEntry) {
|
||||
println!("{}", log_entry.to_colored_string());
|
||||
}
|
||||
|
||||
pub async fn get_system_logs() -> VecDeque<LogEntry> {
|
||||
pub async fn get_system_logs() -> Vec<LogEntry> {
|
||||
Self::instance().await.system_log.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_debug {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Debug, $position, $message, "").await
|
||||
($message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Debug, format!("{}:{}", file!(), line!()), $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Debug, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
($message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Debug, format!("{}:{}", file!(), line!()), $message, $debug_info).await
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_information {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Information, $position, $message, "").await
|
||||
($message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Information, format!("{}:{}", file!(), line!()), $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Information, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_notice {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Notice, $position, $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Notice, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
($message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Information, format!("{}:{}", file!(), line!()), $message, $debug_info).await
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_warning {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Warning, $position, $message, "").await
|
||||
($message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Warning, format!("{}:{}", file!(), line!()), $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Warning, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
($message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Warning, format!("{}:{}", file!(), line!()), $message, $debug_info).await
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_error {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Error, $position, $message, "").await
|
||||
($message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Error, format!("{}:{}", file!(), line!()), $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Error, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
($message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Error, format!("{}:{}", file!(), line!()), $message, $debug_info).await
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_critical {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Critical, $position, $message, "").await
|
||||
($message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Critical, format!("{}:{}", file!(), line!()), $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Critical, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
($message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Critical, format!("{}:{}", file!(), line!()), $message, $debug_info).await
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_alert {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Alert, $position, $message, "").await
|
||||
($message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Alert, format!("{}:{}", file!(), line!()), $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Alert, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
($message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Alert, format!("{}:{}", file!(), line!()), $message, $debug_info).await
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! logging_emergency {
|
||||
($position:expr, $message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Emergency, $position, $message, "").await
|
||||
($message:expr) => {
|
||||
Logger::add_system_log(LogLevel::Emergency, format!("{}:{}", file!(), line!()), $message, "").await
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Emergency, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info)).await
|
||||
($message:expr, $debug_info:expr) => {
|
||||
Logger::add_system_log(LogLevel::Emergency, format!("{}:{}", file!(), line!()), $message, $debug_info).await
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
pub mod config;
|
||||
pub mod logging;
|
||||
|
||||
pub use Macro::*;
|
||||
pub use Common::utils::*;
|
||||
|
||||
1
Build/requirements.txt
Normal file
@ -0,0 +1 @@
|
||||
ffmpeg-python==0.2.0
|
||||
1054
Cargo.lock
generated
@ -1,5 +1,5 @@
|
||||
[workspace]
|
||||
members = ["Management", "ManagementLibrary", "Agent", "AgentLibrary", "Common"]
|
||||
members = ["Management", "ManagementLibrary", "Agent", "AgentLibrary", "Common", "Macro"]
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
|
||||
@ -1,16 +1,22 @@
|
||||
[package]
|
||||
name = "Common"
|
||||
version = "1.0.0"
|
||||
version = "1.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
toml = "0.8.19"
|
||||
glib = "0.20.4"
|
||||
chrono = "0.4.38"
|
||||
colored = "2.1.0"
|
||||
sysinfo = "0.31.4"
|
||||
sysinfo = "0.32.0"
|
||||
gstreamer = "0.23.2"
|
||||
thiserror = "1.0.65"
|
||||
rust-embed = "8.5.0"
|
||||
lazy_static = "1.5.0"
|
||||
tokio = { version = "1.40.0", features = ["full"] }
|
||||
serde = { version = "1.0.210", features = ["derive"] }
|
||||
uuid = { version = "1.10.0", features = ["v4", "fast-rng", "macro-diagnostics", "serde"] }
|
||||
serde_json = "1.0.132"
|
||||
Macro = { path = "../Macro" }
|
||||
serde = { version = "1.0.213", features = ["derive"] }
|
||||
tokio = { version = "1.41.0", features = ["full", "tracing"] }
|
||||
uuid = { version = "1.11.0", features = ["v4", "fast-rng", "macro-diagnostics", "serde"] }
|
||||
|
||||
10
Common/src/connection/packet/file_body_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithData;
|
||||
|
||||
#[derive(DefinePacketWithData)]
|
||||
pub struct FileBodyPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
10
Common/src/connection/packet/file_header_ack_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithoutData;
|
||||
|
||||
#[derive(DefinePacketWithoutData)]
|
||||
pub struct FileHeaderAckPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
10
Common/src/connection/packet/file_header_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithData;
|
||||
|
||||
#[derive(DefinePacketWithData)]
|
||||
pub struct FileHeaderPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
10
Common/src/connection/packet/file_transfer_end_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithoutData;
|
||||
|
||||
#[derive(DefinePacketWithoutData)]
|
||||
pub struct FileTransferEndPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
10
Common/src/connection/packet/file_transfer_result_packet.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use crate::connection::packet::{length_to_byte, Packet, PacketType};
|
||||
use crate::utils::DefinePacketWithData;
|
||||
|
||||
#[derive(DefinePacketWithData)]
|
||||
pub struct FileTransferResultPacket {
|
||||
length: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
data: Vec<u8>,
|
||||
packet_type: PacketType,
|
||||
}
|
||||
@ -1,4 +1,9 @@
|
||||
pub mod base_packet;
|
||||
pub mod file_body_packet;
|
||||
pub mod file_header_ack_packet;
|
||||
pub mod file_header_packet;
|
||||
pub mod file_transfer_end_packet;
|
||||
pub mod file_transfer_result_packet;
|
||||
|
||||
pub trait Packet: Send {
|
||||
fn as_length_byte(&self) -> &[u8];
|
||||
@ -15,26 +20,26 @@ pub trait Packet: Send {
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||
pub enum PacketType {
|
||||
BasePacket,
|
||||
AgentInformationPacket,
|
||||
AgentInformationAcknowledgePacket,
|
||||
AgentInfoPacket,
|
||||
AgentInfoAckPacket,
|
||||
AlivePacket,
|
||||
AliveAcknowledgePacket,
|
||||
AliveAckPacket,
|
||||
ControlPacket,
|
||||
ControlAcknowledgePacket,
|
||||
ControlAckPacket,
|
||||
DataChannelPortPacket,
|
||||
FileBodyPacket,
|
||||
FileHeaderPacket,
|
||||
FileHeaderAcknowledgePacket,
|
||||
FileHeaderAckPacket,
|
||||
FileTransferResultPacket,
|
||||
FileTransferEndPacket,
|
||||
PerformancePacket,
|
||||
PerformanceAcknowledgePacket,
|
||||
ResultPacket,
|
||||
ResultAcknowledgePacket,
|
||||
PerformanceAckPacket,
|
||||
TaskResultPacket,
|
||||
TaskResultAckPacket,
|
||||
StillProcessPacket,
|
||||
StillProcessAcknowledgePacket,
|
||||
StillProcessAckPacket,
|
||||
TaskInfoPacket,
|
||||
TaskInfoAcknowledgePacket,
|
||||
TaskInfoAckPacket,
|
||||
}
|
||||
|
||||
impl PacketType {
|
||||
@ -48,26 +53,26 @@ impl PacketType {
|
||||
byte_array.copy_from_slice(&byte);
|
||||
let id = usize::from_be_bytes(byte_array);
|
||||
match id {
|
||||
1 => PacketType::AgentInformationPacket,
|
||||
2 => PacketType::AgentInformationAcknowledgePacket,
|
||||
1 => PacketType::AgentInfoPacket,
|
||||
2 => PacketType::AgentInfoAckPacket,
|
||||
3 => PacketType::AlivePacket,
|
||||
4 => PacketType::AliveAcknowledgePacket,
|
||||
4 => PacketType::AliveAckPacket,
|
||||
5 => PacketType::ControlPacket,
|
||||
6 => PacketType::ControlAcknowledgePacket,
|
||||
6 => PacketType::ControlAckPacket,
|
||||
7 => PacketType::DataChannelPortPacket,
|
||||
8 => PacketType::FileBodyPacket,
|
||||
9 => PacketType::FileHeaderPacket,
|
||||
10 => PacketType::FileHeaderAcknowledgePacket,
|
||||
10 => PacketType::FileHeaderAckPacket,
|
||||
11 => PacketType::FileTransferResultPacket,
|
||||
12 => PacketType::FileTransferEndPacket,
|
||||
13 => PacketType::PerformancePacket,
|
||||
14 => PacketType::PerformanceAcknowledgePacket,
|
||||
15 => PacketType::ResultPacket,
|
||||
16 => PacketType::ResultAcknowledgePacket,
|
||||
14 => PacketType::PerformanceAckPacket,
|
||||
15 => PacketType::TaskResultPacket,
|
||||
16 => PacketType::TaskResultAckPacket,
|
||||
17 => PacketType::StillProcessPacket,
|
||||
18 => PacketType::StillProcessAcknowledgePacket,
|
||||
18 => PacketType::StillProcessAckPacket,
|
||||
19 => PacketType::TaskInfoPacket,
|
||||
20 => PacketType::TaskInfoAcknowledgePacket,
|
||||
20 => PacketType::TaskInfoAckPacket,
|
||||
_ => PacketType::BasePacket,
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
use crate::connection::packet::base_packet::BasePacket;
|
||||
use crate::connection::packet::Packet;
|
||||
use std::io;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
|
||||
use crate::connection::packet::Packet;
|
||||
use crate::connection::packet::base_packet::BasePacket;
|
||||
use tokio::net::TcpStream;
|
||||
|
||||
pub struct SocketStream {
|
||||
read_half: ReadHalf,
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
use sysinfo::System;
|
||||
use tokio::time::sleep;
|
||||
use std::process::Command;
|
||||
use crate::management::utils::agent_information::AgentInformation;
|
||||
use crate::management::utils::performance::Performance;
|
||||
use crate::utils::log_entry::system::SystemEntry;
|
||||
use crate::utils::logging::*;
|
||||
use lazy_static::lazy_static;
|
||||
use std::process::Command;
|
||||
use sysinfo::System;
|
||||
use tokio::process::Command as AsyncCommand;
|
||||
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
use crate::utils::logging::*;
|
||||
use crate::management::utils::performance::Performance;
|
||||
use crate::management::utils::agent_information::AgentInformation;
|
||||
use tokio::time::sleep;
|
||||
|
||||
lazy_static! {
|
||||
static ref MONITOR: RwLock<Monitor> = RwLock::new(Monitor::new());
|
||||
@ -39,7 +40,7 @@ impl Monitor {
|
||||
tokio::spawn(async {
|
||||
Self::update_performance().await;
|
||||
});
|
||||
logging_console!(information_entry!("Monitor", "Online now"));
|
||||
logging_console!(information_entry!(SystemEntry::Online));
|
||||
}
|
||||
|
||||
pub async fn terminate() {
|
||||
@ -48,17 +49,31 @@ impl Monitor {
|
||||
|
||||
fn system_info() -> AgentInformation {
|
||||
let sys = System::new_all();
|
||||
let host_name = System::host_name().expect("Fail to get system information.");
|
||||
let os_name = if cfg!(target_os = "windows") {
|
||||
let long_os_version = System::long_os_version().expect("Fail to get system information.");
|
||||
let kernel_version = System::kernel_version().expect("Fail to get system information.");
|
||||
format!("{} build {}", long_os_version, kernel_version)
|
||||
} else if cfg!(target_os = "linux") {
|
||||
let long_os_version = System::long_os_version().expect("Fail to get system information.");
|
||||
let kernel_version = System::kernel_version().expect("Fail to get system information.");
|
||||
format!("{} {}", long_os_version, kernel_version)
|
||||
} else {
|
||||
System::long_os_version().expect("Fail to get system information.")
|
||||
};
|
||||
let cpu = sys.cpus().get(0).map(|cpu| cpu.brand())
|
||||
.expect("Monitor: Fail to get system information.")
|
||||
.expect("Fail to get system information.")
|
||||
.to_string();
|
||||
let gpu = Self::get_gpu_name().expect("Monitor: Fail to get system information.");
|
||||
let vram = Self::get_vram_total().expect("Monitor: Fail to get system information.") as f64;
|
||||
let cores = sys.physical_core_count().expect("Fail to get system information.");
|
||||
let ram = sys.total_memory() as f64;
|
||||
let gpu = Self::get_gpu_name().expect("Fail to get system information.");
|
||||
let vram = Self::get_vram_total().expect("Fail to get system information.") as f64;
|
||||
AgentInformation {
|
||||
host_name: System::host_name().expect("Monitor: Fail to get system information."),
|
||||
system_name: System::name().expect("Monitor: Fail to get system information."),
|
||||
host_name,
|
||||
os_name,
|
||||
cpu,
|
||||
cores: sys.physical_core_count().expect("Monitor: Fail to get system information."),
|
||||
ram: sys.total_memory() as f64,
|
||||
cores,
|
||||
ram,
|
||||
gpu,
|
||||
vram,
|
||||
}
|
||||
@ -69,7 +84,7 @@ impl Monitor {
|
||||
.arg("--query-gpu=name")
|
||||
.arg("--format=csv,noheader")
|
||||
.output()
|
||||
.map_err(|_| "Monitor: Fail to get gpu information.".to_string())?;
|
||||
.map_err(|_| "Fail to get gpu information.".to_string())?;
|
||||
let gpu_name = String::from_utf8_lossy(&gpu_name.stdout).trim().to_string();
|
||||
Ok(gpu_name)
|
||||
}
|
||||
@ -79,10 +94,10 @@ impl Monitor {
|
||||
.arg("--query-gpu=memory.total")
|
||||
.arg("--format=csv,noheader,nounits")
|
||||
.output()
|
||||
.map_err(|_| "Monitor: Fail to get gpu information.".to_string())?;
|
||||
.map_err(|_| "Fail to get gpu information.".to_string())?;
|
||||
let vram_total = String::from_utf8_lossy(&vram_total.stdout).trim().to_string()
|
||||
.parse::<u64>()
|
||||
.map_err(|_| "Monitor: Fail to parse gpu information.".to_string())?;
|
||||
.map_err(|_| "Fail to parse gpu information.".to_string())?;
|
||||
Ok(vram_total * 1_048_576_u64)
|
||||
}
|
||||
|
||||
@ -92,10 +107,10 @@ impl Monitor {
|
||||
.arg("--format=csv,noheader,nounits")
|
||||
.output()
|
||||
.await
|
||||
.map_err(|_| "Monitor: Fail to get gpu information.".to_string())?;
|
||||
.map_err(|_| "Fail to get gpu information.".to_string())?;
|
||||
let gpu_usage = String::from_utf8_lossy(&gpu_usage.stdout).trim().to_string()
|
||||
.parse::<u64>()
|
||||
.map_err(|_| "Monitor: Fail to parse gpu information.".to_string())?;
|
||||
.map_err(|_| "Fail to parse gpu information.".to_string())?;
|
||||
Ok(gpu_usage)
|
||||
}
|
||||
|
||||
@ -105,10 +120,10 @@ impl Monitor {
|
||||
.arg("--format=csv,noheader,nounits")
|
||||
.output()
|
||||
.await
|
||||
.map_err(|_| "Monitor: Fail to get gpu information.".to_string())?;
|
||||
.map_err(|_| "Fail to get gpu information.".to_string())?;
|
||||
let vram_used = String::from_utf8_lossy(&vram_used.stdout).trim().to_string()
|
||||
.parse::<u64>()
|
||||
.map_err(|_| "Monitor: Fail to parse gpu information.".to_string())?;
|
||||
.map_err(|_| "Fail to parse gpu information.".to_string())?;
|
||||
Ok(vram_used * 1_048_576_u64)
|
||||
}
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
use std::fmt::Display;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::management::utils::format::format_bytes;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct AgentInformation {
|
||||
pub host_name: String,
|
||||
pub system_name: String,
|
||||
pub os_name: String,
|
||||
pub cpu: String,
|
||||
pub cores: usize,
|
||||
pub ram: f64,
|
||||
@ -16,7 +16,7 @@ pub struct AgentInformation {
|
||||
impl Display for AgentInformation {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = format!("Host Name: {}, System Name: {}, CPU Model: {}, Cores: {}, RAM Total: {}, GPU Model: {}, VRAM Total: {}",
|
||||
self.host_name, self.system_name, self.cpu, self.cores, format_bytes(self.ram), self.gpu, format_bytes(self.vram)
|
||||
self.host_name, self.os_name, self.cpu, self.cores, format_bytes(self.ram), self.gpu, format_bytes(self.vram)
|
||||
);
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
use std::cmp::Ordering;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum AgentState {
|
||||
|
||||
@ -1,11 +0,0 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct BoundingBox {
|
||||
pub xmin: u32,
|
||||
pub xmax: u32,
|
||||
pub ymin: u32,
|
||||
pub ymax: u32,
|
||||
pub name: String,
|
||||
pub confidence: f64,
|
||||
}
|
||||
@ -2,14 +2,14 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct FileHeader {
|
||||
pub filename: String,
|
||||
pub file_name: String,
|
||||
pub packet_count: usize,
|
||||
}
|
||||
|
||||
impl FileHeader {
|
||||
pub fn new(filename: String, packet_count: usize) -> Self {
|
||||
pub fn new(file_name: String, packet_count: usize) -> Self {
|
||||
Self {
|
||||
filename,
|
||||
file_name,
|
||||
packet_count,
|
||||
}
|
||||
}
|
||||
|
||||
35
Common/src/management/utils/inference_argument.rs
Normal file
@ -0,0 +1,35 @@
|
||||
use std::fmt::{Display, Formatter};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct InferenceArgument {
|
||||
pub model_type: ModelType,
|
||||
pub detect_mode: DetectMode,
|
||||
#[serde(default, skip_deserializing)]
|
||||
pub cache: bool,
|
||||
pub imgsz: usize,
|
||||
pub batch: usize,
|
||||
pub conf: f32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
|
||||
pub enum ModelType {
|
||||
Ultralytics,
|
||||
YOLOv4,
|
||||
YOLOv7,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
|
||||
pub enum DetectMode {
|
||||
Predict,
|
||||
Track,
|
||||
}
|
||||
|
||||
impl Display for DetectMode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DetectMode::Predict => write!(f, "predict"),
|
||||
DetectMode::Track => write!(f, "track"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,9 @@
|
||||
pub mod agent_information;
|
||||
pub mod bounding_box;
|
||||
pub mod agent_state;
|
||||
pub mod file_header;
|
||||
pub mod file_transfer_result;
|
||||
pub mod format;
|
||||
pub mod model_type;
|
||||
pub mod inference_argument;
|
||||
pub mod performance;
|
||||
pub mod prevent_reenter;
|
||||
pub mod task_info;
|
||||
pub mod task_result;
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
use std::str::FromStr;
|
||||
use std::fmt::Display;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
|
||||
pub enum ModelType {
|
||||
Ultralytics,
|
||||
YOLOv4,
|
||||
YOLOv7,
|
||||
}
|
||||
|
||||
impl FromStr for ModelType {
|
||||
type Err = ();
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"Ultralytics" => Ok(ModelType::Ultralytics),
|
||||
"YOLOv4" => Ok(ModelType::YOLOv4),
|
||||
"YOLOv7" => Ok(ModelType::YOLOv7),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ModelType {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", match self {
|
||||
ModelType::Ultralytics => "Ultralytics",
|
||||
ModelType::YOLOv4 => "YOLOv4",
|
||||
ModelType::YOLOv7 => "YOLOv7",
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
use std::fmt::Display;
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::management::utils::format::format_bytes;
|
||||
use crate::management::utils::agent_information::AgentInformation;
|
||||
use crate::management::utils::format::format_bytes;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Display;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
|
||||
pub struct Performance {
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
pub struct PreventReenter {
|
||||
flag: &'static AtomicBool,
|
||||
}
|
||||
|
||||
impl PreventReenter {
|
||||
pub fn new(flag: &'static AtomicBool) -> Option<Self> {
|
||||
if flag.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed).is_ok() {
|
||||
Some(PreventReenter {
|
||||
flag
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PreventReenter {
|
||||
fn drop(&mut self) {
|
||||
self.flag.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
@ -1,22 +1,22 @@
|
||||
use uuid::Uuid;
|
||||
use crate::management::utils::inference_argument::InferenceArgument;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::management::utils::model_type::ModelType;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct TaskInfo {
|
||||
pub uuid: Uuid,
|
||||
pub model_filename: String,
|
||||
pub image_filename: String,
|
||||
pub model_type: ModelType,
|
||||
pub model_file_name: String,
|
||||
pub media_file_name: String,
|
||||
pub inference_argument: InferenceArgument,
|
||||
}
|
||||
|
||||
impl TaskInfo {
|
||||
pub fn new(uuid: Uuid, model_filename: String, image_filename: String, model_type: ModelType) -> Self {
|
||||
pub fn new(uuid: Uuid, model_file_name: String, image_file_name: String, inference_argument: InferenceArgument) -> Self {
|
||||
Self {
|
||||
uuid,
|
||||
model_filename,
|
||||
image_filename,
|
||||
model_type,
|
||||
model_file_name,
|
||||
media_file_name: image_file_name,
|
||||
inference_argument,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,19 +1,18 @@
|
||||
use serde::{Serialize, Deserialize};
|
||||
use crate::management::utils::bounding_box::BoundingBox;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct TaskResult {
|
||||
pub result: Result<Vec<BoundingBox>, String>,
|
||||
pub status: Result<(), String>,
|
||||
}
|
||||
|
||||
impl TaskResult {
|
||||
pub fn new(result: Result<Vec<BoundingBox>, String>) -> Self {
|
||||
pub fn new(result: Result<(), String>) -> Self {
|
||||
Self {
|
||||
result,
|
||||
status: result,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into(self) -> Result<Vec<BoundingBox>, String> {
|
||||
self.result
|
||||
pub fn into(self) -> Result<(), String> {
|
||||
self.status
|
||||
}
|
||||
}
|
||||
|
||||
24
Common/src/utils/log_entry/gstreamer.rs
Normal file
@ -0,0 +1,24 @@
|
||||
use glib::error::Error as GError;
|
||||
use gstreamer::StateChangeError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum GStreamerEntry {
|
||||
#[error("GStreamer initialization failed: {0}")]
|
||||
InitializeError(GError),
|
||||
#[error("Failed to create GStreamer pipeline: {0}")]
|
||||
CreatePipelineError(GError),
|
||||
#[error("Failed to get GStreamer bus")]
|
||||
GetBusError,
|
||||
#[error("Failed to set pipeline status: {0}")]
|
||||
PipelineSetStateError(StateChangeError),
|
||||
#[error("GStreamer internal error: {0}")]
|
||||
InternalError(GError),
|
||||
}
|
||||
|
||||
impl From<GStreamerEntry> for String {
|
||||
#[inline(always)]
|
||||
fn from(value: GStreamerEntry) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
44
Common/src/utils/log_entry/io.rs
Normal file
@ -0,0 +1,44 @@
|
||||
use serde_json::error::Error as SerdeJsonError;
|
||||
use std::io::Error as IoError;
|
||||
use std::path::Display;
|
||||
use thiserror::Error;
|
||||
use toml::ser::Error as TomlError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum IOEntry<'a> {
|
||||
#[error("Failed to create directory {0}: {1}")]
|
||||
CreateDirectoryError(Display<'a>, IoError),
|
||||
#[error("Failed to create file {0}: {1}")]
|
||||
CreateFileError(Display<'a>, IoError),
|
||||
#[error("Failed to delete directory {0}: {1}")]
|
||||
DeleteDirectoryError(Display<'a>, IoError),
|
||||
#[error("Failed to delete file {0}: {1}")]
|
||||
DeleteFileError(Display<'a>, IoError),
|
||||
#[error("Failed to move directory {0} to {1}: {2}")]
|
||||
MoveDirectoryError(Display<'a>, Display<'a>, IoError),
|
||||
#[error("Failed to move file {0} to {1}: {2}")]
|
||||
MoveFileError(Display<'a>, Display<'a>, IoError),
|
||||
#[error("Failed to read directory {0}: {1}")]
|
||||
ReadDirectoryError(Display<'a>, IoError),
|
||||
#[error("Failed to read file {0}: {1}")]
|
||||
ReadFileError(Display<'a>, IoError),
|
||||
#[error("Failed to write directory {0}: {1}")]
|
||||
WriteDirectoryError(Display<'a>, IoError),
|
||||
#[error("Failed to write file {0}: {1}")]
|
||||
WriteFileError(Display<'a>, IoError),
|
||||
#[error("Failed to get absolute path of file {0}: {1}")]
|
||||
GetAbsolutePathError(Display<'a>, IoError),
|
||||
#[error("Failed to serialize to TOML: {0}")]
|
||||
TomlSerializeError(TomlError),
|
||||
#[error("Failed to serialize data: {0}")]
|
||||
SerdeSerializeError(SerdeJsonError),
|
||||
#[error("Failed to deserialize data: {0}")]
|
||||
SerdeDeserializeError(SerdeJsonError),
|
||||
}
|
||||
|
||||
impl From<IOEntry<'_>> for String {
|
||||
#[inline(always)]
|
||||
fn from(value: IOEntry) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
20
Common/src/utils/log_entry/misc.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum MiscEntry {
|
||||
#[error("Invalid file name")]
|
||||
InvalidFileNameError,
|
||||
#[error("Missing file block")]
|
||||
MissingFileBlockError,
|
||||
#[error("Received invalid packet")]
|
||||
InvalidPacket,
|
||||
#[error("Wrong packet deliver order")]
|
||||
WrongDeliverOrder,
|
||||
}
|
||||
|
||||
impl From<MiscEntry> for String {
|
||||
#[inline(always)]
|
||||
fn from(value: MiscEntry) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
6
Common/src/utils/log_entry/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub mod gstreamer;
|
||||
pub mod io;
|
||||
pub mod misc;
|
||||
pub mod network;
|
||||
pub mod system;
|
||||
pub mod task;
|
||||
37
Common/src/utils/log_entry/network.rs
Normal file
@ -0,0 +1,37 @@
|
||||
use std::io::Error as IOError;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NetworkEntry {
|
||||
#[error("Channel has been closed")]
|
||||
ChannelClosed,
|
||||
#[error("Control channel timeout")]
|
||||
ControlChannelTimeout,
|
||||
#[error("Create data channel success")]
|
||||
CreateDataChannelSuccess,
|
||||
#[error("Create data channel timeout")]
|
||||
CreateDataChannelTimout,
|
||||
#[error("Data channel timeout")]
|
||||
DataChannelTimeout,
|
||||
#[error("Data channel not ready")]
|
||||
DataChannelNotReady,
|
||||
#[error("Failed to bind port: {0}")]
|
||||
BindPortError(IOError),
|
||||
#[error("Failed to establish connection: {0}")]
|
||||
EstablishConnectionError(IOError),
|
||||
#[error("Receive unexpected packet")]
|
||||
UnexpectedPacket,
|
||||
#[error("Agent side disconnect")]
|
||||
AgentDisconnect,
|
||||
#[error("Management side disconnect")]
|
||||
ManagementDisconnect,
|
||||
#[error("Failed to destroy instance")]
|
||||
DestroyInstanceError,
|
||||
}
|
||||
|
||||
impl From<NetworkEntry> for String {
|
||||
#[inline(always)]
|
||||
fn from(value: NetworkEntry) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
53
Common/src/utils/log_entry/system.rs
Normal file
@ -0,0 +1,53 @@
|
||||
use std::io::Error as IoError;
|
||||
use std::net::SocketAddr;
|
||||
use thiserror::Error;
|
||||
use tokio::task::JoinError;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SystemEntry {
|
||||
#[error("Online now")]
|
||||
Online,
|
||||
#[error("Initializing")]
|
||||
Initializing,
|
||||
#[error("Initialization completed")]
|
||||
InitializeComplete,
|
||||
#[error("Termination in process")]
|
||||
Terminating,
|
||||
#[error("Termination completed")]
|
||||
TerminateComplete,
|
||||
#[error("Cleaning up")]
|
||||
Cleaning,
|
||||
#[error("Cleanup completed")]
|
||||
CleanComplete,
|
||||
#[error("Operation cancel")]
|
||||
Cancel,
|
||||
#[error("Invalid configuration")]
|
||||
InvalidConfig,
|
||||
#[error("Configuration not found")]
|
||||
ConfigNotFound,
|
||||
#[error("Web service ready")]
|
||||
WebReady,
|
||||
#[error("Web service panic: {0}")]
|
||||
WebPanic(IoError),
|
||||
#[error("Management {0} is connected")]
|
||||
ManagementConnect(SocketAddr),
|
||||
#[error("Agent {0} is connected")]
|
||||
AgentConnect(SocketAddr),
|
||||
#[error("Agent instance already exists")]
|
||||
AgentExistError,
|
||||
#[error("Agent instance does not exist")]
|
||||
AgentDoesNotExistError,
|
||||
#[error("Port pool has no available port")]
|
||||
NoAvailablePort,
|
||||
#[error("Child process execution error: {0}")]
|
||||
ChildProcessError(String),
|
||||
#[error("Task panic while execution: {0}")]
|
||||
TaskPanickedError(JoinError),
|
||||
}
|
||||
|
||||
impl From<SystemEntry> for String {
|
||||
#[inline(always)]
|
||||
fn from(value: SystemEntry) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
21
Common/src/utils/log_entry/task.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum TaskEntry {
|
||||
#[error("Task {0}, unsupported file type")]
|
||||
UnSupportFileType(Uuid),
|
||||
#[error("Task {0} cannot be assigned to any agent")]
|
||||
TaskAssignError(Uuid),
|
||||
#[error("Task {0} does not exist")]
|
||||
TaskDoesNotExist(Uuid),
|
||||
#[error("Error occur while agent processing task: {0}")]
|
||||
AgentProcessingError(String),
|
||||
}
|
||||
|
||||
impl From<TaskEntry> for String {
|
||||
#[inline(always)]
|
||||
fn from(value: TaskEntry) -> Self {
|
||||
value.to_string()
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,13 @@
|
||||
use chrono::{DateTime, Local};
|
||||
use colored::*;
|
||||
use std::fmt::Display;
|
||||
use chrono::{DateTime, Local};
|
||||
|
||||
pub use crate::{debug_entry, information_entry, notice_entry, warning_entry, error_entry, critical_entry, alert_entry, emergency_entry, logging_console};
|
||||
pub use crate::{alert_entry, critical_entry, debug_entry, emergency_entry, error_entry, information_entry, logging_console, warning_entry};
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum LogLevel {
|
||||
Debug,
|
||||
Information,
|
||||
Notice,
|
||||
Warning,
|
||||
Error,
|
||||
Critical,
|
||||
@ -17,29 +16,27 @@ pub enum LogLevel {
|
||||
}
|
||||
|
||||
impl LogLevel {
|
||||
pub fn to_plain_string(&self) -> String {
|
||||
pub fn to_plain_string(self) -> String {
|
||||
match self {
|
||||
LogLevel::Debug => "Debug ".to_string(),
|
||||
LogLevel::Information => "Information".to_string(),
|
||||
LogLevel::Notice => "Notice ".to_string(),
|
||||
LogLevel::Warning => "Warning ".to_string(),
|
||||
LogLevel::Error => "Error ".to_string(),
|
||||
LogLevel::Critical => "Critical ".to_string(),
|
||||
LogLevel::Alert => "Alert ".to_string(),
|
||||
LogLevel::Emergency => "Emergency ".to_string(),
|
||||
LogLevel::Debug => "[Debug] ".to_string(),
|
||||
LogLevel::Information => "[Information]".to_string(),
|
||||
LogLevel::Warning => "[Warning] ".to_string(),
|
||||
LogLevel::Error => "[Error] ".to_string(),
|
||||
LogLevel::Critical => "[Critical] ".to_string(),
|
||||
LogLevel::Alert => "[Alert] ".to_string(),
|
||||
LogLevel::Emergency => "[Emergency] ".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_colored_string(&self) -> ColoredString {
|
||||
pub fn to_colored_string(self) -> ColoredString {
|
||||
match self {
|
||||
LogLevel::Debug => "Debug ".to_string().bright_black(),
|
||||
LogLevel::Information => "Information".to_string().bright_blue(),
|
||||
LogLevel::Notice => "Notice ".to_string().bright_green(),
|
||||
LogLevel::Warning => "Warning ".to_string().yellow(),
|
||||
LogLevel::Error => "Error ".to_string().bright_red(),
|
||||
LogLevel::Critical => "Critical ".to_string().bright_yellow(),
|
||||
LogLevel::Alert => "Alert ".to_string().red(),
|
||||
LogLevel::Emergency => "Emergency ".to_string().magenta(),
|
||||
LogLevel::Debug => "[Debug] ".to_string().bright_black(),
|
||||
LogLevel::Information => "[Information]".to_string().bright_blue(),
|
||||
LogLevel::Warning => "[Warning] ".to_string().yellow(),
|
||||
LogLevel::Error => "[Error] ".to_string().bright_red(),
|
||||
LogLevel::Critical => "[Critical] ".to_string().bright_yellow(),
|
||||
LogLevel::Alert => "[Alert] ".to_string().red(),
|
||||
LogLevel::Emergency => "[Emergency] ".to_string().magenta(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,123 +70,106 @@ impl LogEntry {
|
||||
}
|
||||
|
||||
impl LogEntry {
|
||||
pub fn to_plain_string(&self) -> String {
|
||||
pub fn to_plain_string(self) -> String {
|
||||
let level = self.level.to_plain_string();
|
||||
let timestramp = self.timestamp.format("%Y/%m/%d %H:%M:%S").to_string();
|
||||
let timestamp = self.timestamp.format("%Y/%m/%d %H:%M:%S").to_string();
|
||||
let position = self.position.clone();
|
||||
let message = self.message.clone();
|
||||
let str = if self.debug_info.is_empty() {
|
||||
format!("[{}] {} {}: {}", level, timestramp, position, message)
|
||||
format!("{} {} {}: {}", level, timestamp, position, message)
|
||||
} else {
|
||||
let debug_info = self.debug_info.bright_black();
|
||||
format!("[{}] {} {}: {}\n{}", level, timestramp, position, message, debug_info)
|
||||
format!("{} {} {}: {}\n{}", level, timestamp, position, message, debug_info)
|
||||
};
|
||||
str
|
||||
}
|
||||
|
||||
pub fn to_colored_string(&self) -> String {
|
||||
pub fn to_colored_string(self) -> String {
|
||||
let level = self.level.to_colored_string();
|
||||
let timestramp = self.timestamp.format("%Y/%m/%d %H:%M:%S").to_string();
|
||||
let timestamp = self.timestamp.format("%Y/%m/%d %H:%M:%S").to_string();
|
||||
let position = self.position.cyan();
|
||||
let message = self.message.white();
|
||||
let message = self.message;
|
||||
let str = if self.debug_info.is_empty() {
|
||||
format!("[{}] {} {}: {}", level, timestramp, position, message)
|
||||
format!("{} {} {}: {}", level, timestamp, position, message)
|
||||
} else {
|
||||
let debug_info = self.debug_info.bright_black();
|
||||
format!("[{}] {} {}: {}\n{}", level, timestramp, position, message, debug_info)
|
||||
format!("{} {} {}: {}\n{}", level, timestamp, position, message, debug_info)
|
||||
};
|
||||
str
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for LogEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let str = self.to_plain_string();
|
||||
write!(f, "{}", str)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn logging_console(log_entry: LogEntry) {
|
||||
println!("{}", log_entry.to_colored_string());
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Debug, $position, $message, "")
|
||||
($message:expr) => {
|
||||
LogEntry::new(LogLevel::Debug, format!("{}:{}", file!(), line!()), $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Debug, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
($message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Debug, format!("{}:{}", file!(), line!()), $message, $debug_info)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! information_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Information, $position, $message, "")
|
||||
($message:expr) => {
|
||||
LogEntry::new(LogLevel::Information, format!("{}:{}", file!(), line!()), $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Information, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! notice_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Notice, $position, $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Notice, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
($message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Information, format!("{}:{}", file!(), line!()), $message, $debug_info)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! warning_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Warning, $position, $message, "")
|
||||
($message:expr) => {
|
||||
LogEntry::new(LogLevel::Warning, format!("{}:{}", file!(), line!()), $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Warning, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
($message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Warning, format!("{}:{}", file!(), line!()), $message, $debug_info)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! error_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Error, $position, $message, "")
|
||||
($message:expr) => {
|
||||
LogEntry::new(LogLevel::Error, format!("{}:{}", file!(), line!()), $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Error, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
($message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Error, format!("{}:{}", file!(), line!()), $message, $debug_info)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! critical_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Critical, $position, $message, "")
|
||||
($message:expr) => {
|
||||
LogEntry::new(LogLevel::Critical, format!("{}:{}", file!(), line!()), $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Critical, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
($message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Critical, format!("{}:{}", file!(), line!()), $message, $debug_info)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! alert_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Alert, $position, $message, "")
|
||||
($message:expr) => {
|
||||
LogEntry::new(LogLevel::Alert, format!("{}:{}", file!(), line!()), $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Alert, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
($message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Alert, format!("{}:{}", file!(), line!()), $message, $debug_info)
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! emergency_entry {
|
||||
($position:expr, $message:expr) => {
|
||||
LogEntry::new(LogLevel::Emergency, $position, $message, "")
|
||||
($message:expr) => {
|
||||
LogEntry::new(LogLevel::Emergency, format!("{}:{}", file!(), line!()), $message, "")
|
||||
};
|
||||
($position:expr, $message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Emergency, $position, $message, format!("{}:{} {}", file!(), line!(), $debug_info))
|
||||
($message:expr, $debug_info:expr) => {
|
||||
LogEntry::new(LogLevel::Emergency, format!("{}:{}", file!(), line!()), $message, $debug_info)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
pub mod log_entry;
|
||||
pub mod logging;
|
||||
pub mod static_files;
|
||||
|
||||
use tokio::sync::mpsc;
|
||||
use crate::connection::packet::base_packet::BasePacket;
|
||||
use tokio::sync::mpsc;
|
||||
pub use Macro::*;
|
||||
|
||||
#[inline(always)]
|
||||
pub async fn clear_unbounded_channel(rx: &mut mpsc::UnboundedReceiver<BasePacket>) {
|
||||
|
||||
@ -1,108 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Config</title>
|
||||
<link rel="icon" href="/misc/config.ico" type="image/x-icon"/>
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
<script src="/javascript/config.js"></script>
|
||||
<script type="text/javascript">window.onload = loadCurrentConfig;</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h2>Config</h2>
|
||||
<form id="config-form" class="mt-4">
|
||||
<div class="form-group">
|
||||
<label for="internal_timestamp">Internal Timestamp (ms):</label>
|
||||
<input type="number" class="form-control" id="internal_timestamp" name="internal_timestamp" min="1" max="60000" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="agent_listen_port">Agent Listen Port :</label>
|
||||
<input type="number" class="form-control" id="agent_listen_port" name="agent_listen_port" min="1" max="65535" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="http_server_bind_port">HTTP Server Bind Port :</label>
|
||||
<input type="number" class="form-control" id="http_server_bind_port" name="http_server_bind_port" min="1" max="65535" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="dedicated_port_range_start">Dedicated Port Range :</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<input type="number" class="form-control" id="dedicated_port_range_start" name="dedicated_port_range_start" min="1" max="65535" required>
|
||||
<span class="mx-2">~</span>
|
||||
<label for="dedicated_port_range_end"></label>
|
||||
<input type="number" class="form-control" id="dedicated_port_range_end" name="dedicated_port_range_end" min="1" max="65535" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="refresh_interval">Refresh Interval (s):</label>
|
||||
<input type="number" class="form-control" id="refresh_interval" name="refresh_interval" min="1" max="3600" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="polling_interval">Polling Interval (ms):</label>
|
||||
<input type="number" class="form-control" id="polling_interval" name="polling_interval" min="1" max="60000" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="bind_retry_duration">Bind Retry Duration (s):</label>
|
||||
<input type="number" class="form-control" id="bind_retry_duration" name="bind_retry_duration" min="1" max="3600" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="agent_idle_duration">Agent Idle Duration (s):</label>
|
||||
<input type="number" class="form-control" id="agent_idle_duration" name="agent_idle_duration" min="1" max="3600" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="control_channel_timeout">Control Channel timeout (s):</label>
|
||||
<input type="number" class="form-control" id="control_channel_timeout" name="control_channel_timeout" min="1" max="3600" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="data_channel_timeout">Data Channel timeout (s):</label>
|
||||
<input type="number" class="form-control" id="data_channel_timeout" name="data_channel_timeout" min="1" max="3600" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="file_transfer_timeout">File Transfer timeout (s):</label>
|
||||
<input type="number" class="form-control" id="file_transfer_timeout" name="file_transfer_timeout" min="1" max="3600" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font_path">Font Path :</label>
|
||||
<input type="text" class="form-control" id="font_path" name="font_path" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="font_size">Font Size (pt):</label>
|
||||
<input type="number" class="form-control" id="font_size" name="font_size" min="1" step="0.1" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="border_width">Border Width (px):</label>
|
||||
<input type="number" class="form-control" id="border_width" name="border_width" min="1" required>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Border Color:</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="mx-2" for="border_color_r">R</label>
|
||||
<input type="number" class="form-control mx-1" id="border_color_r" min="0" max="255" required>
|
||||
<label class="mx-2" for="border_color_g">G</label>
|
||||
<input type="number" class="form-control mx-1" id="border_color_g" min="0" max="255" required>
|
||||
<label class="mx-2" for="border_color_b">B</label>
|
||||
<input type="number" class="form-control mx-1" id="border_color_b" min="0" max="255" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Text Color:</label>
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="mx-2" for="text_color_r">R</label>
|
||||
<input type="number" class="form-control mx-1" id="text_color_r" min="0" max="255" required>
|
||||
<label class="mx-2" for="text_color_g">G</label>
|
||||
<input type="number" class="form-control mx-1" id="text_color_g" min="0" max="255" required>
|
||||
<label class="mx-2" for="text_color_b">B</label>
|
||||
<input type="number" class="form-control mx-1" id="text_color_b" min="0" max="255" required>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" onclick="submitForm()">Submit</button>
|
||||
</form>
|
||||
<p class="mt-4 text-muted">Some settings will take effect after a restart system.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,119 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
||||
<title>Inference</title>
|
||||
<link href="/misc/inference.ico" rel="icon" type="image/x-icon"/>
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
<script src="/javascript/inference.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<form id="uploadForm">
|
||||
<h1>Inference</h1>
|
||||
<p>Select the format of the model you want to upload:</p>
|
||||
<div class="form-group">
|
||||
<label>Automatic identification</label>
|
||||
<div class="col-2 form-check">
|
||||
<div class="row">
|
||||
<input class="form-check-input" id="defaultRadio" name="modelType"
|
||||
type="radio" value="Ultralytics" checked>
|
||||
<label class="form-check-label" for="defaultRadio">Default</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>YOLO Model</label>
|
||||
<div class="row">
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloV3Radio" name="modelType"
|
||||
type="radio" value="Ultralytics">
|
||||
<label class="form-check-label" for="yoloV3Radio">YOLOv3</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloV4Radio" name="modelType"
|
||||
type="radio" value="YOLOv4">
|
||||
<label class="form-check-label" for="yoloV4Radio">YOLOv4</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloV5Radio" name="modelType"
|
||||
type="radio" value="Ultralytics">
|
||||
<label class="form-check-label" for="yoloV5Radio">YOLOv5</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloV6Radio" name="modelType"
|
||||
type="radio" value="Ultralytics">
|
||||
<label class="form-check-label" for="yoloV6Radio">YOLOv6</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloV7Radio" name="modelType"
|
||||
type="radio" value="YOLOv7">
|
||||
<label class="form-check-label" for="yoloV7Radio">YOLOv7</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloV8Radio" name="modelType"
|
||||
type="radio" value="Ultralytics">
|
||||
<label class="form-check-label" for="yoloV8Radio">YOLOv8</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloV9Radio" name="modelType"
|
||||
type="radio" value="Ultralytics">
|
||||
<label class="form-check-label" for="yoloV9Radio">YOLOv9</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloNASRadio" name="modelType"
|
||||
type="radio" value="Ultralytics">
|
||||
<label class="form-check-label" for="yoloNASRadio">YOLO NAS</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="yoloWorldRadio" name="modelType"
|
||||
type="radio" value="Ultralytics">
|
||||
<label class="form-check-label" for="yoloWorldRadio">YOLO World</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>SAM Model</label>
|
||||
<div class="row">
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="samRadio" name="modelType"
|
||||
type="radio" value="SAM">
|
||||
<label class="form-check-label" for="samRadio">SAM</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="mobileSAMRadio" name="modelType"
|
||||
type="radio" value="Mobile SAM">
|
||||
<label class="form-check-label" for="mobileSAMRadio">Mobile SAM</label>
|
||||
</div>
|
||||
<div class="col-2 form-check">
|
||||
<input class="form-check-input" id="fastSAMRadio" name="modelType"
|
||||
type="radio" value="Fast SAM">
|
||||
<label class="form-check-label" for="fastSAMRadio">Fast SAM</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<label for="modelFile">Model File</label>
|
||||
<input class="form-control-file" id="modelFile" name="modelFile" required type="file">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inferenceFile">Inference file</label>
|
||||
<input class="form-control-file" id="inferenceFile" name="inferenceFile" required type="file">
|
||||
</div>
|
||||
<button class="btn btn-primary" onclick="submitForm()" type="button">Upload</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,33 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Log Viewer</title>
|
||||
<link rel="icon" href="/misc/log.ico" type="image/x-icon"/>
|
||||
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
||||
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||
<script src="/javascript/log.js"></script>
|
||||
<script type="text/javascript">window.onload = loadSystemLog;</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<h1 class="text-center">Log Viewer</h1>
|
||||
<div class="row mb-3">
|
||||
<div class="col-auto">
|
||||
<button class="btn btn-primary" onclick="loadSystemLog()">Load System Log</button>
|
||||
</div>
|
||||
<div class="col">
|
||||
<div class="input-group">
|
||||
<input type="text" id="agent-id-input" class="form-control" placeholder="Agent ID">
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-secondary" onclick="loadAgentLog()">Load Agent Log</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="log-container" class="border rounded p-3" style="height: 725px; overflow-y: auto;"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,36 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>WebSocket Test</title>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const ws = new WebSocket('ws://127.0.0.1:8080/monitor/websocket/performance/system');
|
||||
|
||||
ws.onopen = function() {
|
||||
console.log('WebSocket Connection Opened');
|
||||
};
|
||||
|
||||
ws.onmessage = function(event) {
|
||||
console.log('Message from server ', event.data);
|
||||
const messages = document.getElementById('messages');
|
||||
const message = document.createElement('li');
|
||||
message.textContent = event.data;
|
||||
messages.appendChild(message);
|
||||
};
|
||||
|
||||
ws.onerror = function(error) {
|
||||
console.log('WebSocket Error: ' + error);
|
||||
};
|
||||
|
||||
ws.onclose = function(event) {
|
||||
console.log('WebSocket Connection Closed', event);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>WebSocket Client Test</h1>
|
||||
<ul id="messages"></ul>
|
||||
</body>
|
||||
</html>
|
||||
@ -1,90 +0,0 @@
|
||||
async function loadCurrentConfig() {
|
||||
try {
|
||||
let response = await fetch('/config/get');
|
||||
if(response.ok) {
|
||||
let config = await response.json();
|
||||
document.getElementById('internal_timestamp').value = config.internal_timestamp;
|
||||
document.getElementById('agent_listen_port').value = config.agent_listen_port;
|
||||
document.getElementById('http_server_bind_port').value = config.http_server_bind_port;
|
||||
document.getElementById('dedicated_port_range_start').value = config.dedicated_port_range[0];
|
||||
document.getElementById('dedicated_port_range_end').value = config.dedicated_port_range[1];
|
||||
document.getElementById('refresh_interval').value = config.refresh_interval;
|
||||
document.getElementById('polling_interval').value = config.polling_interval;
|
||||
document.getElementById('bind_retry_duration').value = config.bind_retry_duration;
|
||||
document.getElementById('agent_idle_duration').value = config.agent_idle_duration;
|
||||
document.getElementById('control_channel_timeout').value = config.control_channel_timeout;
|
||||
document.getElementById('data_channel_timeout').value = config.data_channel_timeout;
|
||||
document.getElementById('file_transfer_timeout').value = config.file_transfer_timeout;
|
||||
document.getElementById('font_path').value = config.font_path;
|
||||
document.getElementById('font_size').value = config.font_size;
|
||||
document.getElementById('border_width').value = config.border_width;
|
||||
document.getElementById('border_color_r').value = config.border_color[0];
|
||||
document.getElementById('border_color_g').value = config.border_color[1];
|
||||
document.getElementById('border_color_b').value = config.border_color[2];
|
||||
document.getElementById('text_color_r').value = config.text_color[0];
|
||||
document.getElementById('text_color_g').value = config.text_color[1];
|
||||
document.getElementById('text_color_b').value = config.text_color[2];
|
||||
} else {
|
||||
console.error('Failed to fetch current config:', response.statusText);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error:', error);
|
||||
}
|
||||
}
|
||||
|
||||
async function submitForm() {
|
||||
const getRange = (idPrefix) => {
|
||||
return [
|
||||
parseInt(document.getElementById(idPrefix + '_start').value),
|
||||
parseInt(document.getElementById(idPrefix + '_end').value)
|
||||
];
|
||||
}
|
||||
|
||||
const getRGB = (idPrefix) => {
|
||||
return [
|
||||
parseInt(document.getElementById(idPrefix + '_r').value),
|
||||
parseInt(document.getElementById(idPrefix + '_g').value),
|
||||
parseInt(document.getElementById(idPrefix + '_b').value),
|
||||
];
|
||||
};
|
||||
|
||||
let config = {
|
||||
internal_timestamp: parseInt(document.getElementById('internal_timestamp').value),
|
||||
agent_listen_port: parseInt(document.getElementById('agent_listen_port').value),
|
||||
http_server_bind_port: parseInt(document.getElementById('http_server_bind_port').value),
|
||||
dedicated_port_range: getRange('dedicated_port_range'),
|
||||
refresh_interval: parseInt(document.getElementById('refresh_interval').value),
|
||||
polling_interval: parseInt(document.getElementById('polling_interval').value),
|
||||
bind_retry_duration: parseInt(document.getElementById('bind_retry_duration').value),
|
||||
agent_idle_duration: parseInt(document.getElementById('agent_idle_duration').value),
|
||||
control_channel_timeout: parseInt(document.getElementById('control_channel_timeout').value),
|
||||
data_channel_timeout: parseInt(document.getElementById('data_channel_timeout').value),
|
||||
file_transfer_timeout: parseInt(document.getElementById('file_transfer_timeout').value),
|
||||
font_path: document.getElementById('font_path').value,
|
||||
font_size: parseFloat(document.getElementById('font_size').value),
|
||||
border_width: parseInt(document.getElementById('border_width').value),
|
||||
border_color: getRGB('border_color'),
|
||||
text_color: getRGB('text_color')
|
||||
};
|
||||
|
||||
try {
|
||||
let response = await fetch('/config/update', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(config)
|
||||
});
|
||||
let data = await response.text();
|
||||
|
||||
if (response.ok) {
|
||||
alert('Config updated successfully.')
|
||||
await loadCurrentConfig();
|
||||
} else {
|
||||
alert('Failed to update config: ' + data);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error submitting form:', error);
|
||||
alert('Error: ' + error);
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
function submitForm() {
|
||||
const originalFormData = new FormData(document.getElementById('uploadForm'));
|
||||
const filteredFormData = new FormData();
|
||||
const modelTypeValue = document.querySelector('input[name="modelType"]:checked').value;
|
||||
filteredFormData.append('modelType', modelTypeValue);
|
||||
for (let [key, value] of originalFormData.entries()) {
|
||||
if (value && value.name) {
|
||||
filteredFormData.append(key, value, value.name);
|
||||
}
|
||||
}
|
||||
fetch('/inference/save_file', {
|
||||
method: 'POST',
|
||||
body: filteredFormData
|
||||
})
|
||||
.then(response => {
|
||||
if (response.ok) {
|
||||
alert('File uploaded successfully!');
|
||||
} else {
|
||||
response.text().then(errorMessage => {
|
||||
alert('Upload failed: ' + errorMessage);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('An error occurred: ' + error.message);
|
||||
});
|
||||
}
|
||||
@ -1,75 +0,0 @@
|
||||
let lastLogType = 'system';
|
||||
let lastUpdate = new Date();
|
||||
|
||||
function formatDate(date) {
|
||||
function pad(number) {
|
||||
if (number < 10)
|
||||
return '0' + number;
|
||||
return number;
|
||||
}
|
||||
return date.getFullYear() + '-' + pad(date.getMonth() + 1) + '-' + pad(date.getDate()) + '-' + pad(date.getHours()) + '-' + pad(date.getMinutes()) + '-' + pad(date.getSeconds());
|
||||
}
|
||||
|
||||
function loadSystemLog() {
|
||||
lastLogType = 'system';
|
||||
lastUpdate = new Date();
|
||||
fetch('/log/system_log')
|
||||
.then(response => {
|
||||
if (!response.ok)
|
||||
throw new Error('Network response was not ok.');
|
||||
return response.text();
|
||||
})
|
||||
.then(data => {
|
||||
document.getElementById('log-container').innerHTML = data;
|
||||
})
|
||||
.catch(_ => {
|
||||
document.getElementById('log-container').textContent = 'Error loading system log.';
|
||||
});
|
||||
}
|
||||
|
||||
function loadAgentLog() {
|
||||
const agentId = document.getElementById('agent-id-input').value;
|
||||
if (!agentId) {
|
||||
alert("Please enter a Agent ID.");
|
||||
return;
|
||||
}
|
||||
lastLogType = agentId;
|
||||
lastUpdate = new Date();
|
||||
fetch(`/log/${agentId}`)
|
||||
.then(response => {
|
||||
if (!response.ok)
|
||||
throw new Error('Network response was not ok.');
|
||||
return response.text();
|
||||
})
|
||||
.then(data => {
|
||||
document.getElementById('log-container').innerHTML = data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Fetch error:', error);
|
||||
document.getElementById('log-container').textContent = `Error loading agent ${agentId} log.`;
|
||||
});
|
||||
}
|
||||
|
||||
function updateLog() {
|
||||
const since = formatDate(lastUpdate);
|
||||
let updatePath;
|
||||
if (lastLogType === 'system')
|
||||
updatePath = `/log/system_log/since/${since}`;
|
||||
else
|
||||
updatePath = `/log/${lastLogType}/since/${since}`;
|
||||
fetch(updatePath)
|
||||
.then(response => {
|
||||
if (!response.ok)
|
||||
throw new Error('Network response was not ok.');
|
||||
return response.text();
|
||||
})
|
||||
.then(data => {
|
||||
if (data) {
|
||||
document.getElementById('log-container').innerHTML += data;
|
||||
lastUpdate = new Date();
|
||||
}
|
||||
})
|
||||
.catch(error => console.error('Fetch error:', error));
|
||||
}
|
||||
|
||||
setInterval(updateLog, 10000);
|
||||
|
Before Width: | Height: | Size: 5.3 KiB |
|
Before Width: | Height: | Size: 5.3 KiB |
@ -1,57 +0,0 @@
|
||||
import sys
|
||||
import json
|
||||
from ultralytics import YOLO
|
||||
|
||||
class BoundingBox:
|
||||
def __init__(self, box: list, name: str, confidence: float):
|
||||
self.xmin, self.ymin, self.xmax, self.ymax = box
|
||||
self.xmin = int(self.xmin)
|
||||
self.xmax = int(self.xmax)
|
||||
self.ymin = int(self.ymin)
|
||||
self.ymax = int(self.ymax)
|
||||
self.name = name
|
||||
self.confidence = confidence
|
||||
|
||||
def to_dict(self):
|
||||
return {
|
||||
"xmin": self.xmin,
|
||||
"xmax": self.xmax,
|
||||
"ymin": self.ymin,
|
||||
"ymax": self.ymax,
|
||||
"name": self.name,
|
||||
"confidence": self.confidence
|
||||
}
|
||||
|
||||
def panic(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 3:
|
||||
panic("Unexpected argument.")
|
||||
model_path: str = sys.argv[1]
|
||||
media_path: str = sys.argv[2]
|
||||
try:
|
||||
model: YOLO = YOLO(model_path)
|
||||
try:
|
||||
result: list = model.predict(media_path, verbose=False)
|
||||
bounding_boxs: list = []
|
||||
boxes: list = result[0].boxes.xyxy.tolist()
|
||||
names: list = result[0].names
|
||||
classes: list = result[0].boxes.cls.tolist()
|
||||
confidences: list = result[0].boxes.conf.tolist()
|
||||
for box, cls, conf in zip(boxes, classes, confidences):
|
||||
confidence: float = conf
|
||||
name: str = names[int(cls)]
|
||||
bounding_boxs.append(BoundingBox(box, name, confidence).to_dict())
|
||||
print(json.dumps(bounding_boxs))
|
||||
except AttributeError as err:
|
||||
panic("Unable to inference media.")
|
||||
except IndexError as err:
|
||||
panic("Unknown error.")
|
||||
except FileNotFoundError as err:
|
||||
panic("Model does not exist.")
|
||||
except ValueError as err:
|
||||
panic("Unable to parse model file.")
|
||||
except ImportError as err:
|
||||
panic("Model file is not support.")
|
||||
91
Common/static/script/ultralytics/picture_inference.py
Normal file
@ -0,0 +1,91 @@
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from ultralytics.models.yolo.detect import DetectionPredictor
|
||||
from ultralytics.utils import LOGGER
|
||||
from ultralytics.utils import callbacks
|
||||
|
||||
|
||||
class PictureInference:
|
||||
def __init__(self, model_path: Path, picture_path: Path, save_path: Path, **kwargs):
|
||||
self.model_path = model_path
|
||||
self.picture_path = picture_path
|
||||
self.save_path = save_path
|
||||
self.args = kwargs
|
||||
if not kwargs.get("verbose", False):
|
||||
LOGGER.setLevel(logging.NOTSET)
|
||||
|
||||
def yolo_predict(self):
|
||||
"""
|
||||
Perform YOLO prediction on the input picture and save the results.
|
||||
"""
|
||||
try:
|
||||
default_args = {
|
||||
'conf': 0.25,
|
||||
'imgsz': 640,
|
||||
'mode': 'predict',
|
||||
'model': self.model_path,
|
||||
'save': True,
|
||||
'task': 'detect',
|
||||
}
|
||||
args = {**default_args, **self.args}
|
||||
callback = callbacks.get_default_callbacks()
|
||||
predictor = DetectionPredictor(overrides=args, _callbacks=callback)
|
||||
predictor.setup_model(model=self.model_path)
|
||||
predictor.save_dir = self.save_path
|
||||
predictor.predict_cli(source=self.picture_path)
|
||||
except Exception as e:
|
||||
self.panic(f"Error during YOLO prediction: {e}")
|
||||
|
||||
@staticmethod
|
||||
def panic(*args, **kwargs):
|
||||
"""
|
||||
Print an error message to stderr and exit the program.
|
||||
|
||||
:param args: Arguments to print.
|
||||
:param kwargs: Keyword arguments to print.
|
||||
"""
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
sys.exit(1)
|
||||
|
||||
def inference(self):
|
||||
"""
|
||||
Inference the picture by performing YOLO tracking/prediction and handling codec transformations.
|
||||
"""
|
||||
try:
|
||||
picture_filename = self.picture_path.stem
|
||||
picture_suffix = self.picture_path.suffix.lower().replace(".", "")
|
||||
|
||||
self.yolo_predict()
|
||||
|
||||
predicted_picture = self.save_path / f"{picture_filename}.{picture_suffix}"
|
||||
if not predicted_picture.exists():
|
||||
self.panic("Unexpected error: Predicted picture does not exist.")
|
||||
except Exception as e:
|
||||
self.panic(f"An unexpected error occurred: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description='picture Inference Script',
|
||||
usage='%(prog)s mode model picture save imgsz conf verbose',
|
||||
formatter_class=argparse.RawTextHelpFormatter)
|
||||
parser.add_argument('model_path', type=Path, help='Path to the model')
|
||||
parser.add_argument('picture_path', type=Path, help='Path to the input picture')
|
||||
parser.add_argument('save_path', type=Path, help='Path to save the output')
|
||||
parser.add_argument('imgsz', type=int, help='Image size for processing')
|
||||
parser.add_argument('conf', type=float, help='Confidence threshold')
|
||||
parser.add_argument('--verbose', action='store_true', help='Enable verbose logging')
|
||||
|
||||
args = parser.parse_args()
|
||||
model_path = args.model_path
|
||||
picture_path = args.picture_path
|
||||
save_path = args.save_path
|
||||
other_args = {'imgsz': args.imgsz, 'conf': args.conf, 'verbose': args.verbose}
|
||||
picture_inference = PictureInference(model_path, picture_path, save_path, **other_args)
|
||||
picture_inference.inference()
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
189
Common/static/script/ultralytics/video_inference.py
Normal file
@ -0,0 +1,189 @@
|
||||
import argparse
|
||||
import logging
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import ffmpeg
|
||||
from ultralytics.models.yolo.detect import DetectionPredictor
|
||||
from ultralytics.trackers import register_tracker
|
||||
from ultralytics.utils import LOGGER
|
||||
from ultralytics.utils import callbacks, MACOS, WINDOWS
|
||||
|
||||
|
||||
class VideoInference:
|
||||
def __init__(self, mode: str, model_path: Path, video_path: Path, save_path: Path, **kwargs):
|
||||
self.mode = mode
|
||||
self.model_path = model_path
|
||||
self.video_path = video_path
|
||||
self.save_path = save_path
|
||||
self.args = kwargs
|
||||
if not kwargs.get("verbose", False):
|
||||
LOGGER.setLevel(logging.NOTSET)
|
||||
|
||||
def get_codec_name(self, video_path: Path) -> str:
|
||||
"""
|
||||
Retrieve the codec name of a video using ffmpeg.
|
||||
|
||||
:param video_path: Path to the video file.
|
||||
:return: Codec name as a string.
|
||||
"""
|
||||
try:
|
||||
probe = ffmpeg.probe(str(video_path))
|
||||
streams = [stream for stream in probe['streams'] if stream['codec_type'] == 'video']
|
||||
if not streams:
|
||||
self.panic(f"No video streams found in {video_path}")
|
||||
codec_name = streams[0]['codec_name']
|
||||
return codec_name
|
||||
except Exception as e:
|
||||
self.panic(f"Error getting codec name for {video_path}: {e}")
|
||||
|
||||
def yolo_predict(self):
|
||||
"""
|
||||
Perform YOLO prediction on the input video and save the results.
|
||||
"""
|
||||
try:
|
||||
default_args = {
|
||||
'batch': 16,
|
||||
'conf': 0.25,
|
||||
'imgsz': 640,
|
||||
'mode': 'predict',
|
||||
'model': self.model_path,
|
||||
'save': True,
|
||||
'task': 'detect',
|
||||
}
|
||||
args = {**default_args, **self.args}
|
||||
callback = callbacks.get_default_callbacks()
|
||||
predictor = DetectionPredictor(overrides=args, _callbacks=callback)
|
||||
predictor.setup_model(model=self.model_path)
|
||||
predictor.save_dir = self.save_path
|
||||
predictor.predict_cli(source=self.video_path)
|
||||
except Exception as e:
|
||||
self.panic(f"Error during YOLO prediction: {e}")
|
||||
|
||||
def yolo_track(self):
|
||||
"""
|
||||
Perform YOLO tracking on the input video and save the results.
|
||||
"""
|
||||
try:
|
||||
default_args = {
|
||||
'batch': 1,
|
||||
'conf': 0.1,
|
||||
'imgsz': 640,
|
||||
'mode': 'track',
|
||||
'model': self.model_path,
|
||||
'save': True,
|
||||
'task': 'detect',
|
||||
}
|
||||
args = {**default_args, **self.args}
|
||||
callback = callbacks.get_default_callbacks()
|
||||
predictor = DetectionPredictor(overrides=args, _callbacks=callback)
|
||||
register_tracker(predictor, False)
|
||||
predictor.setup_model(model=self.model_path)
|
||||
predictor.save_dir = self.save_path
|
||||
predictor.predict_cli(source=self.video_path)
|
||||
except Exception as e:
|
||||
self.panic(f"Error during YOLO tracking: {e}")
|
||||
|
||||
@staticmethod
|
||||
def platform_specific_format() -> str:
|
||||
"""
|
||||
Determine the platform-specific video format.
|
||||
|
||||
:return: File extension as a string.
|
||||
"""
|
||||
return "mp4" if MACOS else "avi" if WINDOWS else "avi"
|
||||
|
||||
def transform(self, input_video: Path, temp_video: Path, video_codec: str):
|
||||
"""
|
||||
Convert a video to the target format using ffmpeg-python.
|
||||
|
||||
:param input_video: Path to the input video file to be converted.
|
||||
:param temp_video: Path to save the temporary converted video file.
|
||||
:param video_codec: Target video codec name.
|
||||
"""
|
||||
try:
|
||||
verbose: bool = not self.args.get("verbose", False)
|
||||
(
|
||||
ffmpeg
|
||||
.input(str(input_video))
|
||||
.output(str(temp_video), vcodec=video_codec, **{'strict': '-2'})
|
||||
.overwrite_output()
|
||||
.run(quiet=verbose)
|
||||
)
|
||||
except ffmpeg.Error as e:
|
||||
self.panic(f"Error transforming video {input_video} to {temp_video}: {e}")
|
||||
|
||||
@staticmethod
|
||||
def panic(*args, **kwargs):
|
||||
"""
|
||||
Print an error message to stderr and exit the program.
|
||||
|
||||
:param args: Arguments to print.
|
||||
:param kwargs: Keyword arguments to print.
|
||||
"""
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
sys.exit(1)
|
||||
|
||||
def inference(self):
|
||||
"""
|
||||
Inference the video by performing YOLO tracking/prediction and handling codec transformations.
|
||||
"""
|
||||
try:
|
||||
video_filename = self.video_path.stem
|
||||
video_suffix = self.video_path.suffix.lower().replace(".", "")
|
||||
video_codec = self.get_codec_name(self.video_path)
|
||||
|
||||
if self.mode == "predict":
|
||||
self.yolo_predict()
|
||||
elif mode == "track":
|
||||
self.yolo_track()
|
||||
else:
|
||||
self.panic("Unexpected error: Invalid detect mode.")
|
||||
|
||||
platform_extension = self.platform_specific_format()
|
||||
predicted_video = self.save_path / f"{video_filename}.{platform_extension}"
|
||||
if not predicted_video.exists():
|
||||
self.panic("Unexpected error: Predicted video does not exist.")
|
||||
|
||||
if not MACOS:
|
||||
temp_output_video = self.save_path / f"{video_filename}_temp.{video_suffix}"
|
||||
|
||||
self.transform(predicted_video, temp_output_video, video_codec)
|
||||
|
||||
predicted_video.unlink()
|
||||
final_output_video = self.save_path / f"{video_filename}.{video_suffix}"
|
||||
temp_output_video.rename(final_output_video)
|
||||
else:
|
||||
final_output_video = self.save_path / f"{video_filename}.{video_suffix}"
|
||||
predicted_video.rename(final_output_video)
|
||||
except Exception as e:
|
||||
self.panic(f"An unexpected error occurred: {e}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
parser = argparse.ArgumentParser(description='Video Inference Script',
|
||||
usage='%(prog)s mode model video save imgsz conf batch verbose',
|
||||
formatter_class=argparse.RawTextHelpFormatter)
|
||||
parser.add_argument('mode', type=str, choices=['predict', 'track'],
|
||||
help="Mode of operation: 'predict' or 'track'")
|
||||
parser.add_argument('model_path', type=Path, help='Path to the model')
|
||||
parser.add_argument('video_path', type=Path, help='Path to the input video')
|
||||
parser.add_argument('save_path', type=Path, help='Path to save the output')
|
||||
parser.add_argument('imgsz', type=int, help='Image size for processing')
|
||||
parser.add_argument('conf', type=float, help='Confidence threshold')
|
||||
parser.add_argument('batch', type=int, help='Batch size')
|
||||
parser.add_argument('--verbose', action='store_true', help='Enable verbose logging')
|
||||
|
||||
args = parser.parse_args()
|
||||
mode = args.mode
|
||||
model_path = args.model_path
|
||||
video_path = args.video_path
|
||||
save_path = args.save_path
|
||||
other_args = {'imgsz': args.imgsz, 'conf': args.conf, "batch": args.batch, 'verbose': args.verbose}
|
||||
|
||||
video_inference = VideoInference(mode, model_path, video_path, save_path, **other_args)
|
||||
video_inference.inference()
|
||||
except Exception as e:
|
||||
print(f"An unexpected error occurred: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
13
Common/static/web/asset-manifest.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"files": {
|
||||
"main.css": "/static/css/main.ed8a56b7.css",
|
||||
"main.js": "/static/js/main.01479aeb.js",
|
||||
"index.html": "/index.html",
|
||||
"main.ed8a56b7.css.map": "/static/css/main.ed8a56b7.css.map",
|
||||
"main.01479aeb.js.map": "/static/js/main.01479aeb.js.map"
|
||||
},
|
||||
"entrypoints": [
|
||||
"static/css/main.ed8a56b7.css",
|
||||
"static/js/main.01479aeb.js"
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.5 KiB |
1
Common/static/web/index.html
Normal file
@ -0,0 +1 @@
|
||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="manifest" href="/manifest.json"/><title>VisioGrid</title><script defer="defer" src="/static/js/main.01479aeb.js"></script><link href="/static/css/main.ed8a56b7.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
||||
15
Common/static/web/manifest.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "VisioGrid",
|
||||
"name": "VisioGrid",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
Common/static/web/robots.txt
Normal file
@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
2
Common/static/web/static/css/main.ed8a56b7.css
Normal file
@ -0,0 +1,2 @@
|
||||
body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;margin:0}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}.app{display:flex}.main-content{padding:20px;transition:margin-left .3s ease;width:100%}.main-content.sidebar-open{margin-left:200px}.main-content.sidebar-closed{margin-left:60px}@media (max-width:768px){.main-content.sidebar-closed,.main-content.sidebar-open{margin-left:60px}}.agents-list{padding:20px}.agent-item{background-color:#f9f9f9;border:1px solid #ccc;border-radius:8px;margin-bottom:15px;padding:15px;transition:background-color .3s ease}.agent-item:hover{background-color:#e6e6e6}.agent-link{color:#333;text-decoration:none}.agent-link h2{font-size:20px;margin:0}.agent-link p{margin:5px 0}.dashboard{background-color:#f5f5f5;min-height:80vh;padding:20px}.system-info{background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;margin-bottom:20px;padding:20px}.system-info ul{list-style:none;padding:0}.system-info li{font-size:18px;margin-bottom:10px}.system-info li strong{color:#34495e}.system-info-h1{margin-top:0}.chart-container{display:flex;flex-wrap:wrap;gap:40px;justify-content:center}.chart{background-color:#fff;border-radius:8px;box-shadow:0 2px 4px #0000001a;padding:20px;width:310px}.chart h2{color:#2c3e50;margin-bottom:10px;text-align:center}@media (max-width:768px){.chart-container{align-items:center;flex-direction:column}.chart{width:90%}}.log-container{background-color:#fff;border-top:none;box-sizing:border-box;display:flex;flex-direction:column;margin-top:0;padding:20px;width:100%}.log-search-input{border:1px solid #ccc;border-radius:5px;box-sizing:border-box;font-size:16px;margin-bottom:20px;max-width:300px;padding:10px 15px;width:50%}.log-scroll{background-color:#f9f9f9;border:1px solid #ddd;border-radius:5px;box-sizing:border-box;flex:1 1;overflow:hidden;width:100%}.log-item{align-items:center;border-bottom:1px solid #eee;display:flex;font-family:monospace;min-width:0;overflow:hidden;padding:5px 10px;width:98.5%}.log-content{flex:1 1;min-width:0;overflow-wrap:break-word;white-space:pre-wrap;word-break:break-word}.latest-log{background-color:#e6f7ff}.log-loader{color:#666;padding:20px;text-align:center}.no-logs{color:#555;font-size:16px;text-align:center}.log-item:hover{background-color:#f1f1f1}@media (max-width:600px){.log-search-input{max-width:none;width:100%}}.config-page{background-color:#f9f9f9;min-height:100vh;padding-top:30px}.config-page .container{max-width:800px}.config-page h2{color:#2c3e50;margin-bottom:40px;text-align:center}.config-page .form-group label{color:#34495e;font-weight:700}.config-page .form-control{border-radius:5px}.config-page .btn-primary{background-color:#3498db;border-color:#3498db;border-radius:5px;font-size:18px;padding:10px;width:100%}.config-page .btn-primary:hover{background-color:#2980b9;border-color:#2980b9}.loader-container{align-items:center;display:flex;height:90vh;justify-content:center}.loader{animation:spin 2s linear infinite;border:16px solid #f3f3f3;border-radius:50%;border-top-color:#3498db;height:120px;width:120px}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.sidebar{background-color:#2c3e50;bottom:0;color:#ecf0f1;left:0;overflow:hidden;position:fixed;top:0;transition:width .3s ease;width:180px}.sidebar.closed{background-color:#2c3e50;width:60px}.sidebar-header{align-items:center;background-color:#1a252f;display:flex;height:60px;justify-content:center;padding:0 15px}.sidebar.open .sidebar-header{justify-content:space-between}.sidebar-header h2{font-size:1.5rem;margin:0;padding:0}.toggle-btn{align-items:center;background:#0000;border:none;color:#ecf0f1;cursor:pointer;display:flex;font-size:1.5rem;justify-content:center}.sidebar.closed .toggle-btn{left:50%;margin-left:0;margin-right:0;position:relative;transform:translateX(-50%)}.sidebar-menu{list-style:none;margin:0;padding:0}.sidebar-menu li{padding:15px}.menu-item{align-items:center;color:#ecf0f1;display:flex;text-decoration:none;transition:background .2s}.menu-item:hover{background-color:#34495e}.menu-icon{font-size:1.2rem;margin-right:10px}.sidebar.closed .menu-item{justify-content:center}.sidebar.closed .menu-icon{margin-right:0}.sidebar.closed .menu-item span{display:none}
|
||||
/*# sourceMappingURL=main.ed8a56b7.css.map*/
|
||||
1
Common/static/web/static/css/main.ed8a56b7.css.map
Normal file
103
Common/static/web/static/js/main.01479aeb.js
Normal file
113
Common/static/web/static/js/main.01479aeb.js.LICENSE.txt
Normal file
@ -0,0 +1,113 @@
|
||||
/*!
|
||||
* @kurkle/color v0.3.2
|
||||
* https://github.com/kurkle/color#readme
|
||||
* (c) 2023 Jukka Kurkela
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Chart.js v4.4.5
|
||||
* https://www.chartjs.org
|
||||
* (c) 2024 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-dom.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react-jsx-runtime.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* react.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license React
|
||||
* scheduler.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @mui/styled-engine v6.1.4
|
||||
*
|
||||
* @license MIT
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @remix-run/router v1.20.0
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* React Router DOM v6.27.0
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/**
|
||||
* React Router v6.27.0
|
||||
*
|
||||
* Copyright (c) Remix Software Inc.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE.md file in the root directory of this source tree.
|
||||
*
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
/** @license React v16.13.1
|
||||
* react-is.production.min.js
|
||||
*
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
1
Common/static/web/static/js/main.01479aeb.js.map
Normal file
BIN
GitHub/Agents-1.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
GitHub/Agents-2.png
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
GitHub/Config.png
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
GitHub/Home.png
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
GitHub/Inference.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
GitHub/Task-1.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
GitHub/Task-2.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
14
Macro/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "Macro"
|
||||
version = "1.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
quote = "1.0.37"
|
||||
proc-macro2 = "1.0.89"
|
||||
syn = { version = "2.0.85", features = ["full"] }
|
||||