-
Notifications
You must be signed in to change notification settings - Fork 2
/
app.py
324 lines (304 loc) · 13.5 KB
/
app.py
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
import serial
import argparse
import parse
import logging
import datetime
from waggle.plugin import Plugin, get_timestamp
def parse_values(sample, **kwargs):
# Set a default
ndict = None
# Note: Specific to WXT ASCII query commands
if sample.startswith(b'0R0'):
# ASCII sting changes voltage heater character if
# voltage is supplied or not.
# - '#' for voltage not supplied
# - assigned value - 0
# - 'N' for supplied voltage and above heating temp
# - assigned value - 1
# - 'V' heating is on at 50% duty cycle, between high and middle control
# - assigned value - 2
# - 'W' heating is on 100% duty cycle, between low and middle control temps
# - assigned value - 3
# - 'F' heating is on at 50% duty cycle, heating temp below low control temp
# - assigned value - 4
# The heater character is the last value, skip for now.
data = parse.search("Dm={:d}D," +
"Sm={:f}M," +
"Ta={:f}C," +
"Ua={:f}P," +
"Pa={:f}H," +
"Rc={:f}M," +
"Hc={:f}M," +
"Th={:f}C," +
"Vh={:f}",
sample.decode('utf-8')
)
if data:
parms = ['Dm', 'Sm', 'Ta', 'Ua', 'Pa', 'Rc', 'Hc', 'Th', 'Vh']
# Convert to a list to convert from parse result object
strip = [float(var) for var in data]
ndict = dict(zip(parms, strip))
# Search for remaining heating options
if parse.search("Vs={:f}V,", sample.decode('utf-8')):
ndict.update({'Vs' : [float(var) for var in parse.search("Vs={:f}V", sample.decode('utf-8'))][0]})
if parse.search("Vr={:f}V,", sample.decode('utf-8')):
ndict.update({'Vr' : [float(var) for var in parse.search("Vr={:f}V", sample.decode('utf-8'))][0]})
# Apply the heater status to the dictionary
if parse.search("Vh={:f}N", sample.decode('utf-8')):
ndict.update({'Jo' : 1})
elif parse.search("Vh={:f}V", sample.decode('utf-8')):
ndict.update({'Jo' : 2})
elif parse.search("Vh={:f}W", sample.decode('utf-8')):
ndict.udpate({'Jo' : 3})
elif parse.search("Vh={:f}F", sample.decode('utf-8')):
ndict.update({'Jo' : 5})
else:
ndict.update({'Jo' : 0})
else:
# The WXT summary command is user-defined.
# Thus, WXT may not have same summary configuration as the CROCUS nodes.
# Define the faculty default for the summary command.
# The heater character is the last value, skip for now.
data = parse.search("Dm={:d}D," +
"Sm={:f}M," +
"Ta={:f}C," +
"Ua={:f}P," +
"Pa={:f}H," +
"Rc={:f}M," +
"Th={:f}C," +
"Vh={:f}" ,
sample.decode('utf-8')[:-1]
)
if data:
parms = ['Dm', 'Sm', 'Ta', 'Ua', 'Pa', 'Rc', 'Th', 'Vh']
# Convert to a list to convert from parse result object
strip = [float(var) for var in data]
ndict = dict(zip(parms, strip))
# Apply the heater status to the dictionary
if sample.decode('utf-8')[-1] == 'N':
ndict.update({'Jo' : 1})
elif sample.decode('utf-8')[-1] == 'V':
ndict.update({'Jo' : 2})
elif sample.decode('utf-8')[-1] == 'W':
ndict.udpate({'Jo' : 3})
elif sample.decode('utf-8')[-1] == 'F':
ndict.update({'Jo' : 5})
else:
ndict.update({'Jo' : 0})
elif sample.startswith(b'0R1'):
parms = ['Dn', 'Dm', 'Dx', 'Sn', 'Sm', 'Sx']
data = parse.search("Dn={:d}D," +
"Dm={:d}D," +
"Dx={:d}D," +
"Sn={:f}M," +
"Sm={:f}M," +
"Sx={:f}M" ,
sample.decode('utf-8')
)
if data:
# Can't figure out why I can't format parse class
strip = [float(var) for var in data]
ndict = dict(zip(parms, strip))
elif sample.startswith(b'0R2'):
parms = ['Ta', 'Ua', 'Pa']
data = parse.search("Ta={:f}C," +
"Ua={:f}P," +
"Pa={:f}H" ,
sample.decode('utf-8')
)
if data:
# Can't figure out why I can't format parse class
strip = [float(var) for var in data]
ndict = dict(zip(parms, strip))
elif sample.startswith(b'0R3'):
parms = ['Rc', 'Rd', 'Ri', 'Hc', 'Hd', 'Hi']
data = parse.search("Rc={:f}M," +
"Rd={:f}S," +
"Ri={:f}M," +
"Hc={:f}M," +
"Hd={:f}S," +
"Hi={:f}M" ,
sample.decode('utf-8')
)
if data:
# Can't figure out why I can't format parse class
strip = [float(var) for var in data]
ndict = dict(zip(parms, strip))
else:
ndict = None
return ndict
def start_publishing(args, plugin, dev, query, **kwargs):
"""
start_publishing initializes the Visala WXT530
Begins sampling and publishing data
Functions
---------
Modules
-------
plugin
logging
sched
parse
"""
# Define the timestamp
timestamp = get_timestamp()
# Note: WXT interface commands located within manual
# Note: query command sent to the instrument needs to be byte
dev.write(bytearray(query + '\r\n', 'utf-8'))
line = dev.readline()
# Remove all leading/trailing checksum characters
newstring = b''.join(bytes([byte]) for byte in line if byte > 14)
# check for debug; output direct from the instrument
if kwargs['debug'] == True:
print(datetime.datetime.fromtimestamp(timestamp / 1e9).strftime('%Y-%m-%d %H:%M:%S.%f'), line)
print(newstring)
# Check for valid command
sample = parse_values(newstring)
# If valid parsed values, send to publishing
if sample:
# Define a list to hold the additional meta data for the heater
heater_info = ["Heating Voltage Not Supplied",
"Heating Voltage Supplied and Above Heating Temperature Threshold",
"Heating Voltage Supplied and is between High and Middle Control Temperature Threshold",
"Heating Voltage Supplied and is between Low and Middle Control Temperature Threshold",
"Heating Voltage SUpplied and is Below Low Control Temperature Threshold"]
# setup and run publishing schedule
if kwargs['node_interval'] > 0:
# publish each value in sample
for name, key in kwargs['names'].items():
try:
value = sample[key]
except KeyError:
continue
# Publish to the node
plugin.publish(name,
value=value,
meta={"units" : kwargs['units'][name],
"sensor" : "vaisala-wxt536",
"missing" : "-9999.9",
},
scope="node",
timestamp=timestamp
)
if kwargs['beehive_interval'] > 0:
# publish each value in sample
for name, key in kwargs['names'].items():
try:
value = sample[key]
except KeyError:
continue
# Update the log
if key == 'Jo':
plugin.publish(name,
value=value,
meta={"units" : kwargs['units'][name],
"sensor" : "vaisala-wxt536",
"missing" : "-9999.9",
"status" : heater_info[value]
},
scope="beehive",
timestamp=timestamp
)
else:
plugin.publish(name,
value=value,
meta={"units" : kwargs['units'][name],
"sensor" : "vaisala-wxt536",
"missing" : "-9999.9",
},
scope="beehive",
timestamp=timestamp
)
def main(args):
publish_names = {"wxt.wind.direction" : "Dm",
"wxt.wind.speed" : "Sm",
"wxt.env.temp" : "Ta",
"wxt.env.humidity" : "Ua",
"wxt.env.pressure" : "Pa",
"wxt.rain.accumulation" : "Rc",
"wxt.rain.duration" : "Rd",
"wxt.rain.intensity" : "Ri",
"wxt.rain.peak" : "Rp",
"wxt.hail.accumulation" : "Hc",
"wxt.hail.duration" : "Hd",
"wxt.hail.intensity" : "Hi",
"wxt.hail.peak" : "Hp",
"wxt.heater.temp" : "Th",
"wxt.heater.volt" : "Vh",
"wxt.voltage.supply" : "Vs",
"wxt.voltage.reference" : "Vr",
"wxt.heater.status" : "Jo"
}
units = {"wxt.wind.direction" : "degrees",
"wxt.wind.speed" : "meters per second",
"wxt.env.temp" : "degree Celsius",
"wxt.env.humidity" : "percent",
"wxt.env.pressure" : "hectoPascal",
"wxt.rain.accumulation" : "milimeters",
"wxt.rain.duration" : "seconds",
"wxt.rain.intensity" : "millimeters per hour",
"wxt.rain.peak" : "millimeters per hour",
"wxt.hail.accumulation" : "hits per square centimeter",
"wxt.hail.duration" : "seconds",
"wxt.hail.intensity" : "hits per square centimeter per hour",
"wxt.hail.peak" : "hits per square centimeter per hour",
"wxt.voltage.supply" : "volts",
"wxt.heater.temp" : "degree Celsius",
"wxt.heater.volt" : "volts",
"wxt.heater.status" : "unitless",
"wxt.voltage.reference" : "volts"
}
with Plugin() as plugin, serial.Serial(args.device, baudrate=args.baud_rate, timeout=1.0) as dev:
while True:
start_publishing(args,
plugin,
dev,
args.query,
node_interval=args.node_interval,
beehive_interval=args.beehive_interval,
names=publish_names,
units=units,
debug=args.debug
)
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="Plugin for Pushing Viasala WXT 2D anemometer data through WSN")
parser.add_argument("--debug",
type=bool,
default=False,
dest='debug',
help="enable debug logs"
)
parser.add_argument("--device",
type=str,
dest='device',
default="/dev/ttyUSB7",
help="serial device to use"
)
parser.add_argument("--baudrate",
type=int,
dest='baud_rate',
default=19200,
help="baudrate to use"
)
parser.add_argument("--node-publish-interval",
default=-1.0,
dest='node_interval',
type=float,
help="interval to publish data to node " +
"(negative values disable node publishing)"
)
parser.add_argument("--beehive-publish-interval",
default=1.0,
dest='beehive_interval',
type=float,
help="interval to publish data to beehive " +
"(negative values disable beehive publishing)"
)
parser.add_argument("--query",
type=str,
default="0R0",
help="ASCII query command to send to the instrument"
)
args = parser.parse_args()
main(args)