PAMGuard/src/generalDatabase/sqlite/SqliteSystem.java
Jamie Mac 426c71848a Squashed commit of the following:
commit 514b398a10
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Mar 17 17:27:14 2025 +0000

    Help updates

    Small updates to Tethys and arraysensor helps

commit bbc47c595e
Merge: 6a8c9aff 3dbbdfbe
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Mar 14 14:21:58 2025 +0000

    Merge branch 'main' of https://github.com/PAMGuard/PAMGuard

commit 6a8c9affbc
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Mar 14 14:21:51 2025 +0000

    Add export option to Calibrations table

commit 3dbbdfbe8f
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Mar 14 13:52:37 2025 +0000

    Add notify command

    Add notify command to try to debug notification handling. Send udp command notify with a single integer parameter, e.g. notify 16

commit dfadf25b5e
Merge: 6b63f14d 84d75dfa
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Mar 14 13:17:48 2025 +0000

    Merge branch 'main' of https://github.com/PAMGuard/PAMGuard

commit 6b63f14d5d
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Mar 14 13:17:45 2025 +0000

    Update batch tasks for bearings

    update batch offline tasks so that they support the bearing localiser

commit 84d75dfa57
Author: Jamie Mac <macster110@gmail.com>
Date:   Wed Mar 12 11:42:06 2025 -0700

    Update readme.md

commit ccc59c7b79
Merge: c33ad180 78da90e2
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Mar 12 12:59:40 2025 +0000

    Merge branch 'main' of https://github.com/PAMGuard/PAMGuard

commit c33ad18067
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Mar 12 12:55:54 2025 +0000

    Update FileMapMakingdialog.java

    update map making dialog to always fully dispose dialog

commit 78da90e23e
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Mar 10 17:38:42 2025 +0000

    Update README.html

commit 38ec369a46
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Mar 10 17:09:52 2025 +0000

    Update whistleClassifier_Overview.html

    updated wsl classifier help with link to new model download page

commit 545e2b010a
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Mar 10 13:47:21 2025 +0000

    Clip Display for DL

    Added a clip display for the deep learning classifier. Could do with more decoration.

commit 0941b3ab7f
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Sun Mar 9 18:55:38 2025 +0000

    Rocca graphics

    Added Rocca overlay graphics to RoccaLoggingDataBlock

commit f88be0ca3c
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Sat Mar 8 17:55:51 2025 +0000

    Species map choice

    species map choices for Detection Grouper based on content of Logger Form annotation.

commit 624024709a
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Mar 7 18:27:58 2025 +0000

    Species manager on logger forms

    Nearly working. Needs a bit of tweaking

commit e69520a0b5
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Mar 7 10:44:11 2025 +0000

    Tethys funcs in ROCCA

commit 6bd7131255
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Thu Mar 6 12:37:01 2025 +0000

    Revamp of Whistle Classifier

    Add Tethys output
    Mod data units so they show the start time and duration of classified whistle groups
    Add frequency output
    Refactor code to better separate graphics from functionality

commit 11bb7d5fe1
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Mar 5 17:58:45 2025 +0000

    Fix array manager

    Fix so that it's possible to load a VERY old psf file that didn't have a lot of the new array managmeent features.

commit 2bf023ffed
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Mar 5 17:40:05 2025 +0000

    Tethys updates

    Started adding Tethys functions for click train detector and GPL detector

commit d3907ac8f5
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Mar 5 16:19:21 2025 +0000

    Tethys improvements

    Better species sel panel in export dialog
    updated / corrected help
    Sorted 'final' button in export wizard.

commit cb9b41719f
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Tue Mar 4 17:35:12 2025 +0000

    Tethys updates

    adding localisation info to TM annotation
    Change granularity for group detections to gouped
    Added call type selection for all Detections exports.
    Added super detection into

commit c92be153fa
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Tue Mar 4 11:35:47 2025 +0000

    update filter design dialog and help pages

    Include impulse response plot for IIR Filters

commit cadf61d218
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Mon Mar 3 00:57:54 2025 +0000

    Impulse response plot for IIF filters

    Optional IIF filter impulse response plot.

commit 7b6466b06f
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Feb 28 23:40:29 2025 +0000

    TEthys output of groups detector

commit 0365ebef36
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Thu Feb 20 17:03:33 2025 +0000

    15f. DAQ SUD Bug fix

commit d6dc3839ac
Author: Jamie Mac <macster110@gmail.com>
Date:   Thu Feb 20 16:41:59 2025 +0000

    Fixes to Sound Acquisition module (#197)

    * Sound Acquisition fix

    Fix issue with subfolder check box not being using when loading data.
    Made sub folder default
    Added listener to sub folder button to redo file list.

    * Update .gitignore

    * Squashed commit of the following:

    commit 68ade16b39
    Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
    Date:   Wed Feb 19 16:26:44 2025 +0000

        Update MulticastController.java

        Small update to ignore a test message to Multicast controller which will probably never be sent anyway.

    commit f7a6138774
    Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
    Date:   Tue Feb 18 16:31:08 2025 +0000

        buffer io of serialised datamaps

    commit 50154d99e7
    Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
    Date:   Sun Feb 16 13:38:19 2025 +0000

        V 2.02.15e

        Approaching a final release for PAMGuard Tethys workshop

    commit fce60115f0
    Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
    Date:   Fri Feb 14 15:46:11 2025 +0000

        update to 2.02.15d

        Building 2.02.15d in prep for next major release

    commit 300570712b
    Merge: f476a56b e3b90946
    Author: Jamie Mac <macster110@gmail.com>
    Date:   Fri Feb 14 15:17:14 2025 +0000

        Merge pull request #196 from macster110/main

        Minor fixes to deep leanring module

    commit f476a56bda
    Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
    Date:   Fri Feb 14 14:38:14 2025 +0000

        Small fixes

        Added a 20 minute range to scrollers (common in some duty cycled data)
        Fixed issue 194 of Source dialog crashing in Noise Monitor due to null pointer before first selection of a datablock.
        Fixed issue 195: resetting of scrollers when datamap changes

commit 68ade16b39
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Wed Feb 19 16:26:44 2025 +0000

    Update MulticastController.java

    Small update to ignore a test message to Multicast controller which will probably never be sent anyway.

commit f7a6138774
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Tue Feb 18 16:31:08 2025 +0000

    buffer io of serialised datamaps

commit 50154d99e7
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Sun Feb 16 13:38:19 2025 +0000

    V 2.02.15e

    Approaching a final release for PAMGuard Tethys workshop

commit fce60115f0
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Feb 14 15:46:11 2025 +0000

    update to 2.02.15d

    Building 2.02.15d in prep for next major release

commit 300570712b
Merge: f476a56b e3b90946
Author: Jamie Mac <macster110@gmail.com>
Date:   Fri Feb 14 15:17:14 2025 +0000

    Merge pull request #196 from macster110/main

    Minor fixes to deep leanring module

commit f476a56bda
Author: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com>
Date:   Fri Feb 14 14:38:14 2025 +0000

    Small fixes

    Added a 20 minute range to scrollers (common in some duty cycled data)
    Fixed issue 194 of Source dialog crashing in Noise Monitor due to null pointer before first selection of a datablock.
    Fixed issue 195: resetting of scrollers when datamap changes
2025-03-17 20:22:42 +00:00

544 lines
15 KiB
Java

package generalDatabase.sqlite;
import generalDatabase.pamCursor.NonScrollablePamCursor;
import generalDatabase.pamCursor.PamCursor;
import javafx.stage.FileChooser;
import pamguard.GlobalArguments;
import java.awt.Component;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import org.sqlite.SQLiteConfig;
import PamController.PamControlledUnitSettings;
import PamController.PamController;
import PamController.PamFolders;
import PamController.PamSettingManager;
import PamController.PamSettings;
import PamUtils.PamFileChooser;
import PamUtils.PamFileFilter;
import PamView.dialog.warn.WarnOnce;
import generalDatabase.DBControl;
import generalDatabase.DBSystem;
import generalDatabase.EmptyTableDefinition;
import generalDatabase.PamConnection;
import generalDatabase.SQLTypes;
import generalDatabase.SystemDialogPanel;
import generalDatabase.layoutFX.SystemDialogPaneFX;
/**
* PAMGuard database system to make use of Sqlite databases: https://www.sqlite.org/
* Specifically, this database system makes use of the xerial sqlite-jdbc library,
* https://github.com/xerial/sqlite-jdbc.
*
* NB: The default sqlite-jdbc from xerial does not support JDBC escape syntax, which
* is used by PAMGuard to interpret timestamps in mixed and viewer modes. Until this
* is addressed, sqlite database will fail in mixed and viewer mode.
*
* @author brian_mil
*/
public class SqliteSystem extends DBSystem implements PamSettings {
/* TODO: Fix issues with sqlite-jdbc timestamps and
* JDBC escape syntax so that sqlite database can be
* used in mixed and viewer mode
*/
private DBControl dbControl;
private SqliteDialogPanel dialogPanel;
private SQLTypes sqlTypes = new SqliteSQLTypes();
public static final String SYSTEMNAME = "Sqlite Database";
private static final String driverClass = "org.sqlite.JDBC";
// private static final boolean USEAUTOCOMMIT = true;
private SQLiteParameters sqliteParameters = new SQLiteParameters();
/**
* The JavaFX settings pane.
*/
private SqlitePaneFX sqlPane;
/**
* @return the recentDatabases
*/
public ArrayList<File> getRecentDatabases() {
return sqliteParameters.getRecentDatabases();
}
public SqliteSystem(DBControl dbControl, int settingsStore) {
super();
this.dbControl = dbControl;
PamSettingManager.getInstance().registerSettings(this, settingsStore);
if (sqliteParameters.getRecentDatabases() == null) {
sqliteParameters.setRecentDatabases(new ArrayList<File>());
}
checkCommandLineOption();
}
/**
* Check to see if the database name was included as a command line option,
* in which case, put it at the top of the list. this is better than just using
* it,since it then gets stored within it's own settings.
*/
private void checkCommandLineOption() {
// TODO Auto-generated method stub
/*
* If a database name was passed as a global argument, then use the passed name instead of
* the name in the list of recent databases.
*/
String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg);
if (commandName == null) {
return;
}
File commandFile = new File(commandName);
// check the file end is of the right type. Some batch systems may not get this right.
commandFile = PamFileFilter.checkFileEnd(commandFile, "sqlite3", true);
setDatabaseName(commandFile.getAbsolutePath());
}
/**
* Set the database name, check it exists, check it's end
* and add to top of list of databases.
* @param databaseName
*/
public void setDatabaseName(String databaseName) {
// see if this file exists in the list and if it does, remove it
for (int i = 0; i < getRecentDatabases().size(); i++) {
if (getRecentDatabases().get(i).toString().equalsIgnoreCase(databaseName)) {
getRecentDatabases().remove(i);
}
}
// then insert the file at the top of the list.
File newFile = new File(databaseName);
// if the file doesn't exit, consider creating it.
if (newFile.exists() == false) {
boolean ask = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg) == null;
newFile = createNewDatabase(databaseName, null, ask);
if (newFile == null) {
System.out.println("Unable to create "+newFile);
return;
}
}
getRecentDatabases().add(0, newFile);
}
@Override
public String browseDatabases(Component parent) {
PamFileFilter fileFilter = new PamFileFilter("Sqlite Database", ".sqlite");
fileFilter.addFileType(".sqlite3");
fileFilter.addFileType(".db");
fileFilter.addFileType(".db3");
JFileChooser fileChooser = new PamFileChooser();
fileChooser.setFileFilter(fileFilter);
fileChooser.setAcceptAllFileFilterUsed(false);
File startPlan = PamFolders.getFileChooserPath(getDatabaseName());
fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (startPlan != null) {
if (startPlan.isDirectory()) {
fileChooser.setCurrentDirectory(startPlan);
}
else {
fileChooser.setSelectedFile(startPlan);
}
}
int state = fileChooser.showOpenDialog(parent);
if (state == JFileChooser.APPROVE_OPTION) {
File currFile = fileChooser.getSelectedFile();
//System.out.println(currFile);
return currFile.getAbsolutePath();
}
return null;
}
/**
* Create a new empty database file.
* @param newDB full path for database file (can be missing .sqlit3 if you like - this will get checked and added).
* @param parent window (for confirm dialog, can be null)
* @param askFirst show a confirm dialog before creating the database file.
* @return a path to the file, whether created or no.
*/
public File createNewDatabase(String newDB, Component parent, boolean askFirst) {
File newFile = new File(newDB);
newFile = PamFileFilter.checkFileEnd(newFile, ".sqlite3", true);
if (askFirst) {
int ans = JOptionPane.showConfirmDialog(parent, "Create blank database " + newFile.getAbsolutePath() + " ?", "Sqlite", JOptionPane.OK_CANCEL_OPTION);
if (ans == JOptionPane.CANCEL_OPTION) {
return null;
}
}
Connection connection = null;
try {
// create a database connection;
// Sqlite will automatically create file if it does not exist;
connection = DriverManager.getConnection("jdbc:sqlite:" + newFile);
}
catch(SQLException e)
{
System.err.println(e.getMessage());
}
finally
{
try
{
if(connection != null)
connection.close();
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
}
return newFile;
}
@Override
public boolean canCreate() {
return false;
}
@Override
public boolean create() {
return false;
}
@Override
public PamCursor createPamCursor(EmptyTableDefinition tableDefinition) {
return new NonScrollablePamCursor(tableDefinition);
}
@Override
public boolean exists() {
File file = new File(getDatabaseName());
if (file == null) return false;
return file.exists();
}
@Override
public PamConnection getConnection(String dbName) {
if (dbName == null) return null;
Connection con = null;
try {
/*
* Don't use the driver manager, but open from the built in command in
* SQLiteConfig. This will then correctly set the dateformat of the database.
*/
SQLiteConfig config = new SQLiteConfig();
config.setSharedCache(true);
config.enableRecursiveTriggers(true);
config.enableLoadExtension(true);
config.setDateClass(SqliteSQLTypes.dateClass.getValue());
config.setDateStringFormat(SQLiteConfig.DEFAULT_DATE_STRING_FORMAT);
con = config.createConnection("jdbc:sqlite:" + dbName);
con.setAutoCommit(dbControl.getDbParameters().getUseAutoCommit());
}
catch (SQLException e) {
System.err.println(e.getMessage());
}
if (con == null) {
System.out.println("Unable to open database " + dbName);
}
if (con == null) {
return null;
}
// System.out.println("------------------------------- OpenedSQLite database " + dbName + " handle " + con);
PamConnection connection = new PamConnection(this, con, sqlTypes);
connection.setDatabaseName(dbName);
return connection;
}
@Override
public void closeConnection(PamConnection connection) {
try
{
if(connection != null){
// if (USEAUTOCOMMIT == false) {
if (connection.getConnection().getAutoCommit()) {
connection.getConnection().commit();
}
// }
connection.getConnection().close();
}
}
catch(SQLException e)
{
// connection close failed.
System.err.println(e);
}
// if (connection != null) {
// System.out.println("------------------------------- Closed SQLite database handle " + connection.getConnection());
// }
}
@Override
public boolean hasDriver() {
try {
// return false if it's a 64 bit JVM
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
return false;
}
return true;
}
@Override
public boolean checkDatabaseExists(String dbName) {
String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg);
if (commandName != null) {
return checkCommandLineDatabase();
}
return super.checkDatabaseExists(dbName);
}
private boolean checkCommandLineDatabase() {
String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg);
if (commandName == null) {
return false;
}
File dbFile = new File(commandName);
dbFile = PamFileFilter.checkFileEnd(dbFile, ".sqlite3", true);
commandName = dbFile.getAbsolutePath();
if (dbFile.exists() == false) {
// create a new database without asking.
createNewDatabase(commandName);
}
return dbFile.exists();
}
@Override
public String getDatabaseName() {
/*
* If a database name was passed as a global argument, then use the passed name instead of
* the name in the list of recent databases.
*/
String commandName = GlobalArguments.getParam(DBControl.GlobalDatabaseNameArg);
if (commandName != null) {
File dbFile = new File(commandName);
dbFile = PamFileFilter.checkFileEnd(dbFile, ".sqlite3", true);
commandName = dbFile.getAbsolutePath();
return commandName;
}
if (getRecentDatabases() == null) return null;
if (getRecentDatabases().size() < 1) return null;
return getRecentDatabases().get(0).getAbsolutePath();
}
@Override
public String getShortDatabaseName() {
if (getRecentDatabases() == null) return null;
if (getRecentDatabases().size() < 1) return null;
return getRecentDatabases().get(0).getName();
}
@Override
public SystemDialogPanel getDialogPanel(Component parent) {
if (dialogPanel == null) {
dialogPanel = new SqliteDialogPanel(parent, this);
}
return dialogPanel;
}
@Override
public SQLTypes getSqlTypes() {
return sqlTypes;
}
//
//
// PAM SETTINGS METHODS
//
//
@Override
public String getSystemName() {
return SYSTEMNAME;
}
public String getUnitName() {
return dbControl.getUnitName();
}
public String getUnitType() {
return "Sqlite Database System";
}
//
//
// PAM SETTINGS METHODS
//
//
public Serializable getSettingsReference() {
return sqliteParameters;
}
public long getSettingsVersion() {
return 0;
}
public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) {
Object settings = pamControlledUnitSettings.getSettings();
if (settings instanceof ArrayList) { // deal with old format which just stored a list.
sqliteParameters.getRecentDatabases().clear();
sqliteParameters.getRecentDatabases().addAll((ArrayList) settings);
}
else if (settings instanceof SQLiteParameters){
this.sqliteParameters = ((SQLiteParameters) settings).clone();
}
else {
return false;
}
return true;
}
/* (non-Javadoc)
* @see generalDatabase.DBSystem#getKeywords()
*/
@Override
public String getKeywords() {
// no restrictions at all in MS Access since names
// are wrapped in ""
return null;
}
/* (non-Javadoc)
* @see generalDatabase.DBSystem#canOpenDatabase()
*/
@Override
public boolean canOpenDatabase() {
return true;
}
/* (non-Javadoc)
* @see generalDatabase.DBSystem#openCurrentDatabase()
*/
@Override
public boolean openCurrentDatabase() {
String databaseName = getDatabaseName();
if (databaseName == null) return false;
databaseName = databaseName.trim();
File dbFile = new File(databaseName);
if (dbFile.exists() == false) {
String msg = "Database " + databaseName + " does not exist";
WarnOnce.showWarning("Missing database", msg, WarnOnce.WARNING_MESSAGE, null);
return false;
}
try {
if (Desktop.isDesktopSupported()) {
Desktop.getDesktop().open(dbFile);
}
} catch (IOException ioe) {
// ioe.printStackTrace();
// System.out.println("Unable to open ");
}
return true;
}
/**
* File chooser for JavaFX
*/
private FileChooser fileChooser=new FileChooser();
/**
* FX methods to browse for an sqlite file.
*/
@Override
public String browseDatabasesFX(int type) {
//JavaFX version
configureFileChooser(fileChooser);
//set initial directory as last open database file.
if (getRecentDatabases()!=null && getRecentDatabases().size()>0 && getRecentDatabases().get(0).exists()
&& getRecentDatabases().get(0).getParentFile().isDirectory()){
fileChooser.setInitialDirectory(
getRecentDatabases().get(0).getParentFile()
);
}
//open file chooser
File file=null;
if (type==0) file = fileChooser.showOpenDialog(PamController.getMainStage());
//if (type==1) file = fileChooser.showOpenMultipleDialog(PamController.getMainStage());
if (type==2) file = fileChooser.showSaveDialog(PamController.getMainStage());
if (file != null) {
return file.getAbsolutePath();
}
return null;
}
/**
* Set parameters for the file chooser.
* @param fileChooser - filechooser to configure.
*/
protected static void configureFileChooser(
final FileChooser fileChooser) {
fileChooser.setTitle("Open SQLite Database File");
fileChooser.setInitialDirectory(
new File(PamFolders.getDefaultProjectFolder())
);
fileChooser.getExtensionFilters().addAll(
new FileChooser.ExtensionFilter("SQLite Database File", "*.sqlite", "*.sqlite3", "*.db", "*.db3")
);
}
@Override
public SystemDialogPaneFX getDialogPaneFX() {
if (sqlPane==null) sqlPane=new SqlitePaneFX(this);
return sqlPane;
}
@Override
protected boolean createNewDatabase(String forcedName) {
// TODO Auto-generated method stub
PamConnection con = getConnection(forcedName);
if (con == null) return false;
try {
con.getConnection().close();
} catch (SQLException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
return false;
}
return true;
}
// @Override
// protected PamConnection reOpenConnection(PamConnection connection) {
// closeConnection(connection);
// return getConnection();
// }
}