Browse Source

Merge pull request #58 from nutechsoftware/dev

Merge 1.13.10 into master
main
AJ Schmidt 4 years ago
committed by GitHub
parent
commit
8a1a917dc0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 55 additions and 42 deletions
  1. +18
    -34
      alarmdecoder/devices/serial_device.py
  2. +16
    -0
      alarmdecoder/util.py
  3. +14
    -6
      alarmdecoder/zonetracking.py
  4. +1
    -1
      setup.py
  5. +2
    -0
      test/test_ad2.py
  6. +4
    -1
      test/test_devices.py

+ 18
- 34
alarmdecoder/devices/serial_device.py View File

@@ -14,7 +14,7 @@ import serial.tools.list_ports
import select import select
import sys import sys
from .base_device import Device 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): class SerialDevice(Device):
@@ -141,7 +141,7 @@ class SerialDevice(Device):
def fileno(self): def fileno(self):
""" """
Returns the file number associated with the device Returns the file number associated with the device
:returns: int :returns: int
""" """
return self._device.fileno() return self._device.fileno()
@@ -178,13 +178,13 @@ class SerialDevice(Device):
:returns: character read from the device :returns: character read from the device
:raises: :py:class:`~alarmdecoder.util.CommError` :raises: :py:class:`~alarmdecoder.util.CommError`
""" """
data = ''
data = b''


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


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


except serial.SerialException as err: except serial.SerialException as err:
raise CommError('Error reading from device: {0}'.format(str(err)), err) raise CommError('Error reading from device: {0}'.format(str(err)), err)
@@ -213,54 +213,38 @@ class SerialDevice(Device):
if purge_buffer: if purge_buffer:
self._buffer = b'' self._buffer = b''


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


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


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

if len(read_ready) == 0: if len(read_ready) == 0:
continue 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: except (OSError, serial.SerialException) as err:
raise CommError('Error reading from device: {0}'.format(str(err)), err) raise CommError('Error reading from device: {0}'.format(str(err)), err)


else: else:
if got_line: 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: else:
raise TimeoutError('Timeout while waiting for line terminator.') raise TimeoutError('Timeout while waiting for line terminator.')
@@ -268,7 +252,7 @@ class SerialDevice(Device):
finally: finally:
timer.cancel() timer.cancel()


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


def purge(self): def purge(self):
""" """


+ 16
- 0
alarmdecoder/util.py View File

@@ -93,6 +93,22 @@ def bytes_hack(buf):


return ub 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): def read_firmware_file(file_path):
""" """
Reads a firmware file into a dequeue for processing. Reads a firmware file into a dequeue for processing.


+ 14
- 6
alarmdecoder/zonetracking.py View File

@@ -151,9 +151,7 @@ class Zonetracker(object):
status = Zone.CHECK status = Zone.CHECK


# NOTE: Expander zone faults are handled differently than # 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: try:
self._update_zone(zone, status=status) self._update_zone(zone, status=status)


@@ -198,6 +196,9 @@ class Zonetracker(object):
self._update_zone(zone) self._update_zone(zone)
self._clear_zones(zone) self._clear_zones(zone)


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

else: else:
status = Zone.FAULT status = Zone.FAULT
if message.check_zone: if message.check_zone:
@@ -207,8 +208,8 @@ class Zonetracker(object):
self._zones_faulted.append(zone) self._zones_faulted.append(zone)
self._zones_faulted.sort() 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() self._clear_expired_zones()


@@ -245,6 +246,11 @@ class Zonetracker(object):
:param zone: current zone being processed :param zone: current zone being processed
:type zone: int :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 = [] cleared_zones = []
found_last_faulted = found_current = at_end = False found_last_faulted = found_current = at_end = False


@@ -296,7 +302,9 @@ class Zonetracker(object):


# Actually remove the zones and trigger the restores. # Actually remove the zones and trigger the restores.
for z in cleared_zones: 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): def _clear_expired_zones(self):
""" """


+ 1
- 1
setup.py View File

@@ -14,7 +14,7 @@ if sys.version_info < (3,):
extra_requirements.append('future>=0.14.3') extra_requirements.append('future>=0.14.3')


setup(name='alarmdecoder', setup(name='alarmdecoder',
version='1.13.9',
version='1.13.10',
description='Python interface for the AlarmDecoder (AD2) family ' description='Python interface for the AlarmDecoder (AD2) family '
'of alarm devices which includes the AD2USB, AD2SERIAL and AD2PI.', 'of alarm devices which includes the AD2USB, AD2SERIAL and AD2PI.',
long_description=readme(), long_description=readme(),


+ 2
- 0
test/test_ad2.py View File

@@ -362,5 +362,7 @@ class TestAlarmDecoder(TestCase):
self._decoder._on_read(self, data=b'[00010001000000000A--],005,[f70000051003000008020000000000],"FAULT 05 "') self._decoder._on_read(self, data=b'[00010001000000000A--],005,[f70000051003000008020000000000],"FAULT 05 "')
self.assertEquals(self._zone_faulted, 5) 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._decoder._on_read(self, data=b'[00010001000000000A--],004,[f70000051003000008020000000000],"FAULT 04 "')
self.assertEquals(self._zone_restored, 3) self.assertEquals(self._zone_restored, 3)

+ 4
- 1
test/test_devices.py View File

@@ -80,8 +80,11 @@ class TestSerialDevice(TestCase):
def test_read(self): def test_read(self):
self._device.interface = '/dev/ttyS0' self._device.interface = '/dev/ttyS0'
self._device.open(no_reader_thread=True) 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('serial.Serial.fileno', return_value=1):
with patch.object(select, 'select', return_value=[[1], [], []]): with patch.object(select, 'select', return_value=[[1], [], []]):
ret = self._device.read() ret = self._device.read()


Loading…
Cancel
Save