github.com/binkynet/BinkyNet@v1.12.1-0.20240421190447-da4e34c20be0/apis/v1/objecthelper.go (about)

     1  // Copyright 2018 Ewout Prangsma
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // Author Ewout Prangsma
    16  //
    17  
    18  package v1
    19  
    20  import "strconv"
    21  
    22  // ObjectTypeInfo holds builtin information for a type of objects.
    23  type ObjectTypeInfo struct {
    24  	// Type of the object
    25  	Type ObjectType
    26  	// Description of the object type
    27  	Description string
    28  	// Possible (required & optional) connections of objects of this type to devices.
    29  	Connections []ObjectConnectionInfo
    30  }
    31  
    32  // ConnectionByName returns the information for the connection with given name.
    33  // Returns name, found.
    34  func (i ObjectTypeInfo) ConnectionByName(name ConnectionName) (ObjectConnectionInfo, bool) {
    35  	for _, x := range i.Connections {
    36  		if x.Name == name {
    37  			return x, true
    38  		}
    39  	}
    40  	return ObjectConnectionInfo{}, false
    41  }
    42  
    43  // ObjectConnectionInfo descripts a connection of a type of objects to a device.
    44  type ObjectConnectionInfo struct {
    45  	// Name of the connection from object to device
    46  	Name ConnectionName
    47  	// If this connection required?
    48  	Required bool
    49  	// How many device-pins are expected for this connection
    50  	PinCount int
    51  }
    52  
    53  var (
    54  	objectTypeInfos = []ObjectTypeInfo{
    55  		ObjectTypeInfo{
    56  			Type:        ObjectTypeBinarySensor,
    57  			Description: "Single bit input sensor",
    58  			Connections: []ObjectConnectionInfo{
    59  				ObjectConnectionInfo{Name: ConnectionNameSensor, Required: true, PinCount: 1},
    60  			},
    61  		},
    62  		ObjectTypeInfo{
    63  			Type:        ObjectTypeBinaryOutput,
    64  			Description: "Single bit output",
    65  			Connections: []ObjectConnectionInfo{
    66  				ObjectConnectionInfo{Name: ConnectionNameOutput, Required: true, PinCount: 1},
    67  			},
    68  		},
    69  		ObjectTypeInfo{
    70  			Type:        ObjectTypeServoSwitch,
    71  			Description: "Left or right switch driven by a single servo with optional phase switching relays",
    72  			Connections: []ObjectConnectionInfo{
    73  				ObjectConnectionInfo{Name: ConnectionNameServo, Required: true, PinCount: 1},
    74  				ObjectConnectionInfo{Name: ConnectionNamePhaseStraightRelay, Required: false, PinCount: 1},
    75  				ObjectConnectionInfo{Name: ConnectionNamePhaseOffRelay, Required: false, PinCount: 1},
    76  			},
    77  		},
    78  		ObjectTypeInfo{
    79  			Type:        ObjectTypeRelaySwitch,
    80  			Description: "Left or right switch driven by 2 relays",
    81  			Connections: []ObjectConnectionInfo{
    82  				ObjectConnectionInfo{Name: ConnectionNameStraightRelay, Required: true, PinCount: 1},
    83  				ObjectConnectionInfo{Name: ConnectionNameOffRelay, Required: true, PinCount: 1},
    84  			},
    85  		},
    86  	}
    87  )
    88  
    89  // Validate the given type, returning nil on ok,
    90  // or an error upon validation issues.
    91  func (t ObjectType) Validate() error {
    92  	for _, typeInfo := range objectTypeInfos {
    93  		if typeInfo.Type == t {
    94  			return nil
    95  		}
    96  	}
    97  	return InvalidArgument("invalid object type '%s'", string(t))
    98  }
    99  
   100  // TypeInfo returns the ObjectType information for this type of object.
   101  func (t ObjectType) TypeInfo() ObjectTypeInfo {
   102  	for _, typeInfo := range objectTypeInfos {
   103  		if typeInfo.Type == t {
   104  			return typeInfo
   105  		}
   106  	}
   107  	return ObjectTypeInfo{}
   108  }
   109  
   110  // ConnectionByName returns the connection of the object with given name
   111  func (o Object) ConnectionByName(name ConnectionName) (*Connection, bool) {
   112  	for _, conn := range o.GetConnections() {
   113  		if conn.Key == name {
   114  			return conn, true
   115  		}
   116  	}
   117  	return nil, false
   118  }
   119  
   120  // Validate the given configuration, returning nil on ok,
   121  // or an error upon validation issues.
   122  func (o Object) Validate() error {
   123  	if err := o.Type.Validate(); err != nil {
   124  		return InvalidArgument("Error in Type of '%s': %s", o.Id, err.Error())
   125  	}
   126  	typeInfo := o.Type.TypeInfo()
   127  	// Check configured connections
   128  	for _, conn := range o.Connections {
   129  		cInfo, found := typeInfo.ConnectionByName(conn.Key)
   130  		if !found {
   131  			return InvalidArgument("Object '%s' has an unexpected connection named '%s'", o.Id, conn.Key)
   132  		}
   133  		if cInfo.PinCount != len(conn.Pins) {
   134  			return InvalidArgument("Object '%s' has an unexpected number of pins for connection '%s'. Got %d, expected %d", o.Id, conn.Key, len(conn.Pins), cInfo.PinCount)
   135  		}
   136  		// Validate pins
   137  		for idx, p := range conn.Pins {
   138  			if p.Index == 0 {
   139  				return InvalidArgument("Object '%s' has an invalid index at position %d of the connection named '%s'", o.Id, idx, conn.Key)
   140  			}
   141  		}
   142  	}
   143  	// Look for missing connections
   144  	for _, conn := range typeInfo.Connections {
   145  		if conn.Required {
   146  			if _, found := o.ConnectionByName(conn.Name); !found {
   147  				return InvalidArgument("Object '%s' lacks a required connection named '%s'", o.Id, conn.Name)
   148  			}
   149  		}
   150  	}
   151  	return nil
   152  }
   153  
   154  // GetStringConfig returns the configuration value for the given key.
   155  // If not found, the default value for the key is returned.
   156  func (o Object) GetStringConfig(key ObjectConfigKey) string {
   157  	value, found := o.Configuration[key]
   158  	if found {
   159  		return value
   160  	}
   161  	return key.DefaultValue()
   162  }
   163  
   164  // GetBoolConfig returns the bool-typed configuration value for the given key.
   165  // If not found or not an int, the default value for the key is returned.
   166  func (o Object) GetBoolConfig(key ObjectConfigKey) bool {
   167  	value, found := o.Configuration[key]
   168  	if !found {
   169  		value = key.DefaultValue()
   170  	}
   171  	if tValue, err := strconv.ParseBool(value); err == nil {
   172  		return tValue
   173  	}
   174  	return false
   175  }
   176  
   177  // GetIntConfig returns the int-typed configuration value for the given key.
   178  // If not found or not an int, the default value for the key is returned.
   179  func (o Object) GetIntConfig(key ObjectConfigKey) int {
   180  	value, found := o.Configuration[key]
   181  	if !found {
   182  		value = key.DefaultValue()
   183  	}
   184  	if tValue, err := strconv.Atoi(value); err == nil {
   185  		return tValue
   186  	}
   187  	return 0
   188  }