console_parser.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. # Copyright 2015-2021 Espressif Systems (Shanghai) CO LTD
  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. import textwrap
  15. try:
  16. from typing import Optional
  17. except ImportError:
  18. pass
  19. import serial.tools.miniterm as miniterm
  20. from .constants import (CMD_APP_FLASH, CMD_ENTER_BOOT, CMD_MAKE, CMD_OUTPUT_TOGGLE, CMD_RESET, CMD_STOP,
  21. CMD_TOGGLE_LOGGING, CTRL_A, CTRL_F, CTRL_H, CTRL_L, CTRL_P, CTRL_R, CTRL_RBRACKET, CTRL_T,
  22. CTRL_X, CTRL_Y, TAG_CMD, TAG_KEY, __version__)
  23. from .output_helpers import red_print, yellow_print
  24. key_description = miniterm.key_description
  25. class ConsoleParser(object):
  26. def __init__(self, eol='CRLF'): # type: (str) -> None
  27. self.translate_eol = {
  28. 'CRLF': lambda c: c.replace('\n', '\r\n'),
  29. 'CR': lambda c: c.replace('\n', '\r'),
  30. 'LF': lambda c: c.replace('\r', '\n'),
  31. }[eol]
  32. self.menu_key = CTRL_T
  33. self.exit_key = CTRL_RBRACKET
  34. self._pressed_menu_key = False
  35. def parse(self, key): # type: (str) -> Optional[tuple]
  36. ret = None
  37. if self._pressed_menu_key:
  38. ret = self._handle_menu_key(key)
  39. elif key == self.menu_key:
  40. self._pressed_menu_key = True
  41. elif key == self.exit_key:
  42. ret = (TAG_CMD, CMD_STOP)
  43. else:
  44. key = self.translate_eol(key)
  45. ret = (TAG_KEY, key)
  46. return ret
  47. def _handle_menu_key(self, c): # type: (str) -> Optional[tuple]
  48. ret = None
  49. if c == self.exit_key or c == self.menu_key: # send verbatim
  50. ret = (TAG_KEY, c)
  51. elif c in [CTRL_H, 'h', 'H', '?']:
  52. red_print(self.get_help_text())
  53. elif c == CTRL_R: # Reset device via RTS
  54. ret = (TAG_CMD, CMD_RESET)
  55. elif c == CTRL_F: # Recompile & upload
  56. ret = (TAG_CMD, CMD_MAKE)
  57. elif c in [CTRL_A, 'a', 'A']: # Recompile & upload app only
  58. # "CTRL-A" cannot be captured with the default settings of the Windows command line, therefore, "A" can be used
  59. # instead
  60. ret = (TAG_CMD, CMD_APP_FLASH)
  61. elif c == CTRL_Y: # Toggle output display
  62. ret = (TAG_CMD, CMD_OUTPUT_TOGGLE)
  63. elif c == CTRL_L: # Toggle saving output into file
  64. ret = (TAG_CMD, CMD_TOGGLE_LOGGING)
  65. elif c == CTRL_P:
  66. yellow_print('Pause app (enter bootloader mode), press Ctrl-T Ctrl-R to restart')
  67. # to fast trigger pause without press menu key
  68. ret = (TAG_CMD, CMD_ENTER_BOOT)
  69. elif c in [CTRL_X, 'x', 'X']: # Exiting from within the menu
  70. ret = (TAG_CMD, CMD_STOP)
  71. else:
  72. red_print('--- unknown menu character {} --'.format(key_description(c)))
  73. self._pressed_menu_key = False
  74. return ret
  75. def get_help_text(self): # type: () -> str
  76. text = """\
  77. --- idf_monitor ({version}) - ESP-IDF monitor tool
  78. --- based on miniterm from pySerial
  79. ---
  80. --- {exit:8} Exit program
  81. --- {menu:8} Menu escape key, followed by:
  82. --- Menu keys:
  83. --- {menu:14} Send the menu character itself to remote
  84. --- {exit:14} Send the exit character itself to remote
  85. --- {reset:14} Reset target board via RTS line
  86. --- {makecmd:14} Build & flash project
  87. --- {appmake:14} Build & flash app only
  88. --- {output:14} Toggle output display
  89. --- {log:14} Toggle saving output into file
  90. --- {pause:14} Reset target into bootloader to pause app via RTS line
  91. --- {menuexit:14} Exit program
  92. """.format(version=__version__,
  93. exit=key_description(self.exit_key),
  94. menu=key_description(self.menu_key),
  95. reset=key_description(CTRL_R),
  96. makecmd=key_description(CTRL_F),
  97. appmake=key_description(CTRL_A) + ' (or A)',
  98. output=key_description(CTRL_Y),
  99. log=key_description(CTRL_L),
  100. pause=key_description(CTRL_P),
  101. menuexit=key_description(CTRL_X) + ' (or X)')
  102. return textwrap.dedent(text)
  103. def get_next_action_text(self): # type: () -> str
  104. text = """\
  105. --- Press {} to exit monitor.
  106. --- Press {} to build & flash project.
  107. --- Press {} to build & flash app.
  108. --- Press any other key to resume monitor (resets target).
  109. """.format(key_description(self.exit_key),
  110. key_description(CTRL_F),
  111. key_description(CTRL_A))
  112. return textwrap.dedent(text)
  113. def parse_next_action_key(self, c): # type: (str) -> Optional[tuple]
  114. ret = None
  115. if c == self.exit_key:
  116. ret = (TAG_CMD, CMD_STOP)
  117. elif c == CTRL_F: # Recompile & upload
  118. ret = (TAG_CMD, CMD_MAKE)
  119. elif c in [CTRL_A, 'a', 'A']: # Recompile & upload app only
  120. # "CTRL-A" cannot be captured with the default settings of the Windows command line, therefore, "A" can be used
  121. # instead
  122. ret = (TAG_CMD, CMD_APP_FLASH)
  123. return ret