github.com/enbility/spine-go@v0.7.0/spine/device_local_test.go (about) 1 package spine 2 3 import ( 4 "testing" 5 "time" 6 7 shipapi "github.com/enbility/ship-go/api" 8 "github.com/enbility/spine-go/model" 9 "github.com/enbility/spine-go/util" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/suite" 12 ) 13 14 func TestDeviceLocalSuite(t *testing.T) { 15 suite.Run(t, new(DeviceLocalTestSuite)) 16 } 17 18 type DeviceLocalTestSuite struct { 19 suite.Suite 20 21 lastMessage string 22 } 23 24 var _ shipapi.ShipConnectionDataWriterInterface = (*DeviceLocalTestSuite)(nil) 25 26 func (d *DeviceLocalTestSuite) WriteShipMessageWithPayload(msg []byte) { 27 d.lastMessage = string(msg) 28 } 29 30 func (d *DeviceLocalTestSuite) Test_RemoveRemoteDevice() { 31 sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) 32 33 ski := "test" 34 _ = sut.SetupRemoteDevice(ski, d) 35 rDevice := sut.RemoteDeviceForSki(ski) 36 assert.NotNil(d.T(), rDevice) 37 38 sut.RemoveRemoteDeviceConnection(ski) 39 40 rDevice = sut.RemoteDeviceForSki(ski) 41 assert.Nil(d.T(), rDevice) 42 } 43 44 func (d *DeviceLocalTestSuite) Test_RemoteDevice() { 45 sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) 46 localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4) 47 sut.AddEntity(localEntity) 48 49 f := NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) 50 localEntity.AddFeature(f) 51 f = NewFeatureLocal(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) 52 localEntity.AddFeature(f) 53 54 ski := "test" 55 remote := sut.RemoteDeviceForSki(ski) 56 assert.Nil(d.T(), remote) 57 58 devices := sut.RemoteDevices() 59 assert.Equal(d.T(), 0, len(devices)) 60 61 _ = sut.SetupRemoteDevice(ski, d) 62 remote = sut.RemoteDeviceForSki(ski) 63 assert.NotNil(d.T(), remote) 64 65 devices = sut.RemoteDevices() 66 assert.Equal(d.T(), 1, len(devices)) 67 68 entities := sut.Entities() 69 assert.Equal(d.T(), 2, len(entities)) 70 71 entity1 := sut.Entity([]model.AddressEntityType{1}) 72 assert.NotNil(d.T(), entity1) 73 74 entity2 := sut.Entity([]model.AddressEntityType{2}) 75 assert.Nil(d.T(), entity2) 76 77 featureAddress := &model.FeatureAddressType{ 78 Entity: []model.AddressEntityType{1}, 79 Feature: util.Ptr(model.AddressFeatureType(1)), 80 } 81 feature1 := sut.FeatureByAddress(featureAddress) 82 assert.NotNil(d.T(), feature1) 83 84 feature2 := localEntity.FeatureOfTypeAndRole(model.FeatureTypeTypeMeasurement, model.RoleTypeClient) 85 assert.NotNil(d.T(), feature2) 86 87 featureAddress = &model.FeatureAddressType{ 88 Device: remote.Address(), 89 Entity: []model.AddressEntityType{0}, 90 Feature: util.Ptr(model.AddressFeatureType(0)), 91 } 92 93 subscription := model.SubscriptionManagementRequestCallType{ 94 ClientAddress: featureAddress, 95 ServerAddress: sut.NodeManagement().Address(), 96 ServerFeatureType: util.Ptr(model.FeatureTypeTypeNodeManagement), 97 } 98 err := sut.SubscriptionManager().AddSubscription(remote, subscription) 99 assert.Nil(d.T(), err) 100 101 newSubEntity := NewEntityLocal(sut, model.EntityTypeTypeEV, NewAddressEntityType([]uint{1, 1}), time.Second*4) 102 f = NewFeatureLocal(1, newSubEntity, model.FeatureTypeTypeLoadControl, model.RoleTypeServer) 103 f.AddFunctionType(model.FunctionTypeLoadControlLimitListData, true, true) 104 newSubEntity.AddFeature(f) 105 106 sut.AddEntity(newSubEntity) 107 // A notification should have been sent 108 expectedNotifyMsg := `{"datagram":{"header":{"specificationVersion":"1.3.0","addressSource":{"device":"address","entity":[0],"feature":0},"addressDestination":{"entity":[0],"feature":0},"msgCounter":2,"cmdClassifier":"notify"},"payload":{"cmd":[{"function":"nodeManagementDetailedDiscoveryData","filter":[{"cmdControl":{"partial":{}}}],"nodeManagementDetailedDiscoveryData":{"specificationVersionList":{"specificationVersion":["1.3.0"]},"deviceInformation":{"description":{"deviceAddress":{"device":"address"},"deviceType":"EnergyManagementSystem","networkFeatureSet":"smart"}},"entityInformation":[{"description":{"entityAddress":{"device":"address","entity":[1,1]},"entityType":"EV","lastStateChange":"added"}}],"featureInformation":[{"description":{"featureAddress":{"device":"address","entity":[1,1],"feature":1},"featureType":"LoadControl","role":"server","supportedFunction":[{"function":"loadControlLimitListData","possibleOperations":{"read":{},"write":{"partial":{}}}}]}}]}}]}}}` 109 assert.Equal(d.T(), expectedNotifyMsg, d.lastMessage) 110 111 entities = sut.Entities() 112 assert.Equal(d.T(), 3, len(entities)) 113 114 sut.RemoveEntity(newSubEntity) 115 // A notification should have been sent 116 expectedNotifyMsg = `{"datagram":{"header":{"specificationVersion":"1.3.0","addressSource":{"device":"address","entity":[0],"feature":0},"addressDestination":{"entity":[0],"feature":0},"msgCounter":3,"cmdClassifier":"notify"},"payload":{"cmd":[{"function":"nodeManagementDetailedDiscoveryData","filter":[{"cmdControl":{"partial":{}}}],"nodeManagementDetailedDiscoveryData":{"specificationVersionList":{"specificationVersion":["1.3.0"]},"deviceInformation":{"description":{"deviceAddress":{"device":"address"},"deviceType":"EnergyManagementSystem","networkFeatureSet":"smart"}},"entityInformation":[{"description":{"entityAddress":{"device":"address","entity":[1,1]},"entityType":"EV","lastStateChange":"removed"}}]}}]}}}` 117 assert.Equal(d.T(), expectedNotifyMsg, d.lastMessage) 118 119 entities = sut.Entities() 120 assert.Equal(d.T(), 2, len(entities)) 121 122 sut.RemoveEntity(entity1) 123 // A notification should have been sent 124 expectedNotifyMsg = `{"datagram":{"header":{"specificationVersion":"1.3.0","addressSource":{"device":"address","entity":[0],"feature":0},"addressDestination":{"entity":[0],"feature":0},"msgCounter":4,"cmdClassifier":"notify"},"payload":{"cmd":[{"function":"nodeManagementDetailedDiscoveryData","filter":[{"cmdControl":{"partial":{}}}],"nodeManagementDetailedDiscoveryData":{"specificationVersionList":{"specificationVersion":["1.3.0"]},"deviceInformation":{"description":{"deviceAddress":{"device":"address"},"deviceType":"EnergyManagementSystem","networkFeatureSet":"smart"}},"entityInformation":[{"description":{"entityAddress":{"device":"address","entity":[1]},"entityType":"CEM","lastStateChange":"removed"}}]}}]}}}` 125 assert.Equal(d.T(), expectedNotifyMsg, d.lastMessage) 126 127 entities = sut.Entities() 128 assert.Equal(d.T(), 1, len(entities)) 129 130 sut.RemoveRemoteDevice(ski) 131 remote = sut.RemoteDeviceForSki(ski) 132 assert.Nil(d.T(), remote) 133 } 134 135 func (d *DeviceLocalTestSuite) Test_ProcessCmd_NotifyError() { 136 sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) 137 localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4) 138 localFeature := NewFeatureLocal(50, localEntity, model.FeatureTypeTypeSensing, model.RoleTypeClient) 139 localEntity.AddFeature(localFeature) 140 sut.AddEntity(localEntity) 141 142 ski := "test" 143 _ = sut.SetupRemoteDevice(ski, d) 144 remote := sut.RemoteDeviceForSki(ski) 145 assert.NotNil(d.T(), remote) 146 remoteEntity := NewEntityRemote(remote, model.EntityTypeTypeCEM, []model.AddressEntityType{1}) 147 remoteFeature := NewFeatureRemote(50, remoteEntity, model.FeatureTypeTypeSensing, model.RoleTypeServer) 148 remoteEntity.AddFeature(remoteFeature) 149 remote.AddEntity(remoteEntity) 150 151 datagram := model.DatagramType{ 152 Header: model.HeaderType{ 153 AddressSource: remoteFeature.Address(), 154 AddressDestination: localFeature.Address(), 155 MsgCounter: util.Ptr(model.MsgCounterType(1)), 156 CmdClassifier: util.Ptr(model.CmdClassifierTypeNotify), 157 }, 158 Payload: model.PayloadType{ 159 Cmd: []model.CmdType{ 160 { 161 Function: util.Ptr(model.FunctionTypeSensingListData), 162 Filter: filterEmptyPartial(), 163 SensingListData: &model.SensingListDataType{ 164 SensingData: []model.SensingDataType{ 165 { 166 Timestamp: model.NewAbsoluteOrRelativeTimeTypeFromTime(time.Now()), 167 Value: model.NewScaledNumberType(99), 168 }, 169 }, 170 }, 171 }, 172 }, 173 }, 174 } 175 176 err := sut.ProcessCmd(datagram, remote) 177 assert.NotNil(d.T(), err) 178 } 179 180 func (d *DeviceLocalTestSuite) Test_ProcessCmd_Errors() { 181 sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) 182 localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4) 183 sut.AddEntity(localEntity) 184 185 ski := "test" 186 _ = sut.SetupRemoteDevice(ski, d) 187 remote := sut.RemoteDeviceForSki(ski) 188 assert.NotNil(d.T(), remote) 189 190 datagram := model.DatagramType{ 191 Header: model.HeaderType{ 192 AddressSource: &model.FeatureAddressType{ 193 Device: util.Ptr(model.AddressDeviceType("localdevice")), 194 }, 195 AddressDestination: &model.FeatureAddressType{ 196 Device: util.Ptr(model.AddressDeviceType("localdevice")), 197 }, 198 MsgCounter: util.Ptr(model.MsgCounterType(1)), 199 CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), 200 }, 201 Payload: model.PayloadType{ 202 Cmd: []model.CmdType{}, 203 }, 204 } 205 206 err := sut.ProcessCmd(datagram, remote) 207 assert.NotNil(d.T(), err) 208 209 datagram = model.DatagramType{ 210 Header: model.HeaderType{ 211 AddressSource: &model.FeatureAddressType{ 212 Device: util.Ptr(model.AddressDeviceType("localdevice")), 213 }, 214 AddressDestination: &model.FeatureAddressType{ 215 Device: util.Ptr(model.AddressDeviceType("localdevice")), 216 }, 217 MsgCounter: util.Ptr(model.MsgCounterType(1)), 218 CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), 219 }, 220 Payload: model.PayloadType{ 221 Cmd: []model.CmdType{ 222 {}, 223 }, 224 }, 225 } 226 227 err = sut.ProcessCmd(datagram, remote) 228 assert.NotNil(d.T(), err) 229 } 230 231 func (d *DeviceLocalTestSuite) Test_ProcessCmd() { 232 sut := NewDeviceLocal("brand", "model", "serial", "code", "address", model.DeviceTypeTypeEnergyManagementSystem, model.NetworkManagementFeatureSetTypeSmart) 233 localEntity := NewEntityLocal(sut, model.EntityTypeTypeCEM, NewAddressEntityType([]uint{1}), time.Second*4) 234 sut.AddEntity(localEntity) 235 236 f1 := NewFeatureLocal(1, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeClient) 237 localEntity.AddFeature(f1) 238 f2 := NewFeatureLocal(2, localEntity, model.FeatureTypeTypeMeasurement, model.RoleTypeClient) 239 localEntity.AddFeature(f2) 240 f3 := NewFeatureLocal(3, localEntity, model.FeatureTypeTypeElectricalConnection, model.RoleTypeServer) 241 localEntity.AddFeature(f3) 242 243 ski := "test" 244 remoteDeviceName := "remote" 245 _ = sut.SetupRemoteDevice(ski, d) 246 remote := sut.RemoteDeviceForSki(ski) 247 assert.NotNil(d.T(), remote) 248 249 detailedData := &model.NodeManagementDetailedDiscoveryDataType{ 250 DeviceInformation: &model.NodeManagementDetailedDiscoveryDeviceInformationType{ 251 Description: &model.NetworkManagementDeviceDescriptionDataType{ 252 DeviceAddress: &model.DeviceAddressType{ 253 Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), 254 }, 255 }, 256 }, 257 EntityInformation: []model.NodeManagementDetailedDiscoveryEntityInformationType{ 258 { 259 Description: &model.NetworkManagementEntityDescriptionDataType{ 260 EntityAddress: &model.EntityAddressType{ 261 Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), 262 Entity: []model.AddressEntityType{1}, 263 }, 264 EntityType: util.Ptr(model.EntityTypeTypeEVSE), 265 }, 266 }, 267 }, 268 FeatureInformation: []model.NodeManagementDetailedDiscoveryFeatureInformationType{ 269 { 270 Description: &model.NetworkManagementFeatureDescriptionDataType{ 271 FeatureAddress: &model.FeatureAddressType{ 272 Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), 273 Entity: []model.AddressEntityType{1}, 274 Feature: util.Ptr(model.AddressFeatureType(1)), 275 }, 276 FeatureType: util.Ptr(model.FeatureTypeTypeElectricalConnection), 277 Role: util.Ptr(model.RoleTypeServer), 278 }, 279 }, 280 }, 281 } 282 _, err := remote.AddEntityAndFeatures(true, detailedData) 283 assert.Nil(d.T(), err) 284 285 datagram := model.DatagramType{ 286 Header: model.HeaderType{ 287 AddressSource: &model.FeatureAddressType{ 288 Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), 289 Entity: []model.AddressEntityType{1}, 290 Feature: util.Ptr(model.AddressFeatureType(1)), 291 }, 292 AddressDestination: &model.FeatureAddressType{ 293 Device: util.Ptr(model.AddressDeviceType("localdevice")), 294 Entity: []model.AddressEntityType{1}, 295 }, 296 MsgCounter: util.Ptr(model.MsgCounterType(1)), 297 CmdClassifier: util.Ptr(model.CmdClassifierTypeRead), 298 }, 299 Payload: model.PayloadType{ 300 Cmd: []model.CmdType{}, 301 }, 302 } 303 304 err = sut.ProcessCmd(datagram, remote) 305 assert.NotNil(d.T(), err) 306 307 cmd := model.CmdType{ 308 ElectricalConnectionParameterDescriptionListData: &model.ElectricalConnectionParameterDescriptionListDataType{}, 309 } 310 311 datagram.Payload.Cmd = append(datagram.Payload.Cmd, cmd) 312 313 err = sut.ProcessCmd(datagram, remote) 314 assert.NotNil(d.T(), err) 315 316 datagram.Header.AddressDestination.Feature = util.Ptr(model.AddressFeatureType(1)) 317 318 err = sut.ProcessCmd(datagram, remote) 319 assert.NotNil(d.T(), err) 320 321 datagram.Header.AddressDestination.Feature = util.Ptr(model.AddressFeatureType(3)) 322 323 err = sut.ProcessCmd(datagram, remote) 324 assert.Nil(d.T(), err) 325 326 datagram = model.DatagramType{ 327 Header: model.HeaderType{ 328 AddressSource: &model.FeatureAddressType{ 329 Device: util.Ptr(model.AddressDeviceType(remoteDeviceName)), 330 Entity: []model.AddressEntityType{1}, 331 Feature: util.Ptr(model.AddressFeatureType(1)), 332 }, 333 AddressDestination: &model.FeatureAddressType{ 334 Device: util.Ptr(model.AddressDeviceType("localdevice")), 335 Entity: []model.AddressEntityType{1}, 336 Feature: util.Ptr(model.AddressFeatureType(3)), 337 }, 338 MsgCounter: util.Ptr(model.MsgCounterType(1)), 339 }, 340 Payload: model.PayloadType{ 341 Cmd: []model.CmdType{ 342 {ElectricalConnectionParameterDescriptionListData: util.Ptr(model.ElectricalConnectionParameterDescriptionListDataType{})}, 343 }, 344 }, 345 } 346 347 err = sut.ProcessCmd(datagram, remote) 348 assert.NotNil(d.T(), err) 349 350 datagram.Header.CmdClassifier = util.Ptr(model.CmdClassifierTypeWrite) 351 352 err = sut.ProcessCmd(datagram, remote) 353 assert.NotNil(d.T(), err) 354 355 f3.AddFunctionType(model.FunctionTypeElectricalConnectionParameterDescriptionListData, true, true) 356 err = sut.ProcessCmd(datagram, remote) 357 assert.NotNil(d.T(), err) 358 }