11 lines
17 KiB
JSON
Raw Normal View History

2024-12-29 20:47:35 +01:00
{
"extension": ".py",
"source": "import logging\nimport re\nfrom binascii import unhexlify\n\nfrom ctypes import c_ushort\nfrom decimal import Decimal\n\nfrom dlms_cosem.connection import XDlmsApduFactory\nfrom dlms_cosem.protocol.xdlms import GeneralGlobalCipher\n\nfrom dsmr_parser.objects import MBusObject, MBusObjectPeak, CosemObject, ProfileGenericObject, Telegram\nfrom dsmr_parser.exceptions import ParseError, InvalidChecksumError\nfrom dsmr_parser.value_types import timestamp\n\nlogger = logging.getLogger(__name__)\n\n\nclass TelegramParser(object):\n crc16_tab = []\n\n def __init__(self, telegram_specification, apply_checksum_validation=True):\n \"\"\"\n :param telegram_specification: determines how the telegram is parsed\n :param apply_checksum_validation: validate checksum if applicable for\n telegram DSMR version (v4 and up).\n :type telegram_specification: dict\n \"\"\"\n self.apply_checksum_validation = apply_checksum_validation\n self.telegram_specification = telegram_specification\n # Regexes are compiled once to improve performance\n self.telegram_specification_regexes = {\n object[\"obis_reference\"]: re.compile(object[\"obis_reference\"], re.DOTALL | re.MULTILINE)\n for object in self.telegram_specification['objects']\n }\n\n def parse(self, telegram_data, encryption_key=\"\", authentication_key=\"\", throw_ex=False): # noqa: C901\n \"\"\"\n Parse telegram from string to dict.\n The telegram str type makes python 2.x integration easier.\n\n :param str telegram_data: full telegram from start ('/') to checksum\n ('!ABCD') including line endings in between the telegram's lines\n :param str encryption_key: encryption key\n :param str authentication_key: authentication key\n :rtype: Telegram\n :raises ParseError:\n :raises InvalidChecksumError:\n \"\"\"\n\n if \"general_global_cipher\" in self.telegram_specification:\n if self.telegram_specification[\"general_global_cipher\"]:\n enc_key = unhexlify(encryption_key)\n auth_key = unhexlify(authentication_key)\n telegram_data = unhexlify(telegram_data)\n apdu = XDlmsApduFactory.apdu_from_bytes(apdu_bytes=telegram_data)\n if apdu.security_control.security_suite != 0:\n logger.warning(\"Untested security suite\")\n if apdu.security_control.authenticated and not apdu.security_control.encrypted:\n logger.warning(\"Untested authentication only\")\n if not apdu.security_control.authenticated and not apdu.security_control.encrypted:\n logger.warning(\"Untested not encrypted or authenticated\")\n if apdu.security_control.compressed:\n logger.warning(\"Untested compression\")\n if apdu.security_control.broadcast_key:\n logger.warning(\"Untested broadcast key\")\n telegram_data = apdu.to_plain_apdu(enc_key, auth_key).decode(\"ascii\")\n else:\n try:\n if unhexlify(telegram_data[0:2])[0] == GeneralGlobalCipher.TAG:\n raise RuntimeError(\"Looks like a general_global_cipher frame \"\n \"but telegram specification is not matching!\")\n except Exception:\n pass\n else:\n try:\n if unhexlify(telegram_data[0:2])[0] == GeneralGlobalCipher.TAG:\n raise RuntimeError(\n \"Looks like a general_global_cipher frame but telegram specification is not matching!\")\n except Exception:\n pass\n\n if self.apply_checksum_validation and self.telegram_specification['checksum_support']:\n self.validate_checksum(telegram_data)\n\n telegram = Telegram()\n\n for object in self.telegram_specificatio
"review": "# 7\n\n## Bugs\n- The `crc16_tab` is mutable but populated only once, thus it is vulnerable to potential side-effects or race conditions if accessed in a multi-threaded context.\n- In the `ProfileGenericParser`, the logic assumes `values` length is correctly calculated; however, inadequate handling when regex fails to capture suitable groups is present.\n- The exception handling for hex operations could provide more specific cases rather than catching all Exceptions.\n\n## Optimizations\n- Preloading and reusing the compiled regex patterns across other parsing methods could improve performance and consistency.\n- The `crc16` method tabulation could be optimized or replaced with an alternative optimized library for CRC calculation.\n- Repeated warning checks for unhandled `security_suite` cases could be refactored into a helper method for better code reuse and maintainability.\n- Consider using a more modern Python class inheritance structure by extending directly from `object`.\n\n## Good Points\n- The use of logging to aid error-tracking and debugging.\n- Modular approach with multiple parsing classes, each with a specific responsibility.\n\n## Summary\nThe overall design of the code is modular, which is advantageous for maintainability. However, there are areas where optimizations, especially related to performance and security handling, could be implemented. Bugs are mostly related to error handling and potential issues when operating in a multi-threaded environment. Refactoring for more modern Python practices could enhance code readability and performance.\n\n## Open source alternatives\n- **PyCRC**: It offers common CRC algorithms like CRC16, which can be used instead of manually performing the CRC calculations.\n- **OpenDSS**: Data parsing and manipulation for smart grids and DSMR-related purposes.\n- **pyOBIS**: A library specifically for decoding OBIS (Object Identification System) codes.",
"filename": "parsers.py",
"path": "dsmr_parser/parsers.py",
"directory": "dsmr_parser",
"grade": 7,
"size": 14360,
"line_count": 405
}