001/* Utilities for accessing the Matlab engine. 002 003 Copyright (c) 2003-2015 The Regents of the University of California 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA OR RESEARCH IN MOTION 012 LIMITED BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, 013 INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS 014 SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA 015 OR RESEARCH IN MOTION LIMITED HAVE BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION LIMITED 019 SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 020 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 021 PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" 022 BASIS, AND THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION 023 LIMITED HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 024 ENHANCEMENTS, OR MODIFICATIONS. 025 026 027 */ 028package ptolemy.data.expr; 029 030import java.lang.reflect.InvocationTargetException; 031import java.lang.reflect.Method; 032import java.util.Iterator; 033import java.util.Set; 034import java.util.StringTokenizer; 035 036import ptolemy.data.StringToken; 037import ptolemy.kernel.util.IllegalActionException; 038 039/////////////////////////////////////////////////////////////////// 040//// MatlabUtilities 041 042/** This class provides access to the Ptolemy Matlab interface 043 in ptolemy.matlab by using reflection. 044 045 @author Christopher Hylands, Steve Neuendorffer 046 @author Zoltan Kemenczy, Research in Motion Ltd. 047 @version $Id$ 048 @since Ptolemy II 2.1 049 @Pt.ProposedRating Green (neuendor) 050 @Pt.AcceptedRating Yellow (neuendor) 051 @see ptolemy.data.expr.ParseTreeEvaluator 052 */ 053public class MatlabUtilities { 054 /** Evaluate a Matlab expression within a scope. 055 * The expression argument is of the form 056 * <em>matlab("expression", arg1, arg2, ...)</em>, where 057 * <em>arg1, arg2, ... </em>is a list of Variables appearing in 058 * <em>"expression"</em>. Note that this form of invoking matlab 059 * is limited to returning only the first return value of a 060 * matlab function. If you need multiple return values, use the 061 * matlab actor.<p> 062 * 063 * Note that having an instance of {@link 064 * ptolemy.matlab.Expression} in the model will keep the matlab 065 * engine open from model preinitialize() to wrapup() and hence 066 * opening/closing of additional Engine instances done by this 067 * matlab expression evaluator becomes fast. Most users should 068 * prefer to use {@link ptolemy.matlab.Expression} and resort to 069 * this mechanism of invoking matlab only where necessary, e.g. in 070 * FSM transition action expressions (which was the reason for 071 * introducing this form of matlab engine access).<p> 072 * 073 * If a "packageDirectories" Parameter is in the scope of this 074 * expression, its value is added to the matlab path while the 075 * expression is being executed (like {@link 076 * ptolemy.matlab.Expression}). 077 * @param expression The Matlab expression to be evaluated 078 * @param variableNames The Matlab variables required for evaluating 079 * the expression. Each element of the Set names a Token that 080 * is found in the scope. 081 * @param scope The scope to evaluate the expression within. 082 * @return The results of the evaluation 083 * @exception IllegalActionException If there is a problem initializing 084 * the Matlab interface, invoking the Matlab engine or accessing a 085 * a Token. 086 */ 087 public static ptolemy.data.Token evaluate(String expression, 088 Set variableNames, ParserScope scope) 089 throws IllegalActionException { 090 try { 091 if (_engineClass == null) { 092 _initialize(); 093 } 094 095 ptolemy.data.Token result = null; 096 097 //MatlabEngineInterface matlabEngine = 098 // MatlabEngineFactory.createEngine(); 099 // Engine matlabEngine = new Engine(); 100 Object matlabEngine = null; 101 102 try { 103 matlabEngine = _engineClass.newInstance(); 104 } catch (InstantiationException ex) { 105 throw new IllegalActionException(null, ex, 106 "Failed to instantiate ptolemy.matlab.Engine"); 107 } 108 109 //long[] engine = matlabEngine.open(); 110 // Opening the matlab engine each time is very slow 111 _engine = (long[]) _engineOpen.invoke(matlabEngine, new Object[0]); 112 113 try { 114 synchronized ( 115 //matlabEngine.getSemaphore(); 116 _engine) { 117 // matlabEngine is not very good since it is 118 // "local". 119 // (zk:) I would recommend removing the static, 120 // synchronized engine instance and open/close a new 121 // Engine every time (see updated javadoc for this 122 // function). 123 String addPathCommand = null; // Assume none 124 ptolemy.data.Token previousPath = null; 125 ptolemy.data.Token packageDirectories = null; 126 127 if (scope != null) { 128 packageDirectories = scope.get("packageDirectories"); 129 } 130 131 if (packageDirectories != null 132 && packageDirectories instanceof StringToken) { 133 StringTokenizer dirs = new StringTokenizer( 134 ((StringToken) packageDirectories) 135 .stringValue(), 136 ","); 137 StringBuffer cellFormat = new StringBuffer(512); 138 cellFormat.append("{"); 139 140 if (dirs.hasMoreTokens()) { 141 cellFormat.append("'" + UtilityFunctions 142 .findFile(dirs.nextToken()) + "'"); 143 } 144 145 while (dirs.hasMoreTokens()) { 146 cellFormat.append(",'" + UtilityFunctions 147 .findFile(dirs.nextToken()) + "'"); 148 } 149 150 cellFormat.append("}"); 151 152 if (cellFormat.length() > 2) { 153 addPathCommand = "addedPath_=" 154 + cellFormat.toString() 155 + ";addpath(addedPath_{:});"; 156 157 //matlabEngine.evalString 158 // (engine, "previousPath_=path"); 159 _engineEvalString.invoke(matlabEngine, 160 new Object[] { _engine, 161 "previousPath_=path" }); 162 163 //previousPath = matlabEngine.get 164 // (engine, "previousPath_"); 165 previousPath = (ptolemy.data.Token) _engineGet 166 .invoke(matlabEngine, new Object[] { 167 _engine, "previousPath_" }); 168 } 169 } 170 171 //matlabEngine.evalString 172 // (engine, "clear variables;clear globals"); 173 _engineEvalString.invoke(matlabEngine, new Object[] { 174 _engine, "clear variables;clear globals" }); 175 176 if (addPathCommand != null) { 177 // matlabEngine.evalString(engine, addPathCommand); 178 _engineEvalString.invoke(matlabEngine, 179 new Object[] { _engine, addPathCommand }); 180 } 181 182 // Set matlab variables required for evaluating the 183 // expression 184 if (!variableNames.isEmpty()) { 185 Iterator names = variableNames.iterator(); 186 187 while (names.hasNext()) { 188 String name = (String) names.next(); 189 ptolemy.data.Token token = scope.get(name); 190 191 if (token != null) { 192 //matlabEngine.put 193 // (engine, name, token); 194 _enginePut.invoke(matlabEngine, 195 new Object[] { _engine, name, token }); 196 } 197 } 198 } 199 200 //matlabEngine.evalString(engine, 201 // "result__=" + expression); 202 _engineEvalString.invoke(matlabEngine, 203 new Object[] { _engine, "result__=" + expression }); 204 205 //result = matlabEngine.get(engine, "result__"); 206 result = (ptolemy.data.Token) _engineGet.invoke( 207 matlabEngine, new Object[] { _engine, "result__" }); 208 209 if (previousPath != null) { 210 // Restore the original engine path 211 //matlabEngine.put 212 // (engine, name, token); 213 _enginePut.invoke(matlabEngine, new Object[] { _engine, 214 "previousPath_", previousPath }); 215 216 // matlabEngine.evalString(engine, "path(previousPath_)"); 217 _engineEvalString.invoke(matlabEngine, new Object[] { 218 _engine, "path(previousPath_)" }); 219 } 220 } 221 } finally { 222 //matlabEngine.close(engine); 223 _engineClose.invoke(matlabEngine, new Object[] { _engine }); 224 } 225 226 return result; 227 } catch (IllegalAccessException ex) { 228 throw new IllegalActionException(null, ex, 229 "Problem invoking a method on " + "ptolemy.matlab.Engine"); 230 } catch (InvocationTargetException ex) { 231 throw new IllegalActionException(null, ex, 232 "Problem invoking a method of " + "ptolemy.matlab.Engine"); 233 } 234 } 235 236 /////////////////////////////////////////////////////////////////// 237 //// private methods //// 238 // Initialize private variables. 239 private static void _initialize() throws IllegalActionException { 240 // These could be in the constructor, but since the evaluate() 241 // method is a static method, we break out the functionality into 242 // a separate method. 243 // Use reflection so that we can compile without 244 // ptolemy.matlab and we check to see if is present at runtime. 245 try { 246 _engineClass = Class.forName("ptolemy.matlab.Engine"); 247 } catch (Throwable throwable) { 248 // UnsatsifiedLinkError is an Error, not an Exception, so 249 // we catch Throwable 250 throw new IllegalActionException(null, throwable, 251 "Failed to load ptolemy.matlab.Engine class"); 252 } 253 254 try { 255 // Methods of ptolemy.matlab.Engine, in alphabetical order. 256 _engineClose = _engineClass.getMethod("close", 257 new Class[] { long[].class }); 258 259 _engineEvalString = _engineClass.getMethod("evalString", 260 new Class[] { long[].class, String.class }); 261 262 _engineGet = _engineClass.getMethod("get", 263 new Class[] { long[].class, String.class }); 264 265 _engineOpen = _engineClass.getMethod("open", new Class[0]); 266 267 _enginePut = _engineClass.getMethod("put", new Class[] { 268 long[].class, String.class, ptolemy.data.Token.class }); 269 } catch (NoSuchMethodException ex) { 270 throw new IllegalActionException(null, ex, 271 "Problem finding a method of " + "ptolemy.matlab.Engine"); 272 } 273 } 274 275 /////////////////////////////////////////////////////////////////// 276 //// private variables //// 277 // The matlab engine pointer that is returned by matlab.Engine.open 278 // We cache this value so that each time we evaluate a Matlab 279 // expression, we need not necessarily reopen the Engine. 280 private static long[] _engine; 281 282 // The class of ptolemy.matlab.Engine 283 private static Class _engineClass; 284 285 // Methods of ptolemy.matlab.Engine, in alphabetical order. 286 // ptolemy.matlab.Engine.close(); 287 private static Method _engineClose; 288 289 // ptolemy.matlab.Engine.evalString(); 290 private static Method _engineEvalString; 291 292 // ptolemy.matlab.Engine.get(); 293 private static Method _engineGet; 294 295 // ptolemy.matlab.Engine.open(); 296 private static Method _engineOpen; 297 298 // ptolemy.matlab.Engine.put(); 299 private static Method _enginePut; 300}