1
Fork 0
This commit is contained in:
Andy Killorin 2023-12-22 16:35:30 -06:00
parent ee39602e03
commit dbbc41705e
Signed by: ank
GPG key ID: B6241CA3B552BCA4
9 changed files with 202 additions and 81 deletions

View file

@ -27,6 +27,7 @@ rstar = { version = "0.11.0", features = ["serde"] }
rustmatica = "0.1.1"
serde = { version = "1.0.193", features = ["rc", "derive"] }
serde_json = "1.0.108"
time = { version = "0.3.31", features = ["serde"] }
tokio = { version = "1.0", features = ["full"] }
tower = { version = "0.4", features = ["util", "timeout", "load-shed", "limit"] }
tower-http = { version = "0.5.0", features = [

View file

@ -14,7 +14,7 @@ local: surnames.txt names.txt ipaddr.txt
global: surnames.txt names.txt ipaddr.txt
make -B ipaddr.txt
mkdir -p prod
cd prod; cargo run --release $(PRODPORT) $(PRODFILE)
cargo run --release $(PRODPORT) $(PRODFILE)
surnames.txt:
curl https://raw.githubusercontent.com/Hyneman/moby-project/672f6bdca054c42d375f065ffee87e8ceba0c242/moby/mwords/21986na.mes |\

View file

@ -38,15 +38,15 @@ impl Depots {
// refuel
turtle.execute(Select(1)).await;
let limit = turtle.fuel_limit().await;
while turtle.fuel().await < limit {
let limit = turtle.fuel_limit();
while turtle.fuel() < 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 {
warn!("only received {} fuel", turtle.fuel());
if turtle.fuel() > 5000 {
break;
} else {
turtle.execute(Wait(15)).await;
@ -58,7 +58,7 @@ impl Depots {
drop(depot); // assumes that the turtle will very quickly leave
Some(turtle.fuel().await)
Some(turtle.fuel())
}
pub fn from_vec(vec: Vec<Position>) -> Self {

View file

@ -1,5 +1,76 @@
use crate::{blocks::Vec3, turtle::TurtleCommander};
use serde::{Serialize, Deserialize};
use time::OffsetDateTime;
use tokio::task::JoinHandle;
use typetag::serde;
pub async fn fell(turtle: TurtleCommander, corner: Vec3) -> Option<()> {
use crate::{blocks::{Vec3, Position, Direction}, turtle::TurtleCommander, tasks::Task, depot::Depots, mine::fill};
pub async fn fell_tree(turtle: TurtleCommander, bottom: Vec3) -> Option<()> {
let mut log = bottom;
loop {
let near = turtle.goto_adjacent(log).await?;
if turtle.world().get(log).await.is_some_and(|b| !b.name.contains("log")) {
break;
}
turtle.execute(near.dig(log)?).await;
log += Vec3::y();
}
Some(())
}
/// Minutes before checking
const SWEEP_DELAY: usize = 16;
#[derive(Serialize, Deserialize, Clone)]
struct TreeFarm {
position: Vec3,
size: Vec3,
last_sweep: OffsetDateTime,
}
impl TreeFarm {
pub async fn sweep(&self, turtle: TurtleCommander) -> Option<()> {
let trees = self.size.x * self.size.y * self.size.z;
turtle.dock().await;
for tree in 0..trees {
let index = fill(self.size, tree);
let offset = index.component_mul(&Vec3::new(2, 32, 2));
let tree = self.position + offset;
fell_tree(turtle.clone(), tree).await?;
}
Some(())
}
pub async fn build(&self, turtle: TurtleCommander) -> Option<()> {
let trees = self.size.x * self.size.y * self.size.z;
turtle.dock().await;
for tree in 0..trees {
let index = fill(self.size, tree);
let offset = index.component_mul(&Vec3::new(2, 32, 2));
let tree = self.position + offset;
// TODO: item management
}
Some(())
}
}
#[serde]
impl Task for TreeFarm {
fn run(&mut self,turtle:TurtleCommander) -> JoinHandle<()> {
let frozen = self.clone();
tokio::spawn(async move {
frozen.sweep(turtle).await.unwrap();
})
}
fn poll(&mut self) -> Option<Position> {
let elapsed = OffsetDateTime::now_utc() - self.last_sweep;
if elapsed.whole_minutes() <= 16 {
return None;
}
self.last_sweep = OffsetDateTime::now_utc();
Some(Position::new(self.position, Direction::North)) // request a turtle
}
}

View file

@ -14,6 +14,7 @@ use log::info;
use rstar::RTree;
use names::Name;
use tasks::Scheduler;
use tokio::{sync::{
RwLock, mpsc, OnceCell, Mutex
}, fs};
@ -34,50 +35,6 @@ mod turtle_api;
mod tasks;
mod depot;
#[derive(Serialize, Deserialize)]
struct SavedState {
turtles: Vec<turtle::Turtle>,
world: RTree<Block>,
depots: Vec<Position>,
//chunkloaders: unimplemented!(),
}
struct LiveState {
turtles: Vec<Arc<RwLock<turtle::Turtle>>>,
tasks: Vec<VecDeque<()>>,
world: blocks::World,
depots: Depots,
}
impl LiveState {
async fn save(&self) -> SavedState {
let mut turtles = Vec::new();
for turtle in self.turtles.iter() {
turtles.push(turtle.read().await.info());
};
let depots = self.depots.clone().to_vec().await;
SavedState { turtles, world: self.world.tree().await, depots }
}
fn from_save(save: SavedState) -> Self {
let mut turtles = Vec::new();
for turtle in save.turtles.into_iter() {
let (tx, rx) = mpsc::channel(1);
turtles.push(Turtle::with_channel(turtle.name.to_num(), turtle.position, turtle.fuel, turtle.fuel_limit, tx, rx));
};
let depots = Depots::from_vec(save.depots);
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: Vec::new(), world: World::from_tree(save.world),
depots,
}
}
async fn get_turtle(&self, name: u32) -> Option<TurtleCommander> {
TurtleCommander::new(Name::from_num(name), self).await
}
}
static PORT: OnceCell<u16> = OnceCell::const_new();
static SAVE: OnceCell<path::PathBuf> = OnceCell::const_new();
@ -99,8 +56,6 @@ async fn main() -> Result<(), Error> {
let state = read_from_disk().await?;
let state = LiveState::from_save(state);
let state = SharedControl::new(RwLock::new(state));
let server = Router::new()
@ -115,7 +70,7 @@ async fn main() -> Result<(), Error> {
let server = safe_kill::serve(server, listener).await;
info!("writing");
write_to_disk(state.read().await.save().await).await?;
write_to_disk(&*state.read().await).await?;
server.closed().await;
@ -123,23 +78,29 @@ async fn main() -> Result<(), Error> {
}
async fn flush(State(state): State<SharedControl>) -> &'static str {
write_to_disk(state.read().await.save().await).await.unwrap();
write_to_disk(&*state.read().await).await.unwrap();
"ACK"
}
async fn write_to_disk(state: SavedState) -> anyhow::Result<()> {
async fn write_to_disk(state: &LiveState) -> anyhow::Result<()> {
let tasks = &state.tasks;
let state = state.save().await;
let turtles = serde_json::to_string_pretty(&state.turtles)?;
let world = bincode::serialize(&state.world)?;
let depots = serde_json::to_string_pretty(&state.depots)?;
let tasks = serde_json::to_string_pretty(tasks)?;
let path = &SAVE.get().unwrap();
tokio::fs::write(path.join("turtles.json"), turtles).await?;
tokio::fs::write(path.join("depots.json"), depots).await?;
tokio::fs::write(path.join("tasks.json"), tasks).await?;
tokio::fs::write(path.join("world.bin"), world).await?;
Ok(())
}
async fn read_from_disk() -> anyhow::Result<SavedState> {
async fn read_from_disk() -> anyhow::Result<LiveState> {
let turtles = match tokio::fs::OpenOptions::new()
.read(true)
.open(SAVE.get().unwrap().join("turtles.json"))
@ -164,6 +125,18 @@ async fn read_from_disk() -> anyhow::Result<SavedState> {
},
};
let scheduler = match tokio::fs::OpenOptions::new()
.read(true)
.open(SAVE.get().unwrap().join("scheduler.json"))
.await
{
tokio::io::Result::Ok(file) => serde_json::from_reader(file.into_std().await)?,
tokio::io::Result::Err(e) => match e.kind() {
ErrorKind::NotFound => Default::default(),
_ => panic!(),
},
};
let world = match tokio::fs::OpenOptions::new()
.read(true).open(SAVE.get().unwrap().join("world.bin")).await {
tokio::io::Result::Ok(file) => bincode::deserialize_from(file.into_std().await)?,
@ -174,9 +147,61 @@ async fn read_from_disk() -> anyhow::Result<SavedState> {
};
Ok(SavedState {
let saved = SavedState {
turtles,
world,
depots,
})
};
let mut live = LiveState::from_save(saved, scheduler);
for turtle in live.turtles.iter() {
live.tasks.add_turtle(&TurtleCommander::new(turtle.read().await.name,&live).await.unwrap())
}
Ok(live)
}
#[derive(Serialize, Deserialize)]
struct SavedState {
turtles: Vec<turtle::Turtle>,
world: RTree<Block>,
depots: Vec<Position>,
//chunkloaders: unimplemented!(),
}
struct LiveState {
turtles: Vec<Arc<RwLock<turtle::Turtle>>>,
tasks: Scheduler,
world: blocks::World,
depots: Depots,
}
impl LiveState {
async fn save(&self) -> SavedState {
let mut turtles = Vec::new();
for turtle in self.turtles.iter() {
turtles.push(turtle.read().await.info());
};
let depots = self.depots.clone().to_vec().await;
SavedState { turtles, world: self.world.tree().await, depots }
}
fn from_save(save: SavedState, scheduler: Scheduler) -> Self {
let mut turtles = Vec::new();
for turtle in save.turtles.into_iter() {
let (tx, rx) = mpsc::channel(1);
turtles.push(Turtle::with_channel(turtle.name.to_num(), turtle.position, turtle.fuel, turtle.fuel_limit, tx, rx));
};
let depots = Depots::from_vec(save.depots);
Self { turtles: turtles.into_iter().map(|t| Arc::new(RwLock::new(t))).collect(), tasks: scheduler, world: World::from_tree(save.world),
depots,
}
}
async fn get_turtle(&self, name: u32) -> Option<TurtleCommander> {
TurtleCommander::new(Name::from_num(name), self).await
}
}

View file

@ -25,8 +25,8 @@ pub async fn mine(turtle: TurtleCommander, pos: Vec3, fuel: Position, storage: P
let mut valuables = Vec::new();
async fn refuel_needed(turtle: &TurtleCommander, volume: i32, fuel: Position) -> Option<()> {
Some(if (turtle.fuel().await as f64) < (2 * volume + (fuel.pos-turtle.pos().await.pos).abs().sum()) as f64 * 1.8 {
let name = turtle.name().await.to_str();
Some(if (turtle.fuel() as f64) < (2 * volume + (fuel.pos-turtle.pos().await.pos).abs().sum()) as f64 * 1.8 {
let name = turtle.name().to_str();
info!("{name}: refueling");
turtle.goto(fuel).await?;
info!("{name}: docked");
@ -94,14 +94,14 @@ pub async fn mine_chunk(turtle: TurtleCommander, pos: Vec3, chunk: Vec3) -> Opti
async fn refuel(turtle: TurtleCommander) {
turtle.execute(Select(16)).await;
turtle.execute(DropUp(64)).await;
let limit = turtle.fuel_limit().await;
while turtle.fuel().await < limit {
let limit = turtle.fuel_limit();
while turtle.fuel() < limit {
turtle.execute(SuckFront(64)).await;
let re = turtle.execute(Refuel).await;
if let TurtleCommandResponse::Failure = re.ret {
// partial refuel, good enough
warn!("only received {} fuel", turtle.fuel().await);
if turtle.fuel().await > 5000 {
warn!("only received {} fuel", turtle.fuel());
if turtle.fuel() > 5000 {
break;
} else {
turtle.execute(Wait(15)).await;
@ -148,7 +148,7 @@ fn step(n: i32, x: i32) -> i32 {
}
/// generates a sequence of adjacent positions within a volume
fn fill(scale: Vec3, n: i32) -> Vec3 {
pub fn fill(scale: Vec3, n: i32) -> Vec3 {
assert!(n < scale.x * scale.y * scale.z);
Vec3::new(
step(n,scale.x),

View file

@ -6,18 +6,19 @@ use tokio::sync::{Mutex, MutexGuard, RwLock, OwnedMutexGuard};
use tokio::task::JoinHandle;
use crate::LiveState;
use crate::names::Name;
use crate::{turtle::{self, TurtleCommander}, blocks::Position};
#[typetag::serde(tag = "type")]
trait Task {
pub trait Task: Send + Sync {
/// Execute the task
fn run(&self, turtle: TurtleCommander) -> JoinHandle<()>;
fn run(&mut self, turtle: TurtleCommander) -> JoinHandle<()>;
/// Return Some if the task should be scheduled
fn poll(&self) -> Option<Position>;
fn poll(&mut self) -> Option<Position>;
}
#[derive(Serialize, Deserialize)]
struct Scheduler {
pub struct Scheduler {
#[serde(skip)]
turtles: Vec<(TurtleCommander, Option<JoinHandle<()>>)>,
tasks: Vec<Box<dyn Task>>,
@ -35,18 +36,18 @@ impl Default for Scheduler {
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) {
pub fn add_turtle(&mut self, turtle: &TurtleCommander) {
self.turtles.push((
turtle.clone(),
None
));
}
fn add_task(&mut self, task: Box<dyn Task>) {
pub fn add_task(&mut self, task: Box<dyn Task>) {
self.tasks.push(task);
}
async fn poll(&mut self) {
pub async fn poll(&mut self) {
for turtle in &mut self.turtles {
if let Some(join) = &turtle.1 {
if join.is_finished() {
@ -77,4 +78,9 @@ impl Scheduler {
}
}
pub async fn cancel(&mut self, turtle: Name) -> Option<()> {
self.turtles.iter_mut().find(|t| t.0.name() == turtle)?.1.take()?.abort();
Some(())
}
}

View file

@ -167,6 +167,18 @@ impl TurtleCommander {
})
}
pub fn with_turtle(turtle: &Turtle, state: &LiveState) -> TurtleCommander {
TurtleCommander {
sender: turtle.sender.as_ref().unwrap().clone(),
world: state.world.clone(),
pos: Arc::new(RwLock::new(turtle.position)),
fuel: Arc::new(AtomicUsize::new(turtle.fuel)),
max_fuel: Arc::new(AtomicUsize::new(turtle.fuel_limit)),
name: Arc::new(OnceCell::new_with(Some(turtle.name))),
depots: state.depots.clone(),
}
}
pub async fn execute(&self, command: TurtleCommand) -> TurtleInfo {
let (send, recv) = oneshot::channel::<TurtleInfo>();
@ -179,7 +191,7 @@ impl TurtleCommander {
resp
}
pub async fn name(&self) -> Name {
pub fn name(&self) -> Name {
self.name.get().unwrap().clone()
}
@ -187,11 +199,11 @@ impl TurtleCommander {
self.pos.read().await.clone()
}
pub async fn fuel(&self) -> usize {
pub fn fuel(&self) -> usize {
self.fuel.load(std::sync::atomic::Ordering::SeqCst)
}
pub async fn fuel_limit(&self) -> usize {
pub fn fuel_limit(&self) -> usize {
self.max_fuel.load(std::sync::atomic::Ordering::SeqCst)
}
@ -199,6 +211,10 @@ impl TurtleCommander {
self.world.clone()
}
pub async fn dock(&self) -> Option<usize> {
Depots::dock(&self.depots, self.to_owned()).await
}
pub async fn goto(&self, pos: Position) -> Option<()> {
let mut recent = self.pos().await;
let world = self.world.clone();

View file

@ -1,6 +1,7 @@
use tokio;
use blocks::Vec3;
use crate::turtle::TurtleCommandResponse;
use crate::turtle::TurtleCommander;
use crate::turtle::TurtleInfo;
use axum::extract::Path;
use crate::turtle::TurtleCommand;
@ -45,12 +46,13 @@ pub(crate) async fn create_turtle(
let state = &mut state.write().await;
let id = state.turtles.len() as u32;
let (send, receive) = mpsc::channel(1);
let turtle = turtle::Turtle::with_channel(id, Position::new(req.position, req.facing), req.fuel, req.fuellimit, send,receive);
let commander = TurtleCommander::with_turtle(&turtle, state);
state.tasks.add_turtle(&commander);
state.turtles.push(
Arc::new(RwLock::new(
turtle::Turtle::with_channel(id, Position::new(req.position, req.facing), req.fuel, req.fuellimit, send,receive)
turtle
)));
state.tasks.push(VecDeque::new());
info!("new turtle: {id}");
@ -115,7 +117,7 @@ pub(crate) async fn cancel(
Path(id): Path<u32>,
State(state): State<SharedControl>,
) -> &'static str {
state.write().await.tasks[id as usize].pop_front();
//state.write().await.tasks
"ACK"
}