Java tutorial
/** * 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 <Kuerzel> <datum> <Beschreibung> * * 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 <http://www.gnu.org/licenses/>. */ package org.jpac; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.net.InetAddress; import java.net.UnknownHostException; import java.rmi.RemoteException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; 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.Semaphore; import org.apache.commons.configuration.ConfigurationException; import org.apache.log4j.Logger; import org.jpac.configuration.BooleanProperty; import org.jpac.configuration.Configuration; import org.jpac.configuration.IntProperty; import org.jpac.configuration.LongProperty; import org.jpac.configuration.StringProperty; import org.jpac.statistics.Histogramm; /** * central runtime engine of JPac * @author berndschuster */ public class JPac extends Thread { static Logger Log = Logger.getLogger("jpac.JPac"); private final int OWNMODULEINDEX = 0; private final long DEFAULTCYCLETIME = 100000000L; // 100 ms private final long DEFAULTCYCLETIMEOUTTIME = 1000000000L;// 1 s private final long MAXSHUTDOWNTIME = 2000; // 2 s private final CycleMode DEFAULTCYCLEMODE = CycleMode.FreeRunning;//TODO !!!! CycleMode.FreeRunning; private final int EXITCODEINITIALIZATIONERROR = 100; private final int EXITCODEINTERNALERROR = 101; private final long DEFAULTCYCLICTASKSHUTDOWNTIMEOUTTIME = 5000000000L; //5 s 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<Fireable> awaitedEventList; private Set<Fireable> awaitedSimEventList; private Set<Fireable> firedEventList; private long minRemainingCycleTime; private long maxRemainingCycleTime; private long expectedCycleEndTime; private long cycleStartTime; private long numberOfCyclesExceeded; // private long nextCycleStartTime; 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 running; private final List<Runnable> synchronizedTasks; private final List<CyclicTask> cyclicTasks; private LongProperty propCycleTime; private LongProperty propCycleTimeoutTime; private StringProperty propCycleMode; private BooleanProperty propRunningInsideAnIde; private BooleanProperty propRunningInjUnitTest; private BooleanProperty propEnableTrace; private BooleanProperty propPauseOnBreakPoint; private IntProperty propTraceTimeMinutes; private BooleanProperty propRemoteSignalsEnabled; private IntProperty propRemoteSignalPort; private BooleanProperty propStoreHistogrammsOnShutdown; private StringProperty propHistogrammFile; private LongProperty propCyclicTaskShutdownTimeoutTime; private String instanceIdentifier; private long cycleTime; private long cycleTimeoutTime; private CycleMode cycleMode; private boolean runningInsideAnIde; private boolean runningInjUnitTest; private boolean enableTrace; private boolean pauseOnBreakPoint; private int traceTimeMinutes; private boolean remoteSignalsEnabled; private int remoteSignalPort; private boolean storeHistogrammsOnShutdown; private String histogrammFile; private long cyclicTaskShutdownTimeoutTime; private CountingLock activeEventsLock; private Synchronisation startCycling; private Synchronisation stopCycling; private Synchronisation shutdownRequest; private boolean shutdownByMySelf; private int exitCode; private ProcessEvent awaitedEventOfLastModule; private ArrayList<AbstractModule> moduleList; private TraceQueue traceQueue; private Histogramm cycleHistogramm; //used to determine the overall load per cycle private Histogramm systemHistogramm; //used to determine the system load during a cycle private Histogramm modulesHistogramm; //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; class ShutdownHook extends Thread { public ShutdownHook() { setName(getClass().getSimpleName()); } private 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 !"); } } @Override public void run() { if (Log.isInfoEnabled()) Log.info("shutdown requested. Informing jPac ..."); // public enum Status{initializing, ready, running, halted}; switch (status) { case initializing: case ready: //JPac has been initialized but did start cycling stopBeforeStartUp(); waitUntilShutdownComplete(); break; case running: if (!readyToShutdown) { shutdownRequest.request(); waitUntilShutdownComplete(); } else { if (Log.isInfoEnabled()) Log.info("automation controller already shutdown"); } break; case halted: //nothing to do break; } if (Log.isInfoEnabled()) Log.info("shutdown complete"); } } 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() { synchronized (syncPointRequest) { acknowledged = false; requested = true; syncPointRequest.notify(); } 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<ModuleTrace> { 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(); } } protected JPac() { super(); setName(getClass().getSimpleName()); tracePoint = 0; minRemainingCycleTime = Long.MAX_VALUE; maxRemainingCycleTime = 0; expectedCycleEndTime = 0; cycleStartTime = 0; // nextCycleStartTime = 0; status = Status.initializing; cycleNumber = 0; awaitedEventList = Collections.synchronizedSet(new HashSet<Fireable>()); awaitedSimEventList = Collections.synchronizedSet(new HashSet<Fireable>()); firedEventList = new HashSet<Fireable>(); readyToShutdown = false; emergencyStopRequested = false; emergencyStopActive = false; emergencyStopIsToBeThrown = false; emergencyStopCausedBy = null; synchronizedTasks = Collections.synchronizedList(new ArrayList<Runnable>()); cyclicTasks = Collections.synchronizedList(new ArrayList<CyclicTask>()); startCycle = new Semaphore(1); cycleEnded = new Semaphore(1); startCycling = new Synchronisation(); stopCycling = new Synchronisation(); shutdownRequest = new Synchronisation(); running = false; activeEventsLock = new CountingLock(); awaitedEventOfLastModule = null; moduleList = new ArrayList<AbstractModule>(20); traceQueue = null; cycleHistogramm = null; systemHistogramm = null; modulesHistogramm = 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.FreeRunning.toString(), "[OneCycle | Bound | LazyBound | FreeRunning]", true); propRunningInsideAnIde = new BooleanProperty(this, "RunningInsideAnIde", false, "will pop up a small window to close the application", true); propRunningInjUnitTest = new BooleanProperty(this, "RunningInjUnitTest", false, "helpful, if jPac is run in a jUnit test", 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); propStoreHistogrammsOnShutdown = new BooleanProperty(this, "storeHistogrammsOnShutdown", false, "enables storing of histogramm data on shutdown", true); propHistogrammFile = new StringProperty(this, "HistogrammFile", "./data/histogramm.csv", "file in which the histogramms are stored", true); propCyclicTaskShutdownTimeoutTime = new LongProperty(this, "CyclicTaskShutdownTimeoutTime", DEFAULTCYCLICTASKSHUTDOWNTIMEOUTTIME, "Timeout for all cyclic tasks to stop on shutdown [ns]", true); instanceIdentifier = InetAddress.getLocalHost().getHostName() + ":" + propRemoteSignalPort.get(); cycleTime = propCycleTime.get(); cycleTimeoutTime = propCycleTimeoutTime.get(); cycleMode = CycleMode.valueOf(propCycleMode.get()); runningInsideAnIde = propRunningInsideAnIde.get(); runningInjUnitTest = propRunningInjUnitTest.get(); enableTrace = propEnableTrace.get(); traceTimeMinutes = propTraceTimeMinutes.get(); pauseOnBreakPoint = propPauseOnBreakPoint.get(); remoteSignalsEnabled = propRemoteSignalsEnabled.get(); remoteSignalPort = propRemoteSignalPort.get(); storeHistogrammsOnShutdown = propStoreHistogrammsOnShutdown.get(); histogrammFile = propHistogrammFile.get(); cyclicTaskShutdownTimeoutTime = propCyclicTaskShutdownTimeoutTime.get(); //install configuration saver try { registerCyclicTask(Configuration.getInstance().getConfigurationSaver()); } catch (WrongUseException exc) { /*cannot happen*/} } catch (UnknownHostException ex) { Log.error("Error: ", ex); //properties cannot be initialized //kill application System.exit(99); } catch (ConfigurationException 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 histogramm long modulesLoadStartTime = 0L; //used to compute the modules histogramm 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(); prepareRemoteConnections(); prepareCyclicTasks(); } catch (Exception exc) { //if an error occured during preparation of the system, shutdown immediately Log.error("Error: ", exc); invokeShutdownByMySelf(EXITCODEINITIALIZATIONERROR); } catch (Error exc) { //if an error occured during preparation of the system, shutdown immediately Log.error("Error: ", exc); invokeShutdownByMySelf(EXITCODEINITIALIZATIONERROR); } if (Log.isInfoEnabled()) Log.info("running in " + cycleMode + " mode ..."); setStatus(Status.running); running = true; 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 histogramm information modulesLoadStartTime = System.nanoTime(); systemHistogramm.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 histogramm information modulesHistogramm.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 histogramm information cycleHistogramm.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); invokeShutdownByMySelf(EXITCODEINTERNALERROR); } catch (Error ex) { Log.error("Error", ex); invokeShutdownByMySelf(EXITCODEINTERNALERROR); } //check, if the application is to be shutdown if (isShutdownRequested()) { done = true; //check, if cycling is to be stopped } else if (isStopCyclingRequested()) { if (Log.isInfoEnabled()) Log.info("stop cycling ..."); //acknowledge request (see stopCycling()) acknowledgeStopRequest(); done = true; } if (getCycleMode() == CycleMode.OneCycle) { signalEndOfCycle(); } //trace cycle statistics, if applicable traceCycle(); } while (!done); if (storeHistogrammsOnShutdown) { storeHistogramms(); } //shutdown all active modules shutdownAwaitingModules(getAwaitedEventList()); //shutdown RemoteSignalConnection's closeRemoteConnections(); //clean up context of registered cyclic tasks stopCyclicTasks(); //acknowledge request acknowledgeShutdownRequest(); //new state is halted setStatus(Status.halted); logStatistics(); // if(storeConfigOnShutdown){ // try{ // if (Log.isInfoEnabled()) Log.info("saving the configuration ..."); // Configuration.getInstance().save(); // if (Log.isInfoEnabled()) Log.info("... saving of the configuration done"); // } // catch(ConfigurationException exc){ // Log.error("Error: while saving the configuration",exc); // } // } if (Log.isInfoEnabled()) 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 (!runningInjUnitTest) { System.exit(exitCode); } } catch (Exception exc) { Log.error("Error: ", exc); } catch (Error exc) { Log.error("Error: ", exc); } }; private void handleFireables(Set<Fireable> fireableList) throws SomeEventsNotProcessedException, InconsistencyException { boolean fired = false; //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 boolean fFired = f.evaluateFiredCondition(); boolean fTimedOut = (f instanceof ProcessEvent) && ((ProcessEvent) f).evaluateTimedOutCondition(); fired = fFired || (f instanceof ProcessEvent && (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 && 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); } } private void handleInEveryCycleDos() { try { for (AbstractModule module : moduleList) { //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(); 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 shutdownAwaitingModules(Set<Fireable> 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(); } } /** * used to propagate signal states to connected signal instances */ 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(); } synchronized (cyclicTasks) { for (CyclicTask ct : cyclicTasks) { //run cyclic task ct.run(); } } List<Signal> signals = SignalRegistry.getInstance().getSignals(); synchronized (signals) { for (Signal s : signals) { //handle requested (dis)connections of signals s.handleConnections(); //propagate signal alterations s.propagate(); } } pushSignalsOverRemoteConnections(); } /* * 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(Runnable task) { synchronized (cyclicTasks) { cyclicTasks.remove(task); } } /* * @return the awaitedEventList */ protected Set<Fireable> getAwaitedEventList() { return awaitedEventList; } /** * @return the awaitedSimEventList */ protected Set<Fireable> getAwaitedSimEventList() { return awaitedSimEventList; } /** * @return the firedEventList */ protected Set<Fireable> getFiredEventList() { return firedEventList; } /** * @return the cycleNumber */ public long getCycleNumber() { return cycleNumber; } /** * @return the cycleTime */ public long getCycleTime() { return cycleTime; } public void shutdown() { shutdownRequest.request(); instance = null; //discard actual instance } public void invokeShutdownByMySelf(int exitCode) { shutdownByMySelf = true; this.exitCode = exitCode; } public void startCycling() { if (Log.isInfoEnabled()) Log.info("startCycling requested"); startCycling.request(); if (Log.isInfoEnabled()) Log.info("startCycling() acknowledged"); } public void stopCycling() { if (Log.isInfoEnabled()) { Log.info("stopCycling requested"); } ; stopCycling.request(); if (Log.isInfoEnabled()) { Log.info("stopCycling acknowledged"); } ; } protected boolean isStopCyclingRequested() { return stopCycling.isRequested(); } protected void acknowledgeStopRequest() { stopCycling.acknowledge(); } /** * @return the shutdownRequested */ public boolean isShutdownRequested() { return shutdownRequest.isRequested() || shutdownByMySelf; } protected void acknowledgeShutdownRequest() { if (shutdownRequest.isRequested()) { shutdownRequest.acknowledge(); } shutdownByMySelf = false; } /** * @return the emergencyStopRequested */ public boolean isEmergencyStopActive() { return emergencyStopActive; } public void acknowledgeEmergencyStop() { emergencyStopActive = false; emergencyStopCausedBy = null; } /** * @param emergencyStopRequested the emergencyStopRequested to set */ 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); } ;//TODO //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) {//TODO: (SN) vorlaeufig gepatched wg. Zyklus 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; } } } protected void logStatistics() { 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()); if (Log.isInfoEnabled()) { Log.info("cycle mode : " + getCycleMode()); Log.info("cycle time : " + getCycleTime() + " ns"); if (getCycleMode() != CycleMode.FreeRunning) { Log.info("min remaining cycle time : " + minRemainingCycleTime + " ns (" + percentageMinRemainingCycleTime + "%)"); Log.info("max remaining cycle time : " + maxRemainingCycleTime + " ns (" + percentageMaxRemainingCycleTime + "%)"); } if (getCycleMode() == CycleMode.LazyBound) { Log.info("number of cycles exceeded : " + numberOfCyclesExceeded + " of " + getCycleNumber() + " (" + percentageCyclesExceeded + "%)"); } } } protected void storeHistogramms() { File file = new File(histogrammFile); try { if (Log.isInfoEnabled()) Log.info("storing histogramm informationen to " + histogrammFile); PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)), true); out.println("Cycle overall;" + cycleHistogramm.toCSV()); out.println("System;" + systemHistogramm.toCSV()); out.println("Modules;" + modulesHistogramm.toCSV()); for (Fireable f : getAwaitedEventList()) { AbstractModule module = f.getObservingModule(); StringBuffer sb = new StringBuffer(); sb.append(module.getQualifiedName()).append(';').append(module.getHistogramm().toCSV()); out.println(sb); } out.close(); } catch (IOException exc) { Log.error("Error: ", exc); } } protected void setStatus(Status status) { this.status = status; } public Status getStatus() { return this.status; } protected int register(AbstractModule module) { moduleList.add(module); 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); } } protected void prepareRemoteConnections() throws ConfigurationException, RemoteException, RemoteSignalException { if (remoteSignalsEnabled) { //start serving incoming remote signal requests RemoteSignalServer.start(remoteSignalPort); //instantiate outgoing remote signals ConcurrentHashMap<String, RemoteSignalConnection> remoteHosts = RemoteSignalRegistry.getInstance() .getRemoteHosts(); for (Entry<String, RemoteSignalConnection> entry : remoteHosts.entrySet()) { entry.getValue().open(); } } } 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<String, RemoteSignalConnection> remoteHosts = RemoteSignalRegistry.getInstance() .getRemoteHosts(); for (Entry<String, RemoteSignalConnection> entry : remoteHosts.entrySet()) { entry.getValue().close(); } //wait for all connections to close for (Entry<String, RemoteSignalConnection> 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 pushSignalsOverRemoteConnections() throws ConfigurationException, RemoteSignalException { if (remoteSignalsEnabled) { ConcurrentHashMap<String, RemoteSignalConnection> remoteHosts = RemoteSignalRegistry.getInstance() .getRemoteHosts(); //remoteHosts.entrySet().iterator() for (Entry<String, RemoteSignalConnection> entry : remoteHosts.entrySet()) { entry.getValue().pushSignals(cycleNumber); } } } /** * @return the traceQueue */ public TraceQueue getTraceQueue() { return traceQueue; } protected void prepareHistogramms() { cycleHistogramm = new Histogramm(cycleTime); systemHistogramm = new Histogramm(cycleTime); modulesHistogramm = new Histogramm(cycleTime); } public Histogramm getSystemHistogramm() { return systemHistogramm; } public Histogramm getModulesHistogramm() { return modulesHistogramm; } /** * * @return the systems nanotime synchronized to the current cycle. */ public long getCycleNanoTime() { return cycleStartTime; } public AbstractModule getModule(int i) { return moduleList.get(i); } /** * @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(); } }