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}