Skip to content

Commit

Permalink
Merge pull request #404 from ChristianTremblay/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
ChristianTremblay authored Jul 4, 2023
2 parents 0d12fc1 + 748335b commit 8ea1170
Show file tree
Hide file tree
Showing 72 changed files with 119,253 additions and 939 deletions.
8 changes: 8 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[flake8]
ignore = W291,W503,E265,E266,E302,E722
max-line-length = 127
exclude =
.git,
__pycache__,
.mypy_cache,
.pytest_cache,
7 changes: 5 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.6, 3.7, 3.8, 3.9]
python-version: ['3.7', '3.8', '3.9', '3.10']
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
Expand All @@ -37,13 +37,16 @@ jobs:
pip install netifaces
pip install python-dotenv
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f requirements-mypy.txt ]; then pip install -r requirements-mypy.txt; fi
pip install .
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
# - name: Type check with mypy
# run: mypy -p BAC0
- name: Test with pytest
run: |
coverage run --source BAC0 -m pytest -v
Expand All @@ -55,7 +58,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
COVERALLS_FLAG_NAME: ${{ matrix.test-name }}


# coveralls:
# name: Indicate completion to coveralls.io
# needs: build
Expand Down
15 changes: 7 additions & 8 deletions BAC0/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,23 @@
print("=" * 80)

try:
from . import core
from . import tasks
from .scripts.Base import Base
from . import core, tasks
from .core.devices.Device import Device as device
from .core.devices.Device import DeviceLoad as load
from .core.devices.Trends import TrendLog as TrendLog
from .tasks.Poll import SimplePoll as poll
from .tasks.Match import Match as match
from .tasks.Devices import AddDevice as add_device
from .core.utils.notes import update_log_level as log_level
from .infos import __version__ as version
from .scripts.Base import Base
from .tasks.Devices import AddDevice as add_device
from .tasks.Match import Match as match
from .tasks.Poll import SimplePoll as poll

# To be able to use the complete version pandas, flask and bokeh must be installed.
try:
import pandas
import bokeh
import flask
import flask_bootstrap
import pandas

_COMPLETE = True
except ImportError:
Expand All @@ -54,7 +53,7 @@

connect = gui
else:
connect = lite
connect = lite # type: ignore[assignment, misc]
web = lambda: print(
"All features not available to run BAC0.web(). Some modules are missing (flask, flask-bootstrap, bokeh, pandas). See docs for details. To start BAC0, use BAC0.lite()"
)
Expand Down
5 changes: 1 addition & 4 deletions BAC0/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
from . import app
from . import functions
from . import io
from . import devices
from . import app, devices, functions, io
148 changes: 121 additions & 27 deletions BAC0/core/app/ScriptApplication.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,47 +5,61 @@
# Licensed under LGPLv3, see file LICENSE in this source tree.
#
"""
SimpleApplication
SimpleApplication
=================
A basic BACnet application (bacpypes BIPSimpleApplication) for interacting with
the bacpypes BACnet stack. It enables the base-level BACnet functionality
A basic BACnet application (bacpypes BIPSimpleApplication) for interacting with
the bacpypes BACnet stack. It enables the base-level BACnet functionality
(a.k.a. device discovery) - meaning it can send & receive WhoIs & IAm messages.
Additional functionality is enabled by inheriting this application, and then
Additional functionality is enabled by inheriting this application, and then
extending it with more functions. [See BAC0.scripts for more examples of this.]
"""
import typing as t

# --- standard Python modules ---
from collections import defaultdict
from typing import Any, Dict, Optional

from bacpypes.apdu import IAmRequest, ReadRangeACK, SimpleAckPDU

# --- 3rd party modules ---
from bacpypes.app import ApplicationIOController
from bacpypes.pdu import Address
from bacpypes.service.object import ReadWritePropertyMultipleServices
from bacpypes.service.cov import ChangeOfValueServices
from bacpypes.netservice import NetworkServiceAccessPoint, NetworkServiceElement
from bacpypes.appservice import ApplicationServiceAccessPoint, StateMachineAccessPoint
from bacpypes.bvllservice import (
BIPSimple,
BIPForeign,
BIPBBMD,
AnnexJCodec,
BIPForeign,
BIPSimple,
UDPMultiplexer,
)
from bacpypes.apdu import SubscribeCOVRequest, SimpleAckPDU, RejectPDU, AbortPDU

from bacpypes.appservice import StateMachineAccessPoint, ApplicationServiceAccessPoint
from bacpypes.comm import ApplicationServiceElement, bind, Client
from bacpypes.iocb import IOCB
from bacpypes.comm import Client, bind
from bacpypes.constructeddata import (
Array,
List,
SequenceOfAny,
)
from bacpypes.core import deferred
from bacpypes.errors import ExecutionError, RejectException
from bacpypes.iocb import IOCB
from bacpypes.local.device import LocalDeviceObject
from bacpypes.netservice import NetworkServiceAccessPoint
from bacpypes.object import PropertyError
from bacpypes.pdu import Address
from bacpypes.service.cov import ChangeOfValueServices

# basic services
from bacpypes.service.device import WhoIsIAmServices, WhoHasIHaveServices
from bacpypes.service.object import ReadWritePropertyServices
from bacpypes.service.device import WhoHasIHaveServices, WhoIsIAmServices
from bacpypes.service.object import (
ReadWritePropertyMultipleServices,
ReadWritePropertyServices,
)

from ..functions.Discover import NetworkServiceElementWithRequests

# --- this application's modules ---
from ..utils.notes import note_and_log
from ..functions.Discover import NetworkServiceElementWithRequests

# ------------------------------------------------------------------------------

Expand Down Expand Up @@ -147,6 +161,82 @@ def do_UnconfirmedCOVNotificationRequest(self, apdu):
if context.callback is not None:
context.callback(elements=elements)

def do_ReadRangeRequest(self, apdu):
self._log.debug("do_ReadRangeRequest %r", apdu)

# extract the object identifier
objId = apdu.objectIdentifier

# get the object
obj = self.get_object_id(objId)
self._log.debug(" - object: %r", obj)

if not obj:
raise ExecutionError(errorClass="object", errorCode="unknownObject")

# get the datatype
datatype = obj.get_datatype(apdu.propertyIdentifier)
self._log.debug(" - datatype: %r", datatype)

# must be a list, or an array of lists
if issubclass(datatype, List):
pass
elif (
(apdu.propertyArrayIndex is not None)
and issubclass(datatype, Array)
and issubclass(datatype.subtype, List)
):
pass
else:
raise ExecutionError(errorClass="property", errorCode="propertyIsNotAList")

# get the value
self._log.debug(apdu.__dict__)
value = obj.ReadProperty(apdu.propertyIdentifier, apdu.propertyArrayIndex)
self._log.debug(f" - value: {value.__repr__()} | of type {type(value)}")
if value is None:
raise PropertyError(apdu.propertyIdentifier)
if isinstance(value, List):
self._log.debug(" - value is a list of: %r", datatype.subtype)
# datatype = datatype.subtype

if apdu.range.byPosition:
range_by_position = apdu.range.byPosition
self._log.debug(" - range_by_position: %r", range_by_position)

elif apdu.range.bySequenceNumber:
range_by_sequence_number = apdu.range.bySequenceNumber
self._log.debug(
" - range_by_sequence_number: %r", range_by_sequence_number
)

elif apdu.range.byTime:
range_by_time = apdu.range.byTime
self._log.debug(" - range_by_time: %r", range_by_time)

else:
raise RejectException("missingRequiredParameter")

# this is an ack
resp = ReadRangeACK(context=apdu)
resp.objectIdentifier = objId
resp.propertyIdentifier = apdu.propertyIdentifier
resp.propertyArrayIndex = apdu.propertyArrayIndex

resp.resultFlags = [1, 1, 0]
resp.itemCount = len(value)

# save the result in the item data
item_data = SequenceOfAny()
item_data.cast_in(value)
resp.itemData = item_data
self._log.debug(" - itemData : %r", resp.itemData)
self._log.debug(" - resp: %r", resp)
self.response(resp)
# return the result
# iocb = IOCB(resp) # make an IOCB
# deferred(self.request_io, iocb)


@note_and_log
class BAC0Application(
Expand All @@ -168,15 +258,16 @@ class BAC0Application(

def __init__(
self,
localDevice,
localAddress,
localDevice: LocalDeviceObject,
localAddress: Address,
networkNumber: int = None,
bbmdAddress=None,
bbmdTTL=0,
bbmdTTL: int = 0,
deviceInfoCache=None,
aseID=None,
iam_req=None,
subscription_contexts=None,
):
iam_req: Optional[IAmRequest] = None,
subscription_contexts: Optional[Dict[Any, Any]] = None,
) -> None:

ApplicationIOController.__init__(
self, localDevice, deviceInfoCache, aseID=aseID
Expand All @@ -188,7 +279,7 @@ def __init__(
self.localAddress = localAddress
else:
self.localAddress = Address(localAddress)

self.networkNumber = networkNumber
# include a application decoder
self.asap = ApplicationServiceAccessPoint()

Expand Down Expand Up @@ -220,9 +311,9 @@ def __init__(
bind(self.bip, self.annexj, self.mux.annexJ)

# bind the BIP stack to the network, no network number
self.nsap.bind(self.bip, address=self.localAddress)
self.nsap.bind(self.bip, net=self.networkNumber, address=self.localAddress)

self.i_am_counter = defaultdict(int)
self.i_am_counter: t.Dict[t.Tuple[str, int], int] = defaultdict(int)
self.i_have_counter = defaultdict(int)
self.who_is_counter = defaultdict(int)

Expand Down Expand Up @@ -268,6 +359,7 @@ def __init__(
self,
localDevice,
localAddress,
networkNumber: int = None,
bbmdAddress=None,
bbmdTTL=0,
deviceInfoCache=None,
Expand Down Expand Up @@ -368,6 +460,7 @@ def __init__(
self,
localDevice,
localAddress,
networkNumber: int = None,
bdtable=[],
deviceInfoCache=None,
aseID=None,
Expand Down Expand Up @@ -427,6 +520,7 @@ def __init__(

# bind the NSAP to the stack, no network number
self.nsap.bind(self.bip)
# self.nsap.bind(self.bip, net=self.networkNumber)

self.i_am_counter = defaultdict(int)
self.i_have_counter = defaultdict(int)
Expand Down
Loading

0 comments on commit 8ea1170

Please sign in to comment.