11package ocpp2
22
33import (
4+ "context"
45 "fmt"
56 "reflect"
67
7- "github.com/lorenzodonini/ocpp-go/internal/callbackqueue"
88 "github.com/lorenzodonini/ocpp-go/ocpp"
99 "github.com/lorenzodonini/ocpp-go/ocpp2.0.1/authorization"
1010 "github.com/lorenzodonini/ocpp-go/ocpp2.0.1/availability"
@@ -44,9 +44,6 @@ type chargingStation struct {
4444 diagnosticsHandler diagnostics.ChargingStationHandler
4545 displayHandler display.ChargingStationHandler
4646 dataHandler data.ChargingStationHandler
47- responseHandler chan ocpp.Response
48- errorHandler chan error
49- callbacks callbackqueue.CallbackQueue
5047 stopC chan struct {}
5148 errC chan error // external error channel
5249}
@@ -65,10 +62,15 @@ func (cs *chargingStation) Errors() <-chan error {
6562 return cs .errC
6663}
6764
68- // Callback invoked whenever a queued request is canceled, due to timeout.
69- // By default, the callback returns a GenericError to the caller, who sent the original request.
70- func (cs * chargingStation ) onRequestTimeout (_ string , _ ocpp.Request , err * ocpp.Error ) {
71- cs .errorHandler <- err
65+ // Function invoked whenever a Response or an error is received for a request.
66+ // The callback includes the user-defined callback function, which may be used to handle the response accordingly.
67+ // A reference to the original request is attached to the function, so that the user may identify the request.
68+ func (cs * chargingStation ) onResponse (requestID string , response ocpp.Response , err error , callback ocpp.Callback ) {
69+ if callback == nil {
70+ cs .error (fmt .Errorf ("no callback defined for response to request %v, dropping response" , requestID ))
71+ return
72+ }
73+ callback (response , err )
7274}
7375
7476func (cs * chargingStation ) BootNotification (reason provisioning.BootReason , model string , vendor string , props ... func (request * provisioning.BootNotificationRequest )) (* provisioning.BootNotificationResponse , error ) {
@@ -461,10 +463,17 @@ func (cs *chargingStation) SetDataHandler(handler data.ChargingStationHandler) {
461463}
462464
463465func (cs * chargingStation ) SendRequest (request ocpp.Request ) (ocpp.Response , error ) {
466+ return cs .SendRequestWithContext (request , context .TODO ())
467+ }
468+
469+ func (cs * chargingStation ) SendRequestWithContext (request ocpp.Request , ctx context.Context ) (ocpp.Response , error ) {
464470 featureName := request .GetFeatureName ()
465471 if _ , found := cs .client .GetProfileForFeature (featureName ); ! found {
466472 return nil , fmt .Errorf ("feature %v is unsupported on charging station (missing profile), cannot send request" , featureName )
467473 }
474+ if ctx == nil {
475+ ctx = context .TODO ()
476+ }
468477
469478 // Wraps an asynchronous response
470479 type asyncResponse struct {
@@ -473,23 +482,36 @@ func (cs *chargingStation) SendRequest(request ocpp.Request) (ocpp.Response, err
473482 }
474483 // Create channel and pass it to a callback function, for retrieving asynchronous response
475484 asyncResponseC := make (chan asyncResponse , 1 )
476- send := func () error {
477- return cs .client .SendRequest (request )
485+ callback := func (response ocpp.Response , err error ) {
486+ if ctx .Err () != nil {
487+ // Request was canceled already, ignore callback.
488+ // Response will be handled by select statement below.
489+ return
490+ }
491+ asyncResponseC <- asyncResponse {response , err }
478492 }
479- err := cs .callbacks .TryQueue ("main" , send , func (confirmation ocpp.Response , err error ) {
480- asyncResponseC <- asyncResponse {r : confirmation , e : err }
481- })
493+ // Send request, then start blocking wait for asynchronous response
494+ _ , err := cs .client .SendRequest (request , callback , ctx )
482495 if err != nil {
483496 return nil , err
484497 }
485- asyncResult , ok := <- asyncResponseC
486- if ! ok {
487- return nil , fmt .Errorf ("internal error while receiving result for %v request" , request .GetFeatureName ())
498+ select {
499+ case asyncResult , ok := <- asyncResponseC :
500+ if ! ok {
501+ return nil , fmt .Errorf ("internal error while receiving result for %s request" , request .GetFeatureName ())
502+ }
503+ return asyncResult .r , asyncResult .e
504+ case <- ctx .Done ():
505+ // Request was canceled by user. Return an error and don't handle callbacks any longer
506+ return nil , fmt .Errorf ("request timed out/canceled by user. Any incoming response will be ignored" )
488507 }
489- return asyncResult .r , asyncResult .e
490508}
491509
492- func (cs * chargingStation ) SendRequestAsync (request ocpp.Request , callback func (response ocpp.Response , err error )) error {
510+ func (cs * chargingStation ) SendRequestAsync (request ocpp.Request , callback ocpp.Callback ) error {
511+ return cs .SendRequestAsyncWithContext (request , callback , context .TODO ())
512+ }
513+
514+ func (cs * chargingStation ) SendRequestAsyncWithContext (request ocpp.Request , callback ocpp.Callback , ctx context.Context ) error {
493515 featureName := request .GetFeatureName ()
494516 if _ , found := cs .client .GetProfileForFeature (featureName ); ! found {
495517 return fmt .Errorf ("feature %v is unsupported on charging station (missing profile), cannot send request" , featureName )
@@ -524,37 +546,14 @@ func (cs *chargingStation) SendRequestAsync(request ocpp.Request, callback func(
524546 default :
525547 return fmt .Errorf ("unsupported action %v on charging station, cannot send request" , featureName )
526548 }
527- // Response will be retrieved asynchronously via asyncHandler
528- send := func () error {
529- return cs .client .SendRequest (request )
549+ if ctx == nil {
550+ ctx = context .TODO ()
530551 }
531- err := cs .callbacks .TryQueue ("main" , send , callback )
552+ // Response will be handled asynchronously and passed to the callback function
553+ _ , err := cs .client .SendRequest (request , callback , ctx )
532554 return err
533555}
534556
535- func (cs * chargingStation ) asyncCallbackHandler () {
536- for {
537- select {
538- case confirmation := <- cs .responseHandler :
539- // Get and invoke callback
540- if callback , ok := cs .callbacks .Dequeue ("main" ); ok {
541- callback (confirmation , nil )
542- } else {
543- cs .error (fmt .Errorf ("no callback available for incoming response %v" , confirmation .GetFeatureName ()))
544- }
545- case protoError := <- cs .errorHandler :
546- // Get and invoke callback
547- if callback , ok := cs .callbacks .Dequeue ("main" ); ok {
548- callback (nil , protoError )
549- } else {
550- cs .error (fmt .Errorf ("no callback available for incoming error %w" , protoError ))
551- }
552- case <- cs .stopC :
553- return
554- }
555- }
556- }
557-
558557func (cs * chargingStation ) sendResponse (response ocpp.Response , err error , requestId string ) {
559558 if err != nil {
560559 // Send error response
0 commit comments