1290 lines
45 KiB
Java
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);
|
|
}
|
|
}
|
|
}
|