allow turtles to control one another (with even more mutexes)
This commit is contained in:
parent
e4b7da9527
commit
732435d3d5
5 changed files with 99 additions and 27 deletions
|
@ -68,6 +68,14 @@ local function restartfront()
|
||||||
return front.isOn()
|
return front.isOn()
|
||||||
end
|
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()
|
local function inventoryinfo()
|
||||||
return { ["Inventory"] = peripheral.wrap("front").list() }
|
return { ["Inventory"] = peripheral.wrap("front").list() }
|
||||||
end
|
end
|
||||||
|
@ -143,6 +151,8 @@ local commands = {
|
||||||
["CycleFront"] = restartfront,
|
["CycleFront"] = restartfront,
|
||||||
["Poweroff"] = os.shutdown,
|
["Poweroff"] = os.shutdown,
|
||||||
["GetFuelLimit"] = turtle.getFuelLimit,
|
["GetFuelLimit"] = turtle.getFuelLimit,
|
||||||
|
["Name"] = os.computerLabel,
|
||||||
|
["NameFront"] = namefront,
|
||||||
};
|
};
|
||||||
|
|
||||||
if not ipaddr then
|
if not ipaddr then
|
||||||
|
|
|
@ -51,29 +51,8 @@ impl Depots {
|
||||||
trace!("depot at {:?}", depot.position());
|
trace!("depot at {:?}", depot.position());
|
||||||
turtle.goto(*depot.position()).await?;
|
turtle.goto(*depot.position()).await?;
|
||||||
|
|
||||||
// dump inventory
|
dump(&turtle).await;
|
||||||
for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) {
|
refuel(&turtle).await;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This can fail, we don't really care (as long as it executes once)
|
// This can fail, we don't really care (as long as it executes once)
|
||||||
turtle.execute(Backward(4)).await;
|
turtle.execute(Backward(4)).await;
|
||||||
|
@ -82,7 +61,7 @@ impl Depots {
|
||||||
|
|
||||||
// lava bucket fix
|
// lava bucket fix
|
||||||
for (i, _) in turtle.inventory().await.into_iter().enumerate().filter(|(_,n)| n.is_some()) {
|
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;
|
turtle.execute(DropDown(64)).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,3 +94,30 @@ impl Depots {
|
||||||
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use serde::{Serialize, Deserialize};
|
||||||
use tokio::{task::{JoinHandle, AbortHandle}, sync::RwLock};
|
use tokio::{task::{JoinHandle, AbortHandle}, sync::RwLock};
|
||||||
use typetag::serde;
|
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::*;
|
use TurtleCommand::*;
|
||||||
|
|
||||||
/// Things to leave in the field (not worth fuel)
|
/// Things to leave in the field (not worth fuel)
|
||||||
|
@ -86,7 +86,7 @@ async fn devore(turtle: &TurtleCommander) {
|
||||||
let depot = turtle.get_depot().await;
|
let depot = turtle.get_depot().await;
|
||||||
|
|
||||||
for i in turtles {
|
for i in turtles {
|
||||||
let position = depot.position();
|
let position = depot.position().clone();
|
||||||
|
|
||||||
let staging = position.pos - position.dir.unit();
|
let staging = position.pos - position.dir.unit();
|
||||||
|
|
||||||
|
@ -94,7 +94,30 @@ async fn devore(turtle: &TurtleCommander) {
|
||||||
warn!("devoring {i}");
|
warn!("devoring {i}");
|
||||||
turtle.execute(Select(i)).await;
|
turtle.execute(Select(i)).await;
|
||||||
turtle.execute(Place).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 {
|
loop {
|
||||||
let ret = turtle.execute(Wait(3)).await;
|
let ret = turtle.execute(Wait(3)).await;
|
||||||
// this won't do well with dead (energy-lacking) turtles, perhaps obtaining
|
// this won't do well with dead (energy-lacking) turtles, perhaps obtaining
|
||||||
|
@ -106,6 +129,7 @@ async fn devore(turtle: &TurtleCommander) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
warn!("devored turtle still inactive");
|
warn!("devored turtle still inactive");
|
||||||
|
turtle.execute(CycleFront).await; // rebot child
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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<()> {
|
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() {
|
if let Some(task) = self.turtles.iter_mut().find(|t| t.0.name() == turtle)?.1.as_ref() {
|
||||||
task.abort();
|
task.abort();
|
||||||
|
|
|
@ -12,6 +12,7 @@ use anyhow::Ok;
|
||||||
|
|
||||||
use anyhow;
|
use anyhow;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use tokio::sync::OwnedMutexGuard;
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
use tracing::warn;
|
use tracing::warn;
|
||||||
|
@ -256,6 +257,10 @@ impl TurtleCommander {
|
||||||
self.world.clone()
|
self.world.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn scheduler(&self) -> OwnedMutexGuard<Scheduler> {
|
||||||
|
self.tasks.clone().lock_owned().await
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn inventory(&self) -> Vec<Option<InventorySlot>> {
|
pub async fn inventory(&self) -> Vec<Option<InventorySlot>> {
|
||||||
let mut inventory = self.inventory.write().await;
|
let mut inventory = self.inventory.write().await;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue