/************************************************************************** * File name : MissionSequencer.java * * This file is part a SCJ Level 0 and Level 1 implementation, * based on SCJ Draft, Version 0.94 25 June 2013. * * It is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This SCJ Level 0 and Level 1 implementation 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this SCJ Level 0 and Level 1 implementation. * If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012 * @authors Anders P. Ravn, Aalborg University, DK * Stephan E. Korsholm and Hans Søndergaard, * VIA University College, DK *************************************************************************/ package javax.safetycritical; import javax.realtime.AperiodicParameters; import javax.realtime.PriorityParameters; import javax.safetycritical.annotate.Level; import javax.safetycritical.annotate.Phase; import javax.safetycritical.annotate.SCJAllowed; import javax.safetycritical.annotate.SCJRestricted; /** * A <code>MissionSequencer</code> oversees a sequence of Mission executions. * The sequence may include interleaved execution of independent missions * and repeated executions of missions.<p> * As a subclass of <code>ManagedEventHandler</code>, <code>MissionSequencer</code> * has an execution priority and memory budget as specified by constructor parameters. <p> * The <code>MissionSequencer</code> executes vendor-supplied infrastructure code which * invokes user-defined implementations of the method <code>getNextMission</code>, and for each mission * the user-defined <code>initialize</code> and <code>cleanUp</code> methods. <p> * During execution of an inner-nested mission, the <code>MissionSequencer</code> remains blocked waiting for * the mission to terminate. An invocation of <code>requestSequenceTermination</code> will unblock it * so that it can perform an invocation of the running mission's <code>requestTermination</code> method, * if the mission is still running and its termination has not already been requested. <p> * Note that if a <code>MissionSequencer</code> object is preallocated by the application, it * must be allocated in the same scope as its corresponding <code>Mission</code>s. * * @version 1.2; - December 2013 * * @author Anders P. Ravn, Aalborg University, <A * HREF="mailto:apr@cs.aau.dk">apr@cs.aau.dk</A>, <br> * Hans Søndergaard, VIA University College, Denmark, <A * HREF="mailto:hso@viauc.dk">hso@via.dk</A> * * @scjComment */ @SCJAllowed public abstract class MissionSequencer<MissionType extends Mission> extends ManagedEventHandler { MissionMemory missionMemory; MissionType currMission; interface State { public final static int START = 1; public final static int INITIALIZE = 2; public final static int EXECUTE = 3; public final static int CLEANUP = 4; public final static int TERMINATE = 5; public final static int END = 6; } int currState; boolean terminateSeq = false; static volatile boolean isOuterMostSeq = true; Monitor lock; static ScjProcess missSeqProcess = null; static int howManyMissions = -1; // for testing only // Level2 only: a reference to the nearest outer sequencer MissionSequencer<?> outerSeq = null; /** * Constructs a <code>MissionSequencer</code> to run at the priority and * with the memory resources specified by its parameters. */ // /*@ // also // public exceptional_behaviour // requires ??; // not invoked in appropriate phase // signals (IllegalStateException) true; // @*/ @SCJAllowed @SCJRestricted(Phase.INITIALIZE) public MissionSequencer(PriorityParameters priority, StorageParameters storage, String name) { super(priority, new AperiodicParameters(), storage); this.name = name; // devices.Console.println("MissSeq.constr: " + name // + "; maxMissionMemory " + storage.maxMissionMemory // + "; backingstore: " + this.privateMemory + "; isOuterMost: " + isOuterMostSeq); missionMemory = new MissionMemory((int) storage.maxMissionMemory, // mission memory privateMemory, //backingstore of sequencer name); currState = State.START; if (isOuterMostSeq) { if (Launcher.level != 0) { PriorityScheduler.instance().addOuterMostSeq(this); } } if (Launcher.level == 2) { isOuterMostSeq = false; } if (Launcher.level == 2 && !isOuterMostSeq) { if (Mission.getMission() != null) outerSeq = Mission.getMission().currMissSeq; } Services.setCeiling(this, this.priority.getPriority()); lock = Monitor.getMonitor(this); } @SCJAllowed @SCJRestricted(Phase.INITIALIZE) public MissionSequencer(PriorityParameters priority, final StorageParameters storage) { this(priority, storage, "MisMem"); } synchronized void seqWait() { while (!currMission.terminationPending() && currMission.msSetForMission.msCount > 0) { //devices.Console.println("MS.seqWait msCount:" + currMission.msSetForMission.msCount); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } synchronized void seqNotify() { devices.Console.println("MS.seqNotify msCount:" + currMission.msSetForMission.msCount); if (currMission.msSetForMission.msCount == 0) { notify(); } } /** * This method is declared final because the implementation is provided by * the infrastructure of the SCJ implementation and shall not be overridden. <br> * This method performs all of the activities that correspond to sequencing * of <code>Mission</code>s by this <code>MissionSequencer</code>. */ // //@ also // //@ requires true; // //@ ensures ??; // something to add? @SCJAllowed(Level.INFRASTRUCTURE) public final void handleAsyncEvent() { do { // the main actions of the sequencer governed by currState switch (currState) { case State.START: //devices.Console.println("MS.S: " + name); currMission = getNextMission(); if (currMission != null) { howManyMissions++; } else howManyMissions--; if (currMission == null || terminateSeq) { terminateSeq = true; currState = State.TERMINATE; } else { currMission.missionTerminate = false; currState = State.INITIALIZE; } break; case State.INITIALIZE: //devices.Console.println("MS.I: " + name); currMission.setMissionSeq(this); missionMemory.enterToInitialize(currMission); currState = State.EXECUTE; break; case State.EXECUTE: //devices.Console.println("MS.E"); missionMemory.enterToExecute(currMission); // the ms will wait here until it is notified if (Launcher.level > 0) { seqWait(); } else { while (!currMission.terminationPending() && currMission.msSetForMission.msCount > 0) { vm.RealtimeClock.awaitNextTick(); } } currState = State.CLEANUP; break; case State.CLEANUP: //devices.Console.println("MS.C: " + name); missionMemory.enterToCleanup(currMission); missionMemory.resizeArea(storage.maxMissionMemory); // handleAsyncEvent continues currState = State.START; break; case State.TERMINATE: if (Launcher.level == 2) { devices.Console.println("MS.T: " + name + "; #Missions: " + howManyMissions + "; outerSeq: " + outerSeq); vm.ClockInterruptHandler.instance.disable(); if (outerSeq != null) outerSeq.currMission.msSetForMission.removeMSObject(this); vm.ClockInterruptHandler.instance.enable(); } currState = State.END; default: } } while (currState < State.END); } /** * This method is called by the infrastructure to select the next * <code>Mission</code> to execute. <br> * Prior to each invocation of <code>getNextMission</code> by the * infrastructure, the infrastructure instantiates and enters a <code> * MissionMemory</code>, initially sized to represent all available backing * store. * * @return The next <code>Mission</code> to run, or null if no further * <code>Mission</code>s are to run under the control of this * <code>MissionSequencer</code>. */ /*@ protected behavior requires true; ensures true; @*/ @SCJAllowed(Level.SUPPORT) protected abstract MissionType getNextMission(); public final void register() { super.register(); } @Override public void signalTermination() { vm.ClockInterruptHandler.instance.disable(); devices.Console.println("------ MS.signalTermination: " + name); terminateSeq = true; currMission.requestTermination(); vm.ClockInterruptHandler.instance.enable(); } // //@ also // //@ requires true; // //@ ensures ??; // something to add? public void cleanUp() { super.cleanUp(); missionMemory.removeArea(); } /*@ spec_public @*/MissionMemory getMissionMemory() { return missionMemory; } Monitor getLock() { return lock; } MissionSequencer<?> getOuterSeq() { return outerSeq; } }