net.solarnetwork.node.io.yasdi4j.YasdiMasterDeviceFactory.java Source code

Java tutorial

Introduction

Here is the source code for net.solarnetwork.node.io.yasdi4j.YasdiMasterDeviceFactory.java

Source

/* ==================================================================
 * YasdiMasterDeviceFactory.java - Mar 7, 2013 9:33:17 AM
 * 
 * Copyright 2007-2013 SolarNetwork.net Dev Team
 * 
 * This program is free software; you can redistribute it and/or 
 * modify it under the terms of the GNU General Public License as 
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License 
 * along with this program; if not, write to the Free Software 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 
 * 02111-1307 USA
 * ==================================================================
 */

package net.solarnetwork.node.io.yasdi4j;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import net.solarnetwork.node.settings.SettingSpecifier;
import net.solarnetwork.node.settings.SettingSpecifierProvider;
import net.solarnetwork.node.settings.support.BasicTextFieldSettingSpecifier;
import net.solarnetwork.node.settings.support.BasicTitleSettingSpecifier;
import net.solarnetwork.node.settings.support.BasicToggleSettingSpecifier;
import net.solarnetwork.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.context.MessageSource;
import org.springframework.context.support.ResourceBundleMessageSource;
import de.michaeldenk.yasdi4j.YasdiDevice;
import de.michaeldenk.yasdi4j.YasdiDriver;

/**
 * Factory for {@link YasdiMaster} instances configured to use a serial port.
 * 
 * <p>
 * The configurable properties of this class are:
 * </p>
 * 
 * <dl class="class-properties">
 * <dt>device</dt>
 * <dd>The serial port to use, e.g. {@code /dev/ttyS0}.</dd>
 * 
 * <dt>baud</dt>
 * <dd>The port speed. Defaults to <b>1200</b>.</dd>
 * 
 * <dt>media</dt>
 * <dd>The YASDI media type. Defaults to <b>RS485</b>.</dd>
 * 
 * <dt>protocol</dt>
 * <dd>The packet format. Defaults to <b>SMANet</b>.</dd>
 * 
 * <dt>expectedDeviceCount</dt>
 * <dd>The number of expected devices for a given device. Defaults to <b>1</b>.</dd>
 * </dl>
 * 
 * @author matt
 * @version 1.1
 */
public class YasdiMasterDeviceFactory implements SettingSpecifierProvider, ObjectFactory<YasdiMaster> {

    private static final Object MONITOR = new Object();
    private static MessageSource MESSAGE_SOURCE;
    private static de.michaeldenk.yasdi4j.YasdiMaster MASTER;
    private static List<YasdiDevice> DEVICES = new CopyOnWriteArrayList<YasdiDevice>();
    private static Map<YasdiMasterDeviceFactory, Object> FACTORIES = new WeakHashMap<YasdiMasterDeviceFactory, Object>(
            2);
    private static File INI_FILE = null;

    private final Logger log = LoggerFactory.getLogger(getClass());

    private String driver = "libyasdi_drv_serial";
    private String device = "/dev/ttyS0";
    private int baud = 1200;
    private String media = "RS485";
    private String protocol = "SMANet";
    private int expectedDeviceCount = 1;
    private boolean debugYasdi = false;

    private YasdiMasterDevice master;

    /**
     * Default constructor.
     */
    public YasdiMasterDeviceFactory() {
        super();
        synchronized (FACTORIES) {
            FACTORIES.put(this, new WeakReference<Object>(MONITOR));
        }
    }

    /**
     * Get a UID for this factory, based on device. value.
     * 
     * @return the UID
     */
    public String getUID() {
        return device;
    }

    @Override
    public synchronized YasdiMaster getObject() throws BeansException {
        if (master != null) {
            return master;
        }

        if (MASTER != null) {
            MASTER.reset();
        } else {
            // generate our configuration, based on all configured factories
            setupConfigIniFile();

            log.debug("Initializing YASDI from config {}", INI_FILE.getAbsolutePath());
            MASTER = de.michaeldenk.yasdi4j.YasdiMaster.getInstance();
            try {
                MASTER.initialize(INI_FILE.getAbsolutePath());
            } catch (IOException e) {
                throw new RuntimeException("Unable to initialize YasdiMaster", e);
            }
        }
        try {
            YasdiDriver[] drivers = MASTER.getDrivers();
            for (YasdiDriver d : MASTER.getDrivers()) {
                MASTER.setDriverOnline(d);
            }
            log.debug("Initialized {} drivers", drivers.length);
        } catch (IOException e) {
            throw new RuntimeException("Unable to initialize YasdiMaster", e);
        }

        // detect devices
        int expectedCount = 0;
        for (YasdiMasterDeviceFactory factory : FACTORIES.keySet()) {
            expectedCount += factory.expectedDeviceCount;
        }
        log.debug("Detecting devices, looking for {}", expectedCount);
        try {
            MASTER.detectDevices(expectedCount);
        } catch (IOException e) {
            throw new RuntimeException("Unable to detect devices", e);
        }

        if (log.isInfoEnabled()) {
            List<String> deviceNames = new ArrayList<String>();
            for (YasdiDevice dev : MASTER.getDevices()) {
                deviceNames.add(dev.getName());
            }
            log.info("Detected {} SMA devices: {}", deviceNames.size(),
                    StringUtils.commaDelimitedStringFromCollection(deviceNames));
        }

        DEVICES.clear();
        DEVICES.addAll(Arrays.asList(MASTER.getDevices()));

        master = new YasdiMasterDevice(DEVICES, this.device);
        return master;
    }

    private void setupConfigIniFile() {
        Set<String> drivers = new LinkedHashSet<String>(2);
        List<YasdiMasterDeviceFactory> comDevices = new ArrayList<YasdiMasterDeviceFactory>(2);
        List<YasdiMasterDeviceFactory> ipDevices = new ArrayList<YasdiMasterDeviceFactory>(2);

        for (YasdiMasterDeviceFactory factory : FACTORIES.keySet()) {
            drivers.add(factory.driver);
            if ("libyasdi_drv_serial".equals(factory.driver)) {
                comDevices.add(factory);
            } else {
                ipDevices.add(factory);
            }
        }

        PrintWriter writer = null;
        try {
            if (INI_FILE == null) {
                String filePath = System.getProperty("sn.home", "");
                if (filePath.length() > 0) {
                    filePath += '/';
                }
                filePath += "var/yasdi.ini";
                INI_FILE = new File(filePath);
                INI_FILE.deleteOnExit();
            }
            writer = new PrintWriter(new BufferedWriter(new FileWriter(INI_FILE)), false);
        } catch (IOException e) {
            throw new RuntimeException("Unable to create YASDI ini file", e);
        }

        log.debug("Generating YASDI configuration file {}", INI_FILE.getAbsolutePath());

        int i = 0;
        try {
            writer.println("[DriverModules]");
            for (String driver : drivers) {
                writer.printf("Driver%d=%s\n", i++, driver);
            }
            writer.println();

            if (comDevices.size() > 0) {
                i = 1;
                for (YasdiMasterDeviceFactory factory : comDevices) {
                    writer.printf("[COM%d]\n", i++);
                    writer.printf("Device=%s\n", factory.device);
                    writer.printf("Media=%s\n", factory.media);
                    writer.printf("Baudrate=%d\n", factory.baud);
                    writer.printf("Protocol=%s\n", factory.protocol);
                }
                writer.println();
            }

            if (ipDevices.size() > 0) {
                i = 1;
                for (YasdiMasterDeviceFactory factory : ipDevices) {
                    writer.printf("[IP%d]\n", i++);
                    writer.printf("Device=%s\n", factory.device);
                    writer.printf("Protocol=%s\n", factory.protocol);
                }
                writer.println();
            }

            if (debugYasdi) {
                writer.println("[Misc]");
                writer.println("DebugOutput=/dev/stderr");
            }
        } finally {
            writer.flush();
            writer.close();
        }
    }

    @Override
    public String getSettingUID() {
        return "net.solarnetwork.node.io.yasdi4j";
    }

    @Override
    public String getDisplayName() {
        return "YASDI Master";
    }

    @Override
    public MessageSource getMessageSource() {
        synchronized (MONITOR) {
            if (MESSAGE_SOURCE == null) {
                ResourceBundleMessageSource source = new ResourceBundleMessageSource();
                source.setBundleClassLoader(getClass().getClassLoader());
                source.setBasename(getClass().getName());
                MESSAGE_SOURCE = source;
            }
        }
        return MESSAGE_SOURCE;
    }

    public static List<SettingSpecifier> getDefaultSettingSpecifiers() {
        List<SettingSpecifier> results = new ArrayList<SettingSpecifier>(4);
        YasdiMasterDeviceFactory defaults = new YasdiMasterDeviceFactory();
        results.add(new BasicTextFieldSettingSpecifier("expectedDeviceCount",
                String.valueOf(defaults.expectedDeviceCount)));
        results.add(new BasicTextFieldSettingSpecifier("driver", defaults.driver));
        results.add(new BasicTextFieldSettingSpecifier("device", defaults.device));
        results.add(new BasicTextFieldSettingSpecifier("baud", String.valueOf(defaults.baud)));
        results.add(new BasicTextFieldSettingSpecifier("media", String.valueOf(defaults.media)));
        results.add(new BasicTextFieldSettingSpecifier("protocol", String.valueOf(defaults.protocol)));
        results.add(new BasicToggleSettingSpecifier("debugYasdi", Boolean.FALSE));
        return results;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((device == null) ? 0 : device.hashCode());
        result = prime * result + ((driver == null) ? 0 : driver.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        YasdiMasterDeviceFactory other = (YasdiMasterDeviceFactory) obj;
        if (device == null) {
            if (other.device != null)
                return false;
        } else if (!device.equals(other.device))
            return false;
        if (driver == null) {
            if (other.driver != null)
                return false;
        } else if (!driver.equals(other.driver))
            return false;
        return true;
    }

    @Override
    public List<SettingSpecifier> getSettingSpecifiers() {
        List<SettingSpecifier> results = getDefaultSettingSpecifiers();

        // add in read-only device UIDs
        Set<String> deviceNames = new TreeSet<String>();

        try {
            // call getObject() to initialize
            getObject();

            for (YasdiMasterDeviceFactory factory : FACTORIES.keySet()) {
                YasdiMaster master = factory.getObject();
                deviceNames.add(master.getName());
            }

            for (String deviceName : deviceNames) {
                results.add(0, new BasicTitleSettingSpecifier("availableDevice", deviceName, true));
            }
        } catch (RuntimeException e) {
            log.warn("Exception getting YASDI device names: {}", e.getMessage());
        }

        return results;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public void setDevice(String device) {
        this.device = device;
    }

    public void setBaud(int baud) {
        this.baud = baud;
    }

    public void setMedia(String media) {
        this.media = media;
    }

    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    public void setExpectedDeviceCount(int expectedDeviceCount) {
        this.expectedDeviceCount = expectedDeviceCount;
    }

    public void setDebugYasdi(boolean debugYasdi) {
        this.debugYasdi = debugYasdi;
    }

}