ngx-serial-console
Version:
This is an angular library, to create a component that would connect to a serial port and display the data from the port.
1 lines • 18 kB
Source Map (JSON)
{"version":3,"file":"ngx-serial-console.mjs","sources":["../../../projects/ngx-serial-console/src/lib/ngx-serial-console.ts","../../../projects/ngx-serial-console/src/lib/ngx-serial-console.html","../../../projects/ngx-serial-console/src/public-api.ts","../../../projects/ngx-serial-console/src/ngx-serial-console.ts"],"sourcesContent":["import { Component, computed, ElementRef, Input, Signal, signal, ViewChild, WritableSignal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\n\ninterface SerialMonitorState {\n serialAvailable: boolean;\n connected: boolean;\n maxLines: number;\n outputLines: string[];\n baudRate: number;\n vendorId: string | undefined;\n productId: string | undefined;\n theme: \"CRT\" | \"Plain\";\n}\n\n@Component({\n selector: 'ngx-serial-console',\n imports: [FormsModule],\n templateUrl: './ngx-serial-console.html',\n styleUrl: './ngx-serial-console.css'\n})\n\n// NgxSerialConsole\n// Works only on Chromium based browsers, allows users to select the usb port and baud rate\n// to connect with serial interface. The output of the serial interface can be monitored using this tool.\n// When debugging Arduino devices, ESP32, ESP8266 and other devices with a serial port.\nexport class NgxSerialConsole {\n\n @ViewChild('scrollSerialContainer') private scrollContainer!: ElementRef; \n\n @Input() theme: \"CRT\" | \"Plain\" = \"Plain\";\n\n // state as a signal, so the component to react to state changes immediately.\n state: WritableSignal<SerialMonitorState> = signal({\n serialAvailable: false,\n connected: false,\n maxLines: 500,\n outputLines: [],\n baudRate: 115200,\n vendorId: \"\",\n productId: \"\",\n theme: \"Plain\"\n }); \n \n // computed signal, which listens on state changes, and updates the consoleOutput\n consoleOutput: Signal<string> = computed(() => this.state().outputLines.join(''));\n\n serial: any;\n\n themeStyles: string[] = [\"CRT\", \"Plain\"];\n baudRates: number[] = [300, 600, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 1000000, 1500000];\n\n isUserScrolling = false; // triggered when user scrolls using mouse or touch\n autoScrollEnabled: boolean = true; // when user scrolls to any other position, auto scroll is disabled\n keepConnectionAlive: boolean = true;\n\n constructor() {\n\n let serialAvailable = false;\n if ('serial' in navigator) {\n this.serial = navigator.serial;\n serialAvailable = true;\n console.info(\"Serial device is available\");\n } else {\n this.serial = null;\n console.info(\"Serial device not available, use google chrome browser\");\n }\n\n this.state.update(state => ({ \n ...state, \n serialAvailable: serialAvailable,\n theme: this.theme, \n }));\n\n }\n\n ngAfterViewChecked() {\n\n if (!this.serial) return; // there will be no view to check the scroll\n\n const element = this.scrollContainer.nativeElement;\n\n element.addEventListener('scroll', () => {\n\n // Ignore scroll events triggered by programmatic scroll\n if (!this.isUserScrolling) return;\n // adding a threshold of 100 to scroll bottom, so user does not have to go to the fag end bottom\n const nearBottom = element.scrollHeight - element.scrollTop < (element.clientHeight + 100);\n this.autoScrollEnabled = nearBottom;\n \n // console.log(\"Auto scroll enabled \", this.autoScrollEnabled);\n });\n\n // Enable this flag only on user interaction, for example with mouse or touch:\n element.addEventListener('wheel', () => this.isUserScrolling = true);\n element.addEventListener('touchmove', () => this.isUserScrolling = true);\n\n }\n\n // clear the output of the console window\n clear() {\n this.state.update(state => ({\n ...state,\n outputLines: []\n }));\n }\n\n unsetConnection() {\n this.state.update(state => ({\n ...state,\n connected: false,\n vendorId: \"\",\n productId: \"\",\n }));\n\n this.appendOutput('\\nDisconnected from serial port\\n');\n }\n\n async disconnectSerialPort() {\n this.keepConnectionAlive = false;\n }\n\n // initiate the serial connection\n // this will open a dialog, where the user has to select the console port\n // because of security reasons, we cannot auto connect to a serial port from browser\n connectSerialPort() {\n\n if (this.serial) {\n this.handleSerialEvents();\n this.openSerialPort();\n }\n }\n\n // handle events, so we know if the serial device disconnected\n handleSerialEvents() {\n\n this.serial?.addEventListener('disconnect', (event: any) => {\n //console.log('Serial port disconnected:', event.target);\n this.unsetConnection();\n this.appendOutput('\\n\\nConnection lost, check device or cable. Please reconnect again using the \"Connect Button above\" \\n');\n });\n this.serial?.addEventListener('connect', (event: any) => {\n //console.log('Serial port connected:', event.target);\n this.appendOutput('\\n\\nConnection was reset, check device or cable. Please reconnect again using the \"Connect Button above\" \\n');\n });\n\n }\n\n async openSerialPort() {\n try {\n if (this.serial) {\n // Request the user to select a serial port.\n \n this.appendOutput(`Waiting for user input to connect to serial port\\n`); \n const port = await this.serial.requestPort();\n await port.open({ baudRate: this.state().baudRate });\n\n if (port) {\n this.state.update(state => ({\n ...state,\n connected: true,\n vendorId: port.getInfo().vendorId,\n productId: port.getInfo().productId,\n }));\n this.readSerialPort(port);\n }\n }\n } \n catch (error: unknown) {\n if (error instanceof Error) {\n // this.unsetConnection(); // TODO, depending on error we have to terminate connection\n this.appendOutput(`Error received: ${error.message} \\n`);\n } else {\n this.unsetConnection();\n this.appendOutput(`Unexpected error: ${String(error)}`);\n }\n }\n }\n\n // read stream from the serial port, till a user disconnects\n // or the serial device disconnects\n // handle disconnections gracefully\n async readSerialPort(port: any) {\n this.appendOutput(`Connected serial port with baud rate ${this.state().baudRate}\\n`); \n // Set up a text decoder stream to read from the serial port.\n const textDecoder = new TextDecoderStream();\n let readableStreamClosed = port.readable.pipeTo(textDecoder.writable);\n let reader = textDecoder.readable.getReader();\n \n this.appendOutput('Connected and reading data:\\n');\n\n // if we want to stop the read loop, set this var to false, in other functions\n this.keepConnectionAlive = true;\n \n // we are using zoneless, \n // #TODO binu, revisit why PendingTasks is not needed here\n while (this.keepConnectionAlive) {\n const { value, done } = await reader.read();\n if (done) {\n // console.log(\"Serial Reader closed\");\n reader.releaseLock();\n this.keepConnectionAlive = false;\n break;\n }\n if (value) {\n this.appendOutput(value);\n }\n }\n\n const textEncoder = new TextEncoderStream();\n const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);\n let writer = textEncoder.writable.getWriter();\n\n // close the reader, write and cleanup states\n reader.cancel();\n await readableStreamClosed.catch(() => { /* Ignore the error */ });\n writer.close();\n await writableStreamClosed;\n await port.close();\n \n this.unsetConnection();\n\n }\n \n // appends the new data from serial monitor to the output array\n // if the number of lines exceed maxLines, we trim the array to maxLines length\n appendOutput(newData: string) {\n\n this.state.update(state => {\n // Concatenate existing lines with new lines\n const updatedLines = [...state.outputLines, newData];\n\n // Trim the array if it exceeds maxLines\n const trimmedLines =\n updatedLines.length > state.maxLines\n ? updatedLines.slice(updatedLines.length - state.maxLines)\n : updatedLines;\n\n return {\n ...state,\n outputLines: trimmedLines\n };\n });\n this.scrollToBottomSerialOut();\n }\n\n private scrollToBottomSerialOut(): void {\n\n if (!this.autoScrollEnabled) return;\n this.isUserScrolling = false; // unset flag, till user feedback is present\n try {\n this.scrollContainer.nativeElement.scrollTop = this.scrollContainer.nativeElement.scrollHeight;\n } catch (err) {\n // Handle errors if any\n }\n\n } \n\n}\n","@if (state().serialAvailable) {\n <div class=\"card console-window\" [class]=\"state().theme\">\n \n <div class=\"input-group input-group-sm mb-3\">\n <span class=\"input-group-text\">\n Serial Monitor\n @if (state().connected) {\n <i class=\"fa-solid fa-link px-2\"></i>\n } @else {\n <i class=\"fa-solid fa-link-slash px-2\"></i>\n }\n </span>\n @if (state().connected) {\n <button id=\"serialConnectButton\" class=\"btn btn-outline-secondary\" (click)=\"disconnectSerialPort()\">Disconnect</button>\n } @else {\n <button id=\"serialConnectButton\" class=\"btn btn-outline-secondary\" (click)=\"connectSerialPort()\">Connect</button>\n }\n \n <span class=\"input-group-text ps-4\">Theme</span>\n <select class=\"form-select\" id=\"themeStyleSelect\" name=\"themeStyle\" [(ngModel)]=\"state().theme\">\n @for (rate of themeStyles; track rate) {\n <option [value]=\"rate\">{{ rate }}</option>\n }\n </select>\n\n <span class=\"input-group-text ps-4\">Baud Rate</span>\n <select class=\"form-select\" id=\"baudRateSelect\" name=\"baudRate\" [(ngModel)]=\"state().baudRate\">\n @for (rate of baudRates; track rate) {\n <option [value]=\"rate\">{{ rate }}</option>\n }\n </select>\n <button class=\"btn btn-outline-secondary\" (click)=\"clear()\" tooltip=\"Clear History\">\n Clear\n </button>\n </div>\n\n \n <div class=\"card-body\" #scrollSerialContainer>\n <pre class=\"serial-out-text\">{{ consoleOutput() }}</pre>\n </div>\n\n </div>\n} @else {\n <div class=\"alert alert-info\" role=\"alert\">\n <i class=\"fa-solid fa-triangle-exclamation\"></i> Your browser does not support serial port access.<br/>\n This feature is only available on chromium based browsers.<br/>\n Please use one the below browsers to access this feature.<br/>\n eg: Google Chrome, Opera, Edge<br/>\n <a href=\"https://caniuse.com/?search=usb\" target=\"_blank\">Browser support</a>\n </div>\n}\n\n","/*\n * Public API Surface of ngx-serial-console\n */\n\nexport * from './lib/ngx-serial-console';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}