reworked the Rust extension to create a TCP Client in order to send cot payloads to the connected server

This commit is contained in:
Valmo Trindade
2025-01-29 19:25:18 -03:00
parent 57c69c604c
commit 7bb59ef2ed
3 changed files with 234 additions and 5 deletions

210
src/cot_generator.rs Normal file
View File

@@ -0,0 +1,210 @@
use arma_rs::{FromArma, FromArmaError};
use chrono::{Duration, SecondsFormat, Utc};
use uuid::Uuid;
pub struct CursorOverTime {
pub uuid: Option<String>,
pub r#type: Option<String>,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub point_ce: Option<f32>,
pub point_le: Option<f32>,
pub contact_callsign: String,
pub group_name: Option<String>,
pub group_role: Option<String>,
pub track_course: Option<i32>,
pub track_speed: Option<f32>,
}
impl CursorOverTime {
pub fn convert_to_xml(&self) -> String {
let uuid = match &self.uuid {
Some(uuid) => uuid,
None => &Uuid::new_v4().to_string(),
};
let marker_type = match &self.r#type {
Some(marker_type) => marker_type,
None => &"a-f-G-U-C-I".to_string(),
};
let created_time = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true);
let stale_time =
(Utc::now() + Duration::seconds(360)).to_rfc3339_opts(SecondsFormat::Millis, true);
let mut xml = String::new();
xml.push_str("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
xml.push_str(
format!("<event type=\"{}\" version=\"2.0\" how=\"m-g\" uid=\"{}\" time=\"{}\" start=\"{}\" stale=\"{}\">",
marker_type, uuid, created_time, created_time, stale_time).as_str());
let point_ce = match &self.point_ce {
Some(point_ce) => point_ce,
None => &9999999.0,
};
let point_le = match &self.point_le {
Some(point_le) => point_le,
None => &9999999.0,
};
xml.push_str(
format!(
"<point ce=\"{}\" le=\"{}\" hae=\"{}\" lat=\"{}\" lon=\"{}\" />",
point_ce, point_le, self.point_hae, self.point_lat, self.point_lon
)
.as_str(),
);
xml.push_str("<detail>");
xml.push_str("<takv device=\"Samsung S24\" os=\"Arma 3\" platform=\"ARMATAK\" version=\"0.9.0.0\" />");
xml.push_str(format!("<contact callsign=\"{}\" />", self.contact_callsign).as_str());
xml.push_str(format!("<uid Droid=\"{}\"/>", self.contact_callsign).as_str());
if let (Some(track_course), Some(track_speed)) = (&self.track_course, &self.track_speed) {
xml.push_str(
format!(
"<track course=\"{}\" speed=\"{}\" />",
track_course, track_speed
)
.as_str(),
);
xml.push_str("<status battery=\"89\" />");
}
if let (Some(group_name), Some(group_role)) = (&self.group_name, &self.group_role) {
xml.push_str(
format!(
"<__group name=\"{}\" role=\"{}\" />",
group_name, group_role
)
.as_str(),
);
}
xml.push_str("</detail></event>");
return xml;
}
}
pub struct HumanCoTPayload {
pub uuid: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub contact_callsign: String,
pub group_name: String,
pub group_role: String,
pub track_course: i32,
pub track_speed: f32,
}
impl FromArma for HumanCoTPayload {
fn from_arma(data: String) -> Result<HumanCoTPayload, FromArmaError> {
let (
uuid,
point_lat,
point_lon,
point_hae,
contact_callsign,
group_name,
group_role,
track_course,
track_speed,
) = <(String, f64, f64, f32, String, String, String, i32, f32)>::from_arma(data)?;
Ok(Self {
uuid,
point_lat,
point_lon,
point_hae,
contact_callsign,
group_name,
group_role,
track_course,
track_speed,
})
}
}
impl HumanCoTPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(self.uuid.clone()),
r#type: None,
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: Some(self.group_name.clone()),
group_role: Some(self.group_role.clone()),
track_course: Some(self.track_course),
track_speed: Some(self.track_speed),
}
}
}
pub struct MarkerCoTPayload {
pub uuid: String,
pub r#type: String,
pub point_lat: f64,
pub point_lon: f64,
pub point_hae: f32,
pub contact_callsign: String,
pub track_course: i32,
pub track_speed: f32,
}
impl FromArma for MarkerCoTPayload {
fn from_arma(data: String) -> Result<MarkerCoTPayload, FromArmaError> {
let (
uuid,
r#type,
point_lat,
point_lon,
point_hae,
contact_callsign,
track_course,
track_speed,
) = <(String, String, f64, f64, f32, String, i32, f32)>::from_arma(data)?;
Ok(Self {
uuid,
r#type,
point_lat,
point_lon,
point_hae,
contact_callsign,
track_course,
track_speed,
})
}
}
impl MarkerCoTPayload {
pub fn to_cot(&self) -> CursorOverTime {
CursorOverTime {
uuid: Some(self.uuid.clone()),
r#type: Some(self.r#type.clone()),
point_lat: self.point_lat,
point_lon: self.point_lon,
point_hae: self.point_hae,
point_ce: None,
point_le: None,
contact_callsign: self.contact_callsign.clone(),
group_name: None,
group_role: None,
track_course: Some(self.track_course),
track_speed: Some(self.track_speed),
}
}
}

View File

@@ -6,6 +6,8 @@ use std::sync::{Arc, Mutex};
use std::thread; use std::thread;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use crate::cot_generator::{HumanCoTPayload, MarkerCoTPayload};
pub enum TcpCommand { pub enum TcpCommand {
SendMessage(String), SendMessage(String),
Stop, Stop,
@@ -27,7 +29,6 @@ impl TcpClient {
thread::spawn(move || { thread::spawn(move || {
let mut running = true; let mut running = true;
// TCP connection thread
let tcp_thread = thread::spawn(move || { let tcp_thread = thread::spawn(move || {
match TcpStream::connect(&address) { match TcpStream::connect(&address) {
Ok(stream) => { Ok(stream) => {
@@ -66,11 +67,17 @@ impl TcpClient {
} }
pub fn send_payload(&self, payload: String) { pub fn send_payload(&self, payload: String) {
self.tx.send(TcpCommand::SendMessage(payload)).unwrap(); let tx = self.tx.clone();
thread::spawn(move || {
tx.send(TcpCommand::SendMessage(payload)).unwrap();
});
} }
pub fn stop(&self) { pub fn stop(&self) {
self.tx.send(TcpCommand::Stop).unwrap(); let tx = self.tx.clone();
thread::spawn(move || {
tx.send(TcpCommand::Stop).unwrap();
});
} }
} }
@@ -103,6 +110,16 @@ pub fn send_payload(payload: String) -> &'static str {
"Sending payload to TCP server" "Sending payload to TCP server"
} }
pub fn send_human_cot(cursor_over_time: HumanCoTPayload) -> &'static str {
let payload = cursor_over_time.to_cot().convert_to_xml();
send_payload(payload)
}
pub fn send_marker_cot(cursor_over_time: MarkerCoTPayload) -> &'static str {
let payload = cursor_over_time.to_cot().convert_to_xml();
send_payload(payload)
}
pub fn stop() -> &'static str { pub fn stop() -> &'static str {
if let Some(ref client) = *TCP_CLIENT.lock().unwrap() { if let Some(ref client) = *TCP_CLIENT.lock().unwrap() {
client.stop(); client.stop();

View File

@@ -43,7 +43,9 @@ pub fn init() -> Extension {
"cot_router", "cot_router",
Group::new() Group::new()
.command("start", cot_router::start) .command("start", cot_router::start)
.command("send_cot", cot_router::send_payload) .command("send_payload", cot_router::send_payload)
.command("send_human_cot", cot_router::send_human_cot)
.command("send_marker_cot", cot_router::send_marker_cot)
.command("stop", cot_router::stop) .command("stop", cot_router::stop)
) )
.finish() .finish()