1
Fork 0

allow turtles to control one another (with even more mutexes)

This commit is contained in:
Andy Killorin 2023-12-28 11:54:57 -06:00
parent e4b7da9527
commit 732435d3d5
Signed by: ank
GPG key ID: B6241CA3B552BCA4
5 changed files with 99 additions and 27 deletions

View file

@ -68,6 +68,14 @@ local function restartfront()
return front.isOn()
end
local function namefront()
local front = peripheral.wrap("front")
if not front or not front.shutdown then
return false
end
return front.getLabel()
end
local function inventoryinfo()
return { ["Inventory"] = peripheral.wrap("front").list() }
end
@ -143,6 +151,8 @@ local commands = {
["CycleFront"] = restartfront,
["Poweroff"] = os.shutdown,
["GetFuelLimit"] = turtle.getFuelLimit,
["Name"] = os.computerLabel,
["NameFront"] = namefront,
};
if not ipaddr then

View file

@ -51,29 +51,8 @@ impl Depots {
trace!("depot at {:?}", depot.position());
turtle.goto(*depot.position()).await?;
// dump inventory
for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) {
turtle.execute(Select((i+1) as u32)).await;
turtle.execute(DropDown(64)).await;
}
// refuel
turtle.execute(Select(1)).await;
let limit = turtle.fuel_limit();
while turtle.fuel() + 1000 < 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());
if turtle.fuel() > 1500 {
break;
} else {
turtle.execute(Wait(15)).await;
}
}
}
dump(&turtle).await;
refuel(&turtle).await;
// This can fail, we don't really care (as long as it executes once)
turtle.execute(Backward(4)).await;
@ -82,7 +61,7 @@ impl Depots {
// lava bucket fix
for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) {
turtle.execute(Select(i as u32)).await;
turtle.execute(Select((i+1) as u32)).await;
turtle.execute(DropDown(64)).await;
}
@ -115,3 +94,30 @@ impl Depots {
depots
}
}
pub async fn dump(turtle: &TurtleCommander) {
for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) {
turtle.execute(Select((i+1) as u32)).await;
turtle.execute(DropDown(64)).await;
}
}
pub async fn refuel(turtle: &TurtleCommander) {
turtle.execute(Select(1)).await;
let limit = turtle.fuel_limit();
while turtle.fuel() + 1000 < 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());
if turtle.fuel() > 1500 {
break;
} else {
turtle.execute(Wait(15)).await;
}
}
}
}

View file

@ -6,7 +6,7 @@ use serde::{Serialize, Deserialize};
use tokio::{task::{JoinHandle, AbortHandle}, sync::RwLock};
use typetag::serde;
use crate::{blocks::{Position, Vec3, Direction}, turtle::{TurtleCommand, TurtleCommander, TurtleCommandResponse, InventorySlot}, paths::TRANSPARENT, tasks::{Task, TaskState}};
use crate::{blocks::{Position, Vec3, Direction}, turtle::{TurtleCommand, TurtleCommander, TurtleCommandResponse, InventorySlot}, paths::TRANSPARENT, tasks::{Task, TaskState}, names::Name, depot};
use TurtleCommand::*;
/// Things to leave in the field (not worth fuel)
@ -86,7 +86,7 @@ async fn devore(turtle: &TurtleCommander) {
let depot = turtle.get_depot().await;
for i in turtles {
let position = depot.position();
let position = depot.position().clone();
let staging = position.pos - position.dir.unit();
@ -94,7 +94,30 @@ async fn devore(turtle: &TurtleCommander) {
warn!("devoring {i}");
turtle.execute(Select(i)).await;
turtle.execute(Place).await;
turtle.execute(CycleFront).await;
loop { // cancel the task of the turtle ahead so that it doesn't go wild in the depot
if let TurtleCommandResponse::Name(name) = turtle.execute(NameFront).await.ret {
match Name::from_str(&name) {
Ok(name) => {
let mut scheduler = turtle.scheduler().await;
scheduler.cancel(name).await;
scheduler.do_on(move |turtle| tokio::spawn(async move {
depot::dump(&turtle).await;
depot::refuel(&turtle).await;
// *teleports behind you*
turtle.goto(Position::new(staging - position.dir.unit(), position.dir)).await;
}).abort_handle(), name).unwrap();
break;
},
Err(_) => error!("bad turtle name: {name}"),
}
} else {
error!("could not get name");
}
}
turtle.execute(CycleFront).await; // boot child
loop {
let ret = turtle.execute(Wait(3)).await;
// this won't do well with dead (energy-lacking) turtles, perhaps obtaining
@ -106,6 +129,7 @@ async fn devore(turtle: &TurtleCommander) {
break;
}
warn!("devored turtle still inactive");
turtle.execute(CycleFront).await; // rebot child
}
}
}

View file

@ -120,6 +120,33 @@ impl Scheduler {
});
}
// TODO: make awaiting this feasible
pub fn do_on<T>(&mut self, mut task: T, turtle: Name) -> Option<()>
where T: FnMut(TurtleCommander) -> AbortHandle
{
let turtle = self.turtles.iter_mut().filter(|t| t.0.name() == turtle).next()?;
match turtle.1 {
Some(_) => None,
None => {
trace!("new adhoc task on {}", turtle.0.name().to_str());
turtle.1 = Some(task(turtle.0.clone()));
Some(())
},
}
}
pub fn task_on(&mut self, mut task: Box<dyn Task>, turtle: Name) -> Option<()> {
trace!("new {} task on {}", task.typetag_name(), turtle.clone().to_str());
let turtle = self.turtles.iter_mut().filter(|t| t.0.name() == turtle).next()?;
match turtle.1 {
Some(_) => None,
None => {
turtle.1 = Some(task.run(turtle.0.clone()));
Some(())
},
}
}
pub async fn cancel(&mut self, turtle: Name) -> Option<()> {
if let Some(task) = self.turtles.iter_mut().find(|t| t.0.name() == turtle)?.1.as_ref() {
task.abort();

View file

@ -12,6 +12,7 @@ use anyhow::Ok;
use anyhow;
use anyhow::Context;
use tokio::sync::OwnedMutexGuard;
use tracing::error;
use tracing::trace;
use tracing::warn;
@ -256,6 +257,10 @@ impl TurtleCommander {
self.world.clone()
}
pub async fn scheduler(&self) -> OwnedMutexGuard<Scheduler> {
self.tasks.clone().lock_owned().await
}
pub async fn inventory(&self) -> Vec<Option<InventorySlot>> {
let mut inventory = self.inventory.write().await;