1*61c4878aSAndroid Build Coastguard Worker// Copyright 2022 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 {useEffect, useRef, useState} from "react"; 16*61c4878aSAndroid Build Coastguard Workerimport {pw_tokenizer, Device} from "pigweedjs"; 17*61c4878aSAndroid Build Coastguard Workerimport {AutoSizer, Table, Column} from 'react-virtualized'; 18*61c4878aSAndroid Build Coastguard Workerimport {listenToDefaultLogService} from "../common/logService"; 19*61c4878aSAndroid Build Coastguard Workerimport 'react-virtualized/styles.css'; 20*61c4878aSAndroid Build Coastguard Workerimport styles from "../styles/log.module.css"; 21*61c4878aSAndroid Build Coastguard Worker 22*61c4878aSAndroid Build Coastguard Workertype Detokenizer = pw_tokenizer.Detokenizer; 23*61c4878aSAndroid Build Coastguard Worker 24*61c4878aSAndroid Build Coastguard Workerinterface LogProps { 25*61c4878aSAndroid Build Coastguard Worker device: Device | undefined, 26*61c4878aSAndroid Build Coastguard Worker tokenDB: string | undefined 27*61c4878aSAndroid Build Coastguard Worker} 28*61c4878aSAndroid Build Coastguard Worker 29*61c4878aSAndroid Build Coastguard Workerinterface LogEntry { 30*61c4878aSAndroid Build Coastguard Worker msg: string, 31*61c4878aSAndroid Build Coastguard Worker timestamp: number, 32*61c4878aSAndroid Build Coastguard Worker humanTime: string, 33*61c4878aSAndroid Build Coastguard Worker module: string, 34*61c4878aSAndroid Build Coastguard Worker file: string 35*61c4878aSAndroid Build Coastguard Worker} 36*61c4878aSAndroid Build Coastguard Worker 37*61c4878aSAndroid Build Coastguard Workerfunction parseLogMsg(msg: string): LogEntry { 38*61c4878aSAndroid Build Coastguard Worker const pairs = msg.split("■").slice(1).map(pair => pair.split("♦")); 39*61c4878aSAndroid Build Coastguard Worker 40*61c4878aSAndroid Build Coastguard Worker // Not a valid message, print as-is. 41*61c4878aSAndroid Build Coastguard Worker if (pairs.length === 0) { 42*61c4878aSAndroid Build Coastguard Worker return { 43*61c4878aSAndroid Build Coastguard Worker msg, 44*61c4878aSAndroid Build Coastguard Worker module: "", 45*61c4878aSAndroid Build Coastguard Worker file: "", 46*61c4878aSAndroid Build Coastguard Worker timestamp: Date.now(), 47*61c4878aSAndroid Build Coastguard Worker humanTime: new Date(Date.now()).toLocaleTimeString("en-US") 48*61c4878aSAndroid Build Coastguard Worker } 49*61c4878aSAndroid Build Coastguard Worker } 50*61c4878aSAndroid Build Coastguard Worker 51*61c4878aSAndroid Build Coastguard Worker let map: any = {}; 52*61c4878aSAndroid Build Coastguard Worker pairs.forEach(pair => map[pair[0]] = pair[1]) 53*61c4878aSAndroid Build Coastguard Worker return { 54*61c4878aSAndroid Build Coastguard Worker msg: map.msg, 55*61c4878aSAndroid Build Coastguard Worker module: map.module, 56*61c4878aSAndroid Build Coastguard Worker file: map.file, 57*61c4878aSAndroid Build Coastguard Worker timestamp: Date.now(), 58*61c4878aSAndroid Build Coastguard Worker humanTime: new Date(Date.now()).toLocaleTimeString("en-US") 59*61c4878aSAndroid Build Coastguard Worker } 60*61c4878aSAndroid Build Coastguard Worker} 61*61c4878aSAndroid Build Coastguard Worker 62*61c4878aSAndroid Build Coastguard Workerconst keyToDisplayName: {[key: string]: string} = { 63*61c4878aSAndroid Build Coastguard Worker "msg": "Message", 64*61c4878aSAndroid Build Coastguard Worker "humanTime": "Time", 65*61c4878aSAndroid Build Coastguard Worker "module": "Module", 66*61c4878aSAndroid Build Coastguard Worker "file": "File" 67*61c4878aSAndroid Build Coastguard Worker} 68*61c4878aSAndroid Build Coastguard Worker 69*61c4878aSAndroid Build Coastguard Workerexport default function Log({device, tokenDB}: LogProps) { 70*61c4878aSAndroid Build Coastguard Worker const [logs, setLogs] = useState<LogEntry[]>([]); 71*61c4878aSAndroid Build Coastguard Worker const [detokenizer, setDetokenizer] = useState<Detokenizer | null>(null); 72*61c4878aSAndroid Build Coastguard Worker const logTable = useRef<Table | null>(null); 73*61c4878aSAndroid Build Coastguard Worker const _headerRenderer = ({dataKey, sortBy, sortDirection}: any) => { 74*61c4878aSAndroid Build Coastguard Worker return ( 75*61c4878aSAndroid Build Coastguard Worker <div> 76*61c4878aSAndroid Build Coastguard Worker {keyToDisplayName[dataKey]} 77*61c4878aSAndroid Build Coastguard Worker </div> 78*61c4878aSAndroid Build Coastguard Worker ); 79*61c4878aSAndroid Build Coastguard Worker } 80*61c4878aSAndroid Build Coastguard Worker 81*61c4878aSAndroid Build Coastguard Worker const processFrame = (frame: Uint8Array) => { 82*61c4878aSAndroid Build Coastguard Worker if (detokenizer) { 83*61c4878aSAndroid Build Coastguard Worker const detokenized = detokenizer.detokenizeUint8Array(frame); 84*61c4878aSAndroid Build Coastguard Worker setLogs(oldLogs => [...oldLogs, parseLogMsg(detokenized)]); 85*61c4878aSAndroid Build Coastguard Worker } 86*61c4878aSAndroid Build Coastguard Worker else { 87*61c4878aSAndroid Build Coastguard Worker const decoded = new TextDecoder().decode(frame); 88*61c4878aSAndroid Build Coastguard Worker setLogs(oldLogs => [...oldLogs, parseLogMsg(decoded)]); 89*61c4878aSAndroid Build Coastguard Worker } 90*61c4878aSAndroid Build Coastguard Worker setTimeout(() => { 91*61c4878aSAndroid Build Coastguard Worker logTable.current!.scrollToRow(logs.length - 1); 92*61c4878aSAndroid Build Coastguard Worker }, 100); 93*61c4878aSAndroid Build Coastguard Worker } 94*61c4878aSAndroid Build Coastguard Worker 95*61c4878aSAndroid Build Coastguard Worker useEffect(() => { 96*61c4878aSAndroid Build Coastguard Worker if (device) { 97*61c4878aSAndroid Build Coastguard Worker let cleanupFn: () => void; 98*61c4878aSAndroid Build Coastguard Worker listenToDefaultLogService(device, processFrame).then((unsub) => cleanupFn = unsub); 99*61c4878aSAndroid Build Coastguard Worker return () => { 100*61c4878aSAndroid Build Coastguard Worker if (cleanupFn) cleanupFn(); 101*61c4878aSAndroid Build Coastguard Worker } 102*61c4878aSAndroid Build Coastguard Worker } 103*61c4878aSAndroid Build Coastguard Worker }, [device, detokenizer]); 104*61c4878aSAndroid Build Coastguard Worker 105*61c4878aSAndroid Build Coastguard Worker useEffect(() => { 106*61c4878aSAndroid Build Coastguard Worker if (tokenDB && tokenDB.length > 0) { 107*61c4878aSAndroid Build Coastguard Worker const detokenizer = new pw_tokenizer.Detokenizer(tokenDB); 108*61c4878aSAndroid Build Coastguard Worker setDetokenizer(detokenizer); 109*61c4878aSAndroid Build Coastguard Worker } 110*61c4878aSAndroid Build Coastguard Worker }, [tokenDB]) 111*61c4878aSAndroid Build Coastguard Worker 112*61c4878aSAndroid Build Coastguard Worker return ( 113*61c4878aSAndroid Build Coastguard Worker <> 114*61c4878aSAndroid Build Coastguard Worker {/* @ts-ignore */} 115*61c4878aSAndroid Build Coastguard Worker <AutoSizer> 116*61c4878aSAndroid Build Coastguard Worker {({height, width}) => ( 117*61c4878aSAndroid Build Coastguard Worker <> 118*61c4878aSAndroid Build Coastguard Worker {/* @ts-ignore */} 119*61c4878aSAndroid Build Coastguard Worker <Table 120*61c4878aSAndroid Build Coastguard Worker className={styles.logsContainer} 121*61c4878aSAndroid Build Coastguard Worker headerHeight={40} 122*61c4878aSAndroid Build Coastguard Worker height={height} 123*61c4878aSAndroid Build Coastguard Worker rowCount={logs.length} 124*61c4878aSAndroid Build Coastguard Worker rowGetter={({index}) => logs[index]} 125*61c4878aSAndroid Build Coastguard Worker rowHeight={30} 126*61c4878aSAndroid Build Coastguard Worker ref={logTable} 127*61c4878aSAndroid Build Coastguard Worker width={width} 128*61c4878aSAndroid Build Coastguard Worker > 129*61c4878aSAndroid Build Coastguard Worker {/* @ts-ignore */} 130*61c4878aSAndroid Build Coastguard Worker <Column 131*61c4878aSAndroid Build Coastguard Worker dataKey="humanTime" 132*61c4878aSAndroid Build Coastguard Worker width={190} 133*61c4878aSAndroid Build Coastguard Worker headerRenderer={_headerRenderer} 134*61c4878aSAndroid Build Coastguard Worker /> 135*61c4878aSAndroid Build Coastguard Worker {/* @ts-ignore */} 136*61c4878aSAndroid Build Coastguard Worker <Column 137*61c4878aSAndroid Build Coastguard Worker dataKey="msg" 138*61c4878aSAndroid Build Coastguard Worker flexGrow={1} 139*61c4878aSAndroid Build Coastguard Worker width={290} 140*61c4878aSAndroid Build Coastguard Worker headerRenderer={_headerRenderer} 141*61c4878aSAndroid Build Coastguard Worker /> 142*61c4878aSAndroid Build Coastguard Worker {/* @ts-ignore */} 143*61c4878aSAndroid Build Coastguard Worker <Column 144*61c4878aSAndroid Build Coastguard Worker dataKey="module" 145*61c4878aSAndroid Build Coastguard Worker width={190} 146*61c4878aSAndroid Build Coastguard Worker headerRenderer={_headerRenderer} 147*61c4878aSAndroid Build Coastguard Worker /> 148*61c4878aSAndroid Build Coastguard Worker {/* @ts-ignore */} 149*61c4878aSAndroid Build Coastguard Worker <Column 150*61c4878aSAndroid Build Coastguard Worker dataKey="file" 151*61c4878aSAndroid Build Coastguard Worker flexGrow={1} 152*61c4878aSAndroid Build Coastguard Worker width={190} 153*61c4878aSAndroid Build Coastguard Worker headerRenderer={_headerRenderer} 154*61c4878aSAndroid Build Coastguard Worker /> 155*61c4878aSAndroid Build Coastguard Worker </Table> 156*61c4878aSAndroid Build Coastguard Worker </> 157*61c4878aSAndroid Build Coastguard Worker )} 158*61c4878aSAndroid Build Coastguard Worker </AutoSizer> 159*61c4878aSAndroid Build Coastguard Worker </> 160*61c4878aSAndroid Build Coastguard Worker ) 161*61c4878aSAndroid Build Coastguard Worker} 162