use crate::{PointId, TaskId};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StatusMessage {
#[serde(rename = "ret_code")]
pub code: StatusCode,
#[serde(rename = "err_msg", default)]
pub message: String,
#[serde(rename = "create_on", default)]
pub timestamp: Option<String>,
}
impl StatusMessage {
pub fn into_result(self) -> Result<(), StatusMessage> {
if self.code == StatusCode::Success {
Ok(())
} else {
Err(self)
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)]
#[repr(u32)]
pub enum StatusCode {
Success = 0,
Unavailable = 40000,
ParamMissing = 40001,
ParamTypeError = 40002,
ParamIllegal = 40003,
ModeError = 40004,
IllegalMapName = 40005,
ProgrammingDsp = 40006,
ProgramDspError = 40007,
ShutdownError = 40010,
RebootError = 40011,
MapParseError = 40050,
MapNotExists = 40051,
LoadMapError = 40052,
LoadMapobjError = 40053,
EmptyMap = 40054,
ReqTimeout = 40100,
ReqForbidden = 40101,
RobotBusy = 40102,
RobotInternalError = 40199,
InitStatusError = 41000,
LoadmapStatusError = 41001,
RelocStatusError = 41002,
#[num_enum(default)]
Custom,
}
macro_rules! impl_serde_for_num_enum {
($enum_type:ty) => {
impl<'de> serde::Deserialize<'de> for $enum_type {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let code = u32::deserialize(deserializer)?;
Ok(<$enum_type>::from(code))
}
}
impl serde::Serialize for $enum_type {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u32(*self as u32)
}
}
};
}
impl_serde_for_num_enum!(StatusCode);
impl_serde_for_num_enum!(JackOperationStatus);
pub trait FromResponseBody: Sized {
type Response: serde::de::DeserializeOwned;
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CommonInfo {
pub id: String,
pub version: String,
pub model: String,
#[serde(rename = "ret_code", default)]
pub code: Option<StatusCode>,
#[serde(rename = "err_msg", default)]
pub message: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct OperationInfo {
#[serde(rename = "odo")]
pub mileage: f64,
#[serde(rename = "total")]
pub session_time_ms: f64,
#[serde(rename = "total_time")]
pub total_time_ms: f64,
pub controller_temp: f64,
#[serde(default)]
pub controller_humi: f64,
#[serde(default)]
pub controller_voltage: f64,
#[serde(rename = "ret_code", default)]
pub code: Option<StatusCode>,
#[serde(rename = "err_msg", default)]
pub message: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct RobotPose {
pub x: f64,
pub y: f64,
pub angle: f64,
pub confidence: f64,
#[serde(rename = "ret_code", default)]
pub code: Option<StatusCode>,
#[serde(rename = "err_msg", default)]
pub message: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)]
#[repr(u8)]
pub enum BlockReason {
Laser = 1,
Fallingdown = 2,
Collision = 3,
Infrared = 4,
#[num_enum(default)]
Custom,
}
impl std::fmt::Display for BlockReason {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let description = match self {
BlockReason::Laser => "Laser Obstacle",
BlockReason::Fallingdown => "Falling Down",
BlockReason::Collision => "Collision Detected",
BlockReason::Infrared => "Infrared Obstacle",
BlockReason::Custom => "Custom Reason",
};
write!(f, "{}", description)
}
}
impl<'de> serde::Deserialize<'de> for BlockReason {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let code = u8::deserialize(deserializer)?;
Ok(BlockReason::from(code))
}
}
impl serde::Serialize for BlockReason {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_u8(*self as u8)
}
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BlockStatus {
#[serde(rename = "blocked")]
pub is_blocked: bool,
#[serde(rename = "block_reason", default)]
pub reason: Option<BlockReason>,
#[serde(rename = "block_x", default)]
pub x: Option<f64>,
#[serde(rename = "block_y", default)]
pub y: Option<f64>,
#[serde(rename = "ret_code")]
pub code: StatusCode,
#[serde(rename = "err_msg", default)]
pub message: String,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct BatteryStatus {
pub battery_level: f64,
pub battery_temp: f64,
pub charging: bool,
pub voltage: f64,
pub current: f64,
#[serde(rename = "ret_code", default)]
pub code: Option<StatusCode>,
#[serde(rename = "err_msg", default)]
pub message: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)]
#[repr(u32)]
pub enum JackOperationStatus {
Rising = 0x0,
RisingInPlace = 0x1,
Lowering = 0x2,
LoweringInPlace = 0x3,
Stop = 0x4,
#[num_enum(default)]
Failed = 0xFF,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct JackStatus {
#[serde(rename = "jack_mode")]
pub automatic_mode: bool,
#[serde(rename = "jack_enable")]
pub enabled: bool,
#[serde(rename = "jack_error_code")]
pub error_code: u32,
#[serde(rename = "jack_state")]
pub operation: JackOperationStatus,
#[serde(rename = "jack_isFull")]
pub has_payload: bool,
#[serde(rename = "jack_speed")]
pub speed: u32,
#[serde(rename = "jack_emc")]
pub emergency_stop: bool,
#[serde(rename = "jack_height")]
pub height: f64,
#[serde(rename = "peripheral_data")]
pub peripheral_data: Vec<u8>,
#[serde(rename = "ret_code", default)]
pub code: Option<StatusCode>,
#[serde(rename = "err_msg", default)]
pub message: String,
#[serde(rename = "create_on", default)]
pub timestamp: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct NavStatus {
#[serde(rename = "task_status")]
pub status: TaskStatus,
#[serde(rename = "task_type")]
pub ty: TaskType,
pub target_id: PointId,
pub target_point: [f64; 3],
pub finished_path: Vec<PointId>,
pub unfinished_path: Vec<PointId>,
pub move_status_info: String,
#[serde(rename = "ret_code", default)]
pub code: Option<StatusCode>,
pub create_on: Option<String>,
#[serde(rename = "err_msg", default)]
pub message: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)]
#[repr(u32)]
pub enum TaskType {
NoNav = 0,
FreeNavToPoint = 1,
FreeNavToSite = 2,
PathNavToSite = 3,
Manual = 7,
#[num_enum(default)]
Other = 100,
}
impl_serde_for_num_enum!(TaskType);
#[derive(Debug, Clone, Copy, PartialEq, Eq, num_enum::FromPrimitive)]
#[repr(u32)]
pub enum TaskStatus {
#[num_enum(default)]
None = 0,
Waiting = 1,
Running = 2,
Suspended = 3,
Completed = 4,
Failed = 5,
Canceled = 6,
OverTime = 7,
NotFound = 404,
}
impl_serde_for_num_enum!(TaskStatus);
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TaskStatusItem {
pub task_id: TaskId,
pub status: TaskStatus,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TaskPackage {
pub closest_target: PointId,
pub source_name: TaskId,
pub target_name: TaskId,
pub percentage: f64,
pub distance: Option<f64>,
#[serde(rename = "task_status_list")]
pub tasks: Vec<TaskStatusItem>,
pub info: String,
#[serde(rename = "ret_code", default)]
pub code: Option<StatusCode>,
#[serde(rename = "err_msg", default)]
pub message: String,
pub create_on: Option<String>,
}
#[cfg(test)]
mod tests {
use crate::StatusCode;
#[test]
fn test_error_code_serialization() {
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
struct TestStruct {
code: StatusCode,
}
let test_instance = TestStruct {
code: StatusCode::ParamMissing,
};
let serialized = serde_json::to_string(&test_instance).unwrap();
assert_eq!(serialized, r#"{"code":40001}"#);
let deserialized: TestStruct =
serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.code, StatusCode::ParamMissing);
let custom_code = r#"{"code":99999}"#;
let deserialized_custom: TestStruct =
serde_json::from_str(custom_code).unwrap();
assert_eq!(deserialized_custom.code, StatusCode::Custom);
}
#[test]
#[allow(clippy::approx_constant)]
fn test_robot_pose_serialization_deserializatio() {
let with_error_code = r#"
{
"x": 1.0,
"y": 2.0,
"angle": 0.7854,
"confidence": 0.95,
"ret_code": 40000,
"err_msg": "msg"
}"#;
let pose: super::RobotPose =
serde_json::from_str(with_error_code).unwrap();
assert_eq!(pose.x, 1.0);
assert_eq!(pose.y, 2.0);
assert!((pose.angle - 0.7854).abs() < 0.0001);
assert_eq!(pose.confidence, 0.95);
assert_eq!(pose.code, Some(StatusCode::Unavailable));
assert_eq!(pose.message, "msg");
let without_error_code = r#"
{
"x": 3.0,
"y": 4.0,
"angle": 1.5708,
"confidence": 0.9
}"#;
let pose_no_code: super::RobotPose =
serde_json::from_str(without_error_code).unwrap();
assert_eq!(pose_no_code.x, 3.0);
assert_eq!(pose_no_code.y, 4.0);
assert!((pose_no_code.angle - 1.5708).abs() < 0.0001);
assert_eq!(pose_no_code.confidence, 0.9);
assert_eq!(pose_no_code.code, None);
assert_eq!(pose_no_code.message, "");
}
#[test]
fn test_block_status_serialization_deserialization() {
let with_error_code = r#"
{
"blocked": true,
"block_reason": 2,
"block_x": 1.5,
"block_y": 2.5,
"ret_code": 40002,
"err_msg": "Parameter type error"
}"#;
let status: super::BlockStatus =
serde_json::from_str(with_error_code).unwrap();
assert!(status.is_blocked);
assert_eq!(status.reason, Some(super::BlockReason::Fallingdown));
assert_eq!(status.x, Some(1.5));
assert_eq!(status.y, Some(2.5));
assert_eq!(status.code, StatusCode::ParamTypeError);
assert_eq!(status.message, "Parameter type error");
let without_error_code = r#"
{
"ret_code": 0,
"blocked": false
}"#;
let status_no_code: super::BlockStatus =
serde_json::from_str(without_error_code).unwrap();
assert!(!status_no_code.is_blocked);
assert_eq!(status_no_code.reason, None);
assert_eq!(status_no_code.x, None);
assert_eq!(status_no_code.y, None);
assert_eq!(status_no_code.code, StatusCode::Success);
assert_eq!(status_no_code.message, "");
}
}