Skip to content

Commit 65ae4b4

Browse files
author
Holger Fleischmann
committed
Proper handling of exceptions from callback,
Measurement eq-comparison, String repr for ZacWire- and TsicInputChannel
1 parent 8c72f7e commit 65ae4b4

2 files changed

Lines changed: 66 additions & 20 deletions

File tree

example.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
__license__ = 'Apache License 2.0'
1111

1212
import pigpio
13-
from tsic import TsicInputChannel
13+
from tsic import TsicInputChannel, Measurement
1414
import time
1515

1616
# TsicInputChannel and ZacWireInputChannel require pigpio
@@ -33,6 +33,10 @@
3333
with tsic:
3434
for i in range(3):
3535
time.sleep(1)
36-
print('{:d} {:.1f}°C'.format(i+1, tsic.measurement.degree_celsius))
36+
measurement = tsic.measurement
37+
if measurement == Measurement.UNDEF:
38+
print(measurement)
39+
else:
40+
print('{:d} {:.1f}°C'.format(i+1, measurement.degree_celsius))
3741

3842
pi.stop()

tsic.py

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,40 @@
55
May be used as a command line tool for testing TSIC input.
66
"""
77

8-
__all__ = [ 'ZacWireInputChannel', 'Measurement', 'TsicInputChannel' ]
8+
__all__ = [ 'ZacWireInputChannel', 'Measurement', 'TsicInputChannel', 'Error', 'PigpioNotConnectedError' ]
99

1010
__author__ = 'Holger Fleischmann'
1111
__copyright__ = 'Copyright 2018, Holger Fleischmann, Bavaria/Germany'
1212
__license__ = 'Apache License 2.0'
1313

1414
from datetime import datetime
1515
import argparse
16+
import logging
1617

1718
import pigpio
1819
import threading
1920
import time
2021

22+
logger = logging.getLogger().getChild(__name__)
23+
24+
25+
class Error(Exception):
26+
"""
27+
Base class of exceptions from this module.
28+
"""
29+
30+
def __init__(self, *args, **kwargs):
31+
Exception.__init__(self, *args, **kwargs)
32+
33+
34+
class PigpioNotConnectedError(Error):
35+
"""
36+
Raised when pigpio.pi is not connected.
37+
"""
38+
39+
def __init__(self, *args, **kwargs):
40+
Exception.__init__(self, *args, **kwargs)
41+
2142

2243
class ZacWireInputChannel(object):
2344
"""
@@ -37,31 +58,41 @@ def __init__(self, pigpio_pi, gpio):
3758
Initialize ZACWire receiving channel on a GPIO.
3859
pigpio_pi is the pigpio.pi object to use for GPIO access.
3960
gpio is the GPIO as Broadcom chip number.
61+
Initializes the GPIO as input without pull-up or pull-down.
62+
63+
Raises PigpioNotConnectedError if pigpio_pi is not connected.
4064
"""
4165
self.pi = pigpio_pi
4266
self.gpio = gpio
4367
self.__pi_callback = None
44-
45-
self.pi.set_mode(self.gpio, pigpio.INPUT)
68+
if self.pi.connected:
69+
self.pi.set_mode(self.gpio, pigpio.INPUT)
70+
self.pi.set_pull_up_down(self.gpio, pigpio.PUD_OFF)
71+
else:
72+
raise PigpioNotConnectedError('pigpio.pi is not connected, input for gpio ' + str(gpio) + ' will not work')
4673

4774
def start(self, callback):
4875
"""
4976
Start listening for data and pass received packet bytes
5077
to the a callback callback(status, [bytes]).
5178
Note that the callback is called by a pigpio thread.
79+
80+
Exceptions from the callback function are logged with standard
81+
python logging.
5282
"""
5383
self.stop()
5484
self.packet_callback = callback
55-
self.__pi_callback = self.pi.callback(
56-
self.gpio,
57-
pigpio.EITHER_EDGE,
58-
lambda gpio, level, tick: self.__gpio_callback(gpio, level, tick))
85+
if self.pi.connected:
86+
self.__pi_callback = self.pi.callback(
87+
self.gpio,
88+
pigpio.EITHER_EDGE,
89+
lambda gpio, level, tick: self.__gpio_callback(gpio, level, tick))
5990

6091
def stop(self):
6192
"""
6293
Stop listening for data.
6394
"""
64-
if not self.__pi_callback is None:
95+
if self.__pi_callback is not None:
6596
self.pi.set_watchdog(self.gpio, 0)
6697
self.__pi_callback.cancel()
6798
self.__pi_callback = None
@@ -73,7 +104,7 @@ def is_started(self):
73104
"""
74105
Whether listening for data is running.
75106
"""
76-
return not self.__pi_callback is None
107+
return self.__pi_callback is not None
77108

78109
def __enter__(self):
79110
self.start(None)
@@ -82,7 +113,10 @@ def __exit__(self, exc_type, exc_val, exc_tb):
82113
self.stop()
83114

84115
def __call_packet_callback(self, status, packet_bytes):
85-
self.packet_callback(status, packet_bytes)
116+
try:
117+
self.packet_callback(status, packet_bytes)
118+
except:
119+
logger.exception('Exception from callback' + str(self.packet_callback) + ' in ' + str(self))
86120

87121
def __reset_packet(self):
88122
self.__bit_ticks = None
@@ -91,15 +125,15 @@ def __reset_packet(self):
91125
self.__received_bytes = None
92126

93127
def __pass_any_packet_to_callback(self, status):
94-
if not self.__received_bytes is None:
128+
if self.__received_bytes is not None:
95129
# print('====> RECEIVED {0} STATUS {1}'.format(self.__received_bytes, status))
96130
self.__call_packet_callback(status, self.__received_bytes)
97131
self.__received_bytes = None
98132

99133
def __gpio_callback(self, gpio, level, tick):
100134
if level == pigpio.LOW:
101135

102-
if not self.__last_high_tick is None:
136+
if self.__last_high_tick is not None:
103137
high_ticks = pigpio.tickDiff(self.__last_high_tick, tick)
104138

105139
if high_ticks > 1000:
@@ -111,7 +145,7 @@ def __gpio_callback(self, gpio, level, tick):
111145
self.__bit_count = 0
112146
self.__bit_ticks = None
113147

114-
elif not self.__received_bytes is None and high_ticks > 150:
148+
elif self.__received_bytes is not None and high_ticks > 150:
115149
# next byte in packet
116150
# print('====> NEXT BYTE START')
117151
if self.__bit_count == 9:
@@ -127,11 +161,11 @@ def __gpio_callback(self, gpio, level, tick):
127161

128162
elif level == pigpio.HIGH:
129163

130-
if not self.__last_low_tick is None:
164+
if self.__last_low_tick is not None:
131165
low_ticks = pigpio.tickDiff(self.__last_low_tick, tick)
132166
# print('{0} @ {1} -> {2}: {3}'.format(gpio, tick, level, low_ticks))
133167

134-
if not self.__received_bytes is None:
168+
if self.__received_bytes is not None:
135169
if self.__bit_ticks is None:
136170
# calibration T-strobe at begin of byte
137171
self.__bit_ticks = low_ticks
@@ -163,6 +197,9 @@ def __check_parity(self):
163197
self.__pass_any_packet_to_callback(self.STATUS_PARITY_ERROR)
164198
self.__reset_packet()
165199

200+
def __repr__(self, *args, **kwargs):
201+
return self.__class__.__name__ + ' for GPIO ' + str(self.gpio)
202+
166203

167204
class Measurement(object):
168205
"""
@@ -174,6 +211,9 @@ def __init__(self, degree_celsius, seconds_since_epoch):
174211
self.degree_celsius = degree_celsius
175212
self.seconds_since_epoch = seconds_since_epoch
176213

