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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -68,6 +68,7 @@ class TBGatewayService:
|
||||
"opcua": "OpcUaConnector",
|
||||
"ble": "BLEConnector",
|
||||
"request": "RequestConnector",
|
||||
"bacnet": "BACnetConnector",
|
||||
}
|
||||
self._implemented_connectors = {}
|
||||
self._event_storage_types = {
|
||||
|
||||
Reference in New Issue
Block a user