Protobuf client
parent
c34d5dfa9a
commit
c5854ee5ae
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
/.idea
|
/.idea
|
||||||
|
config.toml
|
|
@ -657,9 +657,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "raas_types"
|
name = "raas_types"
|
||||||
version = "0.0.1"
|
version = "0.0.2"
|
||||||
source = "registry+https://git.jojodev.com/joeyahines/_cargo-index.git"
|
source = "registry+https://git.jojodev.com/joeyahines/_cargo-index.git"
|
||||||
checksum = "35ffd19aaa3beca193b58d06c98565f308083453854fda62bd1d11cbc699d807"
|
checksum = "6104c0c441473bc9c0817f9958a1c1e0db9ea7c72a7fee826b84a1d9d1baea62"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"prost",
|
"prost",
|
||||||
|
@ -702,8 +702,10 @@ dependencies = [
|
||||||
"config",
|
"config",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"log",
|
"log",
|
||||||
|
"prost",
|
||||||
"raas_types",
|
"raas_types",
|
||||||
"rppal",
|
"rppal",
|
||||||
|
"serde",
|
||||||
"structopt",
|
"structopt",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,9 +9,11 @@ edition = "2021"
|
||||||
structopt = "0.3.26"
|
structopt = "0.3.26"
|
||||||
config = "0.14.0"
|
config = "0.14.0"
|
||||||
rppal = "0.18.0"
|
rppal = "0.18.0"
|
||||||
raas_types = { version = "0.0.1", registry = "jojo-dev"}
|
raas_types = { version = "0.0.2", registry = "jojo-dev"}
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
env_logger = "0.11.3"
|
env_logger = "0.11.3"
|
||||||
thiserror = "1.0.61"
|
thiserror = "1.0.61"
|
||||||
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
|
prost = "0.12.6"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,222 @@
|
||||||
|
use crate::config::RollBotConfig;
|
||||||
|
use crate::roll_bot::RollBot;
|
||||||
|
use log::{error, info, warn};
|
||||||
|
use prost::Message;
|
||||||
|
use raas_types::raas::bot::roll::{roll_cmd, Roll, RollCmd, RollImage, RollResponse};
|
||||||
|
use raas_types::raas::cmd::command::Cmd;
|
||||||
|
use raas_types::raas::cmd::Command;
|
||||||
|
use raas_types::raas::ping::{Ping, Pong};
|
||||||
|
use raas_types::raas::register::{BotTypes, Register, RegisterResponse};
|
||||||
|
use raas_types::raas::resp::response::Resp;
|
||||||
|
use raas_types::raas::resp::Response;
|
||||||
|
use raas_types::raas::RaasMessage;
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
use std::net::TcpStream;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Roll Bot Error")]
|
||||||
|
RollBot(#[from] crate::roll_bot::Error),
|
||||||
|
#[error("Network Error")]
|
||||||
|
IO(#[from] std::io::Error),
|
||||||
|
#[error("Protobuf Encode Error")]
|
||||||
|
ProtoBufEncode(#[from] prost::EncodeError),
|
||||||
|
#[error("Protobuf Decode Error")]
|
||||||
|
ProtoBufDecode(#[from] prost::DecodeError),
|
||||||
|
#[error("Invalid command ID")]
|
||||||
|
InvalidCommandID,
|
||||||
|
#[error("Invalid command")]
|
||||||
|
InvalidCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Client {
|
||||||
|
config: RollBotConfig,
|
||||||
|
roll_bot: RollBot,
|
||||||
|
|
||||||
|
id: Option<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Client {
|
||||||
|
pub fn new(config: RollBotConfig, roll_bot: RollBot) -> Self {
|
||||||
|
Self {
|
||||||
|
config,
|
||||||
|
roll_bot,
|
||||||
|
id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_connect(&self) -> Result<TcpStream, Error> {
|
||||||
|
for attempt in 1..10 {
|
||||||
|
info!("Attempting to connect to {}", self.config.server_addr);
|
||||||
|
let socket = TcpStream::connect(self.config.server_addr.clone());
|
||||||
|
|
||||||
|
if let Ok(socket) = socket {
|
||||||
|
return Ok(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
let wait = attempt * 10;
|
||||||
|
warn!("Connection failed, waiting {}s...", wait);
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(wait))
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(std::io::Error::last_os_error().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register(&mut self, socket: &mut TcpStream) -> Result<(), Error> {
|
||||||
|
let register_msg = Register {
|
||||||
|
name: "Roll Bot".to_string(),
|
||||||
|
bot_type: BotTypes::Roll.into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut message = Vec::new();
|
||||||
|
register_msg.encode(&mut message)?;
|
||||||
|
|
||||||
|
Self::send_packet(socket, message)?;
|
||||||
|
|
||||||
|
let resp = Self::receive_packet(socket)?;
|
||||||
|
|
||||||
|
let register_msg = RegisterResponse::decode(&*resp.msg)?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Registered '{}' as id {}",
|
||||||
|
register_msg.name, register_msg.id
|
||||||
|
);
|
||||||
|
|
||||||
|
self.id = Some(register_msg.id);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn receive_packet(socket: &mut TcpStream) -> Result<RaasMessage, Error> {
|
||||||
|
let mut message_len_bytes = [0u8; 4];
|
||||||
|
socket.read_exact(&mut message_len_bytes)?;
|
||||||
|
|
||||||
|
let len = u32::from_be_bytes(message_len_bytes);
|
||||||
|
|
||||||
|
let mut message = vec![0u8; len as usize];
|
||||||
|
|
||||||
|
socket.read_exact(&mut message)?;
|
||||||
|
|
||||||
|
Ok(RaasMessage { len, msg: message })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_packet(socket: &mut TcpStream, data: Vec<u8>) -> Result<(), Error> {
|
||||||
|
let msg = RaasMessage::new(data);
|
||||||
|
|
||||||
|
socket.write_all(&msg.into_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_next_command(&mut self, socket: &mut TcpStream) -> Result<RollCmd, Error> {
|
||||||
|
let msg = Self::receive_packet(socket)?;
|
||||||
|
|
||||||
|
let command = Command::decode(&*msg.msg)?;
|
||||||
|
|
||||||
|
info!(
|
||||||
|
"Received command for id={} timestamp={}",
|
||||||
|
command.id, command.timestamp
|
||||||
|
);
|
||||||
|
|
||||||
|
if command.id != self.id.unwrap() {
|
||||||
|
warn!("Command for a different ID was received, dropping");
|
||||||
|
return Err(Error::InvalidCommandID);
|
||||||
|
}
|
||||||
|
|
||||||
|
match command.cmd.unwrap() {
|
||||||
|
Cmd::RollCmd(cmd) => Ok(cmd),
|
||||||
|
#[allow(unreachable_patterns)]
|
||||||
|
_ => {
|
||||||
|
warn!("Invalid command id received");
|
||||||
|
Err(Error::InvalidCommand)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_ping(&mut self, ping: Ping) -> Result<RollResponse, Error> {
|
||||||
|
info!("Received ping w/ data {:?}", ping.data);
|
||||||
|
|
||||||
|
let pong = Pong {
|
||||||
|
ping: Some(ping),
|
||||||
|
is_ok: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let resp = RollResponse {
|
||||||
|
response: Some(raas_types::raas::bot::roll::roll_response::Response::Pong(
|
||||||
|
pong,
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_roll(&mut self, roll: Roll) -> Result<RollResponse, Error> {
|
||||||
|
info!("Doing roll...");
|
||||||
|
self.roll_bot.roll(roll.rotations)?;
|
||||||
|
|
||||||
|
let resp = RollResponse {
|
||||||
|
response: Some(
|
||||||
|
raas_types::raas::bot::roll::roll_response::Response::RollImage(RollImage {
|
||||||
|
img: vec![],
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_command(&mut self, socket: &mut TcpStream, roll_cmd: RollCmd) -> Result<(), Error> {
|
||||||
|
let roll_resp = match roll_cmd.cmd.unwrap() {
|
||||||
|
roll_cmd::Cmd::Ping(ping) => self.handle_ping(ping),
|
||||||
|
roll_cmd::Cmd::Roll(roll) => self.handle_roll(roll),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
let timestamp = SystemTime::now()
|
||||||
|
.duration_since(UNIX_EPOCH)
|
||||||
|
.unwrap()
|
||||||
|
.as_secs();
|
||||||
|
|
||||||
|
let resp = Response {
|
||||||
|
id: self.id.unwrap(),
|
||||||
|
timestamp,
|
||||||
|
resp: Some(Resp::RollResp(roll_resp)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut message = vec![0u8; 1024];
|
||||||
|
|
||||||
|
resp.encode(&mut message)?;
|
||||||
|
|
||||||
|
Self::send_packet(socket, message)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_client(&mut self) -> Result<(), Error> {
|
||||||
|
loop {
|
||||||
|
let mut socket = self.try_connect()?;
|
||||||
|
|
||||||
|
self.register(&mut socket)?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let roll_cmd_res = self.get_next_command(&mut socket);
|
||||||
|
|
||||||
|
if let Ok(roll_cmd) = roll_cmd_res {
|
||||||
|
self.handle_command(&mut socket, roll_cmd)?;
|
||||||
|
} else if let Err(roll_cmd_err) = roll_cmd_res {
|
||||||
|
match roll_cmd_err {
|
||||||
|
Error::IO(io_error) => {
|
||||||
|
warn!("Connection issue, reattempting connection: {}", io_error);
|
||||||
|
self.id = None;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Error::InvalidCommandID | Error::InvalidCommand => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_ => return Err(roll_cmd_err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
use config::Config;
|
||||||
|
use rppal::pwm::Channel;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
#[derive(StructOpt)]
|
||||||
|
pub struct Args {
|
||||||
|
pub config_file: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum ChannelConfig {
|
||||||
|
Pwm0,
|
||||||
|
Pwm1,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ChannelConfig> for Channel {
|
||||||
|
fn from(value: ChannelConfig) -> Self {
|
||||||
|
match value {
|
||||||
|
ChannelConfig::Pwm0 => Channel::Pwm0,
|
||||||
|
ChannelConfig::Pwm1 => Channel::Pwm1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PWMStep {
|
||||||
|
pub pulse_us: u64,
|
||||||
|
pub delay_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct PWMConfig {
|
||||||
|
pub pwm_channel: ChannelConfig,
|
||||||
|
pub period_ms: u64,
|
||||||
|
pub pulse_min_us: u64,
|
||||||
|
pub pulse_max_us: u64,
|
||||||
|
pub steps: Vec<PWMStep>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct RollBotConfig {
|
||||||
|
pub server_addr: String,
|
||||||
|
|
||||||
|
pub pwm_config: PWMConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RollBotConfig {
|
||||||
|
pub fn new(config: PathBuf) -> Self {
|
||||||
|
let daemon_config = Config::builder()
|
||||||
|
.add_source(config::File::new(
|
||||||
|
config.to_str().unwrap(),
|
||||||
|
config::FileFormat::Toml,
|
||||||
|
))
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
daemon_config.try_deserialize().unwrap()
|
||||||
|
}
|
||||||
|
}
|
38
src/main.rs
38
src/main.rs
|
@ -1,27 +1,30 @@
|
||||||
|
mod client;
|
||||||
|
mod config;
|
||||||
mod roll_bot;
|
mod roll_bot;
|
||||||
|
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
|
use log::{error, info};
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use log::info;
|
|
||||||
|
|
||||||
|
use crate::client::Client;
|
||||||
|
use crate::config::Args;
|
||||||
use crate::roll_bot::RollBot;
|
use crate::roll_bot::RollBot;
|
||||||
use rppal::pwm::{Channel, Polarity, Pwm};
|
use rppal::pwm::{Polarity, Pwm};
|
||||||
|
|
||||||
const PERIOD_MS: u64 = 20;
|
|
||||||
const PULSE_MIN_US: u64 = 1000;
|
|
||||||
const PULSE_NEUTRAL_US: u64 = 1500;
|
|
||||||
const PULSE_MAX_US: u64 = 2000;
|
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
|
let args = Args::from_args();
|
||||||
|
|
||||||
|
let config = config::RollBotConfig::new(args.config_file);
|
||||||
|
|
||||||
info!("Initializing PWM");
|
info!("Initializing PWM");
|
||||||
let pwm = Pwm::with_period(
|
let pwm = Pwm::with_period(
|
||||||
Channel::Pwm0,
|
config.pwm_config.pwm_channel.clone().into(),
|
||||||
Duration::from_millis(PERIOD_MS),
|
Duration::from_millis(config.pwm_config.period_ms),
|
||||||
Duration::from_micros(PULSE_MAX_US),
|
Duration::from_micros(config.pwm_config.pulse_max_us),
|
||||||
Polarity::Normal,
|
Polarity::Normal,
|
||||||
true,
|
true,
|
||||||
)?;
|
)?;
|
||||||
|
@ -29,15 +32,20 @@ fn main() -> Result<(), Box<dyn Error>> {
|
||||||
info!("Initializing Roll Bot");
|
info!("Initializing Roll Bot");
|
||||||
let roll_bot = RollBot {
|
let roll_bot = RollBot {
|
||||||
pwm,
|
pwm,
|
||||||
move_time: Duration::from_millis(1000),
|
steps: config.pwm_config.steps.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
roll_bot.set_pwm_output(Duration::from_micros(PULSE_MIN_US))?;
|
roll_bot.set_pwm_output(Duration::from_micros(config.pwm_config.pulse_min_us))?;
|
||||||
|
|
||||||
loop {
|
let mut client = Client::new(config, roll_bot);
|
||||||
roll_bot.roll(5)?;
|
|
||||||
std::thread::sleep(Duration::from_millis(1000));
|
let res = client.run_client();
|
||||||
|
|
||||||
|
if let Err(err) = res {
|
||||||
|
error!("Error in client: {}", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("Roll Bot shutting down...");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{PULSE_MAX_US, PULSE_MIN_US, PULSE_NEUTRAL_US};
|
use crate::config::PWMStep;
|
||||||
use log::info;
|
use log::info;
|
||||||
use rppal::pwm::Pwm;
|
use rppal::pwm::Pwm;
|
||||||
use std::thread;
|
use std::thread::sleep;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
@ -13,14 +13,13 @@ pub enum Error {
|
||||||
|
|
||||||
pub struct RollBot {
|
pub struct RollBot {
|
||||||
pub pwm: Pwm,
|
pub pwm: Pwm,
|
||||||
pub move_time: Duration,
|
pub steps: Vec<PWMStep>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RollBot {
|
impl RollBot {
|
||||||
pub fn set_pwm_output(&self, pulse: Duration) -> Result<(), Error> {
|
pub fn set_pwm_output(&self, pulse: Duration) -> Result<(), Error> {
|
||||||
info!("Going to {:?}", pulse);
|
info!("Going to {:?}", pulse);
|
||||||
self.pwm
|
self.pwm.set_pulse_width(pulse)?;
|
||||||
.set_pulse_width(pulse)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -28,15 +27,9 @@ impl RollBot {
|
||||||
pub fn roll(&self, tumble_times: u32) -> Result<(), Error> {
|
pub fn roll(&self, tumble_times: u32) -> Result<(), Error> {
|
||||||
for tumble in 1..=tumble_times {
|
for tumble in 1..=tumble_times {
|
||||||
info!("Doing Tumble {}", tumble);
|
info!("Doing Tumble {}", tumble);
|
||||||
|
for step in &self.steps {
|
||||||
for step in (PULSE_MIN_US..=PULSE_MAX_US).step_by(1000) {
|
self.set_pwm_output(Duration::from_micros(step.pulse_us))?;
|
||||||
self.set_pwm_output(Duration::from_micros(step))?;
|
sleep(Duration::from_millis(step.delay_ms));
|
||||||
thread::sleep(self.move_time);
|
|
||||||
}
|
|
||||||
|
|
||||||
for step in (PULSE_MIN_US..=PULSE_MAX_US).rev().step_by(1000) {
|
|
||||||
self.set_pwm_output(Duration::from_micros(step))?;
|
|
||||||
thread::sleep(self.move_time);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue