324 lines
12 KiB
Rust
324 lines
12 KiB
Rust
use eframe::egui;
|
|
use plate_tool_lib::transfer_region::{self, Region, TransferRegion};
|
|
use std::hash::{DefaultHasher, Hash, Hasher};
|
|
use std::sync::{Arc, Mutex};
|
|
|
|
use crate::main_state::{self, 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_uuid();
|
|
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::warn!("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
|
|
}
|
|
|
|
pub fn convert_to_transfer(&self, ms: &MainState) -> Option<plate_tool_lib::transfer::Transfer> {
|
|
let source_plate_uuid = ms.get_current_source_uuid()?;
|
|
let destination_plate_uuid = ms.get_current_destination_uuid()?;
|
|
let source_plate_instance = ms.source_plates.iter().find(|x| x.get_uuid() == source_plate_uuid)?;
|
|
let destination_plate_instance = ms.destination_plates.iter().find(|x| x.get_uuid() == destination_plate_uuid)?;
|
|
let transfer = Some(plate_tool_lib::transfer::Transfer::new(
|
|
source_plate_instance.clone(),
|
|
destination_plate_instance.clone(),
|
|
self.generate_transfer_region(),
|
|
self.transfer_name.clone(),
|
|
));
|
|
transfer.map(|mut x| {x.volume = plate_tool_lib::transfer_volume::TransferVolume::Single(self.volume); x})
|
|
}
|
|
}
|
|
|
|
#[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,
|
|
main_state: &mut MainState,
|
|
) {
|
|
// Can we reduce the length of this lock pls
|
|
let cts = state;
|
|
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),
|
|
);
|
|
});
|
|
});
|
|
|
|
ui.horizontal(|ui| {
|
|
if ui.button("Save").clicked() {
|
|
if let Some(transfer_uuid) = main_state.get_current_transfer_uuid() {
|
|
log::info!("should change transfer");
|
|
if let Some(mut transfer) = main_state.transfers.iter_mut().find(|x| x.id == transfer_uuid) {
|
|
transfer.transfer_region = state.generate_transfer_region();
|
|
transfer.name = state.transfer_name.clone();
|
|
main_state.transfer_region_cache.invalidate(&transfer);
|
|
}
|
|
} else { // Need to make a new transfer
|
|
if state.transfer_name.is_empty() {
|
|
state.transfer_name = "New Transfer".to_string();
|
|
}
|
|
let new_transfer = state.convert_to_transfer(main_state);
|
|
log::info!("{:?}", new_transfer);
|
|
if let Some(new_transfer) = new_transfer {
|
|
main_state.transfers.push(new_transfer);
|
|
let new_transfer = main_state.transfers.last()
|
|
.expect("Cannot be empty, just added a transfer");
|
|
main_state.transfer_region_cache.add_overwrite(new_transfer);
|
|
main_state.set_current_transfer(new_transfer.id);
|
|
}
|
|
}
|
|
}
|
|
if ui.button("New").clicked() {
|
|
*state = CurrentTransferStateInterior::default();
|
|
set_plates(main_state, &mut state);
|
|
main_state.set_no_current_transfer();
|
|
}
|
|
});
|
|
}
|
|
|
|
fn set_plates(ms: &MainState, cts: &mut CurrentTransferStateInterior) {
|
|
if let Some(source_plate) = ms.get_current_source_plateinstance() {
|
|
cts.source_plate = source_plate.plate;
|
|
}
|
|
if let Some(destination_plate) = ms.get_current_destination_plateinstance() {
|
|
cts.destination_plate = destination_plate.plate;
|
|
}
|
|
}
|