001/* An action that sends outputs. 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.actor.Receiver; 034import ptolemy.data.Token; 035import ptolemy.data.expr.ASTPtRootNode; 036import ptolemy.data.expr.UnknownResultException; 037import ptolemy.kernel.Entity; 038import ptolemy.kernel.util.IllegalActionException; 039import ptolemy.kernel.util.NameDuplicationException; 040import ptolemy.kernel.util.NamedObj; 041import ptolemy.kernel.util.Workspace; 042 043/////////////////////////////////////////////////////////////////// 044//// OutputActionsAttribute 045 046/** 047 An action that sends outputs to one or more ports. 048 This action is contained by a transition, 049 and is evaluated whenever that transition becomes enabled. The evaluation 050 is done in the fire() method of the FSMActor that contains the 051 transition (hereafter called "the FSM actor"). 052 Note that the fire() method may be invoked more than once in an 053 iteration, particularly in domains where there is iteration to a fixed point, 054 such as CT. To specify an action that is executed only when the 055 transition is taken (in the postfire() method), use the class 056 CommitActionsAttribute. 057 <p> 058 The value of this attribute is a semicolon separated list of commands, 059 where each command gives a destination port to send data to and a value 060 to send. The actions are given by calling setExpression() with 061 a string of the form: 062 <pre> 063 <i>command</i>; <i>command</i>; ... 064 </pre> 065 where each <i>command</i> has the form: 066 <pre> 067 <i>destination</i> = <i>expression</i> 068 </pre> 069 where <i>destination</i> is either 070 <pre> 071 <i>portName</i> 072 </pre> 073 or 074 <pre> 075 <i>portName</i>(<i>channelNumber</i>) 076 </pre> 077 where <i>portName</i> is the name of a port of the FSM actor. 078 If no <i>channelNumber</i> is given, then the value 079 is broadcast to all channels of the port. 080 <p> 081 The <i>expression</i> is a string giving an expression in the usual 082 Ptolemy II expression language. The expression may include references 083 to variables and parameters contained by the FSM actor. 084 085 @author Xiaojun Liu, Edward A. Lee, Haiyang Zheng 086 @version $Id$ 087 @since Ptolemy II 8.0 088 @Pt.ProposedRating Red (hyzheng) 089 @Pt.AcceptedRating Red (hyzheng) 090 @see CommitActionsAttribute 091 @see Transition 092 @see FSMActor 093 */ 094public class OutputActionsAttribute extends AbstractActionsAttribute 095 implements ChoiceAction { 096 /** Construct an action in the specified workspace with an empty 097 * string as a name. 098 * The object is added to the directory of the workspace. 099 * Increment the version number of the workspace. 100 * @param workspace The workspace that will list the attribute. 101 */ 102 public OutputActionsAttribute(Workspace workspace) { 103 super(workspace); 104 } 105 106 /** Construct an action with the given name contained 107 * by the specified transition. The <i>transition</i> argument must not 108 * be null, or a NullPointerException will be thrown. This action will 109 * use the workspace of the transition for synchronization and 110 * version counts. If the name argument is null, then the name is 111 * set to the empty string. 112 * This increments the version of the workspace. 113 * @param transition The transition that contains this action. 114 * @param name The name of this action. 115 * @exception IllegalActionException If the action is not of an 116 * acceptable class for the container. 117 * @exception NameDuplicationException If the transition already 118 * has an attribute with the name. 119 */ 120 public OutputActionsAttribute(Transition transition, String name) 121 throws IllegalActionException, NameDuplicationException { 122 super(transition, name); 123 } 124 125 /////////////////////////////////////////////////////////////////// 126 //// public methods //// 127 128 /** Send tokens to the designated outputs. Each token is determined 129 * by evaluating the expression portion of the action. 130 * @exception IllegalActionException If expression evaluation fails, 131 * or the specified port is not found, or sending to one of the 132 * channels of the port throws a NoRoomException. 133 */ 134 @Override 135 public void execute() throws IllegalActionException { 136 super.execute(); 137 138 if (_destinations != null && _destinations.size() > 0) { 139 Iterator destinations = _destinations.iterator(); 140 Iterator channels = getChannelNumberList().iterator(); 141 Iterator parseTrees = _parseTrees.iterator(); 142 143 while (destinations.hasNext()) { 144 NamedObj nextDestination = (NamedObj) destinations.next(); 145 146 if (!(nextDestination instanceof IOPort)) { 147 throw new IllegalActionException(this, 148 "Destination is not an IOPort: " 149 + nextDestination.getFullName()); 150 } 151 152 IOPort destination = (IOPort) nextDestination; 153 boolean isInput = destination.isInput(); 154 Integer channel = (Integer) channels.next(); 155 ASTPtRootNode parseTree = (ASTPtRootNode) parseTrees.next(); 156 Token token; 157 158 try { 159 token = _parseTreeEvaluator.evaluateParseTree(parseTree, 160 _getParserScope()); 161 } catch (IllegalActionException ex) { 162 // Chain exceptions to get the actor that 163 // threw the exception. 164 throw new IllegalActionException(this, ex, 165 "Expression invalid."); 166 } 167 168 try { 169 if (token != null) { 170 // get the local receivers of the destination port. 171 // Note that if the destination port is an output port, 172 // an _EMPTY_RECEIVER_ARRAY is returned. 173 Receiver[][] localReceivers = destination 174 .getReceivers(); 175 176 if (channel != null) { 177 int chanelValue = channel.intValue(); 178 destination.send(chanelValue, token); 179 180 if (isInput) { 181 // If the destination is both input and output, 182 // also send the tokens to local receivers. 183 localReceivers[chanelValue][0].put(token); 184 } 185 186 if (_debugging) { 187 _debug(getFullName() + " port: " 188 + destination.getName() + " channel: " 189 + chanelValue + ", token: " + token); 190 } 191 } else { 192 destination.broadcast(token); 193 194 if (isInput) { 195 // If the destination is both input and output, 196 // also send the tokens to local receivers. 197 for (Receiver[] localReceiver : localReceivers) { 198 localReceiver[0].put(token); 199 } 200 } 201 202 if (_debugging) { 203 _debug(getFullName() + " port: " 204 + destination.getName() + " token: " 205 + token); 206 } 207 } 208 } 209 } catch (NoRoomException ex) { 210 throw new IllegalActionException(this, 211 "Cannot complete action: " + ex.getMessage()); 212 } catch (UnknownResultException ex) { 213 // Produce no output. 214 } 215 } 216 } 217 } 218 219 /////////////////////////////////////////////////////////////////// 220 //// protected methods //// 221 222 /** Given a destination name, return a NamedObj that matches that 223 * destination. 224 * @param name The name of the destination, or null if none is found. 225 * @return An object (like a port or a variable) with the specified name. 226 * @exception IllegalActionException If the associated FSMActor 227 * does not have a destination with the specified name. 228 */ 229 @Override 230 protected NamedObj _getDestination(String name) 231 throws IllegalActionException { 232 Transition transition = (Transition) getContainer(); 233 234 if (transition == null) { 235 throw new IllegalActionException(this, 236 "Action has no container transition."); 237 } 238 239 Entity fsm = (Entity) transition.getContainer(); 240 241 if (fsm == null) { 242 throw new IllegalActionException(this, transition, 243 "Transition has no container."); 244 } 245 246 IOPort port = (IOPort) fsm.getPort(name); 247 248 if (port == null) { 249 throw new IllegalActionException(fsm, this, 250 "Cannot find port with name: " + name); 251 } 252 253 if (!port.isOutput()) { 254 throw new IllegalActionException(fsm, this, 255 "The port is not an output port: " + name); 256 } 257 258 return port; 259 } 260}