Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move ocpp.FormationViolation to Endpoint (BC) #232

Merged
merged 14 commits into from
Oct 22, 2023
9 changes: 9 additions & 0 deletions ocpp/ocpp.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,12 @@ func (p *Profile) ParseResponse(featureName string, rawResponse interface{}, res
responseType := feature.GetResponseType()
return responseParser(rawResponse, responseType)
}

// Dialect is the OCPP version the Endpoint supports
type Dialect int

const (
_ Dialect = iota
V16
V2
)
6 changes: 5 additions & 1 deletion ocpp1.6/central_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,11 +405,15 @@ func (cs *centralSystem) SendRequestAsync(clientId string, request ocpp.Request,

func (cs *centralSystem) Start(listenPort int, listenPath string) {
// Overriding some protocol-specific values in the lower layers globally
ocppj.FormationViolation = ocppj.FormatViolationV16
cs.server.Endpoint.Dialect = ocpp.V16
// Start server
cs.server.Start(listenPort, listenPath)
}

func (cs *centralSystem) Dialect() ocpp.Dialect {
return cs.server.Endpoint.Dialect
}

func (cs *centralSystem) sendResponse(chargePointId string, confirmation ocpp.Response, err error, requestId string) {
if err != nil {
// Send error response
Expand Down
2 changes: 1 addition & 1 deletion ocpp1.6/charge_point.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,7 +334,7 @@ func (cp *chargePoint) sendResponse(confirmation ocpp.Response, err error, reque

func (cp *chargePoint) Start(centralSystemUrl string) error {
// Overriding some protocol-specific values in the lower layers globally
ocppj.FormationViolation = ocppj.FormatViolationV16
cp.client.Endpoint.Dialect = ocpp.V16
// Start client
cp.stopC = make(chan struct{}, 1)
err := cp.client.Start(centralSystemUrl)
Expand Down
4 changes: 3 additions & 1 deletion ocpp1.6/v16.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,11 +249,13 @@ type CentralSystem interface {
SendRequestAsync(clientId string, request ocpp.Request, callback func(ocpp.Response, error)) error
// Starts running the central system on the specified port and URL.
// The central system runs as a daemon and handles incoming charge point connections and messages.
//

// The function blocks forever, so it is suggested to wrap it in a goroutine, in case other functionality needs to be executed on the main program thread.
Start(listenPort int, listenPath string)
// Errors returns a channel for error messages. If it doesn't exist it es created.
Errors() <-chan error
// Dialect returns the endpoint dialect.
Dialect() ocpp.Dialect
}

// Creates a new OCPP 1.6 central system.
Expand Down
7 changes: 3 additions & 4 deletions ocpp1.6_test/ocpp16_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -670,8 +670,8 @@ type OcppV16TestSuite struct {
ocppjCentralSystem *ocppj.Server
mockWsServer *MockWebsocketServer
mockWsClient *MockWebsocketClient
chargePoint ocpp16.ChargePoint
centralSystem ocpp16.CentralSystem
chargePoint ocpp16.ChargePoint // ocpp16.chargePoint
centralSystem ocpp16.CentralSystem // ocpp16.centralSystem
messageIdGenerator TestRandomIdGenerator
clientDispatcher ocppj.ClientDispatcher
serverDispatcher ocppj.ServerDispatcher
Expand Down Expand Up @@ -720,8 +720,7 @@ func (suite *OcppV16TestSuite) TestIsConnected() {
assert.False(t, suite.chargePoint.IsConnected())
}

//TODO: implement generic protocol tests

// TODO: implement generic protocol tests
func TestOcpp16Protocol(t *testing.T) {
suite.Run(t, new(OcppV16TestSuite))
}
2 changes: 1 addition & 1 deletion ocpp1.6_test/proto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,5 +157,5 @@ func (suite *OcppV16TestSuite) TestErrorCodes() {
t := suite.T()
suite.mockWsServer.On("Start", mock.AnythingOfType("int"), mock.AnythingOfType("string")).Return(nil)
suite.centralSystem.Start(8887, "somePath")
assert.Equal(t, ocppj.FormatViolationV16, ocppj.FormationViolation)
assert.Equal(t, ocppj.FormatViolationV16, ocppj.FormatErrorForDialect(suite.centralSystem.Dialect()))
}
2 changes: 1 addition & 1 deletion ocpp2.0.1/charging_station.go
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ func (cs *chargingStation) sendResponse(response ocpp.Response, err error, reque

func (cs *chargingStation) Start(csmsUrl string) error {
// Overriding some protocol-specific values in the lower layers globally
ocppj.FormationViolation = ocppj.FormatViolationV2
cs.client.Endpoint.Dialect = ocpp.V2
// Start client
cs.stopC = make(chan struct{}, 1)
err := cs.client.Start(csmsUrl)
Expand Down
6 changes: 5 additions & 1 deletion ocpp2.0.1/csms.go
Original file line number Diff line number Diff line change
Expand Up @@ -811,11 +811,15 @@ func (cs *csms) SendRequestAsync(clientId string, request ocpp.Request, callback

func (cs *csms) Start(listenPort int, listenPath string) {
// Overriding some protocol-specific values in the lower layers globally
ocppj.FormationViolation = ocppj.FormatViolationV2
cs.server.Endpoint.Dialect = ocpp.V16
andig marked this conversation as resolved.
Show resolved Hide resolved
// Start server
cs.server.Start(listenPort, listenPath)
}

func (cs *csms) Dialect() ocpp.Dialect {
return cs.server.Endpoint.Dialect
}

func (cs *csms) sendResponse(chargingStationID string, response ocpp.Response, err error, requestId string) {
if err != nil {
// Send error response
Expand Down
8 changes: 6 additions & 2 deletions ocpp2.0.1/v2.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@ type ChargingStationConnection interface {
TLSConnectionState() *tls.ConnectionState
}

type ChargingStationValidationHandler ws.CheckClientHandler
type ChargingStationConnectionHandler func(chargePoint ChargingStationConnection)
type (
ChargingStationValidationHandler ws.CheckClientHandler
ChargingStationConnectionHandler func(chargePoint ChargingStationConnection)
)

// -------------------- v2.0 Charging Station --------------------

Expand Down Expand Up @@ -384,6 +386,8 @@ type CSMS interface {
Start(listenPort int, listenPath string)
// Errors returns a channel for error messages. If it doesn't exist it es created.
Errors() <-chan error
// Dialect returns the endpoint dialect.
Dialect() ocpp.Dialect
}

// Creates a new OCPP 2.0 CSMS.
Expand Down
2 changes: 1 addition & 1 deletion ocpp2.0.1_test/proto_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,5 +162,5 @@ func (suite *OcppV2TestSuite) TestErrorCodes() {
t := suite.T()
suite.mockWsServer.On("Start", mock.AnythingOfType("int"), mock.AnythingOfType("string")).Return(nil)
suite.csms.Start(8887, "somePath")
assert.Equal(t, ocppj.FormatViolationV2, ocppj.FormationViolation)
assert.Equal(t, ocppj.FormatViolationV2, ocppj.FormatErrorForDialect(suite.csms.Dialect()))
}
8 changes: 4 additions & 4 deletions ocppj/central_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ func (suite *OcppJTestSuite) TestCentralSystemInvalidMessageHook() {
serializedPayload, err := json.Marshal(mockPayload)
require.NoError(t, err)
invalidMessage := fmt.Sprintf("[2,\"%v\",\"%s\",%v]", mockID, MockFeatureName, string(serializedPayload))
expectedError := fmt.Sprintf("[4,\"%v\",\"%v\",\"%v\",{}]", mockID, ocppj.FormationViolation, "json: cannot unmarshal number into Go struct field MockRequest.mockValue of type string")
expectedError := fmt.Sprintf("[4,\"%v\",\"%v\",\"%v\",{}]", mockID, ocppj.FormatErrorForDialect(suite.centralSystem.Endpoint.Dialect), "json: cannot unmarshal number into Go struct field MockRequest.mockValue of type string")
writeHook := suite.mockServer.On("Write", mockChargePointId, mock.Anything).Return(nil).Run(func(args mock.Arguments) {
data := args.Get(1).([]byte)
assert.Equal(t, expectedError, string(data))
Expand All @@ -138,7 +138,7 @@ func (suite *OcppJTestSuite) TestCentralSystemInvalidMessageHook() {
err = suite.mockServer.MessageHandler(mockChargePoint, []byte(invalidMessage))
ocppErr, ok := err.(*ocpp.Error)
require.True(t, ok)
assert.Equal(t, ocppj.FormationViolation, ocppErr.Code)
assert.Equal(t, ocppj.FormatErrorForDialect(suite.centralSystem.Endpoint.Dialect), ocppErr.Code)
// Setup hook 2
mockError := ocpp.NewError(ocppj.InternalError, "custom error", mockID)
expectedError = fmt.Sprintf("[4,\"%v\",\"%v\",\"%v\",{}]", mockError.MessageId, mockError.Code, mockError.Description)
Expand Down Expand Up @@ -197,7 +197,7 @@ func (suite *OcppJTestSuite) TestCentralSystemSendRequestFailed() {
suite.serverDispatcher.CreateClient(mockChargePointId)
mockRequest := newMockRequest("mockValue")
err := suite.centralSystem.SendRequest(mockChargePointId, mockRequest)
//TODO: currently the network error is not returned by SendRequest, but is only generated internally
// TODO: currently the network error is not returned by SendRequest, but is only generated internally
assert.Nil(t, err)
// Assert that pending request was removed
time.Sleep(500 * time.Millisecond)
Expand Down Expand Up @@ -578,7 +578,7 @@ func (suite *OcppJTestSuite) TestEnqueueMultipleRequests() {
assert.False(t, q.IsEmpty())
assert.Equal(t, messagesToQueue, q.Size())
// Analyze enqueued bundle
var i = 0
var i int
for !q.IsEmpty() {
popped := q.Pop()
require.NotNil(t, popped)
Expand Down
8 changes: 4 additions & 4 deletions ocppj/charge_point_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (suite *OcppJTestSuite) TestChargePointInvalidMessageHook() {
serializedPayload, err := json.Marshal(mockPayload)
require.NoError(t, err)
invalidMessage := fmt.Sprintf("[2,\"%v\",\"%s\",%v]", mockID, MockFeatureName, string(serializedPayload))
expectedError := fmt.Sprintf("[4,\"%v\",\"%v\",\"%v\",{}]", mockID, ocppj.FormationViolation, "json: cannot unmarshal number into Go struct field MockRequest.mockValue of type string")
expectedError := fmt.Sprintf("[4,\"%v\",\"%v\",\"%v\",{}]", mockID, ocppj.FormatErrorForDialect(suite.chargePoint.Endpoint.Dialect), "json: cannot unmarshal number into Go struct field MockRequest.mockValue of type string")
writeHook := suite.mockClient.On("Write", mock.Anything).Return(nil).Run(func(args mock.Arguments) {
data := args.Get(0).([]byte)
assert.Equal(t, expectedError, string(data))
Expand All @@ -142,7 +142,7 @@ func (suite *OcppJTestSuite) TestChargePointInvalidMessageHook() {
err = suite.mockClient.MessageHandler([]byte(invalidMessage))
ocppErr, ok := err.(*ocpp.Error)
require.True(t, ok)
assert.Equal(t, ocppj.FormationViolation, ocppErr.Code)
assert.Equal(t, ocppj.FormatErrorForDialect(suite.chargePoint.Endpoint.Dialect), ocppErr.Code)
// Setup hook 2
mockError := ocpp.NewError(ocppj.InternalError, "custom error", mockID)
expectedError = fmt.Sprintf("[4,\"%v\",\"%v\",\"%v\",{}]", mockError.MessageId, mockError.Code, mockError.Description)
Expand Down Expand Up @@ -193,7 +193,7 @@ func (suite *OcppJTestSuite) TestChargePointSendRequestFailed() {
_ = suite.chargePoint.Start("someUrl")
mockRequest := newMockRequest("mockValue")
err := suite.chargePoint.SendRequest(mockRequest)
//TODO: currently the network error is not returned by SendRequest, but is only generated internally
// TODO: currently the network error is not returned by SendRequest, but is only generated internally
assert.Nil(t, err)
// Assert that pending request was removed
time.Sleep(500 * time.Millisecond)
Expand Down Expand Up @@ -472,7 +472,7 @@ func (suite *OcppJTestSuite) TestClientEnqueueMultipleRequests() {
require.False(t, suite.clientRequestQueue.IsEmpty())
assert.Equal(t, messagesToQueue, suite.clientRequestQueue.Size())
// Analyze enqueued bundle
var i = 0
var i int
for !suite.clientRequestQueue.IsEmpty() {
popped := suite.clientRequestQueue.Pop()
require.NotNil(t, popped)
Expand Down
34 changes: 21 additions & 13 deletions ocppj/ocppj.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,14 +198,21 @@ const (
FormatViolationV16 ocpp.ErrorCode = "FormationViolation" // Payload for Action is syntactically incorrect or not conform the PDU structure for Action. This is only valid for OCPP 1.6
)

var (
FormationViolation = FormatViolationV16 // Used as constant, but can be overwritten depending on protocol version. Sett FormatViolationV16 and FormatViolationV2.
)
func FormatErrorForDialect(d ocpp.Dialect) ocpp.ErrorCode {
switch d {
case ocpp.V16:
return FormatViolationV16
case ocpp.V2:
return FormatViolationV2
default:
panic(fmt.Sprintf("invalid dialect: %v", d))
}
}

func IsErrorCodeValid(fl validator.FieldLevel) bool {
andig marked this conversation as resolved.
Show resolved Hide resolved
code := ocpp.ErrorCode(fl.Field().String())
switch code {
case NotImplemented, NotSupported, InternalError, MessageTypeNotSupported, ProtocolError, SecurityError, FormationViolation, PropertyConstraintViolation, OccurrenceConstraintViolation, TypeConstraintViolation, GenericError:
case NotImplemented, NotSupported, InternalError, MessageTypeNotSupported, ProtocolError, SecurityError, FormatViolationV16, FormatViolationV2, PropertyConstraintViolation, OccurrenceConstraintViolation, TypeConstraintViolation, GenericError:
return true
}
return false
Expand Down Expand Up @@ -308,6 +315,7 @@ func jsonMarshal(t interface{}) ([]byte, error) {
// An OCPP-J endpoint is one of the two entities taking part in the communication.
// The endpoint keeps state for supported OCPP profiles and current pending requests.
type Endpoint struct {
Dialect ocpp.Dialect
Profiles []*ocpp.Profile
}

Expand Down Expand Up @@ -378,25 +386,25 @@ func parseRawJsonConfirmation(raw interface{}, confirmationType reflect.Type) (o
func (endpoint *Endpoint) ParseMessage(arr []interface{}, pendingRequestState ClientState) (Message, error) {
// Checking message fields
if len(arr) < 3 {
return nil, ocpp.NewError(FormationViolation, "Invalid message. Expected array length >= 3", "")
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), "Invalid message. Expected array length >= 3", "")
}
rawTypeId, ok := arr[0].(float64)
if !ok {
return nil, ocpp.NewError(FormationViolation, fmt.Sprintf("Invalid element %v at 0, expected message type (int)", arr[0]), "")
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), fmt.Sprintf("Invalid element %v at 0, expected message type (int)", arr[0]), "")
}
typeId := MessageType(rawTypeId)
uniqueId, ok := arr[1].(string)
if !ok {
return nil, ocpp.NewError(FormationViolation, fmt.Sprintf("Invalid element %v at 1, expected unique ID (string)", arr[1]), "")
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), fmt.Sprintf("Invalid element %v at 1, expected unique ID (string)", arr[1]), uniqueId)
}
// Parse message
if typeId == CALL {
if len(arr) != 4 {
return nil, ocpp.NewError(FormationViolation, "Invalid Call message. Expected array length 4", uniqueId)
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), "Invalid Call message. Expected array length 4", uniqueId)
}
action, ok := arr[2].(string)
if !ok {
return nil, ocpp.NewError(FormationViolation, fmt.Sprintf("Invalid element %v at 2, expected action (string)", arr[2]), uniqueId)
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), fmt.Sprintf("Invalid element %v at 2, expected action (string)", arr[2]), uniqueId)
}

profile, ok := endpoint.GetProfileForFeature(action)
Expand All @@ -405,7 +413,7 @@ func (endpoint *Endpoint) ParseMessage(arr []interface{}, pendingRequestState Cl
}
request, err := profile.ParseRequest(action, arr[3], parseRawJsonRequest)
if err != nil {
return nil, ocpp.NewError(FormationViolation, err.Error(), uniqueId)
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), err.Error(), uniqueId)
}
call := Call{
MessageTypeId: CALL,
Expand All @@ -427,7 +435,7 @@ func (endpoint *Endpoint) ParseMessage(arr []interface{}, pendingRequestState Cl
profile, _ := endpoint.GetProfileForFeature(request.GetFeatureName())
confirmation, err := profile.ParseResponse(request.GetFeatureName(), arr[2], parseRawJsonConfirmation)
if err != nil {
return nil, ocpp.NewError(FormationViolation, err.Error(), uniqueId)
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), err.Error(), uniqueId)
}
callResult := CallResult{
MessageTypeId: CALL_RESULT,
Expand All @@ -446,15 +454,15 @@ func (endpoint *Endpoint) ParseMessage(arr []interface{}, pendingRequestState Cl
return nil, nil
}
if len(arr) < 4 {
return nil, ocpp.NewError(FormationViolation, "Invalid Call Error message. Expected array length >= 4", uniqueId)
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), "Invalid Call Error message. Expected array length >= 4", uniqueId)
}
var details interface{}
if len(arr) > 4 {
details = arr[4]
}
rawErrorCode, ok := arr[2].(string)
if !ok {
return nil, ocpp.NewError(FormationViolation, fmt.Sprintf("Invalid element %v at 2, expected rawErrorCode (string)", arr[2]), rawErrorCode)
return nil, ocpp.NewError(FormatErrorForDialect(endpoint.Dialect), fmt.Sprintf("Invalid element %v at 2, expected rawErrorCode (string)", arr[2]), rawErrorCode)
}
errorCode := ocpp.ErrorCode(rawErrorCode)
errorDescription := ""
Expand Down
Loading
Loading