1*61c4878aSAndroid Build Coastguard Worker// Copyright 2024 The Pigweed Authors 2*61c4878aSAndroid Build Coastguard Worker// 3*61c4878aSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); you may not 4*61c4878aSAndroid Build Coastguard Worker// use this file except in compliance with the License. You may obtain a copy of 5*61c4878aSAndroid Build Coastguard Worker// the License at 6*61c4878aSAndroid Build Coastguard Worker// 7*61c4878aSAndroid Build Coastguard Worker// https://www.apache.org/licenses/LICENSE-2.0 8*61c4878aSAndroid Build Coastguard Worker// 9*61c4878aSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*61c4878aSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11*61c4878aSAndroid Build Coastguard Worker// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12*61c4878aSAndroid Build Coastguard Worker// License for the specific language governing permissions and limitations under 13*61c4878aSAndroid Build Coastguard Worker// the License. 14*61c4878aSAndroid Build Coastguard Worker 15*61c4878aSAndroid Build Coastguard Workerimport { WebSocketRPCReplKernel, WebSocketRPCClient } from './repl-kernel'; 16*61c4878aSAndroid Build Coastguard Workerimport { createLogViewer } from './createLogViewer'; 17*61c4878aSAndroid Build Coastguard Workerimport { LogSource } from './log-source'; 18*61c4878aSAndroid Build Coastguard Workerimport { Severity } from './shared/interfaces'; 19*61c4878aSAndroid Build Coastguard Workerimport { Repl } from './components/repl/repl'; 20*61c4878aSAndroid Build Coastguard Worker 21*61c4878aSAndroid Build Coastguard Workerconst logLevelToSeverity = { 22*61c4878aSAndroid Build Coastguard Worker 10: Severity.DEBUG, 23*61c4878aSAndroid Build Coastguard Worker 20: Severity.INFO, 24*61c4878aSAndroid Build Coastguard Worker 21: Severity.INFO, 25*61c4878aSAndroid Build Coastguard Worker 30: Severity.WARNING, 26*61c4878aSAndroid Build Coastguard Worker 40: Severity.ERROR, 27*61c4878aSAndroid Build Coastguard Worker 50: Severity.CRITICAL, 28*61c4878aSAndroid Build Coastguard Worker 70: Severity.CRITICAL, 29*61c4878aSAndroid Build Coastguard Worker}; 30*61c4878aSAndroid Build Coastguard Worker 31*61c4878aSAndroid Build Coastguard Workerconst nonAdditionalDataFields = [ 32*61c4878aSAndroid Build Coastguard Worker '_hosttime', 33*61c4878aSAndroid Build Coastguard Worker 'levelname', 34*61c4878aSAndroid Build Coastguard Worker 'levelno', 35*61c4878aSAndroid Build Coastguard Worker 'args', 36*61c4878aSAndroid Build Coastguard Worker 'fields', 37*61c4878aSAndroid Build Coastguard Worker 'message', 38*61c4878aSAndroid Build Coastguard Worker 'time', 39*61c4878aSAndroid Build Coastguard Worker]; 40*61c4878aSAndroid Build Coastguard Worker 41*61c4878aSAndroid Build Coastguard Worker// Format a date in the standard pw_cli style YYYY-mm-dd HH:MM:SS 42*61c4878aSAndroid Build Coastguard Workerfunction formatDate(dt: Date) { 43*61c4878aSAndroid Build Coastguard Worker function pad2(n: number) { 44*61c4878aSAndroid Build Coastguard Worker return (n < 10 ? '0' : '') + n; 45*61c4878aSAndroid Build Coastguard Worker } 46*61c4878aSAndroid Build Coastguard Worker 47*61c4878aSAndroid Build Coastguard Worker return ( 48*61c4878aSAndroid Build Coastguard Worker dt.getFullYear() + 49*61c4878aSAndroid Build Coastguard Worker pad2(dt.getMonth() + 1) + 50*61c4878aSAndroid Build Coastguard Worker pad2(dt.getDate()) + 51*61c4878aSAndroid Build Coastguard Worker ' ' + 52*61c4878aSAndroid Build Coastguard Worker pad2(dt.getHours()) + 53*61c4878aSAndroid Build Coastguard Worker ':' + 54*61c4878aSAndroid Build Coastguard Worker pad2(dt.getMinutes()) + 55*61c4878aSAndroid Build Coastguard Worker ':' + 56*61c4878aSAndroid Build Coastguard Worker pad2(dt.getSeconds()) 57*61c4878aSAndroid Build Coastguard Worker ); 58*61c4878aSAndroid Build Coastguard Worker} 59*61c4878aSAndroid Build Coastguard Worker 60*61c4878aSAndroid Build Coastguard Worker// New LogSource to consume pw-console log json messages 61*61c4878aSAndroid Build Coastguard Workerclass PwConsoleLogSource extends LogSource { 62*61c4878aSAndroid Build Coastguard Worker // @ts-ignore 63*61c4878aSAndroid Build Coastguard Worker appendLog(data) { 64*61c4878aSAndroid Build Coastguard Worker const fields = [ 65*61c4878aSAndroid Build Coastguard Worker // @ts-ignore 66*61c4878aSAndroid Build Coastguard Worker { key: 'severity', value: logLevelToSeverity[data.levelno] }, 67*61c4878aSAndroid Build Coastguard Worker { key: 'time', value: data.time }, 68*61c4878aSAndroid Build Coastguard Worker ]; 69*61c4878aSAndroid Build Coastguard Worker Object.keys(data.fields || {}).forEach((columnName) => { 70*61c4878aSAndroid Build Coastguard Worker if (nonAdditionalDataFields.indexOf(columnName) === -1) { 71*61c4878aSAndroid Build Coastguard Worker fields.push({ key: columnName, value: data.fields[columnName] }); 72*61c4878aSAndroid Build Coastguard Worker } 73*61c4878aSAndroid Build Coastguard Worker }); 74*61c4878aSAndroid Build Coastguard Worker fields.push({ key: 'message', value: data.message }); 75*61c4878aSAndroid Build Coastguard Worker fields.push({ key: 'py_file', value: data.py_file }); 76*61c4878aSAndroid Build Coastguard Worker fields.push({ key: 'py_logger', value: data.py_logger }); 77*61c4878aSAndroid Build Coastguard Worker this.publishLogEntry({ 78*61c4878aSAndroid Build Coastguard Worker // @ts-ignore 79*61c4878aSAndroid Build Coastguard Worker severity: logLevelToSeverity[data.levelno], 80*61c4878aSAndroid Build Coastguard Worker timestamp: new Date(), 81*61c4878aSAndroid Build Coastguard Worker fields: fields, 82*61c4878aSAndroid Build Coastguard Worker }); 83*61c4878aSAndroid Build Coastguard Worker } 84*61c4878aSAndroid Build Coastguard Worker} 85*61c4878aSAndroid Build Coastguard Worker 86*61c4878aSAndroid Build Coastguard Workerexport async function renderPWConsole(containerEl: HTMLElement, wsUrl = '/ws') { 87*61c4878aSAndroid Build Coastguard Worker const replContainerEl = document.createElement('div'); 88*61c4878aSAndroid Build Coastguard Worker replContainerEl.id = 'repl-container'; 89*61c4878aSAndroid Build Coastguard Worker const logsContainerEl = document.createElement('div'); 90*61c4878aSAndroid Build Coastguard Worker logsContainerEl.id = 'logs-container'; 91*61c4878aSAndroid Build Coastguard Worker createSplitPanel(replContainerEl, logsContainerEl, containerEl, 40); 92*61c4878aSAndroid Build Coastguard Worker 93*61c4878aSAndroid Build Coastguard Worker const ws = new WebSocket(wsUrl); 94*61c4878aSAndroid Build Coastguard Worker const kernel = new WebSocketRPCClient(ws); 95*61c4878aSAndroid Build Coastguard Worker const allLogSourceNames: string[] = await kernel.call('log_source_list', { 96*61c4878aSAndroid Build Coastguard Worker filter: '', 97*61c4878aSAndroid Build Coastguard Worker }); 98*61c4878aSAndroid Build Coastguard Worker const logSources = allLogSourceNames.map( 99*61c4878aSAndroid Build Coastguard Worker (name) => new PwConsoleLogSource(name), 100*61c4878aSAndroid Build Coastguard Worker ); 101*61c4878aSAndroid Build Coastguard Worker logSources.forEach((source, index) => { 102*61c4878aSAndroid Build Coastguard Worker kernel.openStream( 103*61c4878aSAndroid Build Coastguard Worker 'log_source_subscribe', 104*61c4878aSAndroid Build Coastguard Worker { name: allLogSourceNames[index] }, 105*61c4878aSAndroid Build Coastguard Worker (data) => { 106*61c4878aSAndroid Build Coastguard Worker if (data.log_line) 107*61c4878aSAndroid Build Coastguard Worker source.appendLog({ ...data.log_line, time: formatDate(new Date()) }); 108*61c4878aSAndroid Build Coastguard Worker }, 109*61c4878aSAndroid Build Coastguard Worker ); 110*61c4878aSAndroid Build Coastguard Worker }); 111*61c4878aSAndroid Build Coastguard Worker 112*61c4878aSAndroid Build Coastguard Worker const unsubscribe = createLogViewer(logSources, logsContainerEl); 113*61c4878aSAndroid Build Coastguard Worker 114*61c4878aSAndroid Build Coastguard Worker const rpc = new WebSocketRPCReplKernel(kernel); 115*61c4878aSAndroid Build Coastguard Worker const repl = new Repl(rpc, 'Python Shell'); 116*61c4878aSAndroid Build Coastguard Worker replContainerEl.appendChild(repl); 117*61c4878aSAndroid Build Coastguard Worker (window as any).rpc = rpc; 118*61c4878aSAndroid Build Coastguard Worker const resizeObserver = new ResizeObserver((entries) => { 119*61c4878aSAndroid Build Coastguard Worker for (const entry of entries) { 120*61c4878aSAndroid Build Coastguard Worker const splitPanel = document.querySelector('sl-split-panel'); 121*61c4878aSAndroid Build Coastguard Worker if (splitPanel) { 122*61c4878aSAndroid Build Coastguard Worker // Stack vertically for smaller viewport sizes 123*61c4878aSAndroid Build Coastguard Worker splitPanel.vertical = entry.contentRect.width < 800; 124*61c4878aSAndroid Build Coastguard Worker } 125*61c4878aSAndroid Build Coastguard Worker } 126*61c4878aSAndroid Build Coastguard Worker }); 127*61c4878aSAndroid Build Coastguard Worker resizeObserver.observe(document.body); 128*61c4878aSAndroid Build Coastguard Worker return () => { 129*61c4878aSAndroid Build Coastguard Worker unsubscribe(); 130*61c4878aSAndroid Build Coastguard Worker }; 131*61c4878aSAndroid Build Coastguard Worker} 132*61c4878aSAndroid Build Coastguard Worker 133*61c4878aSAndroid Build Coastguard Workerexport function createSplitPanel( 134*61c4878aSAndroid Build Coastguard Worker startEl: HTMLElement, 135*61c4878aSAndroid Build Coastguard Worker endEl: HTMLElement, 136*61c4878aSAndroid Build Coastguard Worker containerEl: HTMLElement, 137*61c4878aSAndroid Build Coastguard Worker initialPosition = 50, 138*61c4878aSAndroid Build Coastguard Worker) { 139*61c4878aSAndroid Build Coastguard Worker const splitPanel = document.createElement('sl-split-panel'); 140*61c4878aSAndroid Build Coastguard Worker 141*61c4878aSAndroid Build Coastguard Worker startEl.setAttribute('slot', 'start'); 142*61c4878aSAndroid Build Coastguard Worker endEl.setAttribute('slot', 'end'); 143*61c4878aSAndroid Build Coastguard Worker splitPanel.setAttribute('position', `${initialPosition}`); 144*61c4878aSAndroid Build Coastguard Worker 145*61c4878aSAndroid Build Coastguard Worker splitPanel.appendChild(startEl); 146*61c4878aSAndroid Build Coastguard Worker splitPanel.appendChild(endEl); 147*61c4878aSAndroid Build Coastguard Worker containerEl.appendChild(splitPanel); 148*61c4878aSAndroid Build Coastguard Worker} 149