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

org.yamcs.Processor Maven / Gradle / Ivy

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

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.stream.Collectors;

import org.yamcs.alarms.EventAlarmServer;
import org.yamcs.cmdhistory.CommandHistoryProvider;
import org.yamcs.cmdhistory.CommandHistoryPublisher;
import org.yamcs.cmdhistory.CommandHistoryRequestManager;
import org.yamcs.cmdhistory.StreamCommandHistoryProvider;
import org.yamcs.cmdhistory.StreamCommandHistoryPublisher;
import org.yamcs.commanding.Acknowledgment;
import org.yamcs.commanding.CommandReleaser;
import org.yamcs.commanding.CommandingManager;
import org.yamcs.container.ContainerRequestManager;
import org.yamcs.logging.Log;
import org.yamcs.mdb.ProcessorData;
import org.yamcs.mdb.Mdb;
import org.yamcs.mdb.MdbFactory;
import org.yamcs.mdb.XtceTmProcessor;
import org.yamcs.parameter.LastValueCache;
import org.yamcs.parameter.ParameterCache;
import org.yamcs.parameter.ParameterCacheConfig;
import org.yamcs.parameter.ParameterPersistence;
import org.yamcs.parameter.ParameterProcessorManager;
import org.yamcs.parameter.ParameterRequestManager;
import org.yamcs.parameter.ParameterValue;
import org.yamcs.protobuf.ServiceState;
import org.yamcs.protobuf.Yamcs.EndAction;
import org.yamcs.protobuf.Yamcs.ReplayRequest;
import org.yamcs.protobuf.Yamcs.ReplaySpeed;
import org.yamcs.protobuf.Yamcs.ReplaySpeed.ReplaySpeedType;
import org.yamcs.protobuf.Yamcs.ReplayStatus.ReplayState;
import org.yamcs.tctm.ArchiveTmPacketProvider;
import org.yamcs.tctm.StreamParameterSender;
import org.yamcs.time.TimeService;
import org.yamcs.xtce.Parameter;
import org.yamcs.yarch.Stream;
import org.yamcs.yarch.YarchDatabase;
import org.yamcs.yarch.YarchDatabaseInstance;

import com.google.common.util.concurrent.AbstractService;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Service;
import com.google.common.util.concurrent.ThreadFactoryBuilder;

/**
 * 
 * This class helps keeping track of the different objects used in a Yamcs Processor - i.e. all the objects required to
 * have a TM/TC processing chain (either realtime or playback).
 *
 */
public class Processor extends AbstractService {
    public static final String PROC_PARAMETERS_STREAM = "proc_param";

    // runs algorithms and distributes parameters
    private ParameterProcessorManager parameterProcessorManager;

    // handles subscriptions to containers
    private ContainerRequestManager containerRequestManager;
    // handles subscriptions to command history
    private CommandHistoryRequestManager commandHistoryRequestManager;

    // handles command building and queues
    private CommandingManager commandingManager;

    // publishes events to command history
    private CommandHistoryPublisher commandHistoryPublisher;

    // these are services defined in the processor.yaml.
    // They have to register themselves to the processor in their init method
    private TmPacketProvider tmPacketProvider;
    private CommandHistoryProvider commandHistoryProvider;
    private CommandReleaser commandReleaser;

    private Mdb mdb;

    private final String name;
    private final String type;
    private final String yamcsInstance;

    private ProcessorConfig config;

    private String creator = "system";
    private boolean persistent = false;
    private boolean protected_ = false;

    final Log log;
    static Set listeners = new CopyOnWriteArraySet<>(); // send notifications for added and removed
    // processors to this

    private boolean quitting;
    // a synchronous processor waits for all the clients to deliver tm packets and parameters
    private boolean synchronous = false;

    XtceTmProcessor tmProcessor;

    private final ScheduledThreadPoolExecutor timer;
    TimeService timeService;

    ProcessorData processorData;
    List serviceList;
    StreamParameterSender streamParameterSender;
    EventAlarmServer eventAlarmServer;
    YamcsServerInstance ysi;

    // Globally available acknowledgments (in addition to Q, R, S)
    private Set acknowledgments = new CopyOnWriteArraySet<>();

    private ParameterPersistence paramPersistence;

    public Processor(String yamcsInstance, String name, String type, String creator) throws ProcessorException {
        if ((name == null) || "".equals(name)) {
            throw new ProcessorException("The processor name must not be empty");
        }
        this.yamcsInstance = yamcsInstance;
        this.name = name;
        this.creator = creator;
        this.type = type;
        log = new Log(Processor.class, yamcsInstance);
        log.info("Creating new processor '{}' of type '{}'", name, type);
        log.setContext(name);
        timer = new ScheduledThreadPoolExecutor(1,
                new ThreadFactoryBuilder().setNameFormat("Processor-" + yamcsInstance + "." + name).build());
    }

    /**
     * If recording to the archive initial values and local parameters is enabled, this class can be used to do it.
     * 
     * Otherwise it will return null.
     * 
     * @return the stream parameter sender that can be used to send data on the {@link #PROC_PARAMETERS_STREAM} stream
     *         to be recorded in the archive
     */
    public StreamParameterSender getStreamParameterSender() {
        return streamParameterSender;
    }

    /**
     * 
     * @param serviceList
     * @param config
     *            - configuration from processor.yaml
     * @param spec
     *            - configuration passed from the client when creating the processor
     * @throws ProcessorException
     * @throws ValidationException
     * @throws ConfigurationException
     */
    void init(List serviceList, ProcessorConfig config, Object spec)
            throws ProcessorException, InitException, ValidationException {
        log.debug("Initialzing the processor with the configuration {}", config);

        mdb = MdbFactory.getInstance(yamcsInstance);

        this.config = config;

        this.serviceList = serviceList;

        timeService = YamcsServer.getTimeService(yamcsInstance);

        Map persistedParams = new HashMap<>();

        if (config.persistParameters) {
            paramPersistence = new ParameterPersistence(yamcsInstance, name);
            var it = paramPersistence.load();

            if (it != null) {
                while (it.hasNext()) {
                    var pv = it.next();
                    var p = mdb.getParameter(pv.getParameterQualifiedName());
                    if (p != null) {
                        if (p.isPersistent()) {
                            pv.setParameter(p);
                            persistedParams.put(p, pv);
                        } else {
                            log.debug("Found persisted parameter without the persistance flag set {}",
                                    pv.getParameterQualifiedName());
                        }
                    } else {
                        log.debug("No parameter found in the MDB for persisted parameter value {}",
                                pv.getParameterQualifiedName());
                    }
                }
            }
        }

        processorData = new ProcessorData(this, config, persistedParams);

        YarchDatabaseInstance ydb = YarchDatabase.getInstance(yamcsInstance);

        Stream pps = ydb.getStream(PROC_PARAMETERS_STREAM);
        if (pps != null) {
            streamParameterSender = new StreamParameterSender(yamcsInstance, pps);
        }
        if (config.recordInitialValues || config.recordLocalValues) {
            if (pps == null) {
                throw new ConfigurationException("recordInitialValues is set to true but the stream '"
                        + PROC_PARAMETERS_STREAM + "' does not exist");
            }
            streamParameterSender.sendParameters(processorData.getLastValueCache().getValues());
        }
        if (config.eventAlarmServerEnabled) {
            eventAlarmServer = new EventAlarmServer(yamcsInstance, config, timer);
        }
        // Shared between prm and crm
        tmProcessor = new XtceTmProcessor(this);
        containerRequestManager = new ContainerRequestManager(this, tmProcessor);
        parameterProcessorManager = new ParameterProcessorManager(this, tmProcessor);

        for (ProcessorServiceWithConfig swc : serviceList) {
            if (swc.service instanceof CommandHistoryPublisher) {
                commandHistoryPublisher = (CommandHistoryPublisher) swc.service;
            }

            if (swc.service instanceof CommandHistoryProvider) {
                setCommandHistoryProvider((CommandHistoryProvider) swc.service);
            }
            if (swc.service instanceof CommandReleaser) {
                this.commandReleaser = (CommandReleaser) swc.service;
            }
        }
        if (commandReleaser != null) {
            if (commandHistoryPublisher == null) {
                commandHistoryPublisher = new StreamCommandHistoryPublisher(yamcsInstance);
            }
            if (commandHistoryProvider == null) {
                setCommandHistoryProvider(new StreamCommandHistoryProvider(yamcsInstance));
            }

            commandingManager = new CommandingManager(this);
            commandReleaser.setCommandHistory(commandHistoryPublisher);
        }
        for (ProcessorServiceWithConfig swc : serviceList) {
            ProcessorService service = (ProcessorService) swc.service;
            service.init(this, swc.getConfig(), spec);
        }

        parameterProcessorManager.init();

        listeners.forEach(l -> l.processorAdded(this));
    }

    public void setPacketProvider(TmPacketProvider tpp) {
        if (tmPacketProvider != null) {
            throw new IllegalStateException("There is already a packet provider");
        }
        tmPacketProvider = tpp;
    }

    public void setCommandHistoryProvider(CommandHistoryProvider chp) {
        if (commandHistoryProvider != null) {
            throw new IllegalStateException("There is already a command history provider");
        }
        commandHistoryProvider = chp;
        commandHistoryRequestManager = new CommandHistoryRequestManager(this);
        commandHistoryProvider.setCommandHistoryRequestManager(commandHistoryRequestManager);
    }

    public CommandHistoryPublisher getCommandHistoryPublisher() {
        return commandHistoryPublisher;
    }

    public ParameterProcessorManager getParameterProcessorManager() {
        return parameterProcessorManager;
    }

    public ContainerRequestManager getContainerRequestManager() {
        return containerRequestManager;
    }

    public XtceTmProcessor getTmProcessor() {
        return tmProcessor;
    }

    /**
     * starts processing by invoking the start method for all the associated components
     *
     */
    @Override
    public void doStart() {
        try {
            tmProcessor.startAsync();
            tmProcessor.addListener(new Listener() {
                @Override
                public void terminated(State from) {
                    stopAsync();
                }
            }, MoreExecutors.directExecutor());
            startIfNecessary(commandHistoryRequestManager);
            startIfNecessary(commandHistoryProvider);
            startIfNecessary(parameterProcessorManager);
            startIfNecessary(tmPacketProvider);
            startIfNecessary(commandingManager);
            startIfNecessary(eventAlarmServer);

            for (ProcessorServiceWithConfig swc : serviceList) {
                startIfNecessary(swc.service);
            }

            tmProcessor.awaitRunning();

            awaitIfNecessary(commandHistoryRequestManager);
            awaitIfNecessary(commandHistoryProvider);
            awaitIfNecessary(parameterProcessorManager);
            awaitIfNecessary(tmPacketProvider);
            awaitIfNecessary(commandingManager);
            awaitIfNecessary(eventAlarmServer);

            for (ProcessorServiceWithConfig swc : serviceList) {
                swc.service.awaitRunning();
            }

            notifyStarted();
        } catch (Exception e) {
            notifyFailed(e);
        }
        propagateProcessorStateChange();
    }

    public List getServices() {
        return serviceList.stream().collect(Collectors.toList());
    }

    private void startIfNecessary(Service service) {
        if (service != null) {
            if (service.state() == State.NEW) {
                service.startAsync();
            }
        }
    }

    void setYamcsServerInstance(YamcsServerInstance ysi) {
        this.ysi = ysi;
    }

    private void awaitIfNecessary(Service service) {
        if (service != null) {
            service.awaitRunning();
        }
    }

    public void pause() {
        ((ArchiveTmPacketProvider) tmPacketProvider).pause();
        propagateProcessorStateChange();
    }

    public void resume() {
        ArchiveTmPacketProvider provider = (ArchiveTmPacketProvider) tmPacketProvider;
        provider.resume();
        if (provider.getSpeed() != null
                && provider.getSpeed().getType() != ReplaySpeedType.STEP_BY_STEP) {
            propagateProcessorStateChange();
        }
    }

    private void propagateProcessorStateChange() {
        listeners.forEach(l -> l.processorStateChanged(this));
    }

    public void seek(long instant) {
        seek(instant, true);
    }

    public void seek(long instant, boolean autostart) {
        getTmProcessor().resetStatistics();
        ((ArchiveTmPacketProvider) tmPacketProvider).seek(instant, autostart);
        propagateProcessorStateChange();
    }

    public void changeSpeed(ReplaySpeed speed) {
        ((ArchiveTmPacketProvider) tmPacketProvider).changeSpeed(speed);
        propagateProcessorStateChange();
    }

    public void changeRange(long start, long stop) {
        ((ArchiveTmPacketProvider) tmPacketProvider).changeRange(start, stop);
        ((ArchiveTmPacketProvider) tmPacketProvider).seek(start, false);
        propagateProcessorStateChange();
    }

    public void changeEndAction(EndAction endAction) {
        ((ArchiveTmPacketProvider) tmPacketProvider).changeEndAction(endAction);
        propagateProcessorStateChange();
    }

    /**
     * @return the tcUplinker
     */
    public CommandReleaser getCommandReleaser() {
        return commandReleaser;
    }

    /**
     * @return the tmPacketProvider
     */
    public TmPacketProvider getTmPacketProvider() {
        return tmPacketProvider;
    }

    public String getName() {
        return name;
    }

    /**
     * @return the type
     */
    public String getType() {
        return type;
    }

    public String getCreator() {
        return creator;
    }

    public void setCreator(String creator) {
        this.creator = creator;
    }

    /**
     * Returns globally available acknowledgments (in addition to Acknowledge_Queued, Acknowledge_Released and
     * Acknowledge_Sent).
     */
    public Collection getAcknowledgments() {
        return acknowledgments;
    }

    /**
     * Add a globally available acknowledgment (in addition to Acknowledge_Queued, Acknowledge_Released and
     * Acknowledge_Sent).
     */
    public void addAcknowledgment(Acknowledgment acknowledgment) {
        acknowledgments.add(acknowledgment);
    }

    /**
     * Closes the processor by stoping the tm/pp and tc It can be that there are still clients connected, but they will
     * not get any data and new clients can not connect to these processors anymore. Once it is closed, you can create a
     * processor with the same name which will make it maybe a bit confusing :(
     *
     */
    @Override
    public void doStop() {
        if (quitting) {
            return;
        }

        log.info("Processor {} quitting", name);
        quitting = true;
        timer.shutdown();
        // first send a STOPPING event
        listeners.forEach(l -> l.processorStateChanged(this));

        for (ProcessorServiceWithConfig swc : serviceList) {
            swc.service.stopAsync();
        }

        if (commandReleaser != null) {
            commandReleaser.stopAsync();
            commandingManager.stopAsync();
        }
        if (tmProcessor != null) {
            tmProcessor.stopAsync();
        }
        if (eventAlarmServer != null) {
            eventAlarmServer.stopAsync();
        }
        log.info("Processor {} is out of business", name);

        if (ysi != null) {
            ysi.removeProcessor(name);
        }

        if (paramPersistence != null) {
            paramPersistence.save(processorData.getValuesToBePersisted().iterator());
        }

        if (getState() == ServiceState.RUNNING || getState() == ServiceState.STOPPING) {
            notifyStopped();
        }
        // and now a CLOSED event
        listeners.forEach(l -> l.processorClosed(this));
    }

    public static void addProcessorListener(ProcessorListener processorListener) {
        listeners.add(processorListener);
    }

    public static void removeProcessorListener(ProcessorListener processorListener) {
        listeners.remove(processorListener);
    }

    public boolean isPersistent() {
        return persistent;
    }

    public void setPersistent(boolean systemSession) {
        this.persistent = systemSession;
    }

    /**
     * Returns if this processor is protected. A protected processor may not be deleted.
     */
    public boolean isProtected() {
        return protected_;
    }

    public void setProtected(boolean protected_) {
        this.protected_ = protected_;
    }

    public boolean isSynchronous() {
        return synchronous;
    }

    public boolean hasCommanding() {
        return commandingManager != null;
    }

    public void setSynchronous(boolean synchronous) {
        this.synchronous = synchronous;
    }

    public boolean isReplay() {
        if (tmPacketProvider == null) {
            return false;
        }

        return tmPacketProvider.isArchiveReplay();
    }

    /**
     * valid only if isArchiveReplay returns true
     * 
     * @return
     */
    public ReplayRequest getReplayRequest() {
        return ((ArchiveTmPacketProvider) tmPacketProvider).getReplayRequest();
    }

    /**
     * valid only if isArchiveReplay returns true
     * 
     * @return
     */
    public ReplayState getReplayState() {
        return ((ArchiveTmPacketProvider) tmPacketProvider).getReplayState();
    }

    public ReplayRequest getCurrentReplayRequest() {
        return ((ArchiveTmPacketProvider) tmPacketProvider).getCurrentReplayRequest();
    }

    public ServiceState getState() {
        return ServiceState.valueOf(state().name());
    }

    public CommandingManager getCommandingManager() {
        return commandingManager;
    }

    /**
     *
     * @return the yamcs instance this processor is part of
     */
    public String getInstance() {
        return yamcsInstance;
    }

    public Mdb getMdb() {
        return mdb;
    }

    public CommandHistoryRequestManager getCommandHistoryManager() {
        return commandHistoryRequestManager;
    }

    public boolean hasAlarmChecker() {
        return config.checkParameterAlarms;
    }

    public boolean hasAlarmServer() {
        return config.parameterAlarmServerEnabled;
    }

    public ScheduledThreadPoolExecutor getTimer() {
        return timer;
    }

    /**
     * Returns the processor time
     * 
     * for realtime processors it is the mission time (could be simulated) for replay processors it is the replay time
     * 
     * @return
     */
    public long getCurrentTime() {
        if (isReplay()) {
            return ((ArchiveTmPacketProvider) tmPacketProvider).getReplayTime();
        } else {
            return timeService.getMissionTime();
        }
    }

    public void quit() {
        stopAsync();
        awaitTerminated();
    }

    public void start() {
        startAsync();
        awaitRunning();
    }

    public void notifyStateChange() {
        propagateProcessorStateChange();
    }

    public ParameterCacheConfig getPameterCacheConfig() {
        return config.parameterCacheConfig;
    }

    public ParameterCache getParameterCache() {
        return parameterProcessorManager.getParameterCache();
    }

    /**
     * Returns the processor data used to store processor specific calibration, alarms
     * 
     * @return processor specific data
     */
    public ProcessorData getProcessorData() {
        return processorData;
    }

    public LastValueCache getLastValueCache() {
        return processorData.getLastValueCache();
    }

    public boolean isSubscribeAll() {
        return config.subscribeAll;
    }

    @SuppressWarnings("unchecked")
    public  List getServices(Class serviceClass) {
        List services = new ArrayList<>();
        if (serviceList != null) {
            for (ProcessorServiceWithConfig swc : serviceList) {
                if (swc.getServiceClass().equals(serviceClass.getName())) {
                    services.add((T) swc.service);
                }
            }
        }
        return services;
    }

    public boolean recordLocalValues() {
        return config.recordLocalValues;
    }

    public EventAlarmServer getEventAlarmServer() {
        return eventAlarmServer;
    }

    public ProcessorConfig getConfig() {
        return config;
    }

    public ParameterRequestManager getParameterRequestManager() {
        return parameterProcessorManager.getParameterRequestManager();
    }

    @Override
    public String toString() {
        return "name: " + name + " type: " + type;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy