001/* Assemble input port channels into output bus.
002
003 Copyright (c) 2002-2014 The Regents of the University of California and
004 Research in Motion Limited.
005 All rights reserved.
006 Permission is hereby granted, without written agreement and without
007 license or royalty fees, to use, copy, modify, and distribute this
008 software and its documentation for any purpose, provided that the above
009 copyright notice and the following two paragraphs appear in all copies
010 of this software.
011
012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA OR RESEARCH IN MOTION
013 LIMITED BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL,
014 INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS
015 SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
016 OR RESEARCH IN MOTION LIMITED HAVE BEEN ADVISED OF THE POSSIBILITY OF
017 SUCH DAMAGE.
018
019 THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION LIMITED
020 SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
022 PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
023 BASIS, AND THE UNIVERSITY OF CALIFORNIA AND RESEARCH IN MOTION
024 LIMITED HAVE NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
025 ENHANCEMENTS, OR MODIFICATIONS.
026 PT_COPYRIGHT_VERSION_2
027 COPYRIGHTENDKEY
028
029 */
030package ptolemy.actor.lib;
031
032import java.util.Iterator;
033import java.util.List;
034
035import ptolemy.actor.TypedAtomicActor;
036import ptolemy.actor.TypedIOPort;
037import ptolemy.actor.TypedIORelation;
038import ptolemy.data.Token;
039import ptolemy.kernel.CompositeEntity;
040import ptolemy.kernel.Port;
041import ptolemy.kernel.util.IllegalActionException;
042import ptolemy.kernel.util.NameDuplicationException;
043
044///////////////////////////////////////////////////////////////////
045//// BusAssembler
046
047/**
048 Aggregate all input relation channels into one output "bus" relation.
049 The width of the output port (bus relation) is set to the sum of the
050 input port widths. During the fire method, the input port channels are
051 scanned sequentially for tokens with the output channel index
052 incremented for each input channel scanned. If an input channel has a
053 token, it is copied to the corresponding output channel.<p>
054
055 Note: The width of a single relation (e.g. connected to an input port)
056 may be controlled by adding a <em>width</em> parameter with an IntToken
057 value
058 representing the desired relation width<p>
059
060 @author Zoltan Kemenczy, Research in Motion Limited
061 @version $Id$
062 @since Ptolemy II 2.1
063 @Pt.ProposedRating Red (zkemenczy)
064 @Pt.AcceptedRating Red (cxh)
065 @see ptolemy.actor.IORelation
066 */
067public class BusAssembler extends TypedAtomicActor {
068    /** Construct a BusAssembler with the given container and name.
069     *  @param container The container.
070     *  @param name The name of this actor.
071     *  @exception IllegalActionException If this actor cannot be contained
072     *   by the proposed container.
073     *  @exception NameDuplicationException If the container already has an
074     *   actor with this name.
075     */
076    public BusAssembler(CompositeEntity container, String name)
077            throws NameDuplicationException, IllegalActionException {
078        super(container, name);
079        output = new TypedIOPort(this, "output", false, true);
080        output.setMultiport(true);
081        _attachText("_iconDescription",
082                "<svg>\n" + "<rect x=\"0\" y=\"0\" width=\"6\" "
083                        + "height=\"40\" style=\"fill:black\"/>\n"
084                        + "</svg>\n");
085    }
086
087    ///////////////////////////////////////////////////////////////////
088    ////                     ports and parameters                  ////
089
090    /** The input port. */
091    public TypedIOPort output;
092
093    ///////////////////////////////////////////////////////////////////
094    ////                         public methods                    ////
095
096    /** React to a change in an input port width. */
097    @Override
098    public void connectionsChanged(Port p) {
099        super.connectionsChanged(p);
100
101        if (p == output) {
102            return;
103        }
104
105        try {
106            _recalculateOutputWidth();
107        } catch (IllegalActionException ex) {
108        }
109    }
110
111    /** Iterate through input ports and transfer data sequentially from
112     *  input channels to output channels, maintaining input to output
113     *  channel mapping.
114     *  @exception IllegalActionException If calling send() or super.fire()
115     *  throws it.
116     */
117    @Override
118    public void fire() throws IllegalActionException {
119        super.fire();
120        Iterator inputPorts = inputPortList().iterator();
121        TypedIOPort inputPort = (TypedIOPort) (inputPorts.hasNext()
122                ? inputPorts.next()
123                : null);
124        int inputWidth = inputPort != null ? inputPort.getWidth() : 0;
125        int i = 0;
126        int j = 0;
127
128        while (inputPort != null) {
129            if (i < inputWidth && inputPort.hasToken(i)) {
130                Token t = inputPort.get(i);
131
132                if (j < _outputWidth) {
133                    output.send(j, t);
134                }
135            }
136
137            j++;
138
139            if (++i >= inputWidth) {
140                inputPort = (TypedIOPort) (inputPorts.hasNext()
141                        ? inputPorts.next()
142                        : null);
143                inputWidth = inputPort != null ? inputPort.getWidth() : 0;
144                i = 0;
145            }
146        }
147    }
148
149    /** Makes sure that there is only one relation connected to the
150     output port and recalculates its width. */
151    @Override
152    public void preinitialize() throws IllegalActionException {
153        if (outputPortList().size() > 1) {
154            throw new IllegalActionException(this,
155                    "can have only one output port.");
156        }
157
158        if (output.linkedRelationList().size() > 1) {
159            throw new IllegalActionException(this,
160                    "can have only one output relation linked.");
161        }
162
163        _recalculateOutputWidth();
164        super.preinitialize();
165    }
166
167    ///////////////////////////////////////////////////////////////////
168    ////                         private methods                   ////
169
170    /** Recalculate the output relation width. */
171    private void _recalculateOutputWidth() throws IllegalActionException {
172        List outputRelations = output.linkedRelationList();
173        _outputWidth = 0;
174
175        if (outputRelations.isEmpty()) {
176            return;
177        }
178
179        TypedIORelation outputRelation = (TypedIORelation) outputRelations
180                .get(0);
181        Iterator inputPorts = inputPortList().iterator();
182
183        while (inputPorts.hasNext()) {
184            TypedIOPort port = (TypedIOPort) inputPorts.next();
185            _outputWidth += port.getWidth(); // includes all linked relations
186        }
187
188        outputRelation.setWidth(_outputWidth);
189
190        // TODO: figure out how to obey if the output relation width is
191        // set (if isWidthFixed() would return a reliable true...)
192    }
193
194    ///////////////////////////////////////////////////////////////////
195    ////                         private variables                 ////
196    int _outputWidth = 0;
197}