Skip to content

Commit

Permalink
Merge pull request #58 from nutechsoftware/dev
Browse files Browse the repository at this point in the history
Merge 1.13.10 into master
  • Loading branch information
ajschmidt8 authored Feb 28, 2021
2 parents 8463a55 + 83cfd4d commit 8a1a917
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 42 deletions.
52 changes: 18 additions & 34 deletions alarmdecoder/devices/serial_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import select
import sys
from .base_device import Device
from ..util import CommError, TimeoutError, NoDeviceError, bytes_hack
from ..util import CommError, TimeoutError, NoDeviceError, bytes_hack, filter_ad2prot_byte


class SerialDevice(Device):
Expand Down Expand Up @@ -141,7 +141,7 @@ def close(self):
def fileno(self):
"""
Returns the file number associated with the device
:returns: int
"""
return self._device.fileno()
Expand Down Expand Up @@ -178,13 +178,13 @@ def read(self):
:returns: character read from the device
:raises: :py:class:`~alarmdecoder.util.CommError`
"""
data = ''
data = b''

try:
read_ready, _, _ = select.select([self._device.fileno()], [], [], 0.5)

if len(read_ready) != 0:
data = self._device.read(1)
data = filter_ad2prot_byte(self._device.read(1))

except serial.SerialException as err:
raise CommError('Error reading from device: {0}'.format(str(err)), err)
Expand Down Expand Up @@ -213,62 +213,46 @@ def timeout_event():
if purge_buffer:
self._buffer = b''

got_line, data = False, ''
got_line, ret = False, None

timer = threading.Timer(timeout, timeout_event)
if timeout > 0:
timer.start()

leftovers = b''
try:
while timeout_event.reading and not got_line:
while timeout_event.reading:
read_ready, _, _ = select.select([self._device.fileno()], [], [], 0.5)

if len(read_ready) == 0:
continue

bytes_avail = 0
if hasattr(self._device, "in_waiting"):
bytes_avail = self._device.in_waiting
else:
bytes_avail = self._device.inWaiting()

buf = self._device.read(bytes_avail)

for idx in range(len(buf)):
c = buf[idx]
buf = filter_ad2prot_byte(self._device.read(1))

ub = bytes_hack(c)
if sys.version_info > (3,):
ub = bytes([ub])
if buf != b'':
self._buffer += buf

# NOTE: AD2SERIAL and AD2PI apparently sends down \xFF on boot.
if ub != b'' and ub != b"\xff":
self._buffer += ub

if ub == b"\n":
self._buffer = self._buffer.strip(b"\r\n")

if len(self._buffer) > 0:
got_line = True
leftovers = buf[idx:]
break
if buf == b"\n":
self._buffer = self._buffer.rstrip(b"\r\n")

if len(self._buffer) > 0:
got_line = True
break
except (OSError, serial.SerialException) as err:
raise CommError('Error reading from device: {0}'.format(str(err)), err)

else:
if got_line:
data, self._buffer = self._buffer, leftovers
ret, self._buffer = self._buffer, b''

self.on_read(data=data)
self.on_read(data=ret)

else:
raise TimeoutError('Timeout while waiting for line terminator.')

finally:
timer.cancel()

return data.decode('utf-8')
return ret.decode('utf-8')

def purge(self):
"""
Expand Down
16 changes: 16 additions & 0 deletions alarmdecoder/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,22 @@ def bytes_hack(buf):

return ub

def filter_ad2prot_byte(buf):
"""
Return the byte sent in back if valid visible terminal characters or line terminators.
"""
if sys.version_info > (3,):
c = buf[0]
else:
c = ord(buf)

if (c == 10 or c == 13):
return buf
if (c > 31 and c < 127):
return buf
else:
return b''

def read_firmware_file(file_path):
"""
Reads a firmware file into a dequeue for processing.
Expand Down
20 changes: 14 additions & 6 deletions alarmdecoder/zonetracking.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,7 @@ def update(self, message):
status = Zone.CHECK

# NOTE: Expander zone faults are handled differently than
# regular messages. We don't include them in
# self._zones_faulted because they are not reported
# by the panel in it's rolling list of faults.
# regular messages.
try:
self._update_zone(zone, status=status)

Expand Down Expand Up @@ -198,6 +196,9 @@ def update(self, message):
self._update_zone(zone)
self._clear_zones(zone)

# Save our spot for the next message.
self._last_zone_fault = zone

else:
status = Zone.FAULT
if message.check_zone:
Expand All @@ -207,8 +208,8 @@ def update(self, message):
self._zones_faulted.append(zone)
self._zones_faulted.sort()

# Save our spot for the next message.
self._last_zone_fault = zone
# A new zone fault, so it is out of sequence.
self._last_zone_fault = 0

self._clear_expired_zones()

Expand Down Expand Up @@ -245,6 +246,11 @@ def _clear_zones(self, zone):
:param zone: current zone being processed
:type zone: int
"""

if self._last_zone_fault == 0:
# We don't know what the last faulted zone was, nothing to do
return

cleared_zones = []
found_last_faulted = found_current = at_end = False

Expand Down Expand Up @@ -296,7 +302,9 @@ def _clear_zones(self, zone):

# Actually remove the zones and trigger the restores.
for z in cleared_zones:
self._update_zone(z, Zone.CLEAR)
# Don't clear expander zones, expander messages will fix this
if self._zones[z].expander is False:
self._update_zone(z, Zone.CLEAR)

def _clear_expired_zones(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def readme():
extra_requirements.append('future>=0.14.3')

setup(name='alarmdecoder',
version='1.13.9',
version='1.13.10',
description='Python interface for the AlarmDecoder (AD2) family '
'of alarm devices which includes the AD2USB, AD2SERIAL and AD2PI.',
long_description=readme(),
Expand Down
2 changes: 2 additions & 0 deletions test/test_ad2.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,5 +362,7 @@ def test_zone_fault_and_restore(self):
self._decoder._on_read(self, data=b'[00010001000000000A--],005,[f70000051003000008020000000000],"FAULT 05 "')
self.assertEquals(self._zone_faulted, 5)

self._decoder._on_read(self, data=b'[00010001000000000A--],004,[f70000051003000008020000000000],"FAULT 04 "')
self._decoder._on_read(self, data=b'[00010001000000000A--],004,[f70000051003000008020000000000],"FAULT 05 "')
self._decoder._on_read(self, data=b'[00010001000000000A--],004,[f70000051003000008020000000000],"FAULT 04 "')
self.assertEquals(self._zone_restored, 3)
5 changes: 4 additions & 1 deletion test/test_devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,11 @@ def test_write_exception(self):
def test_read(self):
self._device.interface = '/dev/ttyS0'
self._device.open(no_reader_thread=True)
side_effect = ["t"]
if sys.version_info > (3,):
side_effect = ["t".encode('utf-8')]

with patch.object(self._device._device, 'read') as mock:
with patch.object(self._device._device, 'read', side_effect=side_effect) as mock:
with patch('serial.Serial.fileno', return_value=1):
with patch.object(select, 'select', return_value=[[1], [], []]):
ret = self._device.read()
Expand Down

0 comments on commit 8a1a917

Please sign in to comment.