1
Fork 0

extracted turtle_api

This commit is contained in:
Andy Killorin 2023-12-22 10:11:14 -06:00
parent 058e12ef23
commit 85c32e48af
Signed by: ank
GPG key ID: B6241CA3B552BCA4
2 changed files with 182 additions and 150 deletions

View file

@ -4,11 +4,11 @@ use std::{collections::VecDeque, io::ErrorKind, sync::Arc, env::args, path};
use anyhow::{Error, Ok}; use anyhow::{Error, Ok};
use axum::{ use axum::{
extract::{Path, State}, extract::{State},
routing::{get, post}, routing::{get},
Json, Router, Router,
}; };
use blocks::{World, Position, Vec3}; use blocks::{World, };
use log::info; use log::info;
use rstar::RTree; use rstar::RTree;
@ -16,7 +16,7 @@ use names::Name;
use tokio::{sync::{ use tokio::{sync::{
RwLock, mpsc, OnceCell RwLock, mpsc, OnceCell
}, fs}; }, fs};
use turtle::{Turtle, TurtleInfo, TurtleCommand, TurtleCommander, TurtleCommandResponse}; use turtle::{Turtle, TurtleCommander};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use indoc::formatdoc; use indoc::formatdoc;
@ -29,6 +29,8 @@ mod fell;
mod paths; mod paths;
mod safe_kill; mod safe_kill;
mod turtle; mod turtle;
mod turtle_api;
mod tasks;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
struct SavedState { struct SavedState {
@ -93,17 +95,9 @@ async fn main() -> Result<(), Error> {
let state = SharedControl::new(RwLock::new(state)); let state = SharedControl::new(RwLock::new(state));
let server = Router::new() let server = Router::new()
.route("/turtle/new", post(create_turtle))
.route("/turtle/:id/update", post(command))
.route("/turtle/client.lua", get(client))
.route("/turtle/:id/setGoal", post(set_goal))
.route("/turtle/:id/dig", post(dig))
.route("/turtle/:id/cancelTask", post(cancel))
.route("/turtle/:id/manual", post(run_command))
.route("/turtle/:id/info", get(turtle_info))
//.route("/turtle/:id/placeUp", get(place_up)) //.route("/turtle/:id/placeUp", get(place_up))
.route("/turtle/updateAll", get(update_turtles))
.route("/flush", get(flush)) .route("/flush", get(flush))
.nest("/turtle", turtle_api::turtle_api())
.with_state(state.clone()); .with_state(state.clone());
let listener = tokio::net::TcpListener::bind(("0.0.0.0", *PORT.get().unwrap())) let listener = tokio::net::TcpListener::bind(("0.0.0.0", *PORT.get().unwrap()))
@ -161,139 +155,3 @@ async fn read_from_disk() -> anyhow::Result<SavedState> {
world, world,
}) })
} }
async fn create_turtle(
State(state): State<SharedControl>,
Json(req): Json<turtle::TurtleRegister>,
) -> Json<turtle::TurtleResponse> {
let state = &mut state.write().await;
let id = state.turtles.len() as u32;
let (send, receive) = mpsc::channel(1);
state.turtles.push(
Arc::new(RwLock::new(
turtle::Turtle::with_channel(id, Position::new(req.position, req.facing), req.fuel, req.fuellimit, send,receive)
)));
state.tasks.push(VecDeque::new());
info!("new turtle: {id}");
Json(turtle::TurtleResponse {
name: Name::from_num(id).to_str(),
id,
command: turtle::TurtleCommand::Update,
})
}
async fn place_up(
Path(id): Path<u32>,
State(state): State<SharedControl>,
) -> Json<TurtleInfo> {
let turtle = state.read().await.get_turtle(id).await.unwrap();
let response = turtle.execute(turtle::TurtleCommand::PlaceUp).await;
Json(response)
}
async fn run_command(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<TurtleCommand>,
) -> Json<TurtleCommandResponse> {
let state = state.read().await;
let commander = state.get_turtle(id).await.unwrap().clone();
drop(state);
Json(commander.execute(req).await.ret)
}
async fn dig(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<(Vec3, Position, Position)>,
) -> &'static str {
let turtle = state.read().await.get_turtle(id).await.unwrap();
let (req, fuel, inventory) = req;
//let fuel = Position::new(Vec3::new(-19, 93, 73), blocks::Direction::East);
//let inventory = Position::new(Vec3::new(-19, 92, 73), blocks::Direction::East);
tokio::spawn(
async move {
mine::mine(turtle.clone(), req, fuel, inventory).await
}
);
"ACK"
}
async fn set_goal(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<Position>,
) -> &'static str {
let turtle = state.read().await.get_turtle(id).await.unwrap();
tokio::spawn(async move {turtle.goto(req).await.expect("route failed")});
"ACK"
}
async fn cancel(
Path(id): Path<u32>,
State(state): State<SharedControl>,
) -> &'static str {
state.write().await.tasks[id as usize].pop_front();
"ACK"
}
async fn update_turtles(State(state): State<SharedControl>) -> &'static str {
for turtle in state.read().await.turtles.iter() {
turtle.write().await.pending_update = true;
}
"ACK"
}
async fn turtle_info(
Path(id): Path<u32>,
State(state): State<SharedControl>,
) -> Json<turtle::Turtle> {
let state = &mut state.read().await;
let turtle = &state.turtles[id as usize].read().await;
let cloned = Turtle::new(
turtle.name.to_num(),
turtle.position,
turtle.fuel,
turtle.fuel_limit,
);
Json(cloned)
}
async fn command(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<turtle::TurtleUpdate>,
) -> Json<turtle::TurtleCommand> {
let mut state = &mut state.write().await;
if id as usize > state.turtles.len() {
return Json(turtle::TurtleCommand::Update);
}
Json(
turtle::process_turtle_update(id, &mut state, req).await
.unwrap_or(turtle::TurtleCommand::Update)
)
}
async fn client() -> String {
formatdoc!(r#"
local ipaddr = {}
local port = "{}"
{}"#,
include_str!("../ipaddr.txt"),
PORT.get().unwrap(),
fs::read_to_string("../client/client.lua").await.unwrap(), // TODO: cache handle if bottleneck
)
}

174
server/src/turtle_api.rs Normal file
View file

@ -0,0 +1,174 @@
use tokio;
use blocks::Vec3;
use crate::turtle::TurtleCommandResponse;
use crate::turtle::TurtleInfo;
use axum::extract::Path;
use crate::turtle::TurtleCommand;
use crate::names::Name;
use log::info;
use std::collections::VecDeque;
use blocks::Position;
use crate::turtle::Turtle;
use tokio::sync::RwLock;
use std::sync::Arc;
use tokio::sync::mpsc;
use crate::turtle;
use axum::Json;
use axum::extract::State;
use axum::routing::get;
use axum::routing::post;
use crate::blocks;
use crate::mine;
use super::SharedControl;
use axum::Router;
use indoc::formatdoc;
use crate::PORT;
use tokio::fs;
pub fn turtle_api() -> Router<SharedControl> {
Router::new()
.route("/new", post(create_turtle))
.route("/:id/update", post(command))
.route("/client.lua", get(client))
.route("/:id/setGoal", post(set_goal))
.route("/:id/dig", post(dig))
.route("/:id/cancelTask", post(cancel))
.route("/:id/manual", post(run_command))
.route("/:id/info", get(turtle_info))
.route("/updateAll", get(update_turtles))
}
pub(crate) async fn create_turtle(
State(state): State<SharedControl>,
Json(req): Json<turtle::TurtleRegister>,
) -> Json<turtle::TurtleResponse> {
let state = &mut state.write().await;
let id = state.turtles.len() as u32;
let (send, receive) = mpsc::channel(1);
state.turtles.push(
Arc::new(RwLock::new(
turtle::Turtle::with_channel(id, Position::new(req.position, req.facing), req.fuel, req.fuellimit, send,receive)
)));
state.tasks.push(VecDeque::new());
info!("new turtle: {id}");
Json(turtle::TurtleResponse {
name: Name::from_num(id).to_str(),
id,
command: turtle::TurtleCommand::Update,
})
}
pub(crate) async fn place_up(
Path(id): Path<u32>,
State(state): State<SharedControl>,
) -> Json<TurtleInfo> {
let turtle = state.read().await.get_turtle(id).await.unwrap();
let response = turtle.execute(turtle::TurtleCommand::PlaceUp).await;
Json(response)
}
pub(crate) async fn run_command(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<TurtleCommand>,
) -> Json<TurtleCommandResponse> {
let state = state.read().await;
let commander = state.get_turtle(id).await.unwrap().clone();
drop(state);
Json(commander.execute(req).await.ret)
}
pub(crate) async fn dig(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<(Vec3, Position, Position)>,
) -> &'static str {
let turtle = state.read().await.get_turtle(id).await.unwrap();
let (req, fuel, inventory) = req;
//let fuel = Position::new(Vec3::new(-19, 93, 73), blocks::Direction::East);
//let inventory = Position::new(Vec3::new(-19, 92, 73), blocks::Direction::East);
tokio::spawn(
async move {
mine::mine(turtle.clone(), req, fuel, inventory).await
}
);
"ACK"
}
pub(crate) async fn set_goal(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<Position>,
) -> &'static str {
let turtle = state.read().await.get_turtle(id).await.unwrap();
tokio::spawn(async move {turtle.goto(req).await.expect("route failed")});
"ACK"
}
pub(crate) async fn cancel(
Path(id): Path<u32>,
State(state): State<SharedControl>,
) -> &'static str {
state.write().await.tasks[id as usize].pop_front();
"ACK"
}
pub(crate) async fn update_turtles(State(state): State<SharedControl>) -> &'static str {
for turtle in state.read().await.turtles.iter() {
turtle.write().await.pending_update = true;
}
"ACK"
}
pub(crate) async fn turtle_info(
Path(id): Path<u32>,
State(state): State<SharedControl>,
) -> Json<turtle::Turtle> {
let state = &mut state.read().await;
let turtle = &state.turtles[id as usize].read().await;
let cloned = Turtle::new(
turtle.name.to_num(),
turtle.position,
turtle.fuel,
turtle.fuel_limit,
);
Json(cloned)
}
pub(crate) async fn command(
Path(id): Path<u32>,
State(state): State<SharedControl>,
Json(req): Json<turtle::TurtleUpdate>,
) -> Json<turtle::TurtleCommand> {
let mut state = &mut state.write().await;
if id as usize > state.turtles.len() {
return Json(turtle::TurtleCommand::Update);
}
Json(
turtle::process_turtle_update(id, &mut state, req).await
.unwrap_or(turtle::TurtleCommand::Update)
)
}
pub(crate) async fn client() -> String {
formatdoc!(r#"
local ipaddr = {}
local port = "{}"
{}"#,
include_str!("../ipaddr.txt"),
PORT.get().unwrap(),
fs::read_to_string("../client/client.lua").await.unwrap(), // TODO: cache handle if bottleneck
)
}