org.tenje.jtrain.runnable.JTrainAccessories.java Source code

Java tutorial

Introduction

Here is the source code for org.tenje.jtrain.runnable.JTrainAccessories.java

Source

/*******************************************************************************
 * Copyright (c): Jonas Tenni 2017
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 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 Lesser Public License for more
 * details.
 * You should have received a copy of the GNU General Lesser Public License
 * along with this program. If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 *******************************************************************************/
package org.tenje.jtrain.runnable;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.MissingFormatArgumentException;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;
import org.tenje.jtrain.AccessoryDecoderAddress;
import org.tenje.jtrain.Sensor;
import org.tenje.jtrain.Signal;
import org.tenje.jtrain.SignalAspect;
import org.tenje.jtrain.SignalAspectControlTurnout;
import org.tenje.jtrain.SwitchableScheduler;
import org.tenje.jtrain.Turnout;
import org.tenje.jtrain.dccpp.PacketFactory;
import org.tenje.jtrain.dccpp.impl.PacketFactoryImpl;
import org.tenje.jtrain.dccpp.impl.PacketSensorRegistry;
import org.tenje.jtrain.dccpp.impl.PacketTurnoutRegistry;
import org.tenje.jtrain.dccpp.server.DccppSocket;
import org.tenje.jtrain.rpi.RPiSensor;
import org.tenje.jtrain.rpi.RPiServoTurnout;
import org.tenje.jtrain.rpi.RPiSignal;
import org.tenje.jtrain.rpi.RPiTurnout;
import org.tenje.jtrain.runnable.JTrainXmlReader.XmlReadException;

import com.pi4j.io.gpio.GpioPinDigitalOutput;

/**
 * A stand-alone program to control some accessories.
 * 
 * @author Jonas Tenni
 */
public class JTrainAccessories {

    /**
     * Starts the program.
     * 
     * @param args
     *            The arguments containing the station host address in the first
     *            (0) index with format <code>address:port</code>.
     * @throws SecurityException
     *             Thrown if a security manager exists and if the caller does
     *             not have LoggingPermission("control").
     * @throws IOException
     *             Thrown if an I/O error occurs when opening the output file.
     */
    public static void main(String[] args) throws SecurityException, IOException {
        Logger logger = new JTrainLogger("Accessory Decoder", System.out,
                new FileOutputStream("accessories.log.txt"));
        try {
            start(args, logger);
        } catch (XmlReadException ex) {
            logger.log(Level.SEVERE, "Failed to start program, invalid configuration:"
                    + System.getProperty("line.separator") + "\t" + ex.getMessage());
        } catch (Throwable t) {
            logger.log(Level.SEVERE, "Failed to start program:", t);
        }
    }

    private static void start(String[] args, Logger logger)
            throws FileNotFoundException, JDOMException, IOException, InterruptedException {
        DccppSocket socket;
        String[] addressParts;
        InetAddress address;
        int port;
        if (args.length < 1) {
            throw new IllegalArgumentException("no station address defined");
        }
        addressParts = args[0].split(":");
        if (addressParts.length < 2) {
            throw new IllegalArgumentException("no station port defined");
        }
        address = InetAddress.getByName(addressParts[0]);
        port = Integer.parseInt(addressParts[1]);
        // -

        PacketFactory packetFactory = new PacketFactoryImpl();
        PacketFactoryImpl.regiserDefaultPackets(packetFactory);
        PacketSensorRegistry sensorRegistry = null;
        PacketTurnoutRegistry turnoutRegistry = new PacketTurnoutRegistry();

        SAXBuilder builder = new SAXBuilder();
        Document document = builder.build(new FileInputStream("accessories.xml"));
        Attribute attribute;
        AccessoryDecoderAddress accessoryAddress;
        List<Sensor> sensors = new ArrayList<>();
        for (Element accessoryElem : document.getRootElement().getChildren()) {
            if (accessoryElem.getName().equals("lightSignal")) {
                Map<SignalAspect, GpioPinDigitalOutput> pins = new EnumMap<>(SignalAspect.class);
                Map<AccessoryDecoderAddress, SignalAspect> addresses = new HashMap<>();
                Signal signal;
                SignalAspect aspect;
                AccessoryDecoderAddress aspectAddress = null;
                GpioPinDigitalOutput pin;
                for (Element aspectElem : accessoryElem.getChildren()) {
                    if ((attribute = aspectElem.getAttribute("color")) != null) {
                        aspect = SignalAspect.valueOf(attribute.getValue().toUpperCase());
                    } else {
                        throw new MissingFormatArgumentException("no color defined: " + aspectElem);
                    }
                    if ((attribute = aspectElem.getAttribute("address")) != null) {
                        aspectAddress = new AccessoryDecoderAddress(Integer.parseInt(attribute.getValue()), 0);
                    } else {
                        throw new MissingFormatArgumentException("no address defined: " + accessoryElem);
                    }
                    pin = JTrainXmlReader.getOutputPin(aspectElem, "pin");
                    if (pins.put(aspect, pin) != null) {
                        throw new IllegalArgumentException("color already defined: " + aspect.name().toLowerCase());
                    }
                    if (addresses.put(aspectAddress, aspect) != null) {
                        throw new IllegalArgumentException(
                                "address already defined: " + aspectAddress.getMainAddress());
                    }
                }
                if (!pins.isEmpty()) {
                    signal = new RPiSignal(aspectAddress, pins);
                    for (Entry<AccessoryDecoderAddress, SignalAspect> entry : addresses.entrySet()) {
                        turnoutRegistry
                                .register(new SignalAspectControlTurnout(entry.getKey(), signal, entry.getValue()));
                    }
                }
            } else if (accessoryElem.getName().equals("scheduler")) {
                SwitchableScheduler scheduler = JTrainXmlReader.getScheduler(accessoryElem);
                if ((attribute = accessoryElem.getAttribute("address")) != null) {
                    accessoryAddress = new AccessoryDecoderAddress(Integer.parseInt(attribute.getValue()), 0);
                    throw new UnsupportedOperationException("Not supported, yet");
                }
                scheduler.setSwitched(true);
            } else {
                if ((attribute = accessoryElem.getAttribute("address")) != null) {
                    accessoryAddress = new AccessoryDecoderAddress(Integer.parseInt(attribute.getValue()), 0);
                    switch (accessoryElem.getName()) {
                    case "sensor": {
                        Sensor sensor = new RPiSensor(accessoryAddress,
                                JTrainXmlReader.getInputPin(accessoryElem, "pin"));
                        sensors.add(sensor);
                    }
                        break;
                    case "turnout": {
                        int switchTime = 0;
                        if ((attribute = accessoryElem.getAttribute("switchTime")) != null) {
                            switchTime = Integer.parseInt(attribute.getValue());
                        }
                        Turnout turnout = new RPiTurnout(accessoryAddress,
                                JTrainXmlReader.getOutputPin(accessoryElem, "straightPin"),
                                JTrainXmlReader.getOutputPin(accessoryElem, "thrownPin"), switchTime);
                        turnoutRegistry.register(turnout);
                    }
                        break;
                    case "servoTurnout": {
                        int pin, switchTime = 0;
                        double straightTime, thrownTime;
                        if ((attribute = accessoryElem.getAttribute("pin")) != null) {
                            pin = Integer.parseInt(attribute.getValue());
                        } else {
                            throw new MissingFormatArgumentException("no pin defined: " + accessoryElem);
                        }
                        if ((attribute = accessoryElem.getAttribute("straightPwm")) != null) {
                            straightTime = Double.parseDouble(attribute.getValue());
                        } else {
                            throw new MissingFormatArgumentException("no straightPwm defined: " + accessoryElem);
                        }
                        if ((attribute = accessoryElem.getAttribute("thrownPwm")) != null) {
                            thrownTime = Double.parseDouble(attribute.getValue());
                        } else {
                            throw new MissingFormatArgumentException("no thrownPwm defined: " + accessoryElem);
                        }
                        if ((attribute = accessoryElem.getAttribute("switchTime")) != null) {
                            switchTime = Integer.parseInt(attribute.getValue());
                        }
                        Turnout turnout = new RPiServoTurnout(accessoryAddress, pin, straightTime, thrownTime,
                                switchTime);
                        turnoutRegistry.register(turnout);
                    }
                        break;
                    }
                } else {
                    throw new MissingFormatArgumentException("no address defined: " + accessoryElem);
                }
            }
        }

        // --
        logger.info("Connecting to " + address + ":" + port + "...");
        while (true) {
            try {
                socket = new DccppSocket(address, port, packetFactory);
                logger.info("Connected");
            } catch (IOException ex) {
                Thread.sleep(1_000);
                logger.log(Level.WARNING, "Connection failed. Retrying...");
                continue; // Retry
            }
            if (sensorRegistry == null) {
                sensorRegistry = new PacketSensorRegistry(socket, socket.getConnectedBroker());
                for (Sensor sensor : sensors) {
                    sensorRegistry.register(sensor);
                }
                sensors = null;
            } else {
                sensorRegistry.setReceiver(socket.getConnectedBroker());
            }
            socket.addPacketListener(sensorRegistry);
            socket.addPacketListener(turnoutRegistry);
            synchronized (socket) {
                socket.wait(); // Wait until connection lost
            }
            logger.log(Level.WARNING, "Connection lost. Trying to reconnect...");
        }
    }

}