1.. _module-pw_rpc-ts: 2 3----------------- 4pw_rpc web module 5----------------- 6.. pigweed-module-subpage:: 7 :name: pw_rpc 8 9The ``pw_rpc`` module makes it possible to call Pigweed RPCs from 10TypeScript or JavaScript. The module includes client library to facilitate handling RPCs. 11 12This module is currently a work in progress. 13 14Creating an RPC Client 15====================== 16The RPC client is instantiated from a list of channels and a set of protos. 17 18.. code-block:: typescript 19 20 import { ProtoCollection } from 'pigweedjs/protos/collection'; 21 22 const channels = [new Channel(1, savePacket), new Channel(5)]; 23 const client = Client.fromProtoSet(channels, new ProtoCollection()); 24 25 function savePacket(packetBytes: Uint8Array): void { 26 const packet = RpcPacket.deserializeBinary(packetBytes); 27 ... 28 } 29 30To generate a ProtoSet/ProtoCollection from your own ``.proto`` files, use 31``pw_proto_compiler`` in your ``package.json`` like this: 32 33.. code-block:: javascript 34 35 ... 36 "scripts": { 37 "build-protos": "pw_proto_compiler -p protos/rpc1.proto -p protos/rpc2.proto --out dist/protos", 38 39This will generate a `collection.js` file which can be used similar to above 40example. 41 42Finding an RPC Method 43===================== 44Once the client is instantiated with the correct proto library, the target RPC 45method is found by searching based on the full name: 46``{packageName}.{serviceName}.{methodName}`` 47 48.. code-block:: typescript 49 50 const channel = client.channel()!; 51 unaryStub = channel.methodStub('pw.rpc.test1.TheTestService.SomeUnary')! 52 as UnaryMethodStub; 53 54The four possible RPC stubs are ``UnaryMethodStub``, 55``ServerStreamingMethodStub``, ``ClientStreamingMethodStub``, and 56``BidirectionalStreamingMethodStub``. Note that ``channel.methodStub()`` 57returns a general stub. Since each stub type has different invoke 58parameters, the general stub should be typecast before using. 59 60Invoke an RPC with callbacks 61============================ 62 63.. code-block:: typescript 64 65 invoke(request?: Message, 66 onNext: Callback = () => {}, 67 onCompleted: Callback = () => {}, 68 onError: Callback = () => {}): Call 69 70All RPC methods can be invoked with a set of callbacks that are triggered when 71either a response is received, the RPC is completed, or an error occurs. The 72example below demonstrates registering these callbacks on a Bidirectional RPC. 73Other RPC types can be invoked in a similar fashion. The request parameter may 74differ slightly between RPC types. 75 76.. code-block:: typescript 77 78 bidiRpc = client.channel()?.methodStub( 79 'pw.rpc.test1.TheTestService.SomeBidi')! 80 as BidirectionalStreamingMethodStub; 81 82 // Configure callback functions 83 const onNext = (response: Message) => { 84 console.log(response); 85 } 86 const onComplete = (status: Status) => { 87 console.log('completed!'); 88 } 89 const onError = (error: Error) => { 90 console.log(); 91 } 92 93 bidiRpc.invoke(request, onNext, onComplete, onError); 94 95Open an RPC: ignore initial errors 96===================================== 97 98Open allows you to start and register an RPC without crashing on errors. This 99is useful for starting an RPC before the server is ready. For instance, starting 100a logging RPC while the device is booting. 101 102.. code-block:: typescript 103 104 open(request?: Message, 105 onNext: Callback = () => {}, 106 onCompleted: Callback = () => {}, 107 onError: Callback = () => {}): Call 108 109Blocking RPCs: promise API 110========================== 111 112Each MethodStub type provides an call() function that allows sending requests 113and awaiting responses through the promise API. The timeout field is optional. 114If no timeout is specified, the RPC will wait indefinitely. 115 116Unary RPC 117--------- 118.. code-block:: typescript 119 120 unaryRpc = client.channel()?.methodStub( 121 'pw.rpc.test1.TheTestService.SomeUnary')! 122 as UnaryMethodStub; 123 const request = new unaryRpc.requestType(); 124 request.setFooProperty(4); 125 const timeout = 2000 // 2 seconds 126 const [status, response] = await unaryRpc.call(request, timeout); 127 128Server Streaming RPC 129-------------------- 130.. code-block:: typescript 131 132 serverStreamRpc = client.channel()?.methodStub( 133 'pw.rpc.test1.TheTestService.SomeServerStreaming')! 134 as ServerStreamingMethodStub; 135 136 const call = serverStreamRpc.invoke(); 137 const timeout = 2000 138 for await (const response of call.getResponses(2, timeout)) { 139 console.log(response); 140 } 141 const responses = call.getResponse() // All responses until stream end. 142 while (!responses.done) { 143 console.log(await responses.value()); 144 } 145 146 147Client Streaming RPC 148-------------------- 149.. code-block:: typescript 150 151 clientStreamRpc = client.channel()!.methodStub( 152 'pw.rpc.test1.TheTestService.SomeClientStreaming')! 153 as ClientStreamingMethodStub; 154 clientStreamRpc.invoke(); 155 const request = new clientStreamRpc.method.requestType(); 156 request.setFooProperty('foo_test'); 157 clientStreamRpc.send(request); 158 159 // Send three more requests, end the stream, and wait for a response. 160 const timeout = 2000 // 2 seconds 161 request.finishAndWait([request, request, request], timeout) 162 .then(() => { 163 console.log('Client stream finished successfully'); 164 }) 165 .catch((reason) => { 166 console.log(`Client stream error: ${reason}`); 167 }); 168 169Bidirectional Stream RPC 170------------------------ 171.. code-block:: typescript 172 173 bidiStreamingRpc = client.channel()!.methodStub( 174 'pw.rpc.test1.TheTestService.SomeBidiStreaming')! 175 as BidirectionalStreamingMethodStub; 176 bidiStreamingRpc.invoke(); 177 const request = new bidiStreamingRpc.method.requestType(); 178 request.setFooProperty('foo_test'); 179 180 // Send requests 181 bidiStreamingRpc.send(request); 182 183 // Receive responses 184 const timeout = 2000 // 2 seconds 185 for await (const response of call.getResponses(1, timeout)) { 186 console.log(response); 187 } 188 189 // Send three more requests, end the stream, and wait for a response. 190 request.finishAndWait([request, request, request], timeout) 191 .then(() => { 192 console.log('Bidirectional stream finished successfully'); 193 }) 194 .catch((reason) => { 195 console.log(`Bidirectional stream error: ${reason}`); 196 }); 197