org.obiba.onyx.jade.instrument.gemac800.CardiosoftInstrumentRunner.java Source code

Java tutorial

Introduction

Here is the source code for org.obiba.onyx.jade.instrument.gemac800.CardiosoftInstrumentRunner.java

Source

/*******************************************************************************
 * Copyright 2008(c) The OBiBa Consortium. All rights reserved.
 *
 * This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/
package org.obiba.onyx.jade.instrument.gemac800;

import javax.swing.*;
import java.awt.*;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import org.obiba.onyx.jade.client.JnlpClient;
import org.obiba.onyx.jade.instrument.ExternalAppLauncherHelper;
import org.obiba.onyx.jade.instrument.InstrumentRunner;
import org.obiba.onyx.jade.instrument.service.InstrumentExecutionService;
import org.obiba.onyx.util.FileUtil;
import org.obiba.onyx.util.data.Data;
import org.obiba.onyx.util.data.DataBuilder;
import org.obiba.onyx.util.data.DataType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;

/**
 * Specified instrument runner for the ECG
 *
 * @author acarey
 */

public class CardiosoftInstrumentRunner implements InstrumentRunner, InitializingBean {

    private static final Logger log = LoggerFactory.getLogger(JnlpClient.class);

    // Injected by spring.
    protected InstrumentExecutionService instrumentExecutionService;

    protected ExternalAppLauncherHelper externalAppHelper;

    private String cardioPath;

    private String initPath;

    private String databasePath;

    private String exportPath;

    private String settingsFileName;

    private String winSettingsFileName;

    private String executableForParticipantInfo;

    private String xmlFileName;

    private ResourceBundle ecgResourceBundle;

    private Locale locale;

    public void afterPropertiesSet() throws Exception {
        setEcgResourceBundle(ResourceBundle.getBundle("ecg-instrument", getLocale()));
    }

    public InstrumentExecutionService getInstrumentExecutionService() {
        return instrumentExecutionService;
    }

    public void setInstrumentExecutionService(InstrumentExecutionService instrumentExecutionService) {
        this.instrumentExecutionService = instrumentExecutionService;
    }

    public ExternalAppLauncherHelper getExternalAppHelper() {
        return externalAppHelper;
    }

    public void setExternalAppHelper(ExternalAppLauncherHelper externalAppHelper) {
        this.externalAppHelper = externalAppHelper;
    }

    public String getCardioPath() {
        return cardioPath;
    }

    public void setCardioPath(String cardioPath) {
        this.cardioPath = cardioPath;
    }

    public String getInitPath() {
        return initPath;
    }

    public void setInitPath(String initPath) {
        this.initPath = initPath;
    }

    public String getDatabasePath() {
        return databasePath;
    }

    public void setDatabasePath(String databasePath) {
        this.databasePath = databasePath;
    }

    public String getExportPath() {
        return exportPath;
    }

    public void setExportPath(String exportPath) {
        this.exportPath = exportPath;
    }

    public String getSettingsFileName() {
        return settingsFileName;
    }

    public void setSettingsFileName(String settingsFileName) {
        this.settingsFileName = settingsFileName;
    }

    public String getWinSettingsFileName() {
        return winSettingsFileName;
    }

    public void setWinSettingsFileName(String winSettingsFileName) {
        this.winSettingsFileName = winSettingsFileName;
    }

    public String getExecutableForParticipantInfo() {
        return executableForParticipantInfo;
    }

    public void setExecutableForParticipantInfo(String executableForParticipantInfo) {
        this.executableForParticipantInfo = executableForParticipantInfo;
    }

    public String getXmlFileName() {
        if (xmlFileName == null) {
            xmlFileName = instrumentExecutionService.getParticipantID() + ".xml";
        }
        return xmlFileName;
    }

    /**
     * Replace the instrument configuration file if needed Delete the result database and files
     *
     * @throws Exception
     */
    protected void deleteDeviceData() {

        // Overwrite the CardioSoft configuration file
        // overwriteIniFile(getSettingsFileName());
        // overwriteIniFile(getWinSettingsFileName());

        // Initialize the CardioSoft database
        FilenameFilter filter = new FilenameFilter() {
            public boolean accept(File dir, String name) {
                return (name.endsWith(".BTR"));
            }
        };

        try {
            File backupDir = new File(getCardioPath(), getInitPath());
            if (backupDir.exists() == false) {
                if (backupDir.mkdir() == false) {
                    log.error("Unable to create backup directory {}", backupDir.getAbsolutePath());
                    throw new RuntimeException("unable to create backup directory " + backupDir.getAbsolutePath());
                }
            }
            File[] backupDatabaseFiles = new File(getCardioPath(), getInitPath()).listFiles(filter);
            if (backupDatabaseFiles.length > 0) {
                for (int i = 0; i < backupDatabaseFiles.length; i++) {
                    FileUtil.copyFile(backupDatabaseFiles[i],
                            new File(getCardioPath() + "/" + getDatabasePath(), backupDatabaseFiles[i].getName()));
                }
            } else {
                File[] databaseFiles = new File(getCardioPath(), getDatabasePath()).listFiles(filter);
                if (databaseFiles != null) {
                    for (int i = 0; i < databaseFiles.length; i++) {
                        FileUtil.copyFile(databaseFiles[i],
                                new File(getCardioPath() + "/" + getInitPath(), databaseFiles[i].getName()));
                    }
                }
            }

        } catch (Exception couldNotInitDbs) {
            throw new RuntimeException("Error initializing ECG database files", couldNotInitDbs);
        }

        File reportFile = new File(getExportPath(), getXmlFileName());
        if (reportFile.exists() && !reportFile.delete()) {
            log.warn("Could not delete Cardiosoft XML output file!");
        }

    }

    private void overwriteIniFile(String settingsFileName) {
        File backupDir = new File(getCardioPath(), getInitPath());
        File backupSettingsFile = new File(backupDir, settingsFileName);
        File currentSettingsFile = new File(getCardioPath(), settingsFileName);
        try {
            if (backupSettingsFile.exists()) {
                log.info("Restoring backup from {} to {}.", backupSettingsFile.getAbsolutePath(),
                        currentSettingsFile.getAbsolutePath());
                FileUtil.copyFile(backupSettingsFile, currentSettingsFile);
            } else {
                log.info("Backup file {} does not exist.", backupSettingsFile.getAbsolutePath());
                if (backupDir.exists() == false) {
                    if (backupDir.mkdir() == false) {
                        log.error("Unable to create backup directory {}", backupDir.getAbsolutePath());
                        throw new RuntimeException(
                                "unable to create backup directory " + backupDir.getAbsolutePath());
                    }
                }
                log.info("Copying {} to {}", currentSettingsFile.getAbsolutePath(),
                        backupSettingsFile.getAbsolutePath());
                FileUtil.copyFile(currentSettingsFile, backupSettingsFile);
            }
        } catch (Exception ex) {
            throw new RuntimeException("Error initializing ECG " + currentSettingsFile.getName() + " file", ex);
        }
    }

    /**
     * Place the results and xml file into a map object to send them to the server for persistence
     *
     * @param resultParser
     * @throws Exception
     */
    public void sendDataToServer(CardiosoftInstrumentResultParser resultParser) {
        Map<String, Data> outputToSend = new HashMap<String, Data>();

        try {
            for (PropertyDescriptor pd : Introspector.getBeanInfo(CardiosoftInstrumentResultParser.class)
                    .getPropertyDescriptors()) {
                if (instrumentExecutionService.hasOutputParameter(pd.getName())) {
                    Object value = pd.getReadMethod().invoke(resultParser);
                    if (value != null) {
                        if (value instanceof Long) {

                            // We need to subtract one to the birthday month since the month of January is represented by "0" in
                            // java.util.Calendar (January is represented by "1" in Cardiosoft).
                            if (pd.getName().equals("participantBirthMonth")) {
                                outputToSend.put(pd.getName(), DataBuilder.buildInteger(((Long) value) - 1));
                            } else {
                                outputToSend.put(pd.getName(), DataBuilder.buildInteger((Long) value));
                            }

                        } else if (value instanceof Double) {
                            outputToSend.put(pd.getName(), DataBuilder.buildDecimal((Double) value));
                        } else {
                            outputToSend.put(pd.getName(), DataBuilder.buildText(value.toString()));
                        }
                    } else { // send null values as well (ONYX-585)
                        log.info("Output parameter " + pd.getName() + " was null; will send null to server");

                        if (pd.getPropertyType().isAssignableFrom(Long.class)) {
                            log.info("Output parameter " + pd.getName() + " is of type INTEGER");
                            outputToSend.put(pd.getName(), new Data(DataType.INTEGER, null));
                        } else if (pd.getPropertyType().isAssignableFrom(Double.class)) {
                            log.info("Output parameter " + pd.getName() + " is of type DECIMAL");
                            outputToSend.put(pd.getName(), new Data(DataType.DECIMAL, null));
                        } else {
                            log.info("Output parameter " + pd.getName() + " is of type TEXT");
                            outputToSend.put(pd.getName(), new Data(DataType.TEXT, null));
                        }
                    }
                }
            }

            // Save the xml and pdf files
            File xmlFile = new File(getExportPath(), getXmlFileName());
            outputToSend.put("xmlFile", DataBuilder.buildBinary(xmlFile));

            instrumentExecutionService.addOutputParameterValues(outputToSend);

        } catch (Exception e) {
            log.debug("Sending data to server failed.", e);
            throw new RuntimeException(e);
        }
    }

    /**
     * Create an information dialog that tells the user to wait while the data is being processed.
     */
    private void showProcessingDialog() {

        JPanel messagePanel = new JPanel();
        messagePanel.setAlignmentX(Component.CENTER_ALIGNMENT);
        messagePanel.setLayout(new BoxLayout(messagePanel, BoxLayout.Y_AXIS));

        JLabel message = new JLabel(ecgResourceBundle.getString("Message.ProcessingEcgMeasurement"));
        message.setFont(new Font(Font.DIALOG, Font.PLAIN, 20));
        messagePanel.add(message);

        JLabel subMessage = new JLabel(ecgResourceBundle.getString("Message.ProcessingEcgMeasurementInstructions"));
        subMessage.setFont(new Font(Font.DIALOG, Font.PLAIN, 14));
        subMessage.setForeground(Color.RED);
        messagePanel.add(subMessage);

        JFrame window = new JFrame();
        window.add(messagePanel);
        window.pack();

        // Make sure dialog stays on top of all other application windows.
        window.setAlwaysOnTop(true);
        window.setLocationByPlatform(true);

        // Center dialog horizontally at the bottom of the screen.
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        window.setLocation((screenSize.width - window.getWidth()) / 2, screenSize.height - window.getHeight() - 70);

        window.setEnabled(false);
        window.setVisible(true);

    }

    /**
     * Implements parent method initialize from InstrumentRunner Delete results from previous measurement
     */
    public void initialize() {
        if (externalAppHelper.isSotfwareAlreadyStarted()) {
            JOptionPane.showMessageDialog(null, externalAppHelper.getExecutable()
                    + " already lock for execution.  Please make sure that another instance is not running.",
                    "Cannot start application!", JOptionPane.ERROR_MESSAGE);
            throw new RuntimeException("already lock for execution");
        }
        File backupDir = new File(getCardioPath(), getInitPath());
        if (backupDir.exists()) {
            try {
                FileUtil.delete(backupDir);
            } catch (IOException e) {
            }
        }
        showProcessingDialog();
        deleteDeviceData();
    }

    /**
     * Implements parent method run from InstrumentRunner Launch the external application, retrieve and send the data
     */
    public void run() {
        externalAppHelper.launchExternalSoftware();
        FileInputStream resultInputStream = null;
        File cardioSoftXmlOutput = new File(getExportPath(), getXmlFileName());

        // log exportPath and then files
        File exportDir = new File(getExportPath());
        log.info("export dir exists: " + exportDir.exists());
        if (exportDir.exists()) {
            log.info("Files in export path: {}", Arrays.toString(exportDir.list()));
        }

        log.info("cardioSoftXmlOutput.getAbsoluteFile().exists(): "
                + cardioSoftXmlOutput.getAbsoluteFile().exists());
        log.info("cardioSoftXmlOutput.exists(): " + cardioSoftXmlOutput.exists());

        // Get data from external app
        log.info("Expected XML file path: {}", cardioSoftXmlOutput.getAbsolutePath());
        if (cardioSoftXmlOutput.exists()) {

            try {
                resultInputStream = new FileInputStream(cardioSoftXmlOutput);
            } catch (FileNotFoundException ex) {
                throw new RuntimeException("Cardiosoft output data file not found", ex);
            }

            CardiosoftInstrumentResultParser resultParser = new CardiosoftInstrumentResultParser(resultInputStream);
            sendDataToServer(resultParser);

            try {
                resultInputStream.close();
            } catch (Exception e) {
                log.warn("Could not close the inputStream", e);
            }

        } else {
            log.error(
                    "Cardiosoft output data file not found.  This usually happens if the application is closed before completing the ECG measurement.");
        }

    }

    /**
     * Implements parent method shutdown from InstrumentRunner Delete results from current measurement
     */
    public void shutdown() {
        deleteDeviceData();
    }

    public Locale getLocale() {
        return locale;
    }

    public void setLocale(Locale locale) {
        this.locale = locale;
    }

    public void setEcgResourceBundle(ResourceBundle ecgResourceBundle) {
        this.ecgResourceBundle = ecgResourceBundle;
    }

}