262 lines
9.4 KiB
Rust
262 lines
9.4 KiB
Rust
use eframe::egui;
|
|
use plate_tool_lib::transfer_region::{self, Region, TransferRegion};
|
|
use std::sync::{Arc, Mutex};
|
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
|
|
|
use crate::main_state::MainState;
|
|
|
|
pub type CurrentTransferState = Arc<Mutex<CurrentTransferStateInterior>>;
|
|
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
pub struct CurrentTransferStateInterior {
|
|
pub transfer_name: String,
|
|
pub source_region: plate_tool_lib::transfer_region::Region,
|
|
pub destination_region: plate_tool_lib::transfer_region::Region,
|
|
pub source_plate: plate_tool_lib::plate::Plate,
|
|
pub destination_plate: plate_tool_lib::plate::Plate,
|
|
pub source_row_interleave: i8,
|
|
pub source_column_interleave: i8,
|
|
pub destination_row_interleave: i8,
|
|
pub destination_column_interleave: i8,
|
|
pub volume: f32,
|
|
#[serde(skip)]
|
|
transfer_hash: Option<u64>,
|
|
#[serde(skip)]
|
|
source_wells: Option<Vec<plate_tool_lib::Well>>,
|
|
#[serde(skip)]
|
|
destination_wells: Option<Vec<plate_tool_lib::Well>>,
|
|
}
|
|
impl Default for CurrentTransferStateInterior {
|
|
fn default() -> Self {
|
|
Self {
|
|
transfer_name: String::default(),
|
|
source_region: plate_tool_lib::transfer_region::Region::Point(plate_tool_lib::Well {
|
|
row: 1,
|
|
col: 1,
|
|
}),
|
|
destination_region: plate_tool_lib::transfer_region::Region::Point(
|
|
plate_tool_lib::Well { row: 1, col: 1 },
|
|
),
|
|
source_plate: plate_tool_lib::plate::SOURCE_W96,
|
|
destination_plate: plate_tool_lib::plate::DESTINATION_W96,
|
|
source_row_interleave: 1,
|
|
source_column_interleave: 1,
|
|
destination_row_interleave: 1,
|
|
destination_column_interleave: 1,
|
|
volume: 5.0,
|
|
transfer_hash: None,
|
|
source_wells: None,
|
|
destination_wells: None,
|
|
}
|
|
}
|
|
}
|
|
impl CurrentTransferStateInterior {
|
|
pub fn get_source_wells(&mut self) -> Box<[plate_tool_lib::Well]> {
|
|
if self.source_wells.is_none() {
|
|
self.recalulate_wells();
|
|
}
|
|
self.recalculate_wells_if_needed();
|
|
self.source_wells
|
|
.clone()
|
|
.expect("Source wells must have been calculated by here.")
|
|
.into_boxed_slice()
|
|
}
|
|
pub fn get_destination_wells(&mut self) -> Box<[plate_tool_lib::Well]> {
|
|
if self.destination_wells.is_none() {
|
|
self.recalulate_wells();
|
|
}
|
|
self.recalculate_wells_if_needed();
|
|
self.destination_wells
|
|
.clone()
|
|
.expect("Destination wells must have been calculated by here.")
|
|
.into_boxed_slice()
|
|
}
|
|
|
|
fn recalculate_wells_if_needed(&mut self) {
|
|
if self.transfer_hash.is_none() {
|
|
self.recalulate_wells();
|
|
}
|
|
if self.transfer_hash.unwrap() != self.calculate_hash() {
|
|
self.recalulate_wells();
|
|
}
|
|
}
|
|
fn recalulate_wells(&mut self) {
|
|
let tr = self.generate_transfer_region();
|
|
|
|
self.source_wells = Some(tr.get_source_wells());
|
|
self.destination_wells = Some(tr.get_destination_wells());
|
|
self.transfer_hash = Some(self.calculate_hash());
|
|
}
|
|
fn calculate_hash(&self) -> u64 {
|
|
let mut hasher = DefaultHasher::new();
|
|
self.generate_transfer_region().hash(&mut hasher);
|
|
hasher.finish()
|
|
}
|
|
|
|
fn generate_transfer_region(&self) -> TransferRegion {
|
|
TransferRegion {
|
|
source_plate: self.source_plate,
|
|
source_region: self.source_region.clone(),
|
|
dest_plate: self.destination_plate,
|
|
dest_region: self.destination_region.clone(),
|
|
interleave_source: (self.source_row_interleave, self.source_column_interleave),
|
|
interleave_dest: (self.destination_row_interleave, self.destination_column_interleave),
|
|
}
|
|
}
|
|
|
|
pub fn try_new_from_main_state_transfer(ms: &MainState) -> Option<Self> {
|
|
let transfer_id = ms.get_current_transfer();
|
|
let transfer = ms.transfers.iter().find(|x| Some(x.get_uuid()) == transfer_id);
|
|
if let Some(transfer) = transfer {
|
|
let volume: f32 = match transfer.volume {
|
|
plate_tool_lib::transfer_volume::TransferVolume::Single(x) => x,
|
|
plate_tool_lib::transfer_volume::TransferVolume::WellMap(_) => {
|
|
log::debug!("COOL BUG: I genuinely don't know when this variant is used and honestly assume that it just never is constructed! Anyway, here's main state:\n{:?}", ms);
|
|
unreachable!("It better not!");
|
|
},
|
|
_ => unreachable!()
|
|
};
|
|
return Some(Self {
|
|
transfer_name: transfer.name.clone(),
|
|
source_region: transfer.transfer_region.source_region.clone(),
|
|
destination_region: transfer.transfer_region.dest_region.clone(),
|
|
source_plate: transfer.transfer_region.source_plate,
|
|
destination_plate: transfer.transfer_region.dest_plate,
|
|
source_row_interleave: transfer.transfer_region.interleave_source.0,
|
|
source_column_interleave: transfer.transfer_region.interleave_source.1,
|
|
destination_row_interleave: transfer.transfer_region.interleave_dest.0,
|
|
destination_column_interleave: transfer.transfer_region.interleave_dest.1,
|
|
volume,
|
|
source_wells: None,
|
|
destination_wells: None,
|
|
transfer_hash: None,
|
|
})
|
|
}
|
|
None
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TransferMenuState {
|
|
pub source_region_string: String,
|
|
pub destination_region_string: String,
|
|
}
|
|
|
|
impl Default for TransferMenuState {
|
|
fn default() -> Self {
|
|
TransferMenuState {
|
|
source_region_string: String::new(),
|
|
destination_region_string: String::new(),
|
|
}
|
|
}
|
|
}
|
|
impl TransferMenuState {
|
|
fn new_from_cts(cts: &CurrentTransferState) -> Self {
|
|
let cts = cts.lock().unwrap();
|
|
let source_region_string = cts.source_region.to_string();
|
|
let destination_region_string = cts.destination_region.to_string();
|
|
TransferMenuState {
|
|
source_region_string,
|
|
destination_region_string,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn transfer_menu(
|
|
ui: &mut egui::Ui,
|
|
state: &CurrentTransferState,
|
|
ui_state: &mut TransferMenuState,
|
|
) {
|
|
// Can we reduce the length of this lock pls
|
|
let mut state = state.lock().unwrap();
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.add(egui::Label::new("Name"));
|
|
ui.add(
|
|
egui::TextEdit::singleline(&mut state.transfer_name)
|
|
.hint_text("Transfer Name")
|
|
.horizontal_align(egui::Align::Center),
|
|
);
|
|
});
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.add(egui::Label::new("Source Region"));
|
|
let resp = ui.add(
|
|
egui::TextEdit::singleline(&mut ui_state.source_region_string)
|
|
.hint_text("Source Region")
|
|
.char_limit(9)
|
|
.horizontal_align(egui::Align::Center),
|
|
);
|
|
if resp.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
|
if let Some(new_region) = Region::try_parse_str(&ui_state.source_region_string) {
|
|
state.source_region = new_region;
|
|
} else {
|
|
log::warn!("Invalid source region entered.")
|
|
}
|
|
}
|
|
if !resp.has_focus() && !resp.lost_focus() {
|
|
ui_state.source_region_string = state.source_region.to_string();
|
|
}
|
|
});
|
|
ui.end_row();
|
|
|
|
ui.horizontal(|ui| {
|
|
ui.add(egui::Label::new("Destination Region"));
|
|
let resp = ui.add(
|
|
egui::TextEdit::singleline(&mut ui_state.destination_region_string)
|
|
.hint_text("Destination Region")
|
|
.char_limit(9)
|
|
.horizontal_align(egui::Align::Center),
|
|
);
|
|
if resp.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
|
|
if let Some(new_region) = Region::try_parse_str(&ui_state.destination_region_string) {
|
|
state.destination_region = new_region;
|
|
} else {
|
|
log::warn!("Invalid destination region entered.")
|
|
}
|
|
}
|
|
if !resp.has_focus() {
|
|
ui_state.destination_region_string = state.destination_region.to_string();
|
|
}
|
|
});
|
|
|
|
ui.vertical(|ui| {
|
|
ui.label("Source Interleave");
|
|
ui.horizontal(|ui| {
|
|
ui.label("Row: ");
|
|
ui.add(
|
|
egui::DragValue::new(&mut state.source_row_interleave)
|
|
.fixed_decimals(0)
|
|
.range(1..=30)
|
|
.speed(0.1),
|
|
);
|
|
ui.label("Col: ");
|
|
ui.add(
|
|
egui::DragValue::new(&mut state.source_column_interleave)
|
|
.fixed_decimals(0)
|
|
.range(1..=30)
|
|
.speed(0.1),
|
|
);
|
|
});
|
|
});
|
|
|
|
ui.vertical(|ui| {
|
|
ui.label("Destination Interleave");
|
|
ui.horizontal(|ui| {
|
|
ui.label("Row: ");
|
|
ui.add(
|
|
egui::DragValue::new(&mut state.destination_row_interleave)
|
|
.fixed_decimals(0)
|
|
.range(0..=30)
|
|
.speed(0.1),
|
|
);
|
|
ui.label("Col: ");
|
|
ui.add(
|
|
egui::DragValue::new(&mut state.destination_column_interleave)
|
|
.fixed_decimals(0)
|
|
.range(0..=30)
|
|
.speed(0.1),
|
|
);
|
|
});
|
|
});
|
|
}
|