test_bacnet_service.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import asyncio
  2. import unittest
  3. from app.schemas.bacnet import BacnetPointReadRequest, BacnetPointSearchRequest
  4. from app.services import bacnet_service
  5. class FailingBacnetApp:
  6. async def read_property(self, *_args):
  7. raise RuntimeError("timeout")
  8. class FailingBacnetContext:
  9. def __init__(self):
  10. self.this_application = type("ThisApplication", (), {"app": FailingBacnetApp()})()
  11. async def __aenter__(self):
  12. return self
  13. async def __aexit__(self, exc_type, exc, tb):
  14. return False
  15. class SearchBacnetApp:
  16. async def read_property(self, _address, objid, prop, array_index=None):
  17. if str(objid) == "device,12345" and str(prop) == "object-list" and array_index == 0:
  18. return 1
  19. if str(objid) == "device,12345" and str(prop) == "object-list" and array_index == 1:
  20. return ("analog-input", 1)
  21. if str(objid) == "analog-input,1" and str(prop) == "object-name":
  22. return "ai-1"
  23. if str(objid) == "analog-input,1" and str(prop) == "description":
  24. return "temperature"
  25. if str(objid) == "analog-input,1" and str(prop) == "present-value":
  26. return 12.3
  27. raise RuntimeError("unexpected read")
  28. class SearchBacnetContext:
  29. def __init__(self):
  30. self.this_application = type("ThisApplication", (), {"app": SearchBacnetApp()})()
  31. async def __aenter__(self):
  32. return self
  33. async def __aexit__(self, exc_type, exc, tb):
  34. return False
  35. class BacnetServiceTest(unittest.TestCase):
  36. def test_read_points_returns_clear_no_response_error(self):
  37. original_start = bacnet_service.BAC0.start
  38. original_local_ip = bacnet_service.local_bacnet_ip
  39. original_local_port = bacnet_service.get_free_udp_port
  40. bacnet_service.BAC0.start = lambda **_kwargs: FailingBacnetContext()
  41. bacnet_service.local_bacnet_ip = lambda _ip, _port: "127.0.0.1/24"
  42. bacnet_service.get_free_udp_port = lambda: 47809
  43. self.addCleanup(setattr, bacnet_service.BAC0, "start", original_start)
  44. self.addCleanup(setattr, bacnet_service, "local_bacnet_ip", original_local_ip)
  45. self.addCleanup(setattr, bacnet_service, "get_free_udp_port", original_local_port)
  46. response = bacnet_service.read_points(
  47. BacnetPointReadRequest(
  48. ip="192.168.1.10",
  49. bacnet_device_id=12345,
  50. points=[{"object_type": "analogInput", "object_id": 1}],
  51. )
  52. )
  53. self.assertEqual(1, response["code"])
  54. self.assertIn("no response from BACnet device 192.168.1.10:47808", response["msg"])
  55. self.assertEqual([], response["data"]["points"])
  56. def test_search_points_reads_object_list_by_index(self):
  57. original_start = bacnet_service.BAC0.start
  58. original_local_ip = bacnet_service.local_bacnet_ip
  59. original_local_port = bacnet_service.get_free_udp_port
  60. bacnet_service.BAC0.start = lambda **_kwargs: SearchBacnetContext()
  61. bacnet_service.local_bacnet_ip = lambda _ip, _port: "127.0.0.1/24"
  62. bacnet_service.get_free_udp_port = lambda: 47809
  63. self.addCleanup(setattr, bacnet_service.BAC0, "start", original_start)
  64. self.addCleanup(setattr, bacnet_service, "local_bacnet_ip", original_local_ip)
  65. self.addCleanup(setattr, bacnet_service, "get_free_udp_port", original_local_port)
  66. response = bacnet_service.search_points(
  67. BacnetPointSearchRequest(ip="192.168.1.10", bacnet_device_id=12345)
  68. )
  69. self.assertEqual(0, response["code"])
  70. self.assertEqual(
  71. [
  72. {
  73. "name": "ai-1",
  74. "description": "temperature",
  75. "object_type": "AnalogInput",
  76. "object_id": 1,
  77. "present_value": 12.3,
  78. }
  79. ],
  80. response["data"]["points"],
  81. )
  82. def test_ignores_cancelled_broadcast_endpoint_callback(self):
  83. class Loop:
  84. default_handler_called = False
  85. def default_exception_handler(self, _context):
  86. self.default_handler_called = True
  87. loop = Loop()
  88. bacnet_service.ignore_cancelled_bacnet_broadcast_endpoint(
  89. loop,
  90. {
  91. "message": "Exception in callback IPv4DatagramServer.set_broadcast_transport_protocol()",
  92. "exception": asyncio.CancelledError(),
  93. },
  94. )
  95. self.assertFalse(loop.default_handler_called)
  96. if __name__ == "__main__":
  97. unittest.main()