@@ -110,57 +110,20 @@ export default class Service<
110110
111111 this . ros . callOnConnection ( call ) ;
112112 }
113+
113114 /**
114- * Advertise the service. This turns the Service object from a client
115- * into a server. The callback will be called with every request
116- * that's made on this service.
117- *
118- * @param callback This works similarly to the callback for a C++ service in that you should take care not to overwrite the response object.
119- * Instead, only modify the values within.
115+ * Common logic for advertising a service
120116 */
121- async advertise (
122- callback : ( request : TRequest , response : Partial < TResponse > ) => boolean ,
117+ #advertiseWithCallback (
118+ callbackWrapper : ( bridgeMessage : AnyServiceOp < TRequest , TResponse > ) => void ,
123119 ) : Promise < void > {
124- // Queue this operation to prevent race conditions
125120 this . #operationQueue = this . #operationQueue
126121 . then ( ( ) => {
127- // If already advertised, unadvertise first
128122 if ( this . isAdvertised ) {
129123 this . #doUnadvertise( ) ;
130124 }
131125
132- // Store the new callback for removal during un-advertisement
133- this . #serviceCallback = ( rosbridgeRequest ) => {
134- if ( ! isRosbridgeCallServiceMessage < TRequest > ( rosbridgeRequest ) ) {
135- throw new Error (
136- `Invalid message received on service channel: ${ JSON . stringify ( rosbridgeRequest ) } ` ,
137- ) ;
138- }
139- const response = { } ;
140- let success : boolean ;
141- try {
142- success = callback ( rosbridgeRequest . args , response ) ;
143- } catch {
144- success = false ;
145- }
146-
147- if ( success ) {
148- this . ros . callOnConnection ( {
149- op : "service_response" ,
150- service : this . name ,
151- values : response ,
152- result : success ,
153- id : rosbridgeRequest . id ,
154- } satisfies RosbridgeServiceResponseMessage < Partial < TResponse > > ) ;
155- } else {
156- this . ros . callOnConnection ( {
157- op : "service_response" ,
158- service : this . name ,
159- result : success ,
160- id : rosbridgeRequest . id ,
161- } satisfies RosbridgeServiceResponseMessage < Partial < TResponse > > ) ;
162- }
163- } ;
126+ this . #serviceCallback = callbackWrapper ;
164127
165128 this . ros . on ( this . name , this . #serviceCallback) ;
166129 this . ros . callOnConnection ( {
@@ -178,6 +141,53 @@ export default class Service<
178141 return this . #operationQueue;
179142 }
180143
144+ /**
145+ * Advertise the service. This turns the Service object from a client
146+ * into a server. The callback will be called with every request
147+ * that's made on this service.
148+ *
149+ * @param callback This works similarly to the callback for a C++ service in that you should take care not to overwrite the response object.
150+ * Instead, only modify the values within.
151+ */
152+ advertise (
153+ callback : ( request : TRequest , response : Partial < TResponse > ) => boolean ,
154+ ) : Promise < void > {
155+ return this . #advertiseWithCallback( ( bridgeMessage ) => {
156+ if ( bridgeMessage . op !== "call_service" ) {
157+ throw new Error (
158+ `Invalid message received on service channel: ${ JSON . stringify ( bridgeMessage ) } ` ,
159+ ) ;
160+ }
161+
162+ const request = bridgeMessage as IncomingCallServiceOp < TRequest > ;
163+ const response : Partial < TResponse > = { } ;
164+
165+ let success : boolean ;
166+ try {
167+ success = callback ( request . args , response ) ;
168+ } catch {
169+ success = false ;
170+ }
171+
172+ if ( success ) {
173+ this . ros . callOnConnection ( {
174+ op : "service_response" ,
175+ service : this . name ,
176+ values : response ,
177+ result : success ,
178+ id : request . id ,
179+ } ) ;
180+ } else {
181+ this . ros . callOnConnection ( {
182+ op : "service_response" ,
183+ service : this . name ,
184+ result : success ,
185+ id : request . id ,
186+ } ) ;
187+ }
188+ } ) ;
189+ }
190+
181191 /**
182192 * Internal method to perform unadvertisement without queueing
183193 */
@@ -233,56 +243,37 @@ export default class Service<
233243 * An alternate form of Service advertisement that supports a modern Promise-based interface for use with async/await.
234244 * @param callback An asynchronous callback processing the request and returning a response.
235245 */
236- async advertiseAsync (
246+ advertiseAsync (
237247 callback : ( request : TRequest ) => Promise < TResponse > ,
238248 ) : Promise < void > {
239- // Queue this operation to prevent race conditions
240- this . #operationQueue = this . #operationQueue
241- . then ( ( ) => {
242- // If already advertised, unadvertise first
243- if ( this . isAdvertised ) {
244- this . #doUnadvertise( ) ;
245- }
249+ return this . #advertiseWithCallback( ( bridgeMessage ) => {
250+ if ( bridgeMessage . op !== "call_service" ) {
251+ throw new Error (
252+ `Invalid message received on service channel: ${ JSON . stringify ( bridgeMessage ) } ` ,
253+ ) ;
254+ }
246255
247- this . #serviceCallback = ( rosbridgeRequest ) => {
248- if ( ! isRosbridgeCallServiceMessage < TRequest > ( rosbridgeRequest ) ) {
249- throw new Error (
250- `Invalid message received on service channel: ${ JSON . stringify ( rosbridgeRequest ) } ` ,
251- ) ;
252- }
253- ( async ( ) => {
254- try {
255- this . ros . callOnConnection ( {
256- op : "service_response" ,
257- service : this . name ,
258- result : true ,
259- values : await callback ( rosbridgeRequest . args ) ,
260- id : rosbridgeRequest . id ,
261- } satisfies RosbridgeServiceResponseMessage < TResponse > ) ;
262- } catch ( err ) {
263- this . ros . callOnConnection ( {
264- op : "service_response" ,
265- service : this . name ,
266- result : false ,
267- values : String ( err ) ,
268- id : rosbridgeRequest . id ,
269- } satisfies RosbridgeServiceResponseMessage < TResponse > ) ;
270- }
271- } ) ( ) . catch ( console . error ) ;
272- } ;
273- this . ros . on ( this . name , this . #serviceCallback) ;
274- this . ros . callOnConnection ( {
275- op : "advertise_service" ,
276- type : this . serviceType ,
277- service : this . name ,
278- } ) ;
279- this . isAdvertised = true ;
280- } )
281- . catch ( ( err : unknown ) => {
282- this . emit ( "error" , err ) ;
283- throw err ;
284- } ) ;
256+ const request = bridgeMessage as IncomingCallServiceOp < TRequest > ;
285257
286- return this . #operationQueue;
258+ ( async ( ) => {
259+ try {
260+ this . ros . callOnConnection ( {
261+ op : "service_response" ,
262+ service : this . name ,
263+ result : true ,
264+ values : await callback ( request . args ) ,
265+ id : request . id ,
266+ } ) ;
267+ } catch ( err ) {
268+ this . ros . callOnConnection ( {
269+ op : "service_response" ,
270+ service : this . name ,
271+ result : false ,
272+ values : String ( err ) ,
273+ id : request . id ,
274+ } ) ;
275+ }
276+ } ) ( ) . catch ( console . error ) ;
277+ } ) ;
287278 }
288279}
0 commit comments