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

org.yamcs.sle.AbstractTmSleLink Maven / Gradle / Ivy

Go to download

Data links for connecting Yamcs to SLE (Space Link Extension) providers such as Ground Stations

There is a newer version: 1.6.0
Show newest version
package org.yamcs.sle;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.yamcs.ConfigurationException;
import org.yamcs.YConfiguration;
import org.yamcs.parameter.AggregateValue;
import org.yamcs.parameter.ParameterValue;
import org.yamcs.parameter.SystemParametersCollector;
import org.yamcs.sle.Constants.DeliveryMode;
import org.yamcs.sle.Constants.FrameQuality;
import org.yamcs.sle.Constants.LockStatus;
import org.yamcs.sle.Constants.ProductionStatus;
import org.yamcs.sle.user.FrameConsumer;
import org.yamcs.sle.user.RacfServiceUserHandler;
import org.yamcs.sle.user.RacfStatusReport;
import org.yamcs.sle.user.RafServiceUserHandler;
import org.yamcs.sle.user.RcfServiceUserHandler;
import org.yamcs.tctm.TcTmException;
import org.yamcs.tctm.ccsds.AbstractTmFrameLink;
import org.yamcs.time.Instant;
import org.yamcs.utils.StringConverter;
import org.yamcs.utils.TimeEncoding;
import org.yamcs.utils.ValueUtility;
import org.yamcs.xtce.util.AggregateMemberNames;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;

public abstract class AbstractTmSleLink extends AbstractTmFrameLink implements FrameConsumer {
    String packetPreprocessorClassName;
    Object packetPreprocessorArgs;
    RacfServiceUserHandler rsuh;

    RacfSleMonitor sleMonitor = new MyMonitor();
    SleConfig sconf;
    DeliveryMode deliveryMode;
    String service;

    // how soon should reconnect in case the connection to the SLE provider is lost
    // if negative, do not reconnect
    int reconnectionIntervalSec;

    private org.yamcs.sle.State sleState = org.yamcs.sle.State.UNBOUND;
    private volatile ParameterValue rafStatus;

    private String sp_sleState_id, sp_rafStatus_id;
    final static AggregateMemberNames rafStatusMembers = AggregateMemberNames.get(new String[] { "productionStatus",
            "carrierLockStatus", "subcarrierLockStatus", "symbolSyncLockStatus", "deliveredFrameNumber",
            "errorFreeFrameNumber" });

    // if null-> RAF, otherwise RCF
    GVCID gvcid = null;

    /**
     * Creates a new UDP Frame Data Link
     * 
     * @param deliveryMode
     * 
     * @throws ConfigurationException
     *             if port is not defined in the configuration
     */
    public void init(String instance, String name, YConfiguration config, DeliveryMode deliveryMode)
            throws ConfigurationException {
        super.init(instance, name, config);
        this.deliveryMode = deliveryMode;
        YConfiguration slec = YConfiguration.getConfiguration("sle").getConfig("Providers")
                .getConfig(config.getString("sleProvider"));
        service = config.getString("service", "RAF");
        gvcid = null;
        if ("RCF".equals(service)) {
            int tfVersion = config.getInt("rcfTfVersion", frameHandler.getFrameType().getVersion());
            int spacecraftId = config.getInt("rcfSpacecraftId", frameHandler.getSpacecraftId());
            int vcId = config.getInt("rcfVcId", -1);
            gvcid = new GVCID(tfVersion, spacecraftId, vcId);
        } else if (!"RAF".equals(service)) {
            throw new ConfigurationException("Invalid service '" + service + "' specified. Use one of RAF or RCF");
        }
        String type;
        switch (deliveryMode) {
        case rtnCompleteOnline:
            type = gvcid == null ? "raf-onlc" : "rcf-onlc";
            break;
        case rtnTimelyOnline:
            type = gvcid == null ? "raf-onlt" : "rcf-onlt";
            break;
        case rtnOffline:
            type = gvcid == null ? "raf-offl" : "rcf-offl";
            break;
        default:
            throw new ConfigurationException("Invalid delivery mode " + deliveryMode + " for this data link");

        }
        this.sconf = new SleConfig(slec, type);

    }

    protected synchronized void connect() {
        eventProducer.sendInfo("Connecting to SLE " + service + " service " + sconf.host + ":" + sconf.port
                + " as user " + sconf.auth.getMyUsername());
        if (gvcid == null) {
            rsuh = new RafServiceUserHandler(sconf.auth, sconf.attr, deliveryMode, this);
        } else {
            rsuh = new RcfServiceUserHandler(sconf.auth, sconf.attr, deliveryMode, this);
        }
        rsuh.setVersionNumber(sconf.versionNumber);
        rsuh.setAuthLevel(sconf.authLevel);
        rsuh.addMonitor(sleMonitor);
        NioEventLoopGroup workerGroup = getEventLoop();
        Bootstrap b = new Bootstrap();
        b.group(workerGroup);
        b.channel(NioSocketChannel.class);
        b.option(ChannelOption.SO_KEEPALIVE, true);
        b.handler(new ChannelInitializer() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(sconf.tmlMaxLength, 4, 4));
                ch.pipeline().addLast(new Isp1Handler(true, sconf.hbSettings));
                ch.pipeline().addLast(rsuh);
            }
        });
        b.connect(sconf.host, sconf.port).addListener(f -> {
            if (!f.isSuccess()) {
                eventProducer.sendWarning("Failed to connect to the SLE provider: " + f.cause().getMessage());
                rsuh = null;
                if (reconnectionIntervalSec >= 0) {
                    workerGroup.schedule(() -> connect(), reconnectionIntervalSec, TimeUnit.SECONDS);
                }
            } else {
                sleBind();
            }
        });
    }

    private void sleBind() {
        rsuh.bind().handle((v, t) -> {
            if (t != null) {
                eventProducer.sendWarning("Failed to bind: " + t.getMessage());
                return null;
            }
            sleStart();
            return null;
        });
    }

    abstract void sleStart();

    @Override
    protected Status connectionStatus() {
        return (rsuh != null && rsuh.getState() == org.yamcs.sle.State.ACTIVE) ? Status.OK : Status.UNAVAIL;
    }

    @Override
    public void acceptFrame(CcsdsTime ert, AntennaId antennaId, int dataLinkContinuity,
            FrameQuality frameQuality, byte[] privAnn, byte[] data) {
        if (isDisabled()) {
            log.debug("Ignoring frame received while disabled");
            return;
        }

        int length = data.length;

        if (log.isTraceEnabled()) {
            log.trace("Received frame length: {}, data: {}", data.length, StringConverter.arrayToHexString(data));
        }
        try {
            if (length < frameHandler.getMinFrameSize()) {
                eventProducer.sendWarning("Error processing frame: size " + length
                        + " shorter than minimum allowed " + frameHandler.getMinFrameSize());
            } else if (length > frameHandler.getMaxFrameSize()) {
                eventProducer.sendWarning("Error processing frame: size " + length + " longer than maximum allowed "
                        + frameHandler.getMaxFrameSize());
            } else {
                frameCount.incrementAndGet();
                Instant ertime = toInstant(ert);

                frameHandler.handleFrame(ertime, data, 0, length);
            }
        } catch (TcTmException e) {
            eventProducer.sendWarning("Error processing frame: " + e.toString());
        } catch (Exception e) {
            log.error("Error processing frame", e);
        }
    }

    @Override
    public void onExcessiveDataBacklog() {
        eventProducer.sendWarning("Excessive Data Backlog reported by the SLE provider");
    }

    @Override
    public void onProductionStatusChange(ProductionStatus productionStatusChange) {
        eventProducer.sendInfo("SLE production satus changed to " + productionStatusChange);

    }

    @Override
    public void onLossFrameSync(CcsdsTime time, LockStatus carrier, LockStatus subcarrier, LockStatus symbolSync) {
        // TODO make some parameters out of this
        eventProducer.sendInfo("SLE loss frame sync time: " + time + " carrier: " + carrier + " subcarrier: "
                + subcarrier + " symbolSync: " + symbolSync);
    }

    @Override
    public void setupSystemParameters(SystemParametersCollector sysParamCollector) {
        super.setupSystemParameters(sysParamCollector);
        if (sysParamCollector != null) {
            sp_sleState_id = sysParamCollector.getNamespace() + "/" + linkName + "/sleState";
            sp_rafStatus_id = sysParamCollector.getNamespace() + "/" + linkName + "/rafStatus";
        }
    }

    @Override
    protected void collectSystemParameters(long time, List list) {
        super.collectSystemParameters(time, list);
        list.add(SystemParametersCollector.getPV(sp_sleState_id, time, sleState.name()));
        if (rafStatus != null) {
            list.add(rafStatus);
            rafStatus = null;
        }
    }

    class MyMonitor implements RacfSleMonitor {

        private ProductionStatus prodStatus;
        private LockStatus subcarrierLockStatus;
        private LockStatus symbolSyncLockStatus;
        private LockStatus carrierLockStatus;

        @Override
        public void connected() {
            eventProducer.sendInfo("SLE connected");
        }

        @Override
        public void disconnected() {
            eventProducer.sendInfo("SLE disconnected");
            if (rsuh != null) {
                rsuh.shutdown();
                rsuh = null;
            }
        }

        @Override
        public void stateChanged(org.yamcs.sle.State newState) {
            eventProducer.sendInfo("SLE state changed to " + newState);
            sleState = newState;
        }

        @Override
        public void exceptionCaught(Throwable t) {
            log.warn("SLE exception caught", t);
            eventProducer.sendInfo("SLE exception caught: " + t.getMessage());
        }

        @Override
        public void onStatusReport(RacfStatusReport rafStatusReport) {
            prodStatus = rafStatusReport.getProductionStatus();
            carrierLockStatus = rafStatusReport.getCarrierLockStatus();
            subcarrierLockStatus = rafStatusReport.getSubcarrierLockStatus();
            symbolSyncLockStatus = rafStatusReport.getSymbolSyncLockStatus();

            AggregateValue tmp = new AggregateValue(rafStatusMembers);

            tmp.setMemberValue("productionStatus", ValueUtility.getStringValue(prodStatus.name()));
            tmp.setMemberValue("carrierLockStatus", ValueUtility.getStringValue(carrierLockStatus.name()));
            tmp.setMemberValue("subcarrierLockStatus", ValueUtility.getStringValue(subcarrierLockStatus.name()));
            tmp.setMemberValue("symbolSyncLockStatus", ValueUtility.getStringValue(symbolSyncLockStatus.name()));

            tmp.setMemberValue("deliveredFrameNumber",
                    ValueUtility.getSint32Value(rafStatusReport.getDeliveredFrameNumber()));
            tmp.setMemberValue("errorFreeFrameNumber",
                    ValueUtility.getSint32Value(rafStatusReport.getErrorFreeFrameNumber()));

            rafStatus = SystemParametersCollector.getPV(sp_rafStatus_id, getCurrentTime(), tmp);
        }
    }

    static Instant toInstant(CcsdsTime ccsdsTime) {
        long picosInDay = ccsdsTime.getPicosecInDay();

        long millis = (ccsdsTime.getNumDays() - CcsdsTime.NUM_DAYS_1958_1970) * (long) CcsdsTime.MS_IN_DAY
                + picosInDay / 1_000_000_000l;
        int picos = (int) (picosInDay % 1_000_000_000);
        return TimeEncoding.fromUnixPicos(millis, picos);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy