ocpp-go

repository·master·Indexed 18 days ago

https://github.com/lorenzodonini/ocpp-go

A Go implementation of the Open Charge Point Protocol (OCPP) for modern charge points and central systems. It supports OCPP 1.6, the 1.6 Security extension, and OCPP 2.0.1 using the OCPP-J (JSON over WebSockets) standard. The library provides tools to implement Central Systems (CS/CSMS) and Charge Points, including support for synchronous and asynchronous request patterns, message validation, and verbose logging.

Tokens
12.8K
Snippets
34
Records
49
Agent score
63%

What's inside ocpp-go

  1. Supported OCPP versions

    master

    The library supports the following versions of the Open Charge Point Protocol:

    • OCPP 1.6
    • OCPP 1.6 Security extension
    • OCPP 2.0.1 (Note: Examples are working, but real-world testing is ongoing)

    This library only supports OCPP-J (JSON over WebSockets). It does not support SOAP or OCPP-S.

  2. Configure Certificate-based authentication (mTLS)

    master
    The security extension supports mTLS (client certificates) for securing communication. While the library provides the necessary functionality to configure TLS, the actual implementation of mTLS (certificate management and handshake) is not in scope and must be handled by the user using the underlying websocket/TLS configuration.
  3. Generate mocks with mockery

    master

    The project uses mockery for generating mocks. To add mocks for new interfaces, follow these steps:

    1. Configure .mockery.yaml: Add the target package or interface to the .mockery.yaml configuration file.
      • Naming Convention: Mocks should generally be generated in the same package as the interface, use snake_case, and be suffixed with _mock.go (e.g., my_interface_mock.go).
    2. Execute mockery: Run the mockery command in your terminal to generate the files.

    Note: Mock generation is integrated into the CI pipeline. If your mocks are out of date, the tests will fail. It is recommended to run tests locally before pushing changes.

    mockery
  4. Enable OCPP 1.6 Security Extension in the Charge Point

    master

    To support the OCPP 1.6j Security Extension in your Charge Point, you must register a handler that implements the security-specific callbacks.

    Register the following handlers on your chargePoint instance:

    • SetCertificateHandler: For certificate management.
    • SetLogHandler: For logging functionality.
    • SetSecureFirmwareHandler: For secure firmware management.
    • SetExtendedTriggerMessageHandler: For extended trigger messages.
    • SetSecurityHandler: For security profile callbacks.
    handler := &ChargePointHandler{}
    // Support callbacks for all OCPP 1.6 profiles
    chargePoint.SetCoreHandler(handler)
    chargePoint.SetFirmwareManagementHandler(handler)
    chargePoint.SetLocalAuthListHandler(handler)
    chargePoint.SetReservationHandler(handler)
    chargePoint.SetRemoteTriggerHandler(handler)
    chargePoint.SetSmartChargingHandler(handler)
    // OCPP 1.6j Security extension
    chargePoint.SetCertificateHandler(handler)
    chargePoint.SetLogHandler(handler)
    chargePoint.SetSecureFirmwareHandler(handler)
    chargePoint.SetExtendedTriggerMessageHandler(handler)
    chargePoint.SetSecurityHandler(handler)
  5. Send requests from a Charge Point to the Central System

    master

    Charge Points can communicate with the Central System using three patterns:

    1. Simplified Synchronous API (Recommended): High-level methods like BootNotification that block until a response is received. This is the easiest way to handle requests.
    2. Manual Synchronous API: Create a request object (e.g., core.NewBootNotificationRequest) and use SendRequest(request). This returns a generic Response interface that requires type assertion to access specific confirmation fields.
    3. Manual Asynchronous API: Use SendRequestAsync(request, callback) to send a message without blocking the main thread. You must handle the response in a provided callback function.

    Note: When using manual methods, you must perform type assertion on the returned confirmation (e.g., confirmation.(*core.BootNotificationConfirmation)) because the APIs use generic interfaces.

    // 1. Simplified Synchronous (Recommended)
    bootConf, err := chargePoint.BootNotification("model1", "vendor1")
    if err != nil {
        log.Fatal(err)
    }
    
    // 2. Manual Synchronous
    request := core.NewBootNotificationRequest("model1", "vendor1")
    confirmation, err := chargePoint.SendRequest(request)
    if err == nil {
        bootConf := confirmation.(*core.BootNotificationConfirmation)
        log.Printf("status: %v", bootConf.Status)
    }
    
    // 3. Manual Asynchronous
    callback := func (confirmation ocpp.Response, e error) {
        if e != nil {
            log.Printf("operation failed: %v", e)
        } else {
            bootConf := confirmation.(*core.BootNotificationConfirmation)
            log.Printf("status: %v", bootConf.Status)
        }
    }
    err := chargePoint.SendRequestAsync(request, callback)
  6. Run OCPP 1.6 examples with Docker and TLS

    master

    The repository provides containerized examples for both the Central System and Charge Point.

    Central System

    Standard:

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

    Docker Compose:

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

    TLS enabled:

    1. Generate certificates using the utility script: cd example/1.6 && ./create-test-certificates.sh.
    2. Ensure certificates are in example/1.6/certs.
    3. Run: docker-compose -f example/1.6/docker-compose.tls.yml up central-system

    Charge Point

    Standard: You must provide the CENTRAL_SYSTEM_URL via environment variable.

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

    Docker:

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

    TLS enabled:

    docker-compose -f example/1.6/docker-compose.tls.yml up charge-point
  7. Enable OCPP 1.6 Security Extension in the Central System

    master

    To support the OCPP 1.6 Security Extension in your Central System, you must register a handler that implements the security-specific callbacks. You can use a single handler instance to satisfy both standard profile callbacks and security extension callbacks.

    Register the following handlers on your centralSystem instance:

    • SetSecurityHandler: For security profile callbacks.
    • SetSecureFirmwareHandler: For secure firmware management.
    • SetLogHandler: For logging functionality.
    // Support callbacks for all OCPP 1.6 profiles
    handler := &CentralSystemHandler{chargePoints: map[string]*ChargePointState{}}
    centralSystem.SetCoreHandler(handler)
    centralSystem.SetLocalAuthListHandler(handler)
    centralSystem.SetFirmwareManagementHandler(handler)
    centralSystem.SetReservationHandler(handler)
    centralSystem.SetRemoteTriggerHandler(handler)
    centralSystem.SetSmartChargingHandler(handler)
    
    // Add callbacks for OCPP 1.6 security profiles
    centralSystem.SetSecurityHandler(handler)
    centralSystem.SetSecureFirmwareHandler(handler)
    centralSystem.SetLogHandler(handler)
  8. Set up a Charging Station for OCPP 2.0.1

    master

    To implement a Charging Station (the 2.0.1 equivalent of a Charge Point), use ocpp2.NewChargingStation.

    Key steps:

    1. Initialize with ocpp2.NewChargingStation(chargingStationID, nil, nil).
    2. Register handlers for incoming requests from the CSMS using methods like SetAvailabilityHandler, SetSmartChargingHandler, etc.
    3. Connect to the CSMS using chargingStation.Start(csmsUrl).
    4. Use chargingStation.Stop() to disconnect.

    Note: The Start method for the Charging Station is non-blocking, allowing you to continue your program logic after a successful connection.

    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)
        // ... your program logic goes here
    }
    
    // Disconnect
    chargingStation.Stop()
  9. Implement a Central System (CS) in OCPP 1.6

    master

    To build a custom Central System, you must implement the callback interfaces defined in the profile packages (e.g., core.CentralSystemHandler).

    Each callback function is invoked in a dedicated goroutine, so you do not need to manage synchronization for the callback itself. For every request received, you must return either a confirmation object or an error; the library handles sending the response back to the charge point automatically.

    To initialize and start the server:

    1. Create a new instance using ocpp16.NewCentralSystem.
    2. Set connection handlers using SetNewChargePointHandler and SetChargePointDisconnectedHandler.
    3. Set your custom logic handler using SetCoreHandler.
    4. Call Start(listenPort, path) to begin listening. Note that Start is a blocking call that runs in daemon mode.
    centralSystem := ocpp16.NewCentralSystem(nil, nil)
    
    // Set connection handlers
    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 logic handler
    handler := &CentralSystemHandler{}
    centralSystem.SetCoreHandler(handler)
    
    // Start server (blocking)
    listenPort := 8887
    centralSystem.Start(listenPort, "/{ws}")
  10. Send requests from a Central System to a Charge Point

    master

    The Central System can send requests to connected charge points using two methods:

    1. Simplified API: High-level methods for common requests (e.g., ChangeAvailability). These are asynchronous and return immediately.
    2. Manual Request Creation: Create a request object using core.New<RequestName>Request and send it via SendRequestAsync. This allows for more granular control.

    In both cases, you must provide a callback function to handle the asynchronous confirmation or any errors that occur during transmission.

    // Option 1: Simplified API
    err := centralSystem.ChangeAvailability("1234", myCallback, 1, core.AvailabilityTypeInoperative)
    
    // Option 2: Manual Request
    request := core.NewChangeAvailabilityRequest(1, core.AvailabilityTypeInoperative)
    err := centralSystem.SendRequestAsync("clientId", request, callbackFunction)
    
    // Callback implementation
    myCallback := func (confirmation *core.ChangeAvailabilityConfirmation, e error) {
        if e != nil {
            log.Printf("operation failed: %v", e)
        } else {
            log.Printf("status: %v", confirmation.Status)
        }
    }
  11. Install ocpp-go

    master

    To use ocpp-go in your project, ensure you are using Go version 1.13 or higher. You can install the library and its dependencies using the following commands:

    go get github.com/lorenzodonini/ocpp-go
    
    # After downloading, fetch dependencies
    cd <path-to-ocpp-go>
    export GO111MODULE=on
    go mod download