From e94b22ff7d81c4fb25205f1f1e720778408460d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergejs=20Derka=C4=8Ds?= Date: Mon, 27 Oct 2025 20:10:13 +0200 Subject: [PATCH] Make CompositeSchedule (2.0.1) type to comply with the schema --- .../smartcharging/get_composite_schedule.go | 7 +- ocpp2.0.1/types/types.go | 1 + ocpp2.0.1_test/common_test.go | 2 + ocpp2.0.1_test/get_composite_schedule_test.go | 96 ++++++++++++------- 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/ocpp2.0.1/smartcharging/get_composite_schedule.go b/ocpp2.0.1/smartcharging/get_composite_schedule.go index 0827b85a..8c02f412 100644 --- a/ocpp2.0.1/smartcharging/get_composite_schedule.go +++ b/ocpp2.0.1/smartcharging/get_composite_schedule.go @@ -30,8 +30,11 @@ func isValidGetCompositeScheduleStatus(fl validator.FieldLevel) bool { } type CompositeSchedule struct { - StartDateTime *types.DateTime `json:"startDateTime,omitempty" validate:"omitempty"` - ChargingSchedule *types.ChargingSchedule `json:"chargingSchedule,omitempty" validate:"omitempty"` + ChargingSchedulePeriod []types.ChargingSchedulePeriod `json:"chargingSchedulePeriod" validate:"required,min=1"` + EvseID int `json:"evseId" validate:"required,gte=0"` + Duration int `json:"duration" validate:"required,gte=0"` + ScheduleStart types.DateTime `json:"scheduleStart" validate:"required"` + ChargingRateUnit types.ChargingRateUnitType `json:"chargingRateUnit" validate:"required,chargingRateUnit201"` } // The field definition of the GetCompositeSchedule request payload sent by the CSMS to the Charging System. diff --git a/ocpp2.0.1/types/types.go b/ocpp2.0.1/types/types.go index 017bf1c2..a8e6a6fc 100644 --- a/ocpp2.0.1/types/types.go +++ b/ocpp2.0.1/types/types.go @@ -500,6 +500,7 @@ type ChargingSchedulePeriod struct { StartPeriod int `json:"startPeriod" validate:"gte=0"` Limit float64 `json:"limit" validate:"gte=0"` NumberPhases *int `json:"numberPhases,omitempty" validate:"omitempty,gte=0"` + PhaseToUse *int `json:"phaseToUse,omitempty" validate:"omitempty,gte=1,lte=3"` } func NewChargingSchedulePeriod(startPeriod int, limit float64) ChargingSchedulePeriod { diff --git a/ocpp2.0.1_test/common_test.go b/ocpp2.0.1_test/common_test.go index d3adf79f..7f7c16bb 100644 --- a/ocpp2.0.1_test/common_test.go +++ b/ocpp2.0.1_test/common_test.go @@ -89,6 +89,8 @@ func (suite *OcppV2TestSuite) TestChargingSchedulePeriodValidation() { {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: -1.0}, false}, {types.ChargingSchedulePeriod{StartPeriod: -1, Limit: 10.0}, false}, {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: newInt(-1)}, false}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: newInt(3), PhaseToUse: newInt(0)}, false}, + {types.ChargingSchedulePeriod{StartPeriod: 0, Limit: 10.0, NumberPhases: newInt(3), PhaseToUse: newInt(4)}, false}, } ExecuteGenericTestTable(t, testTable) } diff --git a/ocpp2.0.1_test/get_composite_schedule_test.go b/ocpp2.0.1_test/get_composite_schedule_test.go index 0aa27775..2ea1c1fc 100644 --- a/ocpp2.0.1_test/get_composite_schedule_test.go +++ b/ocpp2.0.1_test/get_composite_schedule_test.go @@ -29,23 +29,35 @@ func (suite *OcppV2TestSuite) TestGetCompositeScheduleRequestValidation() { func (suite *OcppV2TestSuite) TestGetCompositeScheduleConfirmationValidation() { t := suite.T() - chargingSchedule := types.NewChargingSchedule(1, types.ChargingRateUnitWatts, types.NewChargingSchedulePeriod(0, 10.0)) - chargingSchedule.Duration = newInt(600) - chargingSchedule.MinChargingRate = newFloat(6.0) - chargingSchedule.StartSchedule = types.NewDateTime(time.Now()) compositeSchedule := smartcharging.CompositeSchedule{ - StartDateTime: types.NewDateTime(time.Now()), - ChargingSchedule: chargingSchedule, + ChargingSchedulePeriod: []types.ChargingSchedulePeriod{ + { + StartPeriod: 0, + Limit: 32.0, + NumberPhases: newInt(1), + PhaseToUse: newInt(3), + }, + { + StartPeriod: 3600, + Limit: 16.0, + NumberPhases: newInt(1), + PhaseToUse: newInt(3), + }, + }, + ScheduleStart: types.DateTime{Time: time.Now()}, + EvseID: 1, + Duration: 18000, + ChargingRateUnit: types.ChargingRateUnitAmperes, } var confirmationTable = []GenericTestEntry{ {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, StatusInfo: types.NewStatusInfo("reasoncode", ""), Schedule: &compositeSchedule}, true}, - {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, StatusInfo: types.NewStatusInfo("reasoncode", ""), Schedule: &smartcharging.CompositeSchedule{}}, true}, {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, StatusInfo: types.NewStatusInfo("reasoncode", "")}, true}, {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted}, true}, {smartcharging.GetCompositeScheduleResponse{}, false}, {smartcharging.GetCompositeScheduleResponse{Status: "invalidGetCompositeScheduleStatus"}, false}, {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, StatusInfo: types.NewStatusInfo("invalidreasoncodeasitslongerthan20", "")}, false}, - {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, StatusInfo: types.NewStatusInfo("", ""), Schedule: &smartcharging.CompositeSchedule{StartDateTime: types.NewDateTime(time.Now()), ChargingSchedule: types.NewChargingSchedule(1, "invalidChargingRateUnit")}}, false}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, StatusInfo: types.NewStatusInfo("", ""), Schedule: &smartcharging.CompositeSchedule{ChargingSchedulePeriod: []types.ChargingSchedulePeriod{{StartPeriod: 0, Limit: 32}}, ScheduleStart: types.DateTime{Time: time.Now()}, EvseID: 1, Duration: 86400, ChargingRateUnit: "invalidChargingRateUnit"}}, false}, + {smartcharging.GetCompositeScheduleResponse{Status: smartcharging.GetCompositeScheduleStatusAccepted, StatusInfo: types.NewStatusInfo("", ""), Schedule: &smartcharging.CompositeSchedule{ChargingSchedulePeriod: []types.ChargingSchedulePeriod{}, ScheduleStart: types.DateTime{Time: time.Now()}, EvseID: 1, Duration: 86400, ChargingRateUnit: types.ChargingRateUnitAmperes}}, false}, } ExecuteGenericTestTable(t, confirmationTable) } @@ -57,22 +69,38 @@ func (suite *OcppV2TestSuite) TestGetCompositeScheduleE2EMocked() { wsUrl := "someUrl" evseID := 1 chargingRateUnit := types.ChargingRateUnitWatts - duration := 600 + duration := 18000 status := smartcharging.GetCompositeScheduleStatusAccepted scheduleStart := types.NewDateTime(time.Now()) - chargingSchedulePeriod := types.NewChargingSchedulePeriod(0, 10.0) - chargingSchedulePeriod.NumberPhases = newInt(3) - chargingSchedule := types.NewChargingSchedule(1, chargingRateUnit, chargingSchedulePeriod) - chargingSchedule.Duration = newInt(600) - chargingSchedule.StartSchedule = types.NewDateTime(time.Now()) - chargingSchedule.MinChargingRate = newFloat(6.0) + numberPhases := 1 + phasesToUse := 3 + period1 := types.ChargingSchedulePeriod{ + StartPeriod: 0, + Limit: 10.0, + NumberPhases: &numberPhases, + PhaseToUse: &phasesToUse, + } + period2 := types.ChargingSchedulePeriod{ + StartPeriod: 300, + Limit: 8.0, + NumberPhases: &numberPhases, + PhaseToUse: &phasesToUse, + } + compositeSchedule := smartcharging.CompositeSchedule{ + ChargingSchedulePeriod: []types.ChargingSchedulePeriod{period1, period2}, + ScheduleStart: *scheduleStart, + EvseID: evseID, + Duration: duration, + ChargingRateUnit: chargingRateUnit, + } + statusInfo := types.NewStatusInfo("reasonCode", "") - compositeSchedule := smartcharging.CompositeSchedule{StartDateTime: scheduleStart, ChargingSchedule: chargingSchedule} requestJson := fmt.Sprintf(`[2,"%v","%v",{"duration":%v,"chargingRateUnit":"%v","evseId":%v}]`, messageId, smartcharging.GetCompositeScheduleFeatureName, duration, chargingRateUnit, evseID) - responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","statusInfo":{"reasonCode":"%v"},"schedule":{"startDateTime":"%v","chargingSchedule":{"id":%v,"startSchedule":"%v","duration":%v,"chargingRateUnit":"%v","minChargingRate":%v,"chargingSchedulePeriod":[{"startPeriod":%v,"limit":%v,"numberPhases":%v}]}}}]`, - messageId, status, statusInfo.ReasonCode, compositeSchedule.StartDateTime.FormatTimestamp(), chargingSchedule.ID, chargingSchedule.StartSchedule.FormatTimestamp(), *chargingSchedule.Duration, chargingSchedule.ChargingRateUnit, *chargingSchedule.MinChargingRate, chargingSchedulePeriod.StartPeriod, chargingSchedulePeriod.Limit, *chargingSchedulePeriod.NumberPhases) + responseJson := fmt.Sprintf(`[3,"%v",{"status":"%v","statusInfo":{"reasonCode":"%v"},"schedule":{"chargingSchedulePeriod":[{"startPeriod":%v,"limit":%v,"numberPhases":%v,"phaseToUse":%v},{"startPeriod":%v,"limit":%v,"numberPhases":%v,"phaseToUse":%v}],"evseId":%v,"duration":%v,"scheduleStart":"%v","chargingRateUnit":"%v"}}]`, + messageId, status, statusInfo.ReasonCode, period1.StartPeriod, period1.Limit, numberPhases, phasesToUse, period2.StartPeriod, period2.Limit, numberPhases, phasesToUse, evseID, duration, compositeSchedule.ScheduleStart.FormatTimestamp(), chargingRateUnit) getCompositeScheduleConfirmation := smartcharging.NewGetCompositeScheduleResponse(status) + getCompositeScheduleConfirmation.StatusInfo = statusInfo getCompositeScheduleConfirmation.Schedule = &compositeSchedule channel := NewMockWebSocket(wsId) @@ -98,22 +126,22 @@ func (suite *OcppV2TestSuite) TestGetCompositeScheduleE2EMocked() { assert.Equal(t, status, confirmation.Status) assert.Equal(t, statusInfo.ReasonCode, confirmation.StatusInfo.ReasonCode) require.NotNil(t, confirmation.Schedule) - require.NotNil(t, confirmation.Schedule.StartDateTime) - assert.Equal(t, compositeSchedule.StartDateTime.FormatTimestamp(), confirmation.Schedule.StartDateTime.FormatTimestamp()) - require.NotNil(t, confirmation.Schedule.ChargingSchedule) - assert.Equal(t, chargingSchedule.ID, confirmation.Schedule.ChargingSchedule.ID) - assert.Equal(t, chargingSchedule.ChargingRateUnit, confirmation.Schedule.ChargingSchedule.ChargingRateUnit) - require.NotNil(t, confirmation.Schedule.ChargingSchedule.Duration) - assert.Equal(t, *chargingSchedule.Duration, *confirmation.Schedule.ChargingSchedule.Duration) - require.NotNil(t, confirmation.Schedule.ChargingSchedule.MinChargingRate) - assert.Equal(t, *chargingSchedule.MinChargingRate, *confirmation.Schedule.ChargingSchedule.MinChargingRate) - require.NotNil(t, confirmation.Schedule.ChargingSchedule.StartSchedule) - assert.Equal(t, chargingSchedule.StartSchedule.FormatTimestamp(), confirmation.Schedule.ChargingSchedule.StartSchedule.FormatTimestamp()) - require.Len(t, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod, len(chargingSchedule.ChargingSchedulePeriod)) - assert.Equal(t, chargingSchedule.ChargingSchedulePeriod[0].Limit, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].Limit) - assert.Equal(t, chargingSchedule.ChargingSchedulePeriod[0].StartPeriod, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].StartPeriod) - require.NotNil(t, confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].NumberPhases) - assert.Equal(t, *chargingSchedule.ChargingSchedulePeriod[0].NumberPhases, *confirmation.Schedule.ChargingSchedule.ChargingSchedulePeriod[0].NumberPhases) + require.NotNil(t, confirmation.Schedule.ScheduleStart) + assert.Equal(t, compositeSchedule.ScheduleStart.FormatTimestamp(), confirmation.Schedule.ScheduleStart.FormatTimestamp()) + assert.Equal(t, evseID, confirmation.Schedule.EvseID) + assert.Equal(t, duration, confirmation.Schedule.Duration) + require.Len(t, confirmation.Schedule.ChargingSchedulePeriod, len(compositeSchedule.ChargingSchedulePeriod)) + for i := 0; i < len(compositeSchedule.ChargingSchedulePeriod); i++ { + refPeriod := compositeSchedule.ChargingSchedulePeriod[i] + confPeriod := confirmation.Schedule.ChargingSchedulePeriod[i] + + assert.Equal(t, refPeriod.StartPeriod, confPeriod.StartPeriod) + assert.Equal(t, refPeriod.Limit, confPeriod.Limit) + require.NotNil(t, confPeriod.NumberPhases) + assert.Equal(t, *refPeriod.NumberPhases, *confPeriod.NumberPhases) + require.NotNil(t, confPeriod.PhaseToUse) + assert.Equal(t, *refPeriod.PhaseToUse, *confPeriod.PhaseToUse) + } resultChannel <- true }, duration, evseID, func(request *smartcharging.GetCompositeScheduleRequest) { request.ChargingRateUnit = chargingRateUnit