From da1dc68b35cb803c37c8574b794b51b92143d037 Mon Sep 17 00:00:00 2001 From: Blaz Dular Date: Thu, 4 Jan 2024 10:50:51 +0100 Subject: [PATCH 1/3] Updated ProfileName constants in 1.6 implementation to match the SupportedProfiles allowed values in OCPP 1.6 specification --- ocpp1.6/core/core.go | 2 +- ocpp1.6/firmware/firmware.go | 2 +- ocpp1.6/localauth/local_auth_list.go | 2 +- ocpp1.6/reservation/reservation.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ocpp1.6/core/core.go b/ocpp1.6/core/core.go index fdc3f95f..bb504a35 100644 --- a/ocpp1.6/core/core.go +++ b/ocpp1.6/core/core.go @@ -31,7 +31,7 @@ type ChargePointHandler interface { } // THe profile name -var ProfileName = "core" +var ProfileName = "Core" // Provides support for Basic Charge Point functionality comparable with OCPP 1.5. var Profile = ocpp.NewProfile( diff --git a/ocpp1.6/firmware/firmware.go b/ocpp1.6/firmware/firmware.go index b7862fa9..c1351ba7 100644 --- a/ocpp1.6/firmware/firmware.go +++ b/ocpp1.6/firmware/firmware.go @@ -18,7 +18,7 @@ type ChargePointHandler interface { } // The profile name -const ProfileName = "firmwareManagement" +const ProfileName = "FirmwareManagement" // Provides support for firmware update management and diagnostic log file download. var Profile = ocpp.NewProfile( diff --git a/ocpp1.6/localauth/local_auth_list.go b/ocpp1.6/localauth/local_auth_list.go index c3b89c5f..1357674a 100644 --- a/ocpp1.6/localauth/local_auth_list.go +++ b/ocpp1.6/localauth/local_auth_list.go @@ -14,7 +14,7 @@ type ChargePointHandler interface { } // The profile name -const ProfileName = "localAuthList" +const ProfileName = "LocalAuthListManagement" // Provides support for managing the local authorization list in Charge Points. var Profile = ocpp.NewProfile( diff --git a/ocpp1.6/reservation/reservation.go b/ocpp1.6/reservation/reservation.go index 43ce30e1..d393444a 100644 --- a/ocpp1.6/reservation/reservation.go +++ b/ocpp1.6/reservation/reservation.go @@ -14,7 +14,7 @@ type ChargePointHandler interface { } // The profile name -const ProfileName = "reservation" +const ProfileName = "Reservation" // Provides support for for reservation of a Charge Point. var Profile = ocpp.NewProfile( From 59559babc50f3fb39c8d1ffe122d5758ef7f248a Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Mon, 11 Dec 2023 10:53:52 +0000 Subject: [PATCH 2/3] feat: Add StartWithRetries to handle retries upon first connection --- ocppj/client.go | 11 +++++++++++ ws/websocket.go | 23 ++++++++++++++++++++++- ws/websocket_test.go | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/ocppj/client.go b/ocppj/client.go index 083f544f..731fb540 100644 --- a/ocppj/client.go +++ b/ocppj/client.go @@ -123,6 +123,17 @@ func (c *Client) Start(serverURL string) error { return err } +func (c *Client) StartWithRetries(serverURL string) { + // Set internal message handler + c.client.SetMessageHandler(c.ocppMessageHandler) + c.client.SetDisconnectedHandler(c.onDisconnected) + c.client.SetReconnectedHandler(c.onReconnected) + // Connect & run + fullUrl := fmt.Sprintf("%v/%v", serverURL, c.Id) + c.client.StartWithRetries(fullUrl) + c.dispatcher.Start() +} + // Stops the client. // The underlying I/O loop is stopped and all pending requests are cleared. func (c *Client) Stop() { diff --git a/ws/websocket.go b/ws/websocket.go index 56665a16..fd8d6831 100644 --- a/ws/websocket.go +++ b/ws/websocket.go @@ -722,6 +722,17 @@ type WsClient interface { // // To stop a running client, call the Stop function. Start(url string) error + // Starts the client and attempts to connect to the server on a specified URL. + // If the connection fails, it keeps retrying with Backoff strategy from TimeoutConfig. + // + // For example: + // client.StartWithRetries("ws://localhost:8887/ws/1234") + // + // The function returns only when the connection has been established. + // Incoming messages are passed automatically to the callback function, so no explicit read operation is required. + // + // To stop a running client, call the Stop function. + StartWithRetries(url string) // Closes the output of the websocket Channel, effectively closing the connection to the server with a normal closure. Stop() // Errors returns a channel for error messages. If it doesn't exist it es created. @@ -997,6 +1008,7 @@ func (client *Client) handleReconnection() { return } + log.Info("reconnecting... attempt", reconnectionAttempts) err := client.Start(client.url.String()) if err == nil { // Re-connection was successful @@ -1038,8 +1050,17 @@ func (client *Client) Write(data []byte) error { return nil } +func (client *Client) StartWithRetries(urlStr string) { + err := client.Start(urlStr) + if err != nil { + log.Info("Connection error:", err) + client.handleReconnection() + } +} + func (client *Client) Start(urlStr string) error { url, err := url.Parse(urlStr) + client.url = *url if err != nil { return err } @@ -1072,7 +1093,7 @@ func (client *Client) Start(urlStr string) error { // The id of the charge point is the final path element id := path.Base(url.Path) - client.url = *url + client.webSocket = WebSocket{ connection: ws, id: id, diff --git a/ws/websocket_test.go b/ws/websocket_test.go index 8267d8db..399f7259 100644 --- a/ws/websocket_test.go +++ b/ws/websocket_test.go @@ -166,6 +166,45 @@ func TestWebsocketEcho(t *testing.T) { wsServer.Stop() } +func TestWebsocketBootRetries(t *testing.T) { + verifyConnection := func(client *Client, connected bool) { + maxAttempts := 20 + for i := 0; i <= maxAttempts; i++ { + if client.IsConnected() != connected { + time.Sleep(time.Duration(2) * time.Second) + continue + } + } + assert.Equal(t, connected, client.IsConnected()) + } + wsServer := newWebsocketServer(t, func(data []byte) ([]byte, error) { + return data, nil + }) + wsClient := newWebsocketClient(t, func(data []byte) ([]byte, error) { + return nil, nil + }) + + go func() { + // Start websocket client + host := fmt.Sprintf("localhost:%v", serverPort) + u := url.URL{Scheme: "ws", Host: host, Path: testPath} + wsClient.StartWithRetries(u.String()) + }() + + assert.Equal(t, wsClient.IsConnected(), false) + + time.Sleep(time.Duration(3) * time.Second) + + go wsServer.Start(serverPort, serverPath) + verifyConnection(wsClient, true) + + wsServer.Stop() + verifyConnection(wsClient, false) + + wsServer.Stop() + wsClient.Stop() +} + func TestTLSWebsocketEcho(t *testing.T) { message := []byte("Hello Secure WebSocket!") triggerC := make(chan bool, 1) From c85a755686882e44d17a311af4c078d5ec08a28c Mon Sep 17 00:00:00 2001 From: Alessio Ferri Date: Mon, 11 Dec 2023 11:54:13 +0000 Subject: [PATCH 3/3] refactor: [LB/RB/AF] #13 Add StartWithRetries to handle retries upon first connection at the ChargingStation interface --- ocpp2.0.1/charging_station.go | 8 ++++++++ ocpp2.0.1/v2.go | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/ocpp2.0.1/charging_station.go b/ocpp2.0.1/charging_station.go index 065af9a2..31ef3831 100644 --- a/ocpp2.0.1/charging_station.go +++ b/ocpp2.0.1/charging_station.go @@ -603,6 +603,14 @@ func (cs *chargingStation) Start(csmsUrl string) error { return err } +func (cs *chargingStation) StartWithRetries(csmsUrl string) { + // Start client + cs.stopC = make(chan struct{}, 1) + cs.client.StartWithRetries(csmsUrl) + // Async response handler receives incoming responses/errors and triggers callbacks + go cs.asyncCallbackHandler() +} + func (cs *chargingStation) Stop() { cs.client.Stop() } diff --git a/ocpp2.0.1/v2.go b/ocpp2.0.1/v2.go index 6e58efed..3f9cb495 100644 --- a/ocpp2.0.1/v2.go +++ b/ocpp2.0.1/v2.go @@ -165,6 +165,13 @@ type ChargingStation interface { // // No auto-reconnect logic is implemented as of now, but is planned for the future. Start(csmsUrl string) error + + // Connects to the CSMS and starts the charging station routine, it retries if first attempt fails. + // The function doesn't block and returns right away, after having attempted to open a connection to the CSMS. + // If the connection couldn't be opened, it retries. + // + // Optional client options must be set before calling this function. Refer to NewChargingStation. + StartWithRetries(csmsUrl string) // Stops the charging station routine, disconnecting it from the CSMS. // Any pending requests are discarded. Stop()