001/*
002 * Copyright (c) 2003-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-08-24 22:48:48 +0000 (Mon, 24 Aug 2015) $' 
007 * '$Revision: 33634 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.sms.actors;
031
032import java.awt.event.ActionEvent;
033import java.util.Iterator;
034import java.util.Vector;
035
036import diva.graph.GraphController;
037import ptolemy.actor.IOPort;
038import ptolemy.actor.TypeEvent;
039import ptolemy.actor.TypeListener;
040import ptolemy.actor.TypedAtomicActor;
041import ptolemy.actor.TypedIOPort;
042import ptolemy.data.IntToken;
043import ptolemy.data.Token;
044import ptolemy.data.expr.Parameter;
045import ptolemy.data.type.BaseType;
046import ptolemy.data.type.Type;
047import ptolemy.graph.InequalityTerm;
048import ptolemy.kernel.ComponentEntity;
049import ptolemy.kernel.CompositeEntity;
050import ptolemy.kernel.Relation;
051import ptolemy.kernel.util.ChangeRequest;
052import ptolemy.kernel.util.IllegalActionException;
053import ptolemy.kernel.util.NameDuplicationException;
054import ptolemy.kernel.util.NamedObj;
055import ptolemy.kernel.util.Workspace;
056import ptolemy.vergil.actor.ActorInstanceController;
057import ptolemy.vergil.basic.NamedObjController;
058import ptolemy.vergil.basic.NodeControllerFactory;
059import ptolemy.vergil.toolbox.FigureAction;
060import ptolemy.vergil.toolbox.MenuActionFactory;
061
062/*
063
064 OPERATIONS: 
065
066
067 Vector       getActors()                            // the actors connected to merge actor
068 Iterator     getTargetPorts(IOPort inputPort)       // target (output) i/o ports for inputPort
069 Iterator     getInputPortMappings(IOPort port)      // get mappings for input port
070 void         removeOutputPort(IOPort port)          // removes the given output port
071 Vector       getActorPorts(NamedObj actor)          // output ports of source actor
072 Iterator     getMappings()                          // all merge actor mappings
073 void         setProductionRate()
074
075 void         _computeTargetTypes()                  // sets target (output) types
076 Vector       _getActorPortChannels(NamedObj actor)  // return the ports for the connected actor
077 void         _applyMerge()                          // applies the merge mapping
078 PortChannel  _getPortChannel(IOPort inputPort)      // input ports as port channels
079 Iterator     _getInputPorts()                       // input i/o ports
080 void         _initInputPorts()                      // reset input ports (and listeners)
081 void         _removeMappings() 
082
083
084 */
085
086/**
087 * This is a first-cut, dumbed-down, and simple version of a generic merge
088 * function based on semantic annotations.
089 */
090public class MergeActor extends TypedAtomicActor implements TypeListener {
091
092        public MergeInputPort mergeInputPort; // single multiport for inputs to be
093                                                                                        // merged
094
095        /**
096         * Constructor
097         */
098        public MergeActor(CompositeEntity container, String name)
099                        throws NameDuplicationException, IllegalActionException {
100                super(container, name);
101                init();
102        }
103
104        /**
105         * Constructor
106         */
107        public MergeActor(Workspace workspace) throws NameDuplicationException,
108                        IllegalActionException {
109                super(workspace);
110                init();
111        }
112
113        /**
114         * Initialize the actor
115         */
116        private void init() throws IllegalActionException, NameDuplicationException {
117                // add the input port
118                mergeInputPort = new MergeInputPort(this, "input");
119
120                // black, blue, cyan, darkgray, gray, green, lightgray, magenta,
121                // orange, pink, red, white, yellow
122                _attachText("_iconDescription", "<svg>\n"
123                                + "<rect x=\"0\" y=\"0\" rx=\"10\" ry=\"10\" "
124                                + "width=\"80\" height=\"40\" " + "style=\"fill:orange\"/>\n"
125                                + "<text x=\"12.5\" y=\"25\" "
126                                + "style=\"font-size:20; fill:darkgray; "
127                                + "font-family:SansSerif\">" + "merge</text>\n" + "</svg>\n");
128
129                // inPort.addTypeListener?
130
131                // Create a node controller to control the context menu
132                _nodeController = new MergeActorControllerFactory(this,
133                                "_controllerFactory");
134        }
135
136        public void typeChanged(TypeEvent event) {
137                _computeTargetTypes();
138        }
139
140        public void preinitialize() throws IllegalActionException {
141                super.preinitialize();
142                _initInputPorts();
143                _computeTargetTypes();
144        }
145
146        public void fire() throws IllegalActionException {
147                super.fire();
148
149                Iterator actors = getActors().iterator();
150                while (actors.hasNext()) {
151                        NamedObj actor = (NamedObj) actors.next();
152                        _applyMerge(actor);
153                }
154        }
155
156        public void computeMerge() throws IllegalActionException,
157                        NameDuplicationException {
158                _removeMappings();
159                _initInputPorts();
160
161                SimpleComputeMergeAlgorithm alg = new SimpleComputeMergeAlgorithm(this);
162                alg.computeMerge();
163
164                // assign types to target outputs
165                _computeTargetTypes();
166                setProductionRate();
167
168                // refresh the canvas
169                ChangeRequest r = new EmptyChangeRequest(this, "update request");
170                toplevel().requestChange(r);
171        }
172
173        public void computeMergeOld() throws IllegalActionException,
174                        NameDuplicationException {
175                // check if any mappings already if so, warn that they will be
176                // removed then remove them
177                _removeMappings();
178                _initInputPorts();
179
180                // first step: add one output port per input port
181                Iterator iter = _getInputPorts();
182                for (int i = 0; iter.hasNext(); i++) {
183                        IOPort p = (IOPort) iter.next();
184                        String sourceActor = p.getContainer().getName();
185                        String sourcePort = p.getName();
186                        String targetPort = sourceActor + "_" + sourcePort;
187                        TypedIOPort output = new TypedIOPort(this, targetPort);
188                        output.setOutput(true);
189                        output.setInput(false);
190                        output.setMultiport(false);
191                        // create a mapping
192                        String mergeName = "merge_" + i;
193                        SimpleMergeMapping m = new SimpleMergeMapping(this, mergeName,
194                                        sourceActor, sourcePort, targetPort);
195                }// end for
196
197                // assign types to target outputs
198                _computeTargetTypes();
199                setProductionRate();
200
201                // refresh the canvas
202                ChangeRequest r = new EmptyChangeRequest(this, "update request");
203                toplevel().requestChange(r);
204        }
205
206        public void editMerge() throws IllegalActionException,
207                        NameDuplicationException {
208                // open the dialog
209                MergeEditorDialog editor = new MergeEditorDialog(null, this);
210                editor.setVisible(true);
211        }
212
213        /**
214     * 
215     */
216        public Iterator<SimpleMergeMapping> getInputPortMappings(IOPort port) {
217                Vector<SimpleMergeMapping> results = new Vector<SimpleMergeMapping>();
218                if (port == null)
219                        return results.iterator();
220                Iterator<SimpleMergeMapping> iter = attributeList(SimpleMergeMapping.class).iterator();
221                while (iter.hasNext()) {
222                        try {
223                                SimpleMergeMapping m = iter.next();
224                                String actorName = port.getContainer().getName();
225                                String portName = port.getName();
226                                if (actorName.equals(m.getSourceActor())) {
227                                        if (portName.equals(m.getSourceActorPort()))
228                                                results.add(m);
229                                }
230                        } catch (Exception e) {
231                                e.printStackTrace();
232                        }
233                }
234                return results.iterator();
235        }
236
237        /**
238     *
239     */
240        public Vector getMappings() {
241                return new Vector(attributeList(SimpleMergeMapping.class));
242        }
243
244        /**
245         * removes the given output port
246         */
247        public void removeOutputPort(IOPort port) {
248                try {
249                        port.setContainer(null);
250                        // refresh the canvas
251                        ChangeRequest r = new EmptyChangeRequest(this, "update request");
252                        toplevel().requestChange(r);
253                } catch (Exception e) {
254                        e.printStackTrace();
255                }
256        }
257
258        /**
259         * Determine the output types based on the types of input port.
260         */
261        private void _computeTargetTypes() {
262                Iterator iter = _getInputPorts();
263                while (iter.hasNext()) {
264                        IOPort p = (IOPort) iter.next();
265                        if (p instanceof TypedIOPort) {
266                                Iterator<IOPort> targets = getTargetPorts(p);
267                                while (targets.hasNext()) {
268                                        TypedIOPort target = (TypedIOPort) targets.next();
269                                        Type targetType = target.getType();
270                                        Type inputType = ((TypedIOPort) p).getType();
271                                        if (targetType.equals(BaseType.UNKNOWN)
272                                                        || inputType.isCompatible(targetType))
273                                                target.setTypeEquals(inputType);
274                                }
275                        }
276                }// end while
277        }
278
279        /**
280         * A helper funtion that computes the port channel (port, channel) of the
281         * given port.
282         */
283        private PortChannel _getPortChannel(IOPort inputPort) {
284                Iterator channels = _getInputPorts();
285                for (int channel = 0; channels.hasNext(); channel++) {
286                        IOPort p = (IOPort) channels.next();
287                        if (p == inputPort)
288                                return new PortChannel(inputPort, channel);
289                }// end while
290                return null;
291        }
292
293        /**
294         * Calculates the set of actors that are connected to this merge actor via
295         * one or more input ports.
296         * 
297         * @return An iterator containing actors connected to this merge actor.
298         */
299        public Vector getActors() {
300                Vector results = new Vector();
301                Iterator iter = _getInputPorts();
302                while (iter.hasNext()) {
303                        IOPort p = (IOPort) iter.next();
304                        NamedObj o = p.getContainer();
305                        if (!results.contains(o))
306                                results.add(o);
307                }// end while
308                return results;
309        }
310
311        /**
312         * For a connected actor, applies the merge rules producing data tokens.
313         */
314        private void _applyMerge(NamedObj actor) {
315                Vector portChannels = _getActorPortChannels(actor);
316                while (_actorHasTokens(portChannels)) {
317                        _passMergeTokens(portChannels);
318                        _passNonMergeTokens(portChannels);
319                }
320        }
321
322        private void _passNonMergeTokens(Vector portChannels) {
323                Vector inputActorPorts = new Vector();
324                // get all the input ports from portChannels (i.e., ports for
325                // current actor)
326                Iterator iter = portChannels.iterator();
327                while (iter.hasNext()) {
328                        PortChannel p = (PortChannel) iter.next();
329                        inputActorPorts.add(p.getPort());
330                }
331                // get all the target output ports for ports in inputActorPorts
332                Vector<IOPort> targetOutputPorts = new Vector<IOPort>();
333                iter = inputActorPorts.iterator();
334                while (iter.hasNext()) {
335                        IOPort p = (IOPort) iter.next();
336                        Iterator<IOPort> targets = getTargetPorts(p);
337                        while (targets.hasNext()) {
338                                IOPort target = targets.next();
339                                if (!targetOutputPorts.contains(target))
340                                        targetOutputPorts.add(target);
341                        }
342                }
343                // get all the output ports not in targetOutputPorts
344                Vector nonMergeOutputPorts = new Vector();
345                iter = outputPortList().iterator();
346                while (iter.hasNext()) {
347                        IOPort p = (IOPort) iter.next();
348                        if (!targetOutputPorts.contains(p))
349                                nonMergeOutputPorts.add(p);
350                }
351
352                // send a "null" value to each of the nonMergeOutputPorts
353                iter = nonMergeOutputPorts.iterator();
354                while (iter.hasNext()) {
355                        TypedIOPort p = (TypedIOPort) iter.next();
356                        // get the type of the output port of p (FIXME:
357                        // _computeType should set output port to "largest" of
358                        // inputs that map to it)
359                        Token t = null;
360                        Type type = p.getType();
361                        Class cls = type.getTokenClass();
362                        try {
363                                // FIXME: SHOULD BE TO t.null(), BUT null() UNIMPLEMENTED
364                                t = (Token) cls.newInstance();
365                                p.send(0, t.zero());
366                        } catch (Exception e) {
367                                e.printStackTrace();
368                        }
369                        System.out.println("SENT " + p.getName() + " TOKEN " + t);
370                }
371        }
372
373        private void _passMergeTokens(Vector portChannels) {
374                Iterator iter = portChannels.iterator();
375                while (iter.hasNext()) {
376                        try {
377                                PortChannel p = (PortChannel) iter.next();
378                                IOPort in = p.getPort();
379                                int channel = p.getChannel();
380                                System.out.println("PASS MERGE TOKENS \n ... PORT: '"
381                                                + in.getName() + "' CHANNEL: '" + channel + "'");
382                                Token t = mergeInputPort.get(channel);
383                                Iterator<IOPort> ptargets = getTargetPorts(in);
384                                while (ptargets.hasNext()) {
385                                        IOPort ptarget = ptargets.next();
386                                        ptarget.send(0, t);
387                                        System.out.println("SENT " + ptarget.getName() + " TOKEN "
388                                                        + t + " FROM " + in.getName());
389                                }// end while
390                        } catch (IllegalActionException e) {
391                                e.printStackTrace();
392                        }
393                }
394        }
395
396        /**
397         * Determines if there is a token on the port channels
398         */
399        private boolean _actorHasTokens(Vector portChannels) {
400                Iterator iter = portChannels.iterator();
401                while (iter.hasNext()) {
402                        try {
403                                PortChannel p = (PortChannel) iter.next();
404                                if (!mergeInputPort.hasToken(p.getChannel()))
405                                        return false;
406                        } catch (IllegalActionException e) {
407                                e.printStackTrace();
408                        }
409                }
410                return true;
411        }
412
413        /**
414         * Given the mappings, determines which ports of the given actor are to be
415         * used as well as their channel.
416         * 
417         * @return The given source actor's ports and channels as PortChannels
418         */
419        private Vector _getActorPortChannels(NamedObj actor) {
420                Vector results = new Vector();
421                Iterator iter = _getInputPorts();
422                for (int channel = 0; iter.hasNext(); channel++) {
423                        IOPort p = (IOPort) iter.next();
424                        if (actor.equals(p.getContainer()))
425                                results.add(new PortChannel(p, channel));
426                }
427                return results;
428        }
429
430        /**
431         * @return The output ports of actor connected to the merge actor
432         */
433        public Vector<IOPort> getActorPorts(NamedObj actor) {
434                Vector<IOPort> results = new Vector<IOPort>();
435                Iterator iter = _getInputPorts();
436                while (iter.hasNext()) {
437                        IOPort p = (IOPort) iter.next();
438                        if (actor.equals(p.getContainer()))
439                                results.add(p);
440                }
441                return results;
442        }
443
444        public Iterator<IOPort> getTargetPorts(IOPort inPort) {
445                Vector<IOPort> results = new Vector<IOPort>();
446                String actorSource = inPort.getContainer().getName();
447                String portName = inPort.getName();
448                Iterator maps = attributeList(SimpleMergeMapping.class).iterator();
449                while (maps.hasNext()) {
450                        SimpleMergeMapping m = (SimpleMergeMapping) maps.next();
451                        if (actorSource.equals(m.getSourceActor())
452                                        && portName.equals(m.getSourceActorPort())) {
453                                String outName = m.getTargetPort();
454                                Iterator ports = outputPortList().iterator();
455                                while (ports.hasNext()) {
456                                        IOPort p = (IOPort) ports.next();
457                                        if (outName.equals(p.getName()))
458                                                results.add(p);
459                                }
460                        }
461                }
462                return results.iterator();
463        }
464
465        private void _initInputPorts() {
466                // remove old listeners
467                Iterator iter = _getInputPorts();
468                while (iter.hasNext()) {
469                        IOPort p = (IOPort) iter.next();
470                        if (p instanceof TypedIOPort)
471                                ((TypedIOPort) p).removeTypeListener(this);
472                }// end while
473
474                // create new set of input ports
475                _inputPorts = new Vector();
476                iter = mergeInputPort.connectedPortList().iterator();
477                while (iter.hasNext()) {
478                        IOPort p = (IOPort) iter.next();
479                        _inputPorts.add(p);
480                        if (p instanceof TypedIOPort)
481                                ((TypedIOPort) p).addTypeListener(this);
482                }// end while
483        }
484
485        private Iterator _getInputPorts() {
486                return mergeInputPort.connectedPortList().iterator();
487        }
488
489        private void _removeMappings() throws IllegalActionException,
490                        NameDuplicationException {
491                // remove the mapping attributes
492                Iterator iter = attributeList(SimpleMergeMapping.class).iterator();
493                ;
494                while (iter.hasNext()) {
495                        SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
496                        m.setContainer(null);
497                }
498                // remove the output ports
499                iter = outputPortList().iterator();
500                while (iter.hasNext()) {
501                        IOPort p = (IOPort) iter.next();
502                        p.setContainer(null);
503                }
504        }
505
506        public void setProductionRate() {
507                // the number of production tokens is num(actors)
508                // production rate set on each output port
509                Vector actors = getActors();
510                int rate = actors.size();
511                Iterator iter = outputPortList().iterator();
512                while (iter.hasNext()) {
513                        try {
514                                IOPort p = (IOPort) iter.next();
515                                Parameter tpr = (Parameter) p
516                                                .getAttribute("tokenProductionRate");
517                                if (tpr == null) {
518                                        tpr = new Parameter(this, "tokenProductionRate",
519                                                        new IntToken(rate));
520                                        tpr.setContainer(p);
521                                } else {
522                                        tpr.setToken(new IntToken(rate));
523                                }
524                        } catch (Exception e) {
525                                e.printStackTrace();
526                        }
527                }
528        }
529
530        public void update() {
531                // refresh the canvas
532                ChangeRequest r = new EmptyChangeRequest(this, "update request");
533                toplevel().requestChange(r);
534        }
535
536        //
537        // Inner classes
538        // 
539
540        /**
541         * Specific input port
542         */
543        class MergeInputPort extends TypedIOPort {
544
545                /**
546                 * constructor
547                 */
548                public MergeInputPort(ComponentEntity container, String name)
549                                throws IllegalActionException, NameDuplicationException {
550                        super(container, name);
551                        setInput(true);
552                        setOutput(false);
553                        setMultiport(true);
554                }
555
556                // not sure what needs to be overriden now
557                public void link(Relation relation) throws IllegalActionException {
558                        super.link(relation);
559                }
560
561                /**
562                 * Prevent types from being modified
563                 */
564                public Type getType() {
565                        return BaseType.GENERAL;
566                }
567
568                /**
569                 * Sabotage type resolution ...
570                 */
571                public InequalityTerm getTypeTerm() {
572                        return new MergePortTypeTerm();
573                }
574
575                private class MergePortTypeTerm implements InequalityTerm {
576                        public Object getAssociatedObject() {
577                                return MergeInputPort.this;
578                        }
579
580                        public Object getValue() {
581                                return getType();
582                        }
583
584                        public InequalityTerm[] getVariables() {
585                                return (new InequalityTerm[0]);
586                        }
587
588                        public void initialize(Object type) throws IllegalActionException {
589                        }
590
591                        public boolean isSettable() {
592                                return false;
593                        }
594
595                        public boolean isValueAcceptable() {
596                                return true;
597                        }
598
599                        public void setValue(Object type) throws IllegalActionException {
600                        }
601                }
602        };
603
604        /**
605     * 
606     */
607        private class MergeActorControllerFactory extends NodeControllerFactory {
608                private MergeActor _mergeActor;
609
610                public MergeActorControllerFactory(MergeActor mergeActor, String name)
611                                throws NameDuplicationException, IllegalActionException {
612                        super(mergeActor, name);
613                        _mergeActor = mergeActor;
614                }
615
616                public NamedObjController create(GraphController controller) {
617                        return new MergeActorInstanceController(controller, _mergeActor);
618                }
619        };
620
621        /**
622         * ...
623         */
624        public class MergeActorInstanceController extends ActorInstanceController {
625                public MergeActorInstanceController(GraphController controller,
626                                MergeActor mergeActor) {
627                        this(controller, FULL, mergeActor);
628                }
629
630                public MergeActorInstanceController(GraphController controller,
631                                Access access, MergeActor mergeActor) {
632                        super(controller, access);
633                        MenuActionFactory f1 = new MenuActionFactory(new MergeActorAction1(
634                                        mergeActor));
635                        _menuFactory.addMenuItemFactory(f1);
636                        MenuActionFactory f2 = new MenuActionFactory(new MergeActorAction2(
637                                        mergeActor));
638                        _menuFactory.addMenuItemFactory(f2);
639
640                }
641        }
642
643        /**
644         * ...
645         */
646        protected class MergeActorAction1 extends FigureAction {
647                private MergeActor _mergeActor;
648
649                public MergeActorAction1(MergeActor mergeActor) {
650                        super("Compute Merge");
651                        _mergeActor = mergeActor;
652                }
653
654                public void actionPerformed(ActionEvent ev) {
655                        try {
656                                _mergeActor.computeMerge();
657                        } catch (Exception e) {
658                                e.printStackTrace();
659                        }
660                }
661        };
662
663        /**
664         * ...
665         */
666        protected class MergeActorAction2 extends FigureAction {
667                private MergeActor _mergeActor;
668
669                public MergeActorAction2(MergeActor mergeActor) {
670                        super("Edit Merge Mappings");
671                        _mergeActor = mergeActor;
672                }
673
674                public void actionPerformed(ActionEvent ev) {
675                        try {
676                                _mergeActor.editMerge();
677                        } catch (Exception e) {
678                                e.printStackTrace();
679                        }
680                }
681        };
682
683        private class EmptyChangeRequest extends ChangeRequest {
684                public EmptyChangeRequest(Object o, String s) {
685                        super(o, s);
686                }
687
688                public void _execute() {
689                        // do nothing
690                }
691        };
692
693        /**
694         * A helper class that encapsulates a port and its channel
695         */
696        private class PortChannel {
697                public PortChannel(IOPort port, int channel) {
698                        _port = port;
699                        _channel = channel;
700                }
701
702                public IOPort getPort() {
703                        return _port;
704                }
705
706                public int getChannel() {
707                        return _channel;
708                }
709
710                private IOPort _port;
711                private int _channel;
712        };
713
714        /**
715         * Overriden from NamedObj ...
716         */
717        public String uniqueName(String prefix) {
718                if (prefix == null)
719                        prefix = "null";
720                prefix = _stripNumericSuffix(prefix);
721                String candidate = prefix + "1";
722                int uniqueNameIndex = 2;
723                while (_hasName(candidate))
724                        candidate = prefix + uniqueNameIndex++;
725                return candidate;
726        }
727
728        private boolean _hasName(String name) {
729                Iterator iter = containedObjectsIterator();
730                while (iter.hasNext()) {
731                        NamedObj obj = (NamedObj) iter.next();
732                        if (obj.getName().equals(name))
733                                return true;
734                }
735                return false;
736        }
737
738        // this needs to be a subclass of typed io port
739        private Vector _inputPorts = new Vector(); // the set of ports connected to
740                                                                                                // the mergeInputPort
741        private MergeActorControllerFactory _nodeController = null; // for updating
742                                                                                                                                // canvas
743        private Vector _indexPorts = new Vector();
744
745}