forked from wrbelfield/ws1001wxdata
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathws1001wxdata.pl
357 lines (286 loc) · 10.1 KB
/
ws1001wxdata.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
#!/usr/bin/perl -w
#
# ws1001wxdata.pl: Ver 1.8
#
# This program establishes a TCP connection with the ws1001 console and
# obtains the real time weather data generated by the sensor array.
# It writes an output file containing the ws1001 weather data in a format
# compatible with the cumulus easyweather.dat input file.
# Weather data values received from the console are expected to be given
# in US units (not metric).
# Use -m option if console displays wx data in metric units.
use utf8;
use File::Basename;
use Getopt::Std;
use Time::Local;
use IO::Socket::INET;
use Net::Domain qw(hostname hostfqdn hostdomain);
use sigtrap qw(die INT QUIT);
$dbtest = 0;
# options
$opt_d = ""; # specify output debug file
$opt_m = ""; # console wx data is given in metric units
$opt_i = ""; # specify local host IP addr
getopts('md:i:');
if (@ARGV != 3)
{
print "Usage: $0 [-m] [-i ipaddr] [-d debugfile] search_msgfile req_record_msgfile ewcumulus_file\n";
print "Options: \n";
print " -m ws1001 console uses metric units (otherwise console uses US units)\n";
print " -i specify local host ip addr (otherwise program determines ip addr)\n";
print " -d specify dbug file for certain connection errors\n";
exit -1;
}
$bcmsgfile = $ARGV[0]; # broadcast udp search message
$sndmsgfile = $ARGV[1]; # message to be sent to EW console (ws1001)
$ewcumulusfile = $ARGV[2]; # output data file to be read by cumulus
my $dbugfile = $opt_d;
my $hostaddr = $opt_i;
print "*** ws1001wxdata.pl started ", scalar(localtime(time())), "\n";
##$SIG{PIPE} = sub { };
# initialize ewdat
@ewdata = ("x") x 36;
@msgcontent = ("0") x 3;
if($opt_m) { # console displays data in metric units
$frztemp = 0.;
$tfactor = 1.;
$pfactor = 1.;
$wfactor = 5./18.; # Convert km/h to m/s
$rfactor = 1.;
}
else { # console displays data in US units
$frztemp = 32.;
$tfactor = 5./9.;
$pfactor = 33.8639;
$wfactor = .44704;
$rfactor = 25.4;
}
@compass = ("N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW", "N");
my $tcpport = 6500; # tcp port # console connection
my $udpport = 36745; # udp port # broadcast message port
if($dbtest) {
$tcpport = 8888; # tcp port
$udpport = 7777; # udp port
}
my $wxupdate = 12; # approximate wx data update interval (sec)
my $tcptimeout = 20; # waiting for client connection timeout
my $maxtry = 3; # max attemps to write EW output file
my $trycnt = 0;
my $maxalrm = 5; # max number of connection timeouts
my $maxreset = 10; # max number of consecutive connection resets
my $resetcnt = 0;
my $ltwd = 0;
my $ltyd = 0;
my $lti = 0;
open(BCM, "<", $bcmsgfile)
|| die("$0: ERROR Could not open input file, $bcmsgfile. $!\n");
@bcmsgline = (<BCM>); # Read SEARCH UDP broadcast message
close(BCM);
open(MSG, "<", $sndmsgfile)
|| die("$0: ERROR Could not open input file, $sndmsgfile. $!\n");
@sndmsgline = (<MSG>); # Read NOWRECORD request message to be sent to console
close(MSG);
my $host = hostname; # Local host name
if(! $hostaddr) {
$hostaddr = inet_ntoa(scalar gethostbyname($host)); # Local host IP addr
}
print "local host addr = $hostaddr\n";
@ipaddr = split(/\./, $hostaddr);
$bcaddr = "${ipaddr[0]}.${ipaddr[1]}.${ipaddr[2]}.255"; # local broadcast addr
if($dbtest) {
$bcaddr = "${bcaddr}:5555";
}
else {
$bcaddr = "${bcaddr}:6000"; # broadcast addr
}
print "broadcast address = $bcaddr\n";
RESET:
my $sndcnt = 0;
my $alrmcnt = 0;
# auto-flush on socket
$| = 1;
# Create the TCP socket
my $tcpsocket = new IO::Socket::INET (
LocalHost => $hostaddr,
LocalPort => $tcpport,
Proto => 'tcp',
Listen => 5,
Timeout => $tcptimeout,
Reuse => 1
);
die "Cannot create TCP socket $!\n" unless $tcpsocket;
UDPSEND:
sleep(10);
# Create the UDP Socket.
my $udpsocket = new IO::Socket::INET (
LocalPort => $udpport,
PeerAddr => $bcaddr,
Proto => 'udp',
Broadcast => 1
);
die "Cannot create UDP socket $!\n" unless $udpsocket;
# Send broadcast SEARCH message
my $udpmsize = $udpsocket->send($bcmsgline[0]);
print "Sent UDP broadcast message of length $udpmsize\n";
# notify server that request has been sent
shutdown($udpsocket, 1);
$udpsocket->close();
print "Server waiting for WS1001 console connection on port $tcpport\n";
# waiting for a new client connection (WS1001 console)
$client_socket = $tcpsocket->accept();
if(! defined $client_socket) {
$alrmcnt++;
if($alrmcnt > $maxalrm) {
die("$0: Timed out waiting for client (console) connection: $!\n");
}
goto UDPSEND; # Send another UDP broadcast message
}
# get information about a newly connected client
my $client_address = $client_socket->peerhost();
my $client_port = $client_socket->peerport();
print "connection from $client_address:$client_port\n";
while(1) { # TCP socket send/receive loop
# clear out the file to stop Cumulus incrementing the "Last station update"
# time in the event of any failures
truncate($ewcumulusfile,0);
# send message to the connected client (console)
my $rcvmsg = "";
if(defined $client_socket->peerhost()) {
my $tcpmsize = $client_socket->send($sndmsgline[0]);
if(! $sndcnt) {
print "Sent data request message to console ($tcpmsize bytes)\n";
}
# read up to 1024 characters from the connected client
$client_socket->recv($rcvmsg, 1024);
$client_msg_size = length($rcvmsg);
# Unpack NOWRECORD message received from console
(@msgcontent) = unpack("A8 A8 Z16 S C I C S C2 f14 C2", $rcvmsg);
$msglen = scalar(@msgcontent);
if(! $sndcnt) {
print "Received data message from console ($client_msg_size bytes)\n";
print "Received message size = $msglen \n";
print "... \n";
$sndcnt++;
}
}
else { # console is disconnected
print "Cannot determine peer address. \n";
$client_msg_size = 0;
$msglen = 0;
}
# Check received message properties
if(($msglen < 24) || ($msgcontent[2] ne "NOWRECORD")) {
if($client_msg_size) {
print "Received data message ($client_msg_size bytes)\n";
print "Unexpected console message of size = $msglen \n";
if($msglen > 2) {
print "HP_HEAD = $msgcontent[0] \n";
print "HP_CMD = $msgcontent[1] \n";
print "HP_TABLE = $msgcontent[2] \n";
}
if($dbugfile) {
if(open(RMG, ">", $dbugfile)) {
printf(RMG "received message: \n");
printf(RMG "%s", $rcvmsg);
close(RMG);
}
else {
print "Could not open output debug file, $dbugfile. $!\n";
$dbugfile = "";
}
}
}
elsif($msglen > 0) {
print "Unknown response \n";
}
shutdown($tcpsocket, 2);
$tcpsocket->close();
if($resetcnt >= $maxreset) {
print "Program stopped: max resets = $resetcnt \n";
exit(2);
}
$resetcnt++;
sleep(10);
print "*** connection reset: ", scalar(localtime(time())), "\n";
goto RESET; # reset TCP connection
}
$resetcnt = 0;
# get current time
($sec, $min, $hour, $day, $month, $year, $ltwd, $ltyd, $lti) = localtime;
$year += 1900;
$month++;
# Make sure rain total is reset at the beginning of the new year
if(($month == 1) && ($day == 1) && ($hour == 0) && ( $min <= 2)) {
$msgcontent[22] = 0.; # console rain total
}
$strhour = sprintf ("%02d", $hour);
$strmin = sprintf ("%02d", $min);
$strsec = sprintf ("%02d", $sec);
# convert date to yyyy-mm-dd format
$ewdata[3] = "${year}-${month}-${day}"; # date
$ewdata[4] = "${strhour}:${strmin}:${strsec}"; # time
# indoor humidity
if($msgcontent[8] =~ /\d/) {
$ewdata[6] = sprintf("%.0f", $msgcontent[8]);
}
else { $ewdata[6] = $msgcontent[8]; }
# if required convert units from us to metric
# indoor temp
if($msgcontent[10] =~ /\d/) {
$ewdata[7] = ($msgcontent[10] - $frztemp) * $tfactor;
}
else { $ewdata[7] = $msgcontent[10]; }
# outdoor humidity
if($msgcontent[9] =~ /\d/) {
$ewdata[8] = sprintf("%.0f", $msgcontent[9]);
}
else { $ewdata[8] = $msgcontent[9]; }
# outdoor temp
$ewdata[9] = (($msgcontent[13] =~ /\d/) ? (($msgcontent[13] - $frztemp) * $tfactor) : $msgcontent[13]);
#dewpoint
$ewdata[10] = (($msgcontent[14] =~ /\d/) ? (($msgcontent[14] - $frztemp) * $tfactor) : $msgcontent[14]);
# windchill
$ewdata[11] = (($msgcontent[15] =~ /\d/) ? (($msgcontent[15] - $frztemp) * $tfactor) : $msgcontent[15]);
# barometer
$ewdata[13] = (($msgcontent[12] =~ /\d/) ? ($msgcontent[12] * $pfactor) : $msgcontent[12]);
# wind average
$ewdata[14] = (($msgcontent[16] =~ /\d/) ? ($msgcontent[16] * $wfactor) : $msgcontent[16]);
# wind gust
$ewdata[16] = (($msgcontent[17] =~ /\d/) ? ($msgcontent[17] * $wfactor) : $msgcontent[17]);
# wind direction
# Use compass for wind direction (NNW)
$cdir = $msgcontent[7]/22.5;
$compdir = sprintf ("%.0f", $cdir);
$ewdata[19] = $compass[int($compdir)];
# rain in last hour
# Cumulus uses this value for rain rate (in/hour or mm/hour)
# Use ws1001 rain rate value (in/hour or mm/hour)
$ewdata[23] = (($msgcontent[18] =~ /\d/) ? ($msgcontent[18] * $rfactor) : $msgcontent[18]);
# rain in last year
$ewdata[27] = (($msgcontent[22] =~ /\d/) ? ($msgcontent[22] * $rfactor) : $msgcontent[22]);
# solar radiation (Lux)
# convert W/m2 to lux: 1 W/m2 = 126.7 lux
$ewdata[28] = (($msgcontent[23] =~ /\d/) ? ($msgcontent[23] * 126.7) : $msgcontent[23]);
$ewdata[29] = $msgcontent[24]; # UV Index
if(open(EWP, ">", $ewcumulusfile)) {
$trycnt = 0;
}
elsif($trycnt < $maxtry) {
$trycnt++;
print "$0 : ", scalar(localtime(time())), "\n";
print "$0 - could not open output file, will try again ($trycnt)\n";
sleep(60);
next;
}
else {
die("$0: ERROR Could not open output file, $ewcumulusfile. $!\n");
}
for($i = 0; $i < @ewdata; $i++) {
printf(EWP "%s ", $ewdata[$i]);
}
printf(EWP "\n");
close(EWP);
sleep($wxupdate);
}
$tcpsocket->close();
exit(0);