use std::time::Instant;
use anyhow::Result;
use crossbeam::channel::{bounded, Receiver, Sender};
use egui::{ComboBox, Label, RichText, Ui, WidgetText, Color32};
use crate::common::utils::RcCell;
#[derive(Debug, Clone)]
pub struct Message {
pub start: Instant,
pub end_second: f32,
pub content: String,
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum FunId {
AvailablePort,
OpenPort,
ClosePort,
ChangeId,
Init,
ScanDevices,
CurrentPos,
CurrentSpeed,
CurrentAccSpeed,
SetId,
SetAngle,
SetIncPos,
SetSpeed,
SetAccSpeed,
SetAutoEnable,
SetInitAutoBack,
SetFactor,
SaveParam,
}
impl ToString for FunId {
fn to_string(&self) -> String {
match *self {
FunId::AvailablePort => "查询有效端口".into(),
FunId::OpenPort => "打开端口".into(),
FunId::ClosePort => "关闭端口".into(),
FunId::ChangeId => "修改设备ID".into(),
FunId::Init => "使能设备".into(),
FunId::ScanDevices => "扫描设备".into(),
FunId::CurrentPos => "获取当前位置".into(),
FunId::CurrentSpeed => "获取当前速度".into(),
FunId::CurrentAccSpeed => "获取当前加速度".into(),
FunId::SetId => "设置ID".into(),
FunId::SetAngle => "设置绝对位置".into(),
FunId::SetIncPos => "设置增量位置".into(),
FunId::SetSpeed => "设置速度".into(),
FunId::SetAccSpeed => "设置加速度".into(),
FunId::SetAutoEnable => "设置上电后自动启用".into(),
FunId::SetInitAutoBack => "设置上电后回到初始位置".into(),
FunId::SetFactor => "设置齿轮比例".into(),
FunId::SaveParam => "保存参数".into(),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Request {
AvailablePort,
OpenPort(String),
ClosePort,
ChangeId(u8),
Init(u8),
ScanDevices,
CurrentPos(u8),
CurrentSpeed(u8),
CurrentAccSpeed(u8),
SetId(u8),
SetAngle(u8, f64),
SetIncPos(u8, i32),
SetSpeed(u8, u16),
SetAccSpeed(u8, u16),
SetAutoEnable(u8, bool),
SetInitAutoBack(u8, bool),
SetFactor(f64),
SaveParam(u8),
}
impl Request {
pub fn to_fun_id(&self) -> FunId {
match *self {
Request::AvailablePort => FunId::AvailablePort,
Request::OpenPort(_) => FunId::OpenPort,
Request::ClosePort => FunId::ClosePort,
Request::ChangeId(_) => FunId::ChangeId,
Request::Init(_) => FunId::Init,
Request::ScanDevices => FunId::ScanDevices,
Request::CurrentPos(_) => FunId::CurrentPos,
Request::CurrentSpeed(_) => FunId::CurrentSpeed,
Request::CurrentAccSpeed(_) => FunId::CurrentAccSpeed,
Request::SetId(_) => FunId::SetId,
Request::SetAngle(_, _) => FunId::SetAngle,
Request::SetIncPos(_, _) => FunId::SetIncPos,
Request::SetSpeed(_, _) => FunId::SetSpeed,
Request::SetAccSpeed(_, _) => FunId::SetAccSpeed,
Request::SetAutoEnable(_, _) => FunId::SetAutoEnable,
Request::SetInitAutoBack(_, _) => FunId::SetInitAutoBack,
Request::SetFactor(_) => FunId::SetFactor,
Request::SaveParam(_) => FunId::SaveParam,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResponseCode {
Fail = -1,
Ok = 0,
Ready = 1,
Pending = 2,
}
impl Default for ResponseCode {
fn default() -> Self {
Self::Ready
}
}
impl ToString for ResponseCode {
fn to_string(&self) -> String {
match *self {
ResponseCode::Fail => "执行失败".to_string(),
ResponseCode::Ok => "执行成功".to_string(),
ResponseCode::Ready => "准备就绪".to_string(),
ResponseCode::Pending => "等待执行完成".to_string(),
}
}
}
#[derive(Debug, Clone)]
pub struct Response {
pub fun_id: FunId,
pub code: ResponseCode,
pub msg: String,
}
impl Response {
pub fn new(req: &Request) -> Self {
let code = ResponseCode::default();
Self {
fun_id: req.to_fun_id(),
code,
msg: code.to_string(),
}
}
pub fn pending(req: &Request) -> Self {
let code = ResponseCode::Pending;
Self {
fun_id: req.to_fun_id(),
code,
msg: code.to_string(),
}
}
pub fn operation(&self) -> String {
self.fun_id.to_string()
}
pub fn status(&self) -> String {
self.code.to_string()
}
}
pub struct RequestHandler {
queue: Vec<(Request, Response)>,
res_sender: Sender<Response>,
res_receiver: Receiver<Response>,
services: Vec<RcCell<Box<dyn Service>>>,
}
impl RequestHandler {
pub fn new(res_sender: Sender<Response>, res_receiver: Receiver<Response>) -> Self {
Self {
queue: Vec::new(),
res_sender,
res_receiver,
services: Vec::new(),
}
}
}
impl RequestHandler {
pub fn req(&mut self, req: Request) -> Result<()> {
let item = self.queue.iter().find(|(q, _s)| q == &req);
if item.is_some() {
return Err(anyhow::anyhow!("请求正在处理中"));
}
self.queue.push((req.clone(), Response::new(&req)));
for service in self.services.iter() {
service.as_ref().request(req.clone(), self.res_sender.clone());
}
Ok(())
}
pub fn run_once(&mut self) -> Option<Response> {
while let Ok(res) = self.res_receiver.try_recv() {
let item = self.queue.iter_mut().find(|(req, res)| res.fun_id == req.to_fun_id());
if item.is_some() {
let (_, item_res) = item.unwrap();
*item_res = res;
}
}
match self.queue.last() {
Some(item) => Some(item.1.clone()),
None => None,
}
}
pub fn register(&mut self, service: RcCell<Box<dyn Service>>) {
self.services.push(service);
}
pub fn clean(&mut self, res: Response) {
self.queue.retain(|(_q, s)| res.fun_id != s.fun_id);
}
}
pub trait Service {
fn request(&self, req: Request, res_sender: Sender<Response>) -> bool {
false
}
fn publish(&self) -> Vec<Topic> {
Vec::new()
}
}
#[derive(Debug, PartialEq)]
pub struct TopicAddr(String);
impl TopicAddr {
pub fn name(&self) -> String {
self.0.clone()
}
}
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum TopicId {
MotorData,
}
#[derive(Clone)]
pub struct Topic {
pub id: TopicId,
pub msg: String,
}
pub struct TopicHandler {
services: Vec<RcCell<Box<dyn Service>>>,
}
impl TopicHandler {
pub fn new() -> Self {
Self { services: Vec::new() }
}
pub fn run_once(&self) -> Vec<Topic> {
let mut topics = Vec::new();
for service in self.services.iter() {
topics.extend(service.as_ref().publish());
}
topics
}
pub fn register(&mut self, service: RcCell<Box<dyn Service>>) {
self.services.push(service);
}
}
pub struct AppRuntime {
req_handler: RequestHandler,
topic_handler: TopicHandler,
pub ports: Vec<String>,
pub ids: Vec<u8>,
pub port: String,
pub port_opened: bool,
pub id: String,
pub operation: Option<FunId>,
pub status: Option<ResponseCode>,
pub error_msg: Option<String>,
pub info_msg: Option<String>,
pub current_pos: i32,
pub current_speed: u16,
pub current_acc_speed: u16,
pub init_pos: u16,
pub current_auto_enable: bool,
pub current_init_auto_back: bool,
pub angle: f64,
pub inc_pos: i32,
pub speed: u16,
pub acc_speed: u16,
pub auto_enable: bool,
pub init_auto_back: bool,
pub factor: f64,
}
impl Default for AppRuntime {
fn default() -> Self {
let (res_sender, res_receiver) = bounded::<Response>(100);
let mut req_handler = RequestHandler::new(res_sender, res_receiver);
let (topic_pub, topic_sub) = bounded::<Topic>(100);
let mut topic_handler = TopicHandler::new();
Self {
req_handler,
topic_handler,
ports: Default::default(),
ids: Default::default(),
port: Default::default(),
port_opened: Default::default(),
id: Default::default(),
operation: Default::default(),
status: Default::default(),
error_msg: Default::default(),
info_msg: Default::default(),
current_pos: 0,
current_speed: 0,
current_acc_speed: 0,
current_auto_enable: false,
current_init_auto_back: false,
init_pos: 0,
angle: 0.0,
inc_pos: 0,
speed: 0,
acc_speed: 0,
auto_enable: false,
init_auto_back: false,
factor: 2.04,
}
}
}
impl AppRuntime {
pub fn port(&self) -> String {
self.port.clone()
}
pub fn id(&self) -> u8 {
self.id.parse().unwrap()
}
}
impl AppRuntime {
pub fn call(&mut self, req: Request) {
if let Err(e) = self.req_handler.req(req) {
let msg = format!("异常: {:?}", &e);
log::error!("{:?}", &msg);
self.error(msg, None);
}
}
pub fn info(&mut self, msg: String, operation: Option<FunId>) {
self.error_msg = None;
self.info_msg = Some(msg);
self.operation = operation;
}
pub fn error(&mut self, msg: String, operation: Option<FunId>) {
self.error_msg = Some(msg);
self.operation = operation;
}
pub fn run_once(&mut self) {
let res = self.req_handler.run_once();
if let Some(res) = res {
self.handle_request(res);
}
let res = self.topic_handler.run_once();
self.handle_topic(res);
}
pub fn handle_request(&mut self, res: Response) {
match res.code {
ResponseCode::Fail => {
self.error(res.msg.clone(), Some(res.fun_id));
self.req_handler.clean(res);
}
ResponseCode::Ok => {
match res.fun_id {
FunId::AvailablePort => {
self.ports = serde_json::from_str(&res.msg).unwrap();
}
FunId::OpenPort => {
self.port_opened = true;
}
FunId::ClosePort => {
self.port_opened = false;
}
FunId::ScanDevices => {
self.ids = serde_json::from_str(&res.msg).unwrap();
}
FunId::ChangeId => {}
FunId::Init => {}
FunId::CurrentPos => {
self.current_pos = serde_json::from_str(&res.msg).unwrap();
}
FunId::CurrentSpeed => {
self.current_speed = serde_json::from_str(&res.msg).unwrap();
}
FunId::CurrentAccSpeed => {
self.current_acc_speed = serde_json::from_str(&res.msg).unwrap();
}
FunId::SetId | FunId::SetAngle | FunId::SetIncPos | FunId::SetSpeed | FunId::SetAccSpeed => {}
FunId::SetAutoEnable => {}
FunId::SetInitAutoBack => {}
FunId::SetFactor => {}
FunId::SaveParam => {}
}
self.info(res.status(), Some(res.fun_id));
self.req_handler.clean(res);
}
ResponseCode::Ready | ResponseCode::Pending => {
self.info(res.msg.clone(), Some(res.fun_id));
}
}
}
pub fn handle_topic(&mut self, topics: Vec<Topic>) {
for topic in topics {
match topic.id {
TopicId::MotorData => {
let data: MotorData = serde_json::from_str(&topic.msg).unwrap();
self.current_pos = data.pos;
self.current_speed = data.speed;
self.current_acc_speed = data.acc_speed;
self.init_pos = data.init_pos;
}
}
}
}
}
impl AppRuntime {
pub fn status_bar(&self, ui: &mut Ui) {
ui.horizontal(|ui| {
if let Some(operation) = self.operation.as_ref() {
let operation = WidgetText::RichText(RichText::new(operation.to_string()).color(Color32::RED).size(13.0));
ui.add(Label::new(operation));
}
if let Some(error_msg) = &self.error_msg {
let error_msg = WidgetText::RichText(RichText::new(error_msg.to_string()).color(Color32::RED).size(13.0));
ui.add(Label::new(error_msg));
}
else if let Some(info_msg) = &self.info_msg {
let info_msg = WidgetText::RichText(RichText::new(info_msg.to_string()).color(Color32::RED).size(13.0));
ui.add(Label::new(info_msg));
}
else if let Some(status) = self.status.as_ref() {
let status = WidgetText::RichText(RichText::new(status.to_string()).color(Color32::RED).size(13.0));
ui.add(Label::new(status));
}
});
}
pub fn ids_ui(&mut self, ui: &mut Ui) {
ComboBox::from_id_source("select_ids")
.selected_text(self.id.to_string())
.show_ui(ui, |ui| {
for id in self.ids.clone() {
let changed = ui.selectable_value(&mut self.id, id.to_string(), id.to_string()).changed();
if changed {
self.call(Request::SetId(id));
}
}
});
}
pub fn ports_ui(&mut self, ui: &mut Ui) {
if self.port == "" && self.ports.len() != 0 {
self.port = self.ports.last().unwrap().clone();
}
ComboBox::from_id_source("select_ports")
.selected_text(self.port.clone())
.show_ui(ui, |ui| {
for port in &self.ports {
ui.selectable_value(&mut self.port, port.clone(), port.clone());
}
});
}
}