Skip to content

Commit

Permalink
add remote status
Browse files Browse the repository at this point in the history
  • Loading branch information
SeaDve committed Dec 6, 2024
1 parent ba970de commit 9b6dd02
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 9 deletions.
2 changes: 2 additions & 0 deletions data/resources/icons/scalable/actions/big-dot-symbolic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions data/resources/icons/scalable/actions/update-symbolic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions data/resources/ui/settings_view.ui
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,28 @@
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="title">Remote Status</property>
<property name="header-suffix">
<object class="GtkButton">
<property name="action-name">settings-view.reload-remote-status</property>
<property name="icon-name">update-symbolic</property>
<style>
<class name="flat"/>
<class name="circular"/>
</style>
</object>
</property>
<child>
<object class="GtkListBox" id="remote_status_box">
<style>
<class name="card"/>
</style>
</object>
</child>
</object>
</child>
</object>
</child>
</template>
Expand Down
16 changes: 11 additions & 5 deletions src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use gtk::{
};
use serde::{Deserialize, Serialize};

use crate::jpeg_image::JpegImage;
use crate::{jpeg_image::JpegImage, remote::Remote};

const GTK_SINK_NAME: &str = "gtksink";
const RTSP_SRC_NAME: &str = "rtspsrc";
Expand Down Expand Up @@ -152,10 +152,6 @@ impl Camera {
self.restart()
}

pub fn ip_addr(&self) -> String {
self.imp().ip_addr.borrow().clone()
}

pub fn set_enable_motion_detection(&self, is_enabled: bool) {
let imp = self.imp();

Expand Down Expand Up @@ -426,6 +422,16 @@ impl Camera {
}
}

impl Remote for Camera {
fn ip_addr(&self) -> String {
self.imp().ip_addr.borrow().clone()
}

fn port(&self) -> u16 {
PORT
}
}

#[derive(Debug, Serialize, Deserialize)]
struct SensorDataField {
unit: String,
Expand Down
10 changes: 10 additions & 0 deletions src/detector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::{
entity_data::{EntityData, EntityDataField},
entity_id::EntityId,
jpeg_image::JpegImage,
remote::Remote,
rfid_reader::RfidReader,
sound::Sound,
Application,
Expand Down Expand Up @@ -135,6 +136,15 @@ impl Detector {
}
}

pub fn aux_cameras(&self) -> Vec<Camera> {
self.imp()
.aux_cameras
.borrow()
.iter()
.map(|(camera, _)| camera.clone())
.collect()
}

pub fn bind_rfid_reader(&self, rfid_reader: &RfidReader) {
rfid_reader.connect_detected(clone!(
#[weak(rename_to = obj)]
Expand Down
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mod jpeg_image;
mod log;
mod operation_mode_ext;
mod relay;
mod remote;
mod report;
mod report_table;
mod rfid_reader;
Expand Down
12 changes: 12 additions & 0 deletions src/relay.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use anyhow::{bail, ensure, Context, Result};
use gtk::{glib, subclass::prelude::*};

use crate::remote::Remote;

const PORT: u16 = 8888;

#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -98,3 +100,13 @@ impl Relay {
Ok(response)
}
}

impl Remote for Relay {
fn ip_addr(&self) -> String {
self.imp().ip_addr.borrow().clone()
}

fn port(&self) -> u16 {
PORT
}
}
25 changes: 25 additions & 0 deletions src/remote.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::{
net::{Ipv4Addr, SocketAddrV4, TcpStream},
time::Duration,
};

use anyhow::Result;

use gtk::gio;

const CONNECT_TIMEOUT: Duration = Duration::from_secs(3);

pub trait Remote {
fn ip_addr(&self) -> String;

fn port(&self) -> u16;

async fn check_port_reachability(&self) -> Result<()> {
let parsed_ip_addr = self.ip_addr().parse::<Ipv4Addr>()?;
let addr = SocketAddrV4::new(parsed_ip_addr, self.port());
gio::spawn_blocking(move || TcpStream::connect_timeout(&addr.into(), CONNECT_TIMEOUT))
.await
.unwrap()?;
Ok(())
}
}
16 changes: 14 additions & 2 deletions src/rfid_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ use gtk::{
subclass::prelude::*,
};

const TCP_STREAM_PORT: u16 = 8888;
use crate::remote::Remote;

const PORT: u16 = 8888;

mod imp {
use std::{cell::RefCell, sync::OnceLock};
Expand Down Expand Up @@ -117,7 +119,7 @@ impl RfidReader {
let ip_addr = imp.ip_addr.borrow().clone();
tracing::debug!("Trying to connect to {}", ip_addr);

let stream = TcpStream::connect((ip_addr, TCP_STREAM_PORT)).await?;
let stream = TcpStream::connect((ip_addr, PORT)).await?;
imp.stream.replace(Some(stream.clone()));

tracing::debug!("Connected to {:?}", stream.peer_addr());
Expand Down Expand Up @@ -147,3 +149,13 @@ impl RfidReader {
}
}
}

impl Remote for RfidReader {
fn ip_addr(&self) -> String {
self.imp().ip_addr.borrow().clone()
}

fn port(&self) -> u16 {
PORT
}
}
118 changes: 116 additions & 2 deletions src/ui/settings_view.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use adw::{prelude::*, subclass::prelude::*};
use gtk::{gio, glib};
use anyhow::Result;
use gtk::{
gio,
glib::{self, clone},
pango,
};
use std::process::Command;

use crate::Application;
use crate::{remote::Remote, Application};

mod imp {
use super::*;
Expand Down Expand Up @@ -32,6 +37,8 @@ mod imp {
pub(super) quit_button: TemplateChild<gtk::Button>,
#[template_child]
pub(super) shutdown_button: TemplateChild<gtk::Button>,
#[template_child]
pub(super) remote_status_box: TemplateChild<gtk::ListBox>,
}

#[glib::object_subclass]
Expand All @@ -42,6 +49,12 @@ mod imp {

fn class_init(klass: &mut Self::Class) {
klass.bind_template();

klass.install_action(
"settings-view.reload-remote-status",
None,
move |obj, _, _| obj.update_remote_status_box(),
);
}

fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
Expand Down Expand Up @@ -123,6 +136,8 @@ mod imp {
Application::get().add_message_toast("Failed to start shutdown process");
}
});

obj.update_remote_status_box();
}

fn dispose(&self) {
Expand All @@ -142,4 +157,103 @@ impl SettingsView {
pub fn new() -> Self {
glib::Object::new()
}

pub fn update_remote_status_box(&self) {
glib::spawn_future_local(clone!(
#[strong(rename_to = obj)]
self,
async move {
obj.action_set_enabled("settings-view.reload-remote-status", false);
obj.update_remote_status_inner().await;
obj.action_set_enabled("settings-view.reload-remote-status", true);
}
));
}

async fn update_remote_status_inner(&self) {
struct RemoteStatus {
name: &'static str,
ip_addr: String,
port: u16,
port_reachability: Result<()>,
}

let imp = self.imp();

imp.remote_status_box.remove_all();
imp.remote_status_box.append(
&gtk::Spinner::builder()
.width_request(24)
.height_request(24)
.margin_top(6)
.margin_bottom(6)
.spinning(true)
.build(),
);

let app = Application::get();
let mut statuses = vec![
RemoteStatus {
name: "Camera",
ip_addr: app.camera().ip_addr(),
port: app.camera().port(),
port_reachability: app.camera().check_port_reachability().await,
},
RemoteStatus {
name: "RFID Reader",
ip_addr: app.rfid_reader().ip_addr(),
port: app.rfid_reader().port(),
port_reachability: app.rfid_reader().check_port_reachability().await,
},
RemoteStatus {
name: "Relay",
ip_addr: app.relay().ip_addr(),
port: app.relay().port(),
port_reachability: app.relay().check_port_reachability().await,
},
];
for camera in app.detector().aux_cameras() {
statuses.push(RemoteStatus {
name: "Aux Camera",
ip_addr: camera.ip_addr(),
port: camera.port(),
port_reachability: camera.check_port_reachability().await,
});
}

if statuses.is_empty() {
imp.remote_status_box.append(
&gtk::Label::builder()
.label("No remote status to show")
.build(),
);
}

imp.remote_status_box.remove_all();

for status in statuses {
let row = adw::ActionRow::builder()
.activatable(false)
.selectable(false)
.title(status.name)
.subtitle(format!("{}:{}", status.ip_addr, status.port))
.build();

let label = gtk::Label::builder()
.xalign(1.0)
.ellipsize(pango::EllipsizeMode::End)
.selectable(true)
.build();
if let Err(err) = &status.port_reachability {
label.set_text(&err.to_string());
label.add_css_class("error");
} else {
label.set_text("OK");
label.add_css_class("success");
}
row.add_suffix(&label);

imp.remote_status_box.append(&row);
}
}
}

0 comments on commit 9b6dd02

Please sign in to comment.