// Actor that calls a simulation program of a dynamic system that is coupled to Ptolemy II /* ******************************************************************** Copyright Notice ---------------- Building Controls Virtual Test Bed (BCVTB) Copyright (c) 2008-2009, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved. If you have questions about your rights to use or distribute this software, please contact Berkeley Lab's Technology Transfer Department at TTD@lbl.gov NOTICE. This software was developed under partial funding from the U.S. Department of Energy. As such, the U.S. Government has been granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, prepare derivative works, and perform publicly and display publicly. Beginning five (5) years after the date permission to assert copyright is obtained from the U.S. Department of Energy, and subject to any subsequent five (5) year renewals, the U.S. Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable, worldwide license in the Software to reproduce, prepare derivative works, distribute copies to the public, perform publicly and display publicly, and to permit others to do so. Modified BSD License agreement ------------------------------ Building Controls Virtual Test Bed (BCVTB) Copyright (c) 2008-2009, The Regents of the University of California, through Lawrence Berkeley National Laboratory (subject to receipt of any required approvals from the U.S. Dept. of Energy). All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University of California, Lawrence Berkeley National Laboratory, U.S. Dept. of Energy nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. You are under no obligation whatsoever to provide any bug fixes, patches, or upgrades to the features, functionality or performance of the source code ("Enhancements") to anyone; however, if you choose to make your Enhancements available either publicly, or directly to Lawrence Berkeley National Laboratory, without imposing a separate written license agreement for such Enhancements, then you hereby grant the following license: a non-exclusive, royalty-free perpetual license to install, use, modify, prepare derivative works, incorporate into other computer software, distribute, and sublicense such enhancements or derivative works thereof, in binary and source code form. ******************************************************************** */ package lbnl.actor.lib; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import lbnl.actor.lib.net.Server; import lbnl.util.ClientProcess; import lbnl.util.WarningWindow; import lbnl.util.XMLWriter; import ptolemy.data.BooleanToken; import ptolemy.data.DoubleMatrixToken; import ptolemy.data.DoubleToken; import ptolemy.data.expr.FileParameter; import ptolemy.data.expr.Parameter; import ptolemy.data.type.BaseType; import ptolemy.domains.sdf.lib.SDFTransformer; import ptolemy.kernel.CompositeEntity; import ptolemy.kernel.attributes.URIAttribute; import ptolemy.kernel.util.IllegalActionException; import ptolemy.kernel.util.NameDuplicationException; import ptolemy.kernel.util.NamedObj; import ptolemy.kernel.util.Settable; import ptolemy.kernel.util.Workspace; import ptolemy.util.StringUtilities; /** * Actor that calls a simulation program of a dynamic system * that is coupled to Ptolemy II. At the start of the simulation, * this actor fires a system command that is defined by the parameter * <code>programName</code> with arguments <code>programArguments</code>. * It then initiates a socket connection and uses the socket to * exchange data with the external simulation program each time * the actor is fired. * * @author Michael Wetter * @version $Id$ * @since BCVTB 0.1 * */ public class Simulator extends SDFTransformer { /** Constructs an actor with the given container and name. * @param container The container. * @param name The name of this actor. * @exception IllegalActionException If the actor cannot be contained * by the proposed container. * @exception NameDuplicationException If the container already has an * actor with this name. */ public Simulator(CompositeEntity container, String name) throws NameDuplicationException, IllegalActionException { super(container, name); input.setTypeEquals(BaseType.DOUBLE_MATRIX); input.setMultiport(false); output.setTypeEquals(BaseType.DOUBLE_MATRIX); output.setMultiport(false); programName = new FileParameter(this, "programName"); new Parameter(programName, "allowFiles", BooleanToken.TRUE); new Parameter(programName, "allowDirectories", BooleanToken.FALSE); programArguments = new Parameter(this, "programArguments"); programArguments.setTypeEquals(BaseType.STRING); programArguments.setExpression(""); workingDirectory = new FileParameter(this, "workingDirectory"); new Parameter(workingDirectory, "allowFiles", BooleanToken.FALSE); new Parameter(workingDirectory, "allowDirectories", BooleanToken.TRUE); workingDirectory.setExpression("."); simulationLogFile = new FileParameter(this, "simulationLogFile"); simulationLogFile.setTypeEquals(BaseType.STRING); simulationLogFile.setExpression("simulation.log"); new Parameter(simulationLogFile, "allowFiles", BooleanToken.TRUE); new Parameter(simulationLogFile, "allowDirectories", BooleanToken.FALSE); socketTimeout = new Parameter(this, "socketTimeout"); socketTimeout.setDisplayName("socketTimeout [milliseconds]"); socketTimeout.setExpression("5000"); socketTimeout.setTypeEquals(BaseType.INT); showConsoleWindow = new Parameter(this, "showConsoleWindow"); showConsoleWindow.setTypeEquals(BaseType.BOOLEAN); showConsoleWindow.setToken(BooleanToken.TRUE); // expert settings socketPortNumber = new Parameter(this, "socketPortNumber"); socketPortNumber.setDisplayName("socketPortNumber (used if non-negative)"); socketPortNumber.setExpression("-1"); socketPortNumber.setTypeEquals(BaseType.INT); socketPortNumber.setVisibility(Settable.EXPERT); socketConfigurationFile = new FileParameter(this, "socketConfigurationFile"); socketConfigurationFile.setTypeEquals(BaseType.STRING); socketConfigurationFile.setExpression("socket.cfg"); new Parameter(socketConfigurationFile, "allowFiles", BooleanToken.TRUE); new Parameter(socketConfigurationFile, "allowDirectories", BooleanToken.FALSE); socketConfigurationFile.setVisibility(Settable.EXPERT); // we produce one (DOUBLE_MATRIX) token as the initial output output_tokenInitProduction.setExpression("1"); } /////////////////////////////////////////////////////////////////// //// public methods //// /** Clone the actor into the specified workspace. This calls the * base class and then sets the <code>init</code> and <code>step</code> * public members to the parameters of the new actor. * @param workspace The workspace for the new object. * @return A new actor. * @exception CloneNotSupportedException If a derived class contains * an attribute that cannot be cloned. */ public Object clone(Workspace workspace) throws CloneNotSupportedException { Simulator newObject = (Simulator) super.clone(workspace); newObject.programArguments = (Parameter) newObject .getAttribute("programArguments"); newObject.programName = (FileParameter) newObject .getAttribute("programName"); newObject.socketPortNumber = (Parameter) newObject .getAttribute("socketPortNumber"); newObject.simulationLogFile = (FileParameter) newObject .getAttribute("simulationLogFile"); newObject.socketConfigurationFile = (FileParameter) newObject .getAttribute("socketConfigurationFile"); newObject.socketTimeout = (Parameter) newObject .getAttribute("socketTimeout"); newObject.workingDirectory = (FileParameter) newObject .getAttribute("workingDirectory"); newObject.showConsoleWindow = (Parameter) newObject .getAttribute("showConsoleWindow"); return newObject; } /** Send the input token to the client program and send the * output from the client program to the output port. * * @exception IllegalActionException If the simulation time between Ptolemy * and the client program is not synchronized. */ public void fire() throws IllegalActionException { super.fire(); // Check the input port for a token. if (input.hasToken(0)) { if (server.getClientFlag() == 0) { // If clientflag is non-zero, do not read anymore _writeToServer(); // before the read happens, the client program will advance one time step _readFromServer(); if (server.getClientFlag() == 0) { // Get values sent by simulator double[] dblRea = server.getDoubleArray(); outTok = new DoubleMatrixToken(dblRea, dblRea.length, 1); // Make sure that simulation times are synchronized final double simTimRea = server.getSimulationTimeReadFromClient(); final double simTim = getDirector().getModelTime().getDoubleValue(); if ( firstFire ) firstFire = false; else{ if (Math.abs((simTimRea - simTimReaPre)-(simTim - simTimPre)) > 0.0001) { final String em = "Simulation time of " + this.getFullName() + " is not synchronized." + LS + "Time step in Ptolemy = " + (simTim - simTimPre) + LS + "Time step in client = " + (simTimRea - simTimReaPre) + LS + "Time in client = " + simTimRea; throw new IllegalActionException(this, em); } } // Store simulation time simTimReaPre = simTimRea; simTimPre = simTim; } } else { // Either client is down or this is the first time step. if ( clientTerminated ){ // Client terminated in last call, but Ptolemy keeps doing a // (at least one) more time step. Hence we issue a warning. // Start a new thread for the warning window so that the simulation can continue. if ( warWin == null ){ if (!isHeadless){ warWin = new Thread(new WarningWindow(terminationMessage)); warWin.start(); } System.err.println("*** " + terminationMessage); } } // Consume token input.get(0); final double simTimRea = server.getSimulationTimeReadFromClient(); final double simTim = getDirector().getModelTime().getDoubleValue(); // Store simulation time simTimReaPre = simTimRea; simTimPre = simTim; } } ////////////////////////////////////////////////////// // send output token output.send(0, outTok); } /** Write the data to the server instance, which will send it to * the client program. * * @exception IllegalActionException If there was an error when * writing to the server. */ private void _writeToServer() throws IllegalActionException { ////////////////////////////////////////////////////// // Write data to server dblWri = _getDoubleArray(input.get(0)); try { // Thread.sleep(1000); // in milliseconds server.write(0, tokTim, dblWri); } catch (IOException e) { String em = "Error while writing to client: " + LS + e.getMessage(); throw new IllegalActionException(this, em); } // get tokens' time stamp. This time will be written to the // client in the next time step, this time step read from the client // the output which will be sent to clients in the next time step // as inputs tokTim = getDirector().getModelTime().getDoubleValue(); } /** Read the data from the server instance, which will read it * from the client program. * * @exception IllegalActionException If there was an error when * reading from the server. */ private void _readFromServer() throws IllegalActionException { ////////////////////////////////////////////////////// // Read data from server try { // Thread.sleep(100); // in milliseconds server.read(); int fla = server.getClientFlag(); if (fla < 0) { final String em = "Error: Client " + this.getFullName() + " terminated communication by sending flag = " + fla + " at time " + getDirector().getModelTime().getDoubleValue() + "."; throw new IllegalActionException(this, em); } if (fla > 0) { // Client reached its final time. If this is also the last // step from Ptolemy, then we don't want to issue a warning. // Hence, we store the information, and if there is one more // step from Ptolemy, then we issue a warning. clientTerminated = true; terminationMessage = "Warning: " + this.getFullName() + " terminated communication by sending flag = " + fla + " at time " + getDirector().getModelTime().getDoubleValue() + "." + LS + "Simulation will continue withouth updated values from client program."; } } catch (java.net.SocketTimeoutException e) { String em = "SocketTimeoutException while reading from client in " + this.getFullName() + ": " + LS + e.getMessage() + "." + LS + "Try to increase the value of the parameter 'socketTimeout'." + LS + "It could be that the client \"" + programName.getExpression() + "\" is not executing properly. From the command line, " + "try running:" + LS + " " + programName.getExpression() + " " + programArguments.getExpression() + LS + "You should see something like:" + LS + " Simulation model has time step 60" + LS + " Error: Failed to obtain socket file descriptor. sockfd=-1." + LS + "The error message is expected because Ptolemy is not " + "present." + LS + "Also, make sure that the directory that contains" + LS + "\"bcvtb.dll\" (on Windows), \"libbcvtb.so\" (on Linux) or" + LS + "\"libbcvtb.dylib\" (on Mac OS X) is on your" + "PATH, LD_LIBRARY_PATH or DYLD_LIBRARY_PATH for Windows, " + LS + "Linux and Mac OS X respectively." + LS + "That directory contains the shared library used by the simulator."; try { server.close(); } catch (IOException e2) { } // Check the exit value of the subprocess em += "\nClient subprocess exit value (should be 0): "; try { // If the subprocess is still running, then we may // get an exception here. See Process.exitValue(). em += cliPro.exitValue(); } catch (Throwable throwable) { em += "<<Unknown: " + throwable.getMessage(); } ; // do nothing here throw new IllegalActionException(this, e, em); } catch (IOException e) { String em = "IOException while reading from server."; try { server.close(); } catch (IOException e2) { } ; // do nothing here // If the client sent a termination flag, then clientTerminated=true // In this case, the client may have closed the socket connection, and // hence we don't throw an IOException, but rather issue a warning // in case that Ptolemy proceeds with its iterations. // Without the check (!clientTerminated), an IOException is thrown // on Windows (but not on Mac or Linux) from the actor that connects // to EnergyPlus. if ( ! clientTerminated ) throw new IllegalActionException(this, e, em); } } /** Initializes the data members and checks if the parameters of * the actor are valid. * * @exception IllegalActionException If the parameters of the * actor are invalid, or if the file with the socket * information cannot be written to disk. */ public void preinitialize() throws IllegalActionException { super.preinitialize(); // Flag that indicates whether the client terminated, // and thread for the warning window that is used in such a situation clientTerminated = false; terminationMessage = ""; warWin = null; // Check if we run in headless mode isHeadless = StringUtilities.getProperty("ptolemy.ptII.isHeadless").equals("true"); // Working directory worDir = Simulator.resolveDirectory(getContainer(), cutQuotationMarks(workingDirectory.getExpression())); // Verify that directory exist if (!new File(worDir).isDirectory()) { String em = "Error: Working directory does not exist." + LS + "Working directory is set to: '" + worDir + "'" + LS + "Check configuration of '" + this.getFullName() + "'."; throw new IllegalActionException(this, em); } // Command that starts the simulation final String simCon = socketConfigurationFile.stringValue(); // Assign BSD port number porNo = Integer.valueOf(socketPortNumber.getExpression()); ////////////////////////////////////////////////////////////// // Instantiate server for IPC try { // time out in milliseconds final int timOutMilSec = Integer.valueOf(socketTimeout.getExpression()); if (timOutMilSec <= 0) { final String em = "Parameter for socket time out must be positive." + LS + "Received " + timOutMilSec + " milliseconds"; throw new IllegalActionException(this, em); } if (porNo < 0) { server = new Server(timOutMilSec); // server uses any free port number } else { server = new Server(porNo, timOutMilSec); } // get port number porNo = server.getLocalPort(); } catch (IOException e) { // try to close server unless it is still a null pointer if (server != null) { try { server.close(); } catch (IOException e2) { } } // throw original exception throw new IllegalActionException(this, e, e.getMessage()); } ////////////////////////////////////////////////////////////// // Write xml file for client XMLWriter xmlWri = new XMLWriter(worDir, simCon, porNo); try { xmlWri.write(); } catch (FileNotFoundException e) { String em = "FileNotFoundException when trying to write '" + new File(worDir, simCon).getAbsolutePath() + "'."; throw new IllegalActionException(this, e, em); } catch (IOException e) { throw new IllegalActionException(this, e, e.toString()); } ////////////////////////////////////////////////////////////// // Start the simulation _startSimulation(); } /** Resolve the command string. * * This method replaces $CLASSPATH, relative file names and adds .exe * to the command (under Windows) * * @param programName Name of program that starts the simulation. * @return The command line string. * @exception IllegalActionException If the simulation process arguments * are invalid. */ public static String resolveCommandName(final File programName) throws IllegalActionException { File commandFile = programName; // If we are under Windows, look for the .exe if ( System.getProperty("os.name").startsWith("Windows") ){ File winComFil = new File(commandFile.toString() + ".exe"); if (winComFil.exists()) commandFile = winComFil; } // Remove the path if the argument points to a directory. // This fixes the problem if the argument is matlab, and the // user runs vergil from a directory that has a subdirectory // called matlab. if ( commandFile.isDirectory() ) return commandFile.getName(); String comArg = commandFile.toString(); // Get the absolute file name. Otherwise, invoking a command // like ../cclient will not work commandFile = new File(comArg); if (commandFile.exists() && !commandFile.isDirectory() ) { try { comArg = commandFile.getCanonicalPath(); } catch (IOException exc) { String em = "Error: Could not get canonical path for '" + comArg + "'."; throw new IllegalActionException(em); } } else comArg = commandFile.getName(); return comArg; } /** Get the MoML file. * *@param namedObj A named object, typically the container of the model *@return the MoMOL file *@exception IllegalActionException If an attribute is found with the name "_uri" * that is not an instance of the URIAttribute class */ public static File getMoMLFile(final NamedObj namedObj) throws IllegalActionException{ URIAttribute modelURI = (URIAttribute) namedObj.getAttribute("_uri", URIAttribute.class); return new File(modelURI.getURI()); } /** Resolve the working string. * * This method adds the path of the MoML file to its argument if * the argument is a relative directory. * * @param namedObj A named object, typically the container of the model * @param dir The directory to be resolved. * @return The resolved working string. * @exception IllegalActionException If an attribute is found with the name "_uri" * that is not an instance of the URIAttribute class */ public static String resolveDirectory(final NamedObj namedObj, final String dir) throws IllegalActionException { if ( new File(dir).isAbsolute() ) return dir; String chi = new String(dir); if (chi.length() == 0) { chi = "."; } final File fil = new File(getMoMLFile(namedObj).getParent(), chi); chi = fil.getPath(); return chi; } /** Start the simulation program. * * @exception IllegalActionException If the simulation process arguments * are invalid. */ private void _startSimulation() throws IllegalActionException { ////////////////////////////////////////////////////////////// // Construct the argument list for the process builder List<String> com = new ArrayList<String>(); // Process the program name // Maybe the user specified $CLASSPATH/lbnl/demo/CRoom/client com.add( Simulator.resolveCommandName(programName.asFile() )); // Process program arguments final String argLin = cutQuotationMarks(programArguments.getExpression()); StringTokenizer st = new StringTokenizer(argLin); while (st.hasMoreTokens()) { com.add(st.nextToken()); } // Close the window that contains the console output. // This is needed if a simulation is started multiple times. // Otherwise, each new run would make a new window. if ( cliPro != null ) cliPro.disposeWindow(); cliPro = new ClientProcess(this.getFullName()); cliPro.redirectErrorStream(true); cliPro.setProcessArguments(com, worDir); // Check if we run in headless mode final boolean showConsole = ((BooleanToken)(showConsoleWindow.getToken())).booleanValue(); cliPro.showConsoleWindow( showConsole && (! isHeadless) ); // Set simulation log file. // The call to System.gc() is required on Windows: If this actor is called multiple times // on Windows using vmware fusion and vmware workstation, then the simulation log file // exists but cannot be deleted. Calling System.gc() releases the resources which allows // Java to delete the file. See also http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6266377 // This error does not happen on Linux and on Mac OS X. System.gc(); File slf = simulationLogFile.asFile(); try { if (slf.exists()) { if (slf.delete()) { if (slf.exists()) { throw new Exception("Cannot delete file."); } } } if (!slf.createNewFile()) { throw new Exception("Cannot create file."); } // make sure we can write new file if (!slf.canWrite()) { throw new Exception("Cannot write to file."); } } catch (Exception e) { String em = "Error: Cannot write to simulation log file." + LS + "Simulation log file is '" + slf.getAbsolutePath() + "'" + LS + "Check configuration of '" + this.getFullName() + "'."; throw new IllegalActionException(this, e, em); } cliPro.setSimulationLogFile(slf); cliPro.run(); if (!cliPro.processStarted()) { String em = "Error: Simulation process did not start." + LS + cliPro.getErrorMessage() + LS + "Check configuration of '" + this.getFullName() + "'."; throw new IllegalActionException(this, em); } } /** Initialize state variables. * * @exception IllegalActionException If the parent class throws it or * if the server socket cannot be opened */ public void initialize() throws IllegalActionException { super.initialize(); tokTim = getDirector().getModelTime().getDoubleValue(); firstFire = true; ////////////////////////////////////////////////////////////// // New code since 2008-01-05 // Send initial output token. See also domains/sdf/lib/SampleDelay.java _readFromServer(); double[] dblRea = server.getDoubleArray(); outTok = new DoubleMatrixToken(dblRea, dblRea.length, 1); output.send(0, outTok); } /** Closes sockets and shuts down the simulator. * * @exception IllegalActionException if the base class throws it or * if an I/O error occurs when closing the socket. */ public void wrapup() throws IllegalActionException { super.wrapup(); try { // Send signal to the client, indicating that we are done with the time stepping. // This allows the client to terminate gracefully. server.write(1, tokTim, dblWri); // Close the server. server.close(); } catch (IOException e) { if ( ! clientTerminated ) throw new IllegalActionException(this, e, e.getMessage()); } if (!isHeadless) { // Reset position of window that shows console output so // that for the next simulation, the window will be placed // on top of the screen again ClientProcess.resetWindowLocation(); } } /** Cut the leading and terminating quotation marks if present. * *@param str The string. *@return The string with leading and terminating quotation marks removed if present */ public static String cutQuotationMarks(String str) { if (str.startsWith("\"") && str.endsWith("\"")) { return str.substring(1, str.length() - 1); } else { return str; } } /** Get a double array from the Token. * * @param t the token which must be a type that can be converted to an ArrayToken * @return the double[] array with the elements of the Token * @exception IllegalActionException If the base class throws it. */ private double[] _getDoubleArray(ptolemy.data.Token t) throws IllegalActionException { final DoubleMatrixToken arrTok = (DoubleMatrixToken) t; final int n = arrTok.getRowCount(); double[] ret = new double[n]; for (int i = 0; i < n; i++) { final DoubleToken scaTok = (DoubleToken) arrTok.getElementAsToken( i, 0); ret[i] = scaTok.doubleValue(); if (Double.isNaN(ret[i])) { final String em = "Actor " + this.getFullName() + ": " + LS + "Token number " + i + " is NaN at time " + getDirector().getModelTime().getDoubleValue(); throw new IllegalActionException(this, em); } } return ret; } /////////////////////////////////////////////////////////////////// //// ports and parameters //// /** Arguments of program that starts the simulation. */ public Parameter programArguments; /** Name of program that starts the simulation. */ public FileParameter programName; /** Port number for BSD socket (used if non-negative). */ public Parameter socketPortNumber; /** File name to which this actor writes the simulation log. */ public FileParameter simulationLogFile; /** File name to which this actor writes the socket configuration. */ public FileParameter socketConfigurationFile; /** Socket time out in milliseconds. */ public Parameter socketTimeout; /** Working directory of the simulation. */ public FileParameter workingDirectory; /** If <i>true</i> (the default), a window will be created that shows the console output. */ public Parameter showConsoleWindow; /////////////////////////////////////////////////////////////////// //// protected members //// /** Double values that were written to the socket. */ protected double[] dblWri; /** Thread that runs the simulation. */ protected ClientProcess cliPro; /** Port number that is actually used for BSD socket. */ protected int porNo; /** Server used for data exchange. */ protected Server server; /** Process that runs the simulation. */ protected Process simProJav; /** Working directory of the subprocess. */ protected String worDir; /** Output tokens. */ protected DoubleMatrixToken outTok; /** Ptolemy's time at the last call of the fire method. */ protected double simTimPre; /** Time read from the simulation program at the last call of the fire method. */ protected double simTimReaPre; /** Flag, set to true when the clients terminates the communication. */ protected boolean clientTerminated; /** Thread that is used if a warning window need to be shown. */ protected Thread warWin; /** Message that will be displayed in the warning window when the client terminated, but Ptolemy continues with the simulation. */ protected String terminationMessage; /** Flag, set the <code>true</code> if Ptolemy is run without any graphical * interface. * * If <code>isHeadless=true</code>, this actor will not open any windows for * reporting outputs or warnings. */ protected boolean isHeadless; /////////////////////////////////////////////////////////////////// //// private members //// /** Flag that is true during the first firing of this actor */ private boolean firstFire; /** System dependent line separator */ private final static String LS = System.getProperty("line.separator"); /** Time of token that will be written to the client. This is equal to the Ptolemy time minus one time step, because at time t_k, a client gets the output of other clients at t_{k-1}, which allows the client to compute the states and outputs at t_k */ private double tokTim; }