001/* Record all input tokens for later querying. 002 003 Copyright (c) 1998-2014 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.actor.lib; 029 030import java.util.ArrayList; 031import java.util.Collections; 032import java.util.Enumeration; 033import java.util.Iterator; 034import java.util.LinkedList; 035import java.util.List; 036 037import ptolemy.data.IntToken; 038import ptolemy.data.StringToken; 039import ptolemy.data.Token; 040import ptolemy.data.expr.Parameter; 041import ptolemy.data.type.BaseType; 042import ptolemy.kernel.CompositeEntity; 043import ptolemy.kernel.util.IllegalActionException; 044import ptolemy.kernel.util.NameDuplicationException; 045 046/////////////////////////////////////////////////////////////////// 047//// Recorder 048 049/** 050 <p>Record all input tokens for later querying. This actor can be used for 051 testing configurations of actors. It can also be used in programs that 052 invoke Ptolemy models and wish to query the results after the model 053 is run. The input tokens are read in the postfire() method so that 054 in domains with fixed-point semantics, only the final, settled value 055 is recorded. The current time is also recorded for each value. 056 </p><p> 057 The <i>capacity</i> parameter limits the size of the record. 058 If the capacity is set to zero, then no tokens are recorded, but 059 the total number of input tokens is counted. You can access 060 the count via the getCount() method. If the capacity is 1, 061 then only the most recently seen token on each channel is recorded. 062 If the capacity is negative (the default), then the capacity 063 is infinite.</p> 064 065 @author Edward A. Lee 066 @version $Id$ 067 @since Ptolemy II 0.3 068 @Pt.ProposedRating Green (eal) 069 @Pt.AcceptedRating Green (bilung) 070 */ 071public class Recorder extends Sink { 072 /** Construct an actor with an input multiport that can accept any 073 * Token. 074 * @param container The container. 075 * @param name The name of this actor. 076 * @exception IllegalActionException If the entity cannot be contained 077 * by the proposed container. 078 * @exception NameDuplicationException If the container already has an 079 * actor with this name. 080 */ 081 public Recorder(CompositeEntity container, String name) 082 throws NameDuplicationException, IllegalActionException { 083 super(container, name); 084 085 capacity = new Parameter(this, "capacity", new IntToken(-1)); 086 capacity.setTypeEquals(BaseType.INT); 087 } 088 089 /////////////////////////////////////////////////////////////////// 090 //// ports and parameters //// 091 092 /** The capacity of the record for each channel. 093 * This parameter must contain an IntToken. 094 */ 095 public Parameter capacity; 096 097 /////////////////////////////////////////////////////////////////// 098 //// public methods //// 099 100 /** Get the total number of events seen so far. 101 * @return The total number of events seen so far. 102 */ 103 public int getCount() { 104 return _count; 105 } 106 107 /** Get the history for the specified channel number. If in any 108 * firing there is no such channel, or no token was read on that 109 * channel, then a string token with value "_" is returned in the 110 * position of the list corresponding to that firing. 111 * If nothing has been recorded (there have been no firings), 112 * then return an empty list. 113 * @param channel The input channel for which the history is desired. 114 * @return A list of Token objects. 115 */ 116 public List getHistory(int channel) { 117 ArrayList result = new ArrayList(); 118 119 if (_records != null) { 120 result.ensureCapacity(_records.size()); 121 122 Iterator firings = _records.iterator(); 123 124 while (firings.hasNext()) { 125 Token[] record = (Token[]) firings.next(); 126 127 if (channel < record.length) { 128 if (record[channel] != null) { 129 result.add(record[channel]); 130 continue; 131 } 132 } 133 134 result.add(_bottom); 135 } 136 } 137 138 return result; 139 } 140 141 /** Get the latest input for the specified channel. 142 * If there has been no record yet for the specified channel, 143 * then return the string token "_", representing "bottom". 144 * @param channel The input channel for the record is desired. 145 * @return The latest input token. 146 */ 147 public Token getLatest(int channel) { 148 if (_latest == null || channel >= _latest.length 149 || _latest[channel] == null) { 150 return _bottom; 151 } 152 153 return _latest[channel]; 154 } 155 156 /** Get the record for the specified channel number. If in any 157 * firing there is no such channel, or no token was read on that 158 * channel, then a string token with value "_" is returned. 159 * If nothing has been recorded (there have been no firings), 160 * then return an empty enumeration. 161 * @param channel The input channel for the record is desired. 162 * @return An enumeration of Token objects. 163 * @deprecated This method is deprecated. Use getHistory(). 164 */ 165 @Deprecated 166 public Enumeration getRecord(int channel) { 167 return Collections.enumeration(getHistory(channel)); 168 } 169 170 /** Get the history of the time of each invocation of postfire(). 171 * @return A list of Double objects. 172 */ 173 public List getTimeHistory() { 174 return _timeRecord; 175 } 176 177 /** Get the record of the current time of each invocation of postfire(). 178 * @return An enumeration of Double objects. 179 * @deprecated This method is deprecated. Use getTimeHistory(). 180 */ 181 @Deprecated 182 public Enumeration getTimeRecord() { 183 return Collections.enumeration(_timeRecord); 184 } 185 186 /** Initialize the lists used to record input data. 187 * @exception IllegalActionException If the parent class throws it. 188 */ 189 @Override 190 public void initialize() throws IllegalActionException { 191 super.initialize(); 192 _records = new LinkedList(); 193 _timeRecord = new LinkedList(); 194 _latest = null; 195 _count = 0; 196 } 197 198 /** Read at most one token from each input channel and record its value. 199 * @exception IllegalActionException If there is no director. 200 */ 201 @Override 202 public boolean postfire() throws IllegalActionException { 203 if (!super.postfire()) { 204 return false; 205 } 206 int width = input.getWidth(); 207 Token[] record = new Token[width]; 208 209 for (int i = 0; i < width; i++) { 210 if (input.hasToken(i)) { 211 Token token = input.get(i); 212 record[i] = token; 213 _count++; 214 } 215 } 216 217 int capacityValue = ((IntToken) capacity.getToken()).intValue(); 218 219 if (capacityValue != 0) { 220 _records.add(record); 221 _timeRecord.add(Double 222 .valueOf(getDirector().getModelTime().getDoubleValue())); 223 224 if (capacityValue > 0 && _records.size() > capacityValue) { 225 // Remove the first element. 226 _records.remove(0); 227 _timeRecord.remove(0); 228 } 229 } 230 231 _latest = record; 232 return true; 233 } 234 235 // public void wrapup() throws IllegalActionException { 236 // super.wrapup(); 237 // for (int channel = 0; channel < input.getWidth(); channel++) { 238 // List history = getHistory(channel); 239 // Iterator tokens = history.iterator(); 240 // while (tokens.hasNext()) { 241 // Token token = (Token)tokens.next(); 242 // System.out.println(getFullName() + channel + ": " + token); 243 // } 244 // } 245 // } 246 247 /////////////////////////////////////////////////////////////////// 248 //// private variables //// 249 // Count of events seen. 250 private int _count = 0; 251 252 // A linked list of arrays. 253 private List _records; 254 255 // The most recent set of inputs. 256 Token[] _latest; 257 258 // A linked list of Double objects, which are times. 259 private List _timeRecord; 260 261 // A token to indicate absence. 262 private static Token _bottom = new StringToken("_"); 263}