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

Java tutorial

Introduction

Here is the source code for org.obiba.onyx.jade.instrument.gehealthcare.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.gehealthcare;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

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 btrRecordFileName;

    private String btrDatabaseFileName;

    private String executableForParticipantInfo;

    private String xmlFileName;

    private String pdfFileNameRestingEcg;

    private String pdfFileNameFullEcg;

    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 getBtrRecordFileName() {
        return btrRecordFileName;
    }

    public void setBtrRecordFileName(String btrRecordFileName) {
        this.btrRecordFileName = btrRecordFileName;
    }

    public String getBtrDatabaseFileName() {
        return btrDatabaseFileName;
    }

    public void setBtrDatabaseFileName(String btrDatabaseFileName) {
        this.btrDatabaseFileName = btrDatabaseFileName;
    }

    public String getExecutableForParticipantInfo() {
        return executableForParticipantInfo;
    }

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

    public String getXmlFileName() {
        return xmlFileName;
    }

    public void setXmlFileName(String xmlFileName) {
        this.xmlFileName = xmlFileName;
    }

    public String getPdfFileNameRestingEcg() {
        return pdfFileNameRestingEcg;
    }

    public void setPdfFileNameRestingEcg(String pdfFileNameRestingEcg) {
        this.pdfFileNameRestingEcg = pdfFileNameRestingEcg;
    }

    public String getPdfFileNameFullEcg() {
        return pdfFileNameFullEcg;
    }

    public void setPdfFileNameFullEcg(String pdfFileNameFullEcg) {
        this.pdfFileNameFullEcg = pdfFileNameFullEcg;
    }

    /**
     * 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[] backupDatabaseFiles = new File(getInitPath()).listFiles(filter);
            if (backupDatabaseFiles.length > 0) {
                for (int i = 0; i < backupDatabaseFiles.length; i++) {
                    FileUtil.copyFile(backupDatabaseFiles[i],
                            new File(getDatabasePath(), backupDatabaseFiles[i].getName()));
                }
            } else {
                File[] databaseFiles = new File(getDatabasePath()).listFiles(filter);
                for (int i = 0; i < databaseFiles.length; i++) {
                    FileUtil.copyFile(databaseFiles[i], new File(getInitPath(), databaseFiles[i].getName()));
                }
            }

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

        // Delete BTrieve record file.
        File btrRecordFile = new File(getDatabasePath(), getBtrRecordFileName());
        if (!btrRecordFile.delete()) {
            log.warn("Could not delete BTrieve record file.");
        }

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

        reportFile = new File(getExportPath(), getPdfFileNameRestingEcg());
        if (!reportFile.delete()) {
            log.warn("Could not delete Resting Ecg PDF output file!");
        }

        reportFile = new File(getExportPath(), getPdfFileNameFullEcg());
        if (!reportFile.delete()) {
            log.warn("Could not delete Full Ecg PDF output file!");
        }

    }

    private void overwriteIniFile(String settingsFileName) {
        File backupSettingsFile = new File(getInitPath(), settingsFileName);
        File currentSettingsFile = new File(getCardioPath(), settingsFileName);
        try {
            if (backupSettingsFile.exists()) {
                FileUtil.copyFile(backupSettingsFile, currentSettingsFile);
            } else {
                new File(getInitPath()).mkdir();
                FileUtil.copyFile(currentSettingsFile, backupSettingsFile);
            }
        } catch (Exception ex) {
            throw new RuntimeException("Error initializing ECG " + currentSettingsFile.getName() + " file", ex);
        }
    }

    /**
     * Place the results and xml and pdf files 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 (!pd.getName().equalsIgnoreCase("doc") && !pd.getName().equalsIgnoreCase("xpath")
                        && !pd.getName().equalsIgnoreCase("xmldocument")
                        && !pd.getName().equalsIgnoreCase("class")) {
                    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));

            File pdfRestingEcgFile = new File(getExportPath(), getPdfFileNameRestingEcg());
            outputToSend.put("pdfFile", DataBuilder.buildBinary(pdfRestingEcgFile));

            File pdfFullEcgFile = new File(getExportPath(), getPdfFileNameFullEcg());
            outputToSend.put("pdfFileFull", DataBuilder.buildBinary(pdfFullEcgFile));

            instrumentExecutionService.addOutputParameterValues(outputToSend);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void initParticipantData() {
        File participantDataFile = new File(getDatabasePath() + getBtrRecordFileName());
        try {
            Map<String, Data> inputData = instrumentExecutionService.getInputParametersValue(
                    "INPUT_PARTICIPANT_BARCODE", "INPUT_PARTICIPANT_LAST_NAME", "INPUT_PARTICIPANT_FIRST_NAME",
                    "INPUT_PARTICIPANT_GENDER", "INPUT_PARTICIPANT_HEIGHT", "INPUT_PARTICIPANT_WEIGHT",
                    "INPUT_PARTICIPANT_ETHNIC_GROUP", "INPUT_PARTICIPANT_BIRTH_YEAR",
                    "INPUT_PARTICIPANT_BIRTH_MONTH", "INPUT_PARTICIPANT_BIRTH_DAY", "INPUT_PARTICIPANT_PACEMAKER");

            FileOutputStream participantDataOuputStream = new FileOutputStream(participantDataFile);
            BtrInputGenerator inputGenerator = new BtrInputGenerator();
            participantDataOuputStream.write((inputGenerator.generateByteBuffer(inputData)).array());
            participantDataOuputStream.close();

        } catch (Exception ex) {
            throw new RuntimeException("Error writing ecg participant data file: ", ex);
        }

        List<String> command = new ArrayList<String>();
        command.add("cmd");
        command.add("/c");
        command.add(
                getExecutableForParticipantInfo() + " " + getBtrRecordFileName() + " " + getBtrDatabaseFileName());

        ProcessBuilder builder = new ProcessBuilder(command);
        builder.directory(new File(getDatabasePath()));

        try {
            log.info("Executing command '{}'", command);
            Process process = builder.start();
            log.info("Waiting for process '{}'", process);
            process.waitFor();
        } catch (IOException e) {
            log.error("Could not create external process.", e);
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            log.error("Error waiting for process to end.", 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() {
        showProcessingDialog();
        deleteDeviceData();
        initParticipantData();
    }

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

        // Get data from external app
        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;
    }

}