diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index 00be884a..5c467719 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -49,6 +49,7 @@ import networkTransfer.receive.BuoyStatusDataUnit; import networkTransfer.receive.NetworkReceiver; import printscreen.PrintScreenControl; import rockBlock.RockBlockControl; +import tethys.TethysControl; import turbineops.TurbineOperationControl; import GPS.GpsDataUnit; import Map.MapController; @@ -459,6 +460,13 @@ final public class PamModel implements PamModelInterface, PamSettings { mi.setToolTipText("Manage automated data backups"); mi.setModulesMenuGroup(utilitiesGroup); mi.setMaxNumber(1); + + if (isViewer) { + mi = PamModuleInfo.registerControlledUnit(TethysControl.class.getName(), TethysControl.defaultName); + mi.setToolTipText("Interface to Tethys Database"); + mi.setModulesMenuGroup(utilitiesGroup); + mi.setMaxNumber(1); + } diff --git a/src/generalDatabase/DBSchemaWriter.java b/src/generalDatabase/DBSchemaWriter.java index 213601fe..a29a90b5 100644 --- a/src/generalDatabase/DBSchemaWriter.java +++ b/src/generalDatabase/DBSchemaWriter.java @@ -63,13 +63,14 @@ public class DBSchemaWriter { return true; } - private void exportDatabaseSchema(File outputFolder, PamDataBlock dataBlock, SQLLogging logging, EmptyTableDefinition tableDef) { - - /** - * write a parent item, e.g. if tableDef is a sub class of PamTableDefinition - */ - // String parentName = writeParentTableSchema(outputFolder, dataBlock, tableDef); - + /** + * Generate an xml schema for a datablock. + * @param dataBlock + * @param logging + * @param tableDef + * @return + */ + public Document generateDatabaseSchema(PamDataBlock dataBlock, SQLLogging logging, EmptyTableDefinition tableDef) { String tableName = tableDef.getTableName(); Document doc = PamUtils.XMLUtils.createBlankDoc(); Element schemaEl = doc.createElement("xs:schema"); @@ -95,7 +96,20 @@ public class DBSchemaWriter { } } } + return doc; + } + + private void exportDatabaseSchema(File outputFolder, PamDataBlock dataBlock, SQLLogging logging, EmptyTableDefinition tableDef) { + /** + * write a parent item, e.g. if tableDef is a sub class of PamTableDefinition + */ + // String parentName = writeParentTableSchema(outputFolder, dataBlock, tableDef); + + Document doc = generateDatabaseSchema(dataBlock, logging, tableDef); + + String tableName = tableDef.getTableName(); + try { File outputFile = new File(outputFolder, tableName+".xsd"); XMLUtils.writeToFile(doc, outputFile); diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java new file mode 100644 index 00000000..dc0a46f2 --- /dev/null +++ b/src/tethys/TethysControl.java @@ -0,0 +1,82 @@ +package tethys; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +import PamController.PamControlledUnit; +import PamController.PamController; +import PamguardMVC.PamDataBlock; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; +import tethys.output.swing.TethysExportDialog; + +/** + * Quick play with a simple system for outputting data to Tethys. At it's start + * this is simply going to offer a dialog and have a few functions which show how + * to access data within PAMGuard. + * @author dg50 + * + */ +public class TethysControl extends PamControlledUnit { + + public static final String unitType = "Tethys Interface"; + public static String defaultName = "Tethys"; + + + private TethysExportParams tethysExportParams = new TethysExportParams(); + + + public TethysControl(String unitName) { + super(unitType, unitName); + } + + @Override + public JMenuItem createFileMenu(JFrame parentFrame) { + JMenu tethysMenu = new JMenu("Tethys"); + JMenuItem tethysExport = new JMenuItem("Export ..."); + tethysMenu.add(tethysExport); + tethysExport.addActionListener(new ActionListener() { + + @Override + public void actionPerformed(ActionEvent e) { + tethysExport(parentFrame); + } + }); + return tethysMenu; + } + + /** + * @return the tethysExportParams + */ + public TethysExportParams getTethysExportParams() { + return tethysExportParams; + } + + /** + * We'll probably want to + * @param parentFrame + */ + protected void tethysExport(JFrame parentFrame) { + TethysExportParams newExportParams = TethysExportDialog.showDialog(parentFrame, this); + if (newExportParams != null) { + // dialog returns null if cancel was pressed. + tethysExportParams = newExportParams; + exportTethysData(tethysExportParams); + } + } + + /** + * We'll arrive here if the dialog has been opened and we want to export Tethys data. + * @param tethysExportParams2 + */ + private void exportTethysData(TethysExportParams tethysExportParams) { + TethysExporter tethysExporter = new TethysExporter(this, tethysExportParams); + tethysExporter.doExport(); + } + +} diff --git a/src/tethys/TethysExporter.java b/src/tethys/TethysExporter.java new file mode 100644 index 00000000..6dc816a0 --- /dev/null +++ b/src/tethys/TethysExporter.java @@ -0,0 +1,133 @@ +package tethys; + +import java.util.ArrayList; + +import org.w3c.dom.Document; + +import PamController.PamControlledUnit; +import PamController.PamController; +import PamController.settings.output.xml.PamguardXMLWriter; +import PamguardMVC.PamDataBlock; +import generalDatabase.DBSchemaWriter; +import generalDatabase.SQLLogging; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; + +public class TethysExporter { + + private TethysControl tethysControl; + private TethysExportParams tethysExportParams; + + public TethysExporter(TethysControl tethysControl, TethysExportParams tethysExportParams) { + this.tethysControl = tethysControl; + this.tethysExportParams = tethysExportParams; + } + + /** + * Does the work. In reality this will need an awful lot of changing, for instance + * to provide feedback to an observer class to show progress on the display. + * @return OK if success. + */ + public boolean doExport() { + /* + * Call some general export function + */ + exportGeneralData(tethysExportParams); + /* + * go through the export params and call something for every + * data block that's enabled. + */ + ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); + for (PamDataBlock aDataBlock : allDataBlocks) { + StreamExportParams streamExportParams = tethysExportParams.getStreamParams(aDataBlock); + if (streamExportParams == null || streamExportParams.selected == false) { + continue; // not interested in this one. + } + exportDataStream(aDataBlock, tethysExportParams, streamExportParams); + } + /* + * Then do whatever else is needed to complete the document. + */ + + return true; + } + + /** + * No idea if we need this or not. May want to return something different to void, e.g. + * a reference to the main object for a tethys export. I've no idea ! + * @param tethysExportParams2 + */ + private void exportGeneralData(TethysExportParams tethysExportParams) { + // TODO Auto-generated method stub + + } + + /** + * Here is where we export data for a specific data stream to Tethys. + * + * @param aDataBlock + * @param tethysExportParams + * @param streamExportParams + */ + private void exportDataStream(PamDataBlock aDataBlock, TethysExportParams tethysExportParams, + StreamExportParams streamExportParams) { + /** + * This will probably need to be passed additional parameters and may also want to return something + * other than void in order to build a bigger Tethys document. + */ + /* + * Some examples of how to do whatever is needed to get schema and data out of PAMGuard. + */ + /* + * first we'll probably want a reference to the module containing the data. + * in principle this can't get null, since the datablock was found be searching in + * the other direction. + */ + PamControlledUnit pamControlledUnit = aDataBlock.getParentProcess().getPamControlledUnit(); + /* + * Get the XML settings for that datablock. + */ + PamguardXMLWriter pamXMLWriter = PamguardXMLWriter.getXMLWriter(); + Document doc = pamXMLWriter.writeOneModule(pamXMLWriter, System.currentTimeMillis()); + String moduleXML = null; + if (doc != null) { + // this string should be XML of all the settings for the module controlling this datablock. + moduleXML = pamXMLWriter.getAsString(doc, true); // change to false to get smaller xml + System.out.printf("Module settings for datablock %s are:\n", moduleXML); + System.out.println(moduleXML); + } + /* + * This also should never be null, because we only selected datablocks that had a database + * interface. + * Future versions may need to change this to use binary stores. This will require + * the overriding datablock to return something different to SQLLogging - probably a TethysLogging + * interface, which can probably by default just wrap the SQLLogging , but does allow the + * option of modifying behaviour and of making something work for binary stores. + */ + SQLLogging logging = aDataBlock.getLogging(); + if (logging == null) return; + /** + * From the logging, it's possible to automatically generate a XML schema. This may not + * be entirely right, but will be easy to fix. + */ + DBSchemaWriter schemaWriter = new DBSchemaWriter(); + Document schemaDoc = schemaWriter.generateDatabaseSchema(aDataBlock, logging, logging.getTableDefinition()); + String schemaXML = null; + if (schemaDoc != null) { + schemaXML = pamXMLWriter.getAsString(schemaDoc, true); + } + System.out.printf("Database schema for Module Type %s Name %s are:\n", pamControlledUnit.getUnitType(), pamControlledUnit.getUnitName()); + System.out.println(schemaXML); + + /** + * Now can go through the data. Probably, we'll want to go through all the data in + * the project, but we can hold off on that for now and just go for data that + * are in memory. We'll also have to think a lot about updating parts of the + * database which have been reprocessed - what we want to do, should eventually all + * be options set in the dialog and available within TethysExportParams + * For now though, we're just going to export data that are in memory. + */ + + } + +} diff --git a/src/tethys/output/StreamExportParams.java b/src/tethys/output/StreamExportParams.java new file mode 100644 index 00000000..b19af599 --- /dev/null +++ b/src/tethys/output/StreamExportParams.java @@ -0,0 +1,27 @@ +package tethys.output; + +import java.io.Serializable; + +/** + * Parameters controlling export of a single stream. + * Starts just with a boolean 'selected', but may grow. + * These all contain data names rather than references to a Datablock so that + * they can be serialised. + * @author dg50 + * + */ +public class StreamExportParams implements Serializable { + + public static final long serialVersionUID = 1L; + + public StreamExportParams(String longDataName, boolean selected) { + super(); + this.longDataName = longDataName; + this.selected = selected; + } + + public String longDataName; + + public boolean selected; + +} diff --git a/src/tethys/output/TethysExportParams.java b/src/tethys/output/TethysExportParams.java new file mode 100644 index 00000000..6b43d0df --- /dev/null +++ b/src/tethys/output/TethysExportParams.java @@ -0,0 +1,54 @@ +package tethys.output; + +import java.io.Serializable; +import java.util.HashMap; + +import PamguardMVC.PamDataBlock; + +public class TethysExportParams implements Serializable, Cloneable{ + + public static final long serialVersionUID = 1L; + + /* + * Need to add lots of other parameters here, such as the connection detils + * for the tethys database. + */ + + private HashMap streamParamsMap = new HashMap(); + + @Override + public TethysExportParams clone() { + try { + return (TethysExportParams) super.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + } + + /** + * Set stream export parameters for a given datablock. + * @param dataBlock + * @param exportParams + */ + public void setStreamParams(PamDataBlock dataBlock, StreamExportParams exportParams) { + setStreamParams(dataBlock.getLongDataName(), exportParams); + } + /** + * Set stream export parameters for a given data name. + * @param dataBlock + * @param exportParams + */ + public void setStreamParams(String longDataName, StreamExportParams exportParams) { + streamParamsMap.put(longDataName, exportParams); + } + + public StreamExportParams getStreamParams(PamDataBlock dataBlock) { + return getStreamParams(dataBlock.getLongDataName()); + } + + private StreamExportParams getStreamParams(String longDataName) { + return streamParamsMap.get(longDataName); + } + +} diff --git a/src/tethys/output/swing/TethysExportDialog.java b/src/tethys/output/swing/TethysExportDialog.java new file mode 100644 index 00000000..d9477c52 --- /dev/null +++ b/src/tethys/output/swing/TethysExportDialog.java @@ -0,0 +1,171 @@ +package tethys.output.swing; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Window; +import java.util.ArrayList; + +import javax.swing.JCheckBox; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + +import PamController.PamController; +import PamView.dialog.PamDialog; +import PamView.dialog.PamGridBagContraints; +import PamguardMVC.PamDataBlock; +import tethys.TethysControl; +import tethys.output.StreamExportParams; +import tethys.output.TethysExportParams; + +/** + * Start of a dialog for controlling the export of Tethys data. For first iteration + * this will just be a list of output streams (PamDataBlocks) which have a database + * connection. Each will have a checkbox. On OK it will return back a class listing + * what to output and the calling function can do as it will. Future versions will + * probably want to push the functionality into a SwingWorker to show progress, etc. + * but that can come later. + * + * Normally, I use single instance dialogs for this sort of thing. + * @author dg50 + * + */ +public class TethysExportDialog extends PamDialog { + + private static TethysExportDialog singleInstance; + + private TethysControl tethysControl; + + private TethysExportParams exportParams; + + private JPanel streamsPanel; + + private ArrayList dataStreamSets = new ArrayList<>(); + + private TethysExportDialog(Window parentFrame, TethysControl tethysControl) { + super(parentFrame, "Tethys Export", false); + this.tethysControl = tethysControl; + + JPanel mainPanel = new JPanel(new BorderLayout()); + /* + * Expect to add at least one more panel at the top of this to have options + * for things like connection details to the database. If not another panel, + * then they can be arranged on tabs, as a wizard, etc. + */ + streamsPanel = new JPanel(); + streamsPanel.setBorder(new TitledBorder("Data Streams")); + mainPanel.add(BorderLayout.CENTER, streamsPanel); + + setDialogComponent(mainPanel); + setResizable(true); + + } + + public static TethysExportParams showDialog(Window parentFrame, TethysControl tethysControl) { + if (singleInstance == null || singleInstance.getOwner() != parentFrame || singleInstance.tethysControl != tethysControl) { + singleInstance = new TethysExportDialog(parentFrame, tethysControl); + } + singleInstance.makeStreamsPanel(); + singleInstance.setParams(); + singleInstance.setVisible(true); + return singleInstance.exportParams; + } + + + /** + * remake the panel. Gets rebuilt whenever dialog opens in case + * the list of available data has changed. + */ + private void makeStreamsPanel() { + streamsPanel.removeAll(); + streamsPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new PamGridBagContraints(); + dataStreamSets = findDataStreams(); + for (DataStreamSet aSet : dataStreamSets) { + streamsPanel.add(aSet.checkBox, c); + c.gridy++; + } + pack(); + } + + /** + * Get a set of data blocks that have SQLLogging. + * @return + */ + private ArrayList findDataStreams() { + ArrayList sets = new ArrayList<>(); + ArrayList allDataBlocks = PamController.getInstance().getDataBlocks(); + for (PamDataBlock aDataBlock : allDataBlocks) { + if (aDataBlock.getLogging() != null) { + sets.add(new DataStreamSet(aDataBlock)); + } + } + return sets; + } + + private void setParams() { + this.exportParams = tethysControl.getTethysExportParams(); + if (exportParams == null) { + exportParams = new TethysExportParams(); + } + else { + exportParams = exportParams.clone(); + } + setParams(exportParams); + } + + private void setParams(TethysExportParams exportParams) { + if (exportParams == null || dataStreamSets == null) { + return; + } + for (DataStreamSet streamSet : dataStreamSets) { + StreamExportParams streamOpts = exportParams.getStreamParams(streamSet.dataBlock); + if (streamOpts == null) { + continue; + } + streamSet.checkBox.setSelected(streamOpts.selected); + } + + } + + @Override + public boolean getParams() { + if (exportParams == null || dataStreamSets == null) { + return false; + } + int nSel = 0; + for (DataStreamSet streamSet : dataStreamSets) { + StreamExportParams streamOpts = new StreamExportParams(streamSet.dataBlock.getLongDataName(), streamSet.checkBox.isSelected()); + exportParams.setStreamParams(streamSet.dataBlock, streamOpts); + nSel++; + } + return nSel > 0; + } + + @Override + public void cancelButtonPressed() { + exportParams = null; + } + + @Override + public void restoreDefaultSettings() { + // TODO Auto-generated method stub + + } + + private class DataStreamSet { + + private PamDataBlock dataBlock; + + private JCheckBox checkBox; + + public DataStreamSet(PamDataBlock dataBlock) { + super(); + this.dataBlock = dataBlock; + checkBox = new JCheckBox(dataBlock.getDataName()); + checkBox.setToolTipText(dataBlock.getLongDataName()); + } + + + } +}