github.com/enbility/spine-go@v0.7.0/spine/device_remote.go (about) 1 package spine 2 3 import ( 4 "encoding/json" 5 "errors" 6 "reflect" 7 "sync" 8 9 shipapi "github.com/enbility/ship-go/api" 10 "github.com/enbility/ship-go/logging" 11 "github.com/enbility/spine-go/api" 12 "github.com/enbility/spine-go/model" 13 ) 14 15 type DeviceRemote struct { 16 *Device 17 18 ski string 19 20 entities []api.EntityRemoteInterface 21 entitiesMutex sync.Mutex 22 23 sender api.SenderInterface 24 25 localDevice api.DeviceLocalInterface 26 } 27 28 func NewDeviceRemote(localDevice api.DeviceLocalInterface, ski string, sender api.SenderInterface) *DeviceRemote { 29 res := DeviceRemote{ 30 Device: NewDevice(nil, nil, nil), 31 ski: ski, 32 localDevice: localDevice, 33 sender: sender, 34 } 35 res.addNodeManagement() 36 37 return &res 38 } 39 40 func (d *DeviceRemote) addNodeManagement() { 41 deviceInformation := d.addNewEntity(model.EntityTypeTypeDeviceInformation, NewAddressEntityType([]uint{DeviceInformationEntityId})) 42 nodeManagement := NewFeatureRemote(deviceInformation.NextFeatureId(), deviceInformation, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) 43 deviceInformation.AddFeature(nodeManagement) 44 } 45 46 var _ shipapi.ShipConnectionDataReaderInterface = (*DeviceRemote)(nil) 47 48 /* ShipConnectionDataReaderInterface */ 49 50 // processing incoming SPINE message from the associated SHIP connection 51 func (d *DeviceRemote) HandleShipPayloadMessage(message []byte) { 52 if _, err := d.HandleSpineMesssage(message); err != nil { 53 logging.Log().Errorf("error handling spine message", err) 54 } 55 } 56 57 var _ api.DeviceRemoteInterface = (*DeviceRemote)(nil) 58 59 /* DeviceRemoteInterface */ 60 61 // return the device SKI 62 func (d *DeviceRemote) Ski() string { 63 return d.ski 64 } 65 66 func (d *DeviceRemote) AddEntity(entity api.EntityRemoteInterface) { 67 d.entitiesMutex.Lock() 68 defer d.entitiesMutex.Unlock() 69 70 d.entities = append(d.entities, entity) 71 } 72 73 func (d *DeviceRemote) addNewEntity(eType model.EntityTypeType, address []model.AddressEntityType) api.EntityRemoteInterface { 74 newEntity := NewEntityRemote(d, eType, address) 75 d.AddEntity(newEntity) 76 return newEntity 77 } 78 79 // Remove an entity with a given address from this device 80 func (d *DeviceRemote) RemoveEntityByAddress(addr []model.AddressEntityType) api.EntityRemoteInterface { 81 entityForRemoval := d.Entity(addr) 82 if entityForRemoval == nil { 83 return nil 84 } 85 86 d.entitiesMutex.Lock() 87 defer d.entitiesMutex.Unlock() 88 89 var newEntities []api.EntityRemoteInterface 90 for _, item := range d.entities { 91 if !reflect.DeepEqual(item, entityForRemoval) { 92 newEntities = append(newEntities, item) 93 } 94 } 95 d.entities = newEntities 96 97 return entityForRemoval 98 } 99 100 // Return an entity with a given address 101 func (d *DeviceRemote) Entity(id []model.AddressEntityType) api.EntityRemoteInterface { 102 d.entitiesMutex.Lock() 103 defer d.entitiesMutex.Unlock() 104 105 for _, e := range d.entities { 106 if reflect.DeepEqual(id, e.Address().Entity) { 107 return e 108 } 109 } 110 return nil 111 } 112 113 // Return all entities of this device 114 func (d *DeviceRemote) Entities() []api.EntityRemoteInterface { 115 d.entitiesMutex.Lock() 116 defer d.entitiesMutex.Unlock() 117 118 return d.entities 119 } 120 121 // Return the feature for a given address 122 func (d *DeviceRemote) FeatureByAddress(address *model.FeatureAddressType) api.FeatureRemoteInterface { 123 entity := d.Entity(address.Entity) 124 if entity != nil { 125 return entity.FeatureOfAddress(address.Feature) 126 } 127 return nil 128 } 129 130 // Get the feature for a given entity, feature type and feature role 131 func (r *DeviceRemote) FeatureByEntityTypeAndRole(entity api.EntityRemoteInterface, featureType model.FeatureTypeType, role model.RoleType) api.FeatureRemoteInterface { 132 if len(r.entities) < 1 { 133 return nil 134 } 135 136 r.entitiesMutex.Lock() 137 defer r.entitiesMutex.Unlock() 138 139 for _, e := range r.entities { 140 if entity != e { 141 continue 142 } 143 for _, feature := range entity.Features() { 144 if feature.Type() == featureType && feature.Role() == role { 145 return feature 146 } 147 } 148 } 149 150 return nil 151 } 152 153 func (d *DeviceRemote) HandleSpineMesssage(message []byte) (*model.MsgCounterType, error) { 154 datagram := model.Datagram{} 155 if err := json.Unmarshal([]byte(message), &datagram); err != nil { 156 return nil, err 157 } 158 159 if datagram.Datagram.Header.MsgCounterReference != nil { 160 d.sender.ProcessResponseForMsgCounterReference(datagram.Datagram.Header.MsgCounterReference) 161 } 162 163 err := d.localDevice.ProcessCmd(datagram.Datagram, d) 164 if err != nil { 165 logging.Log().Trace(err) 166 } 167 168 return datagram.Datagram.Header.MsgCounter, nil 169 } 170 171 func (d *DeviceRemote) Sender() api.SenderInterface { 172 return d.sender 173 } 174 175 func (d *DeviceRemote) UseCases() []model.UseCaseInformationDataType { 176 entity := d.Entity(DeviceInformationAddressEntity) 177 178 nodemgmt := d.FeatureByEntityTypeAndRole(entity, model.FeatureTypeTypeNodeManagement, model.RoleTypeSpecial) 179 180 data, ok := nodemgmt.DataCopy(model.FunctionTypeNodeManagementUseCaseData).(*model.NodeManagementUseCaseDataType) 181 if ok && data != nil { 182 return data.UseCaseInformation 183 } 184 185 return nil 186 } 187 188 func (d *DeviceRemote) UpdateDevice(description *model.NetworkManagementDeviceDescriptionDataType) { 189 if description != nil { 190 if description.DeviceAddress != nil && description.DeviceAddress.Device != nil { 191 d.address = description.DeviceAddress.Device 192 } 193 if description.DeviceType != nil { 194 d.dType = description.DeviceType 195 } 196 if description.NetworkFeatureSet != nil { 197 d.featureSet = description.NetworkFeatureSet 198 } 199 } 200 } 201 202 func (d *DeviceRemote) AddEntityAndFeatures(initialData bool, data *model.NodeManagementDetailedDiscoveryDataType) ([]api.EntityRemoteInterface, error) { 203 rEntites := make([]api.EntityRemoteInterface, 0) 204 205 for _, ei := range data.EntityInformation { 206 if err := d.CheckEntityInformation(initialData, ei); err != nil { 207 return nil, err 208 } 209 210 entityAddress := ei.Description.EntityAddress.Entity 211 212 entity := d.Entity(entityAddress) 213 if entity == nil { 214 entity = d.addNewEntity(*ei.Description.EntityType, entityAddress) 215 rEntites = append(rEntites, entity) 216 } 217 218 // make sure the device address is set, which is not on entity 0 on startup ! 219 if entity.Address().Device == nil || len(*entity.Address().Device) == 0 { 220 if data.DeviceInformation != nil && 221 data.DeviceInformation.Description != nil && 222 data.DeviceInformation.Description.DeviceAddress != nil && 223 data.DeviceInformation.Description.DeviceAddress.Device != nil { 224 entity.UpdateDeviceAddress(*data.DeviceInformation.Description.DeviceAddress.Device) 225 } 226 } 227 228 entity.SetDescription(ei.Description.Description) 229 entity.RemoveAllFeatures() 230 231 for _, fi := range data.FeatureInformation { 232 if reflect.DeepEqual(fi.Description.FeatureAddress.Entity, entityAddress) { 233 if f, ok := unmarshalFeature(entity, fi); ok { 234 entity.AddFeature(f) 235 } 236 } 237 } 238 } 239 240 return rEntites, nil 241 } 242 243 // check if the provided entity information is correct 244 // provide initialData to check if the entity is new and not an update 245 func (d *DeviceRemote) CheckEntityInformation(initialData bool, entity model.NodeManagementDetailedDiscoveryEntityInformationType) error { 246 description := entity.Description 247 if description == nil { 248 return errors.New("nodemanagement.replyDetailedDiscoveryData: invalid EntityInformation.Description") 249 } 250 251 if description.EntityAddress == nil { 252 return errors.New("nodemanagement.replyDetailedDiscoveryData: invalid EntityInformation.Description.EntityAddress") 253 } 254 255 if description.EntityAddress.Entity == nil { 256 return errors.New("nodemanagement.replyDetailedDiscoveryData: invalid EntityInformation.Description.EntityAddress.Entity") 257 } 258 259 // Consider on initial NodeManagement Detailed Discovery, the device being empty as it is not yet known 260 if initialData { 261 return nil 262 } 263 264 address := d.Address() 265 if description.EntityAddress.Device != nil && address != nil && *description.EntityAddress.Device != *address { 266 return errors.New("nodemanagement.replyDetailedDiscoveryData: device address mismatch") 267 } 268 269 return nil 270 } 271 272 func unmarshalFeature(entity api.EntityRemoteInterface, 273 featureData model.NodeManagementDetailedDiscoveryFeatureInformationType, 274 ) (api.FeatureRemoteInterface, bool) { 275 var result api.FeatureRemoteInterface 276 277 fid := featureData.Description 278 279 if fid == nil { 280 return nil, false 281 } 282 283 result = NewFeatureRemote(uint(*fid.FeatureAddress.Feature), entity, *fid.FeatureType, *fid.Role) 284 285 result.SetDescription(fid.Description) 286 result.SetMaxResponseDelay(fid.MaxResponseDelay) 287 result.SetOperations(fid.SupportedFunction) 288 289 return result, true 290 }