Skip to content

Commit 9951a0a

Browse files
committed
feat: added support for send and call result err events
1 parent 6dc755a commit 9951a0a

File tree

3 files changed

+164
-2
lines changed

3 files changed

+164
-2
lines changed

ocppj/client.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ocppj
33
import (
44
"errors"
55
"fmt"
6+
"strings"
67

78
"gopkg.in/go-playground/validator.v9"
89

@@ -208,6 +209,35 @@ func (c *Client) SendRequest(request ocpp.Request) error {
208209
return nil
209210
}
210211

212+
func (c *Client) SendEvent(request ocpp.Request) error {
213+
if !c.dispatcher.IsRunning() {
214+
return fmt.Errorf("ocppj client is not started, couldn't send request")
215+
}
216+
217+
// Check if the request feature is a Stream feature
218+
feature := request.GetFeatureName()
219+
if !strings.HasSuffix(feature, "Stream") {
220+
return fmt.Errorf("ocppj client can only send events for Stream features, got: %s", feature)
221+
}
222+
223+
call, err := c.CreateCall(request)
224+
if err != nil {
225+
return err
226+
}
227+
jsonMessage, err := call.MarshalJSON()
228+
if err != nil {
229+
return err
230+
}
231+
// Message will be processed by dispatcher. A dedicated mechanism allows to delegate the message queue handling.
232+
if err = c.dispatcher.SendRequest(RequestBundle{Call: call, Data: jsonMessage}); err != nil {
233+
log.Errorf("error dispatching request [%s, %s]: %v", call.UniqueId, call.Action, err)
234+
return err
235+
}
236+
237+
log.Debugf("enqueued CALL [%s, %s]", call.UniqueId, call.Action)
238+
return nil
239+
}
240+
211241
// Sends an OCPP Response to the server.
212242
// The requestID parameter is required and identifies the previously received request.
213243
//
@@ -313,6 +343,12 @@ func (c *Client) ocppMessageHandler(data []byte) error {
313343
if c.errorHandler != nil {
314344
c.errorHandler(ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails)
315345
}
346+
case CALL_RESULT_ERROR:
347+
callError := message.(*CallResultError)
348+
log.Debugf("handling incoming CALL RESULT ERROR [%s]", callError.UniqueId)
349+
if c.errorHandler != nil {
350+
c.errorHandler(ocpp.NewError(callError.ErrorCode, callError.ErrorDescription, callError.UniqueId), callError.ErrorDetails)
351+
}
316352
}
317353
}
318354
return nil

ocppj/ocppj.go

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,41 @@ func (callError *CallError) MarshalJSON() ([]byte, error) {
182182
return ocppMessageToJson(fields)
183183
}
184184

185-
// -------------------- Call --------------------
185+
// -------------------- Call Result Error --------------------
186+
187+
// An OCPP-J CallResultError message, containing an OCPP Result Error.
188+
type CallResultError struct {
189+
Message
190+
MessageTypeId MessageType `json:"messageTypeId" validate:"required,eq=4"`
191+
UniqueId string `json:"uniqueId" validate:"required,max=36"`
192+
ErrorCode ocpp.ErrorCode `json:"errorCode" validate:"errorCode"`
193+
ErrorDescription string `json:"errorDescription" validate:"omitempty,max=255"`
194+
ErrorDetails interface{} `json:"errorDetails" validate:"omitempty"`
195+
}
196+
197+
func (callError *CallResultError) GetMessageTypeId() MessageType {
198+
return callError.MessageTypeId
199+
}
200+
201+
func (callError *CallResultError) GetUniqueId() string {
202+
return callError.UniqueId
203+
}
204+
205+
func (callError *CallResultError) MarshalJSON() ([]byte, error) {
206+
fields := make([]interface{}, 5)
207+
fields[0] = int(callError.MessageTypeId)
208+
fields[1] = callError.UniqueId
209+
fields[2] = callError.ErrorCode
210+
fields[3] = callError.ErrorDescription
211+
if callError.ErrorDetails == nil {
212+
fields[4] = struct{}{}
213+
} else {
214+
fields[4] = callError.ErrorDetails
215+
}
216+
return ocppMessageToJson(fields)
217+
}
218+
219+
// -------------------- Send --------------------
186220