214+
def __eq__(self, other):
215+
return self.__dict__ == other.__dict__
216+
177217
def __repr__(self, *args, **kwargs):
178218
if self.degree_celsius is None:
179219
return 'Undefined'
@@ -225,7 +265,7 @@ def stop(self):
225265
"""
226266
Stop reading temperatures.
227267
"""
228-
if not self.__zacwire_channel is None:
268+
if self.__zacwire_channel is not None:
229269
self.__zacwire_channel.stop()
230270

231271
def is_started(self):
@@ -281,12 +321,15 @@ def __packet_received(self, status, packet_bytes):
281321
measurement = self.measurement
282322

283323
# print('====> Temperature {0}°C'.format(self.__degree_celsius))
284-
if not self.__callback is None:
324+
if self.__callback is not None:
285325
self.__callback(measurement)
286326

287327
with self.__measure_waiting:
288328
self.__measure_waiting.notifyAll()
289329

330+
def __repr__(self, *args, **kwargs):
331+
return self.__class__.__name__ + ' for GPIO ' + str(self.__zacwire_channel.gpio)
332+
290333

291334
if __name__ == '__main__':
292335
parser = argparse.ArgumentParser(description=
@@ -309,4 +352,3 @@ def __packet_received(self, status, packet_bytes):
309352
pass
310353
finally:
311354
pi.stop()
312-

0 commit comments

Comments
 (0)