Compare commits

..

No commits in common. "1842419f102e0e974e83faa567d6c00dd80afbbc" and "65dd107514f3fc42d882781d738f00e0bcae74ee" have entirely different histories.

22 changed files with 30 additions and 68 deletions

View file

@ -36,8 +36,6 @@ struct Args {
enum Command {
/// Run as a background daemon providing a web UI.
Daemon,
/// Stop the running daemon process.
Kill,
}
/// Default config content, embedded in the binary.
@ -93,65 +91,24 @@ async fn main() {
let log_buffer = logger::setup_logger();
let args = Args::parse();
if let Some(command) = &args.command {
match command {
Command::Daemon => {
log::info!("🚀 Starting daemon...");
if let Some(Command::Daemon) = &args.command {
log::info!("🚀 Starting daemon...");
// Create files for stdout and stderr in the current directory
let stdout =
fs::File::create("daemon.out").expect("Could not create daemon.out");
let stderr =
fs::File::create("daemon.err").expect("Could not create daemon.err");
// Create files for stdout and stderr in the current directory
let stdout = fs::File::create("daemon.out").expect("Could not create daemon.out");
let stderr = fs::File::create("daemon.err").expect("Could not create daemon.err");
let daemonize = Daemonize::new()
.pid_file("ntp_timeturner.pid") // Create a PID file
.working_directory(".") // Keep the same working directory
.stdout(stdout)
.stderr(stderr);
let daemonize = Daemonize::new()
.pid_file("ntp_timeturner.pid") // Create a PID file
.working_directory(".") // Keep the same working directory
.stdout(stdout)
.stderr(stderr);
match daemonize.start() {
Ok(_) => { /* Process is now daemonized */ }
Err(e) => {
log::error!("Error daemonizing: {}", e);
return; // Exit if daemonization fails
}
}
}
Command::Kill => {
log::info!("🛑 Stopping daemon...");
let pid_file = "ntp_timeturner.pid";
match fs::read_to_string(pid_file) {
Ok(pid_str) => {
let pid_str = pid_str.trim();
log::info!("Found daemon with PID: {}", pid_str);
match std::process::Command::new("kill").arg("-9").arg(format!("-{}", pid_str)).status() {
Ok(status) => {
if status.success() {
log::info!("✅ Daemon stopped successfully.");
if fs::remove_file(pid_file).is_err() {
log::warn!("Could not remove PID file '{}'. It may need to be removed manually.", pid_file);
}
} else {
log::error!("'kill' command failed with status: {}. The daemon may not be running, or you may not have permission to stop it.", status);
log::warn!("Attempting to remove stale PID file '{}'...", pid_file);
if fs::remove_file(pid_file).is_ok() {
log::info!("Removed stale PID file.");
} else {
log::warn!("Could not remove PID file.");
}
}
}
Err(e) => {
log::error!("Failed to execute 'kill' command. Is 'kill' in your PATH? Error: {}", e);
}
}
}
Err(_) => {
log::error!("Could not read PID file '{}'. Is the daemon running in this directory?", pid_file);
}
}
return;
match daemonize.start() {
Ok(_) => { /* Process is now daemonized */ }
Err(e) => {
log::error!("Error daemonizing: {}", e);
return; // Exit if daemonization fails
}
}
}
@ -248,9 +205,7 @@ async fn main() {
let state = sync_state.lock().unwrap();
let config = sync_config.lock().unwrap();
if config.is_auto_sync_paused() {
log::info!("Auto-sync is temporarily paused.");
} else if config.auto_sync_enabled && state.latest.is_some() {
if config.auto_sync_enabled && state.latest.is_some() {
let delta = state.get_ewma_clock_delta();
let frame = state.latest.as_ref().unwrap();

View file

@ -45,13 +45,21 @@ pub fn calculate_target_time(frame: &LtcFrame, config: &Config) -> DateTime<Loca
let timecode_secs =
frame.hours as i64 * 3600 + frame.minutes as i64 * 60 + frame.seconds as i64;
// Timecode is always treated as wall-clock time. NDF scaling is not applied
// as the LTC source appears to be pre-compensated.
// Total duration in seconds as a rational number, including frames
let total_duration_secs =
Ratio::new(timecode_secs, 1) + Ratio::new(frame.frames as i64, 1) / frame.frame_rate;
// For non-drop-frame fractional rates (23.98, 29.97), timecode runs slower than wall clock.
// We need to scale the timecode duration up to get wall clock time.
// The scaling factor is 1001/1000. For drop-frame, this isn't necessary.
let scaled_duration_secs = if *frame.frame_rate.denom() == 1001 && !frame.is_drop_frame {
total_duration_secs * Ratio::new(1001, 1000)
} else {
total_duration_secs
};
// Convert to milliseconds
let total_ms = (total_duration_secs * Ratio::new(1000, 1))
let total_ms = (scaled_duration_secs * Ratio::new(1000, 1))
.round()
.to_integer();
@ -149,20 +157,19 @@ pub fn nudge_clock(microseconds: i64) -> Result<(), ()> {
pub fn set_date(date: &str) -> Result<(), ()> {
#[cfg(target_os = "linux")]
{
let datetime_str = format!("{} 10:00:00", date);
let success = Command::new("sudo")
.arg("date")
.arg("--set")
.arg(&datetime_str)
.arg(date)
.status()
.map(|s| s.success())
.unwrap_or(false);
if success {
log::info!("Set system date and time to {}", datetime_str);
log::info!("Set system date to {}", date);
Ok(())
} else {
log::error!("Failed to set system date and time");
log::error!("Failed to set system date");
Err(())
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 981 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 955 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 913 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB