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  }