|
4 | 4 | # This script downloads the latest raw hindcast and forecast from
|
5 | 5 | # the NHC.
|
6 | 6 | #
|
7 |
| -# It can also load the hindcast and forecast files from the local |
| 7 | +# It can also load the hindcast and forecast files from the local |
8 | 8 | # file system.
|
9 | 9 | #
|
10 | 10 | #--------------------------------------------------------------
|
11 |
| -# Copyright(C) 2006--2018 Jason Fleming |
| 11 | +# Copyright(C) 2006--2024 Jason Fleming |
12 | 12 | # Copyright(C) 2006, 2007 Brett Estrade
|
13 |
| -# |
| 13 | +# |
14 | 14 | # This file is part of the ADCIRC Surge Guidance System (ASGS).
|
15 |
| -# |
| 15 | +# |
16 | 16 | # The ASGS is free software: you can redistribute it and/or modify
|
17 | 17 | # it under the terms of the GNU General Public License as published by
|
18 | 18 | # the Free Software Foundation, either version 3 of the License, or
|
19 | 19 | # (at your option) any later version.
|
20 |
| -# |
| 20 | +# |
21 | 21 | # ASGS is distributed in the hope that it will be useful,
|
22 | 22 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
23 | 23 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
24 | 24 | # GNU General Public License for more details.
|
25 |
| -# |
| 25 | +# |
26 | 26 | # You should have received a copy of the GNU General Public License
|
27 | 27 | # along with the ASGS. If not, see <http://www.gnu.org/licenses/>.
|
28 | 28 | #
|
|
35 | 35 | use Net::SSLeay;
|
36 | 36 | use Getopt::Long;
|
37 | 37 | #
|
38 |
| -my $statefile="null"; # shell script with variables and values that |
| 38 | +my $statefile="null"; # shell script with variables and values that |
39 | 39 | # record the current state of the ASGS
|
40 | 40 | our %state; # represents current state of ASGS
|
41 |
| -my $ftpsite; # hostname for hindcast, nowcast, and/or forecast data |
| 41 | +my $ftpsite; # hostname for hindcast, nowcast, and/or forecast data |
42 | 42 | # $ftpsite can also be set to "filesystem" to pick up these
|
43 | 43 | # data from the local filesystem
|
44 | 44 | my $rsssite; # hostname where the RSS feed (index-at.xml) is located
|
45 | 45 | # $rsssite can also be set to "filesystem" to pick up these
|
46 | 46 | # data from the local filesystem
|
47 | 47 | my $fdir; # directory on $ftpsite for ATCF formatted forecast data
|
48 | 48 | my $hdir; # directory on $ftpsite for ATCF formatted hindcast data
|
49 |
| -my $storm; # two digit NHC storm number |
50 |
| -my $year; # four digit year of the storm |
| 49 | +my $storm; # two digit NHC storm number |
| 50 | +my $year; # four digit year of the storm |
51 | 51 | my $adv; # formatted advisory number of the previous advisory, if any
|
52 | 52 | my $trigger = "rss"; # the data source used to detect a new advisory
|
53 |
| - # can also be set to "rssembedded" to get the advisory text |
| 53 | + # can also be set to "rssembedded" to get the advisory text |
54 | 54 | # that is embedded in the RSS xml file, rather than following
|
55 | 55 | # the external link to the text of the forecast/advisory
|
56 | 56 | my $nhcName; # the name given by the NHC, e.g., TWO, GUSTAV, KATRINA, etc
|
57 | 57 | my $insecure;# if set, use "http" instead of "https"
|
58 | 58 | my $body; # text of the forecast/advisory
|
| 59 | +my $advNum; # advisory number detected from the BEST/OFCL data feeds |
| 60 | +my $newAdvisory = 0; |
59 | 61 | GetOptions(
|
60 | 62 | "statefile=s" => \$statefile,
|
61 | 63 | "rsssite=s" => \$rsssite,
|
|
80 | 82 | my $fcDl = 0; # true if the forecast was downloaded successfully
|
81 | 83 |
|
82 | 84 | while (!$dl) {
|
83 |
| - $hcDl = 0; |
| 85 | + $hcDl = 0; |
84 | 86 | $fcDl = 0;
|
85 | 87 | my $ftp;
|
86 | 88 | #
|
87 | 89 | # OPEN FTP SESSION (if needed)
|
88 | 90 | if ( $ftpsite ne "filesystem" ) {
|
89 |
| - $ftp = Net::FTP->new($ftpsite, Debug => 1, Passive => 1); |
| 91 | + $ftp = Net::FTP->new($ftpsite, Debug => 1, Passive => 1); |
90 | 92 | unless ( defined $ftp ) {
|
91 | 93 | stderrMessage("ERROR","ftp: Cannot connect to $ftpsite: $@");
|
92 | 94 | next;
|
|
102 | 104 | if ( $ftpsite eq "filesystem" ) {
|
103 | 105 | # we are getting the hindcast from the local filesystem
|
104 | 106 | if ( -e $hdir && -d $hdir ) {
|
105 |
| - $hcDl = 1; |
| 107 | + $hcDl = 1; |
106 | 108 | } else {
|
107 |
| - stderrMessage("ERROR","Get '$hindcastfile' failed: " |
| 109 | + stderrMessage("ERROR","Get '$hindcastfile' failed: " |
108 | 110 | . " the local directory $hdir does not exist, or is not a directory.");
|
109 |
| - next; |
| 111 | + next; |
110 | 112 | }
|
111 | 113 | } else {
|
112 | 114 | # we are getting the hindcast from an ftp server
|
|
127 | 129 | # grab the name of the storm from the hindcast, if it was not provided
|
128 | 130 | # in the command line parameters
|
129 | 131 | unless ( $nhcName ) {
|
130 |
| - if ( $hcDl ) { |
| 132 | + if ( $hcDl ) { |
131 | 133 | my $hcOpenSuccess;
|
132 | 134 | if ( $ftpsite eq "filesystem" ) {
|
133 | 135 | $hcOpenSuccess = open(HINDCAST,"<$hdir/$hindcastfile");
|
134 | 136 | } else {
|
135 |
| - $hcOpenSuccess = open(HINDCAST,"<$hindcastfile"); |
| 137 | + $hcOpenSuccess = open(HINDCAST,"<$hindcastfile"); |
136 | 138 | }
|
137 |
| - unless ($hcOpenSuccess) { |
138 |
| - stderrMessage("ERROR","Could not open hindcast file '$hindcastfile'."); |
| 139 | + unless ($hcOpenSuccess) { |
| 140 | + stderrMessage("ERROR","Could not open hindcast file '$hindcastfile'."); |
139 | 141 | next;
|
140 | 142 | }
|
141 |
| - # grab the last defined name in the hindcast |
| 143 | + # grab the last defined name in the hindcast |
142 | 144 | while(<HINDCAST>) {
|
143 | 145 | my @line = split(",",$_);
|
144 | 146 | foreach my $j (@line) {
|
|
149 | 151 | }
|
150 | 152 | }
|
151 | 153 | close(HINDCAST);
|
152 |
| - } else { |
| 154 | + } else { |
153 | 155 | stderrMessage("ERROR","Could not get NHC Name from hindcast " .
|
154 | 156 | "because the download of the hindcast file '$hindcastfile' " .
|
155 | 157 | "was not successful; " .
|
|
158 | 160 | next;
|
159 | 161 | }
|
160 | 162 | }
|
161 |
| - # |
| 163 | + # |
162 | 164 | # FORECAST TRACK
|
| 165 | + if ( $trigger eq "auto" ) { |
| 166 | + # there is no forecast track; the forecast track file will not be written |
| 167 | + $advNum = "00"; |
| 168 | + $fcDl = 1; |
| 169 | + $newAdvisory = 1; |
| 170 | + stderrMessage("DEBUG","The new advisory number is $advNum."); |
| 171 | + printf STDOUT "$advNum"; |
| 172 | + } |
163 | 173 | if ( $trigger eq "ftp" ) {
|
164 | 174 | my $fcDirSuccess = $ftp->cwd($fdir);
|
165 |
| - unless ( $fcDirSuccess ) { |
| 175 | + unless ( $fcDirSuccess ) { |
166 | 176 | stderrMessage("ERROR",
|
167 |
| - "ftp: Cannot change working directory to '$fdir': " |
| 177 | + "ftp: Cannot change working directory to '$fdir': " |
168 | 178 | . $ftp->message);
|
169 | 179 | next;
|
170 | 180 | }
|
171 | 181 | $fcDl = $ftp->get($forecastfile);
|
172 | 182 | unless ($fcDl) {
|
173 |
| - stderrMessage("ERROR","ftp: Get '$forecastfile' failed: " |
| 183 | + stderrMessage("ERROR","ftp: Get '$forecastfile' failed: " |
174 | 184 | . $ftp->message);
|
175 | 185 | next;
|
176 | 186 | }
|
177 |
| - # save the advisory number by parsing the name of the file that the |
| 187 | + # save the advisory number by parsing the name of the file that the |
178 | 188 | # file points to (a hack for our ftp test rig)
|
179 | 189 | if ( $ftpsite =~ /ftp.unc.edu/ ) {
|
180 | 190 | my @advisoryDir = $ftp->dir($fdir);
|
|
199 | 209 | if ( $trigger eq "rss" || $trigger eq "rssembedded" ) {
|
200 | 210 | # pick up the RSS feed from the local filesystem
|
201 | 211 | if ( $rsssite eq "filesystem" ) {
|
202 |
| - if ( -e "$fdir/index-at.xml" ) { |
| 212 | + if ( -e "$fdir/index-at.xml" ) { |
203 | 213 | unless (open(FORECAST,"<$fdir/index-at.xml")) {
|
204 | 214 | stderrMessage("ERROR","Cannot open the file $fdir/index-at.xml: $!");
|
205 | 215 | next;
|
206 | 216 | }
|
207 |
| - # stuff the lines of the forecast into a string variable; |
| 217 | + # stuff the lines of the forecast into a string variable; |
208 | 218 | # this mimics what happens if we download the RSS feed from
|
209 | 219 | # the web
|
210 | 220 | $body = '';
|
|
214 | 224 | close(FORECAST);
|
215 | 225 | } else {
|
216 | 226 | stderrMessage("ERROR","Cannot find the file $fdir/index-at.xml.");
|
217 |
| - next; |
| 227 | + next; |
218 | 228 | }
|
219 | 229 | } else {
|
220 | 230 | # pick up the RSS feed from the web
|
|
237 | 247 | my $url = sprintf("%s://%s/index-at.xml", $protocol, $rsssite);
|
238 | 248 | my $response = $http->get($url);
|
239 | 249 |
|
240 |
| - if ( $response->{status} == 599 ) { |
| 250 | + if ( $response->{status} == 599 ) { |
241 | 251 | stderrMessage("ERROR","Failed to download forecast/advisory.");
|
242 | 252 | printf STDERR "content: ";
|
243 | 253 | print STDERR $response->{content};
|
|
262 | 272 | my $i=0;
|
263 | 273 | my $textAdvisoryHost;
|
264 | 274 | my $textAdvisoryPath;
|
265 |
| - my $advNum; |
266 | 275 | my $stormFound = 0;
|
267 | 276 | my $linkFound = 0;
|
268 |
| - my $newAdvisory = 0; |
269 |
| - # printf STDERR "INFO: get_atcf.pl: |
| 277 | + # printf STDERR "INFO: get_atcf.pl: |
270 | 278 | # The index-at.xml file contains $cnt lines.\n";
|
271 | 279 | # Loop over the body of the index file, looking for our storm.
|
272 | 280 | #
|
273 | 281 | # jgf20140804: The storm name may have changed in the forecast, causing
|
274 | 282 | # the storm name in the best track file to be outdated and different
|
275 | 283 | # from the storm name found here. For example, in 2014, TD ONE changed
|
276 | 284 | # to TS ARTHUR, and TD TWO changed to TS BERTHA. Therefore, we must
|
277 |
| - # look for the advisory by storm number, not name. |
| 285 | + # look for the advisory by storm number, not name. |
278 | 286 | while ($i<$cnt) {
|
279 | 287 | # TROPICAL STORM BERTHA FORECAST/ADVISORY NUMBER 22
|
280 | 288 | # NWS NATIONAL HURRICANE CENTER MIAMI FL AL032014
|
281 | 289 | # pre-2006:
|
282 | 290 | # NWS TPC/NATIONAL HURRICANE CENTER MIAMI FL AL182005
|
283 | 291 | if ( $lines[$i] =~ /NATIONAL HURRICANE CENTER MIAMI FL\s+AL(\d{2})(\d{4})/ ) {
|
284 | 292 | if ($1 == $storm && $2 == $year && $lines[$i-1] =~ /FORECAST.ADVISORY/ ) {
|
285 |
| - # we have found the entry containing info about the |
| 293 | + # we have found the entry containing info about the |
286 | 294 | # latest advisory for our storm
|
287 | 295 | $stormFound = 1;
|
288 | 296 | # get the advisory number from the previous line
|
|
300 | 308 | }
|
301 | 309 | }
|
302 | 310 | #
|
303 |
| - # reset the line number to the beginning of the NHC |
| 311 | + # reset the line number to the beginning of the NHC |
304 | 312 | # forecast/advisory text, and grab the whole of the advisory,
|
305 | 313 | # storing it in the $body variable
|
306 | 314 | $i -= 8;
|
|
318 | 326 | last;
|
319 | 327 | }
|
320 | 328 | } else {
|
321 |
| - # just grab the link to the actual text of the advisory |
| 329 | + # just grab the link to the actual text of the advisory |
322 | 330 | # from a webserver
|
323 | 331 | if ( $lines[$i] =~ /<link>http:\/\/(.*?)\/(.*)<\/link>/ ) {
|
324 | 332 | $linkFound = 1;
|
|
340 | 348 | }
|
341 | 349 | }
|
342 | 350 | }
|
343 |
| - } |
344 |
| - last; |
| 351 | + } |
| 352 | + last; |
345 | 353 | }
|
346 |
| - } |
| 354 | + } |
347 | 355 | $i++;
|
348 | 356 | }
|
349 |
| - unless ( $stormFound ) { |
| 357 | + unless ( $stormFound ) { |
350 | 358 | stderrMessage("ERROR","http: The storm number $storm (named '$nhcName') of $year was not found in the RSS feed.");
|
351 | 359 | #stderrMessage("DEBUG","The body of the index-at.xml file was $body.");
|
352 | 360 | next;
|
353 | 361 | }
|
354 | 362 | # if we are supposed to get the text of the forecast file from
|
355 | 363 | # link in the RSS feed, and we find no such link, we are toast
|
356 |
| - if ( $trigger eq "rss" && !$linkFound ) { |
| 364 | + if ( $trigger eq "rss" && !$linkFound ) { |
357 | 365 | stderrMessage("ERROR","http: The link to the Forecast/Advisory for the storm named '$nhcName' was not found in the index file of the RSS feed.");
|
358 | 366 | next;
|
359 | 367 | }
|
360 |
| - unless ( $newAdvisory ) { |
| 368 | + unless ( $newAdvisory ) { |
361 | 369 | #nld wait a bit here to slow down repeated checks to http site stderrMessage("INFO","Napping.");
|
362 | 370 | sleep 60;
|
363 | 371 | next;
|
|
372 | 380 | next;
|
373 | 381 | }
|
374 | 382 | my $advReqSuccess = $advConnect->write_request(
|
375 |
| - GET => "/$textAdvisoryPath", |
| 383 | + GET => "/$textAdvisoryPath", |
376 | 384 | 'User-Agent' => "Mozilla/5.0");
|
377 | 385 | unless ($advReqSuccess) {
|
378 | 386 | stderrMessage("ERROR",
|
|
381 | 389 | }
|
382 | 390 | my ($code, $mess, %h) = $advConnect->read_response_headers();
|
383 | 391 | $body="";
|
384 |
| - while(1) { |
| 392 | + while(1) { |
385 | 393 | my $buf;
|
386 | 394 | my $n = $advConnect->read_entity_body($buf,1024);
|
387 | 395 | unless ( defined $n ) {
|
|
396 | 404 | # the local file system, and we have successfully parsed out the
|
397 | 405 | # full path and file name of the file that contains the text of the
|
398 | 406 | # advsiory, then load it up in $body to mimic what would have happened
|
399 |
| - # if we had downloaded it via http |
| 407 | + # if we had downloaded it via http |
400 | 408 | if ( $rsssite eq "filesystem" and defined $textAdvisoryPath and not defined $textAdvisoryHost ) {
|
401 | 409 | unless ( -e $textAdvisoryPath ) {
|
402 | 410 | stderrMessage("ERROR","The file containing the full text of the forecast advisory ('$textAdvisoryPath', pulled from the link in the RSS feed) does not exist.");
|
403 |
| - next; |
404 |
| - } |
| 411 | + next; |
| 412 | + } |
405 | 413 | unless ( open(ADVTEXT,"<$textAdvisoryPath") ) {
|
406 | 414 | stderrMessage("ERROR","Could not open '$textAdvisoryPath' to read: $!");
|
407 | 415 | next;
|
|
412 | 420 | close(ADVTEXT);
|
413 | 421 | }
|
414 | 422 | # now write out the file, which may contain html or other extraneous
|
415 |
| - # data, so that the relevant data can be parsed out |
416 |
| - # by nhc_advisory_bot.pl |
| 423 | + # data, so that the relevant data can be parsed out |
| 424 | + # by nhc_advisory_bot.pl |
417 | 425 | my $textAdvFile = $forecastfile . ".html";
|
418 | 426 | my $openTxtForecastSuccess = open(TEXTFORECAST,">$textAdvFile");
|
419 | 427 | unless ($openTxtForecastSuccess) {
|
|
425 | 433 | $fcDl=1;
|
426 | 434 | }
|
427 | 435 |
|
428 |
| - if ( $hcDl && $fcDl ) { |
| 436 | + if ( $hcDl && $fcDl ) { |
429 | 437 | $dl=1;
|
430 | 438 | } else {
|
431 | 439 | sleep 60;
|
|
458 | 466 | #$k =~ s/\s+//g;
|
459 | 467 | # remove leading and trailing whitespaces from the key
|
460 | 468 | $k =~ s/^\s+//g;
|
461 |
| - $k =~ s/\s+$//g; |
| 469 | + $k =~ s/\s+$//g; |
462 | 470 | # remove whitespace from the value
|
463 | 471 | $v =~ s/\s+//g;
|
464 | 472 | $H{$k}=$v;
|
|
0 commit comments