__init__.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import struct
  2. import time
  3. import serial
  4. from threading import Thread
  5. from .crc import calculate_crc
  6. __version__ = '0.1.0'
  7. # Frame Synchronization
  8. FS = 0xB5
  9. # Attributes
  10. AT_REQUEST = 0x00
  11. AT_RESPONSE = 0x01
  12. AT_ACK = 0x02
  13. # S-Bus codes
  14. SC_READ_COUNTER = 0x00
  15. SC_READ_FLAGS = 0x02
  16. SC_READ_INPUT = 0x03
  17. SC_READ_RTC = 0x04
  18. SC_READ_OUTPUT = 0x05
  19. SC_READ_REGISTER = 0x06
  20. SC_READ_TIMER = 0x07
  21. SC_READ_DATA_BLOCK = 0x96
  22. SC_WRITE_COUNTER = 0x0A
  23. SC_WRITE_FLAGS = 0x0B
  24. SC_WRITE_RTC = 0x0C
  25. SC_WRITE_OUTPUT = 0x0D
  26. SC_WRITE_REGISTER = 0x0E
  27. SC_WRITE_TIMER = 0x0F
  28. SC_WRITE_DATA_BLOCK = 0x97
  29. REGISTER_SIZE = 4
  30. MAX_REGISTERS = 10
  31. REQUEST_SIZE = 9
  32. TIMEOUT = 1.0
  33. WAIT_BETWEEN_REQUESTS = 0.1
  34. class Instrument:
  35. def __init__(self, address, serial_port, timeout=TIMEOUT, **kwargs):
  36. self.address = address
  37. self.serial = serial.Serial(serial_port, timeout=timeout, **kwargs)
  38. self.last_request_time = 0.0
  39. def read_counter(self, register):
  40. raise NotImplementedError("Reading Counters is not yet implemented.")
  41. def read_flags(self):
  42. raise NotImplementedError("Reading Flags is not yet implemented.")
  43. def read_input(self):
  44. raise NotImplementedError("Reading Inputs is not yet implemented.")
  45. def read_rtc(self):
  46. raise NotImplementedError("Reading RTC is not yet implemented.")
  47. def read_output(self):
  48. raise NotImplementedError("Reading Outputs is not yet implemented.")
  49. def read_register(self, register, num_decimals=0, signed=True):
  50. if register > 255:
  51. raise ValueError("Maximum value for register is 255")
  52. message = [FS, AT_REQUEST, self.address, SC_READ_REGISTER, 0, 0, register]
  53. response_size = 4 + 4
  54. data = self._perform_request(message, response_size=response_size)
  55. return self._interpret_result(data, num_decimals, signed)
  56. def read_multiple_registers(self, start_register, num_registers=1, num_decimals=0, signed=True):
  57. if start_register + num_registers > 255:
  58. raise ValueError("Cannot read registers above number 255.")
  59. remaining_registers = num_registers
  60. results = []
  61. while remaining_registers > 0:
  62. print(f"Requesting from register {start_register}")
  63. num_registers = min(remaining_registers, MAX_REGISTERS)
  64. message = [FS, AT_REQUEST, self.address, SC_READ_REGISTER, num_registers - 1, 0, start_register]
  65. response_size = 4 + 4 * num_registers
  66. data = self._perform_request(message, response_size=response_size)
  67. results += self._interpret_result(data, num_decimals, signed, always_list=True)
  68. remaining_registers -= num_registers
  69. start_register += num_registers
  70. return results
  71. def read_register_list(self, registers_list, num_decimals=0, signed=True):
  72. pass
  73. def read_mixed_registers_list(self, registers_list, num_decimals_list, signed_list):
  74. pass
  75. def read_timer(self, timer):
  76. raise NotImplementedError("Reading Timers is not yet implemented.")
  77. def read_data_block(self):
  78. raise NotImplementedError("Reading Data Blocks is not yet implemented.")
  79. def write_counter(self):
  80. raise NotImplementedError("Writing Counters is not yet implemented.")
  81. def write_flags(self):
  82. raise NotImplementedError("Writing Flags is not yet implemented.")
  83. def write_rtc(self):
  84. raise NotImplementedError("Writing RTC is not yet implemented.")
  85. def write_output(self):
  86. raise NotImplementedError("Writing Outputs is not yet implemented.")
  87. def write_register(self):
  88. raise NotImplementedError("Writing Registers is not yet implemented.")
  89. def write_timer(self):
  90. raise NotImplementedError("Writing Timers is not yet implemented.")
  91. def write_data_block(self):
  92. raise NotImplementedError("Writing Data Blocks is not yet implemented.")
  93. def _perform_request(self, message, response_size):
  94. self._wait_for_next_request()
  95. message += calculate_crc(message)
  96. self.serial.write(message)
  97. response = self.serial.read(response_size)
  98. if not response:
  99. raise TimeoutError("No response received in time")
  100. self._check_crc(response)
  101. return response
  102. def _wait_for_next_request(self):
  103. now = time.time()
  104. time_elapsed = now - self.last_request_time
  105. time.sleep(max(0, WAIT_BETWEEN_REQUESTS - time_elapsed))
  106. self.last_request_time = now
  107. def _check_crc(self, message):
  108. crc = calculate_crc(message[:-2])
  109. if crc != message[-2:]:
  110. return False
  111. return True
  112. def _interpret_result(self, message, num_decimals=0, signed=True, always_list=False):
  113. if message[0] != FS:
  114. raise ValueError("Message does not begin with byte 0xB5. Check your baudrate and cable polarity.")
  115. if message[1] != AT_RESPONSE:
  116. return _interpret_error(message)
  117. register_values = []
  118. data = message[2:-2]
  119. num_registers = int(len(data)/4)
  120. if signed:
  121. format_code = 'l'
  122. else:
  123. format_code = 'L'
  124. register_values = [value / 10 ** num_decimals for value in struct.unpack(f'>{num_registers}{format_code}', data)]
  125. if not always_list and len(register_values) == 1:
  126. return register_values[0]
  127. return register_values
  128. def _chunk_data(self, data):
  129. for i in range(len(data)>>2):
  130. yield data[i*REGISTER_SIZE:(i+1)*REGISTER_SIZE]
  131. class DeviceEmulator:
  132. def __init__(self, address, serial_port, **kwargs):
  133. self.serial = serial.Serial(serial_port, **kwargs)
  134. self.registers = {}
  135. self.thread = None
  136. def start(self):
  137. self.thread = Thread(target=self._run)
  138. self.thread.start()
  139. def stop(self):
  140. self.thread.stop()
  141. def _run(self):
  142. while True:
  143. byte = self.serial.read(1)
  144. if byte != FS:
  145. continue
  146. request = self._read_message()
  147. status, data = self._handle_message(request)
  148. response = [FS, status] + data
  149. response += calculate_crc(response)
  150. self.serial.write(response)
  151. def _read_message(self):
  152. message = FS + self.serial.read(REQUEST_SIZE - 1)
  153. if message[1] != AT_REQUEST or message[2] != self.address:
  154. self._skip_message()
  155. def _check_crc(self, message):
  156. crc = calculate_crc(message[:-2])
  157. if crc != message[-2:]:
  158. raise ValueError("CRC Mismatch")
  159. def _skip_message(self, size):
  160. try:
  161. message = self.serial.read(size)
  162. except:
  163. pass
  164. def _handle_message(self, message):
  165. try:
  166. self._check_crc(message)
  167. except ValueError:
  168. return AT_ERROR, [0,0,0,1]
  169. if message[3] == SC_READ_REGISTER:
  170. num_registers = message[4] + 1
  171. start_register = int.from_bytes(message[5:7], 'big')
  172. data = bytes()
  173. for register in range(start_register, start_register + num_registers):
  174. data += self.get_register_value(register)
  175. return AT_RESPONSE, data
  176. def get_register_value(self, register):
  177. if register in self.registers:
  178. return list(self.registers[register].to_bytes(4, 'big'))
  179. else:
  180. return list((0).to_bytes(4, 'big'))