001/*
002 * Copyright (c) 1998-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
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.ROADnet;
031
032import java.io.BufferedReader;
033import java.io.File;
034import java.util.StringTokenizer;
035import java.util.Vector;
036
037import org.ROADnet.qaqc.DataQAQC.Range;
038import org.ROADnet.qaqc.DataQAQC.RangeList;
039import org.ROADnet.qaqc.DataQAQC.ReadConfig;
040import org.ROADnet.qaqc.DataQAQC.RecordDef;
041
042import ptolemy.actor.TypedAtomicActor;
043import ptolemy.actor.TypedIOPort;
044import ptolemy.data.DoubleToken;
045import ptolemy.data.StringToken;
046import ptolemy.data.expr.FileParameter;
047import ptolemy.data.expr.StringParameter;
048import ptolemy.data.type.BaseType;
049import ptolemy.gui.GraphicalMessageHandler;
050import ptolemy.kernel.CompositeEntity;
051import ptolemy.kernel.util.Attribute;
052import ptolemy.kernel.util.IllegalActionException;
053import ptolemy.kernel.util.NameDuplicationException;
054
055//////////////////////////////////////////////////////////////////////////
056////QAQC
057/**
058 * Produce an output token on each firing with a value that is equal to a QAQC
059 * checked version of the input.
060 * 
061 * @author Brandon Smith and Efrat Jaeger
062 * @version $Id: QAQC.java 24234 2010-05-06 05:21:26Z welker $
063 * @since Ptolemy II 4.0.1
064 */
065
066public class QAQC extends TypedAtomicActor {
067
068        /**
069         * Construct an actor with the given container and name.
070         * 
071         * @param container
072         *            The container.
073         * @param name
074         *            The name of this actor.
075         * @exception IllegalActionException
076         *                If the actor cannot be contained by the proposed
077         *                container.
078         * @exception NameDuplicationException
079         *                If the container already has an actor with this name.
080         */
081        public QAQC(CompositeEntity container, String name)
082                        throws NameDuplicationException, IllegalActionException {
083                super(container, name);
084
085                input = new TypedIOPort(this, "input", true, false);
086                new Attribute(input, "_showName");
087
088                originalValue = new TypedIOPort(this, "originalValue", false, true);
089                originalValue.setTypeEquals(BaseType.DOUBLE);
090                new Attribute(originalValue, "_showName");
091                modifiedValue = new TypedIOPort(this, "modifiedValue", false, true);
092                modifiedValue.setTypeEquals(BaseType.DOUBLE);
093                new Attribute(modifiedValue, "_showName");
094                config = new FileParameter(this, "configuration file");
095                checks = new StringParameter(this, "attribute");
096                checks.setTypeEquals(BaseType.STRING);
097
098                _attachText("_iconDescription", "<svg>\n"
099                                + "<polygon points=\"-30,-10 -30,10 -10,10 -10,30 10,30 10,10"
100                                + " 30,10 30,-10 10,-10 10,-30 -10,-30 -10,-10\" "
101                                + "style=\"fill:red\"/>\n" + "</svg>\n");
102        }
103
104        // /////////////////////////////////////////////////////////////////
105        // // ports and parameters ////
106
107        /**
108         * The input port. This base class imposes no type constraints except that
109         * the type of the input cannot be greater than the type of the output.
110         */
111        public TypedIOPort input;
112
113        /**
114         * The output port. By default, the type of this output is constrained to be
115         * at least that of the input.
116         */
117        public TypedIOPort modifiedValue;
118
119        // TODO: make it configurable..
120        /**
121         * The output port. By default, the type of this output is constrained to be
122         * at least that of the input.
123         */
124        public TypedIOPort originalValue;
125
126        /**
127         * The config file. This parameter is the location of the config file as a
128         * String
129         */
130        public FileParameter config;
131
132        /**
133         * The checks String. This parameter contains the columns to check as a
134         * String
135         */
136        public StringParameter checks;
137
138        // /////////////////////////////////////////////////////////////////
139        // // public methods ////
140
141        /**
142         * Callback for changes in attribute values Get the attributes from the
143         * config file.
144         * 
145         * @param a
146         *            The attribute that changed.
147         * @exception IllegalActionException
148         */
149        public void attributeChanged(Attribute at) throws IllegalActionException {
150                if (at == config) {
151                        if (!config.getExpression().equals(_prevConfig)) { // If the value
152                                                                                                                                // has really
153                                                                                                                                // changed.
154
155                                _prevConfig = config.getExpression();
156                                checks.removeAllChoices();
157                                try {
158                                        BufferedReader reader = config.openForReading();
159                                        reader.readLine();
160                                        String line = "";
161                                        while ((line = reader.readLine()) != null) {
162                                                if (!line.equals("")) {
163                                                        int space = line.indexOf(" ");
164                                                        if (space != -1) {
165                                                                checks.addChoice(line.substring(0, space));
166                                                        } else
167                                                                checks.addChoice(line);
168                                                }
169                                        }
170                                        boolean inChoices = false;
171                                        String[] choices = checks.getChoices();
172                                        for (int i = 0; i < choices.length; i++) {
173                                                if (checks.getExpression().equals(choices[i])) {
174                                                        inChoices = true;
175                                                        break;
176                                                }
177                                        }
178                                        if (!inChoices)
179                                                checks.setExpression("");
180                                        reader.close();
181                                } catch (Exception ex) {
182                                        _debug("<EXCEPTION> There was an error while parsing the config file. "
183                                                        + ex + ". </EXCEPTION>");
184                                        GraphicalMessageHandler
185                                                        .message(ex.getMessage()
186                                                                        + "There was an error while parsing the config file"
187                                                                        + config.getExpression() + "in the actor: "
188                                                                        + this.getName());
189                                }
190
191                        } else {
192                                _debug("The config file" + _prevConfig + " hasn't changed.");
193                        }
194                } else {
195                        super.attributeChanged(at);
196                }
197        }
198
199        /**
200         * Compute the product of the input and the <i>factor</i>. If there is no
201         * input, then produce no output.
202         * 
203         * @exception IllegalActionException
204         *                If there is no director.
205         */
206        public void fire() throws IllegalActionException {
207                if (input.hasToken(0)) {
208
209                        // TODO: remote files..
210                        File config_file = config.asFile();
211                        String line = ((StringToken) input.get(0)).stringValue();
212                        String current_col = ((StringToken) checks.getToken())
213                                        .stringValue();
214                        if (current_col.equals("")) {
215                                throw new IllegalActionException(this,
216                                                "Please select an attribute for actor "
217                                                                + this.getName() + ".");
218                        }
219
220                        // parse the CSV line into Vector data_list
221                        Vector data_list = new Vector();
222                        StringTokenizer st = new StringTokenizer(line);
223                        // don't need if 1 rec type: st.nextToken(","); // skip the first
224                        // token = record id
225                        while (st.hasMoreTokens())
226                                data_list.add(st.nextToken(","));
227
228                        // make call to read config info from file
229                        ReadConfig rc = new ReadConfig(config_file.getAbsolutePath());
230
231                        // only one type of record, so set the config as the single
232                        // RecordDef
233                        RecordDef config = (RecordDef) rc.getConfig().firstElement();
234
235                        // check the value equal to the current_col
236                        int index = ((Vector) config.getTypes()).indexOf(current_col);
237
238                        // get the value to check (as a double)
239                        double value = Double.parseDouble((String) data_list
240                                        .elementAt(index));
241                        originalValue.broadcast(new DoubleToken(value));
242
243                        // get the range values, can have a set/list of ranges, but only 1
244                        // now
245                        RangeList list = (RangeList) ((Vector) config.getRanges())
246                                        .elementAt(index);
247                        double high = ((Range) list.getRangeAt(0)).getHigh();
248                        double low = ((Range) list.getRangeAt(0)).getLow();
249
250                        // do checks, replace value by min if too low or max if too high
251                        // System.err.println(low + "<" + value + "<" + high);
252                        // check if too low
253                        if (value < low) {
254                                Double l = new Double(low);
255                                data_list.setElementAt(l.toString(), index);
256                        }
257                        // check if too high
258                        if (value > high) {
259                                Double h = new Double(high);
260                                data_list.setElementAt(h.toString(), index);
261                        }
262                        double modValue = Double.parseDouble((String) data_list
263                                        .elementAt(index));
264
265                        modifiedValue.broadcast(new DoubleToken(modValue));
266                }
267
268        }
269
270        /**
271         * Post fire the actor. Return false to indicate that the process has
272         * finished. If it returns true, the process will continue indefinitely.
273         */
274        public boolean postfire() throws IllegalActionException {
275                return super.postfire();
276        }
277
278        // /////////////////////////////////////////////////////////////////
279        // // private members ////
280
281        /** Previous value of configuration file. */
282        String _prevConfig = "";
283}