tasks trait v2
This commit is contained in:
parent
c5499b0277
commit
ee39602e03
6 changed files with 176 additions and 11 deletions
|
@ -25,7 +25,7 @@ parking_lot = { version = "0.11", features = ["serde"] }
|
||||||
pathfinding = "4.6.0"
|
pathfinding = "4.6.0"
|
||||||
rstar = { version = "0.11.0", features = ["serde"] }
|
rstar = { version = "0.11.0", features = ["serde"] }
|
||||||
rustmatica = "0.1.1"
|
rustmatica = "0.1.1"
|
||||||
serde = { version = "1.0.193", features = ["rc"] }
|
serde = { version = "1.0.193", features = ["rc", "derive"] }
|
||||||
serde_json = "1.0.108"
|
serde_json = "1.0.108"
|
||||||
tokio = { version = "1.0", features = ["full"] }
|
tokio = { version = "1.0", features = ["full"] }
|
||||||
tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] }
|
tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] }
|
||||||
|
|
79
server/src/depot.rs
Normal file
79
server/src/depot.rs
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use log::warn;
|
||||||
|
use tokio::sync::{Mutex, OwnedMutexGuard};
|
||||||
|
|
||||||
|
use crate::{blocks::Position, turtle::TurtleCommander};
|
||||||
|
use crate::turtle::{TurtleCommand::*, TurtleCommandResponse};
|
||||||
|
|
||||||
|
|
||||||
|
/// List of available depots
|
||||||
|
///
|
||||||
|
/// below the specified position is an output chest of infinite capacity
|
||||||
|
/// ahead of the specified position is a chest of combustibles
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Depots {
|
||||||
|
depots: Arc<Mutex<Vec<Arc<Mutex<Position>>>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Depots {
|
||||||
|
/// Nearest depot to the given position
|
||||||
|
pub async fn nearest(&self, pos: Position) -> Option<OwnedMutexGuard<Position>> {
|
||||||
|
self.depots.lock().await
|
||||||
|
.iter().map(|i| i.clone())
|
||||||
|
.filter_map(|i| i.try_lock_owned().ok())
|
||||||
|
.min_by_key(|d| d.manhattan(pos))
|
||||||
|
.map(|d| d)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn dock(&self, turtle: TurtleCommander) -> Option<usize> {
|
||||||
|
let depot = self.nearest(turtle.pos().await).await?;
|
||||||
|
turtle.goto(*depot).await?;
|
||||||
|
|
||||||
|
// dump inventory
|
||||||
|
for i in 1..=16 {
|
||||||
|
turtle.execute(Select(i)).await;
|
||||||
|
turtle.execute(DropFront(64)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
// refuel
|
||||||
|
turtle.execute(Select(1)).await;
|
||||||
|
let limit = turtle.fuel_limit().await;
|
||||||
|
while turtle.fuel().await < limit {
|
||||||
|
turtle.execute(SuckFront(64)).await;
|
||||||
|
let re = turtle.execute(Refuel).await;
|
||||||
|
turtle.execute(DropDown(64)).await;
|
||||||
|
if let TurtleCommandResponse::Failure = re.ret {
|
||||||
|
// partial refuel, good enough
|
||||||
|
warn!("only received {} fuel", turtle.fuel().await);
|
||||||
|
if turtle.fuel().await > 5000 {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
turtle.execute(Wait(15)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
turtle.execute(Backward(1)).await;
|
||||||
|
|
||||||
|
drop(depot); // assumes that the turtle will very quickly leave
|
||||||
|
|
||||||
|
Some(turtle.fuel().await)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_vec(vec: Vec<Position>) -> Self {
|
||||||
|
let mut depots = Vec::new();
|
||||||
|
for depot in vec {
|
||||||
|
depots.push(Arc::new(Mutex::new(depot)));
|
||||||
|
}
|
||||||
|
Depots { depots: Arc::new(Mutex::new(depots)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn to_vec(self) -> Vec<Position> {
|
||||||
|
let mut depots = Vec::new();
|
||||||
|
for depot in self.depots.lock().await.iter() {
|
||||||
|
depots.push(*depot.lock().await)
|
||||||
|
}
|
||||||
|
depots
|
||||||
|
}
|
||||||
|
}
|
5
server/src/fell.rs
Normal file
5
server/src/fell.rs
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
use crate::{blocks::Vec3, turtle::TurtleCommander};
|
||||||
|
|
||||||
|
pub async fn fell(turtle: TurtleCommander, corner: Vec3) -> Option<()> {
|
||||||
|
Some(())
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ use axum::{
|
||||||
Router,
|
Router,
|
||||||
};
|
};
|
||||||
use blocks::{World, Position, };
|
use blocks::{World, Position, };
|
||||||
|
use depot::Depots;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rstar::RTree;
|
use rstar::RTree;
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ mod safe_kill;
|
||||||
mod turtle;
|
mod turtle;
|
||||||
mod turtle_api;
|
mod turtle_api;
|
||||||
mod tasks;
|
mod tasks;
|
||||||
|
mod depot;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct SavedState {
|
struct SavedState {
|
||||||
|
@ -44,7 +46,7 @@ struct LiveState {
|
||||||
turtles: Vec<Arc<RwLock<turtle::Turtle>>>,
|
turtles: Vec<Arc<RwLock<turtle::Turtle>>>,
|
||||||
tasks: Vec<VecDeque<()>>,
|
tasks: Vec<VecDeque<()>>,
|
||||||
world: blocks::World,
|
world: blocks::World,
|
||||||
depots: Mutex<Vec<Arc<Mutex<Position>>>>,
|
depots: Depots,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LiveState {
|
impl LiveState {
|
||||||
|
@ -53,10 +55,7 @@ impl LiveState {
|
||||||
for turtle in self.turtles.iter() {
|
for turtle in self.turtles.iter() {
|
||||||
turtles.push(turtle.read().await.info());
|
turtles.push(turtle.read().await.info());
|
||||||
};
|
};
|
||||||
let mut depots = Vec::new();
|
let depots = self.depots.clone().to_vec().await;
|
||||||
for depot in self.depots.lock().await.iter() {
|
|
||||||
depots.push(*depot.lock().await);
|
|
||||||
};
|
|
||||||
SavedState { turtles, world: self.world.tree().await, depots }
|
SavedState { turtles, world: self.world.tree().await, depots }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,12 +65,10 @@ impl LiveState {
|
||||||
let (tx, rx) = mpsc::channel(1);
|
let (tx, rx) = mpsc::channel(1);
|
||||||
turtles.push(Turtle::with_channel(turtle.name.to_num(), turtle.position, turtle.fuel, turtle.fuel_limit, tx, rx));
|
turtles.push(Turtle::with_channel(turtle.name.to_num(), turtle.position, turtle.fuel, turtle.fuel_limit, tx, rx));
|
||||||
};
|
};
|
||||||
let mut depots = Vec::new();
|
let depots = Depots::from_vec(save.depots);
|
||||||
for depot in save.depots {
|
|
||||||
depots.push(Arc::new(Mutex::new(depot)));
|
|
||||||
}
|
|
||||||
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: Vec::new(), world: World::from_tree(save.world),
|
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: Vec::new(), world: World::from_tree(save.world),
|
||||||
depots: Mutex::new(depots)
|
depots,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
80
server/src/tasks.rs
Normal file
80
server/src/tasks.rs
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use erased_serde::serialize_trait_object;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::sync::{Mutex, MutexGuard, RwLock, OwnedMutexGuard};
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
|
use crate::LiveState;
|
||||||
|
use crate::{turtle::{self, TurtleCommander}, blocks::Position};
|
||||||
|
|
||||||
|
#[typetag::serde(tag = "type")]
|
||||||
|
trait Task {
|
||||||
|
/// Execute the task
|
||||||
|
fn run(&self, turtle: TurtleCommander) -> JoinHandle<()>;
|
||||||
|
/// Return Some if the task should be scheduled
|
||||||
|
fn poll(&self) -> Option<Position>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct Scheduler {
|
||||||
|
#[serde(skip)]
|
||||||
|
turtles: Vec<(TurtleCommander, Option<JoinHandle<()>>)>,
|
||||||
|
tasks: Vec<Box<dyn Task>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Scheduler {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
turtles: Vec::new(),
|
||||||
|
tasks: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler {
|
||||||
|
/// Add a new turtle to the scheduler
|
||||||
|
/// Whether or not the turtle is already in the scheduler is not verified
|
||||||
|
fn add_turtle(&mut self, turtle: &TurtleCommander) {
|
||||||
|
self.turtles.push((
|
||||||
|
turtle.clone(),
|
||||||
|
None
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_task(&mut self, task: Box<dyn Task>) {
|
||||||
|
self.tasks.push(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn poll(&mut self) {
|
||||||
|
for turtle in &mut self.turtles {
|
||||||
|
if let Some(join) = &turtle.1 {
|
||||||
|
if join.is_finished() {
|
||||||
|
turtle.1 = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut free_turtles: Vec<&mut (TurtleCommander, Option<JoinHandle<()>>)> =
|
||||||
|
self.turtles.iter_mut().filter(|t| t.1.is_none()).collect();
|
||||||
|
|
||||||
|
let mut turtle_positions = Vec::new();
|
||||||
|
for turtle in &free_turtles {
|
||||||
|
turtle_positions.push(turtle.0.pos().await);
|
||||||
|
}
|
||||||
|
|
||||||
|
for task in &mut self.tasks {
|
||||||
|
if let Some(position) = task.poll() {
|
||||||
|
let closest_turtle = match free_turtles.iter_mut().zip(turtle_positions.iter()).min_by_key( |(_,p)| {
|
||||||
|
p.manhattan(position)
|
||||||
|
}) {
|
||||||
|
Some(turtle) => turtle.0,
|
||||||
|
None => break,
|
||||||
|
};
|
||||||
|
|
||||||
|
closest_turtle.1 = Some(task.run(closest_turtle.0.clone()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ use crate::blocks::Direction;
|
||||||
use crate::blocks::Position;
|
use crate::blocks::Position;
|
||||||
use crate::blocks::Vec3;
|
use crate::blocks::Vec3;
|
||||||
use crate::blocks::World;
|
use crate::blocks::World;
|
||||||
|
use crate::depot::Depots;
|
||||||
use crate::paths::route_facing;
|
use crate::paths::route_facing;
|
||||||
|
|
||||||
use anyhow::Ok;
|
use anyhow::Ok;
|
||||||
|
@ -12,6 +13,7 @@ use anyhow::Context;
|
||||||
use log::trace;
|
use log::trace;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use log::info;
|
use log::info;
|
||||||
|
use tokio::sync::Mutex;
|
||||||
use tokio::sync::OnceCell;
|
use tokio::sync::OnceCell;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
|
@ -141,6 +143,7 @@ impl Turtle {
|
||||||
pub struct TurtleCommander {
|
pub struct TurtleCommander {
|
||||||
sender: Arc<Sender>,
|
sender: Arc<Sender>,
|
||||||
world: World,
|
world: World,
|
||||||
|
depots: Depots,
|
||||||
// everything below is best-effort
|
// everything below is best-effort
|
||||||
// TODO: make not bad
|
// TODO: make not bad
|
||||||
pos: Arc<RwLock<Position>>,
|
pos: Arc<RwLock<Position>>,
|
||||||
|
@ -160,6 +163,7 @@ impl TurtleCommander {
|
||||||
fuel: Arc::new(AtomicUsize::new(turtle.fuel)),
|
fuel: Arc::new(AtomicUsize::new(turtle.fuel)),
|
||||||
max_fuel: Arc::new(AtomicUsize::new(turtle.fuel_limit)),
|
max_fuel: Arc::new(AtomicUsize::new(turtle.fuel_limit)),
|
||||||
name: Arc::new(OnceCell::new_with(Some(turtle.name))),
|
name: Arc::new(OnceCell::new_with(Some(turtle.name))),
|
||||||
|
depots: state.depots.clone(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue