Initial commit: Happa Mission Plan v0.1.0
Task planning and path generation software for automated data collection equipment (hyperspectral cameras, DSLR, depth cameras). - Generate mission plans (JSON) with subtasks per device - Generate scan path binary files (.RecordLine3) - Multi-rectangle area planning with reference map support - Device-specific FOV defaults (Pika L 17.6°, Pika NIR 21.7°, etc.) - Timeline scheduling with constraint validation - Tauri 2.x + Vue 3 + Naive UI + Pinia
This commit is contained in:
59
src-tauri/src/models/mission.rs
Normal file
59
src-tauri/src/models/mission.rs
Normal file
@ -0,0 +1,59 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use super::task::Task;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MissionScanConfig {
|
||||
#[serde(rename = "xMin")]
|
||||
pub x_min: f64,
|
||||
#[serde(rename = "xMax")]
|
||||
pub x_max: f64,
|
||||
#[serde(rename = "yMin")]
|
||||
pub y_min: f64,
|
||||
#[serde(rename = "yMax")]
|
||||
pub y_max: f64,
|
||||
}
|
||||
|
||||
impl Default for MissionScanConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
x_min: 0.0,
|
||||
x_max: 100.0,
|
||||
y_min: 0.0,
|
||||
y_max: 100.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct MissionPlan {
|
||||
pub version: String,
|
||||
#[serde(rename = "taskCount")]
|
||||
pub task_count: u32,
|
||||
pub tasks: Vec<Task>,
|
||||
#[serde(rename = "scanConfig", default = "MissionScanConfig::default")]
|
||||
pub scan_config: MissionScanConfig,
|
||||
#[serde(rename = "backgroundImage", skip_serializing_if = "Option::is_none")]
|
||||
pub background_image: Option<String>,
|
||||
}
|
||||
|
||||
impl MissionPlan {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
version: "1.0".to_string(),
|
||||
task_count: 0,
|
||||
tasks: Vec::new(),
|
||||
scan_config: MissionScanConfig::default(),
|
||||
background_image: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_task_id(&self) -> u32 {
|
||||
self.tasks.iter().map(|t| t.id).max().unwrap_or(0) + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MissionPlan {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
9
src-tauri/src/models/mod.rs
Normal file
9
src-tauri/src/models/mod.rs
Normal file
@ -0,0 +1,9 @@
|
||||
pub mod mission;
|
||||
pub mod task;
|
||||
pub mod path_line;
|
||||
pub mod path_plan;
|
||||
|
||||
pub use mission::{MissionPlan, MissionScanConfig};
|
||||
pub use task::{SubTask, SubTaskType, Task};
|
||||
pub use path_line::{PathLineFile, PathLineRecord};
|
||||
pub use path_plan::{ScanMode, ScanParams, ScanRegion, CameraParams, PlannerDefaults};
|
||||
23
src-tauri/src/models/path_line.rs
Normal file
23
src-tauri/src/models/path_line.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PathLineRecord {
|
||||
#[serde(rename = "targetYPosition")]
|
||||
pub target_y_position: f64,
|
||||
#[serde(rename = "speedTargetYPosition")]
|
||||
pub speed_target_y_position: f64,
|
||||
#[serde(rename = "targetXMinPosition")]
|
||||
pub target_x_min_position: f64,
|
||||
#[serde(rename = "speedTargetXMinPosition")]
|
||||
pub speed_target_x_min_position: f64,
|
||||
#[serde(rename = "targetXMaxPosition")]
|
||||
pub target_x_max_position: f64,
|
||||
#[serde(rename = "speedTargetXMaxPosition")]
|
||||
pub speed_target_x_max_position: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PathLineFile {
|
||||
pub count: u64,
|
||||
pub records: Vec<PathLineRecord>,
|
||||
}
|
||||
73
src-tauri/src/models/path_plan.rs
Normal file
73
src-tauri/src/models/path_plan.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ScanRegion {
|
||||
#[serde(rename = "xMin")]
|
||||
pub x_min: f64,
|
||||
#[serde(rename = "xMax")]
|
||||
pub x_max: f64,
|
||||
#[serde(rename = "yMin")]
|
||||
pub y_min: f64,
|
||||
#[serde(rename = "yMax")]
|
||||
pub y_max: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CameraParams {
|
||||
#[serde(rename = "fovDegrees")]
|
||||
pub fov_degrees: f64,
|
||||
#[serde(rename = "heightCm")]
|
||||
pub height_cm: f64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ScanMode {
|
||||
Zigzag,
|
||||
OneWay,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ScanParams {
|
||||
pub region: ScanRegion,
|
||||
pub camera: CameraParams,
|
||||
#[serde(rename = "coverageRate")]
|
||||
pub coverage_rate: f64,
|
||||
#[serde(rename = "speedYCmS")]
|
||||
pub speed_y_cm_s: f64,
|
||||
#[serde(rename = "speedXStartCmS")]
|
||||
pub speed_x_start_cm_s: f64,
|
||||
#[serde(rename = "speedXScanCmS")]
|
||||
pub speed_x_scan_cm_s: f64,
|
||||
pub mode: ScanMode,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PlannerDefaults {
|
||||
#[serde(rename = "fovDegrees")]
|
||||
pub fov_degrees: f64,
|
||||
#[serde(rename = "heightCm")]
|
||||
pub height_cm: f64,
|
||||
#[serde(rename = "coverageRate")]
|
||||
pub coverage_rate: f64,
|
||||
#[serde(rename = "speedYCmS")]
|
||||
pub speed_y_cm_s: f64,
|
||||
#[serde(rename = "speedXStartCmS")]
|
||||
pub speed_x_start_cm_s: f64,
|
||||
#[serde(rename = "speedXScanCmS")]
|
||||
pub speed_x_scan_cm_s: f64,
|
||||
pub mode: String,
|
||||
}
|
||||
|
||||
impl Default for PlannerDefaults {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
fov_degrees: 30.0,
|
||||
height_cm: 50.0,
|
||||
coverage_rate: 30.0,
|
||||
speed_y_cm_s: 5.0,
|
||||
speed_x_start_cm_s: 20.0,
|
||||
speed_x_scan_cm_s: 10.0,
|
||||
mode: "Zigzag".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
130
src-tauri/src/models/task.rs
Normal file
130
src-tauri/src/models/task.rs
Normal file
@ -0,0 +1,130 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub enum SubTaskType {
|
||||
#[serde(rename = "HyperSpectual400_1000nm")]
|
||||
HyperSpectual400_1000nm,
|
||||
#[serde(rename = "HyperSpectual1000_1700nm")]
|
||||
HyperSpectual1000_1700nm,
|
||||
#[serde(rename = "SingleLensReflex")]
|
||||
SingleLensReflex,
|
||||
#[serde(rename = "DepthCamera")]
|
||||
DepthCamera,
|
||||
}
|
||||
|
||||
impl SubTaskType {
|
||||
pub fn is_hyperspectral(&self) -> bool {
|
||||
matches!(self, Self::HyperSpectual400_1000nm | Self::HyperSpectual1000_1700nm)
|
||||
}
|
||||
|
||||
pub fn is_camera_type(&self) -> bool {
|
||||
matches!(self, Self::SingleLensReflex | Self::DepthCamera)
|
||||
}
|
||||
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
Self::HyperSpectual400_1000nm => "Pika L",
|
||||
Self::HyperSpectual1000_1700nm => "Pika NIR",
|
||||
Self::SingleLensReflex => "单反相机",
|
||||
Self::DepthCamera => "深度相机",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SubTask {
|
||||
#[serde(default = "default_subtask_id")]
|
||||
pub id: String,
|
||||
#[serde(rename = "type")]
|
||||
pub sub_task_type: SubTaskType,
|
||||
#[serde(rename = "captureIntervalSeconds")]
|
||||
pub capture_interval_seconds: Option<u32>,
|
||||
#[serde(rename = "defaultRenderBand")]
|
||||
pub default_render_band: Option<u32>,
|
||||
#[serde(rename = "exposureTime")]
|
||||
pub exposure_time: Option<u32>,
|
||||
#[serde(rename = "frameRate")]
|
||||
pub frame_rate: Option<u32>,
|
||||
#[serde(rename = "pathLineFilePath")]
|
||||
pub path_line_file_path: String,
|
||||
#[serde(rename = "durationMinutes")]
|
||||
pub duration_minutes: f64,
|
||||
#[serde(rename = "estimatedDurationMinutes")]
|
||||
pub estimated_duration_minutes: f64,
|
||||
#[serde(rename = "endTime")]
|
||||
pub end_time: Option<String>,
|
||||
#[serde(rename = "startTime")]
|
||||
pub start_time: Option<String>,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
fn default_subtask_id() -> String {
|
||||
Uuid::new_v4().to_string()
|
||||
}
|
||||
|
||||
impl SubTask {
|
||||
pub fn new(sub_task_type: SubTaskType) -> Self {
|
||||
Self {
|
||||
id: default_subtask_id(),
|
||||
sub_task_type,
|
||||
capture_interval_seconds: None,
|
||||
default_render_band: Some(550),
|
||||
exposure_time: None,
|
||||
frame_rate: None,
|
||||
path_line_file_path: String::new(),
|
||||
duration_minutes: 0.0,
|
||||
estimated_duration_minutes: 0.0,
|
||||
end_time: None,
|
||||
start_time: None,
|
||||
status: "Waiting".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Task {
|
||||
pub id: u32,
|
||||
#[serde(rename = "savePath")]
|
||||
pub save_path: String,
|
||||
#[serde(rename = "scheduledTime")]
|
||||
pub scheduled_time: String,
|
||||
#[serde(rename = "HalogenLampPreheatingTime_Minute")]
|
||||
pub halogen_lamp_preheating_time_minute: f64,
|
||||
#[serde(rename = "durationMinutes")]
|
||||
pub duration_minutes: f64,
|
||||
#[serde(rename = "estimatedDurationMinutes")]
|
||||
pub estimated_duration_minutes: f64,
|
||||
#[serde(rename = "endTime")]
|
||||
pub end_time: Option<String>,
|
||||
#[serde(rename = "startTime")]
|
||||
pub start_time: Option<String>,
|
||||
pub status: String,
|
||||
#[serde(rename = "subTasks")]
|
||||
pub sub_tasks: Vec<SubTask>,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
pub fn new(id: u32) -> Self {
|
||||
Self {
|
||||
id,
|
||||
save_path: String::new(),
|
||||
scheduled_time: "2000-01-01T00:00:00".to_string(),
|
||||
halogen_lamp_preheating_time_minute: 0.1,
|
||||
duration_minutes: 0.0,
|
||||
estimated_duration_minutes: 0.0,
|
||||
end_time: None,
|
||||
start_time: None,
|
||||
status: "Waiting".to_string(),
|
||||
sub_tasks: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_hyperspectral(&self) -> bool {
|
||||
self.sub_tasks.iter().any(|s| s.sub_task_type.is_hyperspectral())
|
||||
}
|
||||
|
||||
pub fn has_type(&self, stype: &SubTaskType) -> bool {
|
||||
self.sub_tasks.iter().any(|s| s.sub_task_type == *stype)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user