org.hwo.pulscounter/src/org/hwo/pulscounter/PulsCounterApplication.java

561 lines
15 KiB
Java

package org.hwo.pulscounter;
import static org.hwo.logging.Logging.log;
import static org.hwo.logging.LogLevel.*;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Frame;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.InvalidPropertiesFormatException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.swing.JFrame;
import org.hsqldb.persist.EventLogInterface;
import org.hwo.StringHelper;
import org.hwo.configuration.ConfigurableObjects;
import org.hwo.i18n.Messages;
import org.hwo.io.NewSerialPort.NewSerialPort;
import org.hwo.logging.Logging;
import org.hwo.platform.Platform;
import org.hwo.servicelink.ServiceLink;
import org.hwo.servicelink.ServiceLinkListener;
import org.hwo.pulscounter.db.PulsCounterDatabase;
import org.hwo.pulscounter.device.IDeviceConnector;
import org.hwo.pulscounter.device.ServiceLinkDeviceConnector;
import org.hwo.pulscounter.device.SimpleLinkDeviceConnector;
import org.hwo.pulscounter.device.SimulatedCounter;
import org.hwo.pulscounter.ui.AppSettingsListener;
import org.hwo.pulscounter.ui.BatchRunner;
import org.hwo.pulscounter.ui.NewMainWindow;
import org.hwo.pulscounter.ui.ShutdownNotification;
import org.hwo.scheduler.Scheduler;
public class PulsCounterApplication implements ServiceLinkListener{
static PulsCounterApplication _application;
public static PulsCounterApplication getApplication(){
if (_application == null)
_application = new PulsCounterApplication(null);
return _application;
}
private Properties applicationConfiguration,
defaultConfiguration;
private Object uiSynchronization;
private boolean uiIsFinished;
private boolean shouldSaveConfiguration;
private boolean dontLoadTranslations;
private List<PulsCounterApplicationListener>
applicationListeners;
private Vector<String> unseenMessages;
private List<Class<IDeviceConnector>>
interfaceClasses;
private List<IDeviceConnector> interfaces;
private PulsCounterDatabase database;
private List<String> batchCommands;
private NewSerialPort serialPort;
private ServiceLink serviceLink;
private List<AppSettingsListener> appSettingsListeners;
private boolean snapshotLock;
private List<ExportSetting> exportSettings;
private Scheduler scheduler;
private String[] channelDescriptions;
public PulsCounterApplication(String[] args) {
/* Initialize Logging Framework */
logStartup();
/* Check... */
if (_application != null){
throw new InstantiationError("Only one Instance of PulsCounterApplication can exist!");
} else {
_application = this;
}
/* Initialize fields... */
uiIsFinished = false;
uiSynchronization = new Object();
applicationListeners = new LinkedList<PulsCounterApplicationListener>();
unseenMessages = new Vector<String>();
exportSettings = new ArrayList<>();
interfaceClasses = new ArrayList<>();
interfaces = new ArrayList<>();
batchCommands = new ArrayList<>();
/* Prepare Translation Framework */
Messages i18n = Messages.getInstance();
i18n.setMissingKeysFileName("missing-translation.txt");
/* Prepare for Startup */
loadApplicationConfiguration();
/* Parse Command Line Arguments */
Iterator<String> options = Arrays.asList(args).iterator();
while (options.hasNext()){
String option = options.next();
switch (option){
case "--gui":
if (!options.hasNext()){
log(FATAL,"Argument to --gui is missing");
throw new IllegalArgumentException("Argument to --gui is missing");
} else {
applicationConfiguration.setProperty("ui.class", options.next());
}
break;
case "--batch":
case "-B":
applicationConfiguration.setProperty("ui.class", BatchRunner.class.getCanonicalName());
break;
case "-G":
applicationConfiguration.setProperty("ui.class", NewMainWindow.class.getCanonicalName());
break;
case "--batch-execute":
batchCommands.add( options.next() );
break;
case "--no-translation":
dontLoadTranslations = true;
break;
default:
log(WARN,"Unknown command line parameter: %s", option);
}
}
if (!dontLoadTranslations){
Messages.loadMessages(PulsCounterApplication.class);
} else {
log(INFO,"Translation text fragments are NOT loaded, due to command line switch (--no-translation)");
}
/* Old stuff... */
// this.initialize();
}
private Properties createDefaultApplicationConfiguration(){
defaultConfiguration = new Properties();
defaultConfiguration.setProperty("ui.class", NewMainWindow.class.getCanonicalName());
defaultConfiguration.setProperty("interface.classes", StringHelper.join(new String[]{
ServiceLinkDeviceConnector.class.getCanonicalName(),
SimulatedCounter.class.getCanonicalName(),
SimpleLinkDeviceConnector.class.getCanonicalName()
}, ","));
return defaultConfiguration;
}
private void loadApplicationConfiguration(){
createDefaultApplicationConfiguration();
applicationConfiguration = new Properties(defaultConfiguration);
try {
/* Try to load configuration from file */
FileInputStream fis = new FileInputStream("synololog.cfg");
applicationConfiguration.loadFromXML(fis);
fis.close();
} catch (InvalidPropertiesFormatException e) {
log(WARN,"synololog.cfg is misformated");
} catch (FileNotFoundException e) {
log(WARN,"synololog.cfg not found");
} catch (IOException e) {
log(ERROR,"I/O Error reading synololog.cfg");
}
applicationConfiguration.setProperty("ui.class", NewMainWindow.class.getCanonicalName());
}
public Properties getApplicationConfiguration(){
return this.applicationConfiguration;
}
private static void logStartup(){
log("Synololog Application Startup");
log("JAVA Environment: %s (%s)", System.getProperty("java.version"),
System.getProperty("java.vendor"));
log("Operating System: %s [%s] %s", System.getProperty("os.name"),
System.getProperty("os.arch"),
System.getProperty("os.version"));
log("User Environment: %s (%s) (CWD:%s)", System.getProperty("user.name"),
System.getProperty("user.home"),
System.getProperty("user.dir"));
log("Hostname: %s",Platform.getHostName());
log("OS Search Path: %s", System.getenv("PATH"));
}
public String[] getBatchCommands(){
return batchCommands.toArray(new String[0]);
}
public void start(){
initialize();
String uiClassName = applicationConfiguration.getProperty("ui.class");
Object ui = null;
try {
Class uiClazz = PulsCounterApplication.class.getClassLoader().loadClass(uiClassName);
Constructor<?> constructor = uiClazz.getConstructor(PulsCounterApplication.class);
ui = (Object) constructor.newInstance(this);
} catch (ClassNotFoundException e) {
log(FATAL,"user interface class could not be loaded [%s] %s",uiClassName,e.getMessage());
} catch (NoSuchMethodException e) {
log(FATAL,"user interface class misses valid constructor [%s] %s",uiClassName,e.getMessage());
} catch (SecurityException e) {
e.printStackTrace();
} catch (Exception e) {
log(FATAL,"user interface class could not be instantiated. [%s] %s",uiClassName,e.getMessage());
e.printStackTrace();
}
waitUiFinished();
// ShutdownNotification sn = new ShutdownNotification();
// if (Component.class.isInstance(ui)){
// sn.setLocationRelativeTo((Component)ui);
// }
// sn.setVisible(true);
try {
shutdown();
} catch (Exception e){
log(e);
}
// sn.setVisible(false);
}
private void initialize(){
/* Interface Classes should be merged with application well known*/
HashSet<String> interfaceClassNames = new HashSet<>();
for (String icn: applicationConfiguration.getProperty("interface.classes").split(",")){
interfaceClassNames.add(icn);
}
for (String icn: defaultConfiguration.getProperty("interface.classes").split(",")){
interfaceClassNames.add(icn);
}
for (String interfaceClassName: interfaceClassNames){
try {
Class<IDeviceConnector> clazz = (Class<IDeviceConnector>)PulsCounterApplication.class.getClassLoader().loadClass(interfaceClassName);
interfaceClasses.add(clazz);
} catch (ClassNotFoundException e) {
log(ERROR,"Interface class could not be loaded: %s",interfaceClassName);
}
}
Integer nIntf = Integer.parseInt(applicationConfiguration.getProperty("interfaces.n","0"));
for (int n=0;n<nIntf;n++){
Class<IDeviceConnector> clazz = getInterfaceClass(applicationConfiguration.getProperty(String.format("interfaces.%d.class",n)));
addInterface(clazz, applicationConfiguration.getProperty(String.format("interfaces.%d.settings",n)));
}
database = new PulsCounterDatabase();
log(INFO,"Database Schema Version: %s", database.getSchemaVersion());
String sProfiles = database.getProperty("export.profiles");
if (sProfiles != null){
Integer nProfiles = new Integer(sProfiles);
for (int n=0;n<nProfiles;n++){
String profileConf = database.getProperty(String.format("export.profiles.%d",n));
log(INFO,"Export Profile %d: %s",n,profileConf);
if (profileConf != null){
ExportSetting es = new ExportSetting();
ConfigurableObjects.setConfiguration(es, profileConf);
exportSettings.add(es);
}
}
}
}
private void shutdown(){
log(INFO,"Application shutdown...");
for (IDeviceConnector c: interfaces){
c.shutdown();
}
/* Dispose all left frames */
for (Frame frame: JFrame.getFrames()){
frame.setVisible(false);
frame.dispose();
}
if (shouldSaveConfiguration){
applicationConfiguration.setProperty("interfaces.n", Integer.toString(interfaces.size()));
for (int n=0;n<interfaces.size();n++){
applicationConfiguration.setProperty(String.format("interfaces.%d.class", n), interfaces.get(n).getClass().getCanonicalName());
applicationConfiguration.setProperty(String.format("interfaces.%d.settings", n), interfaces.get(n).getConnectionSettings());
}
for (int n=0;n<exportSettings.size();n++){
String conf = ConfigurableObjects.getConfiguration(exportSettings.get(n));
if (conf != null){
database.setProperty(String.format("export.profiles.%d", n), conf);
}
}
database.setProperty("export.profiles", new Integer(exportSettings.size()).toString());
try {
log(INFO,"Save application configuration");
FileOutputStream fos = new FileOutputStream("synololog.cfg");
applicationConfiguration.storeToXML(fos, "Synololog Application Configuration");
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
log(ERROR,"synololog.cfg could not be created/opened for writing");
} catch (IOException e) {
log(ERROR,"synololog.cfg could not be written");
}
}
database.close();
Messages.getInstance().saveMissingStrings();
}
private void waitUiFinished(){
synchronized (uiSynchronization) {
while (!uiIsFinished){
try {
uiSynchronization.wait(250);
} catch (InterruptedException e){
log(e);
}
}
}
log(INFO,"UI notified finish");
}
public void notifyUiIsFinished(boolean saveApplicationConfiguration){
synchronized (uiSynchronization) {
uiIsFinished = true;
shouldSaveConfiguration = saveApplicationConfiguration;
uiSynchronization.notify();
}
}
/* Interface Types */
public List<Class<IDeviceConnector>> getInterfaceClasses(){
return this.interfaceClasses;
}
private Class<IDeviceConnector> getInterfaceClass(String className){
for (Class<IDeviceConnector> c:interfaceClasses){
if (c.getCanonicalName().equals(className))
return c;
}
return null;
}
/* Physical Interfaces */
public List<IDeviceConnector> getInterfaces(){
return this.interfaces;
}
public void addInterface(Class<IDeviceConnector> clazz,String connectionSettings){
try {
IDeviceConnector idc = clazz.newInstance();
idc.setConnectionSettings(connectionSettings);
interfaces.add(idc);
fireinterfacesChanged();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public void removeInterface(IDeviceConnector intf){
interfaces.remove(intf);
fireinterfacesChanged();
}
private void fireinterfacesChanged(){
log(INFO,"interfaces changed");
for (PulsCounterApplicationListener l: applicationListeners){
l.interfacesChanged(this);
}
}
public PulsCounterDatabase getDatabase() {
return database;
}
/* Snapshots */
public void checkForSnapShots(){
for (IDeviceConnector idc: this.interfaces){
Integer deviceSerial = idc.getDeviceSerial();
Integer highestIndex = database.highestSnapShot(deviceSerial);
log(INFO,"Highest known snapshot index for device #%d is %d", deviceSerial, highestIndex);
SnapShot[] snapshots = idc.readSnapShots(highestIndex+1);
if (snapshots != null){
getDatabase().storeSnapshots(snapshots);
}
}
}
public void addSnapshotToDatabase(SnapShot snapShot){
getDatabase().storeSnapshots(new SnapShot[]{ snapShot });
}
/* ToDO: Upgrade the old stuff ... */
/*
private void initialize(){
appSettingsListeners = new LinkedList<AppSettingsListener>();
exportSettings = new LinkedList<ExportSetting>();
scheduler = new Scheduler();
channelDescriptions = new String[32];
loadPrefs();
try {
snapshotManager = new SnapshotManager();
snapshotManager.notify(Notification.INITIALIZE);
} catch (FileNotFoundException e){
e.printStackTrace();
}
}
*/
public void addAppSettingsListener(AppSettingsListener listener){
appSettingsListeners.add(listener);
}
public void removeAppSettingsListener(AppSettingsListener listener){
appSettingsListeners.remove(listener);
}
public void addPulsCounterApplicationListener(PulsCounterApplicationListener listener){
applicationListeners.add(listener);
}
public void removePulsCounterApplicationListener(PulsCounterApplicationListener listener){
applicationListeners.remove(listener);
}
public void fireServiceLinkChanged(){
for (AppSettingsListener l: appSettingsListeners){
l.ServiceLinkChanged(serviceLink);
}
}
public void message(String message){
log(INFO, message);
if (applicationListeners.size() == 0){
unseenMessages.addElement(message);
} else {
while (!unseenMessages.isEmpty()){
String msg = unseenMessages.remove(0);
for (PulsCounterApplicationListener listener: applicationListeners){
listener.messageArrived(msg);
}
}
for (PulsCounterApplicationListener listener: applicationListeners){
listener.messageArrived(message);
}
};
}
@Override
public void connectionStateChanged(Boolean connected) {
}
public List<ExportSetting> getExportSettings() {
return exportSettings;
}
}