hybridgroup.gobot/drivers/aio/analog_actuator_driver.go

160 lines
4.4 KiB
Go

package aio
import (
"fmt"
"strconv"
)
// actuatorOptionApplier needs to be implemented by each configurable option type
type actuatorOptionApplier interface {
apply(cfg *actuatorConfiguration)
}
// actuatorConfiguration contains all changeable attributes of the driver.
type actuatorConfiguration struct {
scale func(input float64) (value int)
}
// actuatorScaleOption is the type for applying another scaler to the configuration
type actuatorScaleOption struct {
scaler func(input float64) (value int)
}
// AnalogActuatorDriver represents an analog actuator
type AnalogActuatorDriver struct {
*driver
pin string
actuatorCfg *actuatorConfiguration
lastValue float64
lastRawValue int
}
// NewAnalogActuatorDriver returns a new driver for analog actuator, given by an AnalogWriter and pin.
// The driver supports customizable scaling from given float64 value to written int.
// The default scaling is 1:1. An adjustable linear scaler is provided by the driver.
//
// Supported options:
//
// "WithName"
// "WithActuatorScaler"
//
// Adds the following API Commands:
//
// "Write" - See AnalogActuator.Write
// "WriteRaw" - See AnalogActuator.WriteRaw
func NewAnalogActuatorDriver(a AnalogWriter, pin string, opts ...interface{}) *AnalogActuatorDriver {
d := &AnalogActuatorDriver{
driver: newDriver(a, "AnalogActuator"),
pin: pin,
actuatorCfg: &actuatorConfiguration{scale: func(input float64) int { return int(input) }},
}
for _, opt := range opts {
switch o := opt.(type) {
case optionApplier:
o.apply(d.driverCfg)
case actuatorOptionApplier:
o.apply(d.actuatorCfg)
default:
panic(fmt.Sprintf("'%s' can not be applied on '%s'", opt, d.driverCfg.name))
}
}
d.AddCommand("Write", func(params map[string]interface{}) interface{} {
val, err := strconv.ParseFloat(params["val"].(string), 64)
if err != nil {
return err
}
return d.Write(val)
})
d.AddCommand("WriteRaw", func(params map[string]interface{}) interface{} {
val, _ := strconv.Atoi(params["val"].(string))
return d.WriteRaw(val)
})
return d
}
// WithActuatorScaler substitute the default 1:1 return value function by a new scaling function
func WithActuatorScaler(scaler func(input float64) (value int)) actuatorOptionApplier {
return actuatorScaleOption{scaler: scaler}
}
// SetScaler substitute the default 1:1 return value function by a new scaling function
// If the scaler is not changed after initialization, prefer to use [aio.WithActuatorScaler] instead.
func (a *AnalogActuatorDriver) SetScaler(scaler func(float64) int) {
a.mutex.Lock()
defer a.mutex.Unlock()
WithActuatorScaler(scaler).apply(a.actuatorCfg)
}
// Pin returns the drivers pin
func (a *AnalogActuatorDriver) Pin() string { return a.pin }
// Write writes the given value to the actuator
func (a *AnalogActuatorDriver) Write(val float64) error {
a.mutex.Lock()
defer a.mutex.Unlock()
rawValue := a.actuatorCfg.scale(val)
if err := a.WriteRaw(rawValue); err != nil {
return err
}
a.lastValue = val
return nil
}
// RawWrite write the given raw value to the actuator
// Deprecated: Please use [aio.WriteRaw] instead.
func (a *AnalogActuatorDriver) RawWrite(val int) error {
return a.WriteRaw(val)
}
// WriteRaw write the given raw value to the actuator
func (a *AnalogActuatorDriver) WriteRaw(val int) error {
writer, ok := a.connection.(AnalogWriter)
if !ok {
return fmt.Errorf("AnalogWrite is not supported by the platform '%s'", a.Connection().Name())
}
if err := writer.AnalogWrite(a.Pin(), val); err != nil {
return err
}
a.lastRawValue = val
return nil
}
// Value returns the last written value
func (a *AnalogActuatorDriver) Value() float64 {
return a.lastValue
}
// RawValue returns the last written raw value
func (a *AnalogActuatorDriver) RawValue() int {
return a.lastRawValue
}
func (o actuatorScaleOption) String() string {
return "scaler option for analog actuators"
}
func (o actuatorScaleOption) apply(cfg *actuatorConfiguration) {
cfg.scale = o.scaler
}
// AnalogActuatorLinearScaler creates a linear scaler function from the given values.
func AnalogActuatorLinearScaler(fromMin, fromMax float64, toMin, toMax int) func(input float64) (value int) {
m := float64(toMax-toMin) / (fromMax - fromMin)
n := float64(toMin) - m*fromMin
return func(input float64) int {
if input <= fromMin {
return toMin
}
if input >= fromMax {
return toMax
}
return int(input*m + n)
}
}