specctra, egui: add menu option for specctra session export

This commit is contained in:
Tomasz Cichoń 2024-07-10 16:42:22 +02:00
parent 299d11b05f
commit 8c3a3f1e72
3 changed files with 129 additions and 2 deletions

View File

@ -76,6 +76,9 @@ pub struct App {
#[serde(skip)]
maybe_layers: Option<Layers>,
#[serde(skip)]
maybe_design: Option<SpecctraDesign>,
#[serde(skip)]
update_counter: f32,
}
@ -92,6 +95,7 @@ impl Default for App {
top: Top::new(),
bottom: Bottom::new(),
maybe_layers: None,
maybe_design: None,
update_counter: 0.0,
}
}
@ -124,6 +128,7 @@ impl App {
let board = design.make_board();
self.maybe_overlay = Some(Overlay::new(&board).unwrap());
self.maybe_layers = Some(Layers::new(&board));
self.maybe_design = Some(design);
self.arc_mutex_maybe_invoker = Arc::new(Mutex::new(Some(Invoker::new(
Autorouter::new(board).unwrap(),
))))
@ -165,6 +170,7 @@ impl eframe::App for App {
self.arc_mutex_maybe_invoker.clone(),
&mut self.maybe_execute,
&mut self.maybe_overlay,
&self.maybe_design,
);
if let Some(ref mut layers) = self.maybe_layers {

View File

@ -1,13 +1,14 @@
use std::{
fs::File,
sync::{mpsc::Sender, Arc, Mutex},
path::Path,
};
use topola::{
autorouter::invoker::{
Command, Execute, ExecuteWithStatus, Invoker, InvokerError, InvokerStatus,
},
specctra::mesadata::SpecctraMesadata,
specctra::{mesadata::SpecctraMesadata, design::SpecctraDesign},
};
use crate::{
@ -40,9 +41,12 @@ impl Top {
arc_mutex_maybe_invoker: Arc<Mutex<Option<Invoker<SpecctraMesadata>>>>,
maybe_execute: &mut Option<ExecuteWithStatus>,
maybe_overlay: &mut Option<Overlay>,
maybe_design: &Option<SpecctraDesign>,
) -> Result<(), InvokerError> {
let mut open_design =
Trigger::new(Action::new("Open", egui::Modifiers::CTRL, egui::Key::O));
let mut export_session =
Trigger::new(Action::new("Export session file", egui::Modifiers::CTRL, egui::Key::S));
let mut import_history = Trigger::new(Action::new(
"Import history",
egui::Modifiers::CTRL,
@ -72,6 +76,7 @@ impl Top {
egui::menu::bar(ui, |ui| {
ui.menu_button("File", |ui| {
open_design.button(ctx, ui);
export_session.button(ctx, ui);
ui.separator();
@ -119,6 +124,37 @@ impl Top {
ctx.request_repaint();
}
});
} else if export_session.consume_key_triggered(ctx, ui) {
if let Some(design) = maybe_design {
if let Some(invoker) =
arc_mutex_maybe_invoker.clone().lock().unwrap().as_ref()
{
let ctx = ui.ctx().clone();
let board = invoker.autorouter().board();
// FIXME: I don't know how to avoid buffering the entire exported file
let mut writebuf = vec![];
design.write_ses(board, &mut writebuf);
let mut dialog = rfd::AsyncFileDialog::new();
if let Some(filename) = Path::new(design.get_name()).file_stem() {
if let Some(filename) = filename.to_str() {
dialog = dialog.set_file_name(filename);
}
}
let task = dialog
.add_filter("Specctra session file", &["ses"])
.save_file();
execute(async move {
if let Some(file_handle) = task.await {
file_handle.write(&writebuf).await;
ctx.request_repaint();
}
});
}
}
} else if import_history.consume_key_triggered(ctx, ui) {
let ctx = ctx.clone();
let task = rfd::AsyncFileDialog::new().pick_file();

View File

@ -5,12 +5,23 @@ use thiserror::Error;
use crate::{
board::{mesadata::AccessMesadata, Board},
drawing::{dot::FixedDotWeight, seg::FixedSegWeight, Drawing},
drawing::{
dot::FixedDotWeight,
seg::FixedSegWeight,
Drawing,
graph::{GetMaybeNet, GetLayer, MakePrimitive},
primitive::MakePrimitiveShape
},
geometry::{
primitive::{PrimitiveShape},
GetWidth,
},
layout::{poly::SolidPolyWeight, Layout},
math::Circle,
specctra::{
mesadata::SpecctraMesadata,
read::{self, ListTokenizer},
write::{self, ListWriter},
structure::{self, DsnFile, Layer, Pcb, Shape},
},
};
@ -36,6 +47,80 @@ impl SpecctraDesign {
Ok(Self { pcb: dsn.pcb })
}
pub fn get_name(&self) -> &str {
&self.pcb.name
}
pub fn write_ses(
&self,
board: &Board<SpecctraMesadata>,
writer: impl std::io::Write,
) -> Result<(), std::io::Error> {
let mesadata = board.mesadata();
let drawing = board.layout().drawing();
//dbg!(&geometry);
let mut net_outs = HashMap::<usize, structure::NetOut>::new();
for index in drawing.primitive_nodes() {
let primitive = index.primitive(drawing);
match primitive.shape() {
PrimitiveShape::Seg(seg) => {
if let Some(net) = primitive.maybe_net() {
let net_name = mesadata.net_netname(net).unwrap().to_owned();
let wire = structure::Wire {
path: structure::Path {
layer: mesadata.layer_layername(primitive.layer()).unwrap().to_owned(),
width: primitive.width(),
coords: vec![
structure::Point { x: seg.from.x(), y: seg.from.y() },
structure::Point { x: seg.to.x(), y: seg.to.y() },
],
},
net: net_name.clone(),
r#type: "route".to_owned(),
};
if let Some(net) = net_outs.get_mut(&net) {
net.wire.push(wire);
} else {
net_outs.insert(
net,
structure::NetOut {
name: net_name.clone(),
wire: vec![wire],
via: Vec::new(),
},
);
}
}
},
_ => (),
}
}
let ses = structure::SesFile {
session: structure::Session {
id: "ID".to_string(),
routes: structure::Routes {
resolution: structure::Resolution {
unit: "um".into(),
value: 1.0,
},
library_out: structure::Library {
images: Vec::new(),
padstacks: Vec::new(),
},
network_out: structure::NetworkOut {
net: net_outs.into_values().collect(),
},
},
},
};
ListWriter::new(writer).write_value(&ses)
}
pub fn make_board(&self) -> Board<SpecctraMesadata> {
let mesadata = SpecctraMesadata::from_pcb(&self.pcb);
let mut board = Board::new(Layout::new(Drawing::new(