001/* An output port that publishes its data on a named channel.
002
003 Copyright (c) 1997-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.actor;
028
029import ptolemy.data.IntToken;
030import ptolemy.data.Token;
031import ptolemy.data.expr.Parameter;
032import ptolemy.data.type.BaseType;
033import ptolemy.kernel.ComponentEntity;
034import ptolemy.kernel.util.IllegalActionException;
035import ptolemy.kernel.util.NameDuplicationException;
036import ptolemy.kernel.util.Settable;
037
038///////////////////////////////////////////////////////////////////
039//// ConstantPublisherPort
040
041/**
042 This is a specialized output port that publishes constant data sent on
043 the specified named channel.  The tokens are
044 "tunneled" to any instance of {@link SubscriberPort} that names the same channel.
045 If {@link #global} is false (the default), then this publisher
046 will only send to instances of SubscriberPort that are under the
047 control of the same director. That is, it can
048 be at a different level of the hierarchy, or in an entirely different
049 composite actor, as long as the relevant composite actors are
050 transparent (have no director). If {@link #global} is true,
051 then the subscriber may be anywhere in the model, as long as its
052 <i>global</i> parameter is also true.
053  <p>
054 Note that this port should be used just like a {@link PublisherPort}.
055 If you put it in an opaque composite actor, then it requires a
056 token on the inside in order to produce its output. It will replace
057 each token with the constant value. Similarly, if it is put into
058 a transparent composite actor, then tokens must be sent through
059 it. Those tokens will be replaced with the constant value. If it
060 is put into an atomic actor, then the actor should call one of
061 its broadcast or send methods, providing a token that will be
062 replaced with the constant token. This pattern ensures that data
063 dependencies work with this port just as with any other port.
064<p>
065 It is an error to have two instances of PublisherPort
066 or ConstantPublisherPort using the same
067 channel under the control of the same director. When you create a
068 new PublisherPort or
069 ConstantPublisherPort, by default, it has no channel name. You have to
070 specify a channel name to use it.
071 <p>
072 <b>How it works:</b>
073 When the channel name
074 is specified, typically during model construction, this actor
075 causes a relation to be created in the least opaque composite
076 actor above it in the hierarchy and links to that relation.
077 In addition, if {@link #global} is set to true, it causes
078 a port to be created in that composite, and also links that
079 port to the relation on the inside.  The relation is recorded by the opaque
080 composite.  When a SubscriberPort is preinitialized that refers
081 to the same channel, that SubscriberPort finds the relation (by
082 finding the least opaque composite actor above it) and links
083 to the relation. Some of these links are "liberal links" in that
084 they cross levels of the hierarchy.
085 <p>
086 Since publishers are linked to subscribers,
087 any data dependencies that the director might assume on a regular
088 "wired" connection will also be assumed across publisher-subscriber
089 pairs. Similarly, type constraints will propagate across
090 publisher-subscriber pairs. That is, the type of the subscriber
091 output will match the type of the publisher input.
092
093 @author Edward A. Lee
094 @version $Id$
095 @since Ptolemy II 10.0
096 @Pt.ProposedRating Yellow (eal)
097 @Pt.AcceptedRating Red (eal)
098 */
099public class ConstantPublisherPort extends PublisherPort {
100
101    /** Construct a constant publisher port with the specified name and container.
102     *  @param container The container actor.
103     *  @param name The name of the port.
104     *  @exception IllegalActionException If the port is not of an acceptable
105     *   class for the container, or if the container does not implement the
106     *   Actor interface.
107     *  @exception NameDuplicationException If the name coincides with
108     *   a port already in the container.
109     */
110    public ConstantPublisherPort(ComponentEntity container, String name)
111            throws IllegalActionException, NameDuplicationException {
112        super(container, name);
113
114        constantValue = new Parameter(this, "constantValue");
115        constantValue.setExpression("0");
116
117        Parameter UNBOUNDED = new Parameter(this, "UNBOUNDED");
118        UNBOUNDED.setTypeEquals(BaseType.INT);
119        UNBOUNDED.setExpression("-1");
120        UNBOUNDED.setPersistent(false);
121        UNBOUNDED.setVisibility(Settable.NONE);
122
123        numberOfTokens = new Parameter(this, "numberOfTokens");
124        numberOfTokens.setExpression("UNBOUNDED");
125        numberOfTokens.setTypeEquals(BaseType.INT);
126
127        numberOfTokens.moveToFirst();
128        constantValue.moveToFirst();
129
130        // Hide the initial tokens, as they make no sense for this port.
131        initialTokens.setVisibility(Settable.NONE);
132
133        setTypeSameAs(constantValue);
134    }
135
136    ///////////////////////////////////////////////////////////////////
137    ////                         parameters                        ////
138
139    /** The constant value to publish. This can have any type.
140     *  It defaults to the integer 0.
141     */
142    public Parameter constantValue;
143
144    /** The number of constant tokens to publish. By default,
145     *  this is UNBOUNDED, which means that there is no limit.
146     */
147    public Parameter numberOfTokens;
148
149    ///////////////////////////////////////////////////////////////////
150    ////                         public methods                    ////
151
152    /** Override the base class to replace the specified token with
153     *  the value of <i>constantValue</i>.
154     *  @param token A token, which will be replaced.
155     *  @exception IllegalActionException Not thrown in this base class.
156     *  @exception NoRoomException If a send to one of the channels throws
157     *     it.
158     */
159    @Override
160    public void broadcast(Token token)
161            throws IllegalActionException, NoRoomException {
162        if (token == null) {
163            super.broadcast(null);
164        } else {
165            int limit = ((IntToken) numberOfTokens.getToken()).intValue();
166            if (limit >= 0) {
167                if (_numberOfTokensSent >= limit) {
168                    return;
169                }
170                _numberOfTokensSent++;
171            }
172            super.broadcast(constantValue.getToken());
173        }
174    }
175
176    /** Override the base class to replace the specified tokens with
177     *  the value of <i>constantValue</i>.
178     *  @param tokenArray The token array to replace.
179     *  @param vectorLength The number of elements of the token
180     *   array to send.
181     *  @exception NoRoomException If there is no room in the receiver.
182     *  @exception IllegalActionException Not thrown in this base class.
183     */
184    @Override
185    public void broadcast(Token[] tokenArray, int vectorLength)
186            throws IllegalActionException, NoRoomException {
187        Token[] replacement = new Token[tokenArray.length];
188        for (int i = 0; i < tokenArray.length; i++) {
189            replacement[i] = tokenArray[i];
190        }
191        int limit = ((IntToken) numberOfTokens.getToken()).intValue();
192        if (limit >= 0) {
193            if (_numberOfTokensSent >= limit) {
194                return;
195            }
196            _numberOfTokensSent += vectorLength;
197        }
198        super.broadcast(replacement, vectorLength);
199    }
200
201    /** Override the base class to initialize the token count.
202     *  @exception IllegalActionException If initialTokens is invalid.
203     */
204    @Override
205    public void initialize() throws IllegalActionException {
206        super.initialize();
207        _numberOfTokensSent = 0;
208    }
209
210    /** Override the base class to replace the specified token with
211     *  the value of <i>constantValue</i>.
212     *  @param channelIndex The index of the channel, from 0 to width-1
213     *  @param token The token to replace, or null to send no token.
214     *  @exception NoRoomException If there is no room in the receiver.
215     *  @exception IllegalActionException Not thrown in this base class.
216     */
217    @Override
218    public void send(int channelIndex, Token token)
219            throws IllegalActionException, NoRoomException {
220        if (token == null) {
221            super.send(channelIndex, null);
222        } else {
223            int limit = ((IntToken) numberOfTokens.getToken()).intValue();
224            if (limit >= 0) {
225                if (_numberOfTokensSent >= limit) {
226                    return;
227                }
228                _numberOfTokensSent++;
229            }
230            super.send(channelIndex, constantValue.getToken());
231        }
232    }
233
234    /** Override the base class to replace the specified tokens with
235     *  the value of <i>constantValue</i>.
236     *  @param channelIndex The index of the channel, from 0 to width-1
237     *  @param tokenArray The token array to replace.
238     *  @param vectorLength The number of elements of of the token
239     *   array to send.
240     *  @exception NoRoomException If there is no room in the receiver.
241     *  @exception IllegalActionException Not thrown in this base class.
242     */
243    @Override
244    public void send(int channelIndex, Token[] tokenArray, int vectorLength)
245            throws IllegalActionException, NoRoomException {
246        Token[] replacement = new Token[tokenArray.length];
247        for (int i = 0; i < tokenArray.length; i++) {
248            replacement[i] = tokenArray[i];
249        }
250        int limit = ((IntToken) numberOfTokens.getToken()).intValue();
251        if (limit >= 0) {
252            if (_numberOfTokensSent >= limit) {
253                return;
254            }
255            _numberOfTokensSent += vectorLength;
256        }
257        super.send(channelIndex, replacement, vectorLength);
258    }
259
260    ///////////////////////////////////////////////////////////////////
261    ////                         private variables                 ////
262
263    /** Number of tokens sent since initialize(). */
264    private int _numberOfTokensSent = 0;
265}