From 8aa72061357baab5fea68c8df3c27e27951d8b51 Mon Sep 17 00:00:00 2001 From: Andreas Linde Date: Fri, 8 Nov 2024 15:25:57 +0100 Subject: [PATCH] Add JSON Marshalling support to device and entity Both DeviceInterface and EntityInterface implementations (e.g. DeviceRemoteInterface and EntityRemoteInterface) are used as arguments in API calls and return values. When these API calls are used via interprocess communication protocols based on JSON, then the easiest way is to convert them into their addresses to be identified. This change adds support for this need. --- spine/device.go | 26 +++++++++++++++++++++++++- spine/device_test.go | 35 +++++++++++++++++++++++++++++++++++ spine/entity.go | 30 +++++++++++++++++++++++++++++- spine/entity_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 spine/device_test.go create mode 100644 spine/entity_test.go diff --git a/spine/device.go b/spine/device.go index 860f84c..9726813 100644 --- a/spine/device.go +++ b/spine/device.go @@ -1,6 +1,10 @@ package spine -import "github.com/enbility/spine-go/model" +import ( + "encoding/json" + + "github.com/enbility/spine-go/model" +) type Device struct { address *model.AddressDeviceType @@ -33,6 +37,26 @@ func (r *Device) Address() *model.AddressDeviceType { return r.address } +// Add support for JSON Marshalling +// +// Instances of EntityInterface are used as arguments and return values in various API calls, +// therefor it is helpfull to be able to marshal them to JSON and thus make the API calls +// usable with various communication interfaces +func (r *Device) MarshalJSON() ([]byte, error) { + var tempAddress string + + if r.address != nil { + tempAddress = string(*r.address) + } + + bytes, err := json.Marshal(tempAddress) + if err != nil { + return nil, err + } + + return bytes, nil +} + func (r *Device) DeviceType() *model.DeviceTypeType { return r.dType } diff --git a/spine/device_test.go b/spine/device_test.go new file mode 100644 index 0000000..ab4db50 --- /dev/null +++ b/spine/device_test.go @@ -0,0 +1,35 @@ +package spine + +import ( + "encoding/json" + "testing" + + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestDeviceSuite(t *testing.T) { + suite.Run(t, new(DeviceTestSuite)) +} + +type DeviceTestSuite struct { + suite.Suite +} + +func (s *DeviceTestSuite) Test_Device() { + deviceAddress := model.AddressDeviceType("test") + device := NewDevice(&deviceAddress, nil, nil) + + value, err := json.Marshal(device) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + assert.Equal(s.T(), `"test"`, string(value)) + + device = NewDevice(nil, nil, nil) + + value, err = json.Marshal(device) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + assert.Equal(s.T(), `""`, string(value)) +} diff --git a/spine/entity.go b/spine/entity.go index e4b41f1..2b480b8 100644 --- a/spine/entity.go +++ b/spine/entity.go @@ -1,6 +1,7 @@ package spine import ( + "encoding/json" "sync" "github.com/ahmetb/go-linq/v3" @@ -32,7 +33,7 @@ func NewEntity(eType model.EntityTypeType, deviceAddress *model.AddressDeviceTyp Entity: entityAddress, }, } - if entityAddress[0] == 0 { + if entityAddress != nil && entityAddress[0] == 0 { // Entity 0 Feature addresses start with 0 entity.fIdGenerator = newFeatureIdGenerator(0) } else { @@ -47,6 +48,33 @@ func (r *Entity) Address() *model.EntityAddressType { return r.address } +// Add support for JSON Marshalling +// +// Instances of EntityInterface are used as arguments and return values in various API calls, +// therefor it is helpfull to be able to marshal them to JSON and thus make the API calls +// usable with various communication interfaces +func (r *Entity) MarshalJSON() ([]byte, error) { + // we do not want to omit address fields, if they are nil + // and field names should not be lowercased + type tempAddressType struct { + Device model.AddressDeviceType + Entity []model.AddressEntityType + } + var tempAddress tempAddressType + + if r.address.Device != nil { + tempAddress.Device = *r.address.Device + } + tempAddress.Entity = r.address.Entity + + bytes, err := json.Marshal(tempAddress) + if err != nil { + return nil, err + } + + return bytes, nil +} + func (r *Entity) EntityType() model.EntityTypeType { return r.eType } diff --git a/spine/entity_test.go b/spine/entity_test.go new file mode 100644 index 0000000..e7bc407 --- /dev/null +++ b/spine/entity_test.go @@ -0,0 +1,42 @@ +package spine + +import ( + "encoding/json" + "testing" + + "github.com/enbility/spine-go/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/suite" +) + +func TestEntitySuite(t *testing.T) { + suite.Run(t, new(EntityTestSuite)) +} + +type EntityTestSuite struct { + suite.Suite +} + +func (s *EntityTestSuite) Test_Entity() { + deviceAddress := model.AddressDeviceType("test") + entity := NewEntity(model.EntityTypeTypeCEM, &deviceAddress, NewAddressEntityType([]uint{1, 1})) + + value, err := json.Marshal(entity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + assert.Equal(s.T(), `{"Device":"test","Entity":[1,1]}`, string(value)) + + entity = NewEntity(model.EntityTypeTypeCEM, &deviceAddress, nil) + + value, err = json.Marshal(entity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + assert.Equal(s.T(), `{"Device":"test","Entity":null}`, string(value)) + + entity = NewEntity(model.EntityTypeTypeCEM, nil, nil) + + value, err = json.Marshal(entity) + assert.Nil(s.T(), err) + assert.NotNil(s.T(), value) + assert.Equal(s.T(), `{"Device":"","Entity":null}`, string(value)) +}