miniterm_co.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. # Copyright 2024 Google LLC
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. # Stuff we're patching or calling into directly
  15. from serial.tools.miniterm import main
  16. from serial import Serial
  17. from serial.urlhandler.protocol_socket import Serial as SocketSerial
  18. from serial.serialutil import SerialException
  19. # Stuff we need
  20. import os
  21. import sys
  22. import threading
  23. import operator
  24. import time
  25. import unicodedata as ud
  26. import socket
  27. from logdehash import LogDehash
  28. line_buffer = []
  29. def dehash_read(self, size, plain_read):
  30. global line_buffer
  31. # Most of the time, we pass through the results of their read command (if rather reconstituted)
  32. # At the same time, keep track of the contents of the last line
  33. # Once at the end of a line, check if it contains a LH: loghash header
  34. # - If not, continue as usual
  35. # - If it does, dehash the buffered line and use clever tricks to swap it into the terminal
  36. # view in-place (obviously, don't be using this method for raw serial IO or file output)
  37. raw_read_data = plain_read(self, size)
  38. read_data = []
  39. for read_char in raw_read_data:
  40. if read_char == "\n":
  41. read_line = "".join(line_buffer)
  42. line_buffer = []
  43. line_dict = dehasher.dehash(read_line)
  44. read_data.append(dehasher.minicom_format_line(line_dict))
  45. else:
  46. line_buffer.append(read_char)
  47. read_data.append(read_char)
  48. return "".join(read_data)
  49. def socket_serial_read(self, size=1):
  50. """
  51. Read size bytes from the serial port. If a timeout is set it may
  52. return less characters as requested. With no timeout it will block
  53. until the requested number of bytes is read.
  54. This is a replacement for protocol_socket.SocketSerial.read() that is smarter about
  55. handling a closed socket from the remote end. Instead of just immediately returning an
  56. empty string, it attempts to reopen the socket when it detects it has closed.
  57. """
  58. data = bytearray()
  59. if self._timeout is not None:
  60. timeout = time.time() + self._timeout
  61. else:
  62. timeout = None
  63. while len(data) < size and (timeout is None or time.time() < timeout):
  64. if not self._isOpen:
  65. # If not open, try and re-open
  66. try:
  67. self.open()
  68. except SerialException:
  69. # Ignore failure to open and just wait a bit
  70. time.sleep(0.1)
  71. continue
  72. try:
  73. # Read available data
  74. block = self._socket.recv(size - len(data))
  75. if block:
  76. data.extend(block)
  77. else:
  78. # no data -> EOF (remote connection closed). If no data at all, loop until
  79. # we can reopen the socket
  80. self.close()
  81. if data:
  82. break
  83. except socket.timeout:
  84. # just need to get out of recv from time to time to check if
  85. # still alive
  86. continue
  87. except socket.error as e:
  88. # connection fails -> terminate loop
  89. raise SerialException('connection failed (%s)' % e)
  90. return bytes(data)
  91. # Insert ourselves in the serial read routine
  92. # (could also copy-paste the entire miniterm reader() method in here, which would be meh)
  93. # (or patch sys.stdout, which would just suck, because who knows what else that'd break)
  94. plain_read = Serial.read
  95. def dehash_serial_read(self, size):
  96. return dehash_read(self, size, plain_read)
  97. def dehash_socket_read(self, size):
  98. return dehash_read(self, size, socket_serial_read)
  99. try:
  100. from pyftdi.serialext.protocol_ftdi import FtdiSerial
  101. plain_pyftdi_read = FtdiSerial.read
  102. def dehash_pyftdi_serial_read(self, size):
  103. return dehash_read(self, size, plain_pyftdi_read)
  104. FtdiSerial.read = dehash_pyftdi_serial_read
  105. except ImportError:
  106. pass
  107. def yes_no_to_bool(arg):
  108. return True if arg == 'yes' else False
  109. # Process "arguments"
  110. arg_justify = "small"
  111. arg_color = False
  112. arg_bold = -1
  113. arg_core = False
  114. dict_path = os.getenv('PBL_CONSOLE_DICT_PATH')
  115. if not dict_path:
  116. dict_path = 'build/src/fw/loghash_dict.json'
  117. arglist = os.getenv("PBL_CONSOLE_ARGS")
  118. if arglist:
  119. for arg in arglist.split(","):
  120. if not arg:
  121. break
  122. key, value = arg.split('=')
  123. if key == "--justify":
  124. arg_justify = value
  125. elif key == "--color":
  126. arg_color = yes_no_to_bool(value)
  127. elif key == "--bold":
  128. arg_bold = int(value)
  129. elif key == "--dict":
  130. dict_path = value
  131. elif key == "--core":
  132. arg_core = yes_no_to_bool(value)
  133. else:
  134. raise Exception("Unknown console argument '{}'. Choices are ({})".
  135. format(key, ['--justify', '--color', '--bold',
  136. '--dict', '--core']))
  137. dehasher = LogDehash(dict_path, justify=arg_justify,
  138. color=arg_color, bold=arg_bold, print_core=arg_core)
  139. Serial.read = dehash_serial_read
  140. SocketSerial.read = dehash_socket_read
  141. # Make sure that the target is set
  142. if sys.argv[1] == 'None':
  143. raise Exception("No tty specified. Do you have a device attached?")
  144. # Fire it up as usual
  145. main()