├── .gitignore ├── LICENSE ├── documentacao ├── JSON output.png ├── custom parser registry.png ├── empty custom parser div.png ├── empty custom parser registry.png ├── empty graph tracking item.png ├── empty graph window.png ├── empty payload sequencer item.png ├── empty payload sequencer.png ├── empty standalone exception decoder.png ├── esp exception decoder section.png ├── exception decoder output.png ├── exception decoder parser color.png ├── filled graph tracking item.png ├── filter example.png ├── filtered history example.png ├── graph config ranges.png ├── graph max points.png ├── graph tracking empty.png ├── graph window with test invisible.png ├── graph window with values.png ├── grapher button.png ├── json parser color.png ├── log file empty.png ├── output history example.png ├── parser output entry history.png ├── payload sequencer button.png ├── payload sequencer window.png ├── standalone exception decoder button.png ├── standalone exception decoder decoded.png ├── standalone exception decoder filled in.png └── text finder.png ├── documentation ├── JSON output.png ├── custom parser registry.png ├── customParserExample.js ├── empty custom parser div.png ├── empty custom parser registry.png ├── empty graph tracking item.png ├── empty graph window.png ├── empty payload sequencer item.png ├── empty payload sequencer.png ├── empty standalone exception decoder.png ├── esp exception decoder section.png ├── exception decoder output.png ├── exception decoder parser color.png ├── filled graph tracking item.png ├── filter example.png ├── filtered history example.png ├── graph config ranges.png ├── graph max points.png ├── graph tracking empty.png ├── graph window with test invisible.png ├── graph window with values.png ├── graph window.png ├── grapher button.png ├── json parser color.png ├── log file empty.png ├── output history example.png ├── parser output entry history.png ├── payload sequencer button.png ├── payload sequencer window.png ├── standalone exception decoder button.png ├── standalone exception decoder decoded.png ├── standalone exception decoder filled in.png └── text finder.png ├── leia-me.md ├── readme.md └── src ├── decoders ├── riscv32-esp-elf-addr2line.exe ├── xtensa-esp32-elf-addr2line.exe ├── xtensa-esp32s2-elf-addr2line.exe ├── xtensa-esp32s3-elf-addr2line.exe └── xtensa-esp8266-elf-addr2line.exe ├── exception_decoder_window ├── decoder_screen.js └── index.html ├── graph_window ├── graph_handler.js └── index.html ├── images ├── icon.ico ├── icon.png ├── not-visible-eye.svg ├── settings gear.png ├── trash-2-16.png ├── usb-connected-16.png ├── usb-disconnected-16.png └── visible-eye.svg ├── language ├── en.json ├── language_handler.js └── pt-br.json ├── main.js ├── main_window ├── decoder.js ├── graph_parser.js ├── index.html ├── jlink_handler.js ├── parsers.js ├── preferences_handle.js └── renderer.js ├── package-lock.json ├── package.json ├── sequencer_window ├── index.html └── sequencer.js └── style ├── dark_theme_style.css ├── light_theme_style.css └── shared_style.css /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | builds/ 3 | src/preferences.json 4 | src/graph_config.json 5 | src/sequence.json 6 | log.txt 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Benjamim Krug 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /documentacao/JSON output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/JSON output.png -------------------------------------------------------------------------------- /documentacao/custom parser registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/custom parser registry.png -------------------------------------------------------------------------------- /documentacao/empty custom parser div.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/empty custom parser div.png -------------------------------------------------------------------------------- /documentacao/empty custom parser registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/empty custom parser registry.png -------------------------------------------------------------------------------- /documentacao/empty graph tracking item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/empty graph tracking item.png -------------------------------------------------------------------------------- /documentacao/empty graph window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/empty graph window.png -------------------------------------------------------------------------------- /documentacao/empty payload sequencer item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/empty payload sequencer item.png -------------------------------------------------------------------------------- /documentacao/empty payload sequencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/empty payload sequencer.png -------------------------------------------------------------------------------- /documentacao/empty standalone exception decoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/empty standalone exception decoder.png -------------------------------------------------------------------------------- /documentacao/esp exception decoder section.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/esp exception decoder section.png -------------------------------------------------------------------------------- /documentacao/exception decoder output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/exception decoder output.png -------------------------------------------------------------------------------- /documentacao/exception decoder parser color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/exception decoder parser color.png -------------------------------------------------------------------------------- /documentacao/filled graph tracking item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/filled graph tracking item.png -------------------------------------------------------------------------------- /documentacao/filter example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/filter example.png -------------------------------------------------------------------------------- /documentacao/filtered history example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/filtered history example.png -------------------------------------------------------------------------------- /documentacao/graph config ranges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/graph config ranges.png -------------------------------------------------------------------------------- /documentacao/graph max points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/graph max points.png -------------------------------------------------------------------------------- /documentacao/graph tracking empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/graph tracking empty.png -------------------------------------------------------------------------------- /documentacao/graph window with test invisible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/graph window with test invisible.png -------------------------------------------------------------------------------- /documentacao/graph window with values.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/graph window with values.png -------------------------------------------------------------------------------- /documentacao/grapher button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/grapher button.png -------------------------------------------------------------------------------- /documentacao/json parser color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/json parser color.png -------------------------------------------------------------------------------- /documentacao/log file empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/log file empty.png -------------------------------------------------------------------------------- /documentacao/output history example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/output history example.png -------------------------------------------------------------------------------- /documentacao/parser output entry history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/parser output entry history.png -------------------------------------------------------------------------------- /documentacao/payload sequencer button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/payload sequencer button.png -------------------------------------------------------------------------------- /documentacao/payload sequencer window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/payload sequencer window.png -------------------------------------------------------------------------------- /documentacao/standalone exception decoder button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/standalone exception decoder button.png -------------------------------------------------------------------------------- /documentacao/standalone exception decoder decoded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/standalone exception decoder decoded.png -------------------------------------------------------------------------------- /documentacao/standalone exception decoder filled in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/standalone exception decoder filled in.png -------------------------------------------------------------------------------- /documentacao/text finder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentacao/text finder.png -------------------------------------------------------------------------------- /documentation/JSON output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/JSON output.png -------------------------------------------------------------------------------- /documentation/custom parser registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/custom parser registry.png -------------------------------------------------------------------------------- /documentation/customParserExample.js: -------------------------------------------------------------------------------- 1 | function parserFunction(data_line, data_line_index) { 2 | var customResult = document.createElement("pre");//obligatory to work properly, but does not need to be of type pre, can be any type 3 | customResult.setAttribute("id", "p" + data_line_index);//obligatory to work properly 4 | customResult.innerHTML = current_language["results_from_line"] + data_line_index;//just a random example of what can be done with the custom Parser 5 | /* 6 | Inside here you can do anything with the data, be it create a custom way to show the data 7 | or output data, whatever is put into the customResult object will get saved in the output results array 8 | and it must be of node type, an element created by the document.createElement method. 9 | This format gives a lot of flexibility 10 | */ 11 | return customResult; //obligatory 12 | } -------------------------------------------------------------------------------- /documentation/empty custom parser div.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/empty custom parser div.png -------------------------------------------------------------------------------- /documentation/empty custom parser registry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/empty custom parser registry.png -------------------------------------------------------------------------------- /documentation/empty graph tracking item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/empty graph tracking item.png -------------------------------------------------------------------------------- /documentation/empty graph window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/empty graph window.png -------------------------------------------------------------------------------- /documentation/empty payload sequencer item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/empty payload sequencer item.png -------------------------------------------------------------------------------- /documentation/empty payload sequencer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/empty payload sequencer.png -------------------------------------------------------------------------------- /documentation/empty standalone exception decoder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/empty standalone exception decoder.png -------------------------------------------------------------------------------- /documentation/esp exception decoder section.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/esp exception decoder section.png -------------------------------------------------------------------------------- /documentation/exception decoder output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/exception decoder output.png -------------------------------------------------------------------------------- /documentation/exception decoder parser color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/exception decoder parser color.png -------------------------------------------------------------------------------- /documentation/filled graph tracking item.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/filled graph tracking item.png -------------------------------------------------------------------------------- /documentation/filter example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/filter example.png -------------------------------------------------------------------------------- /documentation/filtered history example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/filtered history example.png -------------------------------------------------------------------------------- /documentation/graph config ranges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/graph config ranges.png -------------------------------------------------------------------------------- /documentation/graph max points.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/graph max points.png -------------------------------------------------------------------------------- /documentation/graph tracking empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/graph tracking empty.png -------------------------------------------------------------------------------- /documentation/graph window with test invisible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/graph window with test invisible.png -------------------------------------------------------------------------------- /documentation/graph window with values.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/graph window with values.png -------------------------------------------------------------------------------- /documentation/graph window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/graph window.png -------------------------------------------------------------------------------- /documentation/grapher button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/grapher button.png -------------------------------------------------------------------------------- /documentation/json parser color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/json parser color.png -------------------------------------------------------------------------------- /documentation/log file empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/log file empty.png -------------------------------------------------------------------------------- /documentation/output history example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/output history example.png -------------------------------------------------------------------------------- /documentation/parser output entry history.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/parser output entry history.png -------------------------------------------------------------------------------- /documentation/payload sequencer button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/payload sequencer button.png -------------------------------------------------------------------------------- /documentation/payload sequencer window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/payload sequencer window.png -------------------------------------------------------------------------------- /documentation/standalone exception decoder button.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/standalone exception decoder button.png -------------------------------------------------------------------------------- /documentation/standalone exception decoder decoded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/standalone exception decoder decoded.png -------------------------------------------------------------------------------- /documentation/standalone exception decoder filled in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/standalone exception decoder filled in.png -------------------------------------------------------------------------------- /documentation/text finder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/documentation/text finder.png -------------------------------------------------------------------------------- /leia-me.md: -------------------------------------------------------------------------------- 1 | ### Introdução 2 | Esse é um Monitor Serial que eu estou desenvolvendo com todos os recursos que eu encontrei em diversos monitores diferentes ou então que eu pensei em adicionar para serem uteis. Ele é desenvolvido em Javascript com Node e Electron. Eu comecei esse projeto porque depois de usar muitos monitores seriais sempre alguma coisa faltava em algum deles, quando tinha um determinado recurso outro faltava, então decidi fazer o meu próprio monitor serial que tivesse todos os recursos necessários para um monitor serial. 3 | 4 | ### Recursos incluídos 5 | - Todos os recursos básico: 6 | - Seleção da porta COM 7 | - Seleção do Baudrate com uma vasta quantidade de opções 8 | - Auto Rolagem opcional 9 | - Timestamp de cada linha opcional 10 | - Envio de dados com final de linha selecionado e hotkey de envio configuravel entre Enter e Ctrl + Enter 11 | - Tema Claro e tema Escuro 12 | - Suporte multi-idioma, por hora apenas Inglês e Português Brasileiro estão incluídos, porém qualquer um pode fazer sua tradução e simplesmente adicionar na pasta languages(E se quiser pode ajudar por compartilhar ela para adicionar no programa) 13 | - Botão para descartar os dados recebidos no terminal 14 | - Arquivo de log para os dados recebidos, com a opção para escolher se vai sobrescrever ou concatenar ao arquivo da sessão anterior 15 | - Gráfico para os dados recebidos via a porta serial: 16 | 17 | graph with data 18 | 19 | - Parsers: 20 | - Decodificador de exceções do ESP: detecção automática de arquivo .elf e arquitetura do processador 21 | 22 | exception output 23 | 24 | - Parser de JSON para payloads JSON recebidos na porta serial 25 | 26 | json output 27 | 28 | - Parser customizado que podem ser adicionados pelo usuário, também escritos em JS, e ativados por substrings de gatilho. 29 | 30 | parser registry 31 | 32 | Uma explicação mais profunda sobre como usar o recurso de parser customizado pode ser encontrado [mais para frente no leia-me](#como-usar-o-recurso-de-parsers-customizados) 33 | - Histórico de entradas dos parsers similar ao histórico de pacotes do MQTT Explorer feito pelo [Thomas Nordquist](https://github.com/thomasnordquist) 34 | 35 | parser history 36 | 37 | - Cores customizáveis para as entradas de parser para facilitar a diferenciação de entradas similar ao [MQTT FX da SoftBlade](https://www.softblade.de/) 38 | 39 | exception color 40 | 41 | json color 42 | 43 | - Recurso para encontrar texto na página inteira usando o [electron-find feito pelo TheoXiong](https://github.com/TheoXiong/electron-find) 44 | 45 | text finder 46 | 47 | - Identificador opcional de conexão e desconexão no terminal 48 | - Opção para desconectar ao receber a mensagem de Boot dos microcontroladores da série ESP 49 | - Decodificador de Exceções do ESP independente, para decodificar exceções não provenientes do terminal 50 | 51 | ### Recursos planjeados para versões futuras 52 | - Outros parsers embutidos, como ModBus - RTU 53 | - Conexão de porta RTT 54 | - Upload de arquivos via protocolo X, Y, Z MODEM 55 | - Aumentar usabilidada do histórico de parser 56 | - Mais customização para os parsers embutidos 57 | - Recursos úteis que forem sendo sugeridos pelos usuários 58 | 59 | ### Processo de instalação 60 | O processo de instalação é bem simples, você simplesmente pode baixar a versão mais recente na [página de releases](https://github.com/BenjamimKrug/FullSerialMonitor/releases). Uma versão de Windows já está preparada para uso em um arquivo zip, mas se você usa alguma outra plataforma por hora a sua melhor opção é buildar a sua propria versão a partir do código fonte. 61 | Obs.: Por enquanto não tem nenhuma build para Linux ou Mac pois eu não tenho acesso à um computador com esses Sistemas operacionais, então não consigo testar o programa. 62 | 63 | Com o arquivo .zip, simplesmente descompacte ele e coloque a pasta resultante em algum lugar que você conseugir encontrar facilmente. A única coisa que você precisa ter cuidado é de não colocar dentro de uma pasta que precise de acesso especial, pois isso irá causar problemas quando o programa tentar usar arquivos dentro da pasta 64 | 65 | ### Como usar o recurso de Log 66 | Para usar o recurso de log você pode configurar isso no menu de configurações, na primeira vez que você abrir ele deve estar assim: 67 | 68 | log file empty 69 | 70 | O arquivo de log pode ser configurado de 3 modos diferentes: 71 | - *Nenhum*: Nenhum arquivo de log será criado e nenhum dado será guardado 72 | - *Sobrescrever*: Os dados recebidos no terminal serial vão sobrescrever os dados guardados na última sessão. Então se você fechar e abrir o programa, quando a porta serial for conectada, o log da última sessão será descartado. 73 | - *Concatenar*: Todos os dados recebidos no terminal serial será concatenado ao arquivo de log, então qualquer log guardado não será descartado mesmo ao fechar e abrir o programa. 74 | 75 | A chave "Ad. Timestamp" pode ser habilitada para adicionar o timestamp no arquivo de log. 76 | Para selecionar onde será criado o arquivo de log, simplesmente clique no botão "Buscar..." e selecione a pasta. Uma observação importante é que a pasta precisa ter no minimo 1 arquivo, não pode estar vazia, quando você estiver selecionando via o botão Buscar, isso se deve a uma limitação do sistema de busca de arquivos. Uma forma de contornar isso para poder usar uma pasta vazia é por digitar o caminho da pasta diretamente no input de texto, mas garanta que ele termina com '\' ou '/'. Esse comportamento será arrumado no futuro. 77 | 78 | Depois de configurado, quando você conectar na porta serial o arquivo "log.txt" será criado na pasta selecionada, e os dados recebidos serão tratados de acordo com o modo configurado. 79 | 80 | ### Como usar o Decodificador de Exceções do ESP embutido 81 | Se você quiser usar o Decodificador de Exceções do ESP embutido para o terminal da porta serial você pode selecionar o arquivo .elf para o código que está rodando atualmente no ESP por clicar no botão "Buscar": 82 | 83 | esp exception decoder section 84 | 85 | Isso vai abrir um navegador de arquivos para você escolher o arquivo. Alternativamente você pode simplesmente clicar no botão "Auto Detectar" se você estiver usando a Arduino IDE, que normalmente colocar os arquivos de compilação na pasta Temp, então o programa consegue encontrar por pegar a sketch compilada mais recentemente. 86 | Quando for recebida uma linha iniciando com "Backtrace" na porta serial, o parser faz o trabalho de decodificar esse backtrace e coloca o resultado na parte de baixo da tela: 87 | 88 | exception decoder output 89 | 90 | ## Como usar o Decodificador de Exceções do ESP independente 91 | Além do decodificador embutido no terminal, o programa inclui um Decodificador de Exceções do ESP independente, que você pode abrir pelo menu de ferramentas: 92 | 93 | standalone exception decoder button 94 | 95 | Ao abrir a página ela via ser assim como o seguinte: 96 | 97 | empty standalone exception decoder 98 | 99 | Então preencha o backtrace que você quer decodificar e selecione o caminho do seu arquivo .elf: 100 | 101 | standalone exception decoder filled in 102 | 103 | Se você usa a Arduino IDE, você pode simplesmente clicar no botão "Auto Detectar" para encontrar a sketch compilada mais recentemente. 104 | 105 | Com tudo preenchido, é só clicar em "Decodificar" e o backtrace decodificado vai aparecer na div de Saída: 106 | 107 | standalone exception decoder decoded 108 | 109 | ### Como usar o recurso de Parsers Customizados 110 | Para usar o recurso de parsers customizados, você precisa primeiramente criar um arquivo .js com o código do parser seguindo o padrão especificado no [arquivo de exemplo](/documentacao/customParserExample.js). Com esse arquivo criado, vá para a seção de parsers customizado: 111 | 112 | empty parser div 113 | 114 | Clique no botão "+" para adicionar um novo parser customizado 115 | 116 | parser registry 117 | 118 | Então preencha todos os campos: 119 | 120 | parser registry 121 | 122 | Onde cada campo é o seguinte: 123 | - *Nome*: nome do parser, para fácil identificação por parte do usuário 124 | - *Script*: caminho para o arquivo de script, pode ser escrito manualmente ou você pode clicar no botão "Buscar" para encontrar o arquivo 125 | - *Function*: nome da função que irá fazer o parseamento dos dados, importante cuidar para esse nome sempre ser único. 126 | - *Gatilho*: substring que precisar estar presente na linha para que ela seja enviada para o parser, por exemplo no caso do parser JSON o gatilho é "{" e o decodificador de exceções é "Backtrace" 127 | - *Cor*: cor de identificador do parser para as entradas na div de histório de saída 128 | 129 | Obs.: todo o parseamento é feito quando a linha foi recebida por completo, então ele acontece quando o caracter \n é recebido, terminando a linha. 130 | 131 | Com o parser configurado os resultados vão começar à aparecer na div de histórico de resultado para cada linha que contém a substring de gatilho. Para o exemplo dado ficaria assim: 132 | 133 | parser output entry history 134 | 135 | ### Como usar o recurso de Histórico dos resultados de Parser 136 | Conforme os parsers geram seus resultados, eles vão sendo adicionados na div de histórico como botões com o horário de entrada, o payload usado no parser e a cor configurada para esse parser. 137 | 138 | output history 139 | 140 | No canto direito você pode ver que tem um botão "Filtros" 141 | 142 | filter example 143 | 144 | Nesse dropdown você pode deixar habilitado quais parsers você quer ativamente ver, nesse caso o parser de JSON está escondido então apenas o Decodificador é mostrado: 145 | 146 | filtered history example 147 | 148 | Ao clicar no entrada de resultado de um parser, a div de resultado é preenchida com o resultado gerado para essa entrada. 149 | 150 | ### Como usar o recurso de Gráfico 151 | Para usar o recurso de gráfico, você precisa primeiramente abrir a janela por ir no botão de ferramentas e então clicar no botão de Gráfico 152 | 153 | grapher button 154 | 155 | Ao abrir a página, pelo menos na primeira vez ao abrir ela será assim: 156 | 157 | empty graph window 158 | 159 | Abrindo o menu de configurações, ele deve ser assim: 160 | 161 | empty graph tracking config 162 | 163 | Clique no botão "+" para adicionar um novo rastreio de gráfico 164 | 165 | empty graph tracking item 166 | 167 | Então preencha todos os campos: 168 | 169 | filled graph tracking item 170 | 171 | Onde cada campo é o seguinte: 172 | - *Nome*: nome da série, para a legenda da série no gráfico 173 | - *Gatilho*: substring no começo da linha para identificar o dado na porta serial, será removida do dado da linha e o valor numérico logo após será passado para o gráfico. 174 | - *Cor*: é a cor que será usada para mostrar a série no gráfico 175 | 176 | Então por exemplo, com o rastreio de gráfico configurado no exemplo, o texto recebido no terminal será o seguinte: 177 | `value15` 178 | Onde 15 é o valor que será adicionado à série do gráfico e o texto "value" é descartado. Assim como nos parsers customizados, um caracter de nova linha('\n') é necessário no final da linha para que o dado seja parseado. 179 | 180 | Quando o dado começar à vir na porta serial, o gráfico será preenchido: 181 | 182 | graph window with values 183 | 184 | Importante notar que no topo da tela tem uma legenda para cada uma das séries, nesse caso tem duas configuradas, ao clicar na label você pode esconder qualquer uma dar séries que vocês não quiser mais ver e então pode clicar de novo para vê-la novamente: 185 | 186 | graph window with test invisible 187 | 188 | Você pode configurar o range dos eixos via o menu de configuração: 189 | 190 | graph config ranges 191 | 192 | Se você não quiser configurar manualmente esses valores você pode clicar no slider de auto detectar, assim o gráfico irá identificar automaticamente o mínimo e o máximo valor presente nas séries. 193 | 194 | Por fim você pode configurar quantos pontos de dados do eixo X você quer ter no gráfico. Por exemplo, caso você queira que apenas 100 pontos sejam mostrados, você configura isso no menu: 195 | 196 | graph max points 197 | 198 | Com isso configurado, apenas os 100 pontos mais recentes serão mostrados no gráfico, todos os pontos além disso serão descartados. 199 | Se você não quiser que qualquer dado seja descartado, simplesmente configure esse valor como 0, então todos os dados serãp mantidos no gráfico até que eles sejam deletados com o botão no canto da tela. 200 | 201 | 202 | ### Como usar o recurso do Sequenciador de pacotes 203 | O programa inclui um recurso para programar uma sequência de pacotes à ser enviado pela porta serial, para usá-lo é pelo menu de ferramentas: 204 | 205 | payload sequencer button 206 | 207 | Ao abrir a janela pela primeira vez, ela estará assim: 208 | 209 | empty payload sequencer 210 | 211 | Para adicionar um pacote à sequência simplesmente clique em "Ad. Pacote", com isso um campo vazio será criado no final da sequência: 212 | 213 | empty payload sequencer item 214 | 215 | Onde cada campo é o seguinte: 216 | - *Pacote*: é o campo onde você irá preencher o dado de fato que você quer enviar 217 | - *Delay*: é o campo onde você irá configurar o tempo de delay até que o pacote seja enviado. Ele é contabilizado a partir do envio do último pacote, então para o primeiro pacote contabiliza a partir do momento em que você clica no botão "Enviar sequência" 218 | 219 | payload sequencer window 220 | 221 | Depois que você salvou a sequência, pode começar a enviá-la, se o slider de "Sequência contínua" estiver habilitado, a sequência vai repetir continuamente, se não ela irá enviar a sequência toda e então parar. 222 | 223 | Obs.: o final de linha de cada pacote será o final de linha configurado no topo do terminal 224 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ### Introduction 2 | This is a Serial monitor I developing with every feature I ever saw on various other monitors or found useful to have. Fully developed in Javascript with Node and Electron. I made it after using many serial monitors and not finding any of them complete, always missing some feature, so I decided to make my own serial monitor that would have all the features needed for a serial monitor. 3 | 4 | ### Included features 5 | - Every basic feature: 6 | - COM port selection 7 | - BaudRate selection with wide range of options 8 | - Optional autoscroll 9 | - Optional timestamp 10 | - Data sending with line ending options and optional hotkey, either Enter or Ctrl + Enter 11 | - Dark and Light theme 12 | - Multilanguage support, for now English and Brazillian Portuguese are built in, however anyone can make a translation and add it to the languages folder, (and help with adding them to program if they're kind hearted). 13 | - Received data clean button 14 | - Log file for the received data, with the option to choose whether to overwrite or append to the file 15 | - Graph for the data received on the serial port: 16 | 17 | graph with data 18 | 19 | - Parsers: 20 | - ESP Exception Decoder: automatic .elf file detection and processor architecture detection 21 | 22 | exception output 23 | 24 | - JSON parser for JSON payloads sent on the serial port 25 | 26 | json output 27 | 28 | - Custom parsers that can be added by the user, also written in JS, activated on custom trigger substrings. 29 | 30 | parser registry 31 | 32 | a deeper explanation on how to use the custom parser feature can be found [later on in this readme file](#how-to-use-the-custom-parsers-feature) 33 | - Parser entries history similar to the packet history in MQTT Explorer by [Thomas Nordquist](https://github.com/thomasnordquist) 34 | 35 | parser history 36 | 37 | - Parser entries with customized colors to help differientiate entries according to the parser similar to [MQTT FX by SoftBlade](https://www.softblade.de/) 38 | 39 | exception color 40 | 41 | json color 42 | 43 | - Text finder on the whole page from [electron-find from TheoXiong](https://github.com/TheoXiong/electron-find) 44 | 45 | text finder 46 | 47 | - Optional connection status change display 48 | - Optional disconnection on Boot message from ESP series microcontrollers 49 | - Standalone ESP Exception Decoder 50 | 51 | ### Planned features to be added on future versions 52 | - Other useful built in parsers, like ModBus - RTU 53 | - RTT port connection 54 | - X, Y, Z MODEM upload for files 55 | - Increase parser output history usability 56 | - More customization options for the built in parsers 57 | - Useful features suggested by the users 58 | 59 | ### Installation process 60 | The installation process is very simple, you simply download the most recent version on the [releases page](https://github.com/BenjamimKrug/FullSerialMonitor/releases). A Windows version is already built in a zip file, but if you use another platform for now your best option is to build the version yourself. 61 | Note: For now I don't have access to linux or Mac computer to test builds for those platforms. 62 | 63 | With the .zip file, simply unzip it and put the resulting folder somewhere you'll be able to find it. Only thing to be careful is to not put it somewhere where special admnistrative access is needed to access the files, as that will cause some problems with the program. 64 | 65 | ### How to use the Logging feature 66 | To use the logging feature you can configure it on the config menu, at first it will look like the following: 67 | 68 | log file empty 69 | 70 | The Log file can be configured into 3 modes: 71 | - *None*: No log file will be created and no data logging. 72 | - *Overwrite*: Data received on the serial terminal will overwrite any existing file from the previous session. So if you close the program and open it again, once the serial port is connected the previous log will be discarded. 73 | - *Append*: All data received on the serial terminal will be appended to the log file, so the log persists between closing and opening the program. 74 | 75 | The "Add Timestamp" switch can be checked to add the timestamp to the log file. 76 | To select where the log file will be created, simply click on the "Browse..." button and select the folder. An important note is that the folder must have at least one file, it cannot be empty, when you're selecting it through the Browse button, that is due to a limitation of the file browser system. A workaround for using an empty folder is to simply write the path of the folder into the text input, make sure that it ends in a '\' or '/' character. In the future this quirk will be fixed. 77 | 78 | Once it is all configured, when you connect to the serial port a file called "log.txt" will be created on the folder you selected, and the data received will be treated according to the mode configured. 79 | 80 | ### How to use the built-in ESP Exception Decoder 81 | If you want to use the built in ESP Exception Decoder for the serial port terminal you can just select the .elf file for the current code you're running on your ESP through the browse button: 82 | 83 | esp exception decoder section 84 | 85 | Which will then open a file explorer for you to select the file. Alternatively you can simply click the "Auto detect" button if you're using the Arduino IDE, which normally puts all it's compilation files in the Temp folder, so the program can find them by getting the most recently compiled. 86 | When a line starting with "Backtrace" is received on the serial port, the parser does the decoding automatically and outputs it in the lower half: 87 | 88 | exception decoder output 89 | 90 | ### How to use the standalone ESP Exception Decoder 91 | Beyond the built into the terminal Decoder, the program also includes a standalone ESP Exception decoder, you can open it via the tools menu: 92 | 93 | standalone exception decoder button 94 | 95 | Upon opening the window it will look like this: 96 | 97 | empty standalone exception decoder 98 | 99 | So just fill in your backtrace and the path to your .elf file: 100 | 101 | standalone exception decoder filled in 102 | 103 | If you use the Arduino IDE, you can simply click the "Auto Detect" button for it to find the most recently compiled sketch 104 | 105 | Once it is all filled in, just click the "Decode" button and the decoded backtrace will appear in the Output div: 106 | 107 | standalone exception decoder decoded 108 | 109 | ### How to use the Custom Parsers feature 110 | To use the custom parsers feature, you must first create a .js file with a parser function following the pattern specified in the [example file](/documentation/customParserExample.js). With the created file, go to the config menu on the custom parsers section: 111 | 112 | empty parser div 113 | 114 | Click on the "+" button to add a new custom parser 115 | 116 | parser registry 117 | 118 | Then fill all the fields: 119 | 120 | parser registry 121 | 122 | Where each field is the following: 123 | - *Name*: name of the parser, for easier use on the part of the user 124 | - *Script*: path for the script file, can be written manually or found through the browse function 125 | - *Function*: name of the function use to parse the data, it is important to be careful that the function name must always be unique. 126 | - *Trigger*: substring that must be in the line for it to get sent to the parser, for example in the JSON parser the trigger is "{" and for the exception decoder it's "Backtrace" 127 | - *Color*: identification color used on the parser entry in the output history div 128 | 129 | Note: all the parsing is done only when the line is fully sent, so it happens when a \n character is received, ending the line. 130 | 131 | When the parser is configured the results will start appearing on the output history div for every line that contains the string you filled in the trigger field. It should look something like this: 132 | 133 | parser output entry history 134 | 135 | ### How to use the Parser output history feature 136 | As the parsers put out results, they get added to the history div as a button with the time of entry, the payload used in the parser and the color assigned to that parser. 137 | 138 | output history 139 | 140 | In the right corner you can see there is a "Filters" button 141 | 142 | filter example 143 | 144 | On this dropdown you can check the parsers you actively want to see, in this case the JSON parser is unchecked so only the exception decoder gets show: 145 | 146 | filtered history example 147 | 148 | When you click on the parser output entry, the output div gets filled with the parser result for that entry. 149 | 150 | ### How to use the Graph Feature 151 | To use the graph feature, you must first open the window by going to the tools button and then clicking the Grapher button 152 | 153 | grapher button 154 | 155 | Once you open the window, at least on you first time opening it will look like this: 156 | 157 | empty graph window 158 | 159 | When you open the config menu it should look something like this: 160 | 161 | empty graph tracking config 162 | 163 | Click on the "+" button to add a new graph tracking 164 | 165 | empty graph tracking item 166 | 167 | Then fill all the fields: 168 | 169 | filled graph tracking item 170 | 171 | Where each field is the following: 172 | - *Name*: name of the series, for labeling the series on the graph 173 | - *Trigger*: substring at the start of the line used to identify the graph data on the serial port, it will be removed from the line data and the numerical value right after it will be sent to the graph. 174 | - *Color*: the color that will be used to display the series 175 | 176 | So for example with the graph tracking configured in the example the text received on the terminal will be the following: 177 | `value15` 178 | Where 15 is the value that get added to the graph series and the string "value" is discarted. As with the custom parsers, a new line('\n') character is needed at the end of the data for it to be parsed. 179 | 180 | When data starts to come in the serial port, the graph will be filled: 181 | 182 | graph window with values 183 | 184 | Note that on the top of the screen there is a label for each of the series currently being tracked, in this case there are two series, by clicking on the label you can hide any series you want and then click again to show them once more: 185 | 186 | graph window with test invisible 187 | 188 | You can set the axis ranges on the config menu: 189 | 190 | graph config ranges 191 | 192 | If you do not want to manually set the values you can simply check the auto detect slider, that way the graph will set the ranges based on the minimum and maximum values present in the series'. 193 | 194 | Finally you can configure how many data points in the X axis you wish to have in the graph. For example, In case you only want 100 points to be shown, you can configure it in the menu: 195 | 196 | graph max points 197 | 198 | Once that happens, only the most recent 100 points will be shown in the graph, all points beyond that will be discarded. 199 | If you do not want any data to be discarded, simply set the value to 0, then all data will be kept in the graph until you manually delete them with the delete button in the corner of the screen. 200 | 201 | 202 | ### How to use the Payload sequencer feature 203 | The program includes a feature to program a sequence of packets to be sent via the serial port, to use it you can open it via the tools menu: 204 | 205 | payload sequencer button 206 | 207 | Upon opening the window for the first time, it will look like this: 208 | 209 | empty payload sequencer 210 | 211 | To add a packet to the sequence simply click the "Add Packet" button, with that an empty field will be created at the end of the sequence: 212 | 213 | empty payload sequencer item 214 | 215 | Where each field is the following: 216 | - *Packet*: is the field where you will type in the actual data you want to send 217 | - *Delay*: is the field where you will put the delay until that packet is sent. It starts counting from the time it send the previous packet, so for the first packet it start counting from the moment you click the "send sequence" button. 218 | 219 | payload sequencer window 220 | 221 | After you've saved the sequence, you can start sending the sequence, if "Continuous sequence" is checked the sequence will repeat continously, if not it will run the whole sequence and then stop. 222 | 223 | Note: the line ending for each of the packets will be the line ending configured at the top of the terminal 224 | -------------------------------------------------------------------------------- /src/decoders/riscv32-esp-elf-addr2line.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/decoders/riscv32-esp-elf-addr2line.exe -------------------------------------------------------------------------------- /src/decoders/xtensa-esp32-elf-addr2line.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/decoders/xtensa-esp32-elf-addr2line.exe -------------------------------------------------------------------------------- /src/decoders/xtensa-esp32s2-elf-addr2line.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/decoders/xtensa-esp32s2-elf-addr2line.exe -------------------------------------------------------------------------------- /src/decoders/xtensa-esp32s3-elf-addr2line.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/decoders/xtensa-esp32s3-elf-addr2line.exe -------------------------------------------------------------------------------- /src/decoders/xtensa-esp8266-elf-addr2line.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/decoders/xtensa-esp8266-elf-addr2line.exe -------------------------------------------------------------------------------- /src/exception_decoder_window/decoder_screen.js: -------------------------------------------------------------------------------- 1 | //general code that is needed for every window we create 2 | const fs = require('fs'); 3 | const { ipcRenderer } = require('electron'); 4 | var theme_style = document.getElementById("theme_style"); 5 | 6 | ipcRenderer.on('recvChannel', (_event, arg) => { 7 | switch (arg.cmd) { 8 | case "setTheme": { 9 | theme_style.href = arg.theme; 10 | break; 11 | } 12 | case "setLang": { 13 | current_language = arg.lang; 14 | updateContentLang(); 15 | break; 16 | } 17 | default: 18 | break; 19 | } 20 | }); 21 | ipcRenderer.send('recvMain', { id: 0, cmd: "getTheme", requester: 3 }); 22 | ipcRenderer.send('recvMain', { id: 0, cmd: "getLang", requester: 3 }); 23 | 24 | // just a dummy variabel to not have a problem with the decoder function 25 | var preferences = { decoderColor:"#00000"}; 26 | 27 | var backtrace_data_input = document.getElementById("backtrace_data_input"); 28 | var output = document.getElementById("output"); 29 | 30 | function runManualDecode() { 31 | var backtrace_data = backtrace_data_input.value.trim(); 32 | decodeBacktrace(backtrace_data, 0, Date.now()); 33 | } 34 | 35 | function addParserResult(newResult, newResultSource, color, parserName, timestamp) { 36 | output.innerHTML = newResult.innerHTML; 37 | } -------------------------------------------------------------------------------- /src/exception_decoder_window/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 | 23 |
24 |
25 | 26 | 28 |
29 |
30 | 31 | 32 | 33 |
34 | 35 | 38 | 39 |
40 |
41 |
42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /src/graph_window/graph_handler.js: -------------------------------------------------------------------------------- 1 | //general code that is needed for every window we create 2 | const fs = require('fs'); 3 | const { ipcRenderer } = require('electron'); 4 | const Dygraph = require('dygraphs'); 5 | const theme_style = document.getElementById("theme_style"); 6 | const graph_container = document.getElementById("graph_container"); 7 | const config_menu = document.getElementById("config_menu"); 8 | config_menu.addEventListener("click", (e) => { 9 | e.stopPropagation(); 10 | }); 11 | document.body.addEventListener("click", () => { cancelConfig() }); 12 | const graphs_list_div = document.getElementById("graphs_list_div"); 13 | const data_per_screen_input = document.getElementById("data_per_screen"); 14 | const range_inputs = [ 15 | { 16 | value: document.getElementById("min_value"), 17 | auto: document.getElementById("min_auto") 18 | }, 19 | { 20 | value: document.getElementById("max_value"), 21 | auto: document.getElementById("max_auto") 22 | } 23 | ]; 24 | for (var i = 0; i < 2; i++) { 25 | range_inputs[i].auto.addEventListener("change", (e) => { 26 | var value_input = document.getElementById(e.target.id.replace("_auto", "_value")); 27 | if (e.target.checked) 28 | value_input.disabled = true; 29 | else 30 | value_input.disabled = false; 31 | }); 32 | } 33 | 34 | var dataArraySize = 0; 35 | var data = []; 36 | var dataLength = -1; 37 | var lastKnownValues = []; 38 | var chart; 39 | var graph_inspector = document.getElementById("graph_inspector"); 40 | var graph_legend = document.getElementById("graph_legend"); 41 | var visibility = []; 42 | 43 | var graph_config = { range: [null, null], data_per_screen: 0, array: [] }; 44 | var prev_graph_config = { ...graph_config }; 45 | var deleted_graphs = []; 46 | var graph_count = 0; 47 | 48 | ipcRenderer.on('recvChannel', (_event, arg) => { 49 | switch (arg.cmd) { 50 | case "setTheme": { 51 | theme_style.href = arg.theme; 52 | break; 53 | } 54 | case "createGraph": { 55 | createGraph(arg.name, arg.position, arg.color); 56 | break; 57 | } 58 | case "newGraphData": { 59 | newData(arg.time, arg.value, arg.position + 1); 60 | break; 61 | } 62 | case "setLang": { 63 | current_language = arg.lang; 64 | updateContentLang(); 65 | break; 66 | } 67 | default: 68 | break; 69 | } 70 | }); 71 | 72 | const graph_config_file_path = "./graph_config.json" 73 | 74 | fs.readFile(graph_config_file_path, 'utf8', (err, data) => { 75 | if (err) { 76 | console.log(err); 77 | return; 78 | } 79 | graph_config = JSON.parse(data); 80 | prev_graph_config = { ...graph_config }; 81 | updateGraphConfig(); 82 | }); 83 | 84 | ipcRenderer.send('recvMain', { id: 0, cmd: "getTheme", requester: 2 }); 85 | ipcRenderer.send('recvMain', { id: 0, cmd: "graphOpened", requester: 2 }); 86 | ipcRenderer.send('recvMain', { id: 0, cmd: "getLang", requester: 2 }); 87 | 88 | 89 | window.addEventListener('load', () => { 90 | chart = new Dygraph(graph_container, data, { 91 | showLabelsOnHighlight: true, 92 | connectSeparatedPoints: true, 93 | legend: "never", 94 | highlightCallback: inspectorFormatter, 95 | unhighlightCallback: hideInspector, 96 | highlightSeriesBackgroundAlpha: 1, 97 | highlightSeriesOpts: true 98 | }); 99 | }); 100 | 101 | function updateGraphConfig() { 102 | data_per_screen_input.value = graph_config.data_per_screen; 103 | for (var i = 0; i < 2; i++) { 104 | if (graph_config.range[i] != null) { 105 | range_inputs[i].value.disabled = false; 106 | range_inputs[i].value.value = graph_config.range[i]; 107 | range_inputs[i].auto.checked = false; 108 | } 109 | else { 110 | range_inputs[i].value.disabled = true; 111 | range_inputs[i].value.value = 0; 112 | range_inputs[i].auto.checked = true; 113 | } 114 | } 115 | deleted_graphs = []; 116 | graphs_list_div.innerHTML = ""; 117 | graph_count = 0; 118 | ipcRenderer.send('recvMain', { id: 0, cmd: "setGraphsArray", requester: 2, graphsArray: graph_config.array }); 119 | var graphTriggerSize = graph_config.array.length; 120 | var labels_array = ['Time']; 121 | var colors = []; 122 | visibility = []; 123 | graph_legend.innerHTML = ""; 124 | dataArraySize = graphTriggerSize; 125 | for (var i = 0; i < graphTriggerSize; i++) { 126 | var cur = graph_config.array[i]; 127 | createGraphField(cur.name, cur.color, cur.trigger); 128 | lastKnownValues[i] = [0, 0]; 129 | create_color_label(cur, i); 130 | labels_array.push(cur.name); 131 | colors.push(cur.color); 132 | visibility.push(true); 133 | } 134 | 135 | for (var l = 0; l < dataLength; l++) { 136 | var cur = data[l]; 137 | while (cur.length <= dataArraySize) { 138 | cur.push(null); 139 | } 140 | } 141 | chart.updateOptions({ labels: labels_array, colors: colors, valueRange: graph_config.range, file: data }); 142 | } 143 | 144 | function create_color_label(cur, i) { 145 | var text_label = document.createElement("label"); 146 | text_label.innerText = cur.name; 147 | 148 | var color_label = document.createElement("div"); 149 | color_label.setAttribute("class", "graph_color_label"); 150 | color_label.style["background-color"] = cur.color; 151 | color_label.addEventListener("click", () => { 152 | visibility[i] = !visibility[i]; 153 | if (visibility[i] == true) { 154 | color_label.classList.remove("graph_label_disabled"); 155 | text_label.classList.remove("graph_label_disabled"); 156 | } 157 | else { 158 | color_label.classList.add("graph_label_disabled"); 159 | text_label.classList.add("graph_label_disabled"); 160 | } 161 | chart.updateOptions({ visibility: visibility }); 162 | }); 163 | 164 | graph_legend.appendChild(color_label); 165 | graph_legend.appendChild(text_label); 166 | } 167 | 168 | function newData(time, value, position) { 169 | if (position == 0) 170 | return; 171 | time = time.split(':'); 172 | time.push(time[2].split('.')[1]); 173 | let now = new Date(); 174 | time = new Date(now.getFullYear(), now.getMonth(), now.getDate(), ...time); 175 | value = parseFloat(value); 176 | var createNewLine = false; 177 | try { 178 | if (data[dataLength][0] == time) { 179 | data[dataLength][position] = value; 180 | } 181 | else 182 | createNewLine = true; 183 | } 184 | catch (e) { 185 | createNewLine = true; 186 | } 187 | if (createNewLine) { 188 | var newDataArray = [time]; 189 | for (var i = 0; i < dataArraySize; i++) 190 | newDataArray.push(null); 191 | newDataArray[position] = value; 192 | data.push(newDataArray); 193 | dataLength++; 194 | if (graph_config.data_per_screen > 0) { 195 | while (dataLength > graph_config.data_per_screen) { 196 | data.shift(); 197 | dataLength--; 198 | } 199 | } 200 | } 201 | chart.updateOptions({ 'file': data }); 202 | } 203 | 204 | function saveGraphsConfig() { 205 | for (var i = 0; i < 2; i++) { 206 | if (range_inputs[i].auto.checked) 207 | graph_config.range[i] = null; 208 | else 209 | graph_config.range[i] = parseFloat(range_inputs[i].value.value.trim()); 210 | } 211 | graph_config.data_per_screen = parseFloat(data_per_screen_input.value.trim()); 212 | graph_config.array = []; 213 | for (var i = 0; i < graph_count; i++) { 214 | if (!deleted_graphs.includes(i)) { 215 | var newGraphName = document.getElementById("cgName" + i); 216 | var newGraphColor = document.getElementById("cgColor" + i); 217 | var newGraphTrigger = document.getElementById("cgTrigger" + i); 218 | graph_config.array.push({ 219 | name: newGraphName.value.trim(), 220 | color: newGraphColor.value.trim(), 221 | trigger: newGraphTrigger.value.trim() 222 | }); 223 | if (prev_graph_config.array[i] != undefined) { 224 | if (prev_graph_config.array[i].trigger != graph_config.array[i].trigger) { 225 | // if the trigger changed we discard the last data, as we cannot keep it 226 | for (var l = 0; l <= dataLength; l++) { 227 | data[l][i + 1] = null; 228 | } 229 | } 230 | } 231 | if (dataArraySize <= i) { 232 | for (var l = 0; l < dataLength; l++) { 233 | data[l].push(null); 234 | } 235 | } 236 | } 237 | else { 238 | for (var l = 0; l < dataLength; l++) { 239 | data[l].splice(i + 1, 1); 240 | } 241 | } 242 | } 243 | updateGraphConfig(true); 244 | try { 245 | fs.unlinkSync(graph_config_file_path); 246 | } 247 | catch (err) { 248 | // needs to be a separate try catch because it will fail on the first time configuring 249 | console.log(err); 250 | } 251 | try { 252 | fs.writeFileSync(graph_config_file_path, JSON.stringify(graph_config)); 253 | } 254 | catch (err) { 255 | console.log(err); 256 | ipcRenderer.send("openAlert", current_language["writing_error"]); 257 | } 258 | prev_graph_config = { ...graph_config }; 259 | } 260 | 261 | function setGraphsConfig(newGraphsConfig) { 262 | if (typeof newGraphsConfig != "undefined") 263 | graph_config = newGraphsConfig; 264 | updateGraphConfig(); 265 | } 266 | 267 | function deleteGraphField(id) { 268 | var targetGraphField = document.getElementById(`cgDiv${id}`); 269 | graphs_list_div.removeChild(targetGraphField); 270 | deleted_graphs.push(id); 271 | } 272 | 273 | function createGraphField(name, color, trigger) { 274 | var newGraphField = document.createElement("div"); 275 | newGraphField.setAttribute("id", "cgDiv" + graph_count); 276 | newGraphField.setAttribute("class", "custom_parser_entry"); 277 | 278 | var color_div = document.createElement("div"); 279 | color_div.setAttribute("class", "color_div"); 280 | var newGraphColor = document.createElement("input"); 281 | newGraphColor.setAttribute("type", "color"); 282 | newGraphColor.setAttribute("placeholder", current_language["graph_color_placeholder"]); 283 | newGraphColor.setAttribute("id", "cgColor" + graph_count); 284 | newGraphColor.setAttribute("class", "custom_parser_input"); 285 | if (typeof (color) !== 'undefined') 286 | newGraphColor.setAttribute("value", color); 287 | color_div.appendChild(newGraphColor); 288 | 289 | var newGraphName = document.createElement("input"); 290 | newGraphName.setAttribute("type", "text"); 291 | newGraphName.setAttribute("placeholder", current_language["graph_name_placeholder"]); 292 | newGraphName.setAttribute("id", "cgName" + graph_count); 293 | newGraphName.setAttribute("class", "custom_parser_input"); 294 | if (typeof (name) !== 'undefined') 295 | newGraphName.setAttribute("value", name); 296 | 297 | 298 | var newGraphTrigger = document.createElement("input"); 299 | newGraphTrigger.setAttribute("type", "text"); 300 | newGraphTrigger.setAttribute("placeholder", current_language["graph_trigger_placeholder"]); 301 | newGraphTrigger.setAttribute("id", "cgTrigger" + graph_count); 302 | newGraphTrigger.setAttribute("class", "custom_parser_input"); 303 | if (typeof (trigger) !== 'undefined') 304 | newGraphTrigger.setAttribute("value", trigger); 305 | 306 | var newGraphExclude = document.createElement("img"); 307 | newGraphExclude.setAttribute("width", "16"); 308 | newGraphExclude.setAttribute("height", "16"); 309 | newGraphExclude.setAttribute("src", "../images/trash-2-16.png"); 310 | newGraphExclude.setAttribute("onclick", `deleteGraphField(${graph_count})`); 311 | 312 | newGraphField.appendChild(color_div); 313 | 314 | var line = document.createElement("div"); 315 | line.setAttribute("class", "line"); 316 | var label = document.createElement("label"); 317 | label.setAttribute("data-i18n", "name"); 318 | label.innerText = current_language["name"]; 319 | line.appendChild(label); 320 | line.appendChild(newGraphName); 321 | newGraphField.appendChild(line); 322 | 323 | line = document.createElement("div"); 324 | line.setAttribute("class", "line"); 325 | label = document.createElement("label"); 326 | label.setAttribute("data-i18n", "trigger"); 327 | label.innerText = current_language["trigger"]; 328 | line.appendChild(label); 329 | line.appendChild(newGraphTrigger); 330 | newGraphField.appendChild(line); 331 | 332 | newGraphField.appendChild(newGraphExclude); 333 | graphs_list_div.appendChild(newGraphField); 334 | graph_count++; 335 | } 336 | 337 | function inspectorFormatter(event, x, points, row, seriesName) { 338 | if (event.constructor.name != "MouseEvent") { 339 | hideItem(graph_inspector); 340 | return; 341 | } 342 | graph_inspector.style.display = "block"; 343 | graph_inspector.innerHTML = ""; 344 | var time_label = document.createElement("label"); 345 | var date_x = new Date(x); 346 | time_label.innerText = date_x.toISOString().replace("T", " ").replace("Z", " "); 347 | graph_inspector.appendChild(time_label); 348 | for (var i = 0; i < points.length; i++) { 349 | var cur = points[i]; 350 | var value = document.createElement("label"); 351 | value.innerText = cur.name + ": " + cur.yval; 352 | graph_inspector.appendChild(document.createElement("br")); 353 | graph_inspector.appendChild(value); 354 | } 355 | var style = getComputedStyle(graph_inspector); 356 | var horizontal_compensantion = window.innerWidth - (event.pageX + parseFloat(style.width) + 15); 357 | if (horizontal_compensantion > 0) 358 | horizontal_compensantion = 0; 359 | 360 | var vertical_compensantion = window.innerHeight - (event.pageY + parseFloat(style.height) + 15); 361 | if (vertical_compensantion > 0) 362 | vertical_compensantion = 0; 363 | graph_inspector.style.left = event.pageX + horizontal_compensantion; 364 | graph_inspector.style.top = event.pageY + vertical_compensantion; 365 | } 366 | 367 | function hideInspector(event) { 368 | hideItem(graph_inspector); 369 | } 370 | 371 | function cancelConfig() { 372 | if (config_menu.classList.contains("hidden")) 373 | return; 374 | setGraphsConfig(prev_graph_config); 375 | hideItem(config_menu); 376 | } 377 | 378 | function showItem(item) { 379 | if (item.classList.contains("hidden")) item.classList.remove("hidden"); 380 | } 381 | 382 | function hideItem(item) { 383 | if (!item.classList.contains("hidden")) item.classList.add("hidden"); 384 | } 385 | 386 | function toggleHide(item) { 387 | if (item.classList.contains("hidden")) item.classList.remove("hidden"); 388 | else item.classList.add("hidden"); 389 | } 390 | 391 | document.getElementById("open_config_menu").onclick = function (e) { 392 | e.stopPropagation(); 393 | toggleHide(config_menu); 394 | }; 395 | 396 | document.getElementById("delete_graph_data").onclick = function (e) { 397 | e.stopPropagation(); 398 | data = []; 399 | dataLength = 0; 400 | }; -------------------------------------------------------------------------------- /src/graph_window/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 54 | 55 | 56 |
57 | 61 | 65 |
66 |
67 |
68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/images/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/images/icon.ico -------------------------------------------------------------------------------- /src/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/images/icon.png -------------------------------------------------------------------------------- /src/images/not-visible-eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/images/settings gear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/images/settings gear.png -------------------------------------------------------------------------------- /src/images/trash-2-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/images/trash-2-16.png -------------------------------------------------------------------------------- /src/images/usb-connected-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/images/usb-connected-16.png -------------------------------------------------------------------------------- /src/images/usb-disconnected-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BenjamimKrug/FullSerialMonitor/b9ba980712f3c233f6d6fecbf31b459fbbb24a7b/src/images/usb-disconnected-16.png -------------------------------------------------------------------------------- /src/images/visible-eye.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/language/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "English", 3 | "send": "SEND", 4 | "no_line_ending": "No line ending", 5 | "line_feed": "LF(Line feed or New line)", 6 | "carriage_return": "CR(Carriage return)", 7 | "carriage_plus_new_line": "CR+LF(Carriage return + New line)", 8 | "output": "OUTPUT", 9 | "language_label": "Language:", 10 | "log_file": "Log file", 11 | "none": "None", 12 | "overwrite": "Overwrite", 13 | "append": "Append", 14 | "add_timestamp": "Add Timestamp", 15 | "auto_scroll": "autoScroll", 16 | "miscellaneous": "Miscellaneous", 17 | "theme": "Theme", 18 | "dark": "Dark", 19 | "light": "Light", 20 | "ctrl_enter": "Use Ctrl+Enter to send", 21 | "disconnect_on_boot": "Disconnect on Boot message", 22 | "show_con_changes": "Show Connection changes", 23 | "advanced_config": "Enable advanced port Configurations", 24 | "exception_decoder": "ESP Exception decoder", 25 | "json_parser": "JSON parser", 26 | "custom_parsers": "Custom Parsers", 27 | "delete": "Delete", 28 | "name": "Name", 29 | "parser_name_placeholder": "parser name", 30 | "function": "Function", 31 | "parser_function_placeholder": "parser function", 32 | "parser_color_placeholder": "parser color", 33 | "script": "Script", 34 | "parser_script_placeholder": "script file path", 35 | "trigger": "Trigger", 36 | "parser_trigger_placeholder": "parser trigger", 37 | "browse": "Browse...", 38 | "results_from_line": "results from line ", 39 | "cancel": "Cancel", 40 | "save_and_close": "Save and Close", 41 | "config": "Config", 42 | "port": "Port", 43 | "parity": "Parity", 44 | "odd": "Odd", 45 | "even": "Even", 46 | "databits": "DataBits", 47 | "stopbits": "StopBits", 48 | "rtscts": "RTS/CTS flow control", 49 | "xon": "XON flow control", 50 | "xoff": "XOFF flow control", 51 | "xany": "XANY flow control", 52 | "hupcl": "Drop DTR on close", 53 | "connect": "CONNECT", 54 | "disconnect": "DISCONNECT", 55 | "clean": "CLEAN", 56 | "decode": "Decode", 57 | "exception_decoder_label": "ESP EXCEPTION DECODER", 58 | "auto_detect": "Auto Detect", 59 | "arch": "Arch", 60 | "elf_file": "ELF File", 61 | "output_history": "OUTPUT HISTORY", 62 | "filters": "Filters", 63 | "sequence": "Sequence", 64 | "add_packet": "Add Packet", 65 | "save_sequence": "Save Sequence", 66 | "send_sequence": "Send Sequence", 67 | "stop_sequence": "Stop Sequence", 68 | "continuous_sequence": "Continuous Sequence", 69 | "packet": "Packet", 70 | "packet_data_placeholder": "packet data", 71 | "delay": "Delay", 72 | "edit": "Edit", 73 | "cut": "Cut", 74 | "copy": "Copy", 75 | "paste": "Paste", 76 | "select_all": "Select All", 77 | "help": "Help", 78 | "documentation": "Documentation", 79 | "issues": "Issues", 80 | "tools": "Tools", 81 | "payload_sequencer": "Payload Sequencer", 82 | "grapher": "Grapher", 83 | "toggle_dev_tools": "Toggle Dev Tools", 84 | "backtrace_error": { 85 | "title": "Parse Error", 86 | "content": "Backtrace could not be parsed, choose .elf file please" 87 | }, 88 | "esp_support_error": { 89 | "title": "Support Error", 90 | "content": "Core not supported by the exception decoder" 91 | }, 92 | "elf_not_found_error": { 93 | "title": "Could not find .elf file", 94 | "content": "" 95 | }, 96 | "folder_empty_error": { 97 | "title": "Folder completely empty, must have at least one file", 98 | "content": "" 99 | }, 100 | "writing_error": { 101 | "title": "Error on writing", 102 | "content": "" 103 | }, 104 | "serial_port_not_open_error": { 105 | "title": "Serial Port not Open ", 106 | "content": "Cannot send Sequence, please connect to a serial port" 107 | }, 108 | "no_com_ports_error": { 109 | "title": "No COM ports detected", 110 | "content": "" 111 | }, 112 | "undefined_value_error": { 113 | "title": "Error: undefined value", 114 | "content": "Restart the application, if the issue persists, please file an issue on github" 115 | }, 116 | "serialport_error": "Error with the serial Port:", 117 | "serialport_open_error": "Error on opening port:", 118 | "log_folder_does_not_exist": { 119 | "title": "Folder for the Log file does not exist", 120 | "content": "" 121 | }, 122 | "port_disconnected": "Port disconnected", 123 | "graphs": "Graphs", 124 | "graph_name_placeholder": "graph name", 125 | "graph_color_placeholder": "graph color", 126 | "graph_trigger_placeholder": "graph trigger", 127 | "data_per_screen": "Data points on screen(set to 0 if no limit is wanted)", 128 | "min_range": "Range Minimum Value", 129 | "max_range": "Range Maximum Value" 130 | } 131 | -------------------------------------------------------------------------------- /src/language/language_handler.js: -------------------------------------------------------------------------------- 1 | var current_language; 2 | var language_config = document.getElementById("language_config"); 3 | // Function to update content based on selected language 4 | function updateContentLang() { 5 | document.querySelectorAll('[data-i18n]').forEach(element => { 6 | const key = element.getAttribute('data-i18n'); 7 | element.textContent = current_language[key]; 8 | }); 9 | if (window.location.href.includes("main_window")) { 10 | ipcRenderer.send('setLang', current_language); 11 | ipcRenderer.send('recvMain', { id: 1, cmd: 'setLang', lang: current_language }); 12 | ipcRenderer.send('recvMain', { id: 2, cmd: 'setLang', lang: current_language }); 13 | ipcRenderer.send('recvMain', { id: 3, cmd: 'setLang', lang: current_language }); 14 | } 15 | } 16 | 17 | function readLanguage() { 18 | if (typeof language_config == "undefined") 19 | return; 20 | var data = fs.readFileSync('./language/' + language_config.value, { encoding: 'utf8', flag: 'r' }); 21 | current_language = JSON.parse(data); 22 | } 23 | 24 | function updateLanguageList() { 25 | if (typeof language_config == "undefined") 26 | return; 27 | var current_language_config = language_config.value; 28 | var files = fs.readdirSync('./language/', { withFileTypes: true }); 29 | language_config.innerHTML = ""; 30 | var file_count = files.length; 31 | for (var i = 0; i < file_count; i++) { 32 | if (files[i].name == "language_handler.js") 33 | continue; 34 | var option = document.createElement("option"); 35 | option.value = files[i].name; 36 | option.innerHTML = files[i].name.replace(".json", ""); 37 | if (current_language_config == files[i].name) 38 | option.setAttribute("selected", ""); 39 | language_config.appendChild(option); 40 | } 41 | } -------------------------------------------------------------------------------- /src/language/pt-br.json: -------------------------------------------------------------------------------- 1 | { 2 | "lang": "Português", 3 | "send": "ENVIAR", 4 | "no_line_ending": "Sem final de linha", 5 | "line_feed": "LF(Nova linha)", 6 | "carriage_return": "CR(Retorno de carro)", 7 | "carriage_plus_new_line": "CR+LF(Retorno de carro + Nova linha)", 8 | "output": "SAÍDA", 9 | "language_label": "Idioma:", 10 | "log_file": "Arquivo de registro", 11 | "none": "Nenhum(a)", 12 | "overwrite": "Sobrescrever", 13 | "append": "Concatenar", 14 | "add_timestamp": "Ad. Timestamp", 15 | "auto_scroll": "autoScroll", 16 | "miscellaneous": "Misturados", 17 | "theme": "Tema", 18 | "dark": "Escuro", 19 | "light": "Claro", 20 | "ctrl_enter": "Usar Ctrl+Enter para enviar", 21 | "disconnect_on_boot": "Disconectar com a mensagem de Boot", 22 | "show_con_changes": "Mostrar mudanças de conexão", 23 | "advanced_config": "Habilitar configurações avançadas da porta", 24 | "exception_decoder": "Decodificador de Exceções do ESP", 25 | "json_parser": "Parser de JSON", 26 | "custom_parsers": "Parsers customizados", 27 | "delete": "Deletar", 28 | "name": "Nome", 29 | "parser_name_placeholder": "nome do parser", 30 | "function": "Função", 31 | "parser_function_placeholder": "função do parser", 32 | "parser_color_placeholder": "cor do parser ", 33 | "script": "Script", 34 | "parser_script_placeholder": "caminho do arquivo do script", 35 | "trigger": "Gatilho", 36 | "parser_trigger_placeholder": "Gatilho do parser", 37 | "browse": "Buscar...", 38 | "results_from_line": "Resultados da linha ", 39 | "cancel": "Cancelar", 40 | "save_and_close": "Salvar e fechar", 41 | "config": "Configuração", 42 | "port": "Porta", 43 | "parity": "Paridade", 44 | "odd": "Ímpar", 45 | "even": "Par", 46 | "databits": "DataBits", 47 | "stopbits": "StopBits", 48 | "rtscts": "Controle de fluxo RTS/CTS", 49 | "xon": "Controle de fluxo XON", 50 | "xoff": "Controle de fluxo XOFF", 51 | "xany": "Controle de fluxo XANY", 52 | "hupcl": "Largar DTR ao fechar", 53 | "connect": "CONECTAR", 54 | "disconnect": "DISCONECTAR", 55 | "clean": "LIMPAR", 56 | "decode": "Decodificar", 57 | "exception_decoder_label": "DECODIFICADOR DE EXCEÇÕES DO ESP", 58 | "auto_detect": "Auto Detectar", 59 | "arch": "Arquitetura", 60 | "elf_file": "Arquivo ELF", 61 | "output_history": "Histórico de saídas", 62 | "filters": "Filtros", 63 | "sequence": "Sequencia", 64 | "add_packet": "Ad. Pacote", 65 | "save_sequence": "Salvar Sequência", 66 | "send_sequence": "Enviar Sequência", 67 | "stop_sequence": "Parar Sequência", 68 | "continuous_sequence": "Sequência Contínua", 69 | "packet": "Pacote", 70 | "packet_data_placeholder": "dados do pacote", 71 | "delay": "Delay", 72 | "edit": "Editar", 73 | "cut": "Cortar", 74 | "copy": "Copiar", 75 | "paste": "Colar", 76 | "select_all": "Selecionar todos", 77 | "help": "Ajuda", 78 | "documentation": "Documentação", 79 | "issues": "Problemas", 80 | "tools": "Ferramentas", 81 | "payload_sequencer": "Sequenciador de Pacotes", 82 | "grapher": "Gráfico", 83 | "toggle_dev_tools": "Abrir Ferramentas de Desenvolvedor", 84 | "backtrace_error": { 85 | "title": "Erro de Parseamento", 86 | "content": "Backtrace não pode ser parseado, selecione um arquivo .elf" 87 | }, 88 | "esp_support_error": { 89 | "title": "Erro de Suporte", 90 | "content": "Core não suportado pelo decoder de exceções" 91 | }, 92 | "elf_not_found_error": { 93 | "title": "Não foi possível encontrar o arquivo .elf", 94 | "content": "" 95 | }, 96 | "folder_empty_error": { 97 | "title": "Pasta completamente vazia, precisa ter ao menos um arquivo", 98 | "content": "" 99 | }, 100 | "writing_error": { 101 | "title": "Erro ao escrever", 102 | "content": "" 103 | }, 104 | "serial_port_not_open_error": { 105 | "title": "Porta Serial não está aberta", 106 | "content": "Não pode enviar a sequência, por favor conecte-se com uma porta serial" 107 | }, 108 | "no_com_ports_error": { 109 | "title": "Nenhuma porta COM detectada", 110 | "content": "" 111 | }, 112 | "undefined_value_error": { 113 | "title": "Erro: valor undefined", 114 | "content": "Reinicie o aplicativo, caso o problema não se resolva, registre um issue no github" 115 | }, 116 | "serialport_error": "Erro com a porta serial:", 117 | "serialport_open_error": "Erro ao abrir a porta serial:", 118 | "log_folder_does_not_exist": { 119 | "title": "Pasta do arquivo de registros não existe", 120 | "content": "" 121 | }, 122 | "port_disconnected": "Porta COM desconectada", 123 | "graphs": "Gráficos", 124 | "graph_name_placeholder": "nome do gráfico", 125 | "graph_color_placeholder": "cor do gráfico", 126 | "graph_trigger_placeholder": "gatilho do gráfico", 127 | "data_per_screen": "Pontos de dados na tela", 128 | "min_range": "Valor mínimo do gráfico", 129 | "max_range": "Valor máximo do gráfico" 130 | } -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | /* 2 | Full Serial monitor written by Benjamim Krug 3 | https://github.com/BenjamimKrug/FullSerialMonitor 4 | for more information refer to the readme file 5 | */ 6 | const { app, BrowserWindow, Menu, ipcMain, globalShortcut, shell, dialog } = require('electron'); 7 | const path = require('path'); 8 | const url = require('url'); 9 | require('@electron/remote/main').initialize(); 10 | 11 | // Keep a global reference of the window object, if you don't, the window will 12 | // be closed automatically when the JavaScript object is garbage collected. 13 | const windows = [null, null, null, null, null]; 14 | var options = { 15 | forward: true, 16 | findNext: false, 17 | matchCase: false, 18 | wordStart: false, 19 | medialCapitalAsWordStart: false 20 | } 21 | 22 | // needs to have this for atleast the first time 23 | var current_language = {}; 24 | 25 | function createWindow(file_name, index) { 26 | if (windows[index] != null) { 27 | windows[index].focus(); 28 | return; 29 | } 30 | // Create the browser window. 31 | let newWindow = new BrowserWindow({ 32 | width: 1000, 33 | height: 800, 34 | backgroundColor: "#ccc", 35 | webPreferences: { 36 | nodeIntegration: true, // to allow require 37 | contextIsolation: false, // allow use with Electron 12+ 38 | }, 39 | icon: __dirname + '/images/icon.ico' 40 | }); 41 | newWindow.maximize(); 42 | 43 | newWindow.loadURL(url.format({ 44 | pathname: path.join(__dirname, file_name), 45 | protocol: 'file:', 46 | slashes: true 47 | })); 48 | 49 | // Open the DevTools. 50 | //mainWindow.webContents.openDevTools() 51 | if (index == 2) { 52 | // Emitted when the window is closed. 53 | newWindow.on('closed', function () { 54 | // Dereference the window object, usually you would store windows 55 | // in an array if your app supports multi windows, this is the time 56 | // when you should delete the corresponding element. 57 | 58 | try { 59 | windows[0].webContents.send('recvChannel', { cmd: "graphClosed" }); 60 | } catch (e) { 61 | 62 | } 63 | windows[index] = null; 64 | newWindow = null; 65 | }); 66 | } 67 | else if (index == 0) { 68 | newWindow.on('closed', function () { 69 | // Dereference the window object, usually you would store windows 70 | // in an array if your app supports multi windows, this is the time 71 | // when you should delete the corresponding element. 72 | for (var i = 1; i < windows.length; i++) { 73 | if (windows[i] != null) 74 | windows[i].close(); 75 | } 76 | windows[index] = null; 77 | newWindow = null; 78 | }); 79 | } else newWindow.on('closed', function () { 80 | // Dereference the window object, usually you would store windows 81 | // in an array if your app supports multi windows, this is the time 82 | // when you should delete the corresponding element. 83 | windows[index] = null; 84 | newWindow = null; 85 | }); 86 | 87 | require("@electron/remote/main").enable(newWindow.webContents); 88 | 89 | if (file_name.indexOf("main") > -1) { 90 | newWindow.on('focus', () => { 91 | globalShortcut.register('CmdorCtrl+F', () => { 92 | newWindow.webContents.send('find_request', options); 93 | }); 94 | }); 95 | 96 | newWindow.on('blur', () => { 97 | globalShortcut.unregister('CmdorCtrl+F'); 98 | }); 99 | } 100 | windows[index] = newWindow; 101 | return newWindow; 102 | } 103 | 104 | function makeMenuTemplate() { 105 | const template = [ 106 | { 107 | label: current_language["edit"], 108 | submenu: [ 109 | { label: current_language["cut"], role: 'cut' }, 110 | { label: current_language["copy"], role: 'copy' }, 111 | { label: current_language["paste"], role: 'paste' }, 112 | { label: current_language["select_all"], role: 'selectAll' } 113 | ] 114 | }, 115 | { 116 | label: current_language["help"], 117 | submenu: [ 118 | { 119 | label: current_language["documentation"], 120 | click: function () { 121 | shell.openExternal("https://github.com/BenjamimKrug/FullSerialMonitor#readme"); 122 | } 123 | 124 | }, 125 | { 126 | label: current_language["issues"], 127 | click: function () { 128 | shell.openExternal("https://github.com/BenjamimKrug/FullSerialMonitor/issues"); 129 | } 130 | } 131 | ] 132 | }, 133 | { 134 | label: current_language["tools"], 135 | submenu: [ 136 | { 137 | label: current_language["payload_sequencer"], 138 | click: function () { 139 | createWindow('sequencer_window/index.html', 1); 140 | } 141 | }, 142 | { 143 | label: current_language["grapher"], 144 | click: function () { 145 | createWindow('graph_window/index.html', 2); 146 | } 147 | }, 148 | { 149 | label: current_language["exception_decoder"], 150 | click: function () { 151 | createWindow('exception_decoder_window/index.html', 3); 152 | } 153 | } 154 | ] 155 | }, 156 | { 157 | label: current_language["toggle_dev_tools"], 158 | role: 'toggleDevTools' 159 | } 160 | ]; 161 | const menu = Menu.buildFromTemplate(template); 162 | Menu.setApplicationMenu(menu); 163 | } 164 | 165 | // This method will be called when Electron has finished 166 | // initialization and is ready to create browser windows. 167 | // Some APIs can only be used after this event occurs. 168 | app.on('ready', function () { 169 | createWindow('main_window/index.html', 0); 170 | makeMenuTemplate(); 171 | 172 | ipcMain.on('recvMain', (event, arg) => { 173 | try { 174 | windows[arg.id].webContents.send('recvChannel', arg); // sends the stuff from Window1 to Window2. 175 | } catch (e) { 176 | 177 | } 178 | }); 179 | 180 | ipcMain.on('openAlert', (event, incomingMessage) => { 181 | if (incomingMessage.title == undefined) 182 | return; 183 | if (incomingMessage.content == undefined) 184 | return; 185 | dialog.showErrorBox(incomingMessage.title, incomingMessage.content); 186 | }); 187 | 188 | ipcMain.on('createWindow', (event, arg) => { 189 | createWindow(arg.url, arg.index); 190 | }); 191 | 192 | ipcMain.on('setLang', (event, arg) => { 193 | current_language = arg; 194 | makeMenuTemplate(); 195 | }); 196 | }); 197 | 198 | // Quit when all windows are closed. 199 | app.on('window-all-closed', function () { 200 | // On OS X it is common for applications and their menu bar 201 | // to stay active until the user quits explicitly with Cmd + Q 202 | app.quit(); 203 | globalShortcut.unregister('CmdorCtrl+F') 204 | }); 205 | 206 | app.on('activate', function () { 207 | // On OS X it's common to re-create a window in the app when the 208 | // dock icon is clicked and there are no other windows open. 209 | if (mainWindow === null) { 210 | createWindow(); 211 | } 212 | }); 213 | 214 | 215 | -------------------------------------------------------------------------------- /src/main_window/decoder.js: -------------------------------------------------------------------------------- 1 | const { exec, execSync } = require("child_process"); 2 | const os = require('os'); 3 | var decoder_arch = document.getElementById("decoder_arch"); 4 | var decoder_color = document.getElementById("decoder_color"); 5 | var elf_path = document.getElementById("elf_path"); 6 | var elf_path_input = document.getElementById("elf_path_input"); 7 | var elf_error_warning = false; 8 | var elf_file_auto_path = ""; 9 | var general_core = ""; 10 | 11 | //"C:\Users\benja\AppData\Local\Arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\gcc8_4_0-esp-2021r2-patch3\bin\xtensa-esp32-elf-addr2line.exe" 12 | var addr2line_path = ""; 13 | var memory_address = null; 14 | //xtensa-esp32-elf-addr2line -pfiaC -e build/PROJECT.elf ADDRESS 15 | 16 | function getELF() { 17 | if (typeof (elf_path.files[0]) !== 'undefined') 18 | elf_path_input.value = elf_path.files[0].path.trim(); 19 | elf_path_input.scrollLeft = elf_path_input.scrollWidth; 20 | } 21 | 22 | elf_path_input.addEventListener('blur', () => { 23 | elf_path_input.scrollLeft = elf_path_input.scrollWidth; 24 | }); 25 | 26 | function decodeBacktrace(backtraceDecoder_input, backtraceDecoder_input_line, timestamp) { 27 | if (elf_path_input.value == "") { 28 | if (!elf_error_warning) { 29 | elf_error_warning = true; 30 | ipcRenderer.send("openAlert", current_language["backtrace_error"]); 31 | } 32 | return "No ELF file given"; 33 | } 34 | getESPaddr2line(); 35 | var backtraceResult = document.createElement("a"); 36 | backtraceResult.setAttribute("id", "p" + backtraceDecoder_input_line); 37 | var index = backtraceDecoder_input.indexOf(" "); 38 | var lastIndex = 0; 39 | var m_length = backtraceDecoder_input.length; 40 | while (index > -1) { 41 | memory_address = backtraceDecoder_input.substring(lastIndex, index); 42 | lastIndex = index + 1; 43 | index = backtraceDecoder_input.indexOf(" ", lastIndex); 44 | if (!memory_address.startsWith("Backtrace")) { 45 | var command = addr2line_path + " -pfiaC -e " + elf_path_input.value.trim() + " " + memory_address; 46 | 47 | try { 48 | var stdout = execSync(command).toString(); 49 | backtraceResult.innerHTML += syntaxHighlightDecoder(stdout) + "
"; 50 | } 51 | catch (stderr) { 52 | } 53 | } 54 | } 55 | memory_address = backtraceDecoder_input.substring(lastIndex, m_length - 1); 56 | if (!memory_address.startsWith("Backtrace")) { 57 | var command = addr2line_path + " -pfiaC -e " + elf_path_input.value + " " + memory_address; 58 | try { 59 | var stdout = execSync(command).toString(); 60 | backtraceResult.innerHTML += syntaxHighlightDecoder(stdout) + "
"; 61 | } 62 | catch (stderr) { 63 | backtraceResult.innerHTML += stderr.message + "
"; 64 | } 65 | addParserResult(backtraceResult, backtraceDecoder_input, preferences.decoderColor, "expDecoder", timestamp); 66 | return; 67 | } 68 | } 69 | 70 | 71 | function getESPaddr2line() { 72 | if (decoder_arch.value == "esp32c3") 73 | arch_elf_gcc = `riscv32-esp-elf`; 74 | else 75 | arch_elf_gcc = `xtensa-${decoder_arch.value}-elf`; 76 | addr2line_path = `decoders\\${arch_elf_gcc}-addr2line.exe`; 77 | } 78 | 79 | function getSketchBuild() { 80 | var tempFolder = os.tmpdir(); 81 | var mostRecentBuild = ""; 82 | var mostRecentTimestamp = 0; 83 | var files = fs.readdirSync(tempFolder); 84 | files.forEach(file => { 85 | if (!file.startsWith("arduino")) 86 | return; 87 | var stats = fs.lstatSync(tempFolder + '\\' + file); 88 | if (stats.isDirectory()) { 89 | if (mostRecentBuild != "arduino" && stats.mtimeMs > mostRecentTimestamp) { 90 | mostRecentTimestamp = stats.mtimeMs; 91 | mostRecentBuild = file; 92 | } 93 | } 94 | }); 95 | if (mostRecentBuild == "") 96 | return; 97 | 98 | if (mostRecentBuild == "arduino") { 99 | var currentPath = tempFolder + '\\' + mostRecentBuild + '\\sketches'; 100 | var sketchesFolder = fs.readdirSync(currentPath); 101 | sketchesFolder.forEach(file => { 102 | var stats = fs.lstatSync(currentPath + '\\' + file); 103 | if (stats.isDirectory()) { 104 | if (stats.mtimeMs > mostRecentTimestamp) { 105 | mostRecentTimestamp = stats.mtimeMs; 106 | mostRecentBuild = currentPath + '\\' + file; 107 | } 108 | } 109 | }); 110 | } 111 | 112 | var filesSketch = fs.readdirSync(mostRecentBuild); 113 | var fqbn = ""; 114 | filesSketch.forEach(file => { 115 | if (file == "build.options.json") { 116 | var buildOptionsFile = mostRecentBuild + '\\' + "build.options.json"; 117 | try { 118 | var buildOptions = JSON.parse(fs.readFileSync(buildOptionsFile)); 119 | fqbn = buildOptions.fqbn.split(":"); 120 | if (fqbn[0] == "esp32") { 121 | if (fqbn[2].startsWith("esp")) 122 | decoder_arch.value = fqbn[2]; 123 | else 124 | decoder_arch.value = fqbn[0]; 125 | general_core = fqbn[0]; 126 | } 127 | else if (fqbn[0] == "esp8266") { 128 | decoder_arch.value = fqbn[0]; 129 | general_core = fqbn[0]; 130 | } 131 | else 132 | ipcRenderer.send("openAlert", current_language["esp_support_error"]); 133 | } 134 | catch (err) { 135 | console.log(err); 136 | } 137 | } 138 | 139 | if (typeof (fqbn[0]) === 'undefined') 140 | return; 141 | if (fqbn[0].startsWith("esp") && file.endsWith("ino.elf")) { 142 | elf_file_auto_path = mostRecentBuild + '\\' + file; 143 | } 144 | }); 145 | if (elf_file_auto_path == "") 146 | ipcRenderer.send("openAlert", current_language["elf_not_found_error"]); 147 | elf_path_input.value = elf_file_auto_path; 148 | } 149 | 150 | function syntaxHighlightDecoder(decoded) { 151 | decoded = decoded.replace(/&/g, '&').replace(//g, '>'); 152 | return decoded.replace(/0x[a-f0-9]{8}:|\b:[0-9]{1,}\b|[a-zA-Z0-9_*\(\)]{1,} at\b|\b\/[a-zA-Z0-9_]{1,}\.[a-z]{1,}/g, function (match) { 153 | var cls = 'key'; 154 | if (/ at$/.test(match)) 155 | cls = 'string'; 156 | else if (/\.[a-z]{1,}/.test(match)) 157 | cls = 'boolean'; 158 | else if (/null/.test(match)) 159 | cls = 'null'; 160 | return `` + (cls == 'string' ? `${match.substring(0, match.indexOf(" at"))} at` : `${match}`); 161 | }); 162 | } 163 | -------------------------------------------------------------------------------- /src/main_window/graph_parser.js: -------------------------------------------------------------------------------- 1 | var graphsArray = []; 2 | var graphWindow = false; 3 | 4 | function setGraphsArray(newGraphsArray) { 5 | if (typeof newGraphsArray != "undefined") 6 | graphsArray = newGraphsArray; 7 | } 8 | 9 | function checkGraphTriggers(timestamp, target_line) { 10 | if (!graphWindow) 11 | return; 12 | var graphTriggerSize = graphsArray.length; 13 | for (var i = 0; i < graphTriggerSize; i++) { 14 | if (target_line.startsWith(graphsArray[i].trigger) == false) 15 | continue; 16 | try { 17 | ipcRenderer.send('recvMain', { id: 2, cmd: "newGraphData", time: timestamp.toString().slice(0,-1), value: target_line.replace(graphsArray[i].trigger, ""), position: i }); 18 | } catch (e) { 19 | graphWindow = false; 20 | } 21 | 22 | } 23 | } -------------------------------------------------------------------------------- /src/main_window/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 21 | 22 |
23 |
24 |
25 |
26 |
27 | 28 | 126 | 127 | 130 |
131 | 135 |
136 | 137 |
138 | 139 | 143 |
144 | 145 |
146 | 147 | 151 |
152 | 153 |
154 | 155 | 157 |
158 | 159 |
160 | 161 | 181 |
182 | 183 | 252 |
253 | 254 | 258 | 262 | 266 |
267 | 268 | 269 | 271 | 272 |
273 | 274 | 281 |
282 | 283 | 284 |
285 | 286 | 287 | 288 |
289 |
290 | 291 |
292 | 293 | 318 |
319 | 320 |
321 |
322 |
323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | -------------------------------------------------------------------------------- /src/main_window/jlink_handler.js: -------------------------------------------------------------------------------- 1 | var jlink_path = "start /D \"%ProgramFiles%\\SEGGER\\JLink\""; 2 | console.log(execSync(jlink_path + " Jlink.exe").toString()); -------------------------------------------------------------------------------- /src/main_window/parsers.js: -------------------------------------------------------------------------------- 1 | var current_parser_index = -1; 2 | var results = []; 3 | var line_parsed = 0; 4 | var custom_parsers_count = 0; 5 | var custom_parsers = []; 6 | var parsed_ids = []; 7 | var deleted_custom_parsers = []; 8 | var disconnect_on_boot = document.getElementById("disconnect_on_boot"); 9 | var custom_parsers_div = document.getElementById("custom_parsers_div"); 10 | var json_color = document.getElementById("json_color"); 11 | 12 | var json_filter = document.getElementById("json_filter"); 13 | var decoder_filter = document.getElementById("decoder_filter"); 14 | var filters_box = document.getElementById("filters_box"); 15 | var filters_show = document.getElementById("filters_show"); 16 | 17 | function runParsers() { 18 | for (; line_parsed < current_line_index;) { 19 | var target_line_element = document.getElementById('l' + line_parsed); 20 | if (typeof (target_line_element) === 'undefined') 21 | break; 22 | if (target_line_element == null) 23 | break; 24 | var target_timestamp_element = document.getElementById('t' + line_parsed); 25 | if (typeof (target_timestamp_element) === 'undefined') 26 | break; 27 | var timestamp = target_timestamp_element.innerHTML.split('-')[0] + ':'; 28 | var target_line = target_line_element.innerHTML; 29 | if (disconnect_on_boot.checked && target_line.startsWith("waiting for download")) 30 | disconnect(); 31 | 32 | for (var i = 0; i < custom_parsers.length; i++) { 33 | if (typeof (custom_parsers[i].trigger) !== 'undefined') { 34 | if (target_line.indexOf(custom_parsers[i].trigger) > -1) { 35 | try { 36 | var data_line = target_line.trim(); 37 | var func = `${custom_parsers[i].func}('${data_line}','${target_line_element.id}')`; 38 | addParserResult(eval(func), data_line, custom_parsers[i].color, custom_parsers[i].name, timestamp); 39 | } 40 | catch (e) { 41 | console.log('error:', e); 42 | } 43 | } 44 | } 45 | } 46 | 47 | if (target_line.indexOf("Backtrace") > -1) { 48 | decodeBacktrace(target_line, target_line_element.id, timestamp); 49 | } 50 | 51 | if (target_line.startsWith("{")) { 52 | try { 53 | var parsedJSON = JSON.parse(target_line); 54 | var jsonResult = document.createElement("pre"); 55 | jsonResult.setAttribute("id", "pl" + line_parsed); 56 | jsonResult.innerHTML = syntaxHighlightJSON(JSON.stringify(parsedJSON, null, 2)); 57 | addParserResult(jsonResult, target_line, preferences.jsonColor, "json", timestamp); 58 | } 59 | catch (e) { 60 | console.log('error parsing json:', e); 61 | } 62 | } 63 | 64 | checkGraphTriggers(timestamp, target_line); 65 | 66 | line_parsed++; 67 | } 68 | //line_parsed = current_line_index; 69 | } 70 | 71 | 72 | function syntaxHighlightJSON(json) { 73 | json = json.replace(/&/g, '&').replace(//g, '>'); 74 | return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) { 75 | var cls = 'number'; 76 | if (/^"/.test(match)) { 77 | if (/:$/.test(match)) { 78 | cls = 'key'; 79 | } else { 80 | cls = 'string'; 81 | } 82 | } else if (/true|false/.test(match)) { 83 | cls = 'boolean'; 84 | } else if (/null/.test(match)) { 85 | cls = 'null'; 86 | } 87 | return '' + match + ''; 88 | }); 89 | } 90 | 91 | function getParserScript(id) { 92 | var newParserScript = document.getElementById("cpScript" + id); 93 | var newParserScript_input = document.getElementById("cpScriptInput" + id); 94 | if (typeof (newParserScript.files[0]) !== 'undefined') 95 | newParserScript_input.value = newParserScript.files[0].path; 96 | } 97 | 98 | function clickBrowse(id) { 99 | document.getElementById("cpScript" + id).click(); 100 | } 101 | 102 | function updateParsers() { 103 | custom_parsers_count = 0; 104 | custom_parsers_div.innerHTML = ""; 105 | var filterDropdown = document.getElementById("filter_dropdown"); 106 | filterDropdown.innerHTML = ""; 107 | for (var i = 0; i < custom_parsers.length; i++) { 108 | createCustomParserField(custom_parsers[i].name, custom_parsers[i].script, 109 | custom_parsers[i].func, custom_parsers[i].trigger, custom_parsers[i].color); 110 | if (fs.existsSync(custom_parsers[i].script)) { 111 | var customScript = document.createElement("script"); 112 | customScript.setAttribute("src", custom_parsers[i].script); 113 | customScript.setAttribute("id", custom_parsers[i].name); 114 | document.body.appendChild(customScript); 115 | 116 | var line = document.createElement("div"); 117 | line.setAttribute("class", "line"); 118 | 119 | var customFilterCheck = document.createElement("label"); 120 | customFilterCheck.setAttribute("class", "checkbox_options"); 121 | var customFilter = document.createElement("input"); 122 | customFilter.setAttribute("type", "checkbox"); 123 | customFilter.setAttribute("onchange", "updateParserHistory(this)"); 124 | customFilter.setAttribute("class", "filters"); 125 | customFilter.setAttribute("id", custom_parsers[i].name + "_filter"); 126 | customFilter.setAttribute("data-filter", custom_parsers[i].name); 127 | customFilter.checked = true; 128 | var eye = document.createElement("div"); 129 | eye.setAttribute("class", "eye"); 130 | customFilterCheck.appendChild(customFilter); 131 | customFilterCheck.appendChild(eye); 132 | 133 | var customFilterLabel = document.createElement("label"); 134 | customFilterLabel.setAttribute("for", custom_parsers[i].name + "_filter"); 135 | customFilterLabel.innerHTML = custom_parsers[i].name; 136 | 137 | line.appendChild(customFilterCheck); 138 | line.appendChild(customFilterLabel); 139 | filterDropdown.appendChild(line); 140 | } 141 | } 142 | getESPaddr2line(); 143 | } 144 | 145 | function saveCustomParsers() { 146 | custom_parsers = []; 147 | for (var i = 0; i < custom_parsers_count; i++) { 148 | if (!deleted_custom_parsers.includes(i)) { 149 | var newParserScript = document.getElementById("cpScriptInput" + i); 150 | var newParserName = document.getElementById("cpName" + i); 151 | var newParserFunc = document.getElementById("cpFunc" + i) 152 | var newParserTrigger = document.getElementById("cpTrig" + i); 153 | var newParserColor = document.getElementById("cpColor" + i); 154 | custom_parsers.push({ 155 | script: newParserScript.value.trim(), 156 | name: newParserName.value.trim(), 157 | func: newParserFunc.value.trim(), 158 | trigger: newParserTrigger.value.trim(), 159 | color: newParserColor.value 160 | }); 161 | } 162 | } 163 | updateParsers(); 164 | } 165 | 166 | function addParserResult(newResult, newResultSource, color, parserName, timestamp) { 167 | current_parser_index++; 168 | var newOutputEntry = document.createElement("button"); 169 | newOutputEntry.setAttribute("id", "b" + newResult.id); 170 | newOutputEntry.setAttribute("value", current_parser_index); 171 | newOutputEntry.setAttribute("data-parser", parserName); 172 | newOutputEntry.setAttribute("class", "output_entry output_entry_color"); 173 | 174 | var target_filter = document.querySelector(`[data-filter="${parserName}"]`); 175 | var display_type = 'block'; 176 | if (target_filter != null) 177 | display_type = target_filter.checked ? 'block' : 'none'; 178 | 179 | newOutputEntry.setAttribute("style", `display:${display_type};border-color:${color}`); 180 | newOutputEntry.setAttribute("onclick", "showOutputResult(this.value)"); 181 | 182 | var newOutputTimestamp = document.createElement("span"); 183 | newOutputTimestamp.setAttribute("class", "parser_timestamp"); 184 | newOutputTimestamp.innerHTML = timestamp; 185 | newOutputEntry.appendChild(newOutputTimestamp); 186 | newOutputEntry.innerHTML += newResultSource; 187 | 188 | results.push(newResult); 189 | parsed_ids.push(newResult.id); 190 | output_history.appendChild(newOutputEntry); 191 | 192 | if (auto_scroll.checked == true) { 193 | output_history.scrollTop = output_history.scrollHeight; 194 | if (target_filter == undefined) return; 195 | if (target_filter.checked) 196 | showOutputResult(current_parser_index); 197 | } 198 | } 199 | 200 | function showOutputResult(id) { 201 | output.innerHTML = ""; 202 | output.appendChild(results[id]); 203 | output.scrollTop = 0; 204 | } 205 | 206 | function deleteCustomParserField(id) { 207 | targetParserField = document.getElementById(`cpDiv${id}`); 208 | custom_parsers_div.removeChild(targetParserField); 209 | deleted_custom_parsers.push(id); 210 | } 211 | 212 | function createCustomParserField(name, script, func, trigger, color) { 213 | var newParserField = document.createElement("div"); 214 | newParserField.setAttribute("id", "cpDiv" + custom_parsers_count); 215 | newParserField.setAttribute("class", "custom_parser_entry"); 216 | 217 | var first_half = document.createElement("div"); 218 | first_half.setAttribute("class", "half"); 219 | var second_half = document.createElement("div"); 220 | second_half.setAttribute("class", "half"); 221 | 222 | var newParserName = document.createElement("input"); 223 | newParserName.setAttribute("type", "text"); 224 | newParserName.setAttribute("placeholder", current_language["parser_name_placeholder"]); 225 | newParserName.setAttribute("id", "cpName" + custom_parsers_count); 226 | if (typeof (name) !== 'undefined') 227 | newParserName.setAttribute("value", name); 228 | 229 | var newParserFunc = document.createElement("input"); 230 | newParserFunc.setAttribute("type", "text"); 231 | newParserFunc.setAttribute("placeholder", current_language["parser_function_placeholder"]); 232 | newParserFunc.setAttribute("id", "cpFunc" + custom_parsers_count); 233 | if (typeof (func) !== 'undefined') 234 | newParserFunc.setAttribute("value", func); 235 | 236 | var color_div = document.createElement("div"); 237 | color_div.setAttribute("class", "color_div"); 238 | var newParserColor = document.createElement("input"); 239 | newParserColor.setAttribute("type", "color"); 240 | newParserColor.setAttribute("placeholder", current_language["parser_color_placeholder"]); 241 | newParserColor.setAttribute("id", "cpColor" + custom_parsers_count); 242 | if (typeof (color) !== 'undefined') 243 | newParserColor.setAttribute("value", color); 244 | color_div.appendChild(newParserColor); 245 | 246 | var newParserScriptInput = document.createElement("input"); 247 | newParserScriptInput.setAttribute("type", "text"); 248 | newParserScriptInput.setAttribute("placeholder", current_language["parser_script_placeholder"]); 249 | newParserScriptInput.setAttribute("id", "cpScriptInput" + custom_parsers_count); 250 | if (typeof (script) !== 'undefined') 251 | newParserScriptInput.setAttribute("value", script); 252 | 253 | var newParserScript = document.createElement("input"); 254 | newParserScript.setAttribute("type", "file"); 255 | newParserScript.setAttribute("id", "cpScript" + custom_parsers_count); 256 | newParserScript.setAttribute("style", "display:none"); 257 | newParserScript.setAttribute("accept", ".js"); 258 | newParserScript.setAttribute("onchange", `getParserScript(${custom_parsers_count})`); 259 | 260 | var newParserScriptBrowse = document.createElement("button"); 261 | newParserScriptBrowse.setAttribute("onclick", `clickBrowse(${custom_parsers_count})`); 262 | newParserScriptBrowse.setAttribute("class", "general_btn"); 263 | newParserScriptBrowse.innerText = current_language["browse"]; 264 | 265 | var newParserTrigger = document.createElement("input"); 266 | newParserTrigger.setAttribute("type", "text"); 267 | newParserTrigger.setAttribute("placeholder", current_language["parser_trigger_placeholder"]); 268 | newParserTrigger.setAttribute("id", "cpTrig" + custom_parsers_count); 269 | if (typeof (trigger) !== 'undefined') 270 | newParserTrigger.setAttribute("value", trigger); 271 | 272 | var newParserExclude = document.createElement("img"); 273 | newParserExclude.setAttribute("width", "16"); 274 | newParserExclude.setAttribute("height", "16"); 275 | newParserExclude.setAttribute("src", "../images/trash-2-16.png"); 276 | newParserExclude.setAttribute("onclick", `deleteCustomParserField(${custom_parsers_count})`); 277 | 278 | var line = document.createElement("div"); 279 | line.setAttribute("class", "line"); 280 | var label = document.createElement("label"); 281 | label.setAttribute("data-i18n", "name"); 282 | label.innerText = current_language["name"]; 283 | line.appendChild(label); 284 | line.appendChild(newParserName); 285 | first_half.appendChild(line); 286 | 287 | line = document.createElement("div"); 288 | line.setAttribute("class", "line"); 289 | label = document.createElement("label"); 290 | label.setAttribute("data-i18n", "script"); 291 | label.innerText = current_language["script"]; 292 | line.appendChild(label); 293 | line.appendChild(newParserScriptInput); 294 | line.appendChild(newParserScript); 295 | line.appendChild(newParserScriptBrowse); 296 | first_half.appendChild(line); 297 | 298 | line = document.createElement("div"); 299 | line.setAttribute("class", "line"); 300 | label = document.createElement("label"); 301 | label.setAttribute("data-i18n", "function"); 302 | label.innerText = current_language["function"]; 303 | line.appendChild(label); 304 | line.appendChild(newParserFunc); 305 | second_half.appendChild(line); 306 | 307 | line = document.createElement("div"); 308 | line.setAttribute("class", "line"); 309 | label = document.createElement("label"); 310 | label.setAttribute("data-i18n", "trigger"); 311 | label.innerText = current_language["trigger"]; 312 | line.appendChild(label); 313 | line.appendChild(newParserTrigger); 314 | second_half.appendChild(line); 315 | 316 | newParserField.appendChild(color_div); 317 | newParserField.appendChild(first_half); 318 | newParserField.appendChild(second_half); 319 | newParserField.appendChild(newParserExclude); 320 | custom_parsers_div.appendChild(newParserField); 321 | custom_parsers_count++; 322 | } 323 | 324 | function updateParserHistory(target_filter) { 325 | parsed_ids.forEach((currentValue) => { 326 | var current_parser_line = document.getElementById("b" + currentValue); 327 | if (current_parser_line.dataset.parser === target_filter.dataset.filter) 328 | current_parser_line.style.display = target_filter.checked ? 'block' : 'none'; 329 | }); 330 | } 331 | 332 | 333 | filters_show.addEventListener("click", (e) => { 334 | e.stopPropagation(); 335 | toggleHide(filters_box); 336 | }); -------------------------------------------------------------------------------- /src/main_window/preferences_handle.js: -------------------------------------------------------------------------------- 1 | //log file config elements and variables 2 | var config_menu = document.getElementById("config_menu"); 3 | config_menu.addEventListener("click", (e) => { 4 | e.stopPropagation(); 5 | }); 6 | var log_addTimestamp = document.getElementById("log_add_timestamp"); 7 | var log_type = document.getElementById("log_type"); 8 | var log_folder = document.getElementById("log_folder"); 9 | var log_folder_input = document.getElementById("log_folder_input"); 10 | var json_color = document.getElementById("json_color"); 11 | var theme_style = document.getElementById("theme_style"); 12 | var theme_select = document.getElementById("theme_select"); 13 | var advanced_config = document.getElementById("advanced_config"); 14 | var advanced_config_div = document.getElementById("advanced_config_div"); 15 | var parity_select = document.getElementById("parity"); 16 | var dataBits_select = document.getElementById("dataBits"); 17 | var stopBits_select = document.getElementById("stopBits"); 18 | var rtscts_enable = document.getElementById("rtscts"); 19 | var xon_enable = document.getElementById("xon"); 20 | var xoff_enable = document.getElementById("xoff"); 21 | var xany_enable = document.getElementById("xany"); 22 | var hupcl_enable = document.getElementById("hupcl"); 23 | 24 | const preferences_file_path = "./preferences.json"; 25 | 26 | let log_file_writer = null; 27 | var preferences = null; 28 | var prev_preferences = null; 29 | 30 | var defaultPreferences = { 31 | lang: "en.json", 32 | decoderColor: "#0000ff", 33 | jsonColor: "#00ff00", 34 | disconnectOnBoot: false, 35 | showConChanges: false, 36 | logType: "none", 37 | logAddTimestamp: false, 38 | autoScroll: true, 39 | lineEnding: "", 40 | addTimestamp: true, 41 | comPort: "", 42 | baudrate: 115200, 43 | ctrlEnter: false, 44 | advancedConfig: { 45 | enabled: false, 46 | stopBits: 1, 47 | dataBits: 8, 48 | parity: "none", 49 | rtscts: false, 50 | xon: false, 51 | xoff: false, 52 | xany: false, 53 | hupcl: false 54 | }, 55 | theme: "dark", 56 | elfPath: "", 57 | customParsers: "" 58 | }; 59 | 60 | fs.readFile(preferences_file_path, 'utf8', (err, data) => { 61 | if (err) { 62 | setPreferences(defaultPreferences); 63 | return; 64 | } 65 | preferences = JSON.parse(data); 66 | if (preferences != null) { 67 | setPreferences(preferences); 68 | } 69 | prev_preferences = { ...preferences }; 70 | }); 71 | 72 | 73 | function setPreferences(target_preferences) { 74 | updateLanguageList(); 75 | if (typeof (target_preferences.lang) !== 'undefined') 76 | language_config.value = target_preferences.lang; 77 | readLanguage(); 78 | updateContentLang(); 79 | 80 | if (typeof (target_preferences.comPort) !== 'undefined') 81 | current_port = target_preferences.comPort; 82 | 83 | if (typeof (target_preferences.baudrate) !== 'undefined') 84 | baudrate_input.value = target_preferences.baudrate; 85 | 86 | if (typeof (target_preferences.autoScroll) !== 'undefined') 87 | auto_scroll.checked = target_preferences.autoScroll; 88 | 89 | if (typeof (target_preferences.addTimestamp) !== 'undefined') 90 | add_timestamp.checked = target_preferences.addTimestamp; 91 | 92 | if (typeof (target_preferences.logFolder) !== 'undefined') 93 | log_folder_input.value = target_preferences.logFolder; 94 | 95 | if (typeof (target_preferences.logType) !== 'undefined') 96 | log_type.value = target_preferences.logType; 97 | 98 | if (typeof (target_preferences.logAddTimestamp) !== 'undefined') 99 | log_add_timestamp.checked = target_preferences.logAddTimestamp; 100 | 101 | if (typeof (target_preferences.ctrlEnter) !== 'undefined') 102 | ctrl_enter.checked = target_preferences.ctrlEnter; 103 | 104 | if (typeof (target_preferences.lineEnding) !== 'undefined') 105 | line_ending.value = target_preferences.lineEnding; 106 | 107 | if (typeof (target_preferences.decoderArch) !== 'undefined') 108 | decoder_arch.value = target_preferences.decoderArch; 109 | 110 | if (typeof (target_preferences.decoderColor) !== 'undefined') 111 | decoder_color.value = target_preferences.decoderColor; 112 | 113 | if (typeof (target_preferences.jsonColor) !== 'undefined') 114 | json_color.value = target_preferences.jsonColor; 115 | 116 | if (typeof (target_preferences.disconnectOnBoot) !== 'undefined') 117 | disconnect_on_boot.checked = target_preferences.disconnectOnBoot; 118 | 119 | if (typeof (target_preferences.showConChanges) !== 'undefined') 120 | show_con_changes.checked = target_preferences.showConChanges; 121 | 122 | if (typeof (target_preferences.decoderArch) !== 'undefined') 123 | decoder_arch.value = target_preferences.decoderArch; 124 | 125 | if (typeof (target_preferences.elfPath) !== 'undefined') 126 | elf_path_input.value = target_preferences.elfPath; 127 | 128 | if (typeof (target_preferences.theme) !== 'undefined') { 129 | theme_select.value = target_preferences.theme; 130 | theme_style.href = "../style/" + theme_select.value + "_theme_style.css"; 131 | } 132 | 133 | if (typeof (target_preferences.customParsers) !== 'undefined') { 134 | custom_parsers = target_preferences.customParsers; 135 | updateParsers(); 136 | } 137 | 138 | if (typeof (target_preferences.advancedConfig) !== 'undefined') { 139 | advanced_config.checked = target_preferences.advancedConfig.enabled; 140 | stopBits_select.value = target_preferences.advancedConfig.stopBits; 141 | dataBits_select.value = target_preferences.advancedConfig.dataBits; 142 | parity_select.value = target_preferences.advancedConfig.parity; 143 | rtscts_enable.checked = target_preferences.advancedConfig.rtscts; 144 | xon_enable.checked = target_preferences.advancedConfig.xon; 145 | xoff_enable.checked = target_preferences.advancedConfig.xoff; 146 | xany_enable.checked = target_preferences.advancedConfig.xany; 147 | hupcl_enable.checked = target_preferences.advancedConfig.hupcl; 148 | changeAdvConfigDiv(); 149 | } 150 | } 151 | 152 | function readDirPaths(log, decoder) { 153 | if (log) { 154 | if (typeof (log_folder.files[0]) !== 'undefined') { 155 | var log_folder_path = log_folder.files[0].path.trim(); 156 | log_folder_input.value = log_folder_path.substring(0, log_folder_path.lastIndexOf('\\') + 1); 157 | } 158 | else 159 | ipcRenderer.send("openAlert", current_language["folder_empty_error"]); 160 | } 161 | } 162 | 163 | function changeAdvConfigDiv() { 164 | if (advanced_config.checked) { 165 | showItem(advanced_config_div); 166 | } 167 | else { 168 | hideItem(advanced_config_div); 169 | stopBits_select.value = "1"; 170 | dataBits_select.value = "8"; 171 | parity_select.value = "none"; 172 | rtscts_enable.checked = false; 173 | xon_enable.checked = false; 174 | xoff_enable.checked = false; 175 | xany_enable.checked = false; 176 | hupcl_enable.checked = false; 177 | } 178 | } 179 | 180 | function updatePreferences() { 181 | saveCustomParsers(); 182 | var advancedConfig_json = { 183 | enabled: advanced_config.checked, 184 | stopBits: stopBits_select.value, 185 | dataBits: dataBits_select.value, 186 | parity: parity_select.value, 187 | rtscts: rtscts_enable.checked, 188 | xon: xon_enable.checked, 189 | xoff: xoff_enable.checked, 190 | xany: xany_enable.checked, 191 | hupcl: hupcl_enable.checked 192 | }; 193 | preferences = { 194 | lang: language_config.value.trim(), 195 | logFolder: log_folder_input.value.trim(), 196 | decoderColor: decoder_color.value, 197 | jsonColor: json_color.value, 198 | disconnectOnBoot: disconnect_on_boot.checked, 199 | showConChanges: show_con_changes.checked, 200 | logType: log_type.value, 201 | logAddTimestamp: log_addTimestamp.checked, 202 | autoScroll: auto_scroll.checked, 203 | lineEnding: line_ending.value, 204 | addTimestamp: add_timestamp.checked, 205 | comPort: current_port, 206 | baudrate: baudrate_input.value, 207 | ctrlEnter: ctrl_enter.checked, 208 | advancedConfig: advancedConfig_json, 209 | theme: theme_select.value, 210 | decoderArch: decoder_arch.value.trim(), 211 | elfPath: elf_path_input.value.trim(), 212 | customParsers: custom_parsers 213 | }; 214 | if (preferences.autoScroll == true) { 215 | terminal.scrollTop = terminal.scrollHeight; 216 | output_history.scrollTop = output_history.scrollHeight; 217 | } 218 | try { 219 | fs.unlinkSync(preferences_file_path); 220 | } 221 | catch (err) { 222 | // needs to be a separate try catch because it will fail on the first time configuring 223 | console.log(err); 224 | } 225 | try { 226 | fs.writeFileSync(preferences_file_path, JSON.stringify(preferences)); 227 | } 228 | catch (err) { 229 | console.log(err); 230 | ipcRenderer.send("openAlert", current_language["writing_error"]); 231 | } 232 | prev_preferences = { ...preferences }; 233 | } 234 | 235 | function updateTheme() { 236 | theme_style.href = '../style/' + theme_select.value + '_theme_style.css'; 237 | ipcRenderer.send('recvMain', { id: 1, cmd: "setTheme", theme: theme_style.href }); 238 | ipcRenderer.send('recvMain', { id: 2, cmd: "setTheme", theme: theme_style.href }); 239 | ipcRenderer.send('recvMain', { id: 3, cmd: "setTheme", theme: theme_style.href }); 240 | } 241 | 242 | function cancelConfig() { 243 | if (config_menu.classList.contains("hidden")) 244 | return; 245 | setPreferences({ ...prev_preferences }); 246 | hideItem(config_menu); 247 | } 248 | 249 | document.getElementById("open_config_menu").onclick = function (e) { 250 | e.stopPropagation(); 251 | updateLanguageList(); 252 | prev_preferences = { ...preferences }; 253 | toggleHide(config_menu); 254 | }; -------------------------------------------------------------------------------- /src/main_window/renderer.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { ipcRenderer } = require('electron') 3 | const { SerialPort } = require("serialport"); 4 | const remote = require('@electron/remote'); 5 | const { FindInPage } = require('electron-find'); 6 | 7 | document.body.addEventListener("click", (e) => { 8 | e.stopPropagation(); 9 | cancelConfig(); 10 | if (filters_box.contains(e.target)) 11 | return; 12 | hideItem(filters_box); 13 | }); 14 | 15 | const content = document.getElementById("content"); 16 | const terminal = document.getElementById("terminal"); 17 | const output = document.getElementById("output"); 18 | 19 | //options menu elements 20 | const auto_scroll = document.getElementById("auto_scroll"); 21 | const com_ports = document.getElementById("com_ports"); 22 | var current_port = ""; 23 | const com_ports_input = document.getElementById("com_ports_input"); 24 | const baudrate_input = document.getElementById("baudrate_input"); 25 | const add_timestamp = document.getElementById("add_timestamp"); 26 | const sidebar_resizer = document.getElementById("sidebar_resizer"); 27 | const show_con_changes = document.getElementById("show_con_changes"); 28 | 29 | //send data elements and variables 30 | const send_input = document.getElementById("send_input"); 31 | const send_button = document.getElementById("send_button"); 32 | const line_ending = document.getElementById("line_ending"); 33 | var ctrl_enter = document.getElementById("ctrl_enter"); 34 | var pos = 0; 35 | var input_history = []; 36 | var prev_send_input = ""; 37 | 38 | var serialport = null; 39 | var lineStart; 40 | var first_line = true; 41 | var new_line = true; 42 | var current_line_index = 0; 43 | var current_line = null; 44 | var prev_line = null; 45 | var start_line_index = 0; 46 | var sequence; 47 | var sequenceTimeout; 48 | var sequence_pos = 0; 49 | const find_container = document.getElementById("find_container"); 50 | var find_box = null; 51 | var port_count = 0; 52 | 53 | // config UI of find interface 54 | let findInPage = new FindInPage(remote.getCurrentWebContents(), { 55 | parentElement: find_container 56 | }); 57 | 58 | setInterval(() => { 59 | getPorts(); 60 | if (find_box === null) 61 | return; 62 | if (findInPage[find_box].style.visibility == "hidden") 63 | findInPage[find_box].style.display = "none"; 64 | }, 500); 65 | 66 | ipcRenderer.on('find_request', () => { 67 | if (find_box !== null) { 68 | findInPage[find_box].style.display = "flex"; 69 | find_box = null; 70 | } 71 | findInPage.openFindWindow(); 72 | find_box = Object.getOwnPropertySymbols(findInPage).find( 73 | (s) => s.description === "findBox" 74 | ); 75 | }); 76 | 77 | ipcRenderer.on('recvChannel', (_event, arg) => { 78 | switch (arg.cmd) { 79 | case "sendSequence": { 80 | clearTimeout(sequenceTimeout); 81 | if (serialport == null) { 82 | ipcRenderer.send("openAlert", current_language["serial_port_not_open_error"]); 83 | return; 84 | } 85 | sequence_pos = 0; 86 | sequence = JSON.parse(fs.readFileSync(arg.sequence)); 87 | sequenceTimeout = setTimeout(sendSequence, sequence.packets[sequence_pos].delay); 88 | break; 89 | } 90 | case "stopSequence": { 91 | clearTimeout(sequenceTimeout); 92 | break; 93 | } 94 | case "getTheme": { 95 | ipcRenderer.send('recvMain', { id: arg.requester, cmd: "setTheme", theme: theme_style.href }); 96 | break; 97 | } 98 | case "getLang": { 99 | ipcRenderer.send('recvMain', { id: arg.requester, cmd: "setLang", lang: current_language }); 100 | break; 101 | } 102 | case "graphOpened": { 103 | graphWindow = true; 104 | break; 105 | } 106 | case "graphClosed": { 107 | graphWindow = false; 108 | break; 109 | } 110 | case "setGraphsArray": { 111 | setGraphsArray(arg.graphsArray); 112 | break; 113 | } 114 | } 115 | }); 116 | 117 | function sendSequence() { 118 | var line_end = ""; 119 | if (line_ending.value == "\\n") 120 | line_end = "\n"; 121 | if (line_ending.value == "\\r") 122 | line_end = "\r"; 123 | if (line_ending.value == "\\r\\n") 124 | line_end = "\r\n"; 125 | var data = Buffer.from(sequence.packets[sequence_pos].data + line_end, "utf-8"); 126 | serialport.write(data, function (err) { 127 | if (err) { 128 | ipcRenderer.send("openAlert", current_language["writing_error"]); 129 | return; 130 | } 131 | }); 132 | 133 | if (sequence.count - 1 == sequence_pos) { 134 | if (sequence.continuous) 135 | sequence_pos = -1; 136 | else { 137 | clearTimeout(sequenceTimeout); 138 | return; 139 | } 140 | } 141 | sequence_pos++; 142 | sequenceTimeout = setTimeout(sendSequence, sequence.packets[sequence_pos].delay); 143 | } 144 | 145 | 146 | function createWindow(window_url, i) { 147 | ipcRenderer.send("createWindow", { url: window_url, index: i }); 148 | } 149 | 150 | function makeResizableDiv(div, vertical, horizontal, resizers) { 151 | const element = document.querySelector(div); 152 | const minimum_size = 20; 153 | let original_width = 0; 154 | let original_height = 0; 155 | let original_x = 0; 156 | let original_y = 0; 157 | let original_mouse_x = 0; 158 | let original_mouse_y = 0; 159 | for (let i = 0; i < resizers.length; i++) { 160 | const currentResizer = resizers[i]; 161 | currentResizer.addEventListener('mousedown', function (e) { 162 | e.preventDefault() 163 | original_width = parseFloat(getComputedStyle(element, null).getPropertyValue('width').replace('px', '')); 164 | original_height = parseFloat(getComputedStyle(element, null).getPropertyValue('height').replace('px', '')); 165 | original_x = element.getBoundingClientRect().left; 166 | original_y = element.getBoundingClientRect().top; 167 | original_mouse_x = e.pageX; 168 | original_mouse_y = e.pageY; 169 | window.addEventListener('mousemove', resize) 170 | window.addEventListener('mouseup', stopResize) 171 | }); 172 | 173 | function resize(e) { 174 | if (currentResizer.classList.contains('bottom-right')) { 175 | if (horizontal) { 176 | const width = original_width + (e.pageX - original_mouse_x); 177 | if (width > minimum_size) 178 | element.style.width = width + 'px'; 179 | } 180 | if (vertical) { 181 | const height = original_height + (e.pageY - original_mouse_y); 182 | if (height > minimum_size) 183 | element.style.height = height + 'px'; 184 | } 185 | } 186 | else if (currentResizer.classList.contains('bottom-left')) { 187 | if (horizontal) { 188 | const height = original_height + (e.pageY - original_mouse_y); 189 | if (height > minimum_size) 190 | element.style.height = height + 'px'; 191 | } 192 | if (vertical) { 193 | const width = original_width - (e.pageX - original_mouse_x); 194 | if (width > minimum_size) { 195 | element.style.width = width + 'px'; 196 | element.style.left = original_x + (e.pageX - original_mouse_x) + 'px'; 197 | } 198 | } 199 | } 200 | else if (currentResizer.classList.contains('top-right')) { 201 | if (horizontal) { 202 | const width = original_width + (e.pageX - original_mouse_x); 203 | if (width > minimum_size) 204 | element.style.width = width + 'px'; 205 | } 206 | if (vertical) { 207 | const height = original_height - (e.pageY - original_mouse_y) 208 | if (height > minimum_size) { 209 | element.style.height = height + 'px' 210 | element.style.top = original_y + (e.pageY - original_mouse_y) + 'px' 211 | } 212 | } 213 | } 214 | else { 215 | if (horizontal) { 216 | const width = original_width - (e.pageX - original_mouse_x); 217 | if (width > minimum_size) { 218 | element.style.width = width + 'px'; 219 | } 220 | } 221 | if (vertical) { 222 | const height = original_height - (e.pageY - original_mouse_y); 223 | if (height > minimum_size) { 224 | element.style.height = height + 'px'; 225 | element.style.top = original_y + (e.pageY - original_mouse_y) + 'px'; 226 | } 227 | } 228 | } 229 | content.style.width = window.innerWidth - parseInt(element.style.width, 10) + 'px'; 230 | } 231 | 232 | function stopResize() { 233 | window.removeEventListener('mousemove', resize) 234 | } 235 | } 236 | } 237 | 238 | function showItem(item) { 239 | if (item.classList.contains("hidden")) item.classList.remove("hidden"); 240 | } 241 | 242 | function hideItem(item) { 243 | if (!item.classList.contains("hidden")) item.classList.add("hidden"); 244 | } 245 | 246 | function toggleHide(item) { 247 | if (item.classList.contains("hidden")) item.classList.remove("hidden"); 248 | else item.classList.add("hidden"); 249 | } 250 | 251 | //options menu handlers start 252 | function changeTimestamp() { 253 | for (var i = start_line_index; i < current_line_index; i++) { 254 | if (document.getElementById('t' + i) != null) 255 | document.getElementById('t' + i).style.display = add_timestamp.checked ? "inline" : "none"; 256 | } 257 | } 258 | 259 | function getPorts() { 260 | SerialPort.list().then(function (ports) { 261 | if(ports.length !== port_count) { 262 | var returnList = ""; 263 | ports.forEach(function (port) { 264 | returnList += ""; 265 | }); 266 | com_ports.innerHTML = returnList; 267 | com_ports.value = current_port; 268 | port_count = ports.length; 269 | } 270 | 271 | }); 272 | } 273 | 274 | function connect() { 275 | if (com_ports.value == "") { 276 | ipcRenderer.send("openAlert", current_language["no_com_ports_error"]); 277 | return; 278 | } 279 | var data = { 280 | path: com_ports.value, 281 | baudRate: parseInt(baudrate_input.value), 282 | dataBits: parseInt(dataBits_select.value), 283 | stopBits: parseInt(stopBits_select.value), 284 | parity: parity_select.value, 285 | rtscts: rtscts_enable.checked, 286 | xon: xon_enable.checked, 287 | xoff: xoff_enable.checked, 288 | xany: xany_enable.checked, 289 | hupcl: hupcl_enable.checked 290 | } 291 | if (data.path == undefined && data.baudrate == undefined) { 292 | ipcRenderer.send("openAlert", current_language["undefined_value_error"]); 293 | return; 294 | } 295 | 296 | if (serialport != null && serialport.isOpen) { 297 | serialport.port.close().then((err) => { 298 | connectSerialPort(data); 299 | }); 300 | } 301 | else 302 | connectSerialPort(data); 303 | } 304 | 305 | 306 | function disconnect() { 307 | if (serialport != null && serialport.isOpen) { 308 | serialport.port.close().then((err) => { 309 | send_button.disabled = true; 310 | send_input.disabled = true; 311 | line_ending.disabled = true; 312 | if (show_con_changes.checked) 313 | recvData("\nDISCONNECTED\n"); 314 | if (log_file_writer != null) 315 | log_file_writer.close(); 316 | }); 317 | } 318 | } 319 | 320 | function cleanTerminal() { 321 | terminal.innerHTML = ""; 322 | output_history.innerHTML = ""; 323 | parsed_ids = []; 324 | new_line = true; 325 | line_parsed--; 326 | start_line_index = current_line_index--; 327 | } 328 | 329 | function connectSerialPort(data) { 330 | updatePreferences(); 331 | serialport = new SerialPort(data); 332 | serialport.on('error', function (err) { 333 | ipcRenderer.send("openAlert", { title: current_language["serialport_error"], content: err.message }); 334 | }); 335 | serialport.on("open", function (err) { 336 | if (err) { 337 | ipcRenderer.send("openAlert", { title: current_language["serialport_open_error"], content: err.message }); 338 | return; 339 | } 340 | send_button.disabled = false; 341 | send_input.disabled = false; 342 | line_ending.disabled = false; 343 | if (show_con_changes.checked) 344 | recvData("CONNECTED\n"); 345 | if (log_type.value == 'none') 346 | return; 347 | if (fs.existsSync(log_folder_input.value)) { 348 | log_file_writer = fs.createWriteStream(log_folder_input.value + "log.txt", { 349 | flags: log_type.value 350 | }); 351 | } 352 | else 353 | ipcRenderer.send("openAlert", current_language["log_folder_does_not_exist"]); 354 | 355 | }); 356 | serialport.on("close", function (err) { 357 | send_button.disabled = true; 358 | send_input.disabled = true; 359 | line_ending.disabled = true; 360 | if (err) { 361 | ipcRenderer.send("openAlert", { title: current_language["port_disconnected"], content: err.message }); 362 | if (show_con_changes.checked) 363 | recvData("\nDISCONNECTED\n"); 364 | if (log_file_writer != null) 365 | log_file_writer.close(); 366 | return; 367 | } 368 | }); 369 | serialport.on("readable", function () { 370 | recvData(serialport.read().toString()); 371 | }); 372 | 373 | } 374 | //options menu handlers end 375 | 376 | 377 | //data receive handlers start 378 | function recvData(payload) { 379 | var message = payload; 380 | payload = ""; 381 | let date = new Date(); 382 | var tzoffset = date.getTimezoneOffset() * 60000; //offset in milliseconds 383 | var dateISO = (new Date(date - tzoffset)).toISOString().slice(0, -1); 384 | var current_datetime = dateISO.match(/\d\d:\d\d:\d\d.\d\d\d/); 385 | 386 | var m_length = message.length; 387 | var index = 0; 388 | while (index < m_length) { 389 | var message_new_line_content = ""; 390 | var payload_new_line = ""; 391 | 392 | //add the timestamp if a new line was started 393 | if (new_line == true) { 394 | new_line = false; 395 | var timestamp = document.createElement("a"); 396 | timestamp.innerHTML = current_datetime + "->"; 397 | if (add_timestamp.checked == false) 398 | timestamp.setAttribute("style", "display:none"); 399 | timestamp.setAttribute("id", 't' + current_line_index); 400 | terminal.appendChild(timestamp); 401 | 402 | current_line = document.createElement("a"); 403 | current_line.setAttribute("id", 'l' + current_line_index); 404 | terminal.appendChild(current_line); 405 | 406 | if (log_add_timestamp.checked) 407 | payload_new_line = current_datetime + "->"; 408 | } 409 | 410 | for (; index < m_length; index++) { 411 | var c = message[index]; 412 | payload_new_line += c; 413 | if (c == '\n') { 414 | new_line = true; 415 | index++; 416 | break; 417 | } else if (c != '\r') { 418 | message_new_line_content += c; 419 | } 420 | } 421 | 422 | current_line.innerHTML += message_new_line_content; 423 | 424 | payload += payload_new_line; 425 | 426 | //adds the new line in html if a new line was detected 427 | if (new_line == true) { 428 | terminal.appendChild(document.createElement("br")); 429 | current_line_index++; 430 | } 431 | } 432 | runParsers(); 433 | 434 | if (log_file_writer != null) 435 | log_file_writer.write(payload); 436 | //terminal.innerHTML += message; 437 | if (auto_scroll.checked == true) 438 | terminal.scrollTop = terminal.scrollHeight; 439 | } 440 | //data receive handlers end 441 | 442 | //Data sending handles start 443 | send_input.addEventListener("keyup", (event) => { 444 | switch (event.code) { // 445 | case 'ArrowUp': 446 | if (pos == input_history.length) 447 | prev_send_input = send_input.value; 448 | if (pos > 0) 449 | pos--; 450 | if (input_history[pos] != undefined) 451 | send_input.value = input_history[pos]; 452 | break; 453 | case 'ArrowDown': 454 | if (pos <= input_history.length) 455 | pos++; 456 | if (input_history[pos] != undefined) 457 | send_input.value = input_history[pos]; 458 | else 459 | send_input.value = prev_send_input; 460 | break; 461 | case "Enter": 462 | if (event.ctrlKey == preferences.ctrlEnter) 463 | sendData(); 464 | break; 465 | } 466 | }); 467 | 468 | function sendData() { 469 | var line_end = ""; 470 | if (line_ending.value == "\\n") 471 | line_end = "\n"; 472 | if (line_ending.value == "\\r") 473 | line_end = "\r"; 474 | if (line_ending.value == "\\r\\n") 475 | line_end = "\r\n"; 476 | var data = Buffer.from(send_input.value + line_end, "utf-8"); 477 | serialport.write(data, function (err) { 478 | if (err) { 479 | ipcRenderer.send("openAlert", current_language["writing_error"]); 480 | return; 481 | } 482 | }); 483 | input_history.push(send_input.value); 484 | pos = input_history.length; 485 | send_input.value = ""; 486 | } 487 | 488 | com_ports.addEventListener("change", () => { 489 | if (com_ports.value !== "") 490 | current_port = com_ports.value; 491 | }); 492 | 493 | function init() { 494 | //Data send handles end 495 | getPorts(); 496 | makeResizableDiv('.options_menu', false, true, [document.getElementById("sidebar_resizer")]); 497 | } 498 | 499 | init(); 500 | 501 | -------------------------------------------------------------------------------- /src/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ben-full_serial_monitor", 3 | "productName": "Ben's Full Serial Monitor", 4 | "version": "1.0.2", 5 | "description": "Full serial monitor and ESP32 exception Decoder", 6 | "main": "main.js", 7 | "scripts": { 8 | "start": "electron ." 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/BenjamimKrug/FullSerialMonitor.git" 13 | }, 14 | "author": "BenjamimKrug", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/BenjamimKrug/FullSerialMonitor/issues" 18 | }, 19 | "homepage": "https://github.com/BenjamimKrug/FullSerialMonitor#readme", 20 | "devDependencies": { 21 | "electron": "^20.1.4", 22 | "electron-packager": "^17.1.1" 23 | }, 24 | "dependencies": { 25 | "@electron/remote": "^2.0.8", 26 | "chart.js": "^4.3.0", 27 | "dygraphs": "^2.2.1", 28 | "electron-find": "^1.0.7", 29 | "remote": "^0.2.6", 30 | "serialport": "^10.4.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/sequencer_window/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 15 |
16 |
17 |
18 |
19 | 21 | 25 | 29 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/sequencer_window/sequencer.js: -------------------------------------------------------------------------------- 1 | //general code that is needed for every window we create 2 | const fs = require('fs'); 3 | const { ipcRenderer } = require('electron'); 4 | var theme_style = document.getElementById("theme_style"); 5 | 6 | ipcRenderer.on('recvChannel', (_event, arg) => { 7 | switch (arg.cmd) { 8 | case "setTheme": { 9 | theme_style.href = arg.theme; 10 | break; 11 | } 12 | case "setLang": { 13 | current_language = arg.lang; 14 | updateContentLang(); 15 | break; 16 | } 17 | default: 18 | break; 19 | } 20 | }); 21 | ipcRenderer.send('recvMain', { id: 0, cmd: "getTheme", requester: 1 }); 22 | ipcRenderer.send('recvMain', { id: 0, cmd: "getLang", requester: 1 }); 23 | 24 | /*Start of specific implementation */ 25 | 26 | 27 | const sequence_file_path = "./sequence.json"; 28 | 29 | var deleted_packets = []; 30 | var sequence = { count: 0, continuous: false, packets: [] }; 31 | var packets_div = document.getElementById("packets_div"); 32 | const continuous_sequence = document.getElementById("continuous_sequence"); 33 | fs.readFile(sequence_file_path, 'utf8', (err, data) => { 34 | if (err) { 35 | return; 36 | } 37 | sequence = JSON.parse(data); 38 | continuous_sequence.checked = sequence.continuous; 39 | updateSequence(); 40 | }); 41 | 42 | function stopSequence() { 43 | ipcRenderer.send('recvMain', { id: 0, cmd: "stopSequence" }); 44 | } 45 | 46 | function sendSequence() { 47 | ipcRenderer.send('recvMain', { id: 0, cmd: "sendSequence", sequence: sequence_file_path }); 48 | } 49 | 50 | function updateSequence() { 51 | sequence.count = 0; 52 | packets_div.innerHTML = ""; 53 | for (var i = 0; i < sequence.packets.length; i++) { 54 | createPacketField(sequence.packets[i].data, sequence.packets[i].delay); 55 | } 56 | } 57 | 58 | function saveSequence() { 59 | sequence.packets = []; 60 | for (var i = 0; i < sequence.count; i++) { 61 | if (!deleted_packets.includes(i)) { 62 | var newParserData = document.getElementById("cpData" + i); 63 | var newParserDelay = document.getElementById("cpDelay" + i); 64 | sequence.packets.push({ 65 | data: newParserData.value.trim(), 66 | delay: newParserDelay.value.trim() 67 | }); 68 | } 69 | } 70 | sequence.continuous = continuous_sequence.checked; 71 | updateSequence(); 72 | try { 73 | fs.unlinkSync(sequence_file_path); 74 | } 75 | catch (err) { 76 | // needs to be a separate try catch because it will fail on the first time configuring 77 | console.log(err); 78 | } 79 | try { 80 | fs.writeFileSync(sequence_file_path, JSON.stringify(sequence)); 81 | } 82 | catch (err) { 83 | console.log(err); 84 | ipcRenderer.send("openAlert", current_language["writing_error"]); 85 | } 86 | } 87 | 88 | function deletePacketField(id) { 89 | targetPacketField = document.getElementById(`cpDiv${id}`); 90 | packets_div.removeChild(targetPacketField); 91 | deleted_packets.push(id); 92 | } 93 | 94 | function createPacketField(data_packet, delay) { 95 | var newPacketField = document.createElement("div"); 96 | newPacketField.setAttribute("id", "cpDiv" + sequence.count); 97 | newPacketField.setAttribute("class", "custom_parser_entry"); 98 | 99 | var newPacketData = document.createElement("input"); 100 | newPacketData.setAttribute("type", "text"); 101 | newPacketData.setAttribute("placeholder", current_language["packet_data_placeholder"]); 102 | newPacketData.setAttribute("id", "cpData" + sequence.count); 103 | newPacketData.setAttribute("class", "custom_parser_input"); 104 | if (typeof (data_packet) !== 'undefined') 105 | newPacketData.setAttribute("value", data_packet); 106 | 107 | var newPacketDelay = document.createElement("input"); 108 | newPacketDelay.setAttribute("type", "text"); 109 | newPacketDelay.setAttribute("placeholder", "500"); 110 | newPacketDelay.setAttribute("id", "cpDelay" + sequence.count); 111 | newPacketDelay.setAttribute("class", "custom_parser_input"); 112 | if (typeof (delay) !== 'undefined') 113 | newPacketDelay.setAttribute("value", delay); 114 | 115 | var newPacketExclude = document.createElement("button"); 116 | newPacketExclude.setAttribute("class", "sequencer_button"); 117 | newPacketExclude.setAttribute("style", "position: absolute;right:5px;"); 118 | 119 | var newPacketExclude = document.createElement("img"); 120 | newPacketExclude.setAttribute("width", "16"); 121 | newPacketExclude.setAttribute("height", "16"); 122 | newPacketExclude.setAttribute("src", "../images/trash-2-16.png"); 123 | newPacketExclude.setAttribute("onclick", `deletePacketField(${sequence.count})`); 124 | 125 | var line = document.createElement("div"); 126 | line.setAttribute("class", "line"); 127 | var label = document.createElement("label"); 128 | label.setAttribute("data-i18n", "packet"); 129 | label.innerText = current_language["packet"]; 130 | label.setAttribute("style", "min-width: 5%;"); 131 | line.appendChild(label); 132 | line.appendChild(newPacketData); 133 | line.setAttribute("style", "width:100%"); 134 | newPacketField.appendChild(line); 135 | 136 | line = document.createElement("div"); 137 | line.setAttribute("class", "line"); 138 | label = document.createElement("label"); 139 | label.setAttribute("data-i18n", "delay"); 140 | label.innerText = current_language["delay"]; 141 | line.appendChild(label); 142 | line.appendChild(newPacketDelay); 143 | newPacketField.appendChild(line); 144 | 145 | newPacketField.appendChild(newPacketExclude); 146 | packets_div.appendChild(newPacketField); 147 | sequence.count++; 148 | } -------------------------------------------------------------------------------- /src/style/dark_theme_style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #373737; 3 | color: white; 4 | font-family: Arial, Helvetica, sans-serif; 5 | display: flex; 6 | flex-direction: row; 7 | margin: 0; 8 | } 9 | 10 | .boolean { 11 | color: blue; 12 | } 13 | 14 | .box { 15 | background: rgb(25, 25, 25); 16 | color: white; 17 | border-color: black; 18 | } 19 | 20 | .checkbox_options input:checked+.eye { 21 | background-color: white; 22 | } 23 | 24 | .checkbox_options .eye { 25 | background-color: rgb(151, 150, 150); 26 | } 27 | 28 | .config_menu { 29 | border-color: black; 30 | background-color: #373737; 31 | } 32 | 33 | .custom_parser_entry { 34 | background-color: #373737; 35 | color: white; 36 | border-color: black; 37 | } 38 | 39 | .decoder_window #backtrace_data_input { 40 | background: rgb(25, 25, 25); 41 | color: white; 42 | border-color: black; 43 | } 44 | 45 | .dropdown-content a { 46 | color: white; 47 | padding: 12px 16px; 48 | text-decoration: none; 49 | display: block; 50 | } 51 | 52 | .filters { 53 | border-color: black; 54 | background-color: #373737; 55 | } 56 | 57 | .find-box { 58 | background-color: #555555 !important; 59 | color: inherit !important; 60 | } 61 | 62 | .find-input { 63 | background-color: #555555 !important; 64 | color: inherit !important; 65 | border-color: white !important; 66 | } 67 | 68 | #graph_legend, 69 | #graph_inspector { 70 | background-color: #555555; 71 | border-color: white; 72 | } 73 | 74 | input[type=text] { 75 | background-color: #555555; 76 | color: white; 77 | } 78 | 79 | .key { 80 | color: rgb(105, 190, 45); 81 | } 82 | 83 | .null { 84 | color: magenta; 85 | } 86 | 87 | .number { 88 | color: rgb(255, 0, 255); 89 | } 90 | 91 | .options_menu { 92 | background-color: #373737; 93 | } 94 | 95 | .output_entry { 96 | background-color: grey; 97 | color: white; 98 | } 99 | 100 | select { 101 | background-color: #555555; 102 | color: white; 103 | } 104 | 105 | .string { 106 | color: rgb(182, 182, 2); 107 | } 108 | 109 | ::-webkit-scrollbar-track { 110 | background: #888; 111 | } 112 | 113 | ::-webkit-scrollbar-thumb { 114 | background: #f1f1f1; 115 | border-radius: 7px; 116 | } -------------------------------------------------------------------------------- /src/style/light_theme_style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: white; 3 | color: black; 4 | font-family: Arial, Helvetica, sans-serif; 5 | display: flex; 6 | flex-direction: row; 7 | margin: 0; 8 | } 9 | 10 | .boolean { 11 | color: red; 12 | } 13 | 14 | .box { 15 | background: lightgrey; 16 | color: black; 17 | border-color: black; 18 | } 19 | 20 | .checkbox_options input:checked+.eye { 21 | background-color: black; 22 | } 23 | 24 | .checkbox_options .eye { 25 | background-color: rgb(82, 81, 81); 26 | } 27 | 28 | .config_menu { 29 | border-color: black; 30 | background: white; 31 | } 32 | 33 | .custom_parser_entry { 34 | background-color: white; 35 | color: black; 36 | border-color: black; 37 | } 38 | 39 | .decoder_window #backtrace_data_input { 40 | background: lightgrey; 41 | color: black; 42 | border-color: black; 43 | } 44 | 45 | .dropdown-content a { 46 | color: black; 47 | padding: 12px 16px; 48 | text-decoration: none; 49 | display: block; 50 | } 51 | 52 | .filters { 53 | border-color: black; 54 | background: white; 55 | } 56 | 57 | .find-box { 58 | background-color: white !important; 59 | color: inherit !important; 60 | } 61 | 62 | .find-input { 63 | background-color: white !important; 64 | color: inherit !important; 65 | border-color: black !important; 66 | } 67 | 68 | .graph_label_disabled { 69 | color: rgb(218, 215, 215); 70 | } 71 | 72 | #graph_legend, 73 | #graph_inspector { 74 | background-color: #eee8e8; 75 | border-color: black; 76 | } 77 | 78 | input[type=text] { 79 | background-color: white; 80 | color: black; 81 | } 82 | 83 | .key { 84 | color: blue; 85 | } 86 | 87 | .null { 88 | color: red; 89 | } 90 | 91 | .number { 92 | color: red; 93 | } 94 | 95 | .options_menu { 96 | background-color: #999; 97 | } 98 | 99 | .output_entry { 100 | background-color: white; 101 | color: black; 102 | } 103 | 104 | select { 105 | background-color: white; 106 | color: black; 107 | } 108 | 109 | .string { 110 | color: green; 111 | } 112 | 113 | ::-webkit-scrollbar-track { 114 | background: #777; 115 | } 116 | 117 | ::-webkit-scrollbar-thumb { 118 | background: #fffefe; 119 | border-radius: 7px; 120 | } -------------------------------------------------------------------------------- /src/style/shared_style.css: -------------------------------------------------------------------------------- 1 | .advanced_config_div { 2 | display: flex; 3 | flex-direction: column; 4 | } 5 | 6 | body { 7 | height: 100vh; 8 | width: 100vw; 9 | } 10 | 11 | .box { 12 | padding: 10px; 13 | resize: none; 14 | white-space: nowrap; 15 | border-radius: 5px; 16 | border-width: 1px; 17 | border-style: solid; 18 | } 19 | 20 | button:disabled { 21 | filter: brightness(70%); 22 | } 23 | 24 | button:hover:not([disabled]) { 25 | background: rgb(50, 93, 130); 26 | } 27 | 28 | .checkbox_options { 29 | display: inline-block; 30 | } 31 | 32 | .checkbox_options input { 33 | display: none; 34 | } 35 | 36 | .checkbox_options input:checked+.slider { 37 | background: rgb(100, 143, 200); 38 | } 39 | 40 | .checkbox_options input:checked+.slider:before { 41 | transform: translateX(17px); 42 | } 43 | 44 | .checkbox_options input:checked+.eye { 45 | -webkit-mask: url('../images/visible-eye.svg') no-repeat 50% 50%; 46 | -webkit-mask-size: contain; 47 | mask: url('../images/visible-eye.svg') no-repeat 50% 50%; 48 | mask-size: contain; 49 | } 50 | 51 | .checkbox_options .eye { 52 | -webkit-mask: url('../images/not-visible-eye.svg') no-repeat 50% 50%; 53 | -webkit-mask-size: contain; 54 | mask: url('../images/not-visible-eye.svg') no-repeat 50% 50%; 55 | mask-size: contain; 56 | width: 24px; 57 | height: 24px; 58 | padding: 2px; 59 | cursor: pointer; 60 | } 61 | 62 | .checkbox_options .slider { 63 | width: 36px !important; 64 | height: 18px; 65 | padding: 2px; 66 | box-sizing: border-box; 67 | background-color: #555555; 68 | border-radius: 15px; 69 | transition: .3s; 70 | cursor: pointer; 71 | } 72 | 73 | .checkbox_options .slider:before { 74 | display: flex; 75 | background-color: #fff; 76 | content: ""; 77 | width: 14px; 78 | height: 14px; 79 | transition: .3s; 80 | } 81 | 82 | .checkbox_options .slider.round:before { 83 | border-radius: 50%; 84 | } 85 | 86 | .close_btn { 87 | margin-left: auto; 88 | margin-right: 4px; 89 | } 90 | 91 | .config_menu { 92 | display: flex; 93 | flex-direction: column; 94 | overflow: hidden; 95 | position: fixed; 96 | top: 50%; 97 | left: 50%; 98 | width: 50%; 99 | height: 70%; 100 | border-width: 3px; 101 | border-style: solid; 102 | border-radius: 10px; 103 | padding: 10px; 104 | -ms-transform: translate(-50%, -50%); 105 | transform: translate(-50%, -50%); 106 | z-index: 999; 107 | } 108 | 109 | .config_menu .checkbox_options { 110 | margin-right: 5px; 111 | } 112 | 113 | .config_menu .color_input { 114 | width: 20px; 115 | height: 20px; 116 | background: none; 117 | box-sizing: content-box; 118 | padding: 0px; 119 | border: none; 120 | position: relative; 121 | margin: 0; 122 | margin-right: 15px; 123 | } 124 | 125 | .config_menu .color_input::-webkit-color-swatch { 126 | border-radius: 50%; 127 | padding: 0px; 128 | } 129 | 130 | .config_menu .color_input::-webkit-color-swatch-wrapper { 131 | border: none; 132 | border-radius: 50%; 133 | padding: 0; 134 | } 135 | 136 | .config_menu label { 137 | margin-right: 5px; 138 | } 139 | 140 | .config_menu select { 141 | margin-left: auto; 142 | border-radius: 5px; 143 | width: 50%; 144 | } 145 | 146 | .config_menu_line { 147 | display: flex; 148 | flex-direction: row; 149 | width: auto; 150 | margin-top: 1px; 151 | margin-bottom: 1px; 152 | margin-left: 5px; 153 | margin-right: 5px; 154 | } 155 | 156 | #content { 157 | overflow: hidden; 158 | width: calc(100% - 225px); 159 | display: flex; 160 | flex-direction: column; 161 | margin: 5px; 162 | min-width: 408px; 163 | } 164 | 165 | .customParsersDiv { 166 | padding: 10px; 167 | box-sizing: border-box; 168 | overflow: hidden; 169 | overflow-x: auto; 170 | overflow-y: auto; 171 | width: 100%; 172 | height: 100%; 173 | min-height: 100px; 174 | border-radius: 0px; 175 | } 176 | 177 | .custom_parser_entry { 178 | display: flex; 179 | flex-direction: row; 180 | font-weight: bold; 181 | padding: 5px; 182 | box-sizing: border-box; 183 | border-width: 0px; 184 | border-radius: 5px; 185 | position: relative; 186 | white-space: nowrap; 187 | overflow: hidden; 188 | margin-bottom: 5px; 189 | } 190 | 191 | .custom_parser_entry .half { 192 | display: flex; 193 | flex-direction: column; 194 | width: 43%; 195 | height: 100%; 196 | margin-right: 15px; 197 | } 198 | 199 | .custom_parser_entry img { 200 | margin-left: auto; 201 | margin-top: auto; 202 | margin-bottom: auto; 203 | transform: translate(0, -30%); 204 | } 205 | 206 | .custom_parser_entry img:hover { 207 | scale: 1.1; 208 | } 209 | 210 | .custom_parser_entry input[type=color] { 211 | background: none; 212 | box-sizing: content-box; 213 | padding: 0px; 214 | border: none; 215 | height: 200%; 216 | width: 30px; 217 | position: relative; 218 | margin: 0; 219 | transform: translate(-10px, -20%); 220 | } 221 | 222 | .custom_parser_entry .line { 223 | display: flex; 224 | flex-direction: row; 225 | margin-bottom: 2px; 226 | margin-right: 5px; 227 | } 228 | 229 | .custom_parser_entry label { 230 | display: inline-block; 231 | min-width: 30%; 232 | } 233 | 234 | .custom_parser_entry .line input[type=text] { 235 | position: relative; 236 | margin-left: auto; 237 | width: 100%; 238 | } 239 | 240 | .decoder_window { 241 | display: flex; 242 | flex-direction: column; 243 | padding: 15px; 244 | box-sizing: border-box; 245 | } 246 | 247 | .decoder_window #backtrace_data_input { 248 | width: 100%; 249 | height: 100%; 250 | resize: none; 251 | border-radius: 3px; 252 | font-family: Arial, Helvetica, sans-serif; 253 | } 254 | 255 | .decoder_window .output { 256 | height: 100%; 257 | max-height: 100%; 258 | } 259 | 260 | .dropbtn { 261 | background: rgb(70, 113, 150); 262 | color: white; 263 | padding: 10px; 264 | font-size: 16px; 265 | border: none; 266 | } 267 | 268 | .dropbtn:hover { 269 | background: rgb(60, 96, 128); 270 | } 271 | 272 | .dropdown { 273 | margin-left: auto; 274 | position: relative; 275 | } 276 | 277 | .filters { 278 | display: flex; 279 | flex-direction: column; 280 | position: absolute; 281 | min-width: 200px; 282 | box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2); 283 | z-index: 900; 284 | right: 0px; 285 | padding: 10px; 286 | margin-left: 3px; 287 | box-sizing: border-box; 288 | border-width: 1px; 289 | border-style: solid; 290 | border-radius: 3px; 291 | } 292 | 293 | .filters a:hover { 294 | background-color: #ddd; 295 | } 296 | 297 | .filters .checkbox_options { 298 | margin-left: 0; 299 | } 300 | 301 | .filters label { 302 | display: inline-block; 303 | text-align: left !important; 304 | margin-left: 2px !important; 305 | width: auto !important; 306 | } 307 | 308 | .filters .line { 309 | display: flex; 310 | flex-direction: row; 311 | width: 100%; 312 | margin-bottom: 2px; 313 | align-items: center; 314 | } 315 | 316 | .file_path { 317 | width: 82%; 318 | } 319 | 320 | #find_container { 321 | top: 0px; 322 | right: 0; 323 | display: flex; 324 | width: auto; 325 | position: relative; 326 | width: 0; 327 | } 328 | 329 | .find-box { 330 | position: relative !important; 331 | top: 0 !important; 332 | right: 0 !important; 333 | margin-left: auto !important; 334 | transform: translate(-100%, 31px); 335 | align-items: center; 336 | } 337 | 338 | .find-back, 339 | .find-case, 340 | .find-close, 341 | .find-forward { 342 | color: white !important; 343 | background: rgb(70, 113, 150) !important; 344 | opacity: 1 !important; 345 | margin-left: 1px !important; 346 | margin-right: 1px !important; 347 | } 348 | 349 | .find-back-cover, 350 | .find-forward-cover { 351 | opacity: 0 !important; 352 | } 353 | 354 | .find-back:hover, 355 | .find-case:hover, 356 | .find-close:hover, 357 | .find-forward:hover { 358 | background: rgb(50, 93, 130) !important; 359 | } 360 | 361 | .find-close-inner1, 362 | .find-close-inner2 { 363 | background: rgb(255, 255, 255) !important; 364 | } 365 | 366 | .find-back-line { 367 | border-color: transparent rgb(255 255 255) transparent transparent !important; 368 | } 369 | 370 | .find-input { 371 | border-width: 1px !important; 372 | } 373 | 374 | .find-forward-line { 375 | border-color: transparent transparent transparent rgb(255 255 255) !important; 376 | } 377 | 378 | .find-matches { 379 | color: inherit !important; 380 | } 381 | 382 | .general_btn { 383 | background: rgb(70, 113, 150); 384 | color: white; 385 | border-radius: 4px; 386 | border-style: solid; 387 | border-color: rgb(70, 113, 150); 388 | } 389 | 390 | .general_btn:hover:not([disabled]) { 391 | background: rgb(50, 93, 130); 392 | } 393 | 394 | .graph_color_label { 395 | width: 15px; 396 | height: 15px; 397 | margin-right: 5px; 398 | margin-left: 15px; 399 | } 400 | 401 | .graph_color_label:hover { 402 | cursor: pointer; 403 | scale: 1.1; 404 | } 405 | 406 | #graph_container { 407 | width: 100%; 408 | height: 95%; 409 | margin: auto; 410 | padding: 0; 411 | } 412 | 413 | #graph_inspector { 414 | position: absolute; 415 | width: auto; 416 | height: auto; 417 | box-sizing: border-box; 418 | display: none; 419 | padding: 1%; 420 | z-index: 999; 421 | border-radius: 5px; 422 | border-width: 3px; 423 | } 424 | 425 | .graph_label_disabled { 426 | filter: brightness(70%); 427 | } 428 | 429 | #graph_legend { 430 | z-index: 9999; 431 | position: absolute; 432 | display: flex; 433 | padding-top: 10px; 434 | padding-right: 15px; 435 | padding-bottom: 5px; 436 | border-bottom-left-radius: 5px; 437 | border-bottom-right-radius: 5px; 438 | left: 50%; 439 | transform: translate(-50%, 0); 440 | } 441 | 442 | .graphsListDiv { 443 | overflow: hidden; 444 | overflow-x: auto; 445 | overflow-y: auto; 446 | width: 95%; 447 | height: 100%; 448 | min-height: 100px; 449 | } 450 | 451 | #header { 452 | display: flex; 453 | flex-direction: row; 454 | width: 100%; 455 | height: 30px; 456 | } 457 | 458 | #header input { 459 | height: 28px; 460 | width: 100%; 461 | margin-bottom: 2px; 462 | margin-left: 2px; 463 | margin-right: 2px; 464 | } 465 | 466 | #header input:disabled { 467 | filter: brightness(60%); 468 | } 469 | 470 | #header button { 471 | height: 28px; 472 | width: auto; 473 | margin-bottom: 2px; 474 | margin-right: 2px; 475 | margin-left: auto; 476 | } 477 | 478 | #header button:disabled { 479 | filter: brightness(60%); 480 | } 481 | 482 | #header select { 483 | height: 28px; 484 | width: 180px; 485 | margin-bottom: 2px; 486 | margin-left: 2px; 487 | margin-right: 2px; 488 | } 489 | 490 | #header select:disabled { 491 | filter: brightness(60%); 492 | } 493 | 494 | .hidden { 495 | display: none; 496 | } 497 | 498 | img { 499 | transform: translate(0, 20%); 500 | } 501 | 502 | input[type=text] { 503 | border-radius: 5px; 504 | border-width: 1px; 505 | border-style: solid; 506 | margin-right: 2px; 507 | } 508 | 509 | input::-webkit-calendar-picker-indicator { 510 | opacity: 100; 511 | } 512 | 513 | .label_right { 514 | width: 150px; 515 | } 516 | 517 | .label_left { 518 | width: 150px; 519 | text-align: right; 520 | right: 25px; 521 | } 522 | 523 | .options_button { 524 | background: rgb(70, 113, 150); 525 | color: white; 526 | border-radius: 4px; 527 | border-style: solid; 528 | border-color: rgb(70, 113, 150); 529 | left: 50%; 530 | width: auto; 531 | margin: 5px; 532 | } 533 | 534 | .options_menu { 535 | display: flex; 536 | flex-direction: column; 537 | overflow: hidden; 538 | height: 100%; 539 | min-width: 220px; 540 | width: 220px; 541 | margin-left: auto; 542 | } 543 | 544 | .options_menu_line { 545 | display: flex; 546 | flex-direction: row; 547 | width: auto; 548 | margin-top: 1px; 549 | margin-bottom: 1px; 550 | margin-left: 5px; 551 | margin-right: 5px; 552 | align-items: center; 553 | } 554 | 555 | .options_menu .checkbox_options { 556 | margin-left: auto; 557 | } 558 | 559 | .options_menu_line label { 560 | width: auto; 561 | margin-right: 5px; 562 | } 563 | 564 | .options_menu select { 565 | margin-left: auto; 566 | border-radius: 5px; 567 | width: 50%; 568 | } 569 | 570 | .output { 571 | overflow: hidden; 572 | max-height: 12%; 573 | min-height: 12%; 574 | overflow-x: auto; 575 | overflow-y: auto; 576 | width: calc(100% - 25px); 577 | font-weight: bold; 578 | } 579 | 580 | .output_entry { 581 | white-space: nowrap; 582 | overflow: hidden; 583 | width: 100%; 584 | height: 45px; 585 | text-align: left; 586 | border: none; 587 | border-left: solid; 588 | border-width: 5px; 589 | border-radius: 2px; 590 | margin-top: 2px; 591 | margin-bottom: 2px; 592 | } 593 | 594 | .output_history { 595 | height: 47%; 596 | width: calc(100%-5px); 597 | overflow-x: auto; 598 | overflow-y: auto; 599 | } 600 | 601 | .packetsDiv { 602 | overflow: hidden; 603 | overflow-x: auto; 604 | overflow-y: auto; 605 | height: 100%; 606 | } 607 | 608 | .parser_timestamp { 609 | display: block; 610 | font-size: small; 611 | font-weight: bold; 612 | } 613 | 614 | .select_input { 615 | right: 60px; 616 | text-align: right; 617 | width: 100px; 618 | height: 20px; 619 | } 620 | 621 | .sequencer_button { 622 | background: rgb(70, 113, 150); 623 | color: white; 624 | border-radius: 4px; 625 | border-style: solid; 626 | border-color: rgb(70, 113, 150); 627 | width: auto; 628 | margin-right: auto; 629 | margin: 0.3%; 630 | } 631 | 632 | #sidebar_resizer { 633 | height: 100%; 634 | width: 20px; 635 | background-color: grey; 636 | z-index: 900; 637 | cursor: ew-resize; 638 | } 639 | 640 | .terminal { 641 | max-height: 73%; 642 | min-height: 73%; 643 | overflow: hidden; 644 | overflow-x: auto; 645 | overflow-y: auto; 646 | width: calc(100% - 25px); 647 | } 648 | 649 | .terminal input { 650 | display: table-cell; 651 | border: none; 652 | background: black; 653 | color: white; 654 | outline: none; 655 | } 656 | 657 | .terminal span { 658 | display: table-cell; 659 | width: 1px; 660 | } 661 | 662 | ::-webkit-scrollbar { 663 | width: 10px; 664 | height: 10px; 665 | } 666 | 667 | ::-webkit-scrollbar-thumb:hover { 668 | background: #555; 669 | } --------------------------------------------------------------------------------