001/*
002 *    $RCSfile$
003 *
004 *     $Author: crawl $
005 *       $Date: 2015-08-24 22:44:14 +0000 (Mon, 24 Aug 2015) $
006 *   $Revision: 33630 $
007 *
008 *  For Details: http://kepler-project.org
009 *
010 * Copyright (c) 2007 The Regents of the University of California.
011 * All rights reserved.
012 *
013 * Permission is hereby granted, without written agreement and without
014 * license or royalty fees, to use, copy, modify, and distribute this
015 * software and its documentation for any purpose, provided that the
016 * above copyright notice and the following two paragraphs appear in
017 * all copies of this software.
018 *
019 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
020 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
021 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
022 * IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY
023 * OF SUCH DAMAGE.
024 *
025 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
026 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
027 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
028 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY
029 * OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
030 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
031 */
032package org.kepler.monitor;
033
034import java.awt.Shape;
035import java.awt.geom.Rectangle2D;
036import java.util.ArrayList;
037import java.util.Arrays;
038import java.util.HashMap;
039import java.util.Iterator;
040import java.util.List;
041import java.util.Map;
042
043import javax.swing.JFrame;
044import javax.swing.Timer;
045
046import org.apache.commons.logging.Log;
047import org.apache.commons.logging.LogFactory;
048import org.kepler.monitor.MonitoredStatus.State;
049import org.kepler.monitor.figure.TrafficLightFigure;
050
051import diva.canvas.CompositeFigure;
052import diva.canvas.Figure;
053import diva.graph.GraphController;
054import diva.graph.GraphPane;
055import diva.graph.GraphViewEvent;
056import diva.graph.GraphViewListener;
057import diva.graph.JGraph;
058import ptolemy.actor.CompositeActor;
059import ptolemy.actor.ExecutionListener;
060import ptolemy.actor.IOPort;
061import ptolemy.actor.IOPortEvent;
062import ptolemy.actor.IOPortEventListener;
063import ptolemy.actor.Initializable;
064import ptolemy.actor.Manager;
065import ptolemy.actor.gui.Configuration;
066import ptolemy.actor.gui.Effigy;
067import ptolemy.actor.gui.Tableau;
068import ptolemy.actor.gui.TableauFactory;
069import ptolemy.data.BooleanToken;
070import ptolemy.data.StringToken;
071import ptolemy.data.Token;
072import ptolemy.data.expr.Parameter;
073import ptolemy.data.expr.SingletonParameter;
074import ptolemy.data.expr.StringParameter;
075import ptolemy.data.type.BaseType;
076import ptolemy.kernel.ComponentEntity;
077import ptolemy.kernel.CompositeEntity;
078import ptolemy.kernel.Port;
079import ptolemy.kernel.util.Attribute;
080import ptolemy.kernel.util.ChangeRequest;
081import ptolemy.kernel.util.IllegalActionException;
082import ptolemy.kernel.util.Location;
083import ptolemy.kernel.util.NameDuplicationException;
084import ptolemy.kernel.util.NamedObj;
085import ptolemy.kernel.util.Settable;
086import ptolemy.kernel.util.SingletonAttribute;
087import ptolemy.moml.MoMLChangeRequest;
088import ptolemy.vergil.actor.ActorController;
089import ptolemy.vergil.actor.ActorGraphFrame;
090import ptolemy.vergil.icon.EditorIcon;
091
092/**
093 * The monitor manager.
094 * <p>
095 * An instance of this attribute can be inserted in a workflow to dynamically
096 * associate activity icons to the existing entities (
097 * {@link ptolemy.kernel.ComponentEntity}).
098 * 
099 * <p>
100 * Demo workflows are under $KEPLER/workflows/test/monitor/
101 * 
102 * <p>
103 * Note: Configuration is very limited in this preliminary version.
104 * 
105 * @author Carlos Rueda
106 * @version $Id: MonitorManager.java 33630 2015-08-24 22:44:14Z crawl $
107 */
108public class MonitorManager extends SingletonAttribute implements Initializable {
109
110        /**
111         * The icon type for all created monitor attributes
112         */
113        public StringParameter iconType;
114
115        /** Delay for the timer */
116        public StringParameter timerDelay;
117
118        /**
119         * Add monitor attributes for input ports?
120         */
121        public Parameter addInputPortCounters;
122
123        /**
124         * Add monitor attributes for output ports?
125         */
126        public Parameter addOutputPortCounters;
127
128        /**
129         * Creates a monitor manager.
130         * 
131         * @see ptolemy.kernel.util.SingletonAttribute#SingletonAttribute(NamedObj,
132         *      String)
133         */
134        public MonitorManager(NamedObj container, String name)
135                        throws IllegalActionException, NameDuplicationException {
136                super(container, name);
137
138                // set up my own icon and figure:
139                _managerIcon = new MonitorIcon(this, "_icon");
140                _managerIcon.setText("MonitorManager");
141                _managerFigure = new TrafficLightFigure(1, new Rectangle2D.Double(3, 8,
142                                7, 7));
143                _managerIcon.setFigure(_managerFigure);
144
145                // Parameters:
146                iconType = new StringParameter(this, "iconType");
147                String none = ICON_TYPE_NONE;
148                String[] iconTypes = MonitorAttribute.iconTypes();
149                iconType.setExpression(none);
150                iconType.addChoice(none);
151                for (int i = 0; i < iconTypes.length; i++) {
152                        // don't show the COUNTER style:
153                        if (!iconTypes[i].equals(MonitorAttribute.COUNTER)) {
154                                iconType.addChoice(iconTypes[i]);
155                        }
156                }
157                iconType.setDisplayName("Monitor icon type for entities");
158
159                timerDelay = new StringParameter(this, "timerDelay");
160                timerDelay.setExpression("1000");
161                timerDelay.setDisplayName("Timer delay for created indicators (ms)");
162
163                addInputPortCounters = new Parameter(this, "addInputPortCounters");
164                addInputPortCounters.setTypeEquals(BaseType.BOOLEAN);
165                addInputPortCounters.setExpression("false");
166                addInputPortCounters.setDisplayName("Add counters for input ports?");
167
168                addOutputPortCounters = new Parameter(this, "addOutputPortCounters");
169                addOutputPortCounters.setTypeEquals(BaseType.BOOLEAN);
170                addOutputPortCounters.setExpression("false");
171                addOutputPortCounters.setDisplayName("Add counters for output ports?");
172
173                if (isDebugging) {
174                        log.debug("<" + getFullName() + "> container=" + container
175                                        + "  name=" + name);
176                }
177                
178        // Hide the name.
179        SingletonParameter hideName = new SingletonParameter(this, "_hideName");
180        hideName.setToken(BooleanToken.TRUE);
181        hideName.setVisibility(Settable.EXPERT);
182        }
183
184        /**
185         * Sets the icon type and timer delay according to the changed attribute.
186         */
187        @Override
188    public void attributeChanged(Attribute attribute)
189                        throws IllegalActionException {
190                if (attribute == iconType) {
191                        String iconTypeValue = iconType.stringValue();
192                        if (iconTypeValue.equals(ICON_TYPE_NONE)
193                                        || Arrays.asList(MonitorAttribute.iconTypes()).contains(
194                                                        iconTypeValue)) {
195                                _iconType = iconTypeValue;
196                        } else {
197                                throw new IllegalActionException("Unexpected iconType: "
198                                                + iconTypeValue);
199                        }
200                } else if (attribute == timerDelay) {
201                        try {
202                                _timerDelay = Integer.parseInt(timerDelay.stringValue());
203                        } catch (NumberFormatException e) {
204                                throw new IllegalActionException("NumberFormatException: " + e);
205                        }
206                } else if (attribute == addInputPortCounters) {
207                        Token token = addInputPortCounters.getToken();
208                        boolean newValue = ((BooleanToken) token).booleanValue();
209                        if (newValue != _addInputPortCounters) {
210                                _addInputPortCounters = newValue;
211                        }
212                } else if (attribute == addOutputPortCounters) {
213                        Token token = addOutputPortCounters.getToken();
214                        boolean newValue = ((BooleanToken) token).booleanValue();
215                        if (newValue != _addOutputPortCounters) {
216                                _addOutputPortCounters = newValue;
217                        }
218                } else {
219                        super.attributeChanged(attribute);
220                }
221        }
222
223        /**
224         * Finishes the monitors.
225         */
226        @Override
227    public void wrapup() throws IllegalActionException {
228                if (isDebugging) {
229                        log.debug("<" + getFullName() + "> #wrapup");
230                }
231                Manager manager = ((CompositeActor) getContainer()).getManager();
232                if (manager != null) {
233                        manager.removeExecutionListener(_executionListener);
234                }
235                _executionFinished();
236        }
237
238        /**
239         * If container is null, it finishes monitoring
240         */
241        @Override
242    public void setContainer(NamedObj container) throws IllegalActionException,
243                        NameDuplicationException {
244
245                if (isDebugging) {
246                        log.debug("<" + getFullName() + "> new container=" + container);
247                }
248
249                NamedObj previousContainer = getContainer();
250                super.setContainer(container);
251
252                if (previousContainer != null) {
253                        if (previousContainer instanceof Initializable) {
254                                ((Initializable) previousContainer).removeInitializable(this);
255                        }
256                }
257
258                if (container != null) {
259                        // Try to add this as an initialible object to the toplevel
260                        // container:
261
262                        NamedObj toplevel = toplevel();
263
264                        if (toplevel instanceof Initializable) {
265                                // make sure we don't add ourselves multiple times:
266                                ((Initializable) toplevel).removeInitializable(this);
267                                ((Initializable) toplevel).addInitializable(this);
268                                if (isDebugging) {
269                                        log.debug("<" + getFullName()
270                                                        + "> added initializable to toplevel=" + toplevel);
271                                }
272                        }
273                } else {
274                        _executionFinished();
275                        _removeMIcons();
276                }
277        }
278
279        /**
280         * Creates the MI attributes for the existing component entities in the
281         * container and activates the MI icons.
282         */
283        @Override
284    public void initialize() throws IllegalActionException {
285                if (isDebugging) {
286                        log.debug("<" + getFullName() + "> #initialize()");
287                }
288
289                _getGraphController();
290
291                _initialize();
292
293                Manager manager = ((CompositeActor) getContainer()).getManager();
294                if (manager != null) {
295                        manager.addExecutionListener(_executionListener);
296                }
297
298                for (MonitoredEntity item : _monitoredEntities.values()) {
299                        item.startFigureUpdater();
300                }
301
302                _managerFigure.setState(State.RUNNING);
303
304                if (isDebugging && _graphController != null) {
305                        for (MonitoredEntity item : _monitoredEntities.values()) {
306                                item._showPortLocations(_graphController);
307                        }
308                }
309
310                _registerGraphViewListener(true);
311
312        }
313
314        /** Does nothing. */
315        @Override
316    public void addInitializable(Initializable initializable) {
317        }
318
319        /** Does nothing. */
320        @Override
321    public void preinitialize() throws IllegalActionException {
322        }
323
324        /** Does nothing. */
325        @Override
326    public void removeInitializable(Initializable initializable) {
327        }
328
329        // /////////////////////////////////////////////////////////////////
330        // // private members ////
331
332        /**
333         * Calls _relocate(item) when a monitored entity changes its location, so
334         * the corresponding MI attribute is relocated accordingly.
335         * 
336         * <p>
337         * Note: it seems nodeMoved(e) is never called, so the action is done in
338         * nodeDrawn(e).
339         */
340        private class MyGraphViewListener implements GraphViewListener {
341                @Override
342        public void edgeDrawn(GraphViewEvent e) {
343                        // Ignore.
344                }
345
346                @Override
347        public void edgeRouted(GraphViewEvent e) {
348                        // Ignore.
349                }
350
351                @Override
352        public void nodeDrawn(GraphViewEvent e) {
353                        Object source = e.getSource();
354                        Object target = e.getTarget();
355                        if (source instanceof ActorController && target instanceof Location) {
356                                Location loc = (Location) target;
357                                NamedObj container = loc.getContainer();
358
359                                if (container instanceof ComponentEntity) {
360                                        ComponentEntity<?> entity = (ComponentEntity<?>) container;
361                                        MonitoredEntity item = _monitoredEntities.get(entity
362                                                        .getName());
363                                        if (item != null) {
364                                                for (MonitorAttribute attr : item.attrs) {
365                                                        _relocate(item, attr);
366                                                }
367                                        }
368                                }
369                        }
370                }
371
372                @Override
373        public void nodeMoved(GraphViewEvent e) {
374                        // NOTE haven't seen this method being called -- why?
375                        log.debug("nodeMoved e=" + e);
376                }
377        }
378
379        private void _getGraphController() {
380                Effigy effigy = Configuration.findEffigy(this.toplevel());
381                if (effigy == null) {
382                        if (isDebugging) {
383                                log.debug("<" + getFullName() + "> No top effigy");
384                        }
385                        return;
386                }
387                effigy = effigy.topEffigy();
388                TableauFactory tf = effigy.getTableauFactory();
389                if (isDebugging) {
390                        log.debug("<" + getFullName() + "> top effigy class = "
391                                        + effigy.getClass() + "\n" + "tableau factory = " + tf);
392                }
393                try {
394                        Tableau tableau = tf.createTableau(effigy);
395                        JFrame frame = tableau.getFrame();
396
397                        if (frame instanceof ActorGraphFrame) {
398                                ActorGraphFrame agf = (ActorGraphFrame) frame;
399                                JGraph jgraph = agf.getJGraph();
400                                GraphPane graphPane = jgraph.getGraphPane();
401                                _graphController = (GraphController) graphPane
402                                                .getGraphController();
403                        }
404                } catch (Exception e) {
405                        log.debug("error in _getGraphController", e);
406                }
407        }
408
409        /**
410         * Registers/Unregisters our GraphViewListener so we can react to changes in
411         * location of entities.
412         * 
413         * @param register
414         *            true to call controller.addGraphViewListener(gvl), false to
415         *            call controller.removeGraphViewListener(gvl), where controller
416         *            is the GraphController obtained through the top effigy
417         *            associated with this.toplevel().
418         * 
419         * @return true iff the {add|remove}GraphViewListener call was done.
420         */
421        private boolean _registerGraphViewListener(boolean register) {
422                Effigy effigy = Configuration.findEffigy(this.toplevel());
423                if (effigy == null) {
424                        if (isDebugging) {
425                                log.debug("<" + getFullName() + "> No top effigy");
426                        }
427                        return false;
428                }
429                effigy = effigy.topEffigy();
430                TableauFactory tf = effigy.getTableauFactory();
431                if (isDebugging) {
432                        log.debug("<" + getFullName() + "> top effigy class = "
433                                        + effigy.getClass() + "\n" + "tableau factory = " + tf);
434                }
435                try {
436                        Tableau tableau = tf.createTableau(effigy);
437                        JFrame frame = tableau.getFrame();
438
439                        if (frame instanceof ActorGraphFrame) {
440                                ActorGraphFrame agf = (ActorGraphFrame) frame;
441                                JGraph jgraph = agf.getJGraph();
442                                GraphPane graphPane = jgraph.getGraphPane();
443                                _graphController = (GraphController) graphPane
444                                                .getGraphController();
445
446                                if (register) {
447                                        _graphController.addGraphViewListener(gvl);
448                                        log
449                                                        .debug("<" + getFullName()
450                                                                        + "> GraphViewListener added");
451                                } else {
452                                        _graphController.removeGraphViewListener(gvl);
453                                        log.debug("<" + getFullName()
454                                                        + "> GraphViewListener removed");
455                                }
456                                return true;
457                        }
458                } catch (Exception e) {
459                        log.debug("<" + getFullName()
460                                        + "> error in _registerGraphViewListener", e);
461                }
462                return false;
463        }
464
465        /**
466         * Creates and initialize the monitors to the entities in my container.
467         * 
468         * @throws IllegalActionException
469         */
470        private/* synchronized */void _initialize() throws IllegalActionException {
471                if (getContainer() == null) {
472                        log.debug("<" + getFullName() + "> NO container!");
473                        return;
474                }
475
476                boolean wasDeferringChangeRequests = isDeferringChangeRequests();
477
478                if (isDebugging) {
479                        log.debug("<" + getFullName() + "> isDeferringChangeRequests = "
480                                        + wasDeferringChangeRequests);
481                }
482
483                if (wasDeferringChangeRequests) {
484                        setDeferringChangeRequests(false);
485                }
486
487                List<ComponentEntity> entityList = ((CompositeEntity) getContainer())
488                                .entityList(ComponentEntity.class);
489                for (Iterator<ComponentEntity> it = entityList.iterator(); it.hasNext();) {
490                        ComponentEntity<?> entity = (ComponentEntity<?>) it.next();
491                        _monitor(entity);
492                }
493
494                setDeferringChangeRequests(wasDeferringChangeRequests);
495
496                for (MonitoredEntity item : _monitoredEntities.values()) {
497                        item.update();
498                }
499        }
500
501        /**
502         * Creates the monitors for a given entity.
503         * 
504         * @param entity
505         */
506        private void _monitor(final ComponentEntity<?> entity) {
507                // We need the icon to get the location of the entity
508                Attribute attr = entity.getAttribute("_icon");
509                if (attr == null) {
510                        log.error(" Unexpected: No attribute by name '_icon' in entity "
511                                        + entity);
512                        return;
513                }
514                if (!(attr instanceof EditorIcon)) {
515                        log
516                                        .error(" Unexpected: '_icon' attribute is not an EditorIcon in entity "
517                                                        + entity);
518                        return;
519                }
520
521                log.debug("<" + getFullName() + "> _icon attribute class = "
522                                + attr.getClass().getName());
523
524                // If the entity already uses a MonitorIcon, then do nothing:
525                if (attr instanceof MonitorIcon) {
526                        log.debug("<" + getFullName()
527                                        + "> Entity already uses a MonitorIcon.");
528                        return;
529                }
530
531                // first, create a MonitoredEntity for the entity.
532                // This will be initialized with only the associated MonitoredStatus
533                // object:
534                MonitoredEntity item = new MonitoredEntity(entity);
535                _monitoredEntities.put(entity.getName(), item);
536
537                // now, create and associate a visual attribute for the entity as a
538                // whole:
539                if (!ICON_TYPE_NONE.equalsIgnoreCase(_iconType)) {
540                        _addMonitorAttributeForEntity(item);
541                }
542
543                // now, create and associate visual attributes for the ports:
544                List<?> ports = item.entity.portList();
545                for (Object obj : ports) {
546                        Port port = (Port) obj;
547                        if (port instanceof IOPort) {
548                                IOPort ioport = (IOPort) port;
549                                if ((ioport.isInput() && _addInputPortCounters)
550                                                || (ioport.isOutput() && _addOutputPortCounters)) {
551                                        _addMonitorAttributeForPort(item, ioport);
552                                }
553                        }
554                }
555        }
556
557        /**
558         * Adds a new MI attribute with the given name.
559         */
560        private void _addMonitorAttributeForEntity(final MonitoredEntity item) {
561                final String name = _getMonitorAttributeNameForEntity(item.entity);
562
563                double[] loc = _getEntityAttributeLocation(item, null);
564                double x = loc[0], y = loc[1];
565
566                String moml = _getMonitorAttributeMoml(name, x, y, _iconType,
567                                _timerDelay);
568
569                ChangeRequest request = new MoMLChangeRequest(this, getContainer(),
570                                moml) {
571                        @Override
572            protected void _execute() throws Exception {
573                                try {
574                                        super._execute();
575
576                                        // ok, we now have our MonitorAttribute:
577                                        MonitorAttribute attr = (MonitorAttribute) getContainer()
578                                                        .getAttribute(name);
579
580                                        // associate the MonitorAttribute:
581                                        item.addMonitorAttribute(attr);
582
583                                        attr.setMonitoredStatus(item.status);
584
585                                        FigureUpdater figUpdater = new FigureUpdater();
586                                        figUpdater.setFigure(attr.getIcon().getFigure());
587
588                                        attr.setFigureUpdater(figUpdater);
589
590                                        // and start the figure updater:
591                                        figUpdater.start();
592
593                                        item.status.addPropertyChangeListener(figUpdater);
594                                } catch (Exception e) {
595                                        e.printStackTrace();
596                                }
597                        }
598                };
599
600                if (isDebugging) {
601                        log.debug("<" + getFullName()
602                                        + "> Request to add monitor with name " + name);
603                }
604                request.setPersistent(false);
605                requestChange(request);
606        }
607
608        /**
609         * Adds a new MI attribute with the given name.
610         */
611        private void _addMonitorAttributeForPort(final MonitoredEntity item,
612                        final IOPort ioport) {
613                final String name = _getMonitorAttributeNameForPort(item.entity, ioport);
614
615                double[] loc = _getPortAttributeLocation(item, ioport, null);
616
617                if (loc == null) {
618                        // but we need the location; so, cannot add monitor:
619                        return;
620                }
621
622                double x = loc[0], y = loc[1];
623
624                String moml = _getMonitorAttributeMoml(name, x, y,
625                                MonitorAttribute.COUNTER, _timerDelay);
626
627                ChangeRequest request = new MoMLChangeRequest(this, getContainer(),
628                                moml) {
629                        @Override
630            protected void _execute() throws Exception {
631                                try {
632                                        super._execute();
633
634                                        // ok, we now have our MonitorAttribute:
635                                        MonitorAttribute attr = (MonitorAttribute) getContainer()
636                                                        .getAttribute(name);
637
638                                        // associate the ioport with the attribute:
639                                        attr.setPort(ioport);
640
641                                        // associate the MonitorAttribute:
642                                        item.addMonitorAttribute(attr);
643
644                                        attr.setMonitoredStatus(item.status);
645
646                                        FigureUpdater figUpdater = new FigureUpdater();
647                                        figUpdater.setFigure(attr.getIcon().getFigure());
648
649                                        attr.setFigureUpdater(figUpdater);
650
651                                        // set property for the updates
652                                        String portName = ioport.getName();
653                                        String propName = "portEvent:" + portName;
654                                        figUpdater.setPropertyNameForLabel(propName);
655
656                                        // and start the figure updater:
657                                        figUpdater.start();
658
659                                        item.status.addPropertyChangeListener(figUpdater);
660                                } catch (Exception e) {
661                                        e.printStackTrace();
662                                }
663                        }
664                };
665
666                if (isDebugging) {
667                        log.debug("<" + getFullName()
668                                        + "> Request to add monitor with name " + name);
669                }
670                request.setPersistent(false);
671                requestChange(request);
672        }
673
674        /**
675         * Gets the location for an entity's monitor attribute.
676         */
677        private double[] _getEntityAttributeLocation(final MonitoredEntity item,
678                        double[] loc) {
679                if (loc == null) {
680                        loc = new double[2];
681                }
682
683                Location entityLocation = (Location) item.entity
684                                .getAttribute("_location");
685                loc[0] = entityLocation.getLocation()[0];
686                loc[1] = entityLocation.getLocation()[1];
687
688                loc[0] += 12;
689                loc[1] += 6 + item.entityBounds.getY() + item.entityBounds.getHeight();
690
691                return loc;
692        }
693
694        /**
695         * Gets the location for a port's monitor attribute.
696         */
697        private double[] _getPortAttributeLocation(final MonitoredEntity item,
698                        final IOPort ioport, double[] loc) {
699                boolean ok = false;
700                double x, y;
701                Location portLocation = (Location) ioport.getAttribute("_location");
702                if (portLocation == null) {
703                        Location entityLocation = (Location) item.entity
704                                        .getAttribute("_location");
705                        x = entityLocation.getLocation()[0];
706                        y = entityLocation.getLocation()[1];
707                        Figure portFigure = _graphController.getFigure(ioport);
708                        if (portFigure == null) {
709
710                        } else {
711                                Rectangle2D portBounds = portFigure.getBounds();
712                                if (portBounds == null) {
713
714                                } else {
715                                        if (isDebugging) {
716                                                log.debug("<" + getFullName() + "> " + ioport.getName()
717                                                                + " no _location.  portBounds=" + portBounds);
718                                        }
719                                        x += portBounds.getX();
720                                        y += portBounds.getY();
721                                        ok = true;
722                                }
723                        }
724                } else {
725                        x = portLocation.getLocation()[0];
726                        y = portLocation.getLocation()[1];
727                        ok = true;
728                        if (isDebugging) {
729                                log.debug("<" + getFullName() + "> " + ioport.getName()
730                                                + " port location: " + portLocation);
731                        }
732                }
733
734                if (ok) {
735                        x += (ioport.isInput() ? -8 : +12);
736                        y -= 6;
737
738                        if (loc == null) {
739                                loc = new double[2];
740                        }
741                        loc[0] = x;
742                        loc[1] = y;
743                        return loc;
744                } else {
745                        return null;
746                }
747        }
748
749        /**
750         * Relocates the MI attribute according to the location of its monitored
751         * object.
752         */
753        private void _relocate(final MonitoredEntity item,
754                        final MonitorAttribute attr) {
755                Location attrLocation = (Location) attr.getAttribute("_location");
756                double[] attrLoc = attrLocation.getLocation();
757
758                double[] newAttrLoc;
759                IOPort ioport = attr.getPort();
760                if (ioport == null) {
761                        newAttrLoc = _getEntityAttributeLocation(item, null);
762                } else {
763                        newAttrLoc = _getPortAttributeLocation(item, ioport, null);
764                }
765
766                if (newAttrLoc == null) {
767                        return;
768                }
769
770                if (attrLoc[0] == newAttrLoc[0] && attrLoc[1] == newAttrLoc[1]) {
771                        return;
772                }
773
774                final double[] dLoc = newAttrLoc;
775                ChangeRequest request = new ChangeRequest(this, "update location") {
776                        @Override
777            protected void _execute() throws Exception {
778                                Location newLoc;
779                                try {
780                                        newLoc = new Location(attr, "_location");
781                                        newLoc.setLocation(dLoc);
782                                } catch (IllegalActionException e) {
783                                        log.debug("<" + getFullName() + "> error setting location",
784                                                        e);
785                                } catch (NameDuplicationException e) {
786                                        log.debug("<" + getFullName() + "> error setting location",
787                                                        e);
788                                }
789                        }
790                };
791                if (isDebugging) {
792                        log.debug("<" + getFullName() + "> MI relocation for entity "
793                                        + item.entity);
794                }
795                request.setPersistent(false);
796                requestChange(request);
797        }
798
799        /** Unregisters the GraphViewListener and removes all MI attributes */
800        private/* synchronized */void _executionFinished() {
801                if (isDebugging) {
802                        log.debug("<" + getFullName() + "> #_executionFinished()");
803                }
804
805                try {
806                        _registerGraphViewListener(false);
807
808                        for (MonitoredEntity item : _monitoredEntities.values()) {
809                                item.stopFigureUpdater();
810                        }
811                        _removeMIcons();
812                } finally {
813                        _managerFigure.setState(State.IDLE);
814                }
815        }
816
817        /**
818         * Removes all MI attributes from the container.
819         */
820        private/* synchronized */void _removeMIcons() {
821                final ChangeRequest request = new ChangeRequest(this,
822                                "remove all monitor icons") {
823                        @Override
824            protected void _execute() throws Exception {
825                                for (MonitoredEntity item : _monitoredEntities.values()) {
826                                        item.status.getPropertyTimer().stop();
827                                        item.releaseIOPortEventListeners();
828                                        for (MonitorAttribute attr : item.attrs) {
829                                                _removeProperty(attr.getName());
830                                        }
831                                }
832                                _monitoredEntities.clear();
833
834                        }
835                };
836                if (isDebugging) {
837                        log.debug("<" + getFullName() + "> Calling requestChange");
838                }
839                request.setPersistent(false);
840                requestChange(request);
841        }
842
843        /**
844         * Gets the name for the MI attribute associated with an entity.
845         */
846        private static String _getMonitorAttributeNameForEntity(
847                        ComponentEntity<?> entity) {
848                String entityName = entity.getName();
849                return entityName + "__monIcon";
850        }
851
852        /**
853         * Gets the name for the MI attribute associated with an entity's port.
854         */
855        private static String _getMonitorAttributeNameForPort(
856                        ComponentEntity<?> entity, IOPort ioport) {
857                String entityName = entity.getName();
858                return entityName + "_PORT_" + ioport.getName() + "__monIcon";
859        }
860
861        /** Removes a property with the given name */
862        private void _removeProperty(final String attrName) {
863                if (isDebugging) {
864                        log.debug("<" + getFullName() + "> removing property with name "
865                                        + attrName + " ...");
866                }
867                String moml = "<deleteProperty name=\"" + attrName + "\"/>";
868                ChangeRequest request = new MoMLChangeRequest(this, getContainer(),
869                                moml) {
870                        @Override
871            protected void _execute() throws Exception {
872                                try {
873                                        super._execute();
874                                } catch (Exception e) {
875                                        log.debug("<" + getFullName()
876                                                        + "> exception executing MoMLChangeRequest", e);
877                                }
878                        }
879                };
880                request.setPersistent(false);
881                requestChange(request);
882        }
883
884        /**
885         * Monitored entity. Association (ComponentEntity, MonitoredStatus,
886         * MonitorAttribute*).
887         */
888        private static class MonitoredEntity {
889                ComponentEntity<?> entity;
890                Rectangle2D entityBounds;
891                MonitoredStatus status;
892
893                /** monitor attributes associated to the entity. */
894                List<MonitorAttribute> attrs = new ArrayList<MonitorAttribute>();
895
896                // just to be able to remove them from the ports:
897                Map<String, IOPortEventListener> portEventListeners = new HashMap<String, IOPortEventListener>();
898
899                /**
900                 * Creates the MonitorStatus member for the given entity. Internal
901                 * listeners are registered to update the properties in the status
902                 * object according to activity in the entity.
903                 */
904                MonitoredEntity(ComponentEntity<?> entity) {
905                        this.entity = entity;
906                        entityBounds = _getEntityBounds(entity);
907
908                        status = new MonitoredStatus();
909                        //entity.getAttribute(name)
910                        // the entity as a whole has a state
911                        status.setProperty(State.STATE, State.IDLE);
912
913                        status.getPropertyTimer().start();
914
915                        List<?> ports = entity.portList();
916                        for (Object obj : ports) {
917                                Port port = (Port) obj;
918                                if (port instanceof IOPort) {
919                                        IOPort ioport = (IOPort) port;
920                                        String portName = ioport.getName();
921                                        final String propName = "portEvent:" + portName;
922                                        status.setProperty(propName, "0");
923
924                                        IOPortEventListener portEventListener = new IOPortEventListener() {
925                                                long counter = 0;
926                                                
927                                                @Override
928                        public void portEvent(IOPortEvent event) {
929                                                        status.setProperty(State.STATE, State.RUNNING);
930                                                        
931                                                        /** Determine whether user specified a quality field*/
932                                                        String quality_field_value = null; 
933                                                        Parameter quality_field = (Parameter)event.getPort().getContainer().getAttribute("quality_field");
934                                                        
935                                                        /** If user specified a quality_field, get the value of the field to obtain which port to take
936                                                         *  the quality score from */
937                                                        if (quality_field != null) {
938                                                                Token token = null;
939                                try {
940                                    token = quality_field.getToken();
941                                } catch (IllegalActionException e) {
942                                    // TODO Auto-generated catch block
943                                    e.printStackTrace();
944                                }
945                                
946                                if(token != null) {                                    
947                                                                 if(token instanceof StringToken) {
948                                                                     quality_field_value = ((StringToken)token).stringValue();
949                                                                 } else {
950                                                                     quality_field_value = token.toString();
951                                                                 }
952                                }
953                                                        }
954                                                        
955                                                        /** Initialize parameter attributes to determine
956                                                         *  whether user specified quality fields for quality thresholds*/
957                                                        Parameter quality_high_param = (Parameter)event.getPort().getContainer().getAttribute("high_quality");
958                                                        Parameter quality_low_param = (Parameter)event.getPort().getContainer().getAttribute("low_quality");
959                                                        
960                                                        /** Initialize quality scores and thresholds*/
961                                                        Token quality_high_value;
962                                                        Token quality_low_value; 
963                                                        
964                                                        /** Check whether user specified a port in the quality field and verify
965                                                         *  if the port the event is coming from is the port the user specified 
966                                                         *  in the quality field*/ 
967                                                        if (quality_field_value != null &&
968                                                                        event.getPort().getDisplayName().equals(quality_field_value)) {
969                                                                
970                                                                /** Check whether user specified fields for quality thresholds*/
971                                                                if (quality_high_param != null && quality_low_param != null) { 
972                                                                        try {
973                                                                                
974                                                                                /** Retrieve quality threshold values */
975                                                                                quality_high_value = quality_high_param.getToken();
976                                                                                quality_low_value = quality_low_param.getToken();
977                                                                                
978                                                                                /** Store values in quality states*/
979                                                                                status.setProperty(State.HIGH_QUALITY, quality_high_value);
980                                                                                status.setProperty(State.LOW_QUALITY, quality_low_value);
981
982                                                                        } catch (IllegalActionException e) {
983                                                                                // TODO Auto-generated catch block
984                                                                                e.printStackTrace();
985                                                                        }
986                                                                }
987                                                                        
988                                                                if(event.getToken() != null) {
989                                                                        status.setProperty(State.QUALITY_SCORE, event.getToken());
990                                                                } else {
991                                                                    Token[] tokens = event.getTokenArray();
992                                                                    if(tokens != null) {
993                                                                        for(Token token : tokens) {
994                                                                            status.setProperty(State.QUALITY_SCORE, token);
995                                                                        }
996                                                                    }
997                                                                }
998                                                        }
999                                                        else {
1000                                                                //status.setProperty(State.STATE, event.getToken());
1001                                                                status.setProperty(State.QUALITY_SCORE, State.NO_QUALITY_SCORE);
1002                                                        }
1003                                                        
1004                                                        int eventType = event.getEventType();
1005                                                        if (eventType == IOPortEvent.GET_END
1006                                                                        || eventType == IOPortEvent.SEND_END) {
1007                                                                ++counter;
1008                                                                status.setProperty(propName, String
1009                                                                                .valueOf(counter));
1010                                                        }
1011                                                }
1012                                        };
1013                                        portEventListeners.put(portName, portEventListener);
1014
1015                                        ioport.addIOPortEventListener(portEventListener);
1016                                }
1017                        }
1018                }
1019
1020                private void addMonitorAttribute(MonitorAttribute attr) {
1021                        attrs.add(attr);
1022                }
1023
1024                private void startFigureUpdater() {
1025                        for (MonitorAttribute attr : attrs) {
1026                                FigureUpdater figUpdater = attr.getFigureUpdater();
1027                                if (figUpdater != null) {
1028                                        figUpdater.start();
1029                                }
1030                        }
1031                }
1032
1033                private void stopFigureUpdater() {
1034                        for (MonitorAttribute attr : attrs) {
1035                                FigureUpdater figUpdater = attr.getFigureUpdater();
1036                                if (figUpdater != null) {
1037                                        figUpdater.stop();
1038                                }
1039                        }
1040                }
1041
1042                private void update() {
1043                        for (MonitorAttribute attr : attrs) {
1044                                FigureUpdater fu = attr.getFigureUpdater();
1045                                if (fu != null) {
1046                                        fu.update();
1047                                }
1048                        }
1049                }
1050
1051                /** Removes the registered port event listeners */
1052                void releaseIOPortEventListeners() {
1053                        List<?> ports = entity.portList();
1054                        for (Object obj : ports) {
1055                                Port port = (Port) obj;
1056                                if (port instanceof IOPort) {
1057                                        IOPort ioport = (IOPort) port;
1058                                        String portName = ioport.getName();
1059                                        ioport.removeIOPortEventListener(portEventListeners
1060                                                        .get(portName));
1061                                }
1062                        }
1063                }
1064
1065                /** debug utility */
1066                void _showPortLocations(GraphController gc) {
1067                        log.debug("ENTITY: " + entity);
1068                        List<?> ports = entity.portList();
1069                        for (Object obj : ports) {
1070                                Port port = (Port) obj;
1071                                if (port instanceof IOPort) {
1072                                        IOPort ioport = (IOPort) port;
1073                                        String portName = ioport.getName();
1074
1075                                        Location loc = (Location) ioport.getAttribute("_location");
1076                                        if (loc == null) {
1077                                                log.debug(portName + " no _location.  Attr list: "
1078                                                                + ioport.attributeList());
1079
1080                                                // see ActorController._placePortFigures()
1081                                                Figure portFigure = gc.getFigure(port);
1082                                                if (portFigure == null) {
1083                                                        log.debug("portFigure is null");
1084                                                } else {
1085                                                        Shape shape = portFigure.getShape();
1086                                                        if (shape == null) {
1087                                                                log.debug("shape is null");
1088                                                        } else {
1089                                                                Rectangle2D portBounds = shape.getBounds2D();
1090                                                                log.debug("portBounds=" + portBounds);
1091                                                        }
1092                                                }
1093                                        } else {
1094                                                log.debug(portName + " port location: " + loc);
1095                                        }
1096                                }
1097                        }
1098                }
1099        }
1100
1101        /** For now, intended to detect exceptions and then finish the handling */
1102        private ExecutionListener _executionListener = new ExecutionListener() {
1103                @Override
1104        public void executionError(Manager manager, Throwable throwable) {
1105                        _executionFinished();
1106                }
1107
1108                @Override
1109        public void executionFinished(Manager manager) {
1110                        _executionFinished();
1111                }
1112
1113                @Override
1114        public void managerStateChanged(Manager manager) {
1115                }
1116        };
1117
1118        /** @return the entity bounds. */
1119        private static Rectangle2D _getEntityBounds(ComponentEntity<?> entity) {
1120                EditorIcon entityIcon = (EditorIcon) entity.getAttribute("_icon");
1121                CompositeFigure entityFigure = (CompositeFigure) entityIcon
1122                                .createFigure();
1123                return entityFigure.getBounds();
1124        }
1125
1126        private static String _getMonitorAttributeMoml(String name, double x,
1127                        double y, String iconType, int timerDelay) {
1128                return "<property name=\"" + name + "\" " + "class=\""
1129                                + MonitorAttribute.class.getName() + "\">\n"
1130                                + "<property name=\"_location\" "
1131                                + "class=\"ptolemy.kernel.util.Location\" " + "value=\"[" + x
1132                                + ", " + y + "]\">\n" + "</property>\n"
1133                                + "<property name=\"iconType\" "
1134                                + "class=\"ptolemy.data.expr.StringParameter\" " + "value=\""
1135                                + iconType + "\">\n" + "</property>\n"
1136                                + "<property name=\"timerDelay\" "
1137                                + "class=\"ptolemy.data.expr.StringParameter\" " + "value=\""
1138                                + timerDelay + "\">\n" + "</property>\n"
1139                                + "<property name=\"_hideName\" "
1140                                + "class=\"ptolemy.data.expr.SingletonParameter\" "
1141                                + "value=\"true\">\n" + "</property>" + "\n" + "</property>";
1142        }
1143
1144        /** My own icon */
1145        private MonitorIcon _managerIcon;
1146
1147        /** My own figure */
1148        private TrafficLightFigure _managerFigure;
1149
1150        private String _iconType;
1151
1152        private int _timerDelay;
1153
1154        /** Cache of the value of addInputPortCounters. */
1155        boolean _addInputPortCounters = false;
1156
1157        /** Cache of the value of addOutputPortCounters. */
1158        boolean _addOutputPortCounters = false;
1159
1160        /** Name -&gt; MEntity map of current monitored entities. */
1161        private Map<String, MonitoredEntity> _monitoredEntities = new HashMap<String, MonitoredEntity>();
1162
1163        private GraphController _graphController;
1164
1165        /** The GraphViewListener instance for this MI manager. */
1166        private final GraphViewListener gvl = new MyGraphViewListener();
1167
1168        private static final String ICON_TYPE_NONE = "None";
1169
1170        private static final Log log = LogFactory.getLog(MonitorManager.class);
1171
1172        private static final boolean isDebugging = log.isDebugEnabled();
1173
1174        static {
1175                // this helps in debugging the destruction of the created timers
1176                Log logTimers = LogFactory.getLog(MonitorManager.class.getPackage()
1177                                .getName()
1178                                + ".logTimers");
1179                if (logTimers.isDebugEnabled()) {
1180                        logTimers.debug("setting Timer.setLogTimers(true)");
1181                        Timer.setLogTimers(true);
1182                }
1183        }
1184
1185        private static final long serialVersionUID = 1L;
1186}