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  }