Files
MXSC/src/main/java/ch/psi/mxsc/Controller.java
2018-09-19 17:56:09 +02:00

731 lines
24 KiB
Java

/*
* Copyright (c) 2014 Paul Scherrer Institute. All rights reserved.
*/
package ch.psi.mxsc;
import ch.psi.mxsc.Puck.PuckType;
import ch.psi.pshell.core.Context;
import ch.psi.pshell.core.DevicePool;
import ch.psi.pshell.core.DevicePoolListener;
import ch.psi.pshell.core.JsonSerializer;
import ch.psi.pshell.device.Device;
import ch.psi.pshell.device.DeviceAdapter;
import ch.psi.pshell.device.DeviceListener;
import ch.psi.pshell.device.GenericDevice;
import ch.psi.pshell.device.ReadbackDevice;
import ch.psi.pshell.ui.Panel;
import ch.psi.utils.State;
import ch.psi.utils.swing.SwingUtils;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Window;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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 {
static Controller instance;
final BasePlate basePlate;
final /*Panel*/ MainPanel mainFrame;
Device hexiposi;
Device barcode_reader;
Device puck_detection;
JDialog dialogAskPuckDatamatrix;
public static Controller getInstance() {
return instance;
}
static void createInstance(Panel mainFrame) {
instance = new Controller(mainFrame);
}
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;
}
}
final PuckSensorAccess puckSensorAccess = PuckSensorAccess.RaspberryPi;
static String PUCK_ESERA_DEVICE = "onewire";
public static final int NUMBER_OF_PUCKS = 30;
private Controller(Panel mainFrame) {
basePlate = new BasePlate();
puckState = new PuckState[NUMBER_OF_PUCKS];
for (int i = 0; i < NUMBER_OF_PUCKS; i++) {
puckState[i] = new PuckState(i + 1);
}
this.mainFrame = (MainPanel) mainFrame;
instance = this;
clearPuckStates();
basePlate.addListener(basePlateListener);
}
final DeviceListener basePlateListener = new DeviceAdapter() {
@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) {
getMainFrame().getContext().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();
}
}
void onTimer() {
try {
setPuckLoading(isPuckLoading());
} catch (Exception ex) {
setPuckLoading(false);
}
refreshSamplesTable();
}
final DeviceListener hexiposiListener = new DeviceAdapter() {
@Override
public void onValueChanged(Device device, Object value, Object former) {
updateView();
}
};
final DeviceListener barcodeReaderListener = new DeviceAdapter() {
@Override
public void onValueChanged(Device device, Object value, Object former) {
if (value != null) { //Keep last value
String valStr = value.toString().trim();
if (puckLoading && isBarcodeReaderScanPucks()) {
onPuckBarcode(valStr);
} else {
getMainFrame().setSampleDatamatrix(valStr);
}
}
}
};
final DeviceListener puckDetectionListener = new DeviceAdapter() {
@Override
public void onValueChanged(Device device, Object value, Object former) {
if (value != null) { //Keep last value
onPuckDetectionChanged();
}
}
};
void updateDevices() {
if (hexiposi != null) {
hexiposi.removeListener(hexiposiListener);
}
if (barcode_reader != null) {
hexiposi.removeListener(barcodeReaderListener);
}
if (puck_detection != null) {
hexiposi.removeListener(puckDetectionListener);
}
hexiposi = (Device) getMainFrame().getDevice("hexiposi");
if (hexiposi != null) {
hexiposi.addListener(hexiposiListener);
} else {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, "No hexiposi detected.");
}
mainFrame.hexiposiPanel.setDevice(hexiposi);
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.");
}
puck_detection = (Device) getDevice("puck_detection");
if (puck_detection != null) {
puck_detection.addListener(puckDetectionListener);
} else {
Logger.getLogger(Controller.class.getName()).log(Level.SEVERE, "No puck_detection detected.");
}
}
final 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 String getHexiposiPosition() {
try {
return (String) ((ReadbackDevice) hexiposi).getReadback().take();
} catch (Exception ex) {
return null;
}
}
public boolean isSelectedPuck(Puck puck) {
return ("" + puck.getSegment()).equalsIgnoreCase(getHexiposiPosition());
}
public List<Puck> getSelectedPucks() {
List<Puck> ret = new ArrayList<>();
for (int i = 0; i < NUMBER_OF_PUCKS; i++) {
if (isSelectedPuck(basePlate.getPucks()[i])) {
ret.add(basePlate.getPucks()[i]);
}
}
return ret;
}
public Boolean isLedRoomTemp() {
try {
return getMainFrame().eval("is_led_room_temp()", true).equals(true);
} catch (Exception ex) {
return null;
}
}
public Boolean isRoomTemp() {
try {
return getMainFrame().eval("is_room_temp()", true).equals(true);
} catch (Exception ex) {
return null;
}
}
public Boolean isBarcodeReaderScanPucks() {
try {
return getMainFrame().eval("is_barcode_reader_scan_pucks()", true).equals(true);
} catch (Exception ex) {
return false;
}
}
public Boolean isPuckLoading() {
try {
return getMainFrame().eval("is_puck_loading()", true).equals(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 void imageDetectPucks() throws Context.ContextStateException {
imageDetectPucks(null, null, null);
}
public void imageDetectPucks(JComponent plot, JComponent renderer, JComponent text) throws Context.ContextStateException {
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 Context.ContextStateException, 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();
}
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);
}
}
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.getPuck());
}
void onSampleReleased(Sample sample) {
onPuckReleased(sample.getPuck());
}
void onPlatePressed(BasePlate plate) {
if (plate.getSelectedPuck() == null) {
getMainFrame().setDefaultDetail();
puckPanel = null;
}
}
void onPlateReleased(BasePlate palte) {
}
GenericDevice getDevice(String name) {
return getMainFrame().getDevice(name);
}
DevicePool getDevicePool() {
return getMainFrame().getContext().getDevicePool();
}
Context getContext() {
return getMainFrame().getContext();
}
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);
}
////////////////////// Sample Info /////////////////////////////////
String samplesTableJson;
//final Object samplesTableLock = new Object();
void refreshSamplesTable() {
//synchronized(samplesTableLock){
SwingUtilities.invokeLater(() -> {
try {
String json = (String) Context.getInstance().evalLineBackground("get_samples_info()");
if (!json.equals(samplesTableJson)) {
samplesTableJson = json;
//SamplesInfo sampleInfo = (SamplesInfo) JsonSerializer.decode(json, SampleInfo.class);
SampleInfo[] samples = (SampleInfo[]) JsonSerializer.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[][]{});
}
////////////////////// Puck Loading /////////////////////////////////
Boolean puckLoading;
Puck.Detection[] currentDetection;
void setPuckLoading(Boolean value) {
if (value == null) {
value = false;
}
if (value != puckLoading) {
if ((dialogAskPuckDatamatrix!=null) && (dialogAskPuckDatamatrix.isShowing())){
dialogAskPuckDatamatrix.setVisible(false);
}
puckLoading = value;
getMainFrame().setPuckDatamatrix(null);
if (isBarcodeReaderScanPucks()) {
if (puckLoading) {
execute("barcode_reader.enable()", true);
execute("barcode_reader.polling = 100", true);
currentDetection = basePlate.getDetection();
} else if (getState().isInitialized()) {
execute("barcode_reader.polling = 0", true);
execute("barcode_reader.disable()", true);
getMainFrame().setPuckDatamatrix(null);
}
}
}
}
void onPuckBarcode(String datamatrix) {
if (puckLoading) {
getMainFrame().setPuckDatamatrix(datamatrix);
System.out.println(datamatrix);
}
}
void onPuckDetectionChanged() {
if (puckLoading) {
String datamatrix = getMainFrame().getPuckDatamatrix();
Puck.Detection[] detection = basePlate.getDetection();
for (int i = 0; i < Controller.NUMBER_OF_PUCKS; i++) {
Puck puck = basePlate.getPucks()[i];
//Only manage pucks for current hexiposi position
if (isSelectedPuck(puck)) {
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) {
if (!datamatrix.isEmpty()) {
getMainFrame().setPuckDatamatrix(null);
linkPuckDatamatrix(puck, datamatrix);
} else {
askPuckDatamatrix(puck);
}
} else if (detectedPuckRemoved) {
linkPuckDatamatrix(puck, null);
}
}
}
currentDetection = detection;
}
}
public void linkPuckDatamatrix(Puck puck, String datamatrix) {
// 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();
boolean showMessage = (puck != null) && !datamatrix.isEmpty();
System.out.println("Setting datamatrix '" + datamatrix + "' to puck: " + puckName);
try {
Context.getInstance().evalLineBackground("set_puck_datamatrix('" + puckName + "','" + datamatrix + "')");
if (puck != null) {
puck.setId(datamatrix);
} else {
basePlate.clearId(datamatrix);
}
if (showMessage) {
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();
// }
}
Map getPuckDatamatrix() {
try {
return (Map) Context.getInstance().evalLineBackground("get_puck_datamatrix()");
} catch (Exception ex) {
return null;
}
}
public 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);
}
});
}
}
}