001/*
002 * Copyright (c) 2016 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2016-02-18 00:30:35 +0000 (Thu, 18 Feb 2016) $' 
007 * '$Revision: 34440 $'
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 */
029package org.kepler.gis.actor.farsite;
030
031import java.io.File;
032import java.io.FileWriter;
033import java.io.IOException;
034
035import ptolemy.actor.TypedAtomicActor;
036import ptolemy.actor.TypedIOPort;
037import ptolemy.actor.parameters.PortParameter;
038import ptolemy.data.IntToken;
039import ptolemy.data.StringToken;
040import ptolemy.data.Token;
041import ptolemy.data.type.BaseType;
042import ptolemy.data.type.Type;
043import ptolemy.kernel.CompositeEntity;
044import ptolemy.kernel.util.Attribute;
045import ptolemy.kernel.util.IllegalActionException;
046import ptolemy.kernel.util.NameDuplicationException;
047
048/** Generate a wind file for FARSITE. The file contains a list
049 *  of observations for the wind speed, direction, and cloud cover.
050 * 
051 *  @author Daniel Crawl
052 *  @version $Id: GenerateWindFile.java 34440 2016-02-18 00:30:35Z crawl $
053 */
054public class GenerateWindFile extends TypedAtomicActor {
055
056    public GenerateWindFile(CompositeEntity container, String name)
057            throws IllegalActionException, NameDuplicationException {
058        super(container, name);
059
060        speed = new PortParameter(this, "speed");
061        speed.setTypeEquals(BaseType.INT);
062        speed.getPort().setTypeEquals(BaseType.INT);
063        new Attribute(speed.getPort(), "_showName");
064
065        direction = new PortParameter(this, "direction");
066        direction.setTypeEquals(BaseType.INT);
067        direction.getPort().setTypeEquals(BaseType.INT);
068        new Attribute(direction.getPort(), "_showName");
069
070        cloudCover = new PortParameter(this, "cloudCover");
071        cloudCover.setTypeEquals(BaseType.INT);
072        cloudCover.getPort().setTypeEquals(BaseType.INT);
073        new Attribute(cloudCover.getPort(), "_showName");
074        
075        observationIncrement = new PortParameter(this, "observationIncrement");
076        observationIncrement.setTypeEquals(BaseType.INT);
077        observationIncrement.getPort().setTypeEquals(BaseType.INT);
078        new Attribute(observationIncrement.getPort(), "_showName");
079        
080        startMonth = new PortParameter(this, "startMonth");
081        startMonth.setTypeEquals(BaseType.INT);
082        startMonth.getPort().setTypeEquals(BaseType.INT);
083        new Attribute(startMonth.getPort(), "_showName");
084
085        /*
086        endMonth = new PortParameter(this, "endMonth");
087        endMonth.setTypeEquals(BaseType.INT);
088        endMonth.getPort().setTypeEquals(BaseType.INT);
089        new Attribute(endMonth.getPort(), "_showName");
090         */
091        
092        startDay = new PortParameter(this, "startDay");
093        startDay.setTypeEquals(BaseType.INT);
094        startDay.getPort().setTypeEquals(BaseType.INT);
095        new Attribute(startDay.getPort(), "_showName");
096
097        /*
098        endDay = new PortParameter(this, "endDay");
099        endDay.setTypeEquals(BaseType.INT);
100        endDay.getPort().setTypeEquals(BaseType.INT);
101        new Attribute(endDay.getPort(), "_showName");
102         */
103        
104        startHour = new PortParameter(this, "startHour");
105        startHour.setTypeEquals(BaseType.INT);
106        startHour.getPort().setTypeEquals(BaseType.INT);
107        new Attribute(startHour.getPort(), "_showName");
108
109        endHour = new PortParameter(this, "endHour");
110        endHour.setTypeEquals(BaseType.INT);
111        endHour.getPort().setTypeEquals(BaseType.INT);
112        new Attribute(endHour.getPort(), "_showName");
113
114        file = new PortParameter(this, "file");
115        file.setTypeEquals(BaseType.STRING);
116        file.setStringMode(true);
117        file.getPort().setTypeEquals(BaseType.STRING);
118        new Attribute(file.getPort(), "_showName");
119        
120        out = new TypedIOPort(this, "out", false, true);
121        out.setTypeEquals(BaseType.STRING);
122    }
123    
124    @Override
125    public void attributeChanged(Attribute attribute) throws IllegalActionException {
126        
127        if(attribute == direction) {
128            Token token = direction.getToken();
129            if(token != null) {
130                int val = ((IntToken)token).intValue();
131                if(val < -2 || val > 360) {
132                    throw new IllegalActionException(this,
133                        "Wind direction must be between 0 and 360, or -1 or -2.");
134                }
135            }
136        } else {
137            super.attributeChanged(attribute);
138        }
139    }
140    
141    @Override
142    public void fire() throws IllegalActionException {
143        
144        super.fire();
145        
146        int startMonthVal = ((Integer) _readPortParameter(startMonth)).intValue();
147        if(startMonthVal < 1 || startMonthVal > 12) {
148            throw new IllegalActionException(this, "startMonth must be between 1 and 12.");
149        }
150        
151        /*
152        int endMonthVal = ((Integer) _readPortParameter(endMonth)).intValue();
153        if(endMonthVal < 1 || endMonthVal > 12) {
154            throw new IllegalActionException(this, "endMonth must be between 1 and 12.");
155        }
156        
157        if(startMonthVal > endMonthVal) {
158            throw new IllegalActionException(this, "startMonth must be <= endMonth.");
159        }
160        */
161        
162        int startDayVal = ((Integer) _readPortParameter(startDay)).intValue();
163        if(startDayVal < 1 || startDayVal > 31) {
164            throw new IllegalActionException(this, "startDay must be between 1 and 31.");
165        }
166        
167        /*
168        int endDayVal = ((Integer) _readPortParameter(endDay)).intValue();
169        if(endDayVal < 1 || endDayVal > 31) {
170            throw new IllegalActionException(this, "endDay must be between 1 and 31.");
171        }
172        
173        if(startDayVal > endDayVal) {
174            throw new IllegalActionException(this, "startDay must be <= endDay.");  
175        }
176        */
177        
178        int startHourVal = ((Integer) _readPortParameter(startHour)).intValue();
179        if(startHourVal < 0 || startHourVal > 2400) {
180            throw new IllegalActionException(this, "startHour must be between 0 and 2400.");
181        }
182        
183        int endHourVal = ((Integer) _readPortParameter(endHour)).intValue();
184        if(endHourVal < 0 || endHourVal > 2400) {
185            throw new IllegalActionException(this, "endHour must be between 0 and 2400.");
186        }
187        
188        if(startHourVal > endHourVal) {
189            throw new IllegalActionException(this, "startHour must be <= endHour");
190        }
191        
192        /*
193        // FIXME
194        if(startMonthVal != endMonthVal) {
195            throw new IllegalActionException("startMonth must be same as endMonth");
196        }
197
198        // FIXME
199        if(startDayVal != endDayVal) {
200            throw new IllegalActionException("startDay must be same as endDay");
201        }
202        */
203
204        
205        int speedVal = ((Integer) _readPortParameter(speed)).intValue();
206        if(speedVal < 0) {
207            throw new IllegalActionException(this, "Wind speed must be >= 0.");
208        }
209        
210        int directionVal = ((Integer) _readPortParameter(direction)).intValue();
211        if(directionVal < -2 || directionVal > 360) {
212            throw new IllegalActionException(this,
213                "Wind direction must be between 0 and 360, or -1 or -2.");
214        }
215
216        int cloudCoverVal = ((Integer) _readPortParameter(cloudCover)).intValue();
217        if(cloudCoverVal < 0 || cloudCoverVal > 100) {
218            throw new IllegalActionException(this,
219                "Cloud cover must be between 0 and 100.");
220        }
221
222        int incVal = ((Integer) _readPortParameter(observationIncrement)).intValue();
223        if(incVal < 0) {
224            throw new IllegalActionException(this,
225                "Observation increment must be >= 0.");
226        }
227
228        int startHourOnly = startHourVal / 100;
229        int startMin = startHourVal % 100;
230        
231        String outputFileName = (String) _readPortParameter(file);
232        File outputFile = new File(outputFileName);
233        try(FileWriter writer = new FileWriter(outputFile)) {
234            
235            writer.write("ENGLISH\n");
236            
237            int curHour = startHourOnly;
238            int curMin = startMin;
239            // the time is HHMM
240            int curTime = curHour * 100 + curMin;
241            while(curTime <= endHourVal) {
242                
243                StringBuilder buf = new StringBuilder();
244                buf.append(startMonthVal);
245                buf.append(" ");
246                buf.append(startDayVal);
247                buf.append(" ");
248                buf.append(curTime);
249                buf.append(" ");
250                buf.append(speedVal);
251                buf.append(" ");
252                buf.append(directionVal);
253                buf.append(" ");
254                buf.append(cloudCoverVal);
255                buf.append("\n");
256                
257                writer.write(buf.toString());
258                
259                // increment the minutes
260                curMin += incVal;
261                
262                // if minutes is > 60, increment hour and wrap remaining minutes
263                curHour += curMin / 60;
264                curMin = curMin % 60;
265                
266                // calculate the new time (HHMM)
267                curTime = curHour * 100 + curMin;
268            }
269            
270            
271        } catch (IOException e) {
272            throw new IllegalActionException(this, e,
273                "Error writing to " + outputFileName);
274        }
275        
276        out.broadcast(new StringToken(outputFileName));
277    }
278
279    /** Read the value in a PortParameter. */
280    private Object _readPortParameter(PortParameter pp) throws IllegalActionException {
281        
282        pp.update();
283        Token token = pp.getToken();
284        if(token == null) {
285            throw new IllegalActionException(this, "Missing value for " + pp.getName());
286        }
287        
288        Type type = pp.getType();
289        if(type == BaseType.INT) {
290            return Integer.valueOf(((IntToken)token).intValue());
291        } else if(type == BaseType.STRING) {
292            return ((StringToken)token).stringValue();
293        } else {
294            throw new IllegalActionException(this,
295                "Unexpected type " + type + " for " + pp.getName());
296        }
297    }
298    
299    /** The start month. */
300    public PortParameter startMonth;
301    
302    //public PortParameter endMonth;
303    
304    /** The start day. */
305    public PortParameter startDay;
306    
307    //public PortParameter endDay;
308    
309    /** The start hour and minutes, e.g., 1230. */
310    public PortParameter startHour;
311    
312    /** The end hour and minutes, e.g., 1845. */
313    public PortParameter endHour;
314
315    /** The number of minutes between observations. */
316    public PortParameter observationIncrement;
317    
318    /** The 20ft wind speed in mph. */
319    public PortParameter speed;
320    
321    /** The wind direction, specified in degrees clockwise from north. Set to
322     *  -1 to indicate up slope, -2 to indicate down slope.
323     */
324    public PortParameter direction;
325    
326    /** Percentage of cloud cover. */
327    public PortParameter cloudCover;
328    
329    /** The name of the output file. */
330    public PortParameter file;
331    
332    /** The name of the output file. */
333    public TypedIOPort out;
334}