util.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Utility helpers
  4. #
  5. # Copyright (c) 2013-2020 Michael Buesch <m@bues.ch>
  6. #
  7. # Licensed under the terms of the GNU General Public License version 2,
  8. # or (at your option) any later version.
  9. #
  10. from __future__ import division, absolute_import, print_function, unicode_literals
  11. from pyprofibus.compat import *
  12. import os
  13. import time
  14. import errno
  15. __all__ = [
  16. "ProfibusError",
  17. "bytesToHex",
  18. "intToHex",
  19. "boolToStr",
  20. "fileExists",
  21. "monotonic_time",
  22. "TimeLimit",
  23. "FaultDebouncer",
  24. ]
  25. class ProfibusError(Exception):
  26. __slots__ = (
  27. )
  28. def bytesToHex(b, sep=" "):
  29. if b is None:
  30. return "None"
  31. assert isinstance(b, (bytes, bytearray))
  32. if not b:
  33. return "Empty"
  34. return sep.join("%02X" % c for c in bytearray(b))
  35. def intToHex(val):
  36. if val is None:
  37. return "None"
  38. assert isinstance(val, int)
  39. val &= 0xFFFFFFFF
  40. if val <= 0xFF:
  41. return "0x%02X" % val
  42. elif val <= 0xFFFF:
  43. return "0x%04X" % val
  44. elif val <= 0xFFFFFF:
  45. return "0x%06X" % val
  46. else:
  47. return "0x%08X" % val
  48. def boolToStr(val):
  49. return str(bool(val))
  50. def fileExists(filename):
  51. """Returns True, if the file exists.
  52. Returns False, if the file does not exist.
  53. Returns None, if another error occurred.
  54. """
  55. try:
  56. os.stat(filename)
  57. except OSError as e:
  58. if e.errno == errno.ENOENT:
  59. return False
  60. return None
  61. return True
  62. # Monotonic time. Returns a float second count.
  63. if isMicropython:
  64. def monotonic_time():
  65. return time.ticks_ms() / 1e3
  66. else:
  67. monotonic_time = getattr(time, "monotonic", time.time)
  68. class TimeLimit(object):
  69. """Generic timeout helper.
  70. """
  71. UNLIMITED = -1 # No limit
  72. __slots__ = (
  73. "__limit",
  74. "__startTime",
  75. "__endTime",
  76. )
  77. # limit => The time limit, in seconds.
  78. # Negative value = unlimited.
  79. def __init__(self, limit = 0):
  80. self.__limit = limit
  81. self.start()
  82. # (Re-)start the time.
  83. def start(self, limit = None):
  84. if limit is None:
  85. limit = self.__limit
  86. self.__limit = limit
  87. if limit >= 0:
  88. self.__startTime = monotonic_time()
  89. self.__endTime = self.__startTime + limit
  90. else:
  91. self.__startTime = self.__endTime = -1
  92. # Add seconds to the limit
  93. def add(self, seconds):
  94. if self.__limit >= 0:
  95. self.__limit += seconds
  96. self.__endTime = self.__startTime + self.__limit
  97. # Returns True, if the time limit exceed.
  98. def exceed(self):
  99. if self.__limit < 0:
  100. return False # Unlimited
  101. return monotonic_time() >= self.__endTime
  102. class FaultDebouncer(object):
  103. """Fault counter/debouncer.
  104. """
  105. __slots__ = (
  106. "__countMax",
  107. "__count",
  108. )
  109. def __init__(self, countMax = 0xFFFF):
  110. self.__countMax = countMax
  111. self.reset()
  112. def reset(self):
  113. self.__count = 0
  114. def inc(self):
  115. if self.__count < self.__countMax - 2:
  116. self.__count += 2
  117. return (self.__count + 1) // 2
  118. def dec(self):
  119. if self.__count > 0:
  120. self.__count -= 1
  121. return (self.__count + 1) // 2
  122. fault = inc
  123. ok = dec
  124. def get(self):
  125. return (self.__count + 1) // 2