187221
// An OCPP-J SEND message, containing an OCPP Request.
188222
type Send struct {
@@ -259,7 +293,7 @@ func IsErrorCodeValid(fl validator.FieldLevel) bool {
259293
case NotImplemented, NotSupported, InternalError, MessageTypeNotSupported,
260294
ProtocolError, SecurityError, FormatViolationV16, FormatViolationV2,
261295
PropertyConstraintViolation, OccurrenceConstraintViolationV16, OccurrenceConstraintViolationV2,
262-
TypeConstraintViolation, GenericError:
296+
TypeConstraintViolation, GenericError, RpcFrameworkError:
263297
return true
264298
}
265299
return false
@@ -526,6 +560,47 @@ func (endpoint *Endpoint) CreateCall(request ocpp.Request) (*Call, error) {
526560
return &call, nil
527561
}
528562

563+
func (endpoint *Endpoint) CreateSend(request ocpp.Request) (*Send, error) {
564+
action := request.GetFeatureName()
565+
profile, _ := endpoint.GetProfileForFeature(action)
566+
if profile == nil {
567+
return nil, fmt.Errorf("Couldn't create Send for unsupported action %v", action)
568+
}
569+
// TODO: handle collisions?
570+
uniqueId := messageIdGenerator()
571+
call := Send{
572+
MessageTypeId: SEND,
573+
UniqueId: uniqueId,
574+
Action: action,
575+
Payload: request,
576+
}
577+
if validationEnabled {
578+
err := Validate.Struct(call)
579+
if err != nil {
580+
return nil, err
581+
}
582+
}
583+
return &call, nil
584+
}
585+
586+
// Creates a CallResultError message, given the message's unique ID and the error.
587+
func (endpoint *Endpoint) CreateCallResultError(uniqueId string, code ocpp.ErrorCode, description string, details interface{}) (*CallResultError, error) {
588+
callError := CallResultError{
589+
MessageTypeId: CALL_RESULT_ERROR,
590+
UniqueId: uniqueId,
591+
ErrorCode: code,
592+
ErrorDescription: description,
593+
ErrorDetails: details,
594+
}
595+
if validationEnabled {
596+
err := Validate.Struct(callError)
597+
if err != nil {
598+
return nil, err
599+
}
600+
}
601+
return &callError, nil
602+
}
603+
529604
// Creates a CallResult message, given an OCPP response and the message's unique ID.
530605
//
531606
// Returns an error in case the response's feature is not supported on this endpoint.

ocppj/ocppj_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,15 @@ func CheckCall(call *ocppj.Call, t *testing.T, expectedAction string, expectedId
292292
assert.Nil(t, err)
293293
}
294294

295+
func CheckSend(call *ocppj.Send, t *testing.T, expectedAction string, expectedId string) {
296+
assert.Equal(t, ocppj.SEND, call.GetMessageTypeId())
297+
assert.Equal(t, expectedAction, call.Action)
298+
assert.Equal(t, expectedId, call.GetUniqueId())
299+
assert.NotNil(t, call.Payload)
300+
err := Validate.Struct(call)
301+
assert.Nil(t, err)
302+
}
303+
295304
func ParseCallResult(endpoint *ocppj.Endpoint, state ocppj.ClientState, json string, t *testing.T) *ocppj.CallResult {
296305
parsedData, err := ocppj.ParseJsonMessage(json)
297306
require.NoError(t, err)
@@ -336,6 +345,16 @@ func CheckCallError(t *testing.T, callError *ocppj.CallError, expectedId string,
336345
assert.Nil(t, err)
337346
}
338347

348+
func CheckCallResultError(t *testing.T, callError *ocppj.CallResultError, expectedId string, expectedError ocpp.ErrorCode, expectedDescription string, expectedDetails interface{}) {
349+
assert.Equal(t, ocppj.CALL_RESULT_ERROR, callError.GetMessageTypeId())
350+
assert.Equal(t, expectedId, callError.GetUniqueId())
351+
assert.Equal(t, expectedError, callError.ErrorCode)
352+
assert.Equal(t, expectedDescription, callError.ErrorDescription)
353+
assert.Equal(t, expectedDetails, callError.ErrorDetails)
354+
err := Validate.Struct(callError)
355+
assert.Nil(t, err)
356+
}
357+
339358
func assertPanic(t *testing.T, f func(), recoveredAssertion func(interface{})) {
340359
defer func() {
341360
r := recover()
@@ -496,6 +515,23 @@ func (suite *OcppJTestSuite) TestCreateCall() {
496515
assert.Nil(t, pendingRequest)
497516
}
498517

518+
func (suite *OcppJTestSuite) TestCreateSend() {
519+
t := suite.T()
520+
mockValue := "somevalue"
521+
request := newMockRequest(mockValue)
522+
call, err := suite.chargePoint.CreateSend(request)
523+
assert.Nil(t, err)
524+
CheckSend(call, t, MockFeatureName+"Stream", call.UniqueId)
525+
message, ok := call.Payload.(*MockRequest)
526+
assert.True(t, ok)
527+
assert.NotNil(t, message)
528+
assert.Equal(t, mockValue, message.MockValue)
529+
// Check that request was not yet stored as pending request
530+
pendingRequest, exists := suite.chargePoint.RequestState.GetPendingRequest(call.UniqueId)
531+
assert.False(t, exists)
532+
assert.Nil(t, pendingRequest)
533+
}
534+
499535
func (suite *OcppJTestSuite) TestCreateCallResult() {
500536
t := suite.T()
501537
mockValue := "someothervalue"
@@ -525,6 +561,21 @@ func (suite *OcppJTestSuite) TestCreateCallError() {
525561
CheckCallError(t, callError, mockUniqueId, ocppj.GenericError, mockDescription, mockDetails)
526562
}
527563

564+
func (suite *OcppJTestSuite) TestCreateCallResultError() {
565+
t := suite.T()
566+
mockUniqueId := "123456"
567+
mockDescription := "somedescription"
568+
mockDetailString := "somedetailstring"
569+
type MockDetails struct {
570+
DetailString string
571+
}
572+
mockDetails := MockDetails{DetailString: mockDetailString}
573+
callError, err := suite.chargePoint.CreateCallResultError(mockUniqueId, ocppj.GenericError, mockDescription, mockDetails)
574+
assert.Nil(t, err)
575+
assert.NotNil(t, callError)
576+
CheckCallResultError(t, callError, mockUniqueId, ocppj.GenericError, mockDescription, mockDetails)
577+
}
578+
528579
func (suite *OcppJTestSuite) TestParseMessageInvalidLength() {
529580
t := suite.T()
530581
mockMessage := make([]interface{}, 2)

0 commit comments

Comments
 (0)