From 9763cb6697d628628ec1b7cfa1dc4c650f8fe7ee Mon Sep 17 00:00:00 2001 From: Valmo Trindade Date: Sun, 30 Nov 2025 10:43:18 -0300 Subject: [PATCH] reoganized command groups on extension call --- .../api/fn_send_digital_pointer_cot.sqf | 2 +- .../main/functions/api/fn_send_enemy_cot.sqf | 2 +- addons/main/functions/api/fn_send_eud_cot.sqf | 2 +- .../main/functions/api/fn_send_marker_cot.sqf | 2 +- .../extract_data/fn_extract_role.sqf | 2 +- .../extract_data/fn_extract_sensor_data.sqf | 4 +- src/cot/draws/circle.rs | 126 +++++++++++++++ src/cot/draws/mod.rs | 1 + src/cot/message.rs | 153 ++++++++++++++++++ src/cot/mod.rs | 2 + src/lib.rs | 35 ++-- src/tcp/cot.rs | 32 ++++ src/tcp/draw.rs | 34 ++++ src/{tcp_socket.rs => tcp/mod.rs} | 24 +-- 14 files changed, 382 insertions(+), 39 deletions(-) create mode 100644 src/cot/draws/circle.rs create mode 100644 src/cot/draws/mod.rs create mode 100644 src/cot/message.rs create mode 100644 src/tcp/cot.rs create mode 100644 src/tcp/draw.rs rename src/{tcp_socket.rs => tcp/mod.rs} (84%) diff --git a/addons/main/functions/api/fn_send_digital_pointer_cot.sqf b/addons/main/functions/api/fn_send_digital_pointer_cot.sqf index e1be95e..f1ab107 100644 --- a/addons/main/functions/api/fn_send_digital_pointer_cot.sqf +++ b/addons/main/functions/api/fn_send_digital_pointer_cot.sqf @@ -10,5 +10,5 @@ if (!isNull _digitalPointer) then { _dpCot = [_link_uid, _contact_callsign, _digitalPointerPosition select 1, _digitalPointerPosition select 2, _digitalPointerPosition select 3]; - "armatak" callExtension ["tcp_socket:send_digital_pointer_cot", [_dpCot]]; + "armatak" callExtension ["tcp_socket:cot:digital_pointer", [_dpCot]]; }; diff --git a/addons/main/functions/api/fn_send_enemy_cot.sqf b/addons/main/functions/api/fn_send_enemy_cot.sqf index fd2f528..bc3a335 100644 --- a/addons/main/functions/api/fn_send_enemy_cot.sqf +++ b/addons/main/functions/api/fn_send_enemy_cot.sqf @@ -12,4 +12,4 @@ _callsign = _unit call armatak_fnc_extract_marker_callsign; _marker_cot = [_uuid, _type, _unit_position select 1, _unit_position select 2, _unit_position select 3, _callsign, _unit_position select 5, _unit_position select 6]; -"armatak" callExtension ["tcp_socket:send_marker_cot", [_marker_cot]]; +"armatak" callExtension ["tcp_socket:cot:marker", [_marker_cot]]; diff --git a/addons/main/functions/api/fn_send_eud_cot.sqf b/addons/main/functions/api/fn_send_eud_cot.sqf index a235757..436679d 100644 --- a/addons/main/functions/api/fn_send_eud_cot.sqf +++ b/addons/main/functions/api/fn_send_eud_cot.sqf @@ -9,4 +9,4 @@ _position = _unit call armatak_client_fnc_extractClientPosition; _uuid = _unit call armatak_fnc_extract_uuid; _eud_cot = [_uuid, _position select 1, _position select 2, _position select 3, _callsign, _group_name, _group_role, _position select 5, _position select 6]; -"armatak" callExtension ["tcp_socket:send_eud_cot", [_eud_cot]]; +"armatak" callExtension ["tcp_socket:cot:eud", [_eud_cot]]; diff --git a/addons/main/functions/api/fn_send_marker_cot.sqf b/addons/main/functions/api/fn_send_marker_cot.sqf index 6d48e03..77ccae7 100644 --- a/addons/main/functions/api/fn_send_marker_cot.sqf +++ b/addons/main/functions/api/fn_send_marker_cot.sqf @@ -10,4 +10,4 @@ _uuid = _unit call armatak_fnc_extract_uuid; _marker_cot = [_uuid, _type, _unit_position select 1, _unit_position select 2, _unit_position select 3, _callsign, _unit_position select 5, _unit_position select 6]; -"armatak" callExtension ["tcp_socket:send_marker_cot", [_marker_cot]]; +"armatak" callExtension ["tcp_socket:cot:marker", [_marker_cot]]; diff --git a/addons/main/functions/extract_data/fn_extract_role.sqf b/addons/main/functions/extract_data/fn_extract_role.sqf index 95dc387..ace9204 100644 --- a/addons/main/functions/extract_data/fn_extract_role.sqf +++ b/addons/main/functions/extract_data/fn_extract_role.sqf @@ -123,4 +123,4 @@ if (!isNil "armatak_attribute_marker_type" or armatak_attribute_marker_type != ' _role = armatak_attribute_marker_type; }; -_role \ No newline at end of file +_role diff --git a/addons/main/functions/extract_data/fn_extract_sensor_data.sqf b/addons/main/functions/extract_data/fn_extract_sensor_data.sqf index 5cfe46d..5b1e047 100644 --- a/addons/main/functions/extract_data/fn_extract_sensor_data.sqf +++ b/addons/main/functions/extract_data/fn_extract_sensor_data.sqf @@ -22,6 +22,6 @@ _target = getSensorTargets (_unit); _marker_cot = [_uuid, _type, _unit_position select 1, _unit_position select 2, _unit_position select 3, _callsign, _unit_position select 5, _unit_position select 6]; - "armatak" callExtension ["tcp_socket:send_marker_cot", [_marker_cot]]; + "armatak" callExtension ["tcp_socket:cot:marker", [_marker_cot]]; }; -} forEach _target; \ No newline at end of file +} forEach _target; diff --git a/src/cot/draws/circle.rs b/src/cot/draws/circle.rs new file mode 100644 index 0000000..ff76dc7 --- /dev/null +++ b/src/cot/draws/circle.rs @@ -0,0 +1,126 @@ +use arma_rs::{FromArma, FromArmaError}; + +pub struct CircleCoTPayload { + pub uuid: String, + pub center_lat: f64, + pub center_lon: f64, + pub center_hae: f32, + pub major: f64, + pub minor: f64, + pub angle: f32, + pub callsign: String, + pub creator_uid: String, + pub creator_callsign: String, +} + +impl FromArma for CircleCoTPayload { + fn from_arma(data: String) -> Result { + let ( + uuid, + center_lat, + center_lon, + center_hae, + major, + minor, + angle, + callsign, + creator_uid, + creator_callsign, + ) = <(String, f64, f64, f32, f64, f64, f32, String, String, String)>::from_arma(data)?; + + Ok(Self { + uuid, + center_lat, + center_lon, + center_hae, + major, + minor, + angle, + callsign, + creator_uid, + creator_callsign, + }) + } +} + +pub struct ShapeCircleCoT { + pub uid: String, + pub lat: f64, + pub lon: f64, + pub hae: f32, + pub major: f64, + pub minor: f64, + pub angle: f32, + pub callsign: String, + pub creator_uid: String, + pub creator_callsign: String, +} + +impl CircleCoTPayload { + pub fn to_cot(&self) -> ShapeCircleCoT { + ShapeCircleCoT { + uid: self.uuid.clone(), + lat: self.center_lat, + lon: self.center_lon, + hae: self.center_hae, + major: self.major, + minor: self.minor, + angle: self.angle, + callsign: self.callsign.clone(), + creator_uid: self.creator_uid.clone(), + creator_callsign: self.creator_callsign.clone(), + } + } +} + +impl ShapeCircleCoT { + pub fn to_xml(&self, now: &str, stale: &str) -> String { + format!( + r#" + + + + + + + + + + <__shapeExtras cpvis="true" editable="true" /> + + + + + + + + + + + +"#, + uid = self.uid, + t = now, + stale = stale, + lat = self.lat, + lon = self.lon, + hae = self.hae, + major = self.major, + minor = self.minor, + angle = self.angle, + callsign = self.callsign, + creator_uid = self.creator_uid, + creator_callsign = self.creator_callsign + ) + } +} diff --git a/src/cot/draws/mod.rs b/src/cot/draws/mod.rs new file mode 100644 index 0000000..44ca87b --- /dev/null +++ b/src/cot/draws/mod.rs @@ -0,0 +1 @@ +pub mod circle; \ No newline at end of file diff --git a/src/cot/message.rs b/src/cot/message.rs new file mode 100644 index 0000000..84b4c84 --- /dev/null +++ b/src/cot/message.rs @@ -0,0 +1,153 @@ +use arma_rs::{FromArma, FromArmaError}; +use chrono::{Utc, Duration, SecondsFormat}; +use uuid::Uuid; + +pub struct MessagePayload { + pub sender_callsign: String, + pub chatroom: String, + pub message_text: String, + pub point_lat: f64, + pub point_lon: f64, + pub point_hae: f32, + pub sender_uid: String, +} + +impl FromArma for MessagePayload { + fn from_arma(data: String) -> Result { + let (sender_callsign, chatroom, message_text, + point_lat, point_lon, point_hae, sender_uid) = + <(String, String, String, f64, f64, f32, String)>::from_arma(data)?; + + Ok(Self { + sender_callsign, + chatroom, + message_text, + point_lat, + point_lon, + point_hae, + sender_uid, + }) + } +} + +pub struct MessageCot { + pub sender_callsign: String, + pub chatroom: String, + pub message_text: String, + pub point_lat: f64, + pub point_lon: f64, + pub point_hae: f32, + pub sender_uid: String, +} + +impl MessageCot { + pub fn from_payload(p: MessagePayload) -> Self { + Self { + sender_callsign: p.sender_callsign, + chatroom: p.chatroom, + message_text: p.message_text, + point_lat: p.point_lat, + point_lon: p.point_lon, + point_hae: p.point_hae, + sender_uid: p.sender_uid, + } + } + + pub fn to_xml(&self) -> String { + let created_time = Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true); + let stale_time = (Utc::now() + Duration::days(1)) + .to_rfc3339_opts(SecondsFormat::Millis, true); + + // MESSAGE ID (random UUID) + let message_uuid = Uuid::new_v4().to_string(); + + // FULL EVENT UID + // format: GeoChat.{sender}.{chatroom}.{uuid} + let event_uid = format!( + "GeoChat.{}.{}.{}", + self.sender_uid, + self.chatroom.replace(" ", "_"), + message_uuid, + ); + + let mut xml = String::new(); + + xml.push_str(""); + + xml.push_str( + format!( + "", + event_uid, created_time, created_time, stale_time + ) + .as_str(), + ); + + xml.push_str( + format!( + "", + self.point_lat, self.point_lon, self.point_hae + ) + .as_str(), + ); + + xml.push_str(""); + + // ========== CHAT OBJECT ========== + + xml.push_str( + format!( + "<__chat parent=\"RootContactGroup\" groupOwner=\"false\" \ + messageId=\"{}\" chatroom=\"{}\" id=\"{}\" senderCallsign=\"{}\">", + message_uuid, + self.chatroom, + self.chatroom, + self.sender_callsign, + ) + .as_str(), + ); + + xml.push_str( + format!( + "", + self.sender_uid, + self.chatroom, + self.chatroom + ) + .as_str(), + ); + + xml.push_str(""); + + // ========== LINK ELEMENT ========== + xml.push_str( + format!( + "", + self.sender_uid + ) + .as_str(), + ); + + // ========== SERVER DEST ========== + // This is optional — you may remove or customize it + xml.push_str( + format!( + "<__serverdestination destinations=\"0.0.0.0:0:tcp:{}\" />", + self.sender_uid + ) + .as_str(), + ); + + // ========== MESSAGE REMARKS ========== + xml.push_str( + format!( + "{}", + self.sender_uid, self.chatroom, created_time, self.message_text + ) + .as_str(), + ); + + xml.push_str(""); + + xml + } +} diff --git a/src/cot/mod.rs b/src/cot/mod.rs index 1ae8b47..6c8ca30 100644 --- a/src/cot/mod.rs +++ b/src/cot/mod.rs @@ -1,5 +1,7 @@ +pub mod draws; pub mod cot; pub mod digital_pointer; pub mod eud; pub mod gps; +pub mod message; pub mod nato; \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index d5700cf..61aca55 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ use arma_rs::{arma, Extension, Group}; mod structs; +mod tcp; mod tests; mod udp_socket; -mod tcp_socket; mod video_stream; mod cot; @@ -35,28 +35,43 @@ pub fn init() -> Extension { .command("local_ip", utils::address::get_local_address) .command("uuid", utils::uuid::get_uuid) .command("log", utils::log::log_info) - .group("udp_socket", + .group( + "udp_socket", Group::new() .command("start", udp_socket::start) .command("send_payload", udp_socket::send_payload) .command("send_gps_cot", udp_socket::send_gps_cot) - .command("stop", udp_socket::stop) + .command("stop", udp_socket::stop), ) .group( "tcp_socket", Group::new() - .command("start", tcp_socket::start) - .command("send_payload", tcp_socket::send_payload) - .command("send_eud_cot", tcp_socket::send_eud_cot) - .command("send_marker_cot", tcp_socket::send_marker_cot) - .command("send_digital_pointer_cot", tcp_socket::send_digital_pointer_cot) - .command("stop", tcp_socket::stop) + .command("start", tcp::start) + .command("stop", tcp::stop) + .command("send_payload", tcp::send_payload) + .group( + "cot", + Group::new() + .command("eud", tcp::cot::send_eud_cot) + .command("marker", tcp::cot::send_marker_cot) + .command("digital_pointer", tcp::cot::send_digital_pointer_cot) + .command("chat", tcp::cot::send_message_cot), + ) + .group( + "draw", + Group::new() + .command("circle", tcp::draw::send_circle_cot) + .command("ellipse", tcp::draw::send_ellipse_cot) + .command("rectangle", tcp::draw::send_rectangle_cot) + .command("free", tcp::draw::send_freedraw_cot) + .command("vector", tcp::draw::send_vectordraw_cot), + ), ) .group( "video_stream", Group::new() .command("start", video_stream::start_stream) - .command("stop", video_stream::stop_stream) + .command("stop", video_stream::stop_stream), ) .finish() } diff --git a/src/tcp/cot.rs b/src/tcp/cot.rs new file mode 100644 index 0000000..c5086bc --- /dev/null +++ b/src/tcp/cot.rs @@ -0,0 +1,32 @@ +use arma_rs::Context; + +use crate::{cot, tcp::send_payload}; + +pub fn send_eud_cot(ctx: Context, cursor_over_time: cot::eud::EudCoTPayload) -> &'static str { + let payload = cursor_over_time.to_cot().convert_to_xml(); + send_payload(ctx, payload); + + "Sending End User Device Cursor Over Time to TCP server" +} + +pub fn send_marker_cot(ctx: Context, cursor_over_time: cot::nato::MarkerCoTPayload) -> &'static str { + let payload = cursor_over_time.to_cot().convert_to_xml(); + send_payload(ctx, payload); + + "Sending Marker Cursor Over Time to TCP server" +} + +pub fn send_digital_pointer_cot(ctx: Context, cursor_over_time: cot::digital_pointer::DigitalPointerPayload) -> &'static str { + let payload = cursor_over_time.to_cot().convert_to_xml(); + send_payload(ctx, payload); + + "Sending Digital Pointer Cursor Over Time to TCP server" +} + +pub fn send_message_cot(ctx: Context, message_payload: cot::message::MessagePayload) -> &'static str { + let message_cot = cot::message::MessageCot::from_payload(message_payload); + let payload = message_cot.to_xml(); + send_payload(ctx, payload); + + "Sending Message CoT to TCP server" +} diff --git a/src/tcp/draw.rs b/src/tcp/draw.rs new file mode 100644 index 0000000..c3f859b --- /dev/null +++ b/src/tcp/draw.rs @@ -0,0 +1,34 @@ +use arma_rs::Context; + +use crate::{cot, tcp::send_payload}; + +pub fn send_circle_cot(ctx: Context, circle_payload: cot::draws::circle::CircleCoTPayload) -> &'static str { + let shape_circle_cot = circle_payload.to_cot(); + let now = chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true); + let stale = (chrono::Utc::now() + chrono::Duration::days(1)) + .to_rfc3339_opts(chrono::SecondsFormat::Millis, true); + let payload = shape_circle_cot.to_xml(&now, &stale); + send_payload(ctx, payload); + + "Sending Circle CoT to TCP server" +} + +pub fn send_ellipse_cot(ctx: Context) -> &'static str { + let _ = ctx; + "Not implemented: send_ellipse_cot" +} + +pub fn send_rectangle_cot(ctx: Context) -> &'static str { + let _ = ctx; + "Not implemented: send_ellipse_cot" +} + +pub fn send_freedraw_cot(ctx: Context) -> &'static str { + let _ = ctx; + "Not implemented: send_ellipse_cot" +} + +pub fn send_vectordraw_cot(ctx: Context) -> &'static str { + let _ = ctx; + "Not implemented: send_ellipse_cot" +} \ No newline at end of file diff --git a/src/tcp_socket.rs b/src/tcp/mod.rs similarity index 84% rename from src/tcp_socket.rs rename to src/tcp/mod.rs index 98b99f5..df7b490 100644 --- a/src/tcp_socket.rs +++ b/src/tcp/mod.rs @@ -7,7 +7,8 @@ use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::{Arc, Mutex}; use std::thread; -use crate::cot; +pub mod cot; +pub mod draw; pub enum TcpCommand { SendMessage(String, Context), @@ -123,27 +124,6 @@ pub fn send_payload(ctx: Context, payload: String) -> &'static str { "Sending payload to TCP server" } -pub fn send_eud_cot(ctx: Context, cursor_over_time: cot::eud::EudCoTPayload) -> &'static str { - let payload = cursor_over_time.to_cot().convert_to_xml(); - send_payload(ctx, payload); - - "Sending End User Device Cursor Over Time to TCP server" -} - -pub fn send_marker_cot(ctx: Context, cursor_over_time: cot::nato::MarkerCoTPayload) -> &'static str { - let payload = cursor_over_time.to_cot().convert_to_xml(); - send_payload(ctx, payload); - - "Sending Marker Cursor Over Time to TCP server" -} - -pub fn send_digital_pointer_cot(ctx: Context, cursor_over_time: cot::digital_pointer::DigitalPointerPayload) -> &'static str { - let payload = cursor_over_time.to_cot().convert_to_xml(); - send_payload(ctx, payload); - - "Sending Digital Pointer Cursor Over Time to TCP server" -} - pub fn stop(ctx: Context) -> &'static str { if let Some(ref client) = *TCP_CLIENT.lock().unwrap() { client.stop();