1
0
mirror of https://github.com/thingsboard/thingsboard-gateway synced 2025-10-26 22:31:42 +08:00

Basic implementation of the BACnet connector

This commit is contained in:
zbeacon
2020-03-10 14:50:16 +02:00
parent 86d10a11d8
commit b071031998
6 changed files with 163 additions and 5 deletions

View File

@@ -15,10 +15,13 @@
from random import choice
from string import ascii_lowercase
from thingsboard_gateway.connectors.connector import Connector, log
from thingsboard_gateway.connectors.bacnet.bacnet_utilities.tb_gateway_bacnet_application import TBBACnetApplication
from threading import Thread
from bacpypes.core import run, stop
from time import time, sleep
class BacknetConnector(Thread, Connector):
class BACnetConnector(Thread, Connector):
def __init__(self, gateway, config, connector_type):
self.__connector_type = connector_type
self.statistics = {'MessagesReceived': 0,
@@ -27,21 +30,39 @@ class BacknetConnector(Thread, Connector):
self.__config = config
self.setName(config.get('name', 'BACnet ' + ''.join(choice(ascii_lowercase) for _ in range(5))))
self.__gateway = gateway
self._application = TBBACnetApplication(self.__config)
self.__bacnet_core_thread = Thread(target=run, name="BACnet core thread")
self.__bacnet_core_thread.start()
self.__stopped = False
self.__poll_period = self.__config["general"].get("pollPeriod", 5000)
self.__previous_check_time = 0
self.__connected = False
self.daemon = True
def open(self):
pass
self.__stopped = False
self.start()
def run(self):
self.__connected = True
while not self.__stopped:
cur_time = time()*1000
if cur_time - self.__previous_check_time >= self.__poll_period:
self._application.do_read("192.168.0.4", "analogValue:1", "presentValue")
self.__previous_check_time = cur_time
else:
sleep(.01)
def close(self):
pass
self.__stopped = True
self.__connected = False
stop()
def get_name(self):
pass
return self.name
def is_connected(self):
pass
return self.__connected
def on_attributes_update(self, content):
pass

View File

@@ -12,3 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from thingsboard_gateway.connectors.converter import Converter, log
class BACnetUplinkConverter:
def __init__(self, config):
self.__config = config
def convert(self, config, data):
pass

View File

@@ -0,0 +1,14 @@
# Copyright 2020. ThingsBoard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

View File

@@ -0,0 +1,90 @@
# Copyright 2020. ThingsBoard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from thingsboard_gateway.connectors.connector import log
from thingsboard_gateway.connectors.bacnet.bacnet_utilities.tb_gateway_bacnet_device import TBBACnetDevice
from bacpypes.pdu import Address
from bacpypes.app import BIPSimpleApplication
from bacpypes.core import run, deferred, enable_sleeping
from bacpypes.iocb import IOCB
from bacpypes.apdu import ReadPropertyRequest
from bacpypes.primitivedata import Tag, ObjectIdentifier
from bacpypes.constructeddata import ArrayOf
class TBBACnetApplication(BIPSimpleApplication):
def __init__(self, configuration):
try:
self.__config = configuration
assert self.__config is not None
assert self.__config.get("general") is not None
self.requests_in_progress = {}
self.__device = TBBACnetDevice(self.__config["general"])
super().__init__(self.__device, self.__config["general"]["address"])
except Exception as e:
log.exception(e)
def do_read(self, address, object_id, property_id, property_index=None, converter=None):
try:
object_id = ObjectIdentifier(object_id).value
request = ReadPropertyRequest(
objectIdentifier=object_id,
propertyIdentifier=property_id
)
request.pduDestination = Address(address)
if property_index is not None:
request.propertyArrayIndex = int(property_index)
iocb = IOCB(request)
deferred(self.request_io, iocb)
iocb.add_callback(self.__on_response)
self.requests_in_progress.update({iocb: converter})
log.debug(iocb.ioResponse)
except Exception as e:
log.exception(e)
def __on_response(self, iocb: IOCB):
try:
log.debug(iocb)
log.debug(self.requests_in_progress[iocb])
if iocb.ioResponse:
apdu = iocb.ioResponse
tag_list = apdu.propertyValue.tagList
non_app_tags = [tag for tag in tag_list if tag.tagClass != Tag.applicationTagClass]
if non_app_tags:
raise RuntimeError("Value has some non-application tags")
first_tag = tag_list[0]
other_type_tags = [tag for tag in tag_list[1:] if tag.tagNumber != first_tag.tagNumber]
if other_type_tags:
raise RuntimeError("All tags must be the same type")
datatype = Tag._app_tag_class[first_tag.tagNumber]
if not datatype:
raise RuntimeError("unknown datatype")
if len(tag_list) > 1:
datatype = ArrayOf(datatype)
# datatype = ArrayOf(datatype)
value = apdu.propertyValue.cast_out(datatype)
log.debug("Received callback with data: %s", str(value))
elif iocb.ioError:
log.exception(iocb.ioError)
del self.requests_in_progress[iocb]
except Exception as e:
log.exception(e)
del self.requests_in_progress[iocb]

View File

@@ -0,0 +1,22 @@
# Copyright 2020. ThingsBoard
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from thingsboard_gateway.connectors.connector import log
from bacpypes.local.device import LocalDeviceObject
class TBBACnetDevice(LocalDeviceObject):
def __init__(self, configuration):
assert configuration is not None
super().__init__(**configuration)

View File

@@ -68,6 +68,7 @@ class TBGatewayService:
"opcua": "OpcUaConnector",
"ble": "BLEConnector",
"request": "RequestConnector",
"bacnet": "BACnetConnector",
}
self._implemented_connectors = {}
self._event_storage_types = {