Improvements to udp PAMGuard control (#24)

* comment changes in graphics

* Mods to enable getting and setting parameters for individual modules via
xml i/o via udp commands.
This commit is contained in:
Douglas Gillespie 2022-03-15 14:26:38 +00:00 committed by GitHub
parent 178275939c
commit f48579ff22
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 358 additions and 96 deletions

View File

@ -2262,6 +2262,37 @@ public class PamSettingManager {
return null;
}
/**
* Find a list of unit settings by type and name. If both are specified, then it's going to
* (hopefully only return one setting. Otherwise, with null or wildcard names we may get many.
* @param unitType unit type, can be wildcard * or null
* @param unitName unit name, can be wildcard * or null
* @return Array list of settings.
*/
public ArrayList<PamSettings> findPamSettings(String unitType, String unitName) {
if (owners == null) {
return null;
}
ArrayList<PamSettings> foundSettings = new ArrayList<>();
if (unitType != null && unitType.equals("*")) {
unitType = null;
}
if (unitName != null && unitName.equals("*")) {
unitName = null;
}
for (PamSettings owner:owners) {
if (unitType != null && !unitType.equals(owner.getUnitType())) {
continue;
}
if (unitName != null && !unitName.equals(owner.getUnitName())) {
continue;
}
foundSettings.add(owner);
}
return foundSettings;
}
/**
* Show a dialog to allow the user to select a .psf file path.

View File

@ -1,6 +1,10 @@
package PamController.command;
import java.util.ArrayList;
import java.util.List;
import java.util.function.IntFunction;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import PamController.PamControlledUnit;
import PamController.PamController;
@ -31,6 +35,8 @@ public abstract class CommandManager extends PamControlledUnit {
commandsList.add(new ExitCommand());
commandsList.add(new KillCommand());
commandsList.add(new HelpCommand(this));
commandsList.add(new GetXMLSettings());
commandsList.add(new SetXMLSettings());
}
@ -54,59 +60,66 @@ public abstract class CommandManager extends PamControlledUnit {
* the program (in which case this thread will
* exit and close the port). True otherwise.
*/
public boolean interpretCommand(String command) {
public boolean interpretCommand(String commandString) {
//System.out.println(String.format("New UDP Command %s", command));
if (commandString == null) {
return false;
}
String[] commandWords = commandString.split(" "); //splitCommandLine(commandString);
if (commandWords == null || commandWords.length < 1) {
return false;
}
String first = commandWords[0].toLowerCase();
command = command.toLowerCase();
String command = commandWords[0].toLowerCase();
// strip of the first two letters if they begin pg ...
if (command.substring(0,2).equals("pg")) {
if (command.length() > 2 && command.substring(0,2).equals("pg")) {
command = command.substring(2);
}
ExtCommand extCommand = findCommand(command);
if (extCommand == null) {
sendData("Cmd \"" + command + "\" Not Recognised.");
sendData("Cmd \"" + commandString + "\" Not Recognised.");
return false;
}
if (extCommand.canExecute() == false) {
sendData("Cmd \"" + command + "\" Cannot Execute.");
sendData(" Cmd return string = " + extCommand.getReturnString());
// sendData(" Cmd return string = " + extCommand.getReturnString());
return false;
}
extCommand.execute();
sendData(extCommand.getReturnString());
String output = extCommand.executeCommand(commandString);
sendData(output);
//
// if (command.equals("pgstart")) {
// sendData("PgAck " + "pgstart");
// pamController.pamStart();
// }
// else if (command.equals("pgstop")) {
// sendData("PgAck " + "pgstop");
// pamController.pamStop();
// }
// else if (command.equals("pgping")) {
// sendData("PgAck " + "pgping");
// }
// else if (command.equals("pgstatus")) {
// sendData("PgAck Status " + pamController.getPamStatus());
// }
// else if (command.equals("pgsetrec")) {
// sendData("PgAck pgsetrec");
//
// //triggerRecording(String name, int seconds);
// }
// else if (command.equals("pgexit")) {
// sendData("Exiting PAMGUARD");
// System.exit(0);
// return false;
// }
// else{
// sendData("PgAck " + "Cmd Not Recognised.");
// }
return true;
}
/**
* Slightly nasty split since some module names will be in quotes if they are names with spaces
* so the split needs to a) take out multiple words surrounded by "" and then split what's left
* by word. Or split by work and rejoin anything with "" ?
* @param command
* @return
*/
public static String[] splitCommandLine(String command) {
// https://stackoverflow.com/questions/7804335/split-string-on-spaces-in-java-except-if-between-quotes-i-e-treat-hello-wor
if (command == null) {
return null;
}
Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(command);
List<String> bits = new ArrayList<>();
while (m.find()) {
String bit = m.group(1);
bit = bit.trim();
bit = bit.replace("\"", "");
bits.add(bit);
}
int n = bits.size();
String[] sbits = new String[n];
for (int i = 0; i < bits.size(); i++) {
sbits[i] = (String) bits.get(i);
}
return sbits;
}
/**
* Reply to data called from InterpredData

View File

@ -15,11 +15,11 @@ public class ExitCommand extends ExtCommand {
}
@Override
public boolean execute() {
public String execute(String command) {
PamController.getInstance().pamStop();
PamSettingManager.getInstance().saveFinalSettings();
System.exit(0);
return true;
return getName();
}
@Override

View File

@ -37,14 +37,14 @@ public abstract class ExtCommand {
* @return true if the command executed OK, or if it was sent off
* to execute in a later thread.
*/
public final boolean executeCommand() {
public final String executeCommand(String commandString) {
if (immediate) {
return execute();
return execute(commandString);
}
SwingUtilities.invokeLater(new ExecuteLater());
SwingUtilities.invokeLater(new ExecuteLater(commandString));
return true;
return "Command sent";
}
/**
@ -54,28 +54,35 @@ public abstract class ExtCommand {
*/
private class ExecuteLater implements Runnable {
private String command;
public ExecuteLater(String command) {
this.command = command;
}
@Override
public void run() {
execute();
execute(command);
}
}
/**
* Execute the command
* @param commandWords
* @return true if command executed correctly.
*/
public abstract boolean execute();
public abstract String execute(String command);
/**
* Get the return string from the command.
* <p>By default this is just the command name, but many
* commands will need to send back extensive information.
* @return command output information
*/
public String getReturnString() {
return name;
}
// /**
// * Get the return string from the command.
// * <p>By default this is just the command name, but many
// * commands will need to send back extensive information.
// * @return command output information
// */
// public String getReturnString() {
// return name;
// }
/**
* @return the name of the command

View File

@ -0,0 +1,51 @@
package PamController.command;
import java.util.ArrayList;
import org.w3c.dom.Document;
import PamController.PamControlledUnitSettings;
import PamController.PamSettingManager;
import PamController.PamSettings;
import PamController.settings.output.xml.PamguardXMLWriter;
/**
* Get XML settings from one or more modules. Note that the first two commands are
* the names that are used in PamSettings which are usually the same as module names
* but this gives a little more flexibility.
* @author dg50
*
*/
public class GetXMLSettings extends ExtCommand {
public GetXMLSettings() {
super("getxmlsettings", true);
}
@Override
public String execute(String command) {
String[] commandWords = CommandManager.splitCommandLine(command);
if (commandWords.length < 3) {
return "Unspecified modules for settings";
}
PamSettingManager settingsManager = PamSettingManager.getInstance();
String unitType = commandWords[1];
String unitName = commandWords[2];
ArrayList<PamSettings> foundSettings = settingsManager.findPamSettings(unitType, unitName);
if (foundSettings == null || foundSettings.size() == 0) {
return "No settings";
}
PamguardXMLWriter xmlWriter = PamguardXMLWriter.getXMLWriter();
Document doc = xmlWriter.writeModules(foundSettings);
String txt = xmlWriter.getAsString(doc);
return txt;
}
@Override
public String getHint() {
return "Get XML settings for a named module";
}
}

View File

@ -12,7 +12,7 @@ public class HelpCommand extends ExtCommand {
}
@Override
public boolean execute() {
public String execute(String commandString) {
ArrayList<ExtCommand> commands = commandManager.getCommandsList();
String out = "Available commands are:\n";
for (ExtCommand command : commands) {
@ -26,13 +26,9 @@ public class HelpCommand extends ExtCommand {
}
}
commandManager.sendData(out);
return true;
return getName();
}
@Override
public String getReturnString() {
return super.getReturnString();
}
@Override
public String getHint() {

View File

@ -13,9 +13,9 @@ public class KillCommand extends ExtCommand {
}
@Override
public boolean execute() {
public String execute(String command) {
System.exit(0);
return true;
return getName();
}
@Override

View File

@ -38,7 +38,7 @@ public class NetworkController extends CommandManager {
private boolean initialisationComplete = false;
static private final int MAX_COMMAND_LENGTH = 256;
static private final int MAX_COMMAND_LENGTH = 4096;
private byte[] byteBuffer = new byte[MAX_COMMAND_LENGTH];
@ -127,6 +127,7 @@ public class NetworkController extends CommandManager {
packet.setPort(udpPacket.getPort());
try {
receiveSocket.send(packet);
// receiveSocket.
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();

View File

@ -9,8 +9,8 @@ public class PingCommand extends ExtCommand {
}
@Override
public boolean execute() {
return true;
public String execute(String command) {
return getName();
}
@Override

View File

@ -0,0 +1,89 @@
package PamController.command;
import java.io.Serializable;
import java.io.StringReader;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import PamController.PamControlledUnitSettings;
import PamController.PamSettingManager;
import PamController.PamSettings;
import PamController.settings.output.xml.ModuleNode;
import PamController.settings.output.xml.PamguardXMLReader;
public class SetXMLSettings extends ExtCommand {
public SetXMLSettings() {
super("setxmlsettings", true);
// TODO Auto-generated constructor stub
}
@Override
public String execute(String command) {
/*
* Hopefully, we just need to remove the name of the command and trim and we've got perfect xml to play with.
*/
String xmlString = command.substring(getName().length());
xmlString = xmlString.trim();
PamSettingManager settingManager = PamSettingManager.getInstance();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
Document doc = null;
try {
DocumentBuilder builder = factory.newDocumentBuilder();
doc = builder.parse(new InputSource(new StringReader(xmlString)));
}
catch (Exception e)
{
e.printStackTrace();
return "invalid xml settings can't be pushed to modules";
}
PamguardXMLReader xmlReader = new PamguardXMLReader(doc);
ArrayList<ModuleNode> nodes = xmlReader.getModuleNodes();
for (int i = 0; i < nodes.size(); i++) {
ModuleNode aNode = nodes.get(i);
// now work out the name and type of the module and tell PamSettings to load
// these.
ArrayList<PamSettings> owner = settingManager.findPamSettings(aNode.getUnitType(), aNode.getUnitName());
if (owner == null || owner.size() == 0) {
return "no Java module for xml settings";
}
PamSettings own = owner.get(0);
// System.out.println("XML Node " + aNode.toString());
Object[] objData = xmlReader.unpackModuleNode(aNode);
// System.out.println(objData);
if (objData == null || objData.length == 0) {
return "no Java object(s) for xml settings";
}
for (int iOb = 0; iOb < objData.length; iOb++) {
/*
* Do a test to see if the object is the same type as the current settings.
*/
Object currSettings = own.getSettingsReference();
Object newSettings = objData[iOb];
if (newSettings == null) {
continue;
}
if (currSettings != null && currSettings.getClass() != newSettings.getClass()) {
return "Created java settings object is not the same as currently used: " + currSettings.getClass().getName();
}
PamControlledUnitSettings pcus = new PamControlledUnitSettings(aNode.getUnitType(), aNode.getUnitName(),
aNode.getjClass(), own.getSettingsVersion(), newSettings);
own.restoreSettings(pcus);
}
}
return "xml settings pushed to modules";
}
@Override
public String getHint() {
return "Set XML settings for a named module, (see getxmlsettings to retreive module settings to modify)";
}
}

View File

@ -13,8 +13,9 @@ public class StartCommand extends ExtCommand {
}
@Override
public boolean execute() {
return PamController.getInstance().pamStart();
public String execute(String command) {
boolean ok = PamController.getInstance().pamStart();
return getReturnString();
}
@Override
@ -30,7 +31,6 @@ public class StartCommand extends ExtCommand {
}
}
@Override
public String getReturnString() {
return returnString;
}

View File

@ -18,11 +18,10 @@ public class StatusCommand extends ExtCommand {
}
@Override
public boolean execute() {
return true;
public String execute(String command) {
return getReturnString();
}
@Override
public String getReturnString() {
/*
* Need to work out if it's stalled or not by looking at the Daq systems.

View File

@ -9,9 +9,9 @@ public class StopCommand extends ExtCommand {
}
@Override
public boolean execute() {
public String execute(String command) {
PamController.getInstance().pamStop();
return true;
return getName();
}

View File

@ -20,11 +20,10 @@ public class SummaryCommand extends ExtCommand {
}
@Override
public boolean execute() {
return true;
public String execute(String command) {
return getReturnString();
}
@Override
public String getReturnString() {
PamController pamController = PamController.getInstance();
int nMod = pamController.getNumControlledUnits();

View File

@ -37,12 +37,16 @@ public class PamguardXMLReader {
public PamguardXMLReader(String fileName) {
unpackFile(fileName);
}
public PamguardXMLReader(Document doc) {
NodeList childNodes = doc.getChildNodes();
moduleNodes = new ArrayList<>();
findModuleNodes(childNodes, moduleNodes);
}
private void unpackFile(String fileName) {
Document doc = XMLUtils.createDocument(fileName);
String str = XMLUtils.getStringFromDocument(doc);
// System.out.println(str);
NodeList childNodes = doc.getChildNodes();
moduleNodes = new ArrayList<>();
findModuleNodes(childNodes, moduleNodes);
@ -58,16 +62,18 @@ public class PamguardXMLReader {
* Unpack a module node, creating a class and settings
* it's data.
*/
private Object unpackModuleNode(ModuleNode moduleNode) {
public Object[] unpackModuleNode(ModuleNode moduleNode) {
if (moduleNode == null) {
return null;
}
// first need to find the configuration node, then unpack that.
ArrayList<Node> settingsNodes = findNodesByType(moduleNode.getNode(), "SETTINGS", null);
for (Node node : settingsNodes) {
unpackSettingsNode(node);
Object[] settings = new Object[settingsNodes.size()];
for (int i = 0; i < settingsNodes.size(); i++) {
Node node = settingsNodes.get(i);
settings[i] = unpackSettingsNode(node);
}
return null;
return settings;
}
public Object unpackSettingsNode(Node settingsNode) {
@ -449,6 +455,7 @@ public class PamguardXMLReader {
return classNodes;
}
public ArrayList<Node> findSettingsForClass(String className) {
ArrayList<Node> allSettings = new ArrayList<>();
if (moduleNodes == null) {
@ -566,5 +573,12 @@ public class PamguardXMLReader {
return node.getNodeValue();
}
/**
* @return the moduleNodes
*/
public ArrayList<ModuleNode> getModuleNodes() {
return moduleNodes;
}
}

View File

@ -228,6 +228,26 @@ public class PamguardXMLWriter implements PamSettings {
}
return true;
}
public Document writeModules(ArrayList<PamSettings> settings) {
Document doc = createDocument(System.currentTimeMillis());
Element modules = doc.createElement("MODULES");
Element root = doc.createElement("PAMGUARD");
doc.appendChild(root);
root.appendChild(getInfo(doc, System.currentTimeMillis()));
root.appendChild(modules);
Element moduleData;
for (PamSettings aSet:settings) {
PamSettings[] asArray = new PamSettings[1];
asArray[0] = aSet;
moduleData = writeUnitSettings(doc, modules, aSet, asArray);
if (moduleData != null) {
modules.appendChild(moduleData);
}
}
return doc;
}
private Element getInfo(Document doc, long time) {
Element info = doc.createElement("INFO");

View File

@ -25,9 +25,10 @@ public class PamParameterDataGetter extends PrivatePamParameterData {
* @param field
* @param getter
*/
public PamParameterDataGetter(Object parentObject, Field field, Method getter) {
public PamParameterDataGetter(Object parentObject, Field field, Method getter, Method setter) {
super(parentObject, field);
this.getter = getter;
this.setter = setter;
}
/**
@ -36,9 +37,10 @@ public class PamParameterDataGetter extends PrivatePamParameterData {
* @param shortName
* @param toolTip
*/
public PamParameterDataGetter(Object parentObject, Field field, Method getter, String shortName, String toolTip) {
public PamParameterDataGetter(Object parentObject, Field field, Method getter, Method setter, String shortName, String toolTip) {
super(parentObject, field, shortName, toolTip);
this.getter = getter;
this.setter = setter;
}

View File

@ -10,6 +10,8 @@ import java.util.Collection;
import java.util.Hashtable;
import java.util.List;
import binaryFileStorage.BinaryStoreSettings;
/**
* Description of the parameters within a class. Primarily holds a list
* of PamParameterDataInterface objects each describing one field in the
@ -75,6 +77,9 @@ public class PamParameterSet {
if (special != null) {
return special;
}
// if (parentObject.getClass() == BinaryStoreSettings.class) {
// System.out.println("binary store");
// }
// System.out.println("Auto generate param set for " + parentObject.toString());
ArrayList<Field> allFields = new ArrayList<>();
PamParameterSet pps = new PamParameterSet(parentObject);
@ -110,8 +115,9 @@ public class PamParameterSet {
}
else {
Method getter = findPublicGetter(parentObject, field);
Method setter = findPublicSetter(parentObject, field);
if (getter != null) {
pps.put(new PamParameterDataGetter(parentObject, field, getter));
pps.put(new PamParameterDataGetter(parentObject, field, getter, setter));
}
else {
pps.hiddenFields.add(field);
@ -167,6 +173,48 @@ public class PamParameterSet {
}
return null;
}
/**
* Find a getter who's got one input parameter and void return and who's name starts with
* set and contains the name if the field.
* @param parentObject2
* @param field
*/
private static Method findPublicSetter(Object parentObject, Field field) {
Method[] methods = parentObject.getClass().getMethods();
if (methods == null) {
return null;
}
String fieldNameLower = field.getName().toLowerCase();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i]; /*
* Check that there are no input parameters.
*/
Class<?>[] params = method.getParameterTypes();
if (params != null && params.length != 1) {
continue;
}
if ((method.getModifiers() & Modifier.PUBLIC) == 0) {
continue;
}
// check for void return type.
// Class<?> returnType = method.getReturnType();
// if (returnType != Void.class) {
// continue;
// }
String name = method.getName();
name = name.toLowerCase();
int fieldInd = name.indexOf(fieldNameLower);
int methodLen = name.length();
int fieldLen = fieldNameLower.length();
if (name.startsWith("set") && fieldInd == 3 && methodLen == fieldLen+3) {
return method;
}
}
return null;
}
public static PamParameterSet checkSpecials(Object parentObject, int excludedModifiers) {
if (parentObject.getClass() == Color.class) {

View File

@ -679,12 +679,12 @@ public class PamDetectionOverlayGraphics extends PanelOverlayDraw {
/**
* Draw on spectrogram changed March 2010 so that the default time unit is
* milliseconds (Jave time from 1970) rather than samples. This makes it posible
* to work with data colected over multiple files when operating in viewer mode.
* milliseconds (Java time from 1970) rather than samples. This makes it posible
* to work with data collected over multiple files when operating in viewer mode.
* @param g
* @param pamDataUnit
* @param generalProjector
* @return
* @return updated rectangle
*/
protected Rectangle drawOnSpectrogram(Graphics g, PamDataUnit pamDataUnit, GeneralProjector generalProjector) {
// draw a rectangle with time and frequency bounds of detection.
@ -700,11 +700,8 @@ public class PamDetectionOverlayGraphics extends PanelOverlayDraw {
pamDetection.getSampleDuration() * 1000./parentDataBlock.getSampleRate(),
frequency[0], 0);
boolean isWrapped = false;
if (botRight.x < topLeft.x){
// this means it's wrapped.
isWrapped = true;
botRight.x += g.getClipBounds().width;
botRight.x = g.getClipBounds().width;
}
if (generalProjector.isViewer()) {
Coordinate3d middle = new Coordinate3d();
@ -726,13 +723,8 @@ public class PamDetectionOverlayGraphics extends PanelOverlayDraw {
// Not actually drawing on a spectrogramProjector, so don't have any info on the background color
}
if (isWrapped) {
g.drawRect((int) topLeft.x-g.getClipBounds().width, (int) topLeft.y,
(int) botRight.x - (int) topLeft.x, (int) botRight.y - (int) topLeft.y);
}
g.drawRect((int) topLeft.x, (int) topLeft.y,
(int) botRight.x - (int) topLeft.x, (int) botRight.y - (int) topLeft.y);
return new Rectangle((int) topLeft.x, (int) topLeft.y,
(int) botRight.x - (int) topLeft.x, (int) botRight.y - (int) topLeft.y);
}