| // Copyright 2024 The Pigweed Authors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| // use this file except in compliance with the License. You may obtain a copy of |
| // the License at |
| // |
| // https://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| |
| import { WebSerial, pw_hdlc, pw_rpc, pw_status } from "pigweedjs"; |
| import { ProtoCollection } from "../../protos/collection/collection"; |
| import { BlinkRequest } from "../../protos/collection/blinky/blinky_pb"; |
| import { |
| OnboardTempStreamRequest, |
| OnboardTempResponse, |
| } from "../../protos/collection/board/board_pb"; |
| import { |
| MeasureStreamRequest, |
| Measurement, |
| } from "../../protos/collection/air_sensor/air_sensor_pb"; |
| import { State } from "../../protos/collection/state_manager/state_manager_pb"; |
| class RPCService { |
| transport; |
| decoder; |
| encoder; |
| rpcAddress; |
| channels; |
| client; |
| blinkService; |
| boardTempService; |
| measureService; |
| stateService; |
| constructor(rpcAddress = 82) { |
| this.transport = new WebSerial.WebSerialTransport(); |
| this.decoder = new pw_hdlc.Decoder(); |
| this.encoder = new pw_hdlc.Encoder(); |
| this.rpcAddress = rpcAddress; |
| |
| this.channels = [ |
| new pw_rpc.Channel(1, (bytes: Uint8Array) => { |
| const hdlcBytes = this.encoder.uiFrame(this.rpcAddress, bytes); |
| this.transport.sendChunk(hdlcBytes); |
| }), |
| ]; |
| this.client = pw_rpc.Client.fromProtoSet( |
| this.channels, |
| new ProtoCollection(), |
| ); |
| this.blinkService = this.client.channel().methodStub("blinky.Blinky.Blink"); |
| this.boardTempService = this.client |
| .channel() |
| .methodStub("board.Board.OnboardTempStream"); |
| this.measureService = this.client |
| .channel() |
| .methodStub("air_sensor.AirSensor.MeasureStream"); |
| this.stateService = this.client |
| .channel() |
| .methodStub("state_manager.StateManager.GetState"); |
| } |
| |
| async connect() { |
| await this.transport.connect(); |
| |
| this.transport.chunks.subscribe((item: any) => { |
| const decoded = this.decoder.process(item); |
| for (const frame of decoded) { |
| if (frame.address === this.rpcAddress) { |
| this.client.processPacket(frame.data); |
| } |
| } |
| }); |
| } |
| |
| async disconnect() { |
| await this.transport.disconnect(); |
| } |
| |
| async blink(times: number, intervalMs: number = 300) { |
| const req = new BlinkRequest(); |
| req.setBlinkCount(times); |
| req.setIntervalMs(intervalMs); |
| const [status, response] = await this.blinkService.call(req); |
| } |
| |
| async streamTemp(onTemp?: (temp: number) => void) { |
| const req = new OnboardTempStreamRequest(); |
| req.setSampleIntervalMs(500); |
| await this.boardTempService.invoke( |
| req, |
| (m: OnboardTempResponse) => { |
| console.log(m.toObject()); |
| if (onTemp) onTemp(m.getTemp()); |
| }, |
| undefined, |
| (err) => { |
| console.error(err); |
| }, |
| ); |
| } |
| |
| async streamMeasure(onMeasure?: (measurement: Measurement) => void) { |
| // We check if this RPC exists on device, |
| // if not we fallback to just onboardTemp. |
| return new Promise(async (resolve, reject) => { |
| let resolveCalled = false; |
| const req = new MeasureStreamRequest(); |
| req.setSampleIntervalMs(500); |
| await this.measureService.invoke( |
| req, |
| (m: Measurement) => { |
| if (!resolveCalled) { |
| resolveCalled = true; |
| resolve(true); |
| } |
| if (onMeasure) onMeasure(m); |
| }, |
| undefined, |
| (status: pw_status.Status) => { |
| if ((status = pw_status.Status.NOT_FOUND)) { |
| reject(status); |
| } |
| }, |
| ); |
| }); |
| } |
| |
| async getState(): Promise<State> { |
| const [status, response] = await this.stateService.call(); |
| return response; |
| } |
| } |
| |
| // We keep a singleton of this service. |
| const rpc = new RPCService(); |
| |
| export function getRpcService(): RPCService { |
| return rpc; |
| } |