pico_client.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import socket
  2. import msgpack
  3. import numpy as np
  4. import cmd
  5. import sys
  6. class PicoClient(cmd.Cmd):
  7. intro = 'Welcome to Pico ADC client. Type help or ? to list commands.\n'
  8. prompt = '(pico) '
  9. def __init__(self, host='localhost', port=5003):
  10. super().__init__()
  11. self.host = host
  12. self.port = port
  13. self.sock = None
  14. self.magic = 0xAA
  15. self.connected = False
  16. self.pk = msgpack.Packer()
  17. def connect(self):
  18. if self.connected:
  19. print("Already connected.")
  20. return
  21. try:
  22. self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  23. self.sock.connect((self.host, self.port))
  24. self.connected = True
  25. print(f"Connected to {self.host}:{self.port}")
  26. except Exception as e:
  27. print(f"Connection failed: {e}")
  28. def disconnect(self):
  29. if self.sock:
  30. self.sock.close()
  31. self.sock = None
  32. self.connected = False
  33. print("Disconnected.")
  34. def send_command(self, cmd_code, *args):
  35. if not self.connected:
  36. print("Not connected. Use 'connect' first.")
  37. return None
  38. try:
  39. buf = bytearray()
  40. buf.extend(self.pk.pack(0xAA))
  41. buf.extend(self.pk.pack(cmd_code))
  42. for arg in args:
  43. buf.extend(self.pk.pack(arg))
  44. self.sock.sendall(buf)
  45. return self.receive_response()
  46. except Exception as e:
  47. print(f"Send failed: {e}")
  48. return None
  49. def receive_response(self):
  50. try:
  51. unpacker = msgpack.Unpacker()
  52. while True:
  53. chunk = self.sock.recv(8192)
  54. if not chunk:
  55. break
  56. unpacker.feed(chunk)
  57. objects = []
  58. for obj in unpacker:
  59. objects.append(obj)
  60. print(f"Code: {objects[1]}")
  61. return objects[1], objects[2:]
  62. except Exception as e:
  63. print(f"Receive failed: {e}")
  64. return None
  65. def do_connect(self, arg):
  66. """Connect to the server."""
  67. self.connect()
  68. def do_disconnect(self, arg):
  69. """Disconnect from the server."""
  70. self.disconnect()
  71. def do_open(self, arg):
  72. """Open the Pico device."""
  73. resp = self.send_command(0x01)
  74. if resp:
  75. code, data = resp
  76. if code == 0xCC:
  77. print("Device opened successfully.")
  78. else:
  79. print(f"Error: {code}, {data}")
  80. def do_close(self, arg):
  81. """Close the Pico device."""
  82. resp = self.send_command(0x03)
  83. if resp:
  84. code, data = resp
  85. if code == 0xCC:
  86. print("Device closed successfully.")
  87. else:
  88. print(f"Error: {code}, {data}")
  89. def do_status(self, arg):
  90. """Get status."""
  91. resp = self.send_command(0x30)
  92. if resp:
  93. code, data = resp
  94. if code == 0xFF:
  95. print(f"Error: {code}, {data}")
  96. else:
  97. print(f"Status: {code}")
  98. def do_get_params(self, arg):
  99. """Get current parameters."""
  100. resp = self.send_command(0x04)
  101. if resp:
  102. code, data = resp
  103. if code == 0xCD and data:
  104. params = data
  105. sample_rate, measurement_times, number_channels, trig_channel, threshold, th_direction, trig_autoTrigger_ms = params
  106. print(f"Sample rate: {sample_rate}")
  107. print(f"Measurement times: {measurement_times}")
  108. print(f"Number channels: {number_channels}")
  109. print(f"Trig channel: {trig_channel}")
  110. print(f"Threshold: {threshold}")
  111. print(f"TH direction: {th_direction}")
  112. print(f"Trig auto trigger ms: {trig_autoTrigger_ms}")
  113. else:
  114. print(f"Error: {code}, {data}")
  115. def do_set_rate(self, arg):
  116. """Set sample rate. Usage: set_rate <rate>"""
  117. try:
  118. rate = int(arg)
  119. resp = self.send_command(0x07, rate)
  120. if resp:
  121. code, data = resp
  122. if code == 0xCC:
  123. print("Sample rate set.")
  124. else:
  125. print(f"Error: {code}, {data}")
  126. except ValueError:
  127. print("Invalid rate.")
  128. def do_set_points(self, arg):
  129. """Set points vector. Usage: set_points <p1> <p2> ..."""
  130. try:
  131. points = [int(x) for x in arg.split()]
  132. resp = self.send_command(0x06, points)
  133. if resp:
  134. code, data = resp
  135. if code == 0xCC:
  136. print("Points set.")
  137. else:
  138. print(f"Error: {code}, {data}")
  139. except ValueError:
  140. print("Invalid points.")
  141. def do_set_times(self, arg):
  142. """Set measurement times. Usage: set_times <times>"""
  143. try:
  144. times = int(arg)
  145. resp = self.send_command(0x08, times)
  146. if resp:
  147. code, data = resp
  148. if code == 0xCC:
  149. print("Times set.")
  150. else:
  151. print(f"Error: {code}, {data}")
  152. except ValueError:
  153. print("Invalid times.")
  154. def do_channel_configure(self, arg):
  155. """Configure channels. Usage: channel_configure <num_channels> <range1> <range2> ... <trig_channel>"""
  156. try:
  157. parts = arg.split()
  158. num_channels = int(parts[0])
  159. ranges = [int(x) for x in parts[1:1+num_channels]]
  160. trig_channel = int(parts[1+num_channels])
  161. resp = self.send_command(0x09, num_channels, ranges, trig_channel)
  162. if resp:
  163. code, data = resp
  164. if code == 0xCC:
  165. print("Channels configured.")
  166. else:
  167. print(f"Error: {code}, {data}")
  168. except (ValueError, IndexError):
  169. print("Invalid arguments. Usage: channel_configure <num> <ranges...> <trig>")
  170. def do_trigger_configure(self, arg):
  171. """Configure trigger. Usage: trigger_configure <th_direction> <threshold> <auto_trigger_ms>"""
  172. try:
  173. th_dir, thresh, auto_ms = [int(x) for x in arg.split()]
  174. resp = self.send_command(0x0A, th_dir, thresh, auto_ms) # Assuming 0x0A
  175. if resp:
  176. code, data = resp
  177. if code == 0xCC:
  178. print("Trigger configured.")
  179. else:
  180. print(f"Error: {code}, {data}")
  181. except ValueError:
  182. print("Invalid arguments.")
  183. def do_set_premeasurement(self, arg):
  184. """Set premeasurement percentage. Usage: set_premeasurement <percentage>"""
  185. try:
  186. perc = int(arg)
  187. resp = self.send_command(0x0B, perc) # Assuming 0x0B
  188. if resp:
  189. code, data = resp
  190. if code == 0xCC:
  191. print("Premeasurement set.")
  192. else:
  193. print(f"Error: {code}, {data}")
  194. except ValueError:
  195. print("Invalid percentage.")
  196. def do_measurement(self, arg):
  197. """Start measurement."""
  198. resp = self.send_command(0x20)
  199. if resp:
  200. code, data = resp
  201. if code == 0xCC:
  202. print("Measurement started.")
  203. else:
  204. print(f"Error: {code}, {data}")
  205. def do_stop(self, arg):
  206. """Stop measurement."""
  207. resp = self.send_command(0x0E)
  208. if resp:
  209. code, data = resp
  210. if code == 0xCC:
  211. print("Measurement stopped.")
  212. else:
  213. print(f"Error: {code}, {data}")
  214. def do_send_points(self, arg):
  215. """Send points (after measurement)."""
  216. if not self.connected:
  217. print("Not connected.")
  218. return
  219. try:
  220. packed = msgpack.packb([self.magic, 0xBB])
  221. self.sock.sendall(packed)
  222. # Receive data packets
  223. unpacker = msgpack.Unpacker()
  224. data_collected = {}
  225. while True:
  226. chunk = self.sock.recv(1024)
  227. if not chunk:
  228. break
  229. unpacker.feed(chunk)
  230. for obj in unpacker:
  231. if isinstance(obj, list) and len(obj) >= 2:
  232. magic, resp_code = obj[0], obj[1]
  233. if resp_code == 0xCA:
  234. i, j, packet = obj[2], obj[3], obj[4]
  235. key = (i, j)
  236. if key not in data_collected:
  237. data_collected[key] = []
  238. data_collected[key].extend(packet) # Assuming packet is list of int16
  239. elif resp_code == 0xCE:
  240. # Assemble data
  241. for key, full_data in data_collected.items():
  242. arr = np.array(full_data, dtype=np.int16)
  243. print(f"Data for measurement {key[0]}, channel {key[1]}: shape {arr.shape}, mean {arr.mean():.2f}")
  244. # Optionally save to file
  245. # np.save(f"data_{key[0]}_{key[1]}.npy", arr)
  246. # Send continue
  247. packed = msgpack.packb([self.magic, 0x3E])
  248. self.sock.sendall(packed)
  249. print("Data received and acknowledged.")
  250. return
  251. else:
  252. print(f"Unexpected response: {resp_code}")
  253. return
  254. except Exception as e:
  255. print(f"Send points failed: {e}")
  256. def do_quit(self, arg):
  257. """Quit the client."""
  258. self.disconnect()
  259. return True
  260. if __name__ == '__main__':
  261. if len(sys.argv) > 1:
  262. host = sys.argv[1]
  263. port = int(sys.argv[2]) if len(sys.argv) > 2 else 12345
  264. client = PicoClient(host, port)
  265. else:
  266. client = PicoClient()
  267. client.cmdloop()