Files
MXSC/src/main/java/ch/psi/mxsc/Controller.java
2026-03-25 11:02:53 +01:00

1290 lines
45 KiB
Java

/*
* Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
*/
package ch.psi.mxsc;
import ch.psi.mxsc.Puck.Detection;
import ch.psi.mxsc.Puck.PuckType;
import ch.psi.pshell.sequencer.CommandInfo;
import ch.psi.pshell.framework.Context;
import ch.psi.pshell.devices.DevicePool;
import ch.psi.pshell.devices.DevicePoolListener;
import ch.psi.pshell.utils.EncoderJson;
import ch.psi.pshell.device.Device;
import ch.psi.pshell.device.DeviceListener;
import ch.psi.pshell.device.GenericDevice;
import ch.psi.pshell.app.App;
import ch.psi.pshell.framework.Panel;
import ch.psi.pshell.framework.Setup;
import ch.psi.pshell.utils.Arr;
import ch.psi.pshell.utils.Audio;
import ch.psi.pshell.utils.Chrono;
import ch.psi.pshell.utils.State;
import ch.psi.pshell.swing.SwingUtils;
import ch.psi.pshell.utils.State.StateException;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.script.ScriptException;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
/**
*
*/
public class Controller {
final boolean DIALOG_PUCK_LOAD_SHOW_BASEPLATE = true;
final int REMOVE_PUCK_TIMEOUT = 20000;
static Controller instance;
final BasePlate basePlate;
RoomTemperatureBasePlate roomTemperatureBasePlate;
static /*Panel*/ MainPanel mainFrame;
Device barcode_reader;
Device barcode_reader_puck;
PuckDetection puck_detection;
String currentMountedSample;
public static Controller getInstance() {
return instance;
}
public static boolean isRt() {
return Controller.mainFrame.isRt();
}
static void createInstance(Panel mainFrame) {
Controller.mainFrame = (MainPanel) mainFrame;
//### System.setProperty(GenericDevice.PROPERTY_CONFIG_PATH, Setup.getDevicesPath());
if (isRt()){
instance = new ControllerRT(mainFrame);
} else {
instance = new Controller(mainFrame);
}
}
enum PuckMountMode {
Direct,
Dialog
}
final PuckMountMode puckMountMode = App.hasAdditionalArgument("direct") ? PuckMountMode.Direct : PuckMountMode.Dialog;
enum PuckSensorAccess {
RaspberryPi,
Esera;
}
public enum PuckTypes {
unipuck,
minispine,
mixed
}
PuckTypes puckTypes = PuckTypes.mixed;
public PuckTypes getPuckTypes() {
return puckTypes;
}
void updatePuckTypes() {
try {
puckTypes = PuckTypes.valueOf(getMainFrame().eval("get_puck_types()", true).toString());
} catch (Exception ex) {
puckTypes = PuckTypes.mixed;
}
switch (puckTypes) {
case unipuck:
setSinglePuckType(PuckType.Unipuck);
break;
case minispine:
setSinglePuckType(PuckType.Minispine);
break;
case mixed:
//setSinglePuckType(PuckType.Unknown);
break;
}
}
void setSinglePuckType(Puck.PuckType puckType) {
for (Puck p : basePlate.getPucks()) {
p.setPuckType(puckType);
}
}
void resetPuckTypes() {
for (Puck p : basePlate.getPucks()) {
p.setPuckType(Puck.PuckType.Unknown);
}
}
final PuckSensorAccess puckSensorAccess = PuckSensorAccess.RaspberryPi;
static String PUCK_ESERA_DEVICE = "onewire";
public static final int NUMBER_OF_PUCKS = BasePlate.numberOfPucks;
protected Controller(){
basePlate = new BasePlate();
instance = this;
puckState = new PuckState[NUMBER_OF_PUCKS];
for (int i = 0; i < NUMBER_OF_PUCKS; i++) {
puckState[i] = new PuckState(i + 1);
}
}
private Controller(Panel mainFrame) {
this();
//basePlate = new BasePlate();
clearPuckStates();
basePlate.addListener(basePlateListener);
roomTemperatureBasePlate = new RoomTemperatureBasePlate();
}
final DeviceListener basePlateListener = new DeviceListener() {
@Override
public void onValueChanged(Device device, Object value, Object former) {
if (value != null) {
String segment = ((Object[]) value)[0].toString();
Integer puck = (Integer) ((Object[]) value)[1];
Integer sample = (Integer) ((Object[]) value)[2];
Controller.this.mainFrame.onSelectionChanged(segment, puck, sample);
} else {
Controller.this.mainFrame.onSelectionChanged(null, null, null);
}
}
};
String getCurrentSelection() {
Object value = basePlate.take();
if (value != null) {
String segment = ((Object[]) value)[0].toString();
Integer puck = (Integer) ((Object[]) value)[1];
Integer sample = (Integer) ((Object[]) value)[2];
String ret = segment + String.valueOf(puck);
if (sample != null) {
ret = ret + String.valueOf(sample);
}
return ret;
}
return null;
}
public void selectPuck(Puck puck) {
getMainFrame().basePlatePanel.selectPuck(puck);
}
public void selectSample(Sample sample) {
getMainFrame().basePlatePanel.selectSample(sample);
if ((puckPanel != null) && puckPanel.isShowing()) {
puckPanel.selectSample(sample);
}
}
//public Panel getMainFrame() {
public MainPanel getMainFrame() {
return mainFrame;
}
public void updateView() {
getMainFrame().refresh();
}
void onInitialize(int runCount) {
getMainFrame().addDevice(basePlate);
basePlate.addListener(basePlateListener);
if (puckSensorAccess == PuckSensorAccess.Esera) {
Context.getDevicePool().addListener(new DevicePoolListener() {
@Override
public void onDeviceAdded(GenericDevice dev) {
if (dev.getName().equals(PUCK_ESERA_DEVICE)) {
detection = new EseraDetection((Device) dev);
}
}
@Override
public void onDeviceRemoved(GenericDevice dev) {
if (dev.getName().equals(PUCK_ESERA_DEVICE)) {
detection.close();
detection = null;
}
}
});
if (detection != null) {
detection.close();
detection = null;
}
if ((Device) getMainFrame().getDevice(PUCK_ESERA_DEVICE) != null) {
detection = new EseraDetection((Device) getMainFrame().getDevice(PUCK_ESERA_DEVICE));
}
}
getDevicePool().addListener(new DevicePoolListener() {
@Override
public void onDeviceAdded(GenericDevice dev) {
updateDevices();
}
@Override
public void onDeviceRemoved(GenericDevice dev) {
}
});
updateDevices();
updatePuckTypes();
clearSamplesTable();
refreshSamplesTable();
}
public void onStateChange(State state, State former) {
if (state == State.Initializing) {
getMainFrame().removeDevice(basePlate);
} else if (state == State.Ready) {
refreshSamplesTable();
try {
Sample sample = getMountedSample();
if ((sample == null) && (roomTemperatureBasePlate != null)) {
sample = roomTemperatureBasePlate.getSampleByName(currentMountedSample);
roomTemperatureBasePlate.resetLoadedSample();
}
basePlate.resetLoadedSample();
if (sample != null) {
sample.setLoaded(true);
}
} catch (Exception ex) {
currentMountedSample = null;
basePlate.resetLoadedSample();
}
getMainFrame().refresh();
}
logStateChange(state);
}
void onTimer() {
try {
//setPuckLoading(null);
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.WARNING, null, ex);
}
try {
refreshSamplesTable();
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.WARNING, null, ex);
}
}
final DeviceListener barcodeReaderListener = new DeviceListener() {
@Override
public void onValueChanged(Device device, Object value, Object former) {
if (value != null) { //Keep last value
String valStr = value.toString().trim();
if (isBarcodeReaderScanPucks()) {
onPuckBarcode(valStr);
} else {
onSampleBarcode(valStr);
}
}
}
};
final DeviceListener barcodeReaderPuckListener = new DeviceListener() {
@Override
public void onValueChanged(Device device, Object value, Object former) {
if (value != null) { //Keep last value
String valStr = value.toString().trim();
onPuckBarcode(valStr);
}
}
};
final DeviceListener puckDetectionListener = new DeviceListener() {
@Override
public void onValueChanged(Device device, Object value, Object former) {
if (puck_detection.isEnabled()){
if (value != null) { //Keep last value
onPuckDetectionChanged();
}
}
}
};
void updateDevices() {
barcode_reader = (Device) getDevice("barcode_reader");
if (barcode_reader != null) {
barcode_reader.addListener(barcodeReaderListener);
} else {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, "No barcode_reader detected.");
}
barcode_reader_puck = (Device) getDevice("barcode_reader_puck");
if (barcode_reader_puck != null) {
barcode_reader_puck.addListener(barcodeReaderPuckListener);
} else {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, "No barcode_reader_puck detected.");
}
puck_detection = (PuckDetection) getDevice("puck_detection");
if (puck_detection != null) {
puck_detection.addListener(puckDetectionListener);
} else {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, "No puck_detection detected.");
}
checkPuckDetectionEnabling();
}
GenericDevice getDevice(String name) {
return getMainFrame().getDevice(name);
}
DevicePool getDevicePool() {
return Context.getDevicePool();
}
State getState() {
return getMainFrame().getState();
}
void execute(String statement) {
getMainFrame().execute(statement);
}
void execute(String statement, boolean background) {
getMainFrame().execute(statement, background);
}
void execute(String statement, boolean background, boolean showReturn) {
getMainFrame().execute(statement, background, showReturn);
}
void execute(String statement, boolean background, boolean showReturn, boolean showException) {
getMainFrame().execute(statement, background, showReturn, showException);
}
////////////////////// Puck image detection /////////////////////////////////
public void imageDetectPucks() throws StateException {
imageDetectPucks(null, null, null);
}
public void imageDetectPucks(JComponent plot, JComponent renderer, JComponent text) throws StateException {
Map args = new HashMap();
args.put("PLOT", plot);
args.put("RENDERER", renderer);
args.put("TEXT", text);
getMainFrame().runAsync("imgproc/LedDetectionProc", args).handle((ret, ex) -> {
if (ex == null) {
Map<String, List<String>> map = (Map<String, List<String>>) ret;
setImageDetection(map);
} else {
getMainFrame().showException((Exception) ex);
}
return ret;
});
updateView();
}
public void clearImageDetection() throws StateException, ScriptException, IOException, InterruptedException {
Map<String, List<String>> map = (Map<String, List<String>>) getMainFrame().eval("clear_detection(None)");
setImageDetection(map);
}
void setImageDetection(Map<String, List<String>> map) {
for (Puck.PuckType puckType : Puck.PuckType.values()) {
for (String name : map.get(puckType.toString())) {
Puck p = basePlate.getPuckByName(name);
if (p != null) {
p.setPuckType(puckType);
}
}
}
updateView();
}
////////////////////// BasePlatePanel callbacks /////////////////////////////////
BasePlatePanel puckPanel;
void onPuckPressed(Puck puck) {
//JPanel panel = new SinglePuckPanel(puck);
//panel.setOpaque(false);
//getMainFrame().setDetail(panel);
if ((puckPanel == null) || (puckPanel != getMainFrame().getDetail())) {
puckPanel = new BasePlatePanel();
puckPanel.setMode(BasePlatePanel.Mode.puck);
puckPanel.setSelectionMode(BasePlatePanel.SelectionMode.Samples);
puckPanel.setDevice((Device) getDevice("BasePlate"));
puckPanel.setEnabled(true); //TODO: Puck cannot be shared between two panels (device store single coordinates foe comparing to click)
getMainFrame().setDetail(puckPanel);
}
}
void onPuckReleased(Puck puck) {
if (!puck.isSelected()) {
getMainFrame().setDefaultDetail();
puckPanel = null;
}
}
void onSamplePressed(Sample sample) {
onPuckPressed((sample==null)? null : sample.getPuck());
}
void onSampleReleased(Sample sample) {
onPuckReleased(sample.getPuck());
}
void onPlatePressed(BasePlate plate) {
if (plate.getSelectedPuck() == null) {
getMainFrame().setDefaultDetail();
puckPanel = null;
}
}
void onPlateReleased(BasePlate palte) {
}
////////////////////// Puck & Sample Info /////////////////////////////////
PuckState[] puckState;
public PuckState[] getPuckStates() {
return puckState;
}
public Puck getPuck(String name) {
return basePlate.getPuckByName(name);
}
public Sample getSample(String name) {
return basePlate.getSampleByName(name);
}
public BasePlate getBasePlate() {
return basePlate;
}
EseraDetection detection;
//From 1 to PUCKS_NUMBER
public PuckState getPuckState(int id) throws Exception {
if ((id <= 0) || (id > NUMBER_OF_PUCKS)) {
throw new Exception("invalid puck id: " + id);
}
return getPuckStates()[id - 1];
}
public int getPuckIndex(int address) throws Exception {
if ((address <= 0) || (address > NUMBER_OF_PUCKS)) {
throw new Exception("invalid puck address: " + address);
}
for (int i = 0; i < Puck.ADDRESSES.length; i++) {
if (Puck.ADDRESSES[i] == address) {
return i + 1;
}
}
return -1;
}
public void clearPuckStates() {
for (PuckState puck : getPuckStates()) {
puck.clear();
}
updateView();
}
public List<Puck> getFreePucks() {
List<Puck> ret = new ArrayList<>();
for (Puck p : basePlate.getPucks()) {
if (!p.isDisabled() && (p.getDetection() == Detection.Empty )) {
ret.add(p);
}
}
return ret;
}
public Puck getFreePuck(){
List<Puck> pucks = getFreePucks();
if (pucks.isEmpty()){
return null;
}
Random random = new Random();
return pucks.get(random.nextInt(pucks.size()));
}
String samplesTableJson;
//final Object samplesTableLock = new Object();
void refreshSamplesTable() {
//synchronized(samplesTableLock){
SwingUtilities.invokeLater(() -> {
try {
if (Context.getSequencer().getState().isInitialized()) {
String json = (String) Context.getSequencer().evalLineBackground("get_samples_info()");
if (!json.equals(samplesTableJson)) {
samplesTableJson = json;
//SamplesInfo sampleInfo = (SamplesInfo) EncoderJson.decode(json, SampleInfo.class);
SampleInfo[] samples = (SampleInfo[]) EncoderJson.decode(json, SampleInfo[].class);
Object[][] sampleData = new Object[samples.length][];
for (int i = 0; i < samples.length; i++) {
sampleData[i] = samples[i].getData();
}
getMainFrame().setSamplesTable(sampleData);
}
}
} catch (Exception ex) {
clearSamplesTable();
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
});
//}
}
void clearSamplesTable() {
samplesTableJson = null;
getMainFrame().setSamplesTable(new Object[][]{});
}
Map getPuckDatamatrix() {
try {
return (Map) Context.getSequencer().evalLineBackground("get_puck_datamatrix()");
} catch (Exception ex) {
return null;
}
}
Map getPuckDatamatrixInfo() {
try {
return (Map) Context.getSequencer().evalLineBackground("get_puck_datamatrix_info()");
} catch (Exception ex) {
return null;
}
}
String getPuckFromDatamatrix(String dm){
try {
return (String) Context.getSequencer().evalLineBackground("get_puck_from_datamatrix('" + dm + "')");
} catch (Exception ex) {
return null;
}
}
public Sample getMountedSample() throws Exception{
String mountedSample = (String) Context.getSequencer().evalLineBackground("get_setting('mounted_sample_position')");
checkMountedSampleChange(currentMountedSample, mountedSample);
currentMountedSample = mountedSample;
Sample sample = basePlate.getSampleByName(currentMountedSample);
return sample;
}
public void resetMountedSample() throws Exception{
String mountedSample = (String) Context.getSequencer().evalLineBackground("get_setting('mounted_sample_position')");
checkMountedSampleChange(currentMountedSample, null);
Context.getSequencer().evalLineBackground("set_setting('mounted_sample_position', None)");
}
void checkMountedSampleChange(String former, String current){
if (((current == null) && (former!=null)) || ((current != null) && (!current.equals(former)))){
logSampleMounted(former, current);
}
}
////////////////////// Device callbacks /////////////////////////////////
Point fmm;
public void onCoverDetection(Point fmm ){
this.fmm = fmm;
checkPuckLoading();
}
public boolean isCoverDetected() {
return fmm!=null;
}
boolean doorsOpened;
public void onDoorsOpenChange(boolean value){
doorsOpened = value;
doorsHaveOpened =value;
checkPuckLoading();
}
public void onSampleBarcode(String datamatrix) {
getMainFrame().setSampleDatamatrix(datamatrix);
System.out.println("Detected Sample: " + datamatrix);
}
public void onPuckBarcode(String datamatrix) {
if (isPuckLoading()) {
playSound("scanned");
System.out.println("Detected Puck: " + datamatrix);
onPuckScanned(datamatrix);
}
removedPuck = null;
}
Puck.Detection[] currentDetection;
void onPuckDetectionChanged() {
if (isPuckLoading()) {
Puck.Detection[] detection = basePlate.getDetection();
for (int i = 0; i < Controller.NUMBER_OF_PUCKS; i++) {
Puck puck = basePlate.getPucks()[i];
boolean detectedPuckInserted = (currentDetection[i] != Puck.Detection.Present) && (detection[i] == Puck.Detection.Present);
boolean detectedPuckRemoved = (currentDetection[i] != Puck.Detection.Empty) && (detection[i] == Puck.Detection.Empty);
if (detectedPuckInserted) {
onPuckMounted(puck);
} else if (detectedPuckRemoved) {
onPuckUnmounted(puck);
}
}
currentDetection = detection;
}
}
////////////////////// Puck Loading /////////////////////////////////
PuckLoadingDialog dialogPuckLoading;
JDialog dialogAskPuckDatamatrix;
boolean puckLoading;
boolean puckReaderOk = true;
String insertedPuckDatamatrix;
Puck removedPuck;
boolean doorsHaveOpened;
//Enables/disables puck loading mode. Opens dialod if PuckMountMode is Dialog.
void setPuckLoading(boolean load) {
if (load != puckLoading) {
if (load){
if (!canSetPuckLoading()){
Logger.getLogger(Controller.class.getName()).warning("Cannot set puck loading");
return;
}
insertedPuckDatamatrix = null;
removedPuck = null;
} else {
setLaserPos((Puck)null);
}
if ((dialogAskPuckDatamatrix != null) && (dialogAskPuckDatamatrix.isShowing())) {
dialogAskPuckDatamatrix.setVisible(false);
}
hideDialogPuckLoading();
puckLoading = load;
onPuckScanned(null);
Device reader = getPuckBarcodeReader();
if (reader != null) {
final String name = reader.getName();
BiFunction errorHandler = (BiFunction) (ret, ex) -> {
if (ex != null) {
if (puckReaderOk) {
Logger.getLogger(Controller.class.getName()).warning("Communication failure: " + name);
}
puckLoading = false;
puckReaderOk = false;
} else {
if (!puckReaderOk) {
Logger.getLogger(Controller.class.getName()).warning("Communication resumed: " + name);
}
puckReaderOk = true;
}
return ret;
};
try {
if (puckLoading) {
getMainFrame().evalAsync(name + ".enable(); " + name + ".polling = 100", true).handle(errorHandler);
currentDetection = basePlate.getDetection();
} else if (getState().isInitialized()) {
getMainFrame().evalAsync(name + ".polling = 0; " + name + ".disable()", true).handle(errorHandler);
onPuckScanned(null);
}
getMainFrame().evalAsync("onPuckLoadingChange(" + (puckLoading ? "True" : "False") + ")");
} catch (Exception ex) {
errorHandler.apply(null, ex);
}
if ( hasLoadDialog()) {
if (puckLoading) {
showDialogPuckLoading();
if (!DIALOG_PUCK_LOAD_SHOW_BASEPLATE){
mainFrame.setViewDesign();
}
} else {
hideDialogPuckLoading();
if (!DIALOG_PUCK_LOAD_SHOW_BASEPLATE){
mainFrame.setViewCamera();
}
}
}
}
} else {
if (puckLoading) {
if ((dialogPuckLoading != null) && (dialogPuckLoading.isVisible())) {
dialogPuckLoading.restore();
}
}
}
}
//Returns if can switch to puck loading mode: no cover, doors open and robot parked. Or else, service mode.
public boolean canSetPuckLoading(){
if (!getState().isInitialized()){
return false;
}
if (isServiceMode()){
return true;
}
return !isCoverDetected() && isDoorOpen() && isRobotParked();
}
//Reset puck loading mode id any condition is false - closing the dialog
private void checkPuckLoading(){
try{
if (isServiceMode()){
//Open/close manually in service mode
} else {
if (!canSetPuckLoading()){
setPuckLoading(false);
} else {
//Opens automatically once after door is oppen
if (doorsHaveOpened){
doorsHaveOpened=false;
//setPuckLoading(true);
}
}
}
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
//Returns if puck loading mode is enabled.
public boolean isPuckLoading() {
try {
if (isPuckDetectionLoading()){
if (!puck_detection.isEnabled()){
//Does not process load events if checking missing pucks before re-enabling detection
return false;
}
}
} catch (IOException ex) {
Logger.getLogger(Controller.class.getName()).log(Level.WARNING, null, ex);
}
return puckLoading;
}
//Returns if puck detection is enabled during puck loading only (puck_detection == "loading")
Boolean puckDetectionLoading;
public boolean isPuckDetectionLoading() throws IOException{
if (puckDetectionLoading==null){
puckDetectionLoading = "loading".equals(Context.getSetting("puck_detection"));
}
return puckDetectionLoading;
}
//Manages puck detection enabling/disabling if isPuckDetectionLoading()==true
public void checkPuckDetectionEnabling(){
boolean showingPuckLoadingDialog;
try{
showingPuckLoadingDialog = (dialogPuckLoading != null) && (dialogPuckLoading.isShowing());
if ( isPuckDetectionLoading()){
if (showingPuckLoadingDialog){
var werePresent = new ArrayList<String>();
Puck[] pucks = basePlate.getPucks();
for (Puck p : pucks){
if (p.getDetection() == Puck.Detection.Present){
werePresent.add(p.getName());
}
}
puck_detection.applyCache();
var present = new ArrayList<String>();
for (Puck p : pucks){
if (p.getDetection() == Puck.Detection.Present){
present.add(p.getName());
}
}
var removed = new ArrayList<>(werePresent);
removed.removeAll(present);
if (removed.size()>0){
PuckDetectionErrorDialog dlg = new PuckDetectionErrorDialog(dialogPuckLoading, false);
dlg.initialize(removed);
dlg.setLocationRelativeTo(dialogPuckLoading);
dlg.setVisible(true);
for (String address: removed){
Puck puck = basePlate.getPuckByName(address);
clearPuckMountedSample(puck);
Controller.getInstance().linkPuckDatamatrix(puck, null, false);
}
}
}
puck_detection.setEnabled(showingPuckLoadingDialog);
}
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.WARNING, null, ex);
}
}
//Shows puck loading dialog
void showDialogPuckLoading() {
if ((dialogPuckLoading != null) && (dialogPuckLoading.isVisible())) {
return;
}
dialogPuckLoading = new PuckLoadingDialog(mainFrame.getTopLevel(), false, false, DIALOG_PUCK_LOAD_SHOW_BASEPLATE);
dialogPuckLoading.setVisible(true);
checkPuckDetectionEnabling();
dialogPuckLoading.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
try{
setPuckLoading(false);
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.WARNING, null, ex);
}
}
@Override
public void windowClosed(WindowEvent e) {
checkPuckDetectionEnabling();
}
});
}
//Hides puck loading dialog
void hideDialogPuckLoading() {
if (dialogPuckLoading != null) {
dialogPuckLoading.setVisible(false);
dialogPuckLoading.dispose();
dialogPuckLoading = null;
try {
List puckInfo = (List) getMainFrame().eval("get_puck_info()", true);
Context.getSequencer().sendEvent("DewarContentUpdate", puckInfo);
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
//Called when a puck is scanned
public void onPuckScanned(String datamatrix) {
Logger.getLogger(Controller.class.getName()).info("Scanned puck: " + datamatrix);
if (isPuckLoading()) {
getMainFrame().setPuckDatamatrix(datamatrix);
if ( hasLoadDialog()) {
showDialogPuckLoading();
dialogPuckLoading.onPuckScanned(datamatrix);
}
} else {
getMainFrame().setPuckDatamatrix(null);
}
}
//Called when a puck is detected inserted in the dewar
public void onPuckMounted(Puck puck) {
String datamatrix = getMainFrame().getPuckDatamatrix();
Logger.getLogger(Controller.class.getName()).info("Mounted puck: " + puck.getName() + " - datamatrix:" + datamatrix);
playSound("mounted");
logPuckDetectionChange(puck, true);
onPuckScanned(null);
//If no new puck is scanned and detected mounted in the same place of the last unmount,
//considers the detection is flickering (up to a timeout)
if ((datamatrix==null) || (datamatrix.isBlank())){
if (puck == removedPuck){
if ((System.currentTimeMillis() - removeTimestamp) < REMOVE_PUCK_TIMEOUT){
datamatrix = insertedPuckDatamatrix;
}
} else {
removedPuck = null;
}
}
if ((datamatrix != null) && (!datamatrix.isEmpty())) {
Controller.getInstance().linkPuckDatamatrix(puck, datamatrix, !hasLoadDialog());
}
if (hasLoadDialog()) {
showDialogPuckLoading();
dialogPuckLoading.onPuckMounted(puck, datamatrix);
} else {
if ((datamatrix == null) || (datamatrix.isEmpty())){
askPuckDatamatrix(puck);
}
}
insertedPuckDatamatrix = datamatrix;
removedPuck = null;
}
long removeTimestamp;
//Called when a puck is detected removed from the the dewar
public void onPuckUnmounted(Puck puck) {
Logger.getLogger(Controller.class.getName()).info("Unmounted puck: " + puck.getName());
playSound("unmounted");
clearPuckMountedSample(puck);
Controller.getInstance().linkPuckDatamatrix(puck, null, !hasLoadDialog());
if ( hasLoadDialog()) {
showDialogPuckLoading();
dialogPuckLoading.onPuckUnmounted(puck);
}
removedPuck = puck;
removeTimestamp = System.currentTimeMillis();
}
public boolean hasLoadDialog(){
return(puckMountMode == PuckMountMode.Dialog);
}
//Links a puck to a datamatrix, or removes the link is datamatrix=null.
//This is called by puck detectiokn events and also when puck is manually linked to a datamatrix.
public void linkPuckDatamatrix(Puck puck, String datamatrix, boolean showMessage) {
// if ( ((puck.getId()==null) && (datamatrix!=null)) ||
// (puck.getId()!=null) && (!puck.getId().equals(datamatrix))){
String puckName = (puck == null) ? "" : puck.getName();
if (datamatrix == null) {
datamatrix = "";
}
datamatrix = datamatrix.trim();
System.out.println("Setting datamatrix '" + datamatrix + "' to puck: " + puckName);
try {
Context.getSequencer().evalLineBackground("set_puck_datamatrix('" + puckName + "','" + datamatrix + "')");
if (puck != null) {
puck.setId(datamatrix);
} else {
basePlate.clearId(datamatrix);
}
if (showMessage && (puck != null) && !datamatrix.isEmpty()) {
SwingUtils.showMessage(getMainFrame(), "Puck loading",
"Puck '" + datamatrix + "' set to position " + puckName,
5000);
}
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
refreshSamplesTable();
getMainFrame().refresh();
// }
}
//Manual selection of a datamatrix for a detected puck if does not have a loading dialog
void askPuckDatamatrix(Puck puck) {
if ((dialogAskPuckDatamatrix != null) && (dialogAskPuckDatamatrix.isShowing())) {
dialogAskPuckDatamatrix.setVisible(false);
}
if (puck == null) {
return;
}
Map dms = getPuckDatamatrix();
if ((dms != null) && (dms.size() > 0)) {
JTable table = new JTable();
List<Object[]> list = new ArrayList<>();
for (Object dm : dms.keySet()) {
Object address = dms.get(dm);
list.add(new Object[]{dm, address});
}
table.setModel(new DefaultTableModel(
list.toArray(new Object[0][]),
new String[]{
"Datamatrix", "Address"
}
) {
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
});
JLabel label = new JLabel("Select datamatrix for puck at " + puck.getName() + ":");
label.setFont(label.getFont().deriveFont(20.0f));
table.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
table.setFont(table.getFont().deriveFont(28.0f));
table.setRowHeight(40);
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(table);
JPanel panel = new JPanel();
JButton ok = new JButton("OK");
JButton cancel = new JButton("Cancel");
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BorderLayout());
buttonPanel.add(cancel, BorderLayout.WEST);
buttonPanel.add(ok, BorderLayout.EAST);
panel.setLayout(new BorderLayout());
panel.add(label, BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);
panel.add(buttonPanel, BorderLayout.SOUTH);
ok.setPreferredSize(new Dimension(200, ok.getPreferredSize().height));
cancel.setPreferredSize(new Dimension(200, ok.getPreferredSize().height));
dialogAskPuckDatamatrix = SwingUtils.showDialog(getMainFrame().getTopLevel(), "Puck Loading", new Dimension(400, 600), panel);
cancel.addActionListener((ev) -> {
dialogAskPuckDatamatrix.setVisible(false);
});
ok.addActionListener((ev) -> {
int row = table.getSelectedRow();
String dm = (row >= 0) ? String.valueOf(table.getValueAt(row, 0)) : null;
dialogAskPuckDatamatrix.setVisible(false);
if (dm != null) {
linkPuckDatamatrix(puck, dm, true);
}
});
}
}
//Clear mounted flag of samples of removed puck.
void clearPuckMountedSample(Puck puck){
try{
Sample sample = getMountedSample();
if ((sample!=null) && (sample.getPuck() == puck)){
resetMountedSample();
sample.setLoaded(false);
SwingUtilities.invokeLater(()->{;
getMainFrame().refresh();
});
}
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.WARNING, null, ex);
}
}
////////////////////// Laser Pointer /////////////////////////////////
String laserPos;
public void setLaserPos(String pos){
try {
laserPos = pos;
getMainFrame().evalAsync("set_laser_pos(" + ((pos==null) ? "" : ("'" + pos + "'") ) + ")" ,true);
getMainFrame().basePlatePanel.pointPuck((pos==null) ? null : basePlate.getPuckByName(pos));
if ((dialogPuckLoading != null) && (dialogPuckLoading.isVisible()) && dialogPuckLoading.getBasePlatePanel()!=null) {
dialogPuckLoading.getBasePlatePanel().pointPuck((pos==null) ? null : basePlate.getPuckByName(pos));
}
} catch (StateException ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void setLaserPos(Puck puck){
if (puck==null){
setLaserPos((String)null);
} else {
setLaserPos(puck.getName());
}
}
public String getLaserPos(){
return laserPos;
}
////////////////////// Utilities /////////////////////////////////
void playSound(String name) {
try {
Audio.playFile(new File(Setup.expandPath("{home}/sounds/" + name + ".wav")), false);
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.FINE, null, ex);
}
}
public Boolean isLedRoomTemp() {
try {
if (getMainFrame().getState().isInitialized()){
return getMainFrame().eval("is_led_room_temp()", true).equals(true);
}
} catch (Exception ex) {
}
return null;
}
public Boolean isRoomTemp() {
try {
if (getMainFrame().getState().isInitialized()){
return getMainFrame().eval("is_room_temp()", true).equals(true);
}
} catch (Exception ex) {
}
return null;
}
public Boolean isServiceMode() {
try {
if (getMainFrame().getState().isInitialized()){
return getMainFrame().eval("is_service_mode()", true).equals(true);
}
} catch (Exception ex) {
}
return null;
}
//Non-blocking
public boolean isDoorOpen() {
try {
return doorsOpened;
//if (getMainFrame().getState().isInitialized()){
// return getMainFrame().eval("is_door_closed()", true).equals(false);
// }
} catch (Exception ex) {
}
return false;
}
//Non-blocking
public boolean isRobotParked() {
try {
if (getMainFrame().getState().isInitialized()){
return getMainFrame().eval("'pPark' in robot.get_current_points_cached()", true).equals(true);
}
} catch (Exception ex) {
}
return false;
}
public void setServiceMode(boolean value){
try{
String state = value ? "True" : "False";
getMainFrame().eval("set_service_mode(" + state + ")", true);
} catch (Exception ex) {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, null, ex);
}
checkPuckLoading();
}
public Boolean isBarcodeReaderScanPucks() {
try {
return getMainFrame().eval("is_barcode_reader_scan_pucks()", true).equals(true);
} catch (Exception ex) {
return false;
}
}
public Device getPuckBarcodeReader() {
try {
return (Device) getMainFrame().eval("get_puck_barcode_reader()", true);
} catch (Exception ex) {
return null;
}
}
public String getWorkingMode() {
try {
return String.valueOf(getMainFrame().eval("robot.working_mode", true));
} catch (Exception ex) {
return "Unknown";
}
}
public boolean isManualMode() {
try {
return getMainFrame().eval("is_manual_mode()", true).equals(true);
} catch (Exception ex) {
return false;
}
}
public Boolean isRoomTempEnabled() {
try {
return getMainFrame().eval("is_room_temperature_enabled()", true).equals(true);
} catch (Exception ex) {
return null;
}
}
public Boolean isBeamlineStatusEnabled() {
try {
return getMainFrame().eval("is_beamline_status_enabled()", true).equals(true);
} catch (Exception ex) {
return null;
}
}
public Boolean isImagingEnabled() {
try {
return getMainFrame().eval("is_imaging_enabled()", true).equals(true);
} catch (Exception ex) {
return null;
}
}
////////////////////// Logs /////////////////////////////////
void logSampleMounted(String former, String current){
logEvent((current!=null) ? "Sample Mounted" : "Sample Unmounted", former, current);
}
void logPuckDetectionChange(Puck puck, boolean mounted){
logEvent(mounted ? "Puck Mounted" : "Puck Unmounted", puck.getName(), isPuckLoading() ? "Puck Loading" : "");
}
void logStateChange(State state){
String command = "";
if (state==State.Busy){
CommandInfo info = Context.getCommandBus().getCurrentCommand(true);
if (info != null){
command = (info.script != null) ? info.script : info.command;
}
}
logEvent("State Change", state.toString(), command);
}
public void logEvent(String... event){
try{
long now = System.currentTimeMillis();
Path path = Paths.get(Setup.getOutputPath(), "events", Chrono.getTimeStr(now,"YYYYMMdd") + ".txt");
path.toFile().getParentFile().mkdirs();
String[] data = new String[]{
Chrono.getTimeStr(now, "dd/MM/YY HH:mm:ss.SSS"),
};
data = Arr.append(data, event);
Files.write(path, String.join("\t", data).getBytes(), path.toFile().exists() ? StandardOpenOption.APPEND : StandardOpenOption.CREATE);
Files.write(path, System.lineSeparator().getBytes(), StandardOpenOption.APPEND);
} catch(Exception ex){
Logger.getLogger(Controller.class.getName()).log(Level.WARNING, null, ex);
}
}
}