1*61c4878aSAndroid Build Coastguard Worker// Copyright 2023 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 { Detokenizer } from '../pw_tokenizer/ts'; 16*61c4878aSAndroid Build Coastguard Workerimport { LogSource } from '../pw_web/log-viewer/src/log-source'; 17*61c4878aSAndroid Build Coastguard Workerimport { Device } from './device'; 18*61c4878aSAndroid Build Coastguard Workerimport { LogEntry } from './logging'; 19*61c4878aSAndroid Build Coastguard Worker 20*61c4878aSAndroid Build Coastguard Workerexport class PigweedRPCLogSource extends LogSource { 21*61c4878aSAndroid Build Coastguard Worker private detokenizer: Detokenizer | undefined; 22*61c4878aSAndroid Build Coastguard Worker private logs: LogEntry[] = []; 23*61c4878aSAndroid Build Coastguard Worker private call: any; 24*61c4878aSAndroid Build Coastguard Worker constructor(device: Device, tokenDB: string | undefined) { 25*61c4878aSAndroid Build Coastguard Worker super(); 26*61c4878aSAndroid Build Coastguard Worker if (tokenDB && tokenDB.length > 0) { 27*61c4878aSAndroid Build Coastguard Worker this.detokenizer = new Detokenizer(tokenDB); 28*61c4878aSAndroid Build Coastguard Worker } 29*61c4878aSAndroid Build Coastguard Worker this.call = device.rpcs.pw.log.Logs.Listen((msg: any) => { 30*61c4878aSAndroid Build Coastguard Worker msg 31*61c4878aSAndroid Build Coastguard Worker .getEntriesList() 32*61c4878aSAndroid Build Coastguard Worker .forEach((entry: any) => this.processFrame(entry.getMessage())); 33*61c4878aSAndroid Build Coastguard Worker }); 34*61c4878aSAndroid Build Coastguard Worker } 35*61c4878aSAndroid Build Coastguard Worker 36*61c4878aSAndroid Build Coastguard Worker destroy() { 37*61c4878aSAndroid Build Coastguard Worker this.call.cancel(); 38*61c4878aSAndroid Build Coastguard Worker } 39*61c4878aSAndroid Build Coastguard Worker 40*61c4878aSAndroid Build Coastguard Worker private processFrame(frame: Uint8Array) { 41*61c4878aSAndroid Build Coastguard Worker let entry: LogEntry; 42*61c4878aSAndroid Build Coastguard Worker if (this.detokenizer) { 43*61c4878aSAndroid Build Coastguard Worker const detokenized = this.detokenizer.detokenizeUint8Array(frame); 44*61c4878aSAndroid Build Coastguard Worker entry = this.parseLogMsg(detokenized); 45*61c4878aSAndroid Build Coastguard Worker } else { 46*61c4878aSAndroid Build Coastguard Worker const decoded = new TextDecoder().decode(frame); 47*61c4878aSAndroid Build Coastguard Worker entry = this.parseLogMsg(decoded); 48*61c4878aSAndroid Build Coastguard Worker } 49*61c4878aSAndroid Build Coastguard Worker this.logs = [...this.logs, entry]; 50*61c4878aSAndroid Build Coastguard Worker this.publishLogEntry(entry); 51*61c4878aSAndroid Build Coastguard Worker } 52*61c4878aSAndroid Build Coastguard Worker 53*61c4878aSAndroid Build Coastguard Worker private parseLogMsg(msg: string): LogEntry { 54*61c4878aSAndroid Build Coastguard Worker const pairs = msg 55*61c4878aSAndroid Build Coastguard Worker .split('■') 56*61c4878aSAndroid Build Coastguard Worker .slice(1) 57*61c4878aSAndroid Build Coastguard Worker .map((pair) => pair.split('♦')); 58*61c4878aSAndroid Build Coastguard Worker 59*61c4878aSAndroid Build Coastguard Worker // Not a valid message, print as-is. 60*61c4878aSAndroid Build Coastguard Worker const timestamp = new Date(); 61*61c4878aSAndroid Build Coastguard Worker if (pairs.length === 0) { 62*61c4878aSAndroid Build Coastguard Worker return { 63*61c4878aSAndroid Build Coastguard Worker fields: [ 64*61c4878aSAndroid Build Coastguard Worker { key: 'timestamp', value: timestamp.toISOString() }, 65*61c4878aSAndroid Build Coastguard Worker { key: 'message', value: msg }, 66*61c4878aSAndroid Build Coastguard Worker ], 67*61c4878aSAndroid Build Coastguard Worker timestamp: timestamp, 68*61c4878aSAndroid Build Coastguard Worker }; 69*61c4878aSAndroid Build Coastguard Worker } 70*61c4878aSAndroid Build Coastguard Worker 71*61c4878aSAndroid Build Coastguard Worker const map: any = {}; 72*61c4878aSAndroid Build Coastguard Worker pairs.forEach((pair) => (map[pair[0]] = pair[1])); 73*61c4878aSAndroid Build Coastguard Worker return { 74*61c4878aSAndroid Build Coastguard Worker fields: [ 75*61c4878aSAndroid Build Coastguard Worker { key: 'timestamp', value: timestamp }, 76*61c4878aSAndroid Build Coastguard Worker { key: 'message', value: map.msg }, 77*61c4878aSAndroid Build Coastguard Worker { key: 'module', value: map.module }, 78*61c4878aSAndroid Build Coastguard Worker { key: 'file', value: map.file }, 79*61c4878aSAndroid Build Coastguard Worker ], 80*61c4878aSAndroid Build Coastguard Worker timestamp: timestamp, 81*61c4878aSAndroid Build Coastguard Worker }; 82*61c4878aSAndroid Build Coastguard Worker } 83*61c4878aSAndroid Build Coastguard Worker} 84