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

org.yamcs.sle.provider.CltuServiceProvider Maven / Gradle / Ivy

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

import java.io.IOException;
import java.io.InputStream;
import java.util.PriorityQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.yamcs.sle.CcsdsTime;
import org.yamcs.sle.State;
import org.yamcs.sle.provider.FrameSink.UplinkResult;

import com.beanit.jasn1.ber.BerTag;
import com.beanit.jasn1.ber.types.BerInteger;

import ccsds.sle.transfer.service.cltu.incoming.pdus.CltuGetParameterInvocation;
import ccsds.sle.transfer.service.cltu.incoming.pdus.CltuStartInvocation;
import ccsds.sle.transfer.service.cltu.incoming.pdus.CltuThrowEventInvocation;
import ccsds.sle.transfer.service.cltu.incoming.pdus.CltuTransferDataInvocation;
import ccsds.sle.transfer.service.cltu.outgoing.pdus.CltuAsyncNotifyInvocation;
import ccsds.sle.transfer.service.cltu.outgoing.pdus.CltuProviderToUserPdu;
import ccsds.sle.transfer.service.cltu.outgoing.pdus.CltuStartReturn;
import ccsds.sle.transfer.service.cltu.outgoing.pdus.CltuStatusReportInvocation;
import ccsds.sle.transfer.service.cltu.outgoing.pdus.CltuTransferDataReturn;
import ccsds.sle.transfer.service.cltu.outgoing.pdus.CltuTransferDataReturn.Result;
import ccsds.sle.transfer.service.cltu.structures.BufferSize;
import ccsds.sle.transfer.service.cltu.structures.CltuIdentification;
import ccsds.sle.transfer.service.cltu.structures.CltuLastOk;
import ccsds.sle.transfer.service.cltu.structures.CltuLastProcessed;
import ccsds.sle.transfer.service.cltu.structures.CltuLastProcessed.CltuProcessed;
import ccsds.sle.transfer.service.cltu.structures.CltuNotification;
import ccsds.sle.transfer.service.cltu.structures.CltuStatus;
import ccsds.sle.transfer.service.cltu.structures.DiagnosticCltuStart;
import ccsds.sle.transfer.service.cltu.structures.DiagnosticCltuTransferData;
import ccsds.sle.transfer.service.cltu.structures.NumberOfCltusProcessed;
import ccsds.sle.transfer.service.cltu.structures.NumberOfCltusRadiated;
import ccsds.sle.transfer.service.cltu.structures.NumberOfCltusReceived;
import ccsds.sle.transfer.service.cltu.structures.ProductionStatus;
import ccsds.sle.transfer.service.common.pdus.SleAcknowledgement;
import ccsds.sle.transfer.service.common.pdus.SleStopInvocation;
import ccsds.sle.transfer.service.common.types.Diagnostics;
import ccsds.sle.transfer.service.common.types.InvokeId;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;

import static org.yamcs.sle.Constants.*;

/**
 * Implementation for the
 * CCSDS RECOMMENDED STANDARD FOR SLE FCLTU SERVICE
 * CCSDS 912.1-B-4 August 2016
 * 

* https://public.ccsds.org/Pubs/912x1b4.pdf * *

* * This class implements queueing for CLTUs and it relies on a {@link FrameSink} to uplink the CLTUs one by one. * *

* Due to the java 8 limitations the uplink of the timed CLTUs (those with a defined earliestTransmissionTime) is not * very precise - with the current implementation it will be probably sent 1-2 milliseconds later than the time * specified. * If better precision is required, this has to be changed to send the CLTU a few milliseconds in * advance together with the time and the {@link FrameSink} should take care of the exact transmission start. *

* In any case the {@link FrameSink} has to return the time when the CLTU has been eventually radiated. * * @author nm * */ public class CltuServiceProvider implements SleService { private static final InternalLogger logger = InternalLoggerFactory.getInstance(CltuServiceProvider.class); FrameSink cltuUplinker; private final CltuServiceState cltuStatusReport = new CltuServiceState(); PriorityQueue timedCltus = new PriorityQueue(); BlockingQueue cltus = new LinkedBlockingQueue(); private long minimumDelayTimeMicrosec; int expectedCltuId; State state; final static QueuedCltu SIGNAL_TQC = new QueuedCltu(); SleProvider sleProvider; Thread queueRunner; int sleVersion; public CltuServiceProvider(FrameSink frameSink) { this.cltuUplinker = frameSink; } @Override public void init(SleProvider server) { this.sleProvider = server; this.sleVersion = server.getVersionNumber(); this.state = State.READY; } @Override public void processData(BerTag berTag, InputStream is) throws IOException { if (berTag.equals(BerTag.CONTEXT_CLASS, BerTag.CONSTRUCTED, 10)) { CltuTransferDataInvocation cltuTransferDataInvocation = new CltuTransferDataInvocation(); cltuTransferDataInvocation.decode(is, false); processTransferDataInvocation(cltuTransferDataInvocation); } else if (berTag.equals(BerTag.CONTEXT_CLASS, BerTag.CONSTRUCTED, 0)) { CltuStartInvocation cltuStartInvocation = new CltuStartInvocation(); cltuStartInvocation.decode(is, false); processStartInvocation(cltuStartInvocation); } else if (berTag.equals(BerTag.CONTEXT_CLASS, BerTag.CONSTRUCTED, 6)) { CltuGetParameterInvocation cltuGetParameterInvocation = new CltuGetParameterInvocation(); cltuGetParameterInvocation.decode(is, false); processGetParameterInvocation(cltuGetParameterInvocation); } else if (berTag.equals(BerTag.CONTEXT_CLASS, BerTag.CONSTRUCTED, 8)) { CltuThrowEventInvocation cltuThrowEventInvocation = new CltuThrowEventInvocation(); cltuThrowEventInvocation.decode(is, false); processThrowEventInvocation(cltuThrowEventInvocation); } else if (berTag.equals(BerTag.CONTEXT_CLASS, BerTag.CONSTRUCTED, 2)) { SleStopInvocation sleStopInvocation = new SleStopInvocation(); sleStopInvocation.decode(is, false); processSleStopInvocation(sleStopInvocation); } else { logger.warn("Unexpected berTag: {} ", berTag); throw new IllegalStateException("Unexpected berTag: " + berTag); } } private void processStartInvocation(CltuStartInvocation cltuStartInvocation) { logger.debug("Received CltuStartInvocation {}", cltuStartInvocation); if (state != State.READY) { logger.warn("wrong state {} for start invocation", state); sendNegativeStartReturn(cltuStartInvocation.getInvokeId(), 1); return; } int x = cltuUplinker.start(this); if (x > 0) { logger.warn("Cltu uplinker returned error {}", x); sendNegativeStartReturn(cltuStartInvocation.getInvokeId(), 1); return; } state = State.ACTIVE; cltuStatusReport.prodStatus = CltuProductionStatus.operational; CltuStartReturn.Result.PositiveResult pr = new CltuStartReturn.Result.PositiveResult(); pr.setStartRadiationTime(CcsdsTime.toSle(CcsdsTime.now(), sleVersion)); pr.setStopRadiationTime(COND_TIME_UNDEFINED); CltuStartReturn.Result r = new CltuStartReturn.Result(); r.setPositiveResult(pr); expectedCltuId = cltuStartInvocation.getFirstCltuIdentification().intValue(); queueRunner = new Thread(() -> runUplinkQueue()); queueRunner.start(); CltuStartReturn csr = new CltuStartReturn(); csr.setResult(r); csr.setInvokeId(cltuStartInvocation.getInvokeId()); csr.setPerformerCredentials(sleProvider.getNonBindCredentials()); logger.debug("Sending CltuStartReturn {}", csr); CltuProviderToUserPdu cptu = new CltuProviderToUserPdu(); cptu.setCltuStartReturn(csr); sleProvider.sendMessage(cptu); } private void sendNegativeStartReturn(InvokeId invokeId, int diagnostic) { DiagnosticCltuStart dcs = new DiagnosticCltuStart(); dcs.setSpecific(new BerInteger(diagnostic)); CltuStartReturn.Result r = new CltuStartReturn.Result(); r.setNegativeResult(dcs); CltuStartReturn csr = new CltuStartReturn(); csr.setResult(r); csr.setInvokeId(invokeId); csr.setPerformerCredentials(sleProvider.getNonBindCredentials()); CltuProviderToUserPdu cptu = new CltuProviderToUserPdu(); cptu.setCltuStartReturn(csr); sleProvider.sendMessage(cptu); } private void processTransferDataInvocation(CltuTransferDataInvocation cltuTransferDataInvocation) { logger.debug("Received CltuTransferDataInvocation {}", cltuTransferDataInvocation); sleProvider.verifyNonBindCredentials(cltuTransferDataInvocation.getInvokerCredentials()); CcsdsTime ett = CcsdsTime.fromSle(cltuTransferDataInvocation.getEarliestTransmissionTime()); QueuedCltu qc = (ett == null) ? new QueuedCltu() : new TimedQueuedCltu(ett); CcsdsTime now = CcsdsTime.now(); cltuStatusReport.numCltuReceived++; int result = -1; qc.cltuId = cltuTransferDataInvocation.getCltuIdentification().intValue(); qc.latestTransmissionTime = CcsdsTime.fromSle(cltuTransferDataInvocation.getLatestTransmissionTime()); qc.delayTimeMicrosec = cltuTransferDataInvocation.getDelayTime().longValue(); qc.report = cltuTransferDataInvocation.getSlduRadiationNotification().intValue() == 1; qc.cltuData = cltuTransferDataInvocation.getCltuData().value; qc.queued = now; if (qc.latestTransmissionTime != null && qc.latestTransmissionTime.compareTo(now) < 0) { result = 5;// late sdlu } else if (ett != null && qc.latestTransmissionTime != null && ett.compareTo(qc.latestTransmissionTime) > 0) { result = 3; // inconsistentTimeRange } else if (qc.delayTimeMicrosec < minimumDelayTimeMicrosec) { result = 6;// invalid delay time } else if (qc.cltuId != expectedCltuId) { result = 2; // out of seq } else if (qc.cltuData.length > cltuStatusReport.cltuBufferAvailable) { result = 1; // unable to store } CltuTransferDataReturn ctdr = new CltuTransferDataReturn(); ctdr.setPerformerCredentials(sleProvider.getNonBindCredentials()); ctdr.setInvokeId(cltuTransferDataInvocation.getInvokeId()); ctdr.setCltuBufferAvailable(new BufferSize(cltuStatusReport.cltuBufferAvailable)); Result r = new Result(); if (result < 0) {// ok r.setPositiveResult(BER_NULL); if (qc instanceof TimedQueuedCltu) { timedCltus.add((TimedQueuedCltu) qc); cltus.add(SIGNAL_TQC); // to wake up the thread } else { cltus.add(qc); } expectedCltuId++; } else { DiagnosticCltuTransferData d = new DiagnosticCltuTransferData(); d.setSpecific(new BerInteger(result)); r.setNegativeResult(d); } ctdr.setCltuIdentification(new CltuIdentification(expectedCltuId)); ctdr.setResult(r); logger.debug("Sending CltuTransferDataReturn {}", ctdr); CltuProviderToUserPdu cptu = new CltuProviderToUserPdu(); cptu.setCltuTransferDataReturn(ctdr); sleProvider.sendMessage(cptu); } protected void processSleStopInvocation(SleStopInvocation sleStopInvocation) { logger.debug("Received SleStopInvocation {}", sleStopInvocation); SleAcknowledgement ack = new SleAcknowledgement(); ack.setCredentials(sleProvider.getNonBindCredentials()); ack.setInvokeId(sleStopInvocation.getInvokeId()); SleAcknowledgement.Result result = new SleAcknowledgement.Result(); if (state == State.ACTIVE) { queueRunner.interrupt(); state = State.READY; cltuStatusReport.prodStatus = CltuProductionStatus.configured; int x = cltuUplinker.stop(this); if (x > 0) { logger.warn("Cltu uplinker returned error {}", x); result.setNegativeResult(new Diagnostics(x)); } else { result.setPositiveResult(BER_NULL); } } else { logger.warn("received stop while in state {}", state); result.setNegativeResult(new Diagnostics(127));// other reason } ack.setResult(result); CltuProviderToUserPdu cptu = new CltuProviderToUserPdu(); cptu.setCltuStopReturn(ack); sleProvider.sendMessage(cptu); } private void processThrowEventInvocation(CltuThrowEventInvocation cltuThrowEventInvocation) { logger.debug("Received CltuThrowEventInvocation {}", cltuThrowEventInvocation); } private void processGetParameterInvocation(CltuGetParameterInvocation cltuGetParameterInvocation) { logger.debug("Received CltuGetParameterInvocation {}", cltuGetParameterInvocation); } /** * Called when a CLTU has been radiated * * @param cltuId * @param startTime * @param stopTime * - can be null of the cltu has not been successfully radiated * @param cltuStatus */ private void cltuRadiated(QueuedCltu qc, UplinkResult ur) { cltuStatusReport.cltuLastProcessedId = qc.cltuId; cltuStatusReport.cltuLastProcessedRadiationTime = ur.startTime; cltuStatusReport.cltuLastProcesseStatus = ur.cltuStatus; cltuStatusReport.numCltuProcessed++; if (ur.cltuStatus == ForwardDuStatus.radiated) { cltuStatusReport.cltuLastOkId = qc.cltuId; cltuStatusReport.cltuLastOkTime = ur.stopTime; cltuStatusReport.numCltuRadiated++; } cltuStatusReport.cltuBufferAvailable += qc.cltuData.length; CltuNotification cn = new CltuNotification(); cn.setCltuRadiated(BER_NULL); sendCltuAsyncNotify(cn); } private void sendCltuAsyncNotify(CltuNotification cltuNotification) { CltuAsyncNotifyInvocation cani = new CltuAsyncNotifyInvocation(); // invoker-credentials cani.setInvokerCredentials(sleProvider.getNonBindCredentials()); // notification-type cani.setCltuNotification(cltuNotification); // cltu-last-processed cani.setCltuLastProcessed(getCltuLastProcessed(cltuStatusReport.cltuLastProcessedId, cltuStatusReport.cltuLastProcessedRadiationTime, cltuStatusReport.cltuLastProcesseStatus)); cani.setCltuLastOk(getCltuLastOk(cltuStatusReport.cltuLastOkId, cltuStatusReport.cltuLastOkTime)); // production-status cani.setProductionStatus(new ProductionStatus(cltuStatusReport.prodStatus.getId())); // uplink-status cani.setUplinkStatus( new ccsds.sle.transfer.service.cltu.structures.UplinkStatus(cltuStatusReport.uplinkStatus.getId())); logger.debug("Sending CltuAsyncNotifyInvocation {}", cani); CltuProviderToUserPdu cptu = new CltuProviderToUserPdu(); cptu.setCltuAsyncNotifyInvocation(cani); sleProvider.sendMessage(cptu); } @Override public void sendStatusReport() { CltuStatusReportInvocation csri = new CltuStatusReportInvocation(); // invoker-credentials csri.setInvokerCredentials(sleProvider.getNonBindCredentials()); // cltu-last-processed csri.setCltuLastProcessed(getCltuLastProcessed(cltuStatusReport.cltuLastProcessedId, cltuStatusReport.cltuLastProcessedRadiationTime, cltuStatusReport.cltuLastProcesseStatus)); // cltu-last-OK CltuLastOk cltuLastOk = getCltuLastOk(cltuStatusReport.cltuLastOkId, cltuStatusReport.cltuLastOkTime); csri.setCltuLastOk(cltuLastOk); // production-status csri.setCltuProductionStatus(new ProductionStatus(cltuStatusReport.prodStatus.getId())); // uplink-status csri.setUplinkStatus( new ccsds.sle.transfer.service.cltu.structures.UplinkStatus(cltuStatusReport.uplinkStatus.getId())); csri.setNumberOfCltusReceived(new NumberOfCltusReceived(cltuStatusReport.numCltuReceived)); csri.setNumberOfCltusProcessed(new NumberOfCltusProcessed(cltuStatusReport.numCltuProcessed)); csri.setNumberOfCltusRadiated(new NumberOfCltusRadiated(cltuStatusReport.numCltuRadiated)); // cltu-buffer-available csri.setCltuBufferAvailable(new BufferSize(cltuStatusReport.cltuBufferAvailable)); logger.debug("Sending CltuStatusReportInvocation {}", csri); CltuProviderToUserPdu cptu = new CltuProviderToUserPdu(); cptu.setCltuStatusReportInvocation(csri); sleProvider.sendMessage(cptu); } CltuLastProcessed getCltuLastProcessed(int cltuId, CcsdsTime time, ForwardDuStatus cltuStatus) { CltuLastProcessed cltuLastProcessed = new CltuLastProcessed(); if (cltuId == -1) { cltuLastProcessed.setNoCltuProcessed(BER_NULL); } else { CltuProcessed cltuProcessed = new CltuProcessed(); cltuProcessed.setCltuIdentification(new CltuIdentification(cltuId)); cltuProcessed.setRadiationStartTime(CcsdsTime.toSleConditional(time, sleVersion)); cltuProcessed.setCltuStatus(new CltuStatus(cltuStatus.getId())); cltuLastProcessed.setCltuProcessed(cltuProcessed); } return cltuLastProcessed; } CltuLastOk getCltuLastOk(int cltuId, CcsdsTime time) { CltuLastOk cltuLastOk = new CltuLastOk(); if (cltuId == -1) { cltuLastOk.setNoCltuOk(BER_NULL); } else { CltuLastOk.CltuOk cltuOk = new CltuLastOk.CltuOk(); cltuOk.setCltuIdentification(new CltuIdentification(cltuId)); cltuOk.setRadiationStopTime(CcsdsTime.toSle(time, sleVersion)); cltuLastOk.setCltuOk(cltuOk); } return cltuLastOk; } @Override public void abort() { if (queueRunner != null) { queueRunner.interrupt(); } } @Override public void unbind() { // nothing to do } /* * This runs in a separated thread */ private void runUplinkQueue() { while (state == State.ACTIVE) { CcsdsTime now = CcsdsTime.now(); long timeToSleep = Long.MAX_VALUE; TimedQueuedCltu tqc = timedCltus.poll(); if (tqc != null) { timeToSleep = delta(tqc.earliestTransmissionTime, now); if (timeToSleep < 0) { uplinkCltu(tqc); continue; } } QueuedCltu qc; try { qc = cltus.poll(timeToSleep, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { return; } if (qc == null) { uplinkCltu(tqc); } else if (qc == SIGNAL_TQC) {// this means that a new Timed CLTU has been put in the TimedQueuedCltu timedCltus.add(tqc); } else { uplinkCltu(qc); } } } // returns t1-t2 in milliseconds private long delta(CcsdsTime t1, CcsdsTime t2) { return CcsdsTime.MS_IN_DAY * (t1.getNumDays() - t2.getNumDays()) + (t1.getPicosecInDay() - t2.getPicosecInDay()) / 1000_000_000; } private void uplinkCltu(QueuedCltu qc) { UplinkResult ur = cltuUplinker.uplink(qc.cltuData); cltuRadiated(qc, ur); } static class CltuServiceState { int cltuLastProcessedId = -1; CcsdsTime cltuLastProcessedRadiationTime; ForwardDuStatus cltuLastProcesseStatus; int cltuLastOkId = -1; CcsdsTime cltuLastOkTime; CltuProductionStatus prodStatus = CltuProductionStatus.configured; UplinkStatus uplinkStatus = UplinkStatus.uplinkStatusNotAvailable; int numCltuReceived; int numCltuProcessed; int numCltuRadiated; int cltuBufferAvailable = 1024 * 1024; } static class QueuedCltu { int cltuId; CcsdsTime queued; CcsdsTime latestTransmissionTime; long delayTimeMicrosec; boolean report; byte[] cltuData; } static class TimedQueuedCltu extends QueuedCltu implements Comparable { CcsdsTime earliestTransmissionTime; TimedQueuedCltu(CcsdsTime earliestTransmissionTime) { this.earliestTransmissionTime = earliestTransmissionTime; } @Override public int compareTo(TimedQueuedCltu o) { return this.earliestTransmissionTime.compareTo(o.earliestTransmissionTime); } } @Override public State getState() { return state; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy