|
| 1 | +// Example demonstrating protocol multiplexing for OCPP 1.6 and 2.0.1 on a single port. |
| 2 | +// |
| 3 | +// This server can handle both OCPP 1.6 charge points and OCPP 2.0.1 charging stations |
| 4 | +// simultaneously using WebSocket subprotocol negotiation. |
| 5 | +// |
| 6 | +// When a client connects with "Sec-WebSocket-Protocol: ocpp2.0.1, ocpp1.6", |
| 7 | +// the server negotiates the first mutually-supported protocol. |
| 8 | +package main |
| 9 | + |
| 10 | +import ( |
| 11 | + "fmt" |
| 12 | + "time" |
| 13 | + |
| 14 | + "github.com/lorenzodonini/ocpp-go/multiplex" |
| 15 | + "github.com/lorenzodonini/ocpp-go/ocpp1.6/core" |
| 16 | + "github.com/lorenzodonini/ocpp-go/ocpp1.6/types" |
| 17 | + "github.com/lorenzodonini/ocpp-go/ocpp2.0.1/provisioning" |
| 18 | + types2 "github.com/lorenzodonini/ocpp-go/ocpp2.0.1/types" |
| 19 | + "github.com/lorenzodonini/ocpp-go/ws" |
| 20 | +) |
| 21 | + |
| 22 | +// OCPP 1.6 handler |
| 23 | +type ocpp16Handler struct{} |
| 24 | + |
| 25 | +func (h *ocpp16Handler) OnAuthorize(chargePointId string, request *core.AuthorizeRequest) (*core.AuthorizeConfirmation, error) { |
| 26 | + fmt.Printf("[OCPP 1.6] Authorize from %s: %s\n", chargePointId, request.IdTag) |
| 27 | + return core.NewAuthorizationConfirmation(types.NewIdTagInfo(types.AuthorizationStatusAccepted)), nil |
| 28 | +} |
| 29 | + |
| 30 | +func (h *ocpp16Handler) OnBootNotification(chargePointId string, request *core.BootNotificationRequest) (*core.BootNotificationConfirmation, error) { |
| 31 | + fmt.Printf("[OCPP 1.6] BootNotification from %s: %s %s\n", chargePointId, request.ChargePointVendor, request.ChargePointModel) |
| 32 | + return core.NewBootNotificationConfirmation(types.NewDateTime(time.Now()), 60, core.RegistrationStatusAccepted), nil |
| 33 | +} |
| 34 | + |
| 35 | +func (h *ocpp16Handler) OnDataTransfer(chargePointId string, request *core.DataTransferRequest) (*core.DataTransferConfirmation, error) { |
| 36 | + fmt.Printf("[OCPP 1.6] DataTransfer from %s\n", chargePointId) |
| 37 | + return core.NewDataTransferConfirmation(core.DataTransferStatusAccepted), nil |
| 38 | +} |
| 39 | + |
| 40 | +func (h *ocpp16Handler) OnHeartbeat(chargePointId string, request *core.HeartbeatRequest) (*core.HeartbeatConfirmation, error) { |
| 41 | + fmt.Printf("[OCPP 1.6] Heartbeat from %s\n", chargePointId) |
| 42 | + return core.NewHeartbeatConfirmation(types.NewDateTime(time.Now())), nil |
| 43 | +} |
| 44 | + |
| 45 | +func (h *ocpp16Handler) OnMeterValues(chargePointId string, request *core.MeterValuesRequest) (*core.MeterValuesConfirmation, error) { |
| 46 | + fmt.Printf("[OCPP 1.6] MeterValues from %s\n", chargePointId) |
| 47 | + return core.NewMeterValuesConfirmation(), nil |
| 48 | +} |
| 49 | + |
| 50 | +func (h *ocpp16Handler) OnStartTransaction(chargePointId string, request *core.StartTransactionRequest) (*core.StartTransactionConfirmation, error) { |
| 51 | + fmt.Printf("[OCPP 1.6] StartTransaction from %s\n", chargePointId) |
| 52 | + return core.NewStartTransactionConfirmation(types.NewIdTagInfo(types.AuthorizationStatusAccepted), 1), nil |
| 53 | +} |
| 54 | + |
| 55 | +func (h *ocpp16Handler) OnStatusNotification(chargePointId string, request *core.StatusNotificationRequest) (*core.StatusNotificationConfirmation, error) { |
| 56 | + fmt.Printf("[OCPP 1.6] StatusNotification from %s: connector %d is %s\n", chargePointId, request.ConnectorId, request.Status) |
| 57 | + return core.NewStatusNotificationConfirmation(), nil |
| 58 | +} |
| 59 | + |
| 60 | +func (h *ocpp16Handler) OnStopTransaction(chargePointId string, request *core.StopTransactionRequest) (*core.StopTransactionConfirmation, error) { |
| 61 | + fmt.Printf("[OCPP 1.6] StopTransaction from %s\n", chargePointId) |
| 62 | + return core.NewStopTransactionConfirmation(), nil |
| 63 | +} |
| 64 | + |
| 65 | +// OCPP 2.0.1 handler |
| 66 | +type ocpp201Handler struct{} |
| 67 | + |
| 68 | +func (h *ocpp201Handler) OnBootNotification(chargingStationId string, request *provisioning.BootNotificationRequest) (*provisioning.BootNotificationResponse, error) { |
| 69 | + fmt.Printf("[OCPP 2.0.1] BootNotification from %s: %s %s\n", chargingStationId, request.ChargingStation.VendorName, request.ChargingStation.Model) |
| 70 | + return provisioning.NewBootNotificationResponse(types2.NewDateTime(time.Now()), 60, provisioning.RegistrationStatusAccepted), nil |
| 71 | +} |
| 72 | + |
| 73 | +func (h *ocpp201Handler) OnNotifyReport(chargingStationId string, request *provisioning.NotifyReportRequest) (*provisioning.NotifyReportResponse, error) { |
| 74 | + fmt.Printf("[OCPP 2.0.1] NotifyReport from %s\n", chargingStationId) |
| 75 | + return provisioning.NewNotifyReportResponse(), nil |
| 76 | +} |
| 77 | + |
| 78 | +func main() { |
| 79 | + // Create multi-protocol server |
| 80 | + server := multiplex.NewMultiProtocolServer() |
| 81 | + |
| 82 | + // Register OCPP 1.6 handlers |
| 83 | + server.OCPP16Server().SetCoreHandler(&ocpp16Handler{}) |
| 84 | + |
| 85 | + // Register OCPP 2.0.1 handlers |
| 86 | + server.OCPP201Server().SetProvisioningHandler(&ocpp201Handler{}) |
| 87 | + |
| 88 | + // Track connections and their protocol versions |
| 89 | + server.SetNewClientHandler(func(channel ws.Channel) { |
| 90 | + fmt.Printf("New client connected: %s (protocol: %s)\n", channel.ID(), channel.Subprotocol()) |
| 91 | + }) |
| 92 | + |
| 93 | + server.SetDisconnectedClientHandler(func(channel ws.Channel) { |
| 94 | + fmt.Printf("Client disconnected: %s\n", channel.ID()) |
| 95 | + }) |
| 96 | + |
| 97 | + // Start listening on port 8080 |
| 98 | + fmt.Println("Starting multi-protocol OCPP server on :8080/ocpp/{id}") |
| 99 | + fmt.Println("Supported protocols: ocpp1.6, ocpp2.0.1") |
| 100 | + fmt.Println() |
| 101 | + fmt.Println("Clients can connect with either protocol. The server will negotiate") |
| 102 | + fmt.Println("based on the Sec-WebSocket-Protocol header.") |
| 103 | + |
| 104 | + server.Start(8080, "/ocpp/{id}") |
| 105 | +} |
0 commit comments