All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.yamcs.tctm.TcpTcTmDataLink Maven / Gradle / Ivy

There is a newer version: 5.10.9
Show newest version
package org.yamcs.tctm;


import java.io.EOFException;
import java.io.IOException;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.HashMap;

import org.yamcs.ConfigurationException;
import org.yamcs.Spec;
import org.yamcs.Spec.OptionType;
import org.yamcs.TmPacket;
import org.yamcs.YConfiguration;
import org.yamcs.commanding.PreparedCommand;
import org.yamcs.utils.YObjectLoader;

public class TcpTcTmDataLink extends AbstractTcTmParamLink implements Runnable {
    protected Socket tmSocket;
    protected String host;
    protected int port;
    protected long initialDelay;

    @Override
    public Spec getSpec() {
        var spec = getDefaultSpec();
        spec.addOption("host", OptionType.STRING).withRequired(true);
        spec.addOption("port", OptionType.INTEGER).withRequired(true);
        spec.addOption("initialDelay", OptionType.INTEGER);
        spec.addOption("packetInputStreamClassName", OptionType.STRING);
        spec.addOption("packetInputStreamArgs", OptionType.MAP).withSpec(Spec.ANY);
        return spec;
    }

    @Override
    public void init(String instance, String name, YConfiguration config) throws ConfigurationException {
        super.init(instance, name, config);
        host = config.getString("host");
        port = config.getInt("port");

        initialDelay = config.getLong("initialDelay", -1);
        // Input stream defaults to GenericPacketInputStream
        if (config.containsKey("packetInputStreamClassName")) {
            packetInputStreamClassName = config.getString("packetInputStreamClassName");
            packetInputStreamArgs = config.getConfigOrEmpty("packetInputStreamArgs");
        } else {
            packetInputStreamClassName = GenericPacketInputStream.class.getName();
            HashMap m = new HashMap<>();
            m.put("maxPacketLength", 1000);
            m.put("lengthFieldOffset", 4);
            m.put("lengthFieldLength", 2);
            m.put("lengthAdjustment", 7);
            m.put("initialBytesToStrip", 0);
            packetInputStreamArgs = YConfiguration.wrap(m);
        }      
    }

    protected synchronized void checkAndOpenSocket() throws IOException {
        if (tmSocket != null) {
            return;
        }
        InetAddress address = InetAddress.getByName(host);
        tmSocket = new Socket();
        tmSocket.setKeepAlive(true);
        tmSocket.connect(new InetSocketAddress(address, port), 1000);
        try {
            packetInputStream = YObjectLoader.loadObject(packetInputStreamClassName);
            outputStream = tmSocket.getOutputStream();
        } catch (ConfigurationException e) {
            log.error("Cannot instantiate the packetInput: " + e);
            try {
                tmSocket.close();
            } catch (IOException e2) {
            }
            tmSocket = null;
            outputStream = null;
            packetInputStream = null;
            throw e;
        }
        packetInputStream.init(tmSocket.getInputStream(), packetInputStreamArgs);
        log.info("Link established to {}:{}", host, port);
    }

    protected synchronized boolean isSocketOpen() {
        return tmSocket != null;
    }

    protected synchronized void sendBuffer(byte[] data) throws IOException {
        if (outputStream == null) {
            throw new IOException(String.format("No connection to %s:%d", host, port));
        }
        outputStream.write(data);
    }

    protected synchronized void closeSocket() {
        if (tmSocket != null) {
            try {
                tmSocket.close();
            } catch (IOException e) {
            }
            tmSocket = null;
            outputStream = null;
            packetInputStream = null;
        }
    }

    @Override
    public void run() {
        if (initialDelay > 0) {
            try {
                Thread.sleep(initialDelay);
                initialDelay = -1;
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return;
            }
        }

        while (isRunningAndEnabled()) {
            TmPacket tmpkt = getNextPacket();
            if (tmpkt == null) {
                break;
            }
            processPacket(tmpkt);
        }
    }

    public TmPacket getNextPacket() {
        TmPacket pwt = null;
        while (isRunningAndEnabled()) {
            try {
                checkAndOpenSocket();
                byte[] packet = packetInputStream.readPacket();
                dataIn(1, packet.length);
                TmPacket pkt = new TmPacket(timeService.getMissionTime(), packet);
                pkt.setEarthReceptionTime(timeService.getHresMissionTime());
                pwt = packetPreprocessor.process(pkt);
                if (pwt != null) {
                    break;
                }
            } catch (EOFException e) {
                log.warn("TM Connection closed");
                closeSocket();
            } catch (IOException e) {
                if (isRunningAndEnabled()) {
                    String exc = (e instanceof ConnectException) ? ((ConnectException) e).getMessage() : e.toString();
                    log.info("Cannot open or read TM socket {}:{} {}'. Retrying in 10s", host, port, exc);
                }
                closeSocket();
                for (int i = 0; i < 10; i++) {
                    if (!isRunningAndEnabled()) {
                        break;
                    }
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e1) {
                        Thread.currentThread().interrupt();
                        return null;
                    }
                }
            } catch (PacketTooLongException e) {
                log.warn(e.toString());
                closeSocket();
            }
        }
        return pwt;
    }

    @Override
    public boolean sendCommand(PreparedCommand pc) {
        byte[] binary = postprocess(pc);

        try {
            sendBuffer(binary);
            dataOutCount.getAndIncrement();
            ackCommand(pc.getCommandId());
            return true;
        } catch (IOException e) {
            String reason = String.format("Error writing to TC socket to %s:%d; %s", host, port, e.toString());
            log.warn(reason);
            failedCommand(pc.getCommandId(), reason);
            return true;
        }
    }

    @Override
    public void doStart() {
        if (!isDisabled()) {
            Thread thread = new Thread(this);
            thread.setName(getClass().getSimpleName() + "-" + linkName);
            thread.start();
        }
        notifyStarted();
    }

    @Override
    public void doStop() {
        closeSocket();
        notifyStopped();
    }

    @Override
    public void doDisable() {
        closeSocket();
    }

    @Override
    public void doEnable() {
        Thread thread = new Thread(this);
        thread.setName(getClass().getSimpleName() + "-" + linkName);
        thread.start();
    }

    @Override
    public String getDetailedStatus() {
        if (isDisabled()) {
            return String.format("DISABLED (should connect to %s:%d)", host, port);
        }
        if (isSocketOpen()) {
            return String.format("OK, connected to %s:%d", host, port);
        } else {
            return String.format("Not connected to %s:%d", host, port);
        }
    }

    @Override
    protected Status connectionStatus() {
        return !isSocketOpen() ? Status.UNAVAIL : Status.OK;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy