Refactor transport planning logic in DirectorWorker: Improved handling of empty transports for vehicle retrieval when no vehicles are available in the current branch. Enhanced checks for free vehicles and better selling prices in other branches, along with detailed logging for improved traceability during transport operations.
This commit is contained in:
@@ -282,6 +282,7 @@ const QUERY_GET_USER_BRANCHES: &str = r#"
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
// Freie Transportmittel in einer Region (nicht in aktiven Transporten)
|
// Freie Transportmittel in einer Region (nicht in aktiven Transporten)
|
||||||
|
// Ein Transport ist aktiv, wenn er noch in der Tabelle existiert
|
||||||
const QUERY_GET_FREE_VEHICLES_IN_REGION: &str = r#"
|
const QUERY_GET_FREE_VEHICLES_IN_REGION: &str = r#"
|
||||||
SELECT
|
SELECT
|
||||||
v.id AS vehicle_id,
|
v.id AS vehicle_id,
|
||||||
@@ -295,7 +296,6 @@ const QUERY_GET_FREE_VEHICLES_IN_REGION: &str = r#"
|
|||||||
SELECT DISTINCT t.vehicle_id
|
SELECT DISTINCT t.vehicle_id
|
||||||
FROM falukant_data.transport t
|
FROM falukant_data.transport t
|
||||||
WHERE t.vehicle_id IS NOT NULL
|
WHERE t.vehicle_id IS NOT NULL
|
||||||
AND t.arrived_at IS NULL
|
|
||||||
);
|
);
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@@ -747,6 +747,7 @@ impl DirectorWorker {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Prüfe, ob Transportmittel im aktuellen Branch vorhanden sind
|
// Prüfe, ob Transportmittel im aktuellen Branch vorhanden sind
|
||||||
|
// Ein Transport ist aktiv, wenn er noch in der Tabelle existiert
|
||||||
const QUERY_COUNT_VEHICLES_IN_BRANCH_REGION: &str = r#"
|
const QUERY_COUNT_VEHICLES_IN_BRANCH_REGION: &str = r#"
|
||||||
SELECT COUNT(*) AS count
|
SELECT COUNT(*) AS count
|
||||||
FROM falukant_data.vehicle v
|
FROM falukant_data.vehicle v
|
||||||
@@ -758,7 +759,6 @@ impl DirectorWorker {
|
|||||||
SELECT DISTINCT t.vehicle_id
|
SELECT DISTINCT t.vehicle_id
|
||||||
FROM falukant_data.transport t
|
FROM falukant_data.transport t
|
||||||
WHERE t.vehicle_id IS NOT NULL
|
WHERE t.vehicle_id IS NOT NULL
|
||||||
AND t.arrived_at IS NULL
|
|
||||||
);
|
);
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
@@ -800,6 +800,42 @@ impl DirectorWorker {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wenn keine Transportmittel im Branch vorhanden sind, aber Items vorhanden sind,
|
||||||
|
// versuche leere Transporte zu planen, um Fahrzeuge zurückzuholen
|
||||||
|
if vehicles_in_branch == 0 && !items.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Keine Transportmittel im Branch {} vorhanden, aber {} Items vorhanden. Prüfe auf leere Transporte zum Zurückholen",
|
||||||
|
director.branch_id, items.len()
|
||||||
|
);
|
||||||
|
if let Err(err) = self.plan_empty_transports_for_vehicle_retrieval(
|
||||||
|
&mut conn,
|
||||||
|
falukant_user_id,
|
||||||
|
director.branch_id,
|
||||||
|
) {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Fehler beim Planen leerer Transporte: {err}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Nach dem Planen leerer Transporte erneut prüfen, ob jetzt Transportmittel vorhanden sind
|
||||||
|
let vehicle_count_rows_after = conn.execute(
|
||||||
|
"count_vehicles_in_branch",
|
||||||
|
&[&falukant_user_id, &director.branch_id],
|
||||||
|
)?;
|
||||||
|
let vehicles_in_branch_after = vehicle_count_rows_after
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.and_then(|row| row.get("count").and_then(|v| v.parse::<i32>().ok()))
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
if vehicles_in_branch_after == 0 {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Nach Planen leerer Transporte immer noch keine Transportmittel im Branch {} vorhanden. Überspringe normale Transportplanung.",
|
||||||
|
director.branch_id
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Lohnende Transporte planen. Dabei werden:
|
// Lohnende Transporte planen. Dabei werden:
|
||||||
// - ggf. Transport-Einträge erzeugt
|
// - ggf. Transport-Einträge erzeugt
|
||||||
// - Inventar-Mengen reduziert
|
// - Inventar-Mengen reduziert
|
||||||
@@ -841,9 +877,19 @@ impl DirectorWorker {
|
|||||||
|
|
||||||
// Nach normalen Transporten: Wenn keine Transportmittel mehr im Branch vorhanden sind,
|
// Nach normalen Transporten: Wenn keine Transportmittel mehr im Branch vorhanden sind,
|
||||||
// aber bessere Verkaufspreise in anderen Branches existieren, plane leere Transporte
|
// aber bessere Verkaufspreise in anderen Branches existieren, plane leere Transporte
|
||||||
if vehicles_in_branch == 0 {
|
let vehicle_count_rows_final = conn.execute(
|
||||||
|
"count_vehicles_in_branch",
|
||||||
|
&[&falukant_user_id, &director.branch_id],
|
||||||
|
)?;
|
||||||
|
let vehicles_in_branch_final = vehicle_count_rows_final
|
||||||
|
.into_iter()
|
||||||
|
.next()
|
||||||
|
.and_then(|row| row.get("count").and_then(|v| v.parse::<i32>().ok()))
|
||||||
|
.unwrap_or(0);
|
||||||
|
|
||||||
|
if vehicles_in_branch_final == 0 {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"[DirectorWorker] Keine Transportmittel mehr im Branch {} vorhanden, prüfe auf leere Transporte zum Zurückholen",
|
"[DirectorWorker] Nach Transporten keine Transportmittel mehr im Branch {} vorhanden, prüfe auf leere Transporte zum Zurückholen",
|
||||||
director.branch_id
|
director.branch_id
|
||||||
);
|
);
|
||||||
if let Err(err) = self.plan_empty_transports_for_vehicle_retrieval(
|
if let Err(err) = self.plan_empty_transports_for_vehicle_retrieval(
|
||||||
@@ -1392,29 +1438,14 @@ impl DirectorWorker {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Für jeden anderen Branch prüfen, ob bessere Verkaufspreise existieren
|
// Für jeden anderen Branch prüfen, ob freie Transportmittel verfügbar sind
|
||||||
// und freie Transportmittel verfügbar sind
|
// und ob bessere Verkaufspreise existieren (zur Priorisierung)
|
||||||
conn.prepare("get_free_vehicles_in_region", QUERY_GET_FREE_VEHICLES_IN_REGION)?;
|
conn.prepare("get_free_vehicles_in_region", QUERY_GET_FREE_VEHICLES_IN_REGION)?;
|
||||||
conn.prepare("get_region_worth_for_product", QUERY_GET_REGION_WORTH_FOR_PRODUCT)?;
|
conn.prepare("get_region_worth_for_product", QUERY_GET_REGION_WORTH_FOR_PRODUCT)?;
|
||||||
conn.prepare("insert_empty_transport", QUERY_INSERT_EMPTY_TRANSPORT)?;
|
conn.prepare("insert_empty_transport", QUERY_INSERT_EMPTY_TRANSPORT)?;
|
||||||
|
|
||||||
// Prüfe für jeden Branch, ob es bessere Verkaufspreise gibt
|
// Sammle alle Branches mit freien Transportmitteln und berechne Preisvorteil
|
||||||
// Dazu müssen wir alle Produkte durchgehen, die in town_product_worth existieren
|
let mut branches_with_vehicles: Vec<(i32, i32, i32, f64)> = Vec::new(); // (branch_id, region_id, vehicle_count, price_delta)
|
||||||
const QUERY_GET_ALL_PRODUCTS: &str = r#"
|
|
||||||
SELECT DISTINCT product_id
|
|
||||||
FROM falukant_data.town_product_worth
|
|
||||||
WHERE region_id IN (
|
|
||||||
SELECT region_id
|
|
||||||
FROM falukant_data.branch
|
|
||||||
WHERE falukant_user_id = $1
|
|
||||||
);
|
|
||||||
"#;
|
|
||||||
|
|
||||||
conn.prepare("get_all_products", QUERY_GET_ALL_PRODUCTS)?;
|
|
||||||
let product_rows = conn.execute("get_all_products", &[&falukant_user_id])?;
|
|
||||||
|
|
||||||
let mut best_target_branch: Option<(i32, i32)> = None; // (branch_id, region_id)
|
|
||||||
let mut best_price_delta: f64 = 0.0;
|
|
||||||
|
|
||||||
for branch_row in &branch_rows {
|
for branch_row in &branch_rows {
|
||||||
let target_region_id = branch_row
|
let target_region_id = branch_row
|
||||||
@@ -1430,73 +1461,14 @@ impl DirectorWorker {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfe für jedes Produkt, ob der Preis in der Zielregion besser ist
|
// Prüfe auf freie Transportmittel in dieser Region
|
||||||
for product_row in &product_rows {
|
|
||||||
let product_id = product_row
|
|
||||||
.get("product_id")
|
|
||||||
.and_then(|v| v.parse::<i32>().ok())
|
|
||||||
.unwrap_or(-1);
|
|
||||||
|
|
||||||
if product_id < 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Worth-Percent für beide Regionen abrufen
|
|
||||||
let worth_rows = conn.execute(
|
|
||||||
"get_region_worth_for_product",
|
|
||||||
&[&falukant_user_id, &product_id],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let mut current_worth: Option<f64> = None;
|
|
||||||
let mut target_worth: Option<f64> = None;
|
|
||||||
|
|
||||||
for worth_row in worth_rows {
|
|
||||||
let region_id = worth_row
|
|
||||||
.get("region_id")
|
|
||||||
.and_then(|v| v.parse::<i32>().ok())
|
|
||||||
.unwrap_or(-1);
|
|
||||||
let worth_percent = worth_row
|
|
||||||
.get("worth_percent")
|
|
||||||
.and_then(|v| v.parse::<f64>().ok())
|
|
||||||
.unwrap_or(100.0);
|
|
||||||
|
|
||||||
if region_id == current_region_id {
|
|
||||||
current_worth = Some(worth_percent);
|
|
||||||
} else if region_id == target_region_id {
|
|
||||||
target_worth = Some(worth_percent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wenn der Zielregion einen besseren Preis hat, merke diesen Branch
|
|
||||||
if let (Some(current), Some(target)) = (current_worth, target_worth) {
|
|
||||||
let price_delta = target - current;
|
|
||||||
if price_delta > best_price_delta {
|
|
||||||
best_price_delta = price_delta;
|
|
||||||
best_target_branch = Some((target_branch_id, target_region_id));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wenn ein Branch mit besseren Preisen gefunden wurde, prüfe auf freie Transportmittel
|
|
||||||
if let Some((target_branch_id, target_region_id)) = best_target_branch {
|
|
||||||
eprintln!(
|
|
||||||
"[DirectorWorker] Besserer Verkaufspreis in Branch {} (Region {}) gefunden, prüfe auf freie Transportmittel",
|
|
||||||
target_branch_id, target_region_id
|
|
||||||
);
|
|
||||||
|
|
||||||
// Freie Transportmittel in der Zielregion finden
|
|
||||||
let vehicle_rows = conn.execute(
|
let vehicle_rows = conn.execute(
|
||||||
"get_free_vehicles_in_region",
|
"get_free_vehicles_in_region",
|
||||||
&[&falukant_user_id, &target_region_id],
|
&[&falukant_user_id, &target_region_id],
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if vehicle_rows.is_empty() {
|
if vehicle_rows.is_empty() {
|
||||||
eprintln!(
|
continue;
|
||||||
"[DirectorWorker] Keine freien Transportmittel in Region {} gefunden",
|
|
||||||
target_region_id
|
|
||||||
);
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prüfe, ob eine Route zurück zum aktuellen Branch existiert
|
// Prüfe, ob eine Route zurück zum aktuellen Branch existiert
|
||||||
@@ -1508,33 +1480,86 @@ impl DirectorWorker {
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
if vehicles.is_empty() {
|
if vehicles.is_empty() {
|
||||||
eprintln!(
|
continue;
|
||||||
"[DirectorWorker] Keine Route von Region {} zurück zu Region {} gefunden",
|
|
||||||
target_region_id, current_region_id
|
|
||||||
);
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leere Transporte für alle verfügbaren Fahrzeuge anlegen
|
// Berechne Preisvorteil (vereinfacht: verwende worth_percent-Differenz)
|
||||||
let mut transport_count = 0;
|
// Hole worth_percent für beide Regionen (für ein beliebiges Produkt)
|
||||||
for vehicle in &vehicles {
|
let mut price_delta = 0.0;
|
||||||
conn.execute(
|
const QUERY_GET_AVERAGE_WORTH: &str = r#"
|
||||||
"insert_empty_transport",
|
SELECT
|
||||||
&[&target_region_id, ¤t_region_id, &vehicle.id],
|
AVG(CASE WHEN region_id = $1 THEN worth_percent ELSE NULL END) AS current_worth,
|
||||||
)?;
|
AVG(CASE WHEN region_id = $2 THEN worth_percent ELSE NULL END) AS target_worth
|
||||||
transport_count += 1;
|
FROM falukant_data.town_product_worth
|
||||||
|
WHERE region_id IN ($1, $2);
|
||||||
|
"#;
|
||||||
|
|
||||||
|
conn.prepare("get_average_worth", QUERY_GET_AVERAGE_WORTH)?;
|
||||||
|
let worth_rows = conn.execute(
|
||||||
|
"get_average_worth",
|
||||||
|
&[¤t_region_id, &target_region_id],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
if let Some(worth_row) = worth_rows.into_iter().next() {
|
||||||
|
let current_worth = worth_row
|
||||||
|
.get("current_worth")
|
||||||
|
.and_then(|v| v.parse::<f64>().ok())
|
||||||
|
.unwrap_or(100.0);
|
||||||
|
let target_worth = worth_row
|
||||||
|
.get("target_worth")
|
||||||
|
.and_then(|v| v.parse::<f64>().ok())
|
||||||
|
.unwrap_or(100.0);
|
||||||
|
price_delta = target_worth - current_worth;
|
||||||
}
|
}
|
||||||
|
|
||||||
eprintln!(
|
branches_with_vehicles.push((
|
||||||
"[DirectorWorker] {} leere Transporte geplant: Region {} -> Region {}",
|
target_branch_id,
|
||||||
transport_count, target_region_id, current_region_id
|
target_region_id,
|
||||||
);
|
vehicles.len() as i32,
|
||||||
} else {
|
price_delta,
|
||||||
eprintln!(
|
));
|
||||||
"[DirectorWorker] Kein Branch mit besseren Verkaufspreisen gefunden"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if branches_with_vehicles.is_empty() {
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Keine Branches mit freien Transportmitteln gefunden"
|
||||||
|
);
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wähle den Branch mit dem besten Preisvorteil (oder einfach den ersten, wenn alle gleich sind)
|
||||||
|
branches_with_vehicles.sort_by(|a, b| b.3.partial_cmp(&a.3).unwrap_or(std::cmp::Ordering::Equal));
|
||||||
|
|
||||||
|
let (target_branch_id, target_region_id, vehicle_count, price_delta) = branches_with_vehicles[0];
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] Bester Branch für Fahrzeug-Rückholung: Branch {} (Region {}), {} Fahrzeuge, Preisvorteil: {:.2}%",
|
||||||
|
target_branch_id, target_region_id, vehicle_count, price_delta
|
||||||
|
);
|
||||||
|
|
||||||
|
// Hole die Fahrzeuge nochmal für diesen Branch
|
||||||
|
let vehicles = Self::get_transport_vehicles_for_route(
|
||||||
|
conn,
|
||||||
|
falukant_user_id,
|
||||||
|
target_region_id,
|
||||||
|
current_region_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
// Leere Transporte für alle verfügbaren Fahrzeuge anlegen
|
||||||
|
let mut transport_count = 0;
|
||||||
|
for vehicle in &vehicles {
|
||||||
|
conn.execute(
|
||||||
|
"insert_empty_transport",
|
||||||
|
&[&target_region_id, ¤t_region_id, &vehicle.id],
|
||||||
|
)?;
|
||||||
|
transport_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
eprintln!(
|
||||||
|
"[DirectorWorker] {} leere Transporte geplant: Region {} -> Region {}",
|
||||||
|
transport_count, target_region_id, current_region_id
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user