org.eclipse.scada.protocol.arduino.ArduinoDevice.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.scada.protocol.arduino.ArduinoDevice.java

Source

/*******************************************************************************
 * Copyright (c) 2010, 2013 TH4 SYSTEMS GmbH and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     TH4 SYSTEMS GmbH - initial API and implementation
 *******************************************************************************/
package org.eclipse.scada.protocol.arduino;

import java.net.InetSocketAddress;
import java.util.Calendar;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioDatagramConnector;
import org.eclipse.scada.protocol.arduino.messages.CommonMessage;
import org.eclipse.scada.protocol.arduino.messages.ConfigurationMessage;
import org.eclipse.scada.protocol.arduino.messages.DataMessage;
import org.eclipse.scada.protocol.arduino.messages.WriteRequestMessage;
import org.eclipse.scada.utils.concurrent.ScheduledExportedExecutorService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ArduinoDevice extends IoHandlerAdapter {

    private final static Logger logger = LoggerFactory.getLogger(ArduinoDevice.class);

    private static final long TIMEOUT = Long.getLong("org.eclipse.scada.protocol.arduino.timeout", 2_000);

    private static final long POLL_TIME = Long.getLong("org.eclipse.scada.protocol.arduino.pollTime", 100);

    private static final long POLL_TIMEOUT = Long.getLong("org.eclipse.scada.protocol.arduino.pollTimeout", 1_000);

    private final InetSocketAddress address;

    private boolean started = false;

    private ScheduledExecutorService executorService;

    private IoSession session;

    private DeviceState state;

    private int sequence;

    private final ArduinoDeviceListener listener;

    private NioDatagramConnector connector;

    private ScheduledFuture<?> connectJob;

    private ScheduledFuture<?> requestJob;

    private Calendar lastData;

    private Calendar lastRequest;

    private final boolean activateLogger;

    private long timeout = TIMEOUT;

    private long pollTime = POLL_TIME;

    private long pollTimeout = POLL_TIMEOUT;

    public ArduinoDevice(final InetSocketAddress address, final ArduinoDeviceListener listener,
            final boolean activateLogger) {
        this.address = address;
        this.listener = listener;
        this.activateLogger = activateLogger;
    }

    public void setTimeout(final long timeout) {
        this.timeout = timeout;
    }

    public void setPollTime(final long pollTime) {
        this.pollTime = pollTime;
    }

    public void setPollTimeout(final long pollTimeout) {
        this.pollTimeout = pollTimeout;
    }

    public synchronized void start() {
        if (this.started) {
            return;
        }

        this.started = true;

        this.executorService = ScheduledExportedExecutorService
                .newSingleThreadExportedScheduledExecutor("ArduninoDevice/" + this.address);

        this.connector = new NioDatagramConnector();

        this.connector.setHandler(this);
        if (this.activateLogger) {
            this.connector.getFilterChain().addLast("logger", new LoggingFilter(this.getClass().getName()));
        }

        final ArduinoCodec codec = new ArduinoCodec();
        this.connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(codec, codec));

        this.connector.connect(this.address);
    }

    public synchronized void stop() {
        if (!this.started) {
            return;
        }

        this.started = false;

        this.connector.dispose();

        if (this.session != null) {
            this.session.close(true);
        }

        this.executorService.shutdown();
        this.executorService = null;

    }

    @Override
    public void sessionCreated(final IoSession session) throws Exception {
        this.sequence = 0;
        setState(DeviceState.DISCONNECTED);
        this.session = session;
    }

    private synchronized void setState(final DeviceState state) {
        logger.debug("State changed: {}", state);

        this.state = state;
        this.executorService.execute(new Runnable() {

            @Override
            public void run() {
                ArduinoDevice.this.listener.stateChange(state);
                if (state == DeviceState.DISCONNECTED) {
                    ArduinoDevice.this.listener.deviceDisconnected();
                }
            }
        });
    }

    @Override
    public void sessionOpened(final IoSession session) throws Exception {
        connect();
    }

    @Override
    public void sessionClosed(final IoSession session) throws Exception {
        this.session = null;
        setState(DeviceState.DISCONNECTED);
    }

    private void connect() {
        setState(DeviceState.CONNECTING);
        this.session.write(new CommonMessage(this.sequence++, CommandCode.REQUEST_CONFIGURATION));
        this.connectJob = this.executorService.schedule(new Runnable() {

            @Override
            public void run() {
                connectTimeout();
            }
        }, 10, TimeUnit.SECONDS);
    }

    protected synchronized void connectTimeout() {
        if (!this.started) {
            return;
        }

        setState(DeviceState.DISCONNECTED);
        triggerReconnect();
    }

    private synchronized void triggerReconnect() {
        this.connectJob = this.executorService.schedule(new Runnable() {

            @Override
            public void run() {
                connect();
            }
        }, this.timeout, TimeUnit.MILLISECONDS);
    }

    @Override
    public void messageReceived(final IoSession session, final Object message) throws Exception {
        if (message instanceof CommonMessage) {
            switch (((CommonMessage) message).getCommandCode()) {
            case RESPOND_CONFIGURATION:
                respondConfiguration((ConfigurationMessage) message);
                break;
            case RESPOND_DATA:
                respondData((DataMessage) message);
                break;
            }
        }
    }

    private synchronized void respondData(final DataMessage message) {
        this.lastData = Calendar.getInstance();
        this.executorService.execute(new Runnable() {

            @Override
            public void run() {
                ArduinoDevice.this.listener.dataChange(message.getData());
            }
        });
    }

    private synchronized void respondConfiguration(final ConfigurationMessage message) {
        if (this.state != DeviceState.CONNECTING) {
            return;
        }

        setState(DeviceState.CONNECTED);
        // stop the connect job
        if (this.connectJob != null) {
            this.connectJob.cancel(false);
            this.connectJob = null;
        }

        this.executorService.execute(new Runnable() {

            @Override
            public void run() {
                ArduinoDevice.this.listener.deviceConnected(message.getParameters());
            }
        });

        startRequestJob();

        triggerRequest();
    }

    private synchronized void startRequestJob() {
        stopRequestJob();

        this.lastData = Calendar.getInstance();

        this.requestJob = this.executorService.scheduleAtFixedRate(new Runnable() {

            @Override
            public void run() {
                checkTimeout();
                triggerRequest();
            }
        }, this.pollTime, this.pollTime, TimeUnit.MILLISECONDS);
    }

    protected synchronized void checkTimeout() {
        if (this.lastData != null && System.currentTimeMillis() - this.lastData.getTimeInMillis() > this.timeout) {
            handleTimeout();
        }
    }

    private synchronized void stopRequestJob() {
        if (this.requestJob != null) {
            this.requestJob.cancel(false);
            this.requestJob = null;
        }
    }

    protected synchronized void triggerRequest() {
        if (this.state != DeviceState.CONNECTED) {
            return;
        }

        if (this.lastRequest == null
                || System.currentTimeMillis() - this.lastRequest.getTimeInMillis() > this.pollTimeout) {
            this.lastRequest = Calendar.getInstance();
            this.session.write(new CommonMessage(this.sequence++, CommandCode.REQUEST_DATA));
        }
    }

    protected synchronized void handleTimeout() {
        stopRequestJob();
        setState(DeviceState.DISCONNECTED);
        triggerReconnect();
    }

    public synchronized void sendWrite(final short itemIndex, final Object value) {
        this.session.write(new WriteRequestMessage(this.sequence++, itemIndex, value));
    }

}