Update dependencies and enhance WebSocket server logging: Add 'chrono' and 'android_system_properties' to Cargo.lock, improve error handling and logging in websocket_server.rs, and streamline character creation notifications in worker modules for better clarity and maintainability.
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
use crate::db::{Row, Rows};
|
||||
use crate::message_broker::MessageBroker;
|
||||
use std::cmp::min;
|
||||
use std::sync::Mutex;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
@@ -14,12 +13,8 @@ use crate::worker::sql::{
|
||||
QUERY_DELETE_PRODUCTION,
|
||||
QUERY_INSERT_INVENTORY,
|
||||
QUERY_INSERT_UPDATE_PRODUCTION_LOG,
|
||||
QUERY_SET_PRODUCTION_SLEEP,
|
||||
QUERY_GET_SLEEP_PRODUCTIONS,
|
||||
QUERY_ADD_OVERPRODUCTION_NOTIFICATION,
|
||||
};
|
||||
use crate::worker::insert_notification_conn;
|
||||
|
||||
const QUERY_COUNT_OPEN_PRODUCTIONS: &str = r#"SELECT COUNT(*) AS cnt FROM falukant_data.production;"#;
|
||||
|
||||
/// Abbildet eine abgeschlossene Produktion aus der Datenbank.
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -45,8 +40,6 @@ struct StockInfo {
|
||||
pub struct ProduceWorker {
|
||||
base: BaseWorker,
|
||||
last_iteration: Option<Instant>,
|
||||
last_start_fix: Option<Instant>,
|
||||
last_sleep_check: Option<Instant>,
|
||||
}
|
||||
|
||||
impl ProduceWorker {
|
||||
@@ -54,8 +47,6 @@ impl ProduceWorker {
|
||||
Self {
|
||||
base: BaseWorker::new("ProduceWorker", pool, broker),
|
||||
last_iteration: None,
|
||||
last_start_fix: None,
|
||||
last_sleep_check: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,8 +67,6 @@ impl ProduceWorker {
|
||||
|
||||
self.base.set_current_step("Process Productions");
|
||||
self.process_productions();
|
||||
self.base.set_current_step("Process Sleep Productions");
|
||||
self.process_sleep_productions();
|
||||
self.base.set_current_step("Signal Activity");
|
||||
// TODO: Später Analogie zu signalActivity() aus der C++-Basisklasse herstellen.
|
||||
self.base.set_current_step("Loop Done");
|
||||
@@ -123,20 +112,6 @@ impl ProduceWorker {
|
||||
}
|
||||
|
||||
fn process_productions(&mut self) {
|
||||
// Heartbeat (damit sichtbar ist, dass der Worker läuft), max. 1x/Stunde.
|
||||
static LAST_HEARTBEAT: Mutex<Option<Instant>> = Mutex::new(None);
|
||||
{
|
||||
let mut last = LAST_HEARTBEAT.lock().unwrap();
|
||||
let should_log = last
|
||||
.map(|t| t.elapsed().as_secs() >= 3600)
|
||||
.unwrap_or(true);
|
||||
if should_log {
|
||||
eprintln!("[ProduceWorker] Heartbeat: alive");
|
||||
*last = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
|
||||
self.maybe_fix_null_production_starts();
|
||||
self.base
|
||||
.set_current_step("Fetch Finished Productions");
|
||||
|
||||
@@ -148,50 +123,6 @@ impl ProduceWorker {
|
||||
}
|
||||
};
|
||||
|
||||
// Debug: Gedrosselt loggen, wenn nie etwas "fertig" wird.
|
||||
if finished_productions.is_empty() {
|
||||
static LAST_EMPTY_LOG: Mutex<Option<Instant>> = Mutex::new(None);
|
||||
let mut last = LAST_EMPTY_LOG.lock().unwrap();
|
||||
let should_log = last
|
||||
.map(|t| t.elapsed().as_secs() >= 60)
|
||||
.unwrap_or(true);
|
||||
if should_log {
|
||||
let mut conn = match self.base.pool.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
eprintln!("[ProduceWorker] DB-Verbindung fehlgeschlagen (debug): {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let open_cnt = (|| -> Result<i32, crate::db::DbError> {
|
||||
conn.prepare("count_open_productions", QUERY_COUNT_OPEN_PRODUCTIONS)?;
|
||||
let rows = conn.execute("count_open_productions", &[])?;
|
||||
Ok(rows
|
||||
.first()
|
||||
.and_then(|r| r.get("cnt"))
|
||||
.and_then(|v| v.parse::<i32>().ok())
|
||||
.unwrap_or(0))
|
||||
})()
|
||||
.unwrap_or(0);
|
||||
|
||||
let mut sql_preview = QUERY_GET_FINISHED_PRODUCTIONS.to_string();
|
||||
const MAX_SQL_PREVIEW: usize = 1200;
|
||||
if sql_preview.len() > MAX_SQL_PREVIEW {
|
||||
sql_preview.truncate(MAX_SQL_PREVIEW);
|
||||
sql_preview.push_str(" …");
|
||||
}
|
||||
|
||||
eprintln!(
|
||||
"[ProduceWorker] Keine fertigen Produktionen gefunden. Offene Produktionen in DB: {}. Query(get_finished_productions): {}",
|
||||
open_cnt,
|
||||
sql_preview
|
||||
);
|
||||
|
||||
*last = Some(Instant::now());
|
||||
}
|
||||
}
|
||||
|
||||
self.base
|
||||
.set_current_step("Process Finished Productions");
|
||||
|
||||
@@ -221,37 +152,12 @@ impl ProduceWorker {
|
||||
production_id,
|
||||
} = *production;
|
||||
|
||||
// Prüfe VOR dem Abschluss, ob genug Lagerplatz vorhanden ist
|
||||
if self.has_enough_storage_capacity(branch_id, quantity) {
|
||||
// Genug Platz: Produktion abschließen
|
||||
if self.add_to_inventory(branch_id, product_id, quantity, quality, user_id) {
|
||||
self.delete_production(production_id);
|
||||
self.add_production_to_log(region_id, user_id, product_id, quantity);
|
||||
}
|
||||
} else {
|
||||
// Nicht genug Platz: Produktion auf sleep setzen
|
||||
if let Err(err) = self.set_production_sleep(production_id) {
|
||||
eprintln!("[ProduceWorker] Fehler beim Setzen von sleep für Produktion {}: {}", production_id, err);
|
||||
}
|
||||
if self.add_to_inventory(branch_id, product_id, quantity, quality, user_id) {
|
||||
self.delete_production(production_id);
|
||||
self.add_production_to_log(region_id, user_id, product_id, quantity);
|
||||
}
|
||||
}
|
||||
|
||||
fn has_enough_storage_capacity(&self, branch_id: i32, required_quantity: i32) -> bool {
|
||||
let stocks = match self.get_available_stocks(branch_id) {
|
||||
Ok(rows) => rows,
|
||||
Err(err) => {
|
||||
eprintln!("[ProduceWorker] Fehler in getAvailableStocks: {err}");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
let total_free_capacity: i32 = stocks.iter()
|
||||
.map(|stock| (stock.total_capacity - stock.filled).max(0))
|
||||
.sum();
|
||||
|
||||
total_free_capacity >= required_quantity
|
||||
}
|
||||
|
||||
fn add_to_inventory(
|
||||
&mut self,
|
||||
branch_id: i32,
|
||||
@@ -395,37 +301,6 @@ impl ProduceWorker {
|
||||
conn.execute("get_finished_productions", &[])
|
||||
}
|
||||
|
||||
fn maybe_fix_null_production_starts(&mut self) {
|
||||
let now = Instant::now();
|
||||
let should_run = self
|
||||
.last_start_fix
|
||||
.map(|t| now.saturating_duration_since(t) >= Duration::from_secs(3600))
|
||||
.unwrap_or(true);
|
||||
if !should_run {
|
||||
return;
|
||||
}
|
||||
|
||||
self.last_start_fix = Some(now);
|
||||
|
||||
let mut conn = match self.base.pool.get() {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
eprintln!("[ProduceWorker] DB-Verbindung fehlgeschlagen (start_fix): {e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// best-effort: nur loggen, wenn es scheitert
|
||||
use crate::worker::sql::QUERY_FIX_NULL_PRODUCTION_START;
|
||||
if let Err(err) = conn.prepare("fix_null_production_start", QUERY_FIX_NULL_PRODUCTION_START) {
|
||||
eprintln!("[ProduceWorker] Fehler prepare fix_null_production_start: {err}");
|
||||
return;
|
||||
}
|
||||
if let Err(err) = conn.execute("fix_null_production_start", &[]) {
|
||||
eprintln!("[ProduceWorker] Fehler exec fix_null_production_start: {err}");
|
||||
}
|
||||
}
|
||||
|
||||
fn load_available_stocks(&self, branch_id: i32) -> Result<Rows, crate::db::DbError> {
|
||||
let mut conn = self
|
||||
.base
|
||||
@@ -489,95 +364,6 @@ impl ProduceWorker {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_production_sleep(&self, production_id: i32) -> Result<(), crate::db::DbError> {
|
||||
let mut conn = self
|
||||
.base
|
||||
.pool
|
||||
.get()
|
||||
.map_err(|e| crate::db::DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
conn.prepare("set_production_sleep", QUERY_SET_PRODUCTION_SLEEP)?;
|
||||
conn.execute("set_production_sleep", &[&production_id])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn process_sleep_productions(&mut self) {
|
||||
const SLEEP_CHECK_INTERVAL_SECS: u64 = 300; // 5 Minuten
|
||||
|
||||
let now = Instant::now();
|
||||
let should_check = match self.last_sleep_check {
|
||||
None => {
|
||||
self.last_sleep_check = Some(now);
|
||||
true
|
||||
}
|
||||
Some(last) => {
|
||||
if now.saturating_duration_since(last).as_secs() >= SLEEP_CHECK_INTERVAL_SECS {
|
||||
self.last_sleep_check = Some(now);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if !should_check {
|
||||
return;
|
||||
}
|
||||
|
||||
let sleep_productions = match self.get_sleep_productions() {
|
||||
Ok(rows) => rows,
|
||||
Err(err) => {
|
||||
eprintln!("[ProduceWorker] Fehler beim Laden von sleep-Produktionen: {err}");
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
for production in sleep_productions {
|
||||
self.handle_sleep_production(&production);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_sleep_productions(&self) -> Result<Vec<FinishedProduction>, crate::db::DbError> {
|
||||
let rows = self.load_sleep_productions()?;
|
||||
Ok(rows
|
||||
.into_iter()
|
||||
.filter_map(Self::map_row_to_finished_production)
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn load_sleep_productions(&self) -> Result<Rows, crate::db::DbError> {
|
||||
let mut conn = self
|
||||
.base
|
||||
.pool
|
||||
.get()
|
||||
.map_err(|e| crate::db::DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
conn.prepare("get_sleep_productions", QUERY_GET_SLEEP_PRODUCTIONS)?;
|
||||
conn.execute("get_sleep_productions", &[])
|
||||
}
|
||||
|
||||
fn handle_sleep_production(&mut self, production: &FinishedProduction) {
|
||||
let FinishedProduction {
|
||||
branch_id,
|
||||
product_id,
|
||||
quantity,
|
||||
quality,
|
||||
user_id,
|
||||
region_id,
|
||||
production_id,
|
||||
} = *production;
|
||||
|
||||
// Prüfe erneut, ob jetzt genug Lagerplatz vorhanden ist
|
||||
if self.has_enough_storage_capacity(branch_id, quantity) {
|
||||
// Jetzt genug Platz: Produktion abschließen
|
||||
if self.add_to_inventory(branch_id, product_id, quantity, quality, user_id) {
|
||||
self.delete_production(production_id);
|
||||
self.add_production_to_log(region_id, user_id, product_id, quantity);
|
||||
}
|
||||
}
|
||||
// Wenn immer noch nicht genug Platz: Produktion bleibt auf sleep
|
||||
}
|
||||
|
||||
fn insert_or_update_production_log(
|
||||
&self,
|
||||
region_id: i32,
|
||||
@@ -616,13 +402,22 @@ impl ProduceWorker {
|
||||
.get()
|
||||
.map_err(|e| crate::db::DbError::new(format!("DB-Verbindung fehlgeschlagen: {e}")))?;
|
||||
|
||||
conn.prepare(
|
||||
"add_overproduction_notification",
|
||||
QUERY_ADD_OVERPRODUCTION_NOTIFICATION,
|
||||
)?;
|
||||
|
||||
// Zusätzlich zur Menge die Branch-ID in der Payload mitschicken, damit
|
||||
// das Frontend die Überproduktion einem konkreten Branch zuordnen kann.
|
||||
let notification = format!(
|
||||
r#"{{"tr":"production.overproduction","value":{},"branch_id":{}}}"#,
|
||||
remaining_quantity, branch_id
|
||||
);
|
||||
insert_notification_conn(&mut conn, user_id, ¬ification, None)?;
|
||||
|
||||
conn.execute(
|
||||
"add_overproduction_notification",
|
||||
&[&user_id, ¬ification],
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user