247 lines
9.3 KiB
Python
247 lines
9.3 KiB
Python
# FlightMonitor/gui/panels/data_logging_panel.py
|
|
"""
|
|
Defines the GUI panel for controlling raw data logging functionality.
|
|
This panel is designed to be shared across different monitoring modes.
|
|
"""
|
|
import tkinter as tk
|
|
from tkinter import ttk, filedialog, font as tkFont
|
|
from typing import Optional, Any
|
|
from datetime import datetime, timezone, timedelta
|
|
import time
|
|
|
|
from flightmonitor.utils.logger import get_logger
|
|
|
|
module_logger = get_logger(__name__)
|
|
|
|
|
|
class DataLoggingPanel:
|
|
"""
|
|
Manages the GUI elements for enabling and configuring raw data logging.
|
|
"""
|
|
|
|
def __init__(self, parent_frame: ttk.Frame, controller: Any):
|
|
"""
|
|
Initializes the DataLoggingPanel.
|
|
|
|
Args:
|
|
parent_frame: The parent ttk.Frame where this panel will be placed.
|
|
controller: The application controller instance.
|
|
"""
|
|
self.parent_frame = parent_frame
|
|
self.controller = controller
|
|
|
|
# --- Internal State ---
|
|
self.session_start_time: Optional[float] = None
|
|
self.session_query_count: int = 0
|
|
self.session_query_rate_sec: Optional[int] = None
|
|
self._session_update_after_id: Optional[str] = None
|
|
|
|
# --- Tkinter Variables ---
|
|
self.enable_logging_var = tk.BooleanVar(value=False)
|
|
self.log_directory_var = tk.StringVar(value="")
|
|
self.last_query_result_var = tk.StringVar(value="Last query result: N/A")
|
|
self.session_info_var = tk.StringVar(value="Session not active.")
|
|
|
|
# --- Widget References ---
|
|
self.log_dir_entry: Optional[ttk.Entry] = None
|
|
self.browse_button: Optional[ttk.Button] = None
|
|
self.open_folder_button: Optional[ttk.Button] = None
|
|
self.summary_table: Optional[ttk.Treeview] = None
|
|
|
|
self._build_ui()
|
|
module_logger.debug("DataLoggingPanel initialized.")
|
|
|
|
def _build_ui(self):
|
|
"""Builds all the widgets for the panel."""
|
|
container = ttk.LabelFrame(
|
|
self.parent_frame, text="Data Logging Session", padding=10
|
|
)
|
|
container.pack(fill=tk.X, expand=True, padx=2, pady=5)
|
|
|
|
self.enable_checkbox = ttk.Checkbutton(
|
|
container,
|
|
text="Enable Raw Data Logging",
|
|
variable=self.enable_logging_var,
|
|
command=self._on_toggle_logging,
|
|
)
|
|
self.enable_checkbox.pack(anchor=tk.W, pady=(0, 5))
|
|
|
|
dir_frame = ttk.Frame(container)
|
|
dir_frame.pack(fill=tk.X, expand=True)
|
|
dir_frame.columnconfigure(1, weight=1)
|
|
|
|
ttk.Label(dir_frame, text="Save to:").grid(
|
|
row=0, column=0, sticky=tk.W, padx=(0, 5)
|
|
)
|
|
self.log_dir_entry = ttk.Entry(
|
|
dir_frame, textvariable=self.log_directory_var, state=tk.DISABLED
|
|
)
|
|
self.log_dir_entry.grid(row=0, column=1, sticky=tk.EW)
|
|
|
|
self.browse_button = ttk.Button(
|
|
dir_frame,
|
|
text="Browse...",
|
|
command=self._on_browse_directory,
|
|
state=tk.DISABLED,
|
|
)
|
|
self.browse_button.grid(row=0, column=2, sticky=tk.E, padx=(5, 2))
|
|
|
|
self.open_folder_button = ttk.Button(
|
|
dir_frame,
|
|
text="Open Folder",
|
|
command=self._on_open_folder,
|
|
state=tk.DISABLED,
|
|
)
|
|
self.open_folder_button.grid(row=0, column=3, sticky=tk.E, padx=(2, 0))
|
|
|
|
info_font = tkFont.Font(family="Helvetica", size=9, slant="italic")
|
|
last_result_label = ttk.Label(
|
|
container,
|
|
textvariable=self.last_query_result_var,
|
|
font=info_font,
|
|
foreground="navy",
|
|
)
|
|
last_result_label.pack(anchor=tk.W, pady=(8, 0))
|
|
|
|
session_info_label = ttk.Label(
|
|
container,
|
|
textvariable=self.session_info_var,
|
|
font=info_font,
|
|
foreground="darkgreen",
|
|
)
|
|
session_info_label.pack(anchor=tk.W, pady=(0, 2))
|
|
|
|
table_frame = ttk.Frame(container)
|
|
table_frame.pack(fill=tk.BOTH, expand=True, pady=(2, 0))
|
|
|
|
self.summary_table = ttk.Treeview(
|
|
table_frame, columns=("timestamp", "count"), show="headings", height=4
|
|
)
|
|
self.summary_table.heading("timestamp", text="Fetch Timestamp (UTC)")
|
|
self.summary_table.heading("count", text="Aircraft Count")
|
|
self.summary_table.column("timestamp", width=160, anchor=tk.W)
|
|
self.summary_table.column("count", width=100, anchor=tk.CENTER)
|
|
|
|
scrollbar = ttk.Scrollbar(
|
|
table_frame, orient="vertical", command=self.summary_table.yview
|
|
)
|
|
self.summary_table.configure(yscrollcommand=scrollbar.set)
|
|
|
|
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
|
self.summary_table.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
|
|
def _on_toggle_logging(self):
|
|
is_enabled = self.enable_logging_var.get()
|
|
new_state = tk.NORMAL if is_enabled else tk.DISABLED
|
|
if self.log_dir_entry:
|
|
self.log_dir_entry.config(state=new_state)
|
|
if self.browse_button:
|
|
self.browse_button.config(state=new_state)
|
|
if self.open_folder_button:
|
|
self.open_folder_button.config(state=new_state)
|
|
|
|
def _on_browse_directory(self):
|
|
directory = filedialog.askdirectory(
|
|
title="Select Directory to Save Raw Data", parent=self.parent_frame
|
|
)
|
|
if directory:
|
|
self.log_directory_var.set(directory)
|
|
|
|
def _on_open_folder(self):
|
|
directory_path = self.get_log_directory()
|
|
if self.controller and hasattr(self.controller, "open_log_directory"):
|
|
self.controller.open_log_directory(directory_path)
|
|
|
|
def session_started(self, start_timestamp: float, query_rate_sec: Optional[int]):
|
|
"""To be called by the controller when a monitoring/download session starts."""
|
|
self.session_start_time = start_timestamp
|
|
self.session_query_count = 0
|
|
self.session_query_rate_sec = query_rate_sec
|
|
|
|
if self._session_update_after_id:
|
|
self.parent_frame.after_cancel(self._session_update_after_id)
|
|
|
|
self._update_session_duration()
|
|
module_logger.info(f"DataLoggingPanel session timer started at {start_timestamp}.")
|
|
|
|
def session_stopped(self):
|
|
"""To be called by the controller when a session stops."""
|
|
if self._session_update_after_id:
|
|
self.parent_frame.after_cancel(self._session_update_after_id)
|
|
self._session_update_after_id = None
|
|
|
|
self.session_start_time = None
|
|
self.session_query_count = 0
|
|
self.session_query_rate_sec = None
|
|
self.session_info_var.set("Session not active.")
|
|
module_logger.info("DataLoggingPanel session timer stopped.")
|
|
|
|
def _update_session_duration(self):
|
|
"""Periodically updates the session duration label."""
|
|
if self.session_start_time is None:
|
|
return
|
|
|
|
elapsed_seconds = time.time() - self.session_start_time
|
|
duration_str = str(timedelta(seconds=int(elapsed_seconds)))
|
|
start_time_str = datetime.fromtimestamp(self.session_start_time, timezone.utc).strftime('%H:%M:%S')
|
|
|
|
rate_str = ""
|
|
if self.session_query_rate_sec is not None and self.session_query_rate_sec > 0:
|
|
rate_str = f" (1 per {self.session_query_rate_sec:.0f}s)"
|
|
|
|
session_text = (
|
|
f"Session started: {start_time_str} (UTC) | "
|
|
f"Duration: {duration_str} | "
|
|
f"Queries: {self.session_query_count}{rate_str}"
|
|
)
|
|
self.session_info_var.set(session_text)
|
|
|
|
self._session_update_after_id = self.parent_frame.after(1000, self._update_session_duration)
|
|
|
|
def increment_query_count(self):
|
|
"""Increments the session query counter."""
|
|
self.session_query_count += 1
|
|
# The label will be updated by the next scheduled _update_session_duration call
|
|
|
|
def is_logging_enabled(self) -> bool:
|
|
return self.enable_logging_var.get()
|
|
|
|
def get_log_directory(self) -> str:
|
|
return self.log_directory_var.get()
|
|
|
|
def add_summary_entry(self, timestamp: float, count: int):
|
|
if not self.summary_table or not self.summary_table.winfo_exists():
|
|
return
|
|
try:
|
|
dt_object = datetime.fromtimestamp(timestamp, timezone.utc)
|
|
time_str = dt_object.strftime("%Y-%m-%d %H:%M:%S")
|
|
self.summary_table.insert("", 0, values=(time_str, count))
|
|
except (ValueError, TypeError, tk.TclError):
|
|
pass
|
|
|
|
def update_last_query_result(self, timestamp: float, count: int):
|
|
try:
|
|
dt_object = datetime.fromtimestamp(timestamp, timezone.utc)
|
|
time_str = dt_object.strftime("%H:%M:%S")
|
|
result_text = f"Last query result: {count} aircraft at {time_str} UTC"
|
|
self.last_query_result_var.set(result_text)
|
|
except (ValueError, TypeError, tk.TclError):
|
|
pass
|
|
|
|
def clear_summary_table(self):
|
|
if not self.summary_table or not self.summary_table.winfo_exists():
|
|
return
|
|
|
|
self.last_query_result_var.set("Last query result: N/A")
|
|
self.session_stopped()
|
|
|
|
try:
|
|
for item in self.summary_table.get_children():
|
|
self.summary_table.delete(item)
|
|
except tk.TclError:
|
|
pass
|
|
|
|
def update_settings(self, enabled: bool, directory: str):
|
|
self.enable_logging_var.set(enabled)
|
|
self.log_directory_var.set(directory)
|
|
self._on_toggle_logging() |