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

org.jpac.JPac Maven / Gradle / Ivy

Go to download

Open-source runtime system for component-based implementation of automation solutions with Java

The newest version!
/**
 * PROJECT   : Elbfisch - java process automation controller (jPac)
 * MODULE    : JPac.java
 * VERSION   : -
 * DATE      : -
 * PURPOSE   : 
 * AUTHOR    : Bernd Schuster, MSK Gesellschaft fuer Automatisierung mbH, Schenefeld
 * REMARKS   : -
 * CHANGES   : CH#n   
 *
 * This file is part of the jPac process automation controller.
 * jPac is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * jPac is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the jPac If not, see .
 */

package org.jpac;

import org.eclipse.milo.opcua.stack.core.Stack;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import org.apache.commons.configuration.ConfigurationException;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import org.jpac.configuration.BooleanProperty;
import org.jpac.configuration.Configuration;
import org.jpac.configuration.DoubleProperty;
import org.jpac.configuration.IntProperty;
import org.jpac.configuration.LongProperty;
import org.jpac.configuration.StringProperty;
import org.jpac.console.TelnetService;
import org.jpac.ef.CommandHandler;
import org.jpac.ef.EfService;
import org.jpac.opc.Opc;
import org.jpac.opc.Opc.AccessLevel;
import org.jpac.opc.OpcUaService;
import org.jpac.snapshot.Snapshot;
import org.jpac.statistics.Histogram;

/**
 * central runtime engine of JPac
 * @author berndschuster
 */
public class JPac extends Thread {
    static Logger Log = LoggerFactory.getLogger("jpac.JPac");

    private     final int       OWNMODULEINDEX                       = 0;
    private     final long      DEFAULTCYCLETIME                     = 100000000L; // 100 ms
    private     final long      DEFAULTCYCLETIMEOUTTIME              = 1000000000L;// 1 s
    private     final int       MAXSHUTDOWNTIME                      = 2000;       // 2 s
    private     final int       EXITCODENORMALSHUTDOWN               = 0;
    private     final int       EXITCODEINITIALIZATIONERROR          = 100;
    private     final int       EXITCODEINTERNALERROR                = 101;
    private     final long      DEFAULTSHUTDOWNTIMEOUTTIME           = 5000000000L; //5 s
    private     final String    OPCACCESSLEVELNONE                   = "NONE";
    private     final String    OPCACCESSLEVELREADONLY               = "READ_ONLY";
    private     final String    OPCACCESSLEVELREADWRITE              = "READ_WRITE";
    private     final int       CONSOLESERVICEDEFAULTPORT            = 8023;
    private     final String    DEFAULTSERVICEBINDADDRESS     = "localhost";
    
    private     final String    CFGDIR                               = "./cfg";
    private     final String    DATADIR                              = "./data";
    
    public enum CycleMode{OneCycle, Bound, LazyBound, FreeRunning}
    
    public      enum    Status{initializing, ready, running, halted};

    protected   static JPac  instance   = null;

    private     int                    tracePoint;//used for internal trace purposes
    private     Set          awaitedEventList;
    private     Set          awaitedSimEventList;
    private     Set          firedEventList;
    private     long                   minRemainingCycleTime;
    private     long                   maxRemainingCycleTime;
    private     long                   expectedCycleEndTime;
    private     long                   shutdownRequestTime;
    private     long                   cycleStartTime;
    private     long                   expansionTime;
    private     long                   numberOfCyclesExceeded;
    private     long                   cycleNumber;
    private     Status                 status;
    private     boolean                emergencyStopRequested;
    private     boolean                emergencyStopActive;
    private     EmergencyStopException emergencyStopCausedBy;
    private     boolean                emergencyStopIsToBeThrown;

    private     boolean                readyToShutdown;

    private     Semaphore              startCycle;
    private     Semaphore              cycleEnded;
    
    private     boolean                normalShutdownPending;
    
    private     final List   synchronizedTasks;
    private     final List cyclicTasks;
    
    private LongProperty    propCycleTime;
    private LongProperty    propCycleTimeoutTime;
    private StringProperty  propCycleMode;
    private BooleanProperty propRunningStandalone;
    private BooleanProperty propEnableTrace;
    private BooleanProperty propPauseOnBreakPoint;
    private IntProperty     propTraceTimeMinutes;
    private BooleanProperty propRemoteSignalsEnabled;
    private IntProperty     propRemoteSignalPort;
    private StringProperty  propHistogramFile;
    private LongProperty    propCyclicTaskShutdownTimeoutTime;
    private LongProperty    propMaxShutdownTime;
    private BooleanProperty propOpcUaServiceEnabled;
    private IntProperty     propOpcUaServicePort;
    private StringProperty  propOpcUaServiceName;
    private DoubleProperty  propOpcUaMinSupportedSampleInterval;
    private StringProperty  propOpcUaDefaultAccessLevel;
    private BooleanProperty propEfServiceEnabled;
    private StringProperty  propEfBindAddress;
    private IntProperty     propEfServicePort;
    private StringProperty  propEfDefaultAccessLevel;
    private IntProperty     propEfReceiveBufferSize;
    private BooleanProperty propConsoleServiceEnabled;
    private IntProperty     propConsoleServicePort;
    private StringProperty  propConsoleBindAddress;
    private BooleanProperty propGenerateSnapshotOnShutdown;
    
    private String            instanceIdentifier;
    private long              cycleTime;
    private long              cycleTimeoutTime;
    private CycleMode         cycleMode;
    private boolean           runningStandalone;
    private boolean           enableTrace;
    private boolean           pauseOnBreakPoint;
    private int               traceTimeMinutes;
    private boolean           remoteSignalsEnabled;
    private int               remoteSignalPort;
    private String            histogramFile;
    private long              cyclicTaskShutdownTimeoutTime;
    private long              maxShutdownTime;
    private boolean           opcUaServiceEnabled;
    private int               opcUaServicePort;
    private String            opcUaServiceName;
    private double            opcUaMinSupportedSampleInterval;
    private Opc.AccessLevel   opcUaDefaultAccessLevel;
    private List      opcUaBindAddresses;
    private boolean           efServiceEnabled;
    private int               efServicePort;
    private Opc.AccessLevel   efDefaultAccessLevel;
    private String            efBindAddress;
    private int               efReceiveBufferSize;
    private boolean           consoleServiceEnabled;
    private int               consoleServicePort;
    private String            consoleBindAddress;
    private boolean           generateSnapshotOnShutdown;
    
    private CountingLock      activeEventsLock;
    
    private Synchronisation   startCycling;
    private Synchronisation   shutdownRequest;
    
    private boolean           immediateShutdownRequested;
    private int               exitCode;
        
    private ProcessEvent      awaitedEventOfLastModule;
    
    private TraceQueue      traceQueue;
    
    private Histogram       cycleHistogram;   //used to determine the overall load per cycle
    private Histogram       systemHistogram;  //used to determine the system load during a cycle
    private Histogram       modulesHistogram; //used to determine the load produced by the application modules during a cycle
    
    private AbstractModule  processedModule;   //
    
    private int             incrementCounter;
    private int             decrementCounter;

    private boolean         stopBeforeStartup;
    
    private String          versionNumber;
    private String          buildNumber;
    private String          buildDate;
    private String          projectName;
    
    private OpcUaService    opcUaService;   
    private EfService       efService;
    private TelnetService   consoleService;
    
    //    private ArrayList moduleList;
    private Hashtable moduleList;


    @SuppressWarnings("unchecked")
	protected JPac(){
        super();
        setName(getClass().getSimpleName());
        
        tracePoint                  = 0;
        minRemainingCycleTime       = Long.MAX_VALUE;
        maxRemainingCycleTime       = 0;
        expectedCycleEndTime        = 0;
        cycleStartTime              = 0;
        expansionTime               = 0;
        status                      = Status.initializing;
        cycleNumber                 = 0;

        awaitedEventList            = Collections.synchronizedSet(new HashSet());
        awaitedSimEventList         = Collections.synchronizedSet(new HashSet());
        firedEventList              = new HashSet();

        readyToShutdown             = false;
        emergencyStopRequested      = false;
        emergencyStopActive         = false;
        emergencyStopIsToBeThrown   = false;
        emergencyStopCausedBy       = null;
        
        synchronizedTasks           = Collections.synchronizedList(new ArrayList());
        cyclicTasks                 = Collections.synchronizedList(new ArrayList());
        
        startCycle                  = new Semaphore(1);
        cycleEnded                  = new Semaphore(1);
        
        startCycling                = new Synchronisation();
        shutdownRequest             = new Synchronisation();
        
        immediateShutdownRequested  = false;        
        
        normalShutdownPending       = false;
                
        activeEventsLock            = new CountingLock();
        awaitedEventOfLastModule    = null;
        
        moduleList                  = new Hashtable<>(20);
        traceQueue                  = null;
        
        cycleHistogram             = null;
        systemHistogram            = null;
        modulesHistogram           = null;
        processedModule             = null;
        
        exitCode                    = 0;
        
        incrementCounter            = 0;
        decrementCounter            = 0;        
                
        try{
            propCycleTime                       = new LongProperty(this,"CycleTime",DEFAULTCYCLETIME,"[ns]",true);
            propCycleTimeoutTime                = new LongProperty(this,"CycleTimeoutTime",DEFAULTCYCLETIMEOUTTIME,"[ns]",true);
            propCycleMode                       = new StringProperty(this,"CycleMode",CycleMode.LazyBound.toString(),"[OneCycle | Bound | LazyBound | FreeRunning]",true);
            propRunningStandalone               = new BooleanProperty(this,"RunningStandalone",true,"must be true, if Elbfisch is run standalone",true);
            propEnableTrace                     = new BooleanProperty(this,"EnableTrace",false,"enables tracing of the module activity",true);
            propTraceTimeMinutes                = new IntProperty(this,"TraceTimeMinutes",0,"used to estimate the length of the trace buffer [min]",true);
            propPauseOnBreakPoint               = new BooleanProperty(this,"pauseOnBreakPoint", false, "cycle is paused, until all modules enter waiting state", true);
            propRemoteSignalsEnabled            = new BooleanProperty(this,"RemoteSignalsEnabled", false, "enable connections to/from remote JPac instances", true);
            propRemoteSignalPort                = new IntProperty(this,"RemoteSignalPort",10002,"server port for remote signal access",true);
            propHistogramFile                   = new StringProperty(this,"HistogramFile","./data/histogram.csv","file in which the histograms are stored", true);
            propCyclicTaskShutdownTimeoutTime   = new LongProperty(this,"CyclicTaskShutdownTimeoutTime",DEFAULTSHUTDOWNTIMEOUTTIME,"Timeout for all cyclic tasks to stop on shutdown [ns]",true);
            propMaxShutdownTime                 = new LongProperty(this,"MaxShutdownTime",DEFAULTSHUTDOWNTIMEOUTTIME,"period of time in which all modules must have been terminated in case of a shutdown [ns]",true);
            propOpcUaServiceEnabled             = new BooleanProperty(this,"OpcUa.ServiceEnabled",false,"enables the opc ua service", true);
            propOpcUaServicePort                = new IntProperty(this,"OpcUa.ServicePort",OpcUaService.DEFAULTPORT,"port over which the opc ua service is provided", true);
            propOpcUaServiceName                = new StringProperty(this,"OpcUa.ServiceName",OpcUaService.DEFAULTSERVERNAME,"name of the server instance", true);
            propOpcUaMinSupportedSampleInterval = new DoubleProperty(this,"OpcUa.MinSupportedSampleInterval",OpcUaService.MINIMUMSUPPORTEDSAMPLEINTERVAL,"minimum supported sample interval [ms]", true);
            propOpcUaDefaultAccessLevel         = new StringProperty(this,"OpcUa.DefaultAccessLevel","NONE","access levels can be NONE,READ_ONLY,READ_WRITE", true);
            propEfServiceEnabled                = new BooleanProperty(this,"Ef.ServiceEnabled",false,"enables the elbfisch service", true);
            propEfBindAddress                   = new StringProperty(this,"Ef.BindAddress","localhost","address this service is bound to", true);
            propEfServicePort                   = new IntProperty(this,"Ef.ServicePort",EfService.DEFAULTPORT,"port over which the elbfisch service is provided", true);
            propEfDefaultAccessLevel            = new StringProperty(this,"Ef.DefaultAccessLevel","NONE","access levels can be NONE,READ_ONLY,READ_WRITE", true);
            propEfReceiveBufferSize             = new IntProperty(this,"Ef.ReceiveBufferSize",EfService.DEFAULTRECEIVEBUFFERSIZE,"size of the receive buffer [byte]", true);
            propConsoleServiceEnabled           = new BooleanProperty(this,"Console.ServiceEnabled",false,"enables the console service", true);
            propConsoleServicePort              = new IntProperty(this,"Console.ServicePort",CONSOLESERVICEDEFAULTPORT,"port over which the console service is provided", true);
            propConsoleBindAddress              = new StringProperty(this,"Console.BindAddress",DEFAULTSERVICEBINDADDRESS,"address the console service is bound to", true);
            propGenerateSnapshotOnShutdown      = new BooleanProperty(this,"GenerateSnapShotOnShutdown",false,"used to enable the generation of a snapshot on shutdown", true);
            
            instanceIdentifier              = InetAddress.getLocalHost().getHostName() + ":" + propRemoteSignalPort.get();
            cycleTime                       = propCycleTime.get();
            cycleTimeoutTime                = propCycleTimeoutTime.get();
            cycleMode                       = CycleMode.valueOf(propCycleMode.get());
            runningStandalone               = propRunningStandalone.get();
            enableTrace                     = propEnableTrace.get();
            traceTimeMinutes                = propTraceTimeMinutes.get();
            pauseOnBreakPoint               = propPauseOnBreakPoint.get();
            remoteSignalsEnabled            = propRemoteSignalsEnabled.get();
            remoteSignalPort                = propRemoteSignalPort.get();
            histogramFile                   = propHistogramFile.get();
            cyclicTaskShutdownTimeoutTime   = propCyclicTaskShutdownTimeoutTime.get();
            maxShutdownTime                 = propMaxShutdownTime.get();
            
            opcUaServiceEnabled             = propOpcUaServiceEnabled.get();
            opcUaServicePort                = propOpcUaServicePort.get();
            opcUaServiceName                = propOpcUaServiceName.get();
            opcUaMinSupportedSampleInterval = propOpcUaMinSupportedSampleInterval.get();
            opcUaDefaultAccessLevel         = AccessLevel.valueOf(propOpcUaDefaultAccessLevel.get());
            opcUaBindAddresses              = (List)Configuration.getInstance().getList("org..jpac..JPac.OpcUa.BindAddresses.BindAddress");
            if (opcUaBindAddresses.isEmpty()) {
            	//if bind addresses not specified add one default address to the list
            	StringProperty defaultBindAddress = new StringProperty(this,"OpcUa.BindAddresses.BindAddress",DEFAULTSERVICEBINDADDRESS,"address the opc ua service is bound to", true);
            	opcUaBindAddresses.add(defaultBindAddress.get());
            }
            
            efServiceEnabled                = propEfServiceEnabled.get();
            efServicePort                   = propEfServicePort.get();
            efBindAddress                   = propEfBindAddress.get();
            efReceiveBufferSize             = propEfReceiveBufferSize.get();
            
            consoleServiceEnabled           = propConsoleServiceEnabled.get();
            consoleServicePort              = propConsoleServicePort.get();
            consoleBindAddress              = propConsoleBindAddress.get();
            generateSnapshotOnShutdown      = propGenerateSnapshotOnShutdown.get();
            
            if (opcUaServiceEnabled){
                if (propOpcUaDefaultAccessLevel.get().equals(OPCACCESSLEVELNONE)){
                    opcUaDefaultAccessLevel = Opc.AccessLevel.NONE;
                } else if (propOpcUaDefaultAccessLevel.get().equals(OPCACCESSLEVELREADONLY)){
                    opcUaDefaultAccessLevel = Opc.AccessLevel.READ_ONLY;
                } else if (propOpcUaDefaultAccessLevel.get().equals(OPCACCESSLEVELREADWRITE)){
                    opcUaDefaultAccessLevel = Opc.AccessLevel.READ_WRITE;
                } else {
                    opcUaDefaultAccessLevel = Opc.AccessLevel.NONE;                    
                }
            }
            if (efServiceEnabled){//TODO define a AccessLevel policy for all protocols.
                if (propEfDefaultAccessLevel.get().equals(OPCACCESSLEVELNONE)){
                    efDefaultAccessLevel = Opc.AccessLevel.NONE;
                } else if (propOpcUaDefaultAccessLevel.get().equals(OPCACCESSLEVELREADONLY)){
                    efDefaultAccessLevel = Opc.AccessLevel.READ_ONLY;
                } else if (propOpcUaDefaultAccessLevel.get().equals(OPCACCESSLEVELREADWRITE)){
                    efDefaultAccessLevel = Opc.AccessLevel.READ_WRITE;
                } else {
                    efDefaultAccessLevel = Opc.AccessLevel.NONE;                    
                }
            }
            try{
                //get version.number, build.number and build.date
                Class clazz = JPac.class;
                String className = clazz.getSimpleName() + ".class";
                String classPath = clazz.getResource(className).toString();
                String manifestPath = "";
                if(classPath.endsWith("org.jpac/build/classes/org/jpac/JPac.class")){
                    //instantiated inside IDE
                    manifestPath = classPath.replace("build/classes/org/jpac/JPac.class", "").concat("MANIFEST.MF");
                }
                else{
                    //contained in org.jpac.jar
                    manifestPath = classPath.substring(0, classPath.lastIndexOf("!") + 1) + "/META-INF/MANIFEST.MF";
                }
                Manifest manifest = new Manifest(new URL(manifestPath).openStream());
                Attributes attr = manifest.getMainAttributes();
                versionNumber = attr.getValue("Bundle-Version");
                buildNumber   = attr.getValue("Bundle-Build");
                buildDate     = attr.getValue("Bundle-Date");
                projectName   = attr.getValue("Bundle-Name");
            }
            catch(Exception exc){
                //build information cannot be retrieved
                versionNumber = "unknown";
                buildNumber   = "unknown";
                buildDate     = "unknown";
            }
            //install configuration saver
            try{registerCyclicTask(Configuration.getInstance().getConfigurationSaver());}catch(WrongUseException exc){/*cannot happen*/}
        }
        catch(ConfigurationException | IOException ex){
            Log.error("Error: ", ex);
            //properties cannot be initialized
            //kill application
            System.exit(99);            
        }
                
        //install a shutdown hook to handle application shutdowns
        Runtime.getRuntime().addShutdownHook(new ShutdownHook());
        setPriority(MAX_PRIORITY);
        //start instance of the automationController
        start();
    }

	public static JPac getInstance(){
        if (instance == null) {
            instance = new JPac();
        }
        return instance;
    }
    
    @Override
    public void run(){
        boolean done                 = false;
        long    systemLoadStartTime  = 0L; //used to compute the system histogram
        long    modulesLoadStartTime = 0L; //used to compute the modules histogram
        
        if (Log.isInfoEnabled()) Log.info("STARTING");
        try{
            try{
                //wait, until start up is requested
                setStatus(Status.ready);
                waitForStartUpSignal();
                if (stopBeforeStartup){
                    //termination requested before first cycle (see ShutdownHook)
                    //shut down immediately
                    if (Log.isInfoEnabled()) Log.info("SHUTDOWN COMPLETE");
                    readyToShutdown = true;// inform the shutdown hook that we are done
                    return;
                }
                prepareTrace();
                prepareHistogramms();
                prepareOpcUaService();
                prepareEfService();
                prepareConsoleService();
                prepareRemoteConnections(); 
                prepareCyclicTasks();
            }
            catch(Exception exc){
                //if an error occured during preparation of the system, shutdown immediately
                Log.error("Error: ", exc);
                invokeImmediateShutdown(EXITCODEINITIALIZATIONERROR);
            }
            catch(Error exc){
                //if an error occured during preparation of the system, shutdown immediately
                Log.error("Error: ", exc);
                invokeImmediateShutdown(EXITCODEINITIALIZATIONERROR);
            }
            
            if (Log.isInfoEnabled()) Log.info("running in " + cycleMode + " mode ...");
            setStatus(Status.running);
            if (getCycleMode() == CycleMode.OneCycle){
                prepareOneCycleMode();
            }
            //initialize values for cycle time computation
            initializeCycle();
            do{//!done && running
                try{
                    tracePoint = 1000;
                    if (getCycleMode() == CycleMode.OneCycle){
                        waitForStartCycleSignal();
                        //initialize values for cycle time computation
                        initializeCycle();
                    }                
                    systemLoadStartTime = System.nanoTime();
                    tracePoint = 1100;
                    //update cycle var's for this cycle
                    prepareCycle();
                    
                    //handle deferred tasks, which must be synchronized to the cycle:
                    //propagation of signals, connect/disconnect of signals etc.
                    tracePoint = 1200;
                    handleDeferredTasks();
                    //now fire events awaited by application modules
                    tracePoint = 1300;
                    handleFireables(getAwaitedEventList());       
                    //acquire system histogram information
                    modulesLoadStartTime = System.nanoTime();
                    systemHistogram.update(modulesLoadStartTime - systemLoadStartTime);
                    //invoke the inEveryCycleDo() for every active module
                    tracePoint = 1400;
                    handleInEveryCycleDos();                    
                    //now start up application modules which have been awakenend before by fired process events
                    tracePoint = 1450;
                    handleAwakenedModules();
                    
                    //acquire modules histogram information
                    modulesHistogram.update(System.nanoTime() - modulesLoadStartTime);
                    
                    //handle emergency stop requests occured in current cycle
                    emergencyStopIsToBeThrown = false; //true only for one cycle
                    if (emergencyStopRequested){
                        emergencyStopRequested    = false;
                        //throw EmergencyStopException an all awaiting modules in next cycle
                        emergencyStopIsToBeThrown = true;
                    }

                    //acquire overall load histogram information
                    cycleHistogram.update(System.nanoTime() - systemLoadStartTime);

                    tracePoint = 1500;
                    long remainingCycleTime = expectedCycleEndTime - System.nanoTime();
                    acquireStatistics(remainingCycleTime);
                    if(getCycleMode() != CycleMode.FreeRunning){
                        //if not in FreeRunning  mode synchronize to the end of the cycle
                        //now, wait for the end of the cycle
                        wait4EndOfCycle(remainingCycleTime);
                    }
                }
                catch (Exception ex){
                    Log.error("Error",ex);
                    invokeImmediateShutdown(EXITCODEINTERNALERROR);
                }
                catch (Error ex){
                    Log.error("Error",ex);
                    invokeImmediateShutdown(EXITCODEINTERNALERROR);
                }
                //check, if the application is to be shutdown normally
                done = normalShutdownPending && (allModulesShutdown() || shutdownTimeExceeded());
                //check, if the application is to be shutdown immediately
                if (isImmediateShutdownRequested()){
                    if (generateSnapshotOnShutdown){
                        generateSnapshot();
                    }
                    done = true;// force shutdown
                    //shutdown all active modules
                    shutdownModulesImmediately(getAwaitedEventList());
                }                
                if (getCycleMode() == CycleMode.OneCycle){
                    signalEndOfCycle();
                }            
                //trace cycle statistics, if applicable
                traceCycle();
            }
            while(!done);
            if (!allModulesShutdown()){
                getModules().values().stream().forEach((m)-> {if(m.getState() != State.TERMINATED) Log.error("failed to shutdown module '{}' in time.", m.getQualifiedName());});
            }
            //shutdown RemoteSignalConnection's
            closeRemoteConnections();
            //stop opc ua service, if running
            stopOpcUaService();
            //stop elbdfisch communication service, if running
            stopEfService();
            //stop console service, if running
            stopConsoleService();
            //clean up context of registered cyclic tasks
            stopCyclicTasks();
            //acknowledge request
            acknowledgeShutdownRequest();
            //new state is halted
            setStatus(Status.halted);
            if (Log.isInfoEnabled()){
                ArrayList lines = logStatistics();
                lines.forEach(l -> Log.info(l));
                Log.info("SHUTDOWN COMPLETE");
            }
            readyToShutdown = true;// inform the shutdown hook that we are done
            try{sleep(MAXSHUTDOWNTIME);} catch (InterruptedException ex){}
            //jUnit test need special handling
            if (runningStandalone){
                System.exit(exitCode);
            }
        }
        catch(Exception exc){
            Log.error("Error: ", exc);
        }
        catch(Error exc){
            Log.error("Error: ", exc);
        }
    };

    private void handleFireables(Set fireableList) throws SomeEventsNotProcessedException, InconsistencyException{
        boolean fired                        = false;
        boolean shutdownRequestedInThisCycle = isNormalShutdownRequested() && !normalShutdownPending;;
        //check, if some of the currently registered Fireables can be fired in this cycle
        for (Fireable f: fireableList) {
            try{//the fireable is fired, if
                //it is fired by its own,
                //or if it is a ProcessEvent and an emergency stop is pending and the ProcessEvent is not awaited by a module, which threw
                //an emergency stop exception during the last cycle,
                //or if it is a ProcessEvent and it timed out during this cycle
                //or shutdown is requested.
                boolean fFired    = f.evaluateFiredCondition();
                boolean fTimedOut = (f instanceof ProcessEvent) && ((ProcessEvent)f).evaluateTimedOutCondition();
                
                fired = fFired ||
                        (f instanceof ProcessEvent && (shutdownRequestedInThisCycle                                                                       ||
                                                       emergencyStopIsToBeThrown   && !((ProcessEvent)f).getObservingModule().isRequestingEmergencyStop())||
                                                       fTimedOut                                                                                       );
            }
            catch(ProcessException exc){
                //the fireable threw a process exception
                //let the observing module handle this
                fired = true;
            }
            if (fired){
                //add Fireable to the list of fired events
                if (f instanceof ProcessEvent && shutdownRequestedInThisCycle){
                    //if an shutdown request is pending,
                    //let the awaiting module know about it
                    ((ProcessEvent)f).setShutdownRequested(true);
                } else if (f instanceof ProcessEvent && emergencyStopIsToBeThrown){
                    //if an emergency stop request is pending,
                    //let the awaiting module know about it
                    ((ProcessEvent)f).setEmergencyStopOccured(true);
                    ((ProcessEvent)f).setEmergencyStopCause(emergencyStopCausedBy.getMessage());                        
                }
                getFiredEventList().add(f);
            }
            if (f instanceof ProcessEvent && ((ProcessEvent)f).getObservingModule().isRequestingEmergencyStop()){
                //emergency stop request has been recognized above. Reset it instantly
               ((ProcessEvent)f).getObservingModule().setRequestingEmergencyStop(false);                                                 
            }
        }
        //remove fireables from awaited event list
        for (Fireable f: getFiredEventList()) {
             fireableList.remove(f);
        }
        
        if (shutdownRequestedInThisCycle){
            normalShutdownPending        = true;//send shutdown request only once
            shutdownRequestedInThisCycle = false;
            if (generateSnapshotOnShutdown){
                generateSnapshot();
            }
        }
    }
    
    private void handleInEveryCycleDos(){
        try{
            for (AbstractModule module: moduleList.values()){
                //invoke inEveryCycleDo() for all active modules
                processedModule = module;
                module.invokeInEveryCycleDo();
                processedModule = null;
            }
        }
        finally{
            processedModule = null;
        }
    }
    
    private void handleAwakenedModules() throws SomeEventsNotProcessedException, InconsistencyException{
        try{
            //awaken associated modules
            for (Fireable f: getFiredEventList()) {
                tracePoint = 1501;
                f.notifyObservingModule();
            }
            //wait until all fired events are processed by the associated modules
            switch(getCycleMode()){
                case OneCycle:
                     //wait until all modules have completed their tasks without time limit
                     activeEventsLock.waitForUnlock();
                     break;
                case Bound:
                     //wait until all modules have completed their tasks but not beyond the end of the cycle
                     activeEventsLock.waitForUnlock(expectedCycleEndTime - System.nanoTime());
                     break;
                case LazyBound:
                     //wait until all modules have completed their tasks but not beyond the end of the cycle
                     activeEventsLock.waitForUnlock(expectedCycleEndTime - System.nanoTime());
                     if (activeEventsLock.getCount() > 0){
                        //cycle exceedance encountered. Give the application time to bring the cycle to an end
                        activeEventsLock.waitForUnlock(cycleTimeoutTime);
                        //record cycle exceedance
                        numberOfCyclesExceeded++;
                     }
                     break;
                case FreeRunning:
                     //wait until all modules have completed their tasks without time limit
                     activeEventsLock.waitForUnlock();
                     break;
            }
            if (activeEventsLock.getCount() > 0){
                if (pauseOnBreakPoint){
                    //a module might have run on a break point
                    //wait until all modules have completed their tasks without time limit
                    if (Log.isInfoEnabled()) Log.info("jPac paused ...");
                    activeEventsLock.waitForUnlock();  
                    //lengthen time line for the time halted on the break point (time line is freezed for that period of time)
                    expansionTime += System.nanoTime() - expectedCycleEndTime;
                    if (Log.isInfoEnabled()) Log.info("jPac continued ...");
                }
                else{
                    //assert failure, if at least one fired event
                    //has failed to be properly handled during the last cycle
                    Log.error("at least one module hung up !!!!: ");
                    Log.error("  elapsed cycle time     : " + (System.nanoTime() - cycleStartTime ));
                    Log.error("  trace point            : " + tracePoint);
                    Log.error("  activeEventsCount      : " + activeEventsLock.getCount());
                    Log.error("  max. activeEventsCount : " + activeEventsLock.getMaxCount());
                    Log.error("  incrementCounter       : " + incrementCounter);
                    Log.error("  decrementCounter       : " + decrementCounter);
                    
                    for (Fireable f: getFiredEventList()){
                        AbstractModule module = ((ProcessEvent)f).getObservingModule();
                        if (module.getState() != Thread.State.WAITING){
                            Log.error("  module '" + module + "' invoked by " + f + " hung up in state " + module.getStatus());                        
                        }
                        else{
                            Log.info("  module '" + module + "' invoked by " + f + " state " + module.getStatus());                                                    
                        }
                    }
                    throw new SomeEventsNotProcessedException(getFiredEventList());
                }
            }
            else{
                //all modules came to an end for this cycle
                //wait until the last made its wait() call
                synchronizeOnLastModule();
            }
        }
        finally{
            //clear list of fired ProcessEvents (simulation or productive) for next usage
            getFiredEventList().clear();
        }

    }

    private void shutdownModulesImmediately(Set fireableList) throws InconsistencyException{
        try{
            //invoke all waiting modules and let them handle their ShutdownException
            for (Fireable f: fireableList) {
                if (f instanceof ProcessEvent){
                    //retrieve all pending process events
                    getFiredEventList().add(f);
                }
            }
            if (Log.isInfoEnabled()) Log.info("shutting down modules ...");
            //awaken waiting modules with a ShutdownException
            //which is automatically thrown by means of the ProcessEvent,
            //if this.shutdownRequested = true
            for (Fireable f: getFiredEventList()) {
                AbstractModule module = f.getObservingModule();
                String moduleName = module.toString();
                if (Log.isDebugEnabled()) Log.debug("   shutting down module " + moduleName + " ...");
                ((ProcessEvent)f).setShutdownRequested(true);
                f.notifyObservingModule();
                boolean moduleEnded = false;
                do
                  try{
                      module.join(MAXSHUTDOWNTIME);
                      moduleEnded = true;
                  }
                  catch(InterruptedException exc){}
                while(!moduleEnded);
                if (module.getState() == Thread.State.TERMINATED){
                    if (Log.isDebugEnabled()) Log.debug("   module " + moduleName + " shutdown succeeded");
                }
                else{
                    Log.error("   !!!! failed to shutdown module " + moduleName + ". It's current state is " + module.getStatus());                    
                }
            }
            if (Log.isInfoEnabled()) Log.info("... shutting down modules done");
        }
        finally{
            //clear list of fired ProcessEvents (simulation or productive) for next usage
            getFiredEventList().clear();
        }
    }
    
    private void shutdownModules(Set fireableList) throws InconsistencyException{
        try{
            //invoke all waiting modules and let them handle their ShutdownException
            for (Fireable f: fireableList) {
                if (f instanceof ProcessEvent){
                    //retrieve all pending process events
                    getFiredEventList().add(f);
                }
            }
            if (Log.isInfoEnabled()) Log.info("requesting shutdown of modules");
            //awaken waiting modules with a ShutdownException
            //which is automatically thrown by means of the ProcessEvent,
            //if this.shutdownRequested = true
            for (Fireable f: getFiredEventList()) {
                AbstractModule module = f.getObservingModule();
                ((ProcessEvent)f).setShutdownRequested(true);
                f.notifyObservingModule();
            }
        }
        finally{
            //clear list of fired ProcessEvents (simulation or productive) for next usage
            getFiredEventList().clear();
        }
    }
    
    private void generateSnapshot(){
        try{
            Snapshot snapshot = getSnapshot();
            snapshot.dump(getDataDir());
            Log.info("snapshot dumped to '" + snapshot.getFilename() + "'");
        }
        catch(Exception exc){
            Log.error("Error: ", exc);
        }
    }
    
    private boolean allModulesShutdown(){
        return getModules().values().stream().allMatch((m) -> m.getState() == State.TERMINATED);
    }
    
    /**
     * used to propagate signal states to connected signal instances
     */
    @SuppressWarnings("deprecation")
	private void handleDeferredTasks() throws SignalAlreadyConnectedException, SignalInvalidException, ConfigurationException, RemoteSignalException {
        synchronized(synchronizedTasks){
            for(Runnable r: synchronizedTasks){
                //run synchronized task
                r.run();
            }            
            //everything done. Prepare list for next cycle
            synchronizedTasks.clear();
        }

        ConcurrentHashMap signals = SignalRegistry.getInstance().getSignals();
        synchronized(signals){
            for(Signal s: signals.values()){
                //handle requested (dis)connections of signals
                s.handleConnections();
                //apply intrinsic function
                s.applyIntrinsicFunction();
                //propagate signal alterations
                s.propagate();
            }
        }
        
        synchronized(cyclicTasks){
            for(CyclicTask ct: cyclicTasks){
                //run cyclic task
                ct.run();
            }            
        }
        
        if (efServiceEnabled){
            efService.exchangeChangedSignals();
        }

        pushSignalsOverRemoteConnections();                 

        //propagate signals altered due to I/O operations 
        synchronized(signals){
            for(Signal s: signals.values()){
                s.propagate();
            }
        }
    }
    
    /*
     * used to invoke a task synchronized to the next jpac cycle
     * CAUTION: The given task may not call invokeLater() directly or indirectly by itself !
     */
    public void invokeLater(Runnable task){
        synchronized(synchronizedTasks){
            synchronizedTasks.add(task);
        }
    }

    /*
     * used to register a task, which is run at the beginning of every jpac cycle
     * @param  task cyclic task 
     * @throws WrongUseException, if the given task is already registered
     */
    public void registerCyclicTask(CyclicTask task) throws WrongUseException{
        synchronized(cyclicTasks){
            if (cyclicTasks.contains(task)){
                throw new WrongUseException("cyclic task " + task + " already registered.");
            }
            cyclicTasks.add(task);
        }
    }

    /*
     * used to register a task, which is run at the beginning of every jpac cycle
     */
    public void unregisterCyclicTask(CyclicTask task){
        synchronized(cyclicTasks){
            cyclicTasks.remove(task);
        }
    }

    /*
     * @return the awaitedEventList
     */
    protected Set getAwaitedEventList() {
        return awaitedEventList;
    }

    /**
     * @return the awaitedSimEventList
     */
    protected Set getAwaitedSimEventList() {
        return awaitedSimEventList;
    }

    /**
     * @return the firedEventList
     */
    protected Set getFiredEventList() {
        return firedEventList;
    }
    
    protected boolean shutdownTimeExceeded(){
        return (System.nanoTime() - shutdownRequestTime) > maxShutdownTime;
    }

    /**
     * @return the cycleNumber
     */
    public long getCycleNumber() {
        return cycleNumber;
    }

    /**
     * @return the cycleTime
     */
    public long getCycleTime() {
        return cycleTime;
    }
    /**
     * invoked a shutdown of the elbfisch application.
     * If called by a module shutdown() returns immediately. Otherwise it blocks, until the shutdown is acknowledged by jPac
     * @param exitCode 
     */
    
    protected void shutdown(int exitCode, boolean waitForAcknowledgement) {
        if (Log.isInfoEnabled()) Log.info("shutdown requested. Informing jPac ...");
        this.shutdownRequestTime = System.nanoTime();
        this.exitCode            = exitCode;
        shutdownRequest.request(waitForAcknowledgement);
    }

    public void shutdownDeferred(int exitCode) {
        shutdown(exitCode, false);
    }

    public void invokeImmediateShutdown(int exitCode) {
        immediateShutdownRequested = true;
        shutdownRequestTime        = System.nanoTime();
        this.exitCode              = exitCode; 
    }

    public void startCycling() {
        if (Log.isInfoEnabled()) Log.info("startCycling requested");
        startCycling.request(true);
        if (Log.isInfoEnabled()) Log.info("startCycling() acknowledged");
    }
    
    public boolean isNormalShutdownRequested() {
        return shutdownRequest.isRequested();
    }

    public boolean isImmediateShutdownRequested() {
        return immediateShutdownRequested;
    }

    protected void acknowledgeShutdownRequest(){
        if (shutdownRequest.isRequested()){
            shutdownRequest.acknowledge();
        }
        immediateShutdownRequested = false;
    }

    /**
     * @return the emergencyStopRequested
     */
    public boolean isEmergencyStopActive() {
        return emergencyStopActive;
    }

    public void acknowledgeEmergencyStop(){
        emergencyStopActive    = false;
        emergencyStopCausedBy  = null;
    }

    public void requestEmergencyStop(EmergencyStopException causedBy) {
        //set emergencyStopRequested. Will be reset by the automation controller after notification of all modules
        if (!this.emergencyStopActive){
            if (Log.isDebugEnabled()){Log.debug("EMERGENCY STOP REQUESTESD requestEmergencyStop: " + emergencyStopRequested);};
            //set request only once per emergency stop case
            this.emergencyStopRequested = true;
            this.emergencyStopActive    = true;
            this.emergencyStopCausedBy  = causedBy;
        }
    }

    /**
     * @return the emergencyStopCause
     */
    public String getEmergencyStopCause() {
        return emergencyStopCausedBy.getMessage();
    }

    protected void incrementAwakenedModulesCount() {
        incrementCounter++;// debug
        activeEventsLock.increment();
    }    

    protected void decrementAwakenedModulesCount(ProcessEvent awaitedEvent) throws InconsistencyException {
        decrementCounter++;// debug
        if (activeEventsLock.decrement()){
            //if last module went to sleep, store its awaited event
            awaitedEventOfLastModule = awaitedEvent;
        }
    }    
    
    protected void indicateCheckBack(ProcessEvent awaitedEvent) throws InconsistencyException{
        tracePoint = 100;
        if (enableTrace){
            AbstractModule module = awaitedEvent.getObservingModule();
            getTraceQueue().putTrace(cycleNumber, module.getModuleIndex(), module.getWakeUpNanoTime(), module.getSleepNanoTime());
        }
        tracePoint = 101;
        decrementAwakenedModulesCount(awaitedEvent);
        tracePoint = 102;
    }

    protected void synchronizeOnLastModule() throws InconsistencyException {
       if (awaitedEventOfLastModule != null){
           synchronized(awaitedEventOfLastModule){
               //do nothing meaningful.
               //Actually wait, until the module leaves the monitor 
               //on its awaited process event by calling awaitedEvent.wait()
               //Acquire it and leave it instantly
               //Note: see synchronized(this){} block inside ProcessEvent.awaitImpl() 
               long dummy = awaitedEventOfLastModule.getCycleNumber();
           }
       }
    }    
    
    protected void prepareOneCycleMode(){
        startCycle.acquireUninterruptibly();        
    }
    
    protected void waitForStartUpSignal(){
        if (Log.isInfoEnabled()) Log.info("awaiting start up signal ...");
        startCycling.waitForRequest();
        startCycling.acknowledge();
    }

    protected void waitForStartCycleSignal(){
        startCycle.acquireUninterruptibly();
    }
    
    protected void signalEndOfCycle(){
        //signal end of cycle
        cycleEnded.release();
        //aquire start semaphore in preparation of next cycle
        //must be released instantly
        startCycle.acquireUninterruptibly();
    }
    
    public void invokeNextCycle(){
        //aquire end of cycle semaphore in preparation for next cycle
        //must be released instantly
        cycleEnded.acquireUninterruptibly();
        //start cycle
        startCycle.release();
        //wait, until cycle has finished
        cycleEnded.acquireUninterruptibly();
    }

    protected void prepareCycle(){
        //compute cycle time
        cycleStartTime       = System.nanoTime();//the cycle starts now
        expectedCycleEndTime = cycleStartTime + getCycleTime();
//        expectedCycleEndTime = nextCycleStartTime + getCycleTime();
//        if (expectedCycleEndTime < cycleStartTime){
//            //if last cycle exceeded its end time more than one cycle time
//            //sync cycle to current time
//            expectedCycleEndTime = cycleStartTime + getCycleTime();
//        }
//        nextCycleStartTime = expectedCycleEndTime;
        cycleNumber++;
                
        //initialize active event counter
        activeEventsLock.reset();  
        //reset awaited event of last module
        awaitedEventOfLastModule = null;
        
        //debugging info
        incrementCounter = 0;
        decrementCounter = 0;
    }
    
    protected void traceCycle(){
        if (enableTrace){
            getTraceQueue().putTrace(cycleNumber, OWNMODULEINDEX, cycleStartTime, System.nanoTime());
        }
    }

    protected void initializeCycle(){
        //set cycleEndTime, nextCycleStartTime to actual time, as if I am at the end of a previous cycle
        expectedCycleEndTime = System.nanoTime();
//        nextCycleStartTime   = expectedCycleEndTime;
    }

    protected void wait4EndOfCycle(long remainingCycleTime){
        boolean done = remainingCycleTime <= 0;
        while(!done){
            try{    
                Thread.sleep(remainingCycleTime / 1000000l, (int)(remainingCycleTime % 1000000));
                done = true;
            }
            catch(InterruptedException exc){};
        }
    }
    
    protected void acquireStatistics(long remainingCycleTime){
        if(cycleNumber > 1){
            //acquire some statistics concerning the cycle time
            if (remainingCycleTime > getCycleTime()){
                remainingCycleTime = getCycleTime();
            }
            else if (remainingCycleTime < 0){
                remainingCycleTime = 0;
            }
            if (minRemainingCycleTime > remainingCycleTime)
                minRemainingCycleTime = remainingCycleTime;
            else if (maxRemainingCycleTime < remainingCycleTime){
                maxRemainingCycleTime = remainingCycleTime;
            }
        }
    }
    
    public ArrayList logStatistics(){
        ArrayList lines = new ArrayList<>();
        if (minRemainingCycleTime < 0){
            minRemainingCycleTime = 0;
        }
        if (maxRemainingCycleTime > getCycleTime()){
            maxRemainingCycleTime = getCycleTime();
        }
        
        int percentageMinRemainingCycleTime = (int)(100L * minRemainingCycleTime/getCycleTime());
        int percentageMaxRemainingCycleTime = (int)(100L * maxRemainingCycleTime/getCycleTime());
        int percentageCyclesExceeded        = (int)(100L * numberOfCyclesExceeded/getCycleNumber());        
        lines.add("cycle mode                : " + getCycleMode());
        lines.add("cycle time                : " + getCycleTime() + " ns");
        if (getCycleMode() != CycleMode.FreeRunning){
            lines.add("min remaining cycle time  : " + minRemainingCycleTime + " ns (" + percentageMinRemainingCycleTime + "%)");
            lines.add("max remaining cycle time  : " + maxRemainingCycleTime + " ns (" + percentageMaxRemainingCycleTime + "%)");
        }
        if (getCycleMode() == CycleMode.LazyBound){
            lines.add("number of cycles exceeded : " + numberOfCyclesExceeded + " of " + getCycleNumber() + " (" + percentageCyclesExceeded + "%)");
        }
        return lines;
    }
    
    public ArrayList showStateOfModule(String qualifiedName){
        ArrayList lines = new ArrayList<>();
        AbstractModule module = getModules().values().stream().filter(m -> m.getQualifiedName().equals(qualifiedName)).findFirst().get();
        if (module != null){
            CharString[] cs = module.getStackTraceSignals();
            for (int i = 0; i < cs.length; i++){
                if (cs[i].isValid()){
                    try{lines.add(cs[i].get());}catch(SignalInvalidException exc){/*cannot happen*/};
                }
                else{
                    break;
                }
            }
        }
        return lines;
    }

    public ArrayList getHistograms(){
        ArrayList histograms = new ArrayList<>();
        histograms.add(cycleHistogram);
        histograms.add(systemHistogram);
        histograms.add(modulesHistogram);
        getModules().values().stream().sorted((m1,m2) -> m1.getQualifiedName().compareTo(m2.getQualifiedName())).forEach(m -> histograms.add(m.getHistogram()));
        return histograms;
    }
    
    public Snapshot getSnapshot(){
        Snapshot snapshot = new Snapshot();    
        return snapshot;
    }

    protected void setStatus(Status status){
        this.status = status;
    }

    public Status getStatus(){
        return this.status;
    }
    
    public String getDataDir(){
        return this.DATADIR;
    }
    
    public String getCfgDir(){
        return this.CFGDIR;
    }

    public String getHistogramFile(){
        return this.histogramFile;
    }
    
    protected int register(AbstractModule module)throws ModuleAlreadyRegisteredException{
        if (!moduleList.containsKey(module.getQualifiedName())){
            moduleList.put(module.getQualifiedName(), module);
        }
        else{
            throw new ModuleAlreadyRegisteredException(module.getQualifiedName());
        }
        return moduleList.size() - 1;
    }
    
    protected void prepareTrace() throws InvalidPropertyException{
        if (enableTrace){
           if (traceTimeMinutes <= 0){
               throw new InvalidPropertyException("traceTimeMinutes must be a positive integer:" + traceTimeMinutes);
           }
           if (Log.isInfoEnabled()) Log.info("tracing enabled");
           //determine estimated number of entries in relation to the given trace interval
           //assuming that one sample (ModuleTrace) will be generated per cycle
           int numberOfEntries = (int)(traceTimeMinutes * 60000000000L / cycleTime);
           traceQueue = new TraceQueue(numberOfEntries);
        }
    }
    
    @SuppressWarnings("deprecation")
	protected void prepareRemoteConnections() throws ConfigurationException, RemoteException, RemoteSignalException{
        if (remoteSignalsEnabled){
            //start serving incoming remote signal requests
            RemoteSignalServer.start(remoteSignalPort);
            //instantiate outgoing remote signals
            ConcurrentHashMap remoteHosts = RemoteSignalRegistry.getInstance().getRemoteHosts();
            for (Entry entry: remoteHosts.entrySet()){
                entry.getValue().open();
            }
        }
    }
    
    @SuppressWarnings("deprecation")
	protected void closeRemoteConnections(){
        final long CLOSECONNECTIONTIMEOUT = 3000000000L; // 3 sec
        try{
            if (remoteSignalsEnabled){
                if (Log.isInfoEnabled()) Log.info("closing remote connections ...");
                //invoke closure of all open remote connections
                ConcurrentHashMap remoteHosts = RemoteSignalRegistry.getInstance().getRemoteHosts();
                for (Entry entry: remoteHosts.entrySet()){
                    entry.getValue().close();
                }
                //wait for all connections to close
                for (Entry entry: remoteHosts.entrySet()){
                    long timeoutTime = System.nanoTime() + CLOSECONNECTIONTIMEOUT;
                    while(!entry.getValue().isClosed() && System.nanoTime() < timeoutTime);
                    if (!entry.getValue().isClosed()){
                        Log.error("   failed to close remote connection to " + entry.getValue().getRemoteJPacInstance());
                    }
                }                    
                RemoteSignalRegistry.getInstance().stopWatchdog();
                if (Log.isInfoEnabled()) Log.info("... closing of remote connections done");
            }
        }
        catch(Exception exc){
            Log.error("Error:", exc);
        }
        catch(Error exc){
            Log.error("Error:", exc);
        }
    }

    protected void prepareCyclicTasks(){
         synchronized(cyclicTasks){
            for(CyclicTask ct: cyclicTasks){
                //prepare cyclic task
                ct.prepare();
            }            
        }
    }
    
    protected void stopCyclicTasks(){
        boolean atLeastOneCyclicTaskRunning = false;
        int     ticks                       = 0;
        synchronized(cyclicTasks){
            //stop all cyclic tasks
            for(CyclicTask ct: cyclicTasks){
                ct.stop();
            }            
            //wait for all cyclic tasks coming to an end
            do{
                atLeastOneCyclicTaskRunning = false;
                for(CyclicTask ct: cyclicTasks){
                    atLeastOneCyclicTaskRunning = atLeastOneCyclicTaskRunning || !ct.isFinished();
                }  
                if (atLeastOneCyclicTaskRunning){
                    try{Thread.sleep(100);}catch(InterruptedException exc){}
                }
                ticks++;
            }
            while(atLeastOneCyclicTaskRunning && ticks < (cyclicTaskShutdownTimeoutTime/100000000L));
            if (atLeastOneCyclicTaskRunning){
                for(CyclicTask ct: cyclicTasks){
                    if (!ct.isFinished()){
                        Log.error("cyclic task " + ct.getClass().getCanonicalName() + " did not stop in time.");
                    }
                }  
            }
        }
    }

    protected void prepareOpcUaService() throws Exception{
        if (opcUaServiceEnabled){
            opcUaService = new OpcUaService(opcUaServiceName, opcUaBindAddresses, opcUaServicePort, opcUaMinSupportedSampleInterval);
            opcUaService.start();
            Log.info("OPC UA service started");
        }
    }
    
    protected void stopOpcUaService() {
        //stop opc server, if running
        if (opcUaService != null){
            Log.info("stopping opc ua service ...");
            if (opcUaService.getServer() != null){
            	try {
            		opcUaService.getServer().shutdown().get(5000, TimeUnit.MILLISECONDS);
            	} catch(InterruptedException | ExecutionException | TimeoutException exc) {
            		Log.error("Error: Failed to properly stop opc ua service", exc);
            	}
                Stack.releaseSharedResources();
            }
            Log.info("opc ua service stopped");
        }        
    }
    
    protected void prepareEfService() throws Exception{
        if (efServiceEnabled){
            efService = new EfService(false, efBindAddress, efServicePort, efReceiveBufferSize);
            efService.start();
            Log.info("Elbfisch communication service started");
        }
    }
    
    protected void stopEfService() {
        //stop opc server, if running
        if (efService != null){
            Log.info("stopping Elbfisch communication service ...");
            efService.stop();
            Log.info("Elbfisch communication service stopped");
        }        
    }

    protected void prepareConsoleService() throws Exception{
        if (consoleServiceEnabled){
            consoleService = new TelnetService(false, consoleBindAddress, consoleServicePort);
            Log.info("console service started");            
        }
    }

    protected void stopConsoleService(){
        //stop console server, if running
        if (consoleService != null){
            Log.info("stopping console service ...");
            consoleService.stop();
            Log.info("console service stopped");
        }        
    }

    public Boolean cleanUpConfiguration() throws ConfigurationException{
        boolean done = false;
        try{
            Configuration.getInstance().cleanUp();
            done = true;
        }
        catch(ConfigurationException exc){
            Log.error("Error:", exc);
        }
        return done;
    }
    
    @SuppressWarnings("deprecation")
	protected void pushSignalsOverRemoteConnections() throws ConfigurationException, RemoteSignalException{
        if (remoteSignalsEnabled){
            ConcurrentHashMap remoteHosts = RemoteSignalRegistry.getInstance().getRemoteHosts();
            //remoteHosts.entrySet().iterator()
            for (Entry entry: remoteHosts.entrySet()){
                entry.getValue().pushSignals(cycleNumber);
            }
        }        
    }
    
    /**
     * @return the traceQueue
     */
    public TraceQueue getTraceQueue() {
        return traceQueue;
    }
    
    protected void prepareHistogramms(){
        cycleHistogram   = new Histogram("overall cycle", cycleTime);
        systemHistogram  = new Histogram("system load", cycleTime);
        modulesHistogram = new Histogram("modules load", cycleTime);
    }
    
    public Histogram getSystemHistogramm(){
        return systemHistogram;
    }
    public Histogram getModulesHistogramm(){
        return modulesHistogram;
    }

    /**
     * 
     * @return the systems nanotime synchronized to the current cycle.
     */
    public long getCycleNanoTime(){
        return cycleStartTime;
    }
    
    /**
     * 
     * @return the start time of the current cycle (ns) corrected by the time spent on break points during a debug session
     */
    public long getExpandedCycleNanoTime(){
        return cycleStartTime - expansionTime;
    }
    
//    public AbstractModule getModule(int i){
//        return moduleList.get(i);
//    }

    public Hashtable getModules(){
        return moduleList;
    }

    /**
     * @return the cycleMode
     */
    public CycleMode getCycleMode() {
        return cycleMode;
    }
    
    public String getInstanceIdentifier(){
        return instanceIdentifier;
    }
    
    public void setEmergencyStopExceptionCausedBy(EmergencyStopException causedBy){
        this.emergencyStopCausedBy = causedBy;
    }
 
    public EmergencyStopException getEmergencyStopExceptionCausedBy(){
        return this.emergencyStopCausedBy;
    }
    
    public AbstractModule getProcessedModule(){
        return this.processedModule;
    }
    
    public void stopBeforeStartUp(){
        if (Log.isInfoEnabled()) Log.info("aborting jPac before startup");
        this.stopBeforeStartup = true;
        startCycling.request(true);
    }
    
    public Opc.AccessLevel getOpcUaDefaultAccesslevel(){
        return this.opcUaDefaultAccessLevel;
    }
        
    public String getVersion(){
        return versionNumber;
    }
    
    public String getBuild(){
        return buildNumber;
    }

    public String getBuildDate(){
        return buildDate;
    }
    
    public String getProjectName(){
        return projectName;
    }

    public boolean isEfServiceEnabled() {
		return efServiceEnabled;
	}

	public int getEfServicePort() {
		return efServicePort;
	}

	public String getEfBindAddress() {
		return efBindAddress;
	}

	protected void waitUntilShutdownComplete(){
        int times100ms;
        for(times100ms = 0; times100ms < 100 && !readyToShutdown; times100ms++){
            try {Thread.sleep(100);}catch(InterruptedException ex) {}
        }
        if (times100ms >= 100){
            //automation controller did not finish in time
            Log.error("jPac stopped abnormaly !");
        }            
    }

    /**
     * used to shutdown the Elbfisch environment by an embedding application. Blocks, until shutdown has completed.
     * HINT: Do not use it from inside an Elbfisch module. Use Module.shutdown(exit code) instead.
     */
    public void shutdownGraceFully() {
        switch (status){
            case initializing:
            case ready:
                //JPac has been initialized but did not start cycling
                stopBeforeStartUp();
                waitUntilShutdownComplete();                    
                break;
            case running:
                if (!readyToShutdown){
                    shutdown(EXITCODENORMALSHUTDOWN, true);
                    waitUntilShutdownComplete();                    
                }
                break;
            case halted:
                //nothing to do
                break;
        }
    }
        
    class ShutdownHook extends Thread{
        public ShutdownHook(){
            setName(getClass().getSimpleName());
        }
        
        @Override
        public void run() {
            shutdownGraceFully();
        }
    }
    
    protected class CountingLock{
        private       int    count       = 0;
        private       int    maxcount    = 0;
        private final Object zeroReached = new Object();
        
        protected void increment(){
            synchronized(zeroReached){
                count++;
                maxcount++;
            }
        }
        
        protected boolean decrement() throws InconsistencyException{
            synchronized(zeroReached){
                count--;
                if (count < 0){
                    throw new InconsistencyException("CountingLock.decrement(): module counter inconsistent !!!!!!");
                }
                if (count == 0){
                    zeroReached.notify();
                }
            }
            return count == 0;
        }
        
        protected int getCount(){
            int localCount = 0;
            synchronized(zeroReached){
                localCount = count;
            }
            return localCount;
        }
        
        protected int getMaxCount(){
            int localCount = 0;
            synchronized(zeroReached){
                localCount = maxcount;
            }
            return localCount;
        }

        protected void reset(){
            synchronized(zeroReached){
                count    = 0;
                maxcount = 0;
            }
        }
        
        protected void waitForUnlock(){
            synchronized(zeroReached){
                while(count > 0 && !shutdownRequest.isRequested()){
                    try{zeroReached.wait(100);}catch(InterruptedException exc){};
                }
            }            
        }
        
        protected void waitForUnlock(long waittime){//nanoseconds
            boolean waitReturned = false;
            if (waittime > 0){
                synchronized(zeroReached){
                    while(count > 0 && !waitReturned){
                        try{
                            zeroReached.wait(waittime / 1000000L, (int)(waittime % 1000000));
                            //wait returned normally or due to timeout
                            waitReturned = true;
                        }catch(InterruptedException exc){};
                    }
                }
            }
            else{
                if (Log.isDebugEnabled()) Log.debug("cycle time expired before synchronization on modules !!!!!!: " + (-waittime) + " ns");
            }
        }
    }
    
    protected class Synchronisation{
        private boolean       requested                = false;
        private boolean       acknowledged             = false;
        private final Object  syncPointRequest         = new Object();
        private final Object  syncPointAcknowledgement = new Object();
        
        protected void waitForRequest(){
            synchronized(syncPointRequest){
                while(!requested){
                    try{syncPointRequest.wait();}catch(InterruptedException exc){};
                }
                requested = false;
            }            
        }
        
        protected void acknowledge(){
            synchronized(syncPointAcknowledgement){
                requested    = false;
                acknowledged = true;
                syncPointAcknowledgement.notify();
            }
        }

        protected void request(boolean waitForAcknowledgement){
            synchronized(syncPointRequest){
               acknowledged = false;
               requested    = true;
               syncPointRequest.notify();
            }
            if (waitForAcknowledgement){
                synchronized(syncPointAcknowledgement){
                    while(!acknowledged){
                        try{syncPointAcknowledgement.wait();}catch(InterruptedException exc){};
                    }
                   acknowledged = false;
                }
            }
        }
        
        protected boolean isRequested(){
            return requested;
        }
        
    }

    /**
     * used to hold the latest tracing information for a given number of execution cycles.
     */
    public class TraceQueue extends ArrayBlockingQueue{
        private int  numberOfEntries;
                
        protected TraceQueue(int numberOfEntries){
            super(numberOfEntries);
            this.numberOfEntries = numberOfEntries;
        }
                
        public void putTrace(long cycleNumber, int moduleIndex, long startNanos, long endNanos) {
            ModuleTrace moduleTrace;
            synchronized(this){
                moduleTrace = getNextModuleTrace();
                moduleTrace.setCycleNumber(cycleNumber);
                moduleTrace.setModuleIndex(moduleIndex);
                moduleTrace.setStartNanos(startNanos);
                moduleTrace.setEndNanos(endNanos);
                //put the actual entry
                try{super.put(moduleTrace);} catch(InterruptedException exc){/*cannot happen*/}
            }
        }
        private ModuleTrace getNextModuleTrace(){
            ModuleTrace moduleTrace;
            if(remainingCapacity() == 0){
                //queue is full, remove the oldest entry (head)
                //and recycle it
                moduleTrace = poll();
            }
            else{
                //create an new module trace record
                moduleTrace = new ModuleTrace();
            }
            return moduleTrace;
        }
    }

    public class ModuleTrace{
        private long cycleNumber;
        private long moduleIndex;
        private long startNanos;
        private long endNanos;
                
        public long getStartNanos() {
            return startNanos;
        }

        public long getEndNanos() {
            return endNanos;
        }
        
        protected void setCycleNumber(long cycleNumber){
            this.cycleNumber = cycleNumber;
        }
        protected void setModuleIndex(int moduleIndex){
            this.moduleIndex = moduleIndex;
        }
        protected void setStartNanos(long nanos){
            this.startNanos = nanos;
        }
        protected void setEndNanos(long nanos){
            this.endNanos = nanos;
        }
        
        public String toCSV(){
            StringBuffer csvString = new StringBuffer();
            csvString.append(cycleNumber);csvString.append(';');
            csvString.append(moduleIndex);csvString.append(';');
            csvString.append(startNanos);csvString.append(';');
            csvString.append(endNanos);csvString.append(';');
            return csvString.toString();
        }
    }
    
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy