001/* An action that sends outputs or sets variable values. 002 003 Copyright (c) 2000-2014 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 BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 */ 027package ptolemy.domains.modal.kernel; 028 029import java.util.Iterator; 030 031import ptolemy.actor.IOPort; 032import ptolemy.actor.NoRoomException; 033import ptolemy.data.Token; 034import ptolemy.data.expr.ASTPtRootNode; 035import ptolemy.data.expr.UnknownResultException; 036import ptolemy.data.expr.Variable; 037import ptolemy.kernel.CompositeEntity; 038import ptolemy.kernel.Entity; 039import ptolemy.kernel.util.Attribute; 040import ptolemy.kernel.util.IllegalActionException; 041import ptolemy.kernel.util.NameDuplicationException; 042import ptolemy.kernel.util.Nameable; 043import ptolemy.kernel.util.NamedObj; 044import ptolemy.kernel.util.Workspace; 045 046/////////////////////////////////////////////////////////////////// 047//// CommitActionsAttribute 048 049/** 050 An action that changes the state of the system. Typical commit actions set 051 variables either in the containing FSMActor or in a state refinement. 052 This action is contained by a transition, 053 and is evaluated whenever that transition is taken. The evaluation 054 is done in the postfire() method of the FSMActor that contains the 055 transition (hereafter called "the FSM actor"). 056 To specify an action that is executed earlier, in the fire() 057 method, when the transition is enabled rather than taken, 058 use the class OutputActionsAttribute. 059 <p> 060 The value of this attribute is a semicolon separated list of commands, 061 where each command gives a destination and a value. 062 The actions are given by calling setExpression() with 063 a string of the form: 064 <pre> 065 <i>command</i>; <i>command</i>; ... 066 </pre> 067 where each <i>command</i> has the form: 068 <pre> 069 <i>destination</i> = <i>expression</i> 070 </pre> 071 where <i>destination</i> is <i>variableName</i>, 072 where <i>variableName</i> is either a variable or parameter of 073 the FSM actor, or a variable or parameter of a refinement state. 074 To give a variable of a refinement state, use a dotted name, 075 as follows: 076 <pre> 077 <i>refinementStateName</i>.<i>variableName</i> 078 </pre> 079 <p> 080 The <i>expression</i> is a string giving an expression in the usual 081 Ptolemy II expression language. The expression may include references 082 to variables and parameters contained by the FSM actor. 083 <p> 084 The <i>destination</i> can also be a port, however, this is discouraged. 085 Commit actions are not executed until postfire(), and the Ptolemy II 086 abstract semantics requires that outputs be produced in fire(). 087 Nonetheless, this is supported. In this case, the <i>destination</i> is 088 either 089 <pre> 090 <i>portName</i> 091 </pre> 092 or 093 <pre> 094 <i>portName</i>(<i>channelNumber</i>) 095 </pre> 096 Here, <i>portName</i> is the name of a port of the FSM actor, 097 If no <i>channelNumber</i> is given, then the value 098 is broadcast to all channels of the port. 099 If destination name is given where there is both a port and a variable 100 with that name, then the port will be used. 101 102 @author Xiaojun Liu and Edward A. Lee 103 @version $Id$ 104 @since Ptolemy II 8.0 105 @Pt.ProposedRating Yellow (eal) 106 @Pt.AcceptedRating Red (eal) 107 @see CommitActionsAttribute 108 @see Transition 109 @see FSMActor 110 */ 111public class CommitActionsAttribute extends AbstractActionsAttribute 112 implements CommitAction { 113 /** Construct an action in the specified workspace with an empty 114 * string as a name. 115 * The object is added to the directory of the workspace. 116 * Increment the version number of the workspace. 117 * @param workspace The workspace that will list the attribute. 118 */ 119 public CommitActionsAttribute(Workspace workspace) { 120 super(workspace); 121 } 122 123 /** Construct an action with the given name contained 124 * by the specified transition. The <i>transition</i> argument must not 125 * be null, or a NullPointerException will be thrown. This action will 126 * use the workspace of the transition for synchronization and 127 * version counts. If the name argument is null, then the name is 128 * set to the empty string. 129 * This increments the version of the workspace. 130 * @param transition The transition that contains this action. 131 * @param name The name of this action. 132 * @exception IllegalActionException If the action is not of an 133 * acceptable class for the container. 134 * @exception NameDuplicationException If the transition already 135 * has an attribute with the name. 136 */ 137 public CommitActionsAttribute(Transition transition, String name) 138 throws IllegalActionException, NameDuplicationException { 139 super(transition, name); 140 } 141 142 /////////////////////////////////////////////////////////////////// 143 //// public methods //// 144 145 /** Send tokens to the designated outputs. Each token is determined 146 * by evaluating the expression portion of the action. 147 * @exception IllegalActionException If expression evaluation fails, 148 * or the specified port is not found, or sending to one of the 149 * channels of the port throws a NoRoomException. 150 */ 151 @Override 152 public void execute() throws IllegalActionException { 153 super.execute(); 154 155 if (_destinations != null && _destinations.size() > 0) { 156 Iterator destinations = _destinations.iterator(); 157 Iterator channels = getChannelNumberList().iterator(); 158 Iterator parseTrees = _parseTrees.iterator(); 159 160 while (destinations.hasNext()) { 161 NamedObj nextDestination = (NamedObj) destinations.next(); 162 163 // Need to get the next channel even if it's not used. 164 Integer channel = (Integer) channels.next(); 165 ASTPtRootNode parseTree = (ASTPtRootNode) parseTrees.next(); 166 Token token; 167 168 try { 169 token = _parseTreeEvaluator.evaluateParseTree(parseTree, 170 _getParserScope()); 171 } catch (IllegalActionException ex) { 172 // Chain exceptions to get the actor that 173 // threw the exception. 174 throw new IllegalActionException(this, ex, 175 "Expression invalid."); 176 } 177 178 if (nextDestination instanceof IOPort) { 179 IOPort destination = (IOPort) nextDestination; 180 181 // FIXME: Domain-polymorphic actors should 182 // not be writing to output ports in commit actions. 183 // Should we through an exception? Also: 184 // If the IOPort is an input as well as an output, 185 // then we need to set its shadow variables. 186 187 try { 188 if (channel != null) { 189 destination.send(channel.intValue(), token); 190 191 if (_debugging) { 192 _debug(getFullName() + " port: " 193 + destination.getName() + " channel: " 194 + channel.intValue() + ", token: " 195 + token); 196 } 197 } else { 198 if (token == null) { 199 destination.broadcastClear(); 200 201 if (_debugging) { 202 _debug(getFullName() + " port: " 203 + destination.getName() 204 + " broadcast Clear!"); 205 } 206 } else { 207 destination.broadcast(token); 208 209 if (_debugging) { 210 _debug(getFullName() + " port: " 211 + destination.getName() 212 + " broadcast token: " + token); 213 } 214 } 215 } 216 } catch (NoRoomException ex) { 217 throw new IllegalActionException(this, 218 "Cannot complete action: " + ex.getMessage()); 219 } catch (UnknownResultException ex) { 220 // Produce no output. 221 } 222 } else if (nextDestination instanceof Variable) { 223 Variable destination = (Variable) nextDestination; 224 225 try { 226 //Token token = variable.getToken(); 227 destination.setToken(token); 228 229 // Force all dependents to re-evaluate. 230 // This makes the parameters in the actors of 231 // the refinement take on new values immediately 232 // after the action is committed. 233 destination.validate(); 234 235 if (_debugging) { 236 _debug(getFullName() + " variable: " 237 + destination.getName() + ", value: " 238 + token); 239 } 240 } catch (UnknownResultException ex) { 241 destination.setUnknown(true); 242 } 243 } else { 244 throw new IllegalActionException(this, 245 "Destination is neither an IOPort nor a Variable: " 246 + nextDestination.getFullName()); 247 } 248 } 249 } 250 } 251 252 /////////////////////////////////////////////////////////////////// 253 //// protected methods //// 254 255 /** Given a destination name, return a NamedObj that matches that 256 * destination. 257 * @param name The name of the destination, or null if none is found. 258 * @return An object (like a port or a variable) with the specified name. 259 * @exception IllegalActionException If the associated FSMActor 260 * does not have a destination with the specified name. 261 */ 262 @Override 263 protected NamedObj _getDestination(String name) 264 throws IllegalActionException { 265 Transition transition = (Transition) getContainer(); 266 267 if (transition == null) { 268 throw new IllegalActionException(this, 269 "Action has no container transition."); 270 } 271 272 Entity fsm = (Entity) transition.getContainer(); 273 274 if (fsm == null) { 275 throw new IllegalActionException(this, transition, 276 "Transition has no container."); 277 } 278 279 IOPort port = (IOPort) fsm.getPort(name); 280 281 if (port == null) { 282 // No port found. Try for a variable. 283 Attribute variable = fsm.getAttribute(name); 284 285 if (variable == null) { 286 // Try for a refinement variable. 287 int period = name.indexOf("."); 288 289 if (period > 0) { 290 String refinementName = name.substring(0, period); 291 String entryName = name.substring(period + 1); 292 293 // FIXME: Look in the container of the fsm??? 294 // Below we look for an attribute only in the fsm 295 // itself. 296 Nameable fsmContainer = fsm.getContainer(); 297 298 if (fsmContainer instanceof CompositeEntity) { 299 Entity refinement = ((CompositeEntity) fsmContainer) 300 .getEntity(refinementName); 301 302 if (refinement != null) { 303 Attribute entry = refinement 304 .getAttribute(entryName); 305 306 if (entry instanceof Variable) { 307 return entry; 308 } 309 } 310 } 311 } 312 313 // FIXME: If not found, instead of throwing an exception, we 314 // look for an attribute with the given name in the containers. 315 // This, however, split the name by periods, so it only looks 316 // for attributes with the exact given name. Look at 317 // ptolemy.domains.ptera.kernel.ActionsAttribute for a solution. 318 NamedObj container = getContainer(); 319 while (container != null) { 320 Attribute attribute = container.getAttribute(name); 321 if (attribute != null) { 322 return attribute; 323 } 324 NamedObj containerContainer = container.getContainer(); 325 if (container instanceof RefinementActor) { 326 State state = ((RefinementActor) container) 327 .getRefinedState(); 328 if (state == null) { 329 container = containerContainer; 330 } else { 331 container = state.getContainer(); 332 } 333 } else { 334 container = containerContainer; 335 } 336 } 337 338 throw new IllegalActionException(fsm, this, 339 "Cannot find port or variable with the name: " + name); 340 } else { 341 if (!(variable instanceof Variable)) { 342 throw new IllegalActionException(fsm, this, 343 "The attribute with name \"" + name 344 + "\" is not an " 345 + "instance of Variable."); 346 } 347 348 return variable; 349 } 350 } else { 351 if (!port.isOutput()) { 352 throw new IllegalActionException(fsm, this, 353 "The port is not an output port: " + name); 354 } 355 356 return port; 357 } 358 } 359}