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

esa.mo.nmf.groundmoproxy.GroundMOProxy Maven / Gradle / Ivy

/* ----------------------------------------------------------------------------
 * Copyright (C) 2015      European Space Agency
 *                         European Space Operations Centre
 *                         Darmstadt
 *                         Germany
 * ----------------------------------------------------------------------------
 * System                : ESA NanoSat MO Framework
 * ----------------------------------------------------------------------------
 * Licensed under the European Space Agency Public License, Version 2.0
 * You may not use this file except in compliance with the License.
 *
 * Except as expressly set forth in this License, the Software is provided to
 * You on an "as is" basis and without warranties of any kind, including without
 * limitation merchantability, fitness for a particular purpose, absence of
 * defects or errors, accuracy or non-infringement of intellectual property rights.
 * 
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 * ----------------------------------------------------------------------------
 */
package esa.mo.nmf.groundmoproxy;

import esa.mo.com.impl.consumer.ArchiveConsumerServiceImpl;
import esa.mo.com.impl.util.COMServicesProvider;
import esa.mo.common.impl.proxy.DirectoryProxyServiceImpl;
import esa.mo.helpertools.connections.ConnectionConsumer;
import esa.mo.helpertools.connections.ConnectionProvider;
import esa.mo.helpertools.connections.SingleConnectionDetails;
import esa.mo.helpertools.helpers.HelperMisc;
import esa.mo.helpertools.helpers.HelperTime;
import esa.mo.helpertools.misc.HelperNMF;
import esa.mo.nmf.NMFConsumer;
import esa.mo.sm.impl.consumer.HeartbeatConsumerServiceImpl;
import esa.mo.sm.impl.provider.AppsLauncherManager;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.ccsds.moims.mo.com.COMService;
import org.ccsds.moims.mo.com.archive.ArchiveHelper;
import org.ccsds.moims.mo.com.archive.consumer.ArchiveAdapter;
import org.ccsds.moims.mo.com.archive.structures.ArchiveQuery;
import org.ccsds.moims.mo.com.archive.structures.ArchiveQueryList;
import org.ccsds.moims.mo.common.directory.DirectoryHelper;
import org.ccsds.moims.mo.common.directory.structures.ProviderSummaryList;
import org.ccsds.moims.mo.common.directory.structures.ServiceFilter;
import org.ccsds.moims.mo.common.structures.ServiceKey;
import org.ccsds.moims.mo.mal.MALException;
import org.ccsds.moims.mo.mal.MALInteractionException;
import org.ccsds.moims.mo.mal.structures.FineTime;
import org.ccsds.moims.mo.mal.structures.Identifier;
import org.ccsds.moims.mo.mal.structures.IdentifierList;
import org.ccsds.moims.mo.mal.structures.LongList;
import org.ccsds.moims.mo.mal.structures.Subscription;
import org.ccsds.moims.mo.mal.structures.Time;
import org.ccsds.moims.mo.mal.structures.UIntegerList;
import org.ccsds.moims.mo.mal.structures.UOctet;
import org.ccsds.moims.mo.mal.structures.URI;
import org.ccsds.moims.mo.mal.structures.UShort;
import org.ccsds.moims.mo.mal.transport.MALMessageHeader;
import org.ccsds.moims.mo.softwaremanagement.appslauncher.AppsLauncherHelper;
import org.ccsds.moims.mo.softwaremanagement.heartbeat.HeartbeatHelper;
import org.ccsds.moims.mo.softwaremanagement.heartbeat.consumer.HeartbeatAdapter;

/**
 * The Ground MO Proxy class.
 *
 * @author Cesar Coelho
 */
public abstract class GroundMOProxy {

    private final static long PERIOD = 10000; // 10 seconds
    private final AtomicBoolean nmsAliveStatus = new AtomicBoolean(false);
    protected final COMServicesProvider localCOMServices;
    protected final DirectoryProxyServiceImpl localDirectoryService;
    private Timer timer;
    private ProviderStatusAdapter providerStatusAdapter;
    private SingleConnectionDetails cdRemoteArchive;

    public GroundMOProxy() {
        ConnectionProvider.resetURILinksFile(); // Resets the providerURIs.properties file
        HelperMisc.loadPropertiesFile(); // Loads: provider.properties; settings.properties; transport.properties

        // Initialize the Helpers for the APIs
        NMFConsumer.initHelpers();
        localCOMServices = new COMServicesProvider();
        localDirectoryService = new DirectoryProxyServiceImpl();
    }

    public void init(final URI centralDirectoryServiceURI, final URI routedURI) {
        try {
            localCOMServices.init();
            localDirectoryService.init(localCOMServices);
        } catch (MALException ex) {
            Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
        }

        // Start the timer to publish the heartbeat
        timer = new Timer("Central_Directory_service_Periodic_Sync");
        timer.scheduleAtFixedRate(new TimerTask() {
            private boolean firstTime = true;

            @Override
            public void run() {
                if (!nmsAliveStatus.get()) {
                    try {
                        if (firstTime) {
                            localDirectoryService.syncLocalDirectoryServiceWithCentral(centralDirectoryServiceURI, routedURI);
                            cdRemoteArchive = cdFromService(ArchiveHelper.ARCHIVE_SERVICE);

                            // If it is first time, then we need to conect to the
                            // heartbeat service and listen to the beat
                            SingleConnectionDetails connectionDetails = cdFromService(HeartbeatHelper.HEARTBEAT_SERVICE);
                            HeartbeatConsumerServiceImpl heartbeat = new HeartbeatConsumerServiceImpl(connectionDetails, null);
                            providerStatusAdapter = new ProviderStatusAdapter(heartbeat);
                            Subscription heartbeatSubscription = ConnectionConsumer.subscriptionWildcardRandom();

                            try {
                                heartbeat.getHeartbeatStub().beatRegister(heartbeatSubscription, providerStatusAdapter);
                                firstTime = false;
                            } catch (MALInteractionException ex) {
                                Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                            } catch (MALException ex) {
                                Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                            }
                        }

                        nmsAliveStatus.set(true);
                        additionalHandling();
                    } catch (MALException ex) {
                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (MalformedURLException ex) {
                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (MALInteractionException ex) {
                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }
        }, 0, PERIOD);

        // Add the periodic check if new NMF Apps were started/stopped
        timer.scheduleAtFixedRate(new TimerTask() {
            private boolean firstRun = true;
            private ArchiveConsumerServiceImpl archiveService;
            private FineTime lastTime = new FineTime(0);

            @Override
            public void run() {
                if (nmsAliveStatus.get()) { // If alive
                    try {
                        if (firstRun) {
                            archiveService = new ArchiveConsumerServiceImpl(cdRemoteArchive);
                            firstRun = false;
                        }

                        // Check the remote COM Archive for new objects!
                        FineTime currentTime = providerStatusAdapter.getLastBeat();

                        ArchiveQuery archiveQuery = new ArchiveQuery(
                                archiveService.getConnectionDetails().getDomain(),
                                null,
                                null,
                                new Long(0),
                                null,
                                lastTime,
                                currentTime,
                                false,
                                null
                        );

                        ArchiveQueryList archiveQueryList = new ArchiveQueryList();
                        archiveQueryList.add(archiveQuery);

                        ArchiveAdapter adapter = new ArchiveAdapter() {
                            @Override
                            public synchronized void countResponseReceived(MALMessageHeader msgHeader,
                                    LongList countList, Map qosProperties) {
                                long count = countList.get(0);

                                if (count != 0) {
                                    Logger.getLogger(GroundMOProxy.class.getName()).log(Level.INFO,
                                            "A change in the Central Directory service was detected."
                                            + " The list of providers will be synchronized...");
                                    try {
                                        // If there are new objects, then synchronize!
                                        localDirectoryService.syncLocalDirectoryServiceWithCentral(centralDirectoryServiceURI, routedURI);
                                        additionalHandling();
                                   } catch (MALException ex) {
                                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                                    } catch (MalformedURLException ex) {
                                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                                    } catch (MALInteractionException ex) {
                                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                                    }
                                }
                            }
                        };

                        // Use the cout operation from the Archive for Common.Directory.ServiceProvider
                        archiveService.getArchiveStub().count(DirectoryHelper.SERVICEPROVIDER_OBJECT_TYPE,
                                archiveQueryList, null, adapter);

                        // use the cout operation from the Archive for SoftwareManagement.AppsLauncher.StopApp
                        archiveService.getArchiveStub().count(AppsLauncherHelper.STOPAPP_OBJECT_TYPE,
                                archiveQueryList, null, adapter);

                        lastTime = currentTime;
                    } catch (MALInteractionException ex) {
                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (MALException ex) {
                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (MalformedURLException ex) {
                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IOException ex) {
                        Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
            }

        }, PERIOD, PERIOD);

    }

    private SingleConnectionDetails cdFromService(COMService service) {
        final ServiceKey serviceKey = new ServiceKey(
                service.getArea().getNumber(),
                service.getNumber(),
                service.getArea().getVersion()
        );

        try {
            ProviderSummaryList list = getRemoteNMSProviderSpecificService(serviceKey);
            return AppsLauncherManager.getSingleConnectionDetailsFromProviderSummaryList(list);
        } catch (MALInteractionException ex) {
            Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
        } catch (MALException ex) {
            Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(GroundMOProxy.class.getName()).log(Level.SEVERE, null, ex);
        }
        return null;
    }

    public ProviderSummaryList getRemoteNMSProvider() throws MALInteractionException, MALException {
        IdentifierList wildcardList = new IdentifierList();
        wildcardList.add(new Identifier("*"));

        ServiceFilter filter = new ServiceFilter();
        filter.setDomain(wildcardList);
        filter.setNetwork(new Identifier("*"));
        filter.setSessionType(null);
        filter.setSessionName(new Identifier("*"));
        filter.setServiceKey(new ServiceKey(new UShort((short) 0), new UShort((short) 0), new UOctet((short) 0)));
        filter.setRequiredCapabilities(new UIntegerList());
        filter.setServiceProviderName(new Identifier(HelperNMF.NMF_NMS_NAME));

        return localDirectoryService.lookupProvider(filter, null);
    }

    public ProviderSummaryList getRemoteNMSProviderSpecificService(ServiceKey key) throws MALInteractionException, MALException {
        IdentifierList wildcardList = new IdentifierList();
        wildcardList.add(new Identifier("*"));

        ServiceFilter filter = new ServiceFilter();
        filter.setDomain(wildcardList);
        filter.setNetwork(new Identifier("*"));
        filter.setSessionType(null);
        filter.setSessionName(new Identifier("*"));
        filter.setServiceKey(key);
        filter.setRequiredCapabilities(new UIntegerList());
        filter.setServiceProviderName(new Identifier(HelperNMF.NMF_NMS_NAME));

        return localDirectoryService.lookupProvider(filter, null);
    }

    public abstract void additionalHandling();

    public URI getDirectoryServiceURI() {
        return localDirectoryService.getConnection().getPrimaryConnectionDetails().getProviderURI();
    }

    public URI getCOMArchiveServiceURI() {
        return localCOMServices.getArchiveService().getConnection().getPrimaryConnectionDetails().getProviderURI();
    }

    private class ProviderStatusAdapter extends HeartbeatAdapter {

        private static final long DELTA_ERROR = 2 * 1000; // 2 seconds = 2000 milliseconds
        private final long period; // In seconds
        private long lag; // In milliseconds
        private final Timer timer;
        private Time lastBeatAt = HelperTime.getTimestampMillis();

        public ProviderStatusAdapter(final HeartbeatConsumerServiceImpl heartbeat) throws MALInteractionException, MALException {
            long timestamp = System.currentTimeMillis();
            double value = heartbeat.getHeartbeatStub().getPeriod().getValue();
            lag = System.currentTimeMillis() - timestamp;
            period = (long) (value * 1000);

            Logger.getLogger(ProviderStatusAdapter.class.getName()).log(Level.INFO,
                    "The provider is reachable! Beat period: " + value + " seconds");
            nmsAliveStatus.set(true);

            timer = new Timer();
            timer.scheduleAtFixedRate(new TimerTask() {
                int tryNumber = 0;

                @Override
                public void run() {
                    synchronized (timer) {
                        final Time currentTime = HelperTime.getTimestampMillis();

                        // If the current time has passed the last beat + the beat period + a delta error
                        long threshold = lastBeatAt.getValue() + period + DELTA_ERROR;

                        if (currentTime.getValue() > threshold) {
                            // Then the provider is unresponsive
                            nmsAliveStatus.set(false);
                            Logger.getLogger(ProviderStatusAdapter.class.getName()).log(Level.INFO,
                                    "The heartbeat message from the provider was not received.");
                        } else {
                            if (tryNumber == 3) { // Every third try...
                                try {
                                    long timestamp = System.currentTimeMillis();
                                    heartbeat.getHeartbeatStub().getPeriod();
                                    lag = System.currentTimeMillis() - timestamp; // Calculate the lag
                                } catch (MALInteractionException ex) {
                                    Logger.getLogger(ProviderStatusAdapter.class.getName()).log(Level.SEVERE, null, ex);
                                } catch (MALException ex) {
                                    Logger.getLogger(ProviderStatusAdapter.class.getName()).log(Level.SEVERE, null, ex);
                                }
                                tryNumber = 0;
                            }
                        }

                        tryNumber++;
                    }
                }
            }, period, period);
        }

        @Override
        public synchronized void beatNotifyReceived(org.ccsds.moims.mo.mal.transport.MALMessageHeader msgHeader,
                org.ccsds.moims.mo.mal.structures.Identifier _Identifier0,
                org.ccsds.moims.mo.mal.structures.UpdateHeaderList _UpdateHeaderList1,
                java.util.Map qosProperties) {
            synchronized (timer) {
                lastBeatAt = HelperTime.getTimestampMillis();
                final Time onboardTime = msgHeader.getTimestamp();
                final long iDiff = lastBeatAt.getValue() - onboardTime.getValue();

                Logger.getLogger(ProviderStatusAdapter.class.getName()).log(Level.INFO,
                        "(Clocks diff: " + iDiff + " ms"
                        + " | Round-Trip Delay time: " + lag + " ms"
                        + " | Last beat received at: " + HelperTime.time2readableString(lastBeatAt)
                        + ")"
                );

                nmsAliveStatus.set(true);
            }
        }

        public FineTime getLastBeat() {
            return HelperTime.timeToFineTime(lastBeatAt);
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy