Giter Site home page Giter Site logo

ocpp-go's Introduction

ocpp-go

Build Status GoDoc Coverage Status Go report

Open Charge Point Protocol implementation in Go.

The library targets modern charge points and central systems, running OCPP version 1.6+.

Given that SOAP will no longer be supported in future versions of OCPP, only OCPP-J is supported in this library. There are currently no plans of supporting OCPP-S.

Status & Roadmap

Note: Releases 0.10.0 introduced breaking changes in some API, due to refactoring. The functionality remains the same, but naming changed.

Planned milestones and features:

  • OCPP 1.6
  • OCPP 2.0.1 (examples working, but will need more real-world testing)
  • Dedicated package for configuration management

OCPP 1.6 Usage

Go version 1.13+ is required.

go get github.com/lorenzodonini/ocpp-go

You will also need to fetch some dependencies:

cd <path-to-ocpp-go>
export GO111MODULE=on
go mod download

Your application may either act as a Central System (server) or as a Charge Point (client).

Central System

If you want to integrate the library into your custom Central System, you must implement the callbacks defined in the profile interfaces, e.g.:

import (
    "github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
    "github.com/lorenzodonini/ocpp-go/ocpp1.6/types"
    "time"
)

const defaultHeartbeatInterval = 600

type CentralSystemHandler struct {
	// ... your own state variables
}

func (handler *CentralSystemHandler) OnAuthorize(chargePointId string, request *core.AuthorizeRequest) (confirmation *core.AuthorizeConfirmation, err error) {
	// ... your own custom logic
	return core.NewAuthorizationConfirmation(types.NewIdTagInfo(types.AuthorizationStatusAccepted)), nil
}

func (handler *CentralSystemHandler) OnBootNotification(chargePointId string, request *core.BootNotificationRequest) (confirmation *core.BootNotificationConfirmation, err error) {
	// ... your own custom logic
	return core.NewBootNotificationConfirmation(types.NewDateTime(time.Now()), defaultHeartbeatInterval, core.RegistrationStatusAccepted), nil
}

// further callbacks... 

Every time a request from the charge point comes in, the respective callback function is called. For every callback you must return either a confirmation or an error. The result will be sent back automatically to the charge point. The callback is invoked inside a dedicated goroutine, so you don't have to worry about synchronization.

You need to implement at least all other callbacks defined in the core.CentralSystemHandler interface.

Depending on which OCPP profiles you want to support in your application, you will need to implement additional callbacks as well.

To start a central system instance, simply run the following:

centralSystem := ocpp16.NewCentralSystem(nil, nil)

// Set callback handlers for connect/disconnect
centralSystem.SetNewChargePointHandler(func(chargePointId string) {
	log.Printf("new charge point %v connected", chargePointId)
})
centralSystem.SetChargePointDisconnectedHandler(func(chargePointId string) {
	log.Printf("charge point %v disconnected", chargePointId)
})

// Set handler for profile callbacks
handler := &CentralSystemHandler{}
centralSystem.SetCoreHandler(handler)

// Start central system
listenPort := 8887
log.Printf("starting central system")
centralSystem.Start(listenPort, "/{ws}") // This call starts server in daemon mode and is blocking
log.Println("stopped central system")

Sending requests

To send requests to the charge point, you may either use the simplified API:

err := centralSystem.ChangeAvailability("1234", myCallback, 1, core.AvailabilityTypeInoperative)
if err != nil {
	log.Printf("error sending message: %v", err)
}

or create a message manually:

request := core.NewChangeAvailabilityRequest(1, core.AvailabilityTypeInoperative)
err := centralSystem.SendRequestAsync("clientId", request, callbackFunction)
if err != nil {
	log.Printf("error sending message: %v", err)
}

In both cases, the request is sent asynchronously and the function returns right away. You need to write the callback function to check for errors and handle the confirmation on your own:

myCallback := func(confirmation *core.ChangeAvailabilityConfirmation, e error) {
	if e != nil {
		log.Printf("operation failed: %v", e)
	} else {
		log.Printf("status: %v", confirmation.Status)
		// ... your own custom logic
	}
}

Since the initial centralSystem.Start call blocks forever, you may want to wrap it in a goroutine (that is, if you need to run other operations on the main thread).

Example

You can take a look at the full example. To run it, simply execute:

go run ./example/1.6/cs/*.go

Docker

A containerized version of the central system example is available:

docker pull ldonini/ocpp1.6-central-system:latest
docker run -it -p 8887:8887 --rm --name central-system ldonini/ocpp1.6-central-system:latest

You can also run it directly using docker-compose:

docker-compose -f example/1.6/docker-compose.yml up central-system

TLS

If you wish to test the central system using TLS, make sure you put your self-signed certificates inside the example/1.6/certs folder.

Feel free to use the utility script cd example/1.6 && ./create-test-certificates.sh for generating test certificates.

Then run the following:

docker-compose -f example/1.6/docker-compose.tls.yml up central-system

Charge Point

If you want to integrate the library into your custom Charge Point, you must implement the callbacks defined in the profile interfaces, e.g.:

import (
    "github.com/lorenzodonini/ocpp-go/ocpp1.6/core"
    "github.com/lorenzodonini/ocpp-go/ocpp1.6/types"
)

type ChargePointHandler struct {
	// ... your own state variables
}

func (handler *ChargePointHandler) OnChangeAvailability(request *core.ChangeAvailabilityRequest) (confirmation *core.ChangeAvailabilityConfirmation, err error) {
	// ... your own custom logic
	return core.NewChangeAvailabilityConfirmation(core.AvailabilityStatusAccepted), nil
}

func (handler *ChargePointHandler) OnChangeConfiguration(request *core.ChangeConfigurationRequest) (confirmation *core.ChangeConfigurationConfirmation, err error) {
	// ... your own custom logic
	return core.NewChangeConfigurationConfirmation(core.ConfigurationStatusAccepted), nil
}

// further callbacks...

When a request from the central system comes in, the respective callback function gets invoked. For every callback you must return either a confirmation or an error. The result will be sent back automatically to the central system.

You need to implement at least all other callbacks defined in the core.ChargePointHandler interface.

Depending on which OCPP profiles you want to support in your application, you will need to implement additional callbacks as well.

To start a charge point instance, simply run the following:

chargePointId := "cp0001"
csUrl = "ws://localhost:8887"
chargePoint := ocpp16.NewChargePoint(chargePointId, nil, nil)

// Set a handler for all callback functions
handler := &ChargePointHandler{}
chargePoint.SetCoreHandler(handler)

// Connects to central system
err := chargePoint.Start(csUrl)
if err != nil {
	log.Println(err)
} else {
	log.Printf("connected to central system at %v", csUrl) 
	mainRoutine() // ... your program logic goes here
}

// Disconnect
chargePoint.Stop()
log.Printf("disconnected from central system")

Sending requests

To send requests to the central station, you have two options. You may either use the simplified synchronous blocking API (recommended):

bootConf, err := chargePoint.BootNotification("model1", "vendor1")
if err != nil {
	log.Fatal(err)
} else {
	log.Printf("status: %v, interval: %v, current time: %v", bootConf.Status, bootConf.Interval, bootConf.CurrentTime.String())
}
// ... do something with the confirmation

or create a message manually:

request := core.NewBootNotificationRequest("model1", "vendor1")

You can then decide to send the message using a synchronous blocking call:

// Synchronous call
confirmation, err := chargePoint.SendRequest(request)
if err != nil {
	log.Printf("error sending message: %v", err)
}
bootConf := confirmation.(*core.BootNotificationConfirmation)
// ... do something with the confirmation

or an asynchronous call:

// Asynchronous call
err := chargePoint.SendRequestAsync(request, callbackFunction)
if err != nil {
	log.Printf("error sending message: %v", err)
}

In the latter case, you need to write the callback function and check for errors on your own:

callback := func(confirmation ocpp.Response, e error) {
	bootConf := confirmation.(*core.BootNotificationConfirmation)
	if e != nil {
		log.Printf("operation failed: %v", e)
	} else {
		log.Printf("status: %v", bootConf.Status)
		// ... your own custom logic
	}
}

When creating a message manually, you always need to perform type assertion yourself, as the SendRequest and SendRequestAsync APIs use generic Request and Confirmation interfaces.

Example

You can take a look at the full example. To run it, simply execute:

CLIENT_ID=chargePointSim CENTRAL_SYSTEM_URL=ws://<host>:8887 go run example/1.6/cp/*.go

You need to specify the URL of a running central station server via environment variable, so the charge point can reach it.

Docker

A containerized version of the charge point example is available:

docker pull ldonini/ocpp1.6-charge-point:latest
docker run -e CLIENT_ID=chargePointSim -e CENTRAL_SYSTEM_URL=ws://<host>:8887 -it --rm --name charge-point ldonini/ocpp1.6-charge-point:latest

You need to specify the host, on which the central system is running, in order for the charge point to connect to it.

You can also run it directly using docker-compose:

docker-compose -f example/1.6/docker-compose.yml up charge-point

TLS

If you wish to test the charge point using TLS, make sure you put your self-signed certificates inside the example/1.6/certs folder.

Feel free to use the utility script cd example/1.6 && ./create-test-certificates.sh for generating test certificates.

Then run the following:

docker-compose -f example/1.6/docker-compose.tls.yml up charge-point

Advanced Features

The library offers several advanced features, especially at websocket and ocpp-j level.

Automatic message validation

All incoming and outgoing messages are validated by default, using the validator package. Constraints are defined on every request/response struct, as per OCPP specs.

Validation may be disabled at a package level if needed:

ocppj.SetMessageValidation(false)

Use at your own risk, as this will disable validation for all messages!

I will be evaluating the possibility to selectively disable validation for a specific message, e.g. by passing message options.

Verbose logging

The ws and ocppj packages offer the possibility to enable verbose logs, via your logger of choice, e.g.:

// Setup your own logger
log = logrus.New()
log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
log.SetLevel(logrus.DebugLevel) // Debug level needed to see all logs
// Pass own logger to ws and ocppj packages
ws.SetLogger(log.WithField("logger", "websocket"))
ocppj.SetLogger(log.WithField("logger", "ocppj"))

The logger you pass needs to conform to the logging.Logger interface. Commonly used logging libraries, such as zap or logrus, adhere to this interface out-of-the-box.

If you are using a logger, that isn't conform, you can simply write an adapter between the Logger interface and your own logging system.

Websocket ping-pong

The websocket package currently supports client-initiated pings only.

If your setup requires the server to be the initiator of a ping-pong (e.g. for web-based charge points), you may disable ping-pong entirely and just rely on the heartbeat mechanism:

cfg := ws.NewServerTimeoutConfig()
cfg.PingWait = 0 // this instructs the server to wait forever
websocketServer.SetTimeoutConfig(cfg)

A server-initiated ping may be supported in a future release.

OCPP 2.0.1 Usage

Experimental support for version 2.0.1 is now supported!

Version 2.0 was skipped entirely, since it is considered obsolete.

Requests and responses in OCPP 2.0.1 are handled the same way they were in v1.6. The notable change is that there are now significantly more supported messages and profiles (feature sets), which also require their own handlers to be implemented.

The library API to the lower websocket and ocpp-j layers remains unchanged.

Below are very minimal setup code snippets, to get you started. CSMS is now the equivalent of the Central System, while the Charging Station is the new equivalent of a Charge Point.

Refer to the examples folder for a full working example. More in-depth documentation for v2.0.1 will follow.

Bug reports for this version are welcome.

CSMS

To start a CSMS instance, run the following:

import "github.com/lorenzodonini/ocpp-go/ocpp2.0.1"

csms := ocpp2.NewCSMS(nil, nil)

// Set callback handlers for connect/disconnect
csms.SetNewChargingStationHandler(func(chargingStation ocpp2.ChargingStationConnection) {
	log.Printf("new charging station %v connected", chargingStation.ID())
})
csms.SetChargingStationDisconnectedHandler(func(chargingStation ocpp2.ChargingStationConnection) {
	log.Printf("charging station %v disconnected", chargingStation.ID())
})

// Set handler for profile callbacks
handler := &CSMSHandler{}
csms.SetAuthorizationHandler(handler)
csms.SetAvailabilityHandler(handler)
csms.SetDiagnosticsHandler(handler)
csms.SetFirmwareHandler(handler)
csms.SetLocalAuthListHandler(handler)
csms.SetMeterHandler(handler)
csms.SetProvisioningHandler(handler)
csms.SetRemoteControlHandler(handler)
csms.SetReservationHandler(handler)
csms.SetTariffCostHandler(handler)
csms.SetTransactionsHandler(handler)

// Start central system
listenPort := 8887
log.Printf("starting CSMS")
csms.Start(listenPort, "/{ws}") // This call starts server in daemon mode and is blocking
log.Println("stopped CSMS")

Sending requests

Similarly to v1.6, you may send requests using the simplified API, e.g.

err := csms.GetLocalListVersion(chargingStationID, myCallback)
if err != nil {
	log.Printf("error sending message: %v", err)
}

Or you may build requests manually and send them using the asynchronous API.

Docker image

There is a Dockerfile and a docker image available upstream. Feel free

Charging Station

To start a charging station instance, simply run the following:

chargingStationID := "cs0001"
csmsUrl = "ws://localhost:8887"
chargingStation := ocpp2.NewChargingStation(chargingStationID, nil, nil)

// Set a handler for all callback functions
handler := &ChargingStationHandler{}
chargingStation.SetAvailabilityHandler(handler)
chargingStation.SetAuthorizationHandler(handler)
chargingStation.SetDataHandler(handler)
chargingStation.SetDiagnosticsHandler(handler)
chargingStation.SetDisplayHandler(handler)
chargingStation.SetFirmwareHandler(handler)
chargingStation.SetISO15118Handler(handler)
chargingStation.SetLocalAuthListHandler(handler)
chargingStation.SetProvisioningHandler(handler)
chargingStation.SetRemoteControlHandler(handler)
chargingStation.SetReservationHandler(handler)
chargingStation.SetSmartChargingHandler(handler)
chargingStation.SetTariffCostHandler(handler)
chargingStation.SetTransactionsHandler(handler)

// Connects to CSMS
err := chargingStation.Start(csmsUrl)
if err != nil {
	log.Println(err)
} else {
	log.Printf("connected to CSMS at %v", csmsUrl) 
	mainRoutine() // ... your program logic goes here
}

// Disconnect
chargingStation.Stop()
log.Println("disconnected from CSMS")

Sending requests

Similarly to v1.6 you may send requests using the simplified API (recommended), e.g.

bootResp, err := chargingStation.BootNotification(provisioning.BootReasonPowerUp, "model1", "vendor1")
if err != nil {
	log.Printf("error sending message: %v", err)
} else {
	log.Printf("status: %v, interval: %v, current time: %v", bootResp.Status, bootResp.Interval, bootResp.CurrentTime.String())
}

Or you may build requests manually and send them using either the synchronous or asynchronous API.

ocpp-go's People

Contributors

alessioerosferri avatar andig avatar derartem avatar dwibudut avatar frednesto avatar gq0 avatar guelfey avatar jonesywolf avatar lorenzodonini avatar michaelbeaumont avatar rbright avatar sc-atompower avatar shiv3 avatar tretmar avatar volkerlieber avatar xblaz3kx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ocpp-go's Issues

Chargepoint: Handling connection drops

Consider this simple client:

chargePoint := ocpp16.NewChargePoint(chargePointId, nil, nil)

// Set a handler for all callback functions
handler := new(ChargePointHandler)
chargePoint.SetCoreHandler(handler)

go func() {
	for err := range chargePoint.Errors() {
		fmt.Println(err)
	}
}()

// Connects to central system
if err := chargePoint.Start(url); err != nil {
	log.Fatal(err)
}

log.Printf("connected to central system at %v", url)
bootConf, err := chargePoint.BootNotification("model1", "vendor1")
if err != nil {
	log.Fatal(err)
} else {
	log.Printf("status: %v, interval: %v, current time: %v", bootConf.Status, bootConf.Interval, bootConf.CurrentTime.String())
}

select {}

I've noticed that no errors are logged when the websocket connection goes away, even if it can't be re-established.

What is the best practice to diagnose/handle connection issues?

Rel. 0.16 setTimeout

Added a timeout option for outgoing requests: if a response is not received within the configured timeframe, the request is discarded and an error is returned to the sender

I've looked at the code and it seems that this is only available for chargepoints, correct? It might make sense to make the readme/ release notes clearer?

websocket: bad handshake for OCPP 2.0

Hi,

I try to create and example for OCPP 2.0 with ocpp2.0-features branch. When i try to connect to CSMS from CS it throws websocket: bad handshake error.

Here is sample CSMS and CS code

cs.go

package main

import (
	"fmt"
	"github.com/lorenzodonini/ocpp-go/ocpp2.0"
)

func main() {
	fmt.Println("Charging Station")
	cs := ocpp2.NewChargingStation("uniqueFox", nil, nil)
	err := cs.Start("ws://127.0.0.1:8080")
	fmt.Println(err)
}

csms.go

package main

import (
	"fmt"
	"github.com/lorenzodonini/ocpp-go/ocpp2.0"
)

func main() {
	csms := ocpp2.NewCSMS(nil, nil)
	csms.SetNewChargingStationHandler(func(chargePointId string) {
		fmt.Println("Charge point connected ", chargePointId)
	})
	csms.Start(8080, "")
}

I have not set any handler yet. But i assume it should connect without handler.

I am able to run ocpp1.6 example without any issue.

updateStatus needs timestamp parameter

I have tried to register ocpp-go to the Steve OCPP backend.

Currently the registration fails as Steve needs a valid timestamp:

`
[INFO ] 2020-07-01 10:34:31,150 de.rwth.idsg.steve.ocpp.ws.WebSocketLogger - [chargeBoxId=AM1, sessionId=7e7b0364-fa2e-9836-6a96-60c7a2cfabc3] Received: [2,"2854263694","StatusNotification",{"connectorId":0,"errorCode":"NoError","status"
:"Available","timestamp":"0001-01-01T00:00:00Z"}]

[ERROR] 2020-07-01 10:34:31,298 de.rwth.idsg.steve.ocpp.ws.ocpp16.Ocpp16WebSocketEndpoint$Ocpp16CallHandler - Exception occurred
org.jooq.exception.DataAccessException: SQL [insert into stevedb.connector_status (connector_pk, status_timestamp, status, error_code, error_info, vendor_id, vendor_error_code) values ((select stevedb.connector.con nector_pk from stevedb.connector where (stevedb.connector.charge_box_id = ? and stevedb.connector.connector_id = ?)), ?, ?, ?, ?, ?, ?)]; Data truncation: Incorrect datetime value: '0001-01-03 00:00:00' for column steve db.connector_status.status_timestamp at row 1
`

Logging requests/responses

It would be nice, if there was a facility for logging all requests/responses sent or received by server/client as seen on the websocket. Such logging could be on the websocket (plain) or on the dispatcher level (parsed).

RemoteTriggers / GetConfiguration and ChangeConfiguration stop working after invoking the charging station after a while

Hi @lorenzodonini , a big thank you to all of the contributors for building this library, I've been using your library and a forked version of it (specifically to get local times instead of UTC) for about a month now.

The Problem

I've been seeing an issue with remote triggers since the beginning where the charging station remains connected to my CSMS, sends regular StatusNotification and Heartbeats but after a while I am not able to trigger further TriggerMessages on the charging station. The only thing that solves the issue is if I restart my CSMS and the charging station connects back to it again, then I am able to start sending those TriggerMessages again with a response from the charging station.

Screenshot 2021-04-20 at 11 11 00 PM

I would highly appreciate if you can point me to a possible fix or file where I can look into and even possibly send a PR to fix this issue.

OCPP Version

1.6

OCPP Server becomes unresponsive after a while on v0.16.0

Description:

Hi @lorenzodonini :) We use your library at production @daheimladen
We have been having issues with stations connected over WiFi, where at times the stations either would not get or receive ocpp messages from the server, and we would continue to get no response from the stations unless until we restart the station of the csms server itself.
I realised that chaning the defaultMessageTimeout from 30 seconds down to 8 seconds solved our issue.
So doing something like this,

const DEFAULT_MESSAGE_TIMEOUT_SEC time.Duration = time.Second * 8

func setupChargingStationManagementSystem() ocpp16.CentralSystem {
	dispatcher := ocppj.NewDefaultServerDispatcher(ocppj.NewFIFOQueueMap(0))
	dispatcher.SetTimeout(DEFAULT_MESSAGE_TIMEOUT_SEC)
	s := ws.NewServer()
	endpoint := ocppj.NewServer(s, dispatcher, nil, core.Profile, localauth.Profile, remotetrigger.Profile, reservation.Profile, firmware.Profile, smartcharging.Profile)
	return ocpp16.NewCentralSystem(endpoint, s)
}

While this solved most of our troubles, and this worked perfectly in a beta environment with a handful of users, we ran into issues where the status of the stations failed to update, and all station -> server messages (like OnAuthorize, StatusNotification) failed to work just after 24 hours after promoting the changes to production with about 2000 stations connected with us.
Is there something wrong with my approach above or is the decision of 8 second timeout is to blame. I would really appreciate if you can give some feedback :)

Thanks

Does v2.0.1 implementation fully conform to the OCPP 2.0.1 spec?

I am a bit new to the OCPP protocol. I have gone thru the documentation via OCA for v2.0.1 and looking for Go implementation.

Does the v2.0.1 implementation is fully compatible with the OCPP 2.0.1 spec? I see from the Readme.md states Experimental support for version 2.0.1 is now supported!.

Is this still in experimental phase or the code is fully compatible but not update the info in readme?

OCPP 2.0.1 support

I see you are actively working on ocpp 2.0 branch. Since ocpp 2.0.1 was released in March this year are you already considering it? And compliments for a great work!!

Custom error structs in websocket package

Follow-up to #40

Errors sent over the errC channels in WsServer and WsClient are currently basic errors.

In some cases errors should be structs, containing some additional info, such as:

  • client ID
  • operation? (ping, write, close, ...)

Any struct will still be sent as an error, therefore not breaking the API. Channel consumers, however, may optionally perform type assertion and access the additional info.

Howto implement error handling?

Once the CP has successfully started (error=nil) its not entirely clear how to track it's status or restart the client should it become disconnected?

UPDATE:

Here's an example where the CP closes when it's not happy with the CS message:

INFO[2020-11-02T17:16:31+01:00] ocpp OccurrenceConstraintViolation - Field Call.Payload.Value required but not found 
ERRO[2020-11-02T17:16:31+01:00] error while handling message: ocpp OccurrenceConstraintViolation - Field Call.Payload.Value required but not found 
ERRO[2020-11-02T17:16:31+01:00] error while reading from websocket: websocket: close 1011 (internal server error) 

How would I handle that in my client code?

Make ConfigurationKey specification compatible with ocpp1.6

Issue

Make the ConfigurationKey struct's ReadOnly element required

Description

While going through the OCPP 1.6 documentation around KeyValue for GetConfiguration, I noticed, readonly is described as required.

Screenshot 2021-08-04 at 4 59 11 PM

Possible Solution

If it was missed out, I'm linking a PR that fixes it.

SetChargingProfile - Validation issue with ChargingProfilePurposeChargePointMaxProfile

Dear all,

when using 1.6 and SetChargingProfile like so:

	cProfile.ChargingProfileId = 1
	cProfile.StackLevel = 0
	cProfile.ChargingProfilePurpose = types.ChargingProfilePurposeChargePointMaxProfile
	cProfile.ChargingProfileKind = types.ChargingProfileKindAbsolute

	cProfile.ChargingSchedule = types.NewChargingSchedule(types.ChargingRateUnitAmperes,
		types.ChargingSchedulePeriod{
			StartPeriod:  0,
			Limit:        float64(limit),
			NumberPhases: &phases,
		})

	err := cs.SetChargingProfile(chargePointName, func(confirmation *smartcharging.SetChargingProfileConfirmation, err error) {
               // some error handling/logging ....
	}, 0, &cProfile)
	if err != nil {
		logrus.WithFields(logrus.Fields{
			"ChargePoint": chargePointName,
		}).Errorf("SetChargingProfile NOT sent %v", err)
		return false
	}

returns an error when using types.ChargingProfilePurposeChargePointMaxProfile, with ChargingProfilePurposeTxDefaultProfile it works ok. I could not spot the issue but removing the validation chargingProfilePurpose brings success, also using ChargingProfilePurposeTxDefaultProfile works.

Lets me know if I can bring more info.

Thanks for all the work.

scalabillity suggestion question

Hi,

Thanks for developing this library, it's been very useful!
I have a question or rather asking for suggestion. Right now I'm using this library directly with backend server that serves REST API requests, So for example, we just call "RemoteStartTransaction" request in this library directly when a call to a REST API come. The problem with this approach is it's not scalable, since the websocket connection will be cached inside the memory in one executable, so if we want to scale the server to become multiple instances, some charge points will not be available to every instsance of the server.

The solution to this could be many:

  1. To separate REST API server and websocket (this library's) server, one big websocket server will serve all websocket chargepoint connection, and then the REST API server can be replicated, the communication between websocket server and REST API server will be a pubsub broker like RabbitMQ.
  2. To cache OCPP command request queues separately in another in-memory cache such as redis, this request queue will be common to all server instances. So regardless which instance of server a CP is connected to, a request will still be there in memory when the server is down, and goes back up.

I wonder which approach is more suitable and simpler to implement. And I wonder if this is a problem to any of you implementers out there.

Contribution to OCPP 2.X

Hi guys!
@lorenzodonini I'm wondering if you have any roadmap/plan for integration latest OCPP 2.0 in this repository.
As far as I see the version 2.0 only stating here. I'd like to discuss it with you and maybe help from my side with that OCPP, please contact me if you're keen to sync on it.

Query about 3 tier model and how it is implemented in ocpp2.0.1

Hello @lorenzodonini

My query is how you are planning to adopt 3 tier model in the ocpp2.0.1 implementation?

According to the below definition

type ChargingStationConnection interface {
	ID() string
	TLSConnectionState() *tls.ConnectionState
}

ID gives the charging station id and do we also have a similar struct for chargePointConnection also to browse through different charge points?

Error: unsupported subprotocols [ocpp2.0.1.1] for new client cs001

Is there a way to fix this error without having to add an extra code to configure the server to accept handshake w/ the ocpp2.0.1.1 protocol header?

Server log

% go run .
INFO[2022-02-23T01:52:02-03:00] starting central system on port 7777         
INFO[2022-02-23T01:52:02-03:00] listening on tcp network :7777                logger=websocket
ERRO[2022-02-23T01:52:06-03:00] unsupported subprotocols [ocpp2.0.1.1] for new client cs001 ([::1]:64384)  logger=websocket

Possible Cause

const (
V2Subprotocol = "ocpp2.0.1"
V201Subprotocol = "ocpp2.0.1.1"
)

ocpp-go/ocpp2.0.1/v2.go

Lines 208 to 210 in a2707ff

if !alreadyExists {
dialer.Subprotocols = append(dialer.Subprotocols, types.V201Subprotocol)
}

Server

func main() {
  // Run central system
  log.Infof("starting central system on port %v", listenPort)
  csms.Start(listenPort, "/{ws}")
  log.Info("stopped central system")
}

func init() {
  log = logrus.New()
  log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
  // Set this to DebugLevel if you want to retrieve verbose logs from the ocppj and websocket layers
  log.SetLevel(logrus.InfoLevel)
  ws.SetLogger(log.WithField("logger", "websocket"))

  server = ws.NewServer()
  server.SetBasicAuthHandler(func(user string, pass string) bool {
    ok := authenticate(user, pass) // ... check for user and pass correctness
    return ok
  })

  // This extra code solves the problem
  // would it be possible the default server setup itself 
  // already include the subprotocol configuration?
  // server.AddSupportedSubprotocol(types.V201Subprotocol)

  csms = ocpp2.NewCSMS(nil, server)
  csms.SetNewChargingStationHandler(func(chargePoint ocpp2.ChargingStationConnection) {
    log.WithField("client", chargePoint.ID()).Info("new charge point connected")
  })
  csms.SetChargingStationDisconnectedHandler(func(chargePoint ocpp2.ChargingStationConnection) {
    log.WithField("client", chargePoint.ID()).Info("charge point disconnected")
  })
}

func authenticate(user string, pass string) bool {
  return user == "cs001" && pass == "s3cr3t"
}

Charging Station

const (
  csUrl = "ws://localhost:7777"
)

var log *logrus.Logger
var client *ws.Client
var cs ocpp2.ChargingStation

// Start function
func main() {
  // Connects to central system
  err := cs.Start(csUrl)
  if err != nil {
    log.Errorln(err)
  } else {
    log.Infof("connected to central system at %v", csUrl)
    // Disconnect
    cs.Stop()
    log.Infof("disconnected from central system")
  }
}

func init() {
  log = logrus.New()
  log.SetFormatter(&logrus.TextFormatter{FullTimestamp: true})
  // Set this to DebugLevel if you want to retrieve verbose logs from the ocppj and websocket layers
  log.SetLevel(logrus.InfoLevel)

  ws.SetLogger(log.WithField("logger", "websocket"))
  ocppj.SetLogger(log.WithField("logger", "ocppj"))

  client = ws.NewClient()
  client.SetBasicAuth("cs001", "s3cr3t")
  cs = ocpp2.NewChargingStation("cs001", nil, client)
}

undefined: ws.SetLogger

Why I'm facing this issue? SetLogger is a function defined in the WS package. I took the example code for CS and CP but everything is working except this WebSockets package. What is the issue and what I'm doing wrong?

Chargepoint.Stop does not return an error and crashes if provider is not available

I am trying to connect to the Steve Backend Provider. There is a use case where I want to disconnect. For the same I am using the Chargepoint.Stop() method.

For some reason, if Steve is not available, this call crashes the client. Is there a way to handle it more gracefully in a way that I can catch the error and it does not crash the client.

Regards
Ankur

Ocpp 2.0.1 - Error defining RequestStartTransactionRequest structure

I have been developing an EV Charger simulator using this repository.

When was building the remote handler and testing it against an OCPP connector the remote request start transaction was failing with the error:

"FormationViolation","json: cannot unmarshal object into Go struct field RequestStartTransactionRequest.idToken of type types.IdTokenType",null]
error_code=FormationViolation, error_description=json: cannot unmarshal object into Go struct field RequestStartTransactionRequest.idToken of type types.IdTokenType, error_details=None>'

I looked at the message sent by the connector, the specifications, the code, and the RequestStartTransactionRequest structure in the file request_start_transaction.go has an error.

The IdToken is defined as types.IdTokenType which is a string, but should be an object as the types.IdToken.

To have sure that it was not an error from the connector or the work that I'd done I changed the code in some places and after dose changes it worked again.

  • ocpp2.0.1/remotecontrol/request_start_transaction.go (line 36)
type RequestStartTransactionRequest struct {
	EvseID          *int                   `json:"evseId,omitempty" validate:"omitempty,gt=0"`
	RemoteStartID   int                    `json:"remoteStartId" validate:"gte=0"`
	IDToken         types.IdToken      `json:"idToken" validate:"required"`
	ChargingProfile *types.ChargingProfile `json:"chargingProfile,omitempty"`
	GroupIdToken    types.IdTokenType      `json:"groupIdToken,omitempty" validate:"omitempty,idTokenType"`
}
  • ocpp2.0.1/remotecontrol/request_start_transaction.go (line 80)
func NewRequestStartTransactionRequest(remoteStartID int, IdToken types.IdToken) *RequestStartTransactionRequest {
	return &RequestStartTransactionRequest{RemoteStartID: remoteStartID, IDToken: IdToken}
}
  • ocpp2.0.1/csms.go (line 434)
func (cs *csms) RequestStartTransaction(clientId string, callback func(*remotecontrol.RequestStartTransactionResponse, error), remoteStartID int, IdToken types.IdToken, props ...func(request *remotecontrol.RequestStartTransactionRequest)) error {
	request := remotecontrol.NewRequestStartTransactionRequest(remoteStartID, IdToken)
	for _, fn := range props {
		fn(request)
	}
	genericCallback := func(response ocpp.Response, protoError error) {
		if response != nil {
			callback(response.(*remotecontrol.RequestStartTransactionResponse), protoError)
		} else {
			callback(nil, protoError)
		}
	}
	return cs.SendRequestAsync(clientId, request, genericCallback)
}
  • ocpp2.0.1/v2.go (line 304)
    RequestStartTransaction(clientId string, callback func(*remotecontrol.RequestStartTransactionResponse, error), remoteStartID int, IdToken types.IdToken, props ...func(request *remotecontrol.RequestStartTransactionRequest)) error

Not sure that these are the only changes that need to be done to fully correct this, but the simple test that I did was enough .

Hope this will help.

Feature Request: Initialize library in 'relaxed mode'

Rationale

I'm currently working on a system to talk to an already installed set of charge points (about 1500).
These charge points communicate OCPP 1.6 but do not conform strict to the spec.

e.g. The charge points will reject the RemoteStartTransaction command if no connectorId is provided. They assume connectorId 0 means that no connectorId is present in the request.
This ofcourse is not possible to send right now, as the library validates every object for correctness (as it should actually... ).

Unfortunately upgrading/fixing the charge points is not an option. These are already in the field for many of years and the manufacturer is unwilling to upgrade / adjust their OCPP implementation.

Request

Have the option start the library in 'relax mode'; meaning to disable the validate calls on the objects send / received.
Of course this would be an explicit option that needs to be set before initializing the library.

1006 ws connection error with tls certificates

I am using a simultor and it is working with this code snippet:
#103 (comment)

Now, I would like to have the same functionality using the TLS certificates. I generated the certificates as you have shown in the example folder of this repository. But I am facing an error of "1006 ws connection error" It is working with setupCentralSystem() function but not with setupTlsChargePoint function.

How do I specify ConnectId in RemoteStartTransaction?

Thank you for the source code.
In our case, the charge point has four connectors.
In OPCC 1.6J specification 「6.33. RemoteStartTransaction.req」
connectorId: Optional. Number of the connector on which to start the transaction. connectorId SHALL be > 0
And how to use in ocpp-go

OCPP 2.0.1 - Sampled Value validation is failing when 0 value is sent

When sending the meter value to the central system and some of the values in the sampled value are zero, the framework is failing to send the request.

The central system doesn't receive the request and it returns the error:
Key: 'Call.Payload.MeterValue[0].SampledValue[1].Value' Error:Field validation for 'Value' failed on the 'required' tag

The transaction request is:
{Started 2022-12-09 23:09:59.722428058 +0000 WET m=+11.004372863 CablePluggedIn 0 false <nil> <nil> <nil> {nwjH6PvYQh Charging <nil> <nil>} <nil> 0xc000222360 [{2022-12-09 23:09:59.722416663 +0000 WET m=+11.004361473 [{500 Current.Import Outlet <nil> 0xa09c60} {0.1 Energy.Active.Import.Register Outlet <nil> 0xa09c80} {179267 Power.Active.Import Outlet <nil> 0xa09ca0} {230 Voltage Outlet <nil> 0xa09cc0}]}]}

I'm new to the OCPP and still reading the OCPP 2.0.1 specifications, but I don't see any restrictions on the specifications to the value being zero.

Can someone help me?

And tks in advance.

FYI: Nice job on this repo @lorenzodonini .

Issue with Websockets using Browser Chargepoint Simulator?

I'm starting to integrate ocpp-go in my own application.
For testing I wanted to use one of the HTML/JS chargepoint simulators.

So I'm running the example/1.6/cs application on my pc, if I run also the examples/1.6/cp simulator everything works.
The simulated chagrpoint registers in the central system example, and so on.

I'm for example tried this one:
https://github.com/zzerk/OCPP-ChargePoint-Simulator

I entered 'ws://127.0.0.1:8887' as OCCP server, and the click 'connect'. The log in the simulator states the following error:
OCPP Simulator ready connection cannot be opened: error Connection error: 1006 [OCPP] Connection error: 1006

An the console in Chrome gives following error:
grafik

But I don't understand where the problem could come from.

Could this be a problem of the Simulator, or maybe of the occp-go package?

I'm on commit 017c9dc

ocpp service stucked ,Every 3 days or so

after getted BootNotification request more than 10 times from chargepoint ,ths ocpp server is stucked ,see logs likes this:

websocekt CP->CS json data: [2,"28ad5fa3-bde3-4eb7-8427-62bca72c1dec","Heartbeat",{}]
websocekt CP->CS json data: [2,"2a513217-43e5-40e1-b01e-62bca8d85e4f","Heartbeat",{}]
websocekt CP->CS json data: [2,"33cfae6f-eee8-41de-b557-62bca98442f3","Heartbeat",{}]
websocekt CP->CS json data: [2,"d55973d5-c39a-4890-9c16-62bcab311012","Heartbeat",{}]
websocekt CP->CS json data: [2,"c02371d0-1b09-41e4-8827-62bcabdcbff4","Heartbeat",{}]
websocekt CP->CS json data: [2,"6cdd8150-eac8-4db3-a2c4-62bcad8934a8","Heartbeat",{}]
websocekt CP->CS json data: [2,"9f2508d5-9784-45c6-b467-62bcae3413d5","Heartbeat",{}]
websocekt CP->CS json data: [2,"5a9472c8-b0c7-4a15-9a42-62bcafe1ceda","Heartbeat",{}]
websocekt CP->CS json data: [2,"5c8d4a65-7b7f-48d2-a926-62bcb08d2898","Heartbeat",{}]
websocekt CP->CS json data: [2,"89053057-d9e8-4066-b63d-62bcb239ad60","Heartbeat",{}]
websocekt CP->CS json data: [2,"4f5387f8-596e-4c46-a38c-62bcb2e5f1c3","Heartbeat",{}]
websocekt CP->CS json data: [2,"601325b7-5727-4818-b944-62bcb49164bb","Heartbeat",{}]
websocekt CP->CS json data: [2,"a0495576-bc26-43bc-ae69-62bcb53d52b7","Heartbeat",{}]
websocekt CP->CS json data: [2,"943cf97d-4e97-479c-ac8b-62bcb6e9ef89","Heartbeat",{}]
websocekt CP->CS json data: [2,"e2d6b23b-ee6d-4c2f-a07e-62bcb795b9f8","Heartbeat",{}]
websocekt CP->CS json data: [2,"dca508fc-f7aa-4809-be8f-62bcb941fe35","Heartbeat",{}]
websocekt CP->CS json data: [2,"78df67c1-2e97-4bd7-8368-62bcb9ed3f71","Heartbeat",{}]
websocekt CP->CS json data: [2,"418da474-001b-4b85-acd9-62bcbb997cbb","Heartbeat",{}]
websocekt CP->CS json data: [2,"02f905f4-6ed0-4d48-9e16-62bcbc459df1","Heartbeat",{}]
websocekt CP->CS json data: [2,"8ed944d7-5a8d-4d9e-b77d-62bcbdf1e2c8","Heartbeat",{}]
websocekt CP->CS json data: [2,"75bd1853-87cb-4d1b-8755-62bcbe9d052f","Heartbeat",{}]
websocekt CP->CS json data: [2,"8d0a5c76-b458-4848-a9e1-62bcc04986f6","Heartbeat",{}]
websocekt CP->CS json data: [2,"fcbdb650-e37b-462b-aac5-62bcc0f5a814","Heartbeat",{}]
websocekt CP->CS json data: [2,"fff03667-e5c9-4244-be1c-62bcc2a10bac","Heartbeat",{}]
websocekt CP->CS json data: [2,"0c750b88-68c0-46a3-823d-62bcc34d23ac","Heartbeat",{}]
websocekt CP->CS json data: [2,"0baf48a6-41b6-4d15-8a46-62bcc4f963e5","Heartbeat",{}]
websocekt CP->CS json data: [2,"e140b08b-e5fa-4514-852b-62bcc5a5d578","Heartbeat",{}]
websocekt CP->CS json data: [2,"a16d8916-6ad6-424d-8091-62bcc752c290","Heartbeat",{}]
websocekt CP->CS json data: [2,"5e5ff694-3039-4e1a-90a9-62bcc7fedaf2","Heartbeat",{}]
websocekt CP->CS json data: [2,"285c764d-f884-4edc-8bec-62bcc9aa17a8","Heartbeat",{}]
websocekt CP->CS json data: [2,"b248b90b-fcc2-4069-9181-62bcca56a8c2","Heartbeat",{}]
websocekt CP->CS json data: [2,"6437ac52-8746-4bd1-8abc-62bccc020426","Heartbeat",{}]
websocekt CP->CS json data: [2,"52564832-4b47-4777-ac8a-62bcccae3896","Heartbeat",{}]
websocekt CP->CS json data: [2,"98ea83d5-9b74-4b86-9c5b-62bcce5af667","Heartbeat",{}]
websocekt CP->CS json data: [2,"1d1f0d0e-e35e-4a11-bd95-62bccf06dc52","Heartbeat",{}]
websocekt CP->CS json data: [2,"3f09b2eb-9b8b-44c9-bb91-62bcd0b2ace2","Heartbeat",{}]
websocekt CP->CS json data: [2,"a8354306-6679-416d-b407-62bcd15e2a9b","Heartbeat",{}]
websocekt CP->CS json data: [2,"2a2833d5-9b0e-4d49-8712-62bcd30ad615","Heartbeat",{}]

How to send optional ...props for BootNotification?

Hi,

Please pardon it might be a little basic Go doubt. I am trying to send a bootNotification to a backendProvider. The chargingStation.BootNotification method expects chargePointModel, chargePointVendor. But how can I set the other optional props like Iccid,Imsi, etc while calling this API.

2.0.1. types.SampleValue validation Value

Hi there,

I discovered an issue with the validation of types.SampleValue.Value, required would exclude 0.0 Values.

type SampledValue struct {
	Value            float64           `json:"value" validate:"required"`                             // Indicates the measured value.
	Context          ReadingContext    `json:"context,omitempty" validate:"omitempty,readingContext"` // Type of detail value: start, end or sample. Default = "Sample.Periodic"
	Measurand        Measurand         `json:"measurand,omitempty" validate:"omitempty,measurand"`    // Type of measurement. Default = "Energy.Active.Import.Register"
	Phase            Phase             `json:"phase,omitempty" validate:"omitempty,phase"`            // Indicates how the measured value is to be interpreted. For instance between L1 and neutral (L1-N) Please note that not all values of phase are applicable to all Measurands. When phase is absent, the measured value is interpreted as an overall value.
	Location         Location          `json:"location,omitempty" validate:"omitempty,location"`      // Indicates where the measured value has been sampled.
	SignedMeterValue *SignedMeterValue `json:"signedMeterValue,omitempty" validate:"omitempty"`       // Contains the MeterValueSignature with sign/encoding method information.
	UnitOfMeasure    *UnitOfMeasure    `json:"unitOfMeasure,omitempty" validate:"omitempty"`          // Represents a UnitOfMeasure including a multiplier.
}

float64 and required will lead to something like Key: 'VAL.Value' Error:Field validation for 'Value' failed on the 'required' tag

Please let me know what do you think.

Allow Many Pending Request

Hi I think you should allow many pending Request, because a chargepoint can send two or more request in very close time, and and the server may be busy answering

hasPendingRequest

Charge point disconnects after 1 minute

Due to the way the pongWait is hardcoded in the ws server there is no way for me to increase the ping rate.
The charge points we use ping every 2 minutes. I suggest to make this configurable, for now I've copied over the ws server and modified the code to accept pings every 2 minutes

Check if the ocppj client is connected?

By adding handlers for disconnect and reconnect I can find out and store whether or not the client is connected to the server, but wouldn't it be easier if I could ask the client for that information?

If I create the wsClient and give it to the client at creation, I can keep it and check that with wsClient.IsConnected(), but that breaks the encapsulation in an ugly way, IMO.

Is there another way to do it, or wouldn't it be nice to have a IsConnected()-function on the client (just sending the question on to endpoint/wsClient)?

Network error handling missing

As noted in the code: once a CP/CS was connected to, errors are not fully handled:

  • no reconnection or stopping
  • no timeout notification (could be asynchronously added based on #40)

Concurrent usage of OCPP 1.6 and OCPP 2.0.1

Hello there!

My company already uses this OCPP go library for OCCP 1.6.
We now want to support OCPP 2.0.1 as well which means we need to able to provide OCPP 1.6 and OCPP 2.0.1 concurrently.
We've already tested the OCPP 2.0.1 server against a client simulation and it worked like a charm.

Problem
We have a lot of custom logic in our code handling the connections, monitoring, and controlling of charging stations.
In many cases, we directly depend on the OCPP 1.6 packages from this library.
Right now, the OCPP 2.0.1 implementation was put in a new directory and thus, there are new packages for every feature.

Do you have any idea how we could face this issue? We really want to prevent to just copy/paste our current implementation and use the OCPP 2.0.1 packages.
Maybe, there there is a way to have a common CentralSystem or CSMS Interface?

Any help is appreciated. Thank you very much.

Panic when TriggerMessage and Change/Get Configuration are called at the same time

panic: interface conversion: ocpp.Response is *core.ChangeConfigurationConfirmation, not *remotetrigger.TriggerMessageConfirmation

The Problem

I've have a cron job that sends a TriggerMessage request to the charging station every 30 seconds, I also have a rest API that can call ChangeConfiguration and GetConfiguration on the charging station.
When either the cron job or the REST API invokes the CSMS there's no panic, but when both of them are invoking the above two calls - TriggerMessage and Change/Get Configuration respectively, then I get this panic
Screenshot 2021-04-21 at 2 05 43 PM

@lorenzodonini Can you point me in the right direction for a possible fix for this issue?

Though I'm yet to test if the latest changes to the library are something that can fix this issue. I'll test it out and make further comments about it.

OCPP Version

1.6

Configuration management

Hi @lorenzodonini,

this is not an issue, but rather a discussion. I've seen that you plan to add support for configuration management. I assume that is for OCPP Configuration Variables.

I've already created a very simplified version of a configuration library for managing the OCPP variables. Feel free to check it out and give feedback or use it as inspiration.

It still need some work (value validation), and it could definetly use some improvements in the API segment.

Go 1.18 - Type Parameters

[Off-topic; not related to bugs or feature suggestions]

Hi,

I heard that Go 1.18 which includes Type Parameters will be released in February 2022.

Do you think the library could potentially benefit from the feature? If you are so willing to entertain the possibility, how would the library be refactored to take advantage of this feature, if at all?

Abort sending request on dropped connection?

Is there a way to abort sending a request when the connection is dropped? I found that I can set a DisconnectedHandler to realize that the connection is dropped, but is there a way to cancel the outstanding request?

I know that I can use SetTimeout() on the dispatcher to abort the request and get an error, but it would be cleaner if the dropped connection could trigger that instead of a fixed timeout. Is there a way to do that?

Running CP against CS crashes CS

I'm running the CP against the CS from this repo and CS crashes after a couple of seconds:

CS:

    ❯ gor github.com/lorenzodonini/ocpp-go/example/1.6/cs
    INFO[2020-10-30T14:06:32+01:00] no valid SERVER_LISTEN_PORT environment variable found, using default port 
    INFO[2020-10-30T14:06:32+01:00] starting central system on port 8887         
    INFO[2020-10-30T14:06:44+01:00] new client on URL /evcc                      
    INFO[2020-10-30T14:06:44+01:00] new charge point connected                    client=/evcc
    INFO[2020-10-30T14:06:44+01:00] boot confirmed                                client=/evcc message=BootNotification
    INFO[2020-10-30T14:06:44+01:00] all connectors updated status to Available    client=/evcc message=StatusNotification
    INFO[2020-10-30T14:06:46+01:00] connector 1 reserved for client l33t until 2020-10-30T14:06:46Z (reservation ID 42)  client=/evcc message=ReserveNow
    INFO[2020-10-30T14:06:46+01:00] connector 1 updated status to Reserved        client=/evcc message=StatusNotification
    INFO[2020-10-30T14:06:47+01:00] reservation 42 canceled successfully          client=/evcc message=CancelReservation
    INFO[2020-10-30T14:06:47+01:00] connector 1 updated status to Available       client=/evcc message=StatusNotification
    INFO[2020-10-30T14:06:49+01:00] client authorized                             client=/evcc message=Authorize
    INFO[2020-10-30T14:06:49+01:00] connector 1 updated status to Preparing       client=/evcc message=StatusNotification
    INFO[2020-10-30T14:06:49+01:00] started transaction 0 for connector 1         client=/evcc message=StartTransaction
    INFO[2020-10-30T14:06:49+01:00] connector 1 updated status to Charging        client=/evcc message=StatusNotification
    INFO[2020-10-30T14:06:52+01:00] current local list version: 0                 client=/evcc message=GetLocalListVersion
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x12f786e]

    goroutine 23 [running]:
    main.(*CentralSystemHandler).OnMeterValues(0xc000010190, 0xc00025cfc4, 0x5, 0xc000235b00, 0x0, 0x0, 0x0)
            /Users/andig/htdocs/ocpp-go/example/1.6/cs/handler.go:89 +0x1ee
    github.com/lorenzodonini/ocpp-go/ocpp1%2e6.(*centralSystem).handleIncomingRequest.func1(0xc00023ee10, 0xb, 0xc000262850, 0xc00025cfc4, 0x5, 0x1408de0, 0xc000235b00, 0xc000233de0, 0xc000233df0, 0xc00023ee00, ...)
            /Users/andig/htdocs/ocpp-go/ocpp1.6/central_system.go:467 +0x342
    created by github.com/lorenzodonini/ocpp-go/ocpp1%2e6.(*centralSystem).handleIncomingRequest
            /Users/andig/htdocs/ocpp-go/ocpp1.6/central_system.go:456 +0x192
    exit status 2

CP:

    ❯ CENTRAL_SYSTEM_URL=ws://localhost:8887 CLIENT_ID=evcc gor github.com/lorenzodonini/ocpp-go/example/1.6/cp
    INFO[2020-10-30T14:06:44+01:00] connected to central system at ws://localhost:8887 
    INFO[2020-10-30T14:06:44+01:00] status: Accepted, interval: 600, current time: 2020-10-30 14:06:44 +0100 CET  message=BootNotification
    INFO[2020-10-30T14:06:44+01:00] status for all connectors updated to Available  message=StatusNotification
    INFO[2020-10-30T14:06:46+01:00] reservation 42 for connector 1 accepted       message=ReserveNow
    INFO[2020-10-30T14:06:46+01:00] status for connector 1 updated to Reserved    message=StatusNotification
    INFO[2020-10-30T14:06:47+01:00] reservation 42 for connector 1 canceled       message=CancelReservation
    INFO[2020-10-30T14:06:47+01:00] status for connector 1 updated to Available   message=StatusNotification
    INFO[2020-10-30T14:06:49+01:00] status: Accepted                              message=Authorize
    INFO[2020-10-30T14:06:49+01:00] status for connector 1 updated to Preparing   message=StatusNotification
    INFO[2020-10-30T14:06:49+01:00] status: Accepted, transaction 0               message=StartTransaction
    INFO[2020-10-30T14:06:49+01:00] status for connector 1 updated to Charging    message=StatusNotification
    INFO[2020-10-30T14:06:52+01:00] returning current local list version: 0       message=GetLocalListVersion
    ^Csignal: interrupt

Central System concurrent requests

Callbacks currently get overwritten, in case a further request to the same Charge Point is sent, before the response to a previous one was received.

This may be solved by using a very simple callback queue, as a follow-up work to #56.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.