sdk

package
v2.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 22, 2022 License: GPL-3.0 Imports: 43 Imported by: 8

Documentation

Overview

Package sdk enables the development of plugins for the Synse Platform.

The Synse SDK implements the core plugin logic and makes it easy to create new plugins. These plugins form the backbone of the Synse platform, interfacing with devices and exposing them to Synse Server so all devices can be managed and controlled though a simple HTTP interface, regardless of backend protocol.

For more information, see: https://synse.readthedocs.io/en/latest/sdk/intro/

Index

Constants

View Source
const (
	// Tag namespace constants
	TagNamespaceDefault = "default"
	TagNamespaceSystem  = "system"

	// Tag annotation constants
	TagAnnotationID   = "id"
	TagAnnotationType = "type"

	// Special tag components
	TagLabelAll = "**"
)

Tag component constants.

View Source
const (
	// DeviceEnvOverride defines the environment variable that can be used to
	// set an override config location for device configuration files.
	DeviceEnvOverride = "PLUGIN_DEVICE_CONFIG"
)
View Source
const (
	// PluginEnvOverride defines the environment variable that can be used to
	// set an override config location for the Plugin configuration file.
	PluginEnvOverride = "PLUGIN_CONFIG"
)
View Source
const Version = "v2.1.2"

Version specifies the version of the Synse Plugin SDK.

Variables

View Source
var (
	ErrDeviceNotWritable  = errors.New("writing is not enabled for the device")
	ErrDeviceWriteTimeout = errors.New("device write timed out")
	ErrNilDevice          = errors.New("cannot perform action on nil device")
	ErrNilData            = errors.New("cannot write nil data to device")
)

Scheduler error definitions.

View Source
var (
	ErrServerNeedsConfig    = errors.New("server requires configuration to initialize")
	ErrServerNotInitialized = errors.New("unable to run: server not initialized")

	ErrSelectorRequiresID  = sdkError.InvalidArgumentErr("selector must specify device id")
	ErrNoDeviceForSelector = sdkError.NotFoundErr("no device found for specified selector")
	ErrTransactionNotFound = sdkError.NotFoundErr("transaction not found")
)

Server error definitions.

View Source
var (
	ErrNilTransformConfig = errors.New("cannot create transformer: nil config")
	ErrUnknownTransformFn = errors.New("unknown transform apply function specified")
)

Errors relating to Transformer creation and application.

View Source
var (
	// BuildDate is the timestamp for when the build happened.
	BuildDate string

	// GitCommit is the commit hash at which the plugin was built.
	GitCommit string

	// GitTag is the git tag at which the plugin was built.
	GitTag string

	// GoVersion is the version of Go used to build the plugin.
	GoVersion string

	// PluginVersion is the canonical version string for the plugin.
	PluginVersion string
)
View Source
var (
	ErrDeviceIDExists = errors.New("conflict: device id already exists")
)

Device manager error definitions.

Functions

func SetPluginInfo

func SetPluginInfo(name, maintainer, desc, vcs string)

SetPluginInfo sets the meta-information for a Plugin. This should be this first step in creating a new plugin.

Types

type AliasCache

type AliasCache struct {
	// contains filtered or unexported fields
}

AliasCache is a cache which is used to lookup devices based on any aliases registered by the device.

func NewAliasCache

func NewAliasCache() *AliasCache

NewAliasCache creates a new AliasCache instance.

func (*AliasCache) Add

func (cache *AliasCache) Add(alias string, device *Device) error

Add adds a device alias mapping to the cache.

func (*AliasCache) Get

func (cache *AliasCache) Get(alias string) *Device

Get gets the device associated with the specified alias. If the given alias is not associated with a device, this returns nil.

type AliasContext

type AliasContext struct {
	Meta   *PluginMetadata
	Device *Device
}

AliasContext is the context that is used to render alias templates.

type ApplyTransformer

type ApplyTransformer struct {
	Func *funcs.Func
}

ApplyTransformer is a device reading Transformer which applies pre-defined functions to a device's reading(s).

func NewApplyTransformer

func NewApplyTransformer(fn string) (*ApplyTransformer, error)

NewApplyTransformer creates a new device reading Transformer which is used to apply pre-defined functions to a device's reading(s). The SDK has some built-in functions in the 'funcs' package. A plugin may also register its own. Functions are referenced by name.

func (*ApplyTransformer) Apply

func (t *ApplyTransformer) Apply(reading *output.Reading) error

Apply the transformer function to the given reading value.

func (*ApplyTransformer) Name

func (t *ApplyTransformer) Name() string

Name returns a human-readable name for the apply transformer.

type Device

type Device struct {
	// Type is the type of the device. This is largely metadata that can
	// be used upstream to categorize the device.
	Type string

	// Info is a human-readable string that provides a summary of what
	// the device is or what it does.
	Info string

	// Tags is the set of tags that are associated with the device.
	Tags []*Tag

	// Data contains any plugin-specific configuration data for the device.
	Data map[string]interface{}

	// Context contains any contextual information which should be associated
	// with the device's reading(s).
	Context map[string]string

	// Handler is the name of the device's handler.
	Handler string

	// SortIndex is an optional 1-based index sort ordinal that is used upstream
	// (by Synse Server) to sort the device in Scan output. This can be set to
	// give devices custom ordering. If not set, they will be sorted based on
	// default sorting parameters.
	SortIndex int32

	// Alias is a human-readable alias for the device which can be used to
	// to reference it as well.
	Alias string

	// Transforms is a collection of the device reading transformers that are to
	// be applied to the device's reading(s). These are generally (but not limited)
	// to scale or convert the device reading(s). Typically, these will be defined
	// for general-purpose plugins where the device handler is not specific enough
	// to know the desired output of a device reading.
	//
	// Transformations are applied in the order in which they are defined.
	//
	// See the TransformConfig and Transformer godoc for more details.
	Transforms []Transformer

	// WriteTimeout defines the time within which a write action (transaction)
	// will remain valid for this device.
	WriteTimeout time.Duration

	// Output is the name of the Output that this device instance will use. This
	// is not needed for all devices/plugins, as many DeviceHandlers will already
	// know which output to use. This field is used in cases of generalized plugins,
	// such as Modbus-IP, where a generalized handler will need to map something
	// (like a set of registers) to a reading output.
	Output string
	// contains filtered or unexported fields
}

Device is a single physical or virtual device which the Plugin manages.

It defines all of the information known about the device, which typically comes from configuration file. A Device's supported actions are determined by the DeviceHandler which it is configured to use.

func NewDeviceFromConfig

func NewDeviceFromConfig(
	proto *config.DeviceProto,
	instance *config.DeviceInstance,
	handlers map[string]*DeviceHandler,
) (*Device, error)

NewDeviceFromConfig creates a new instance of a Device from its device prototype and device instance configuration. This is the primary and recommended way of building devices.

These configuration components are loaded from config file.

The device instance and prototype may specify a device handler name in their Handler field. This is only the name of the Handler, not a reference to the device Handler itself. A map[string]*DeviceHandler must be passed to this function to register the device handler with the device (in the private `handler` field, looking up the appropriate handler from the map using the `Handler` name.

If no handler is found in the map for the device, an error is returned.

func (*Device) GetContext

func (device *Device) GetContext(key string) string

GetContext gets a value out of the device's context map.

func (*Device) GetHandler

func (device *Device) GetHandler() *DeviceHandler

GetHandler gets the DeviceHandler of the device.

func (*Device) GetID

func (device *Device) GetID() string

GetID gets the unique ID of the device.

func (*Device) IsReadable

func (device *Device) IsReadable() bool

IsReadable checks if the Device is readable based on the presence/absence of a Read/BulkRead action defined in its DeviceHandler.

func (*Device) IsWritable

func (device *Device) IsWritable() bool

IsWritable checks if the Device is writable based on the presence/absence of a Write action defined in its DeviceHandler.

func (*Device) Read

func (device *Device) Read() (*ReadContext, error)

Read performs the read action for the device, as set by its DeviceHandler.

If reading is not supported on the device, an UnsupportedCommandError is returned.

func (*Device) Write

func (device *Device) Write(data *WriteData) error

Write performs the write action for the device, as set by its DeviceHandler.

If writing is not supported on the device, an UnsupportedCommandError is returned.

type DeviceAction

type DeviceAction struct {
	// Name is the name of the action. This is used to identify the action.
	Name string

	// Filter is the device filter that scopes which devices this action
	// should apply to. This filter is run on the entire set of registered
	// devices and is additive (e.g. a device does not need to match all
	// filters to be included, it just needs to match one).
	//
	// The filter provided should be a map, where the key is the field to filter
	// on and the value are the allowable values for that field. The currently
	// supported filters include:
	//  * "type" : the device type
	Filter map[string][]string

	// The action to execute for the device.
	Action func(p *Plugin, d *Device) error
}

DeviceAction defines an action that can be run before the main Plugin run logic. This is generally used for doing device-specific setup actions.

type DeviceDataValidator

type DeviceDataValidator func(map[string]interface{}) error

DeviceDataValidator is a handler function that takes the `Data` field of a device config and performs some validation on it. This allows users to provide validation on the plugin-specific config fields.

type DeviceHandler

type DeviceHandler struct {

	// Name is the name of the handler. This is how the handler will be referenced
	// and associated with Device instances via their configuration. This name should
	// match with the "Handler" configuration field.
	Name string

	// Write is a function that handles Write requests for the handler's devices. If
	// the devices do not support writing, this can be left unspecified.
	Write func(*Device, *WriteData) error

	// Read is a function that handles Read requests for the handler's devices. If the
	// devices do not support reading, this can be left unspecified.
	Read func(*Device) ([]*output.Reading, error)

	// BulkRead is a function that handles bulk read operations for the handler's devices.
	// A bulk read is where all devices of a given kind are read at once, instead of individually.
	// If a device does not support bulk read, this can be left as nil. Additionally,
	// a device can only be bulk read if there is no Read handler set.
	BulkRead func([]*Device) ([]*ReadContext, error)

	// Listen is a function that will listen for push-based data from the device.
	// This function is called one per device using the handler, even if there are
	// other handler functions (e.g. read, write) defined. The listener function
	// will run in a separate goroutine for each device. The goroutines are started
	// before the read/write loops.
	//
	// NOTE: The Listen function is deprecated and will be removed in a future version
	// of the SDK.
	Listen func(*Device, chan *ReadContext) error

	// Actions specifies a list of the supported write actions for the handler.
	// This is optional and is just used as metadata surfaced by the SDK to the
	// client via the gRPC API.
	Actions []string
}

DeviceHandler specifies the read and write handlers for a Device based on its type and model.

func (*DeviceHandler) CanBulkRead

func (handler *DeviceHandler) CanBulkRead() bool

CanBulkRead returns true if the handler has a bulk read function defined and no regular read function defined (both cannot coexist); false otherwise.

func (*DeviceHandler) CanListen

func (handler *DeviceHandler) CanListen() bool

CanListen returns true if the handler has a listen function defined; false otherwise.

func (*DeviceHandler) CanRead

func (handler *DeviceHandler) CanRead() bool

CanRead returns true if the handler has a read function defined; false otherwise.

func (*DeviceHandler) CanWrite

func (handler *DeviceHandler) CanWrite() bool

CanWrite returns true if the handler has a write function defined; false otherwise.

func (*DeviceHandler) GetCapabilitiesMode

func (handler *DeviceHandler) GetCapabilitiesMode() string

GetCapabilitiesMode gets the capabilities mode string representation for a device based on its device handler. This will be one of: "r" (read-only), "w" (write-only), or "rw" (read-write).

Note that a device is considered readable here if it can supply reading data. Currently, Read, BulkRead, and Listen can all supply reading data, so if any one of those are defined for the handler, the capabilities string will reflect that.

type DeviceIdentifier

type DeviceIdentifier func(map[string]interface{}) string

DeviceIdentifier is a handler function that produces a string that can be used to identify a device deterministically. The returned string should be a composite from the Device's config data.

type DynamicDeviceConfigRegistrar

type DynamicDeviceConfigRegistrar func(map[string]interface{}) ([]*config.DeviceProto, error)

DynamicDeviceConfigRegistrar is a handler function that takes a Plugin config's "dynamic registration" data and generates Devices config instances from it. How this is done is specific to the plugin/protocol.

type DynamicDeviceRegistrar

type DynamicDeviceRegistrar func(map[string]interface{}) ([]*Device, error)

DynamicDeviceRegistrar is a handler function that takes a Plugin config's "dynamic registration" data and generates Device instances from it. How this is done is specific to the plugin/protocol.

type ListenerCtx

type ListenerCtx struct {
	// contains filtered or unexported fields
}

ListenerCtx is the context needed for a listener function to be called and retried at a later time if it errors out after the listener goroutine is initially dispatched.

func NewListenerCtx

func NewListenerCtx(handler *DeviceHandler, device *Device) *ListenerCtx

NewListenerCtx creates a new ListenerCtx for the given handler and device.

type Plugin

type Plugin struct {
	// contains filtered or unexported fields
}

Plugin is a Synse Plugin.

func NewPlugin

func NewPlugin(options ...PluginOption) (*Plugin, error)

NewPlugin creates a new instance of a Plugin. This should be the only way that a Plugin is initialized.

This constructor will load the plugin configuration; if it is not present or invalid, this will fail. All other Plugin component initialization is deferred until Run is called.

func (*Plugin) AddDevice

func (plugin *Plugin) AddDevice(device *Device) error

AddDevice adds a new device to the plugin's device manager.

func (*Plugin) GenerateDeviceID

func (plugin *Plugin) GenerateDeviceID(device *Device) string

GenerateDeviceID generates the deterministic ID for a device using the data contained within a Device definition as well as the DeviceIdentifier function, whether custom or default.

func (*Plugin) GetDevice

func (plugin *Plugin) GetDevice(id string) *Device

GetDevice gets a device from the plugin's device manager.

func (*Plugin) NewDevice

func (plugin *Plugin) NewDevice(proto *config.DeviceProto, instance *config.DeviceInstance) (*Device, error)

NewDevice creates a new device, using the Device handlers registered with the plugin.

Note that this does not add the new device to the plugin.

func (*Plugin) RegisterDeviceHandlers

func (plugin *Plugin) RegisterDeviceHandlers(handlers ...*DeviceHandler) error

RegisterDeviceHandlers adds DeviceHandlers to the Plugin.

These DeviceHandlers are matched with the Device instances by their name and provide the read/write functionality for Devices. If a DeviceHandler is not registered for a Device, the Device will not be usable by the plugin.

func (*Plugin) RegisterDeviceSetupActions

func (plugin *Plugin) RegisterDeviceSetupActions(actions ...*DeviceAction) error

RegisterDeviceSetupActions registers actions with the device manager which will be executed on start. These actions are used for device-specific setup.

func (*Plugin) RegisterHealthChecks

func (plugin *Plugin) RegisterHealthChecks(checks ...health.Check) error

RegisterHealthChecks registers custom health checks with the plugin.

func (*Plugin) RegisterOutputs

func (plugin *Plugin) RegisterOutputs(outputs ...*output.Output) error

RegisterOutputs registers new Outputs with the plugin. A plugin will automatically register the built-in SDK outputs. This function allows a plugin do augment that set of outputs with its own custom outputs.

If any registered output names conflict with those of built-in or other custom outputs, an error is returned.

func (*Plugin) RegisterPostRunActions

func (plugin *Plugin) RegisterPostRunActions(actions ...*PluginAction)

RegisterPostRunActions registers actions with the Plugin which will be called after it terminates.

These actions are generally cleanup and teardown actions.

func (*Plugin) RegisterPreRunActions

func (plugin *Plugin) RegisterPreRunActions(actions ...*PluginAction)

RegisterPreRunActions registers actions with the Plugin which will be called prior to the business logic of the Plugin.

Pre-run actions are considered setup/validator actions and as such, they are included in the Plugin dry-run.

func (*Plugin) Run

func (plugin *Plugin) Run() error

Run starts the plugin.

This is the functional starting point for all plugins. Once this is called, the plugin will initialize all of its components and validate its state. Once everything is ready, it will run each of its components. The gRPC server is run in the foreground; all other components are run as goroutines.

type PluginAction

type PluginAction struct {
	Name   string
	Action func(p *Plugin) error
}

PluginAction defines an action that can be run before or after the main Plugin run logic. This is generally used for setup/teardown.

type PluginHandlers

type PluginHandlers struct {
	// DeviceIdentifier is a plugin-defined function for uniquely identifying devices.
	DeviceIdentifier DeviceIdentifier

	// DynamicRegistrar is a plugin-defined function which generates devices dynamically.
	DynamicRegistrar DynamicDeviceRegistrar

	// DynamicConfigRegistrar is a plugin-defined function which generates device configs dynamically.
	DynamicConfigRegistrar DynamicDeviceConfigRegistrar

	// DeviceDataValidator is a plugin-defined function that can be used to validate a
	// Device's Data field.
	DeviceDataValidator DeviceDataValidator
}

PluginHandlers defines the set of handler functions that a Plugin instance may use.

func NewDefaultPluginHandlers

func NewDefaultPluginHandlers() *PluginHandlers

NewDefaultPluginHandlers returns the default set of plugin handlers.

type PluginMetadata

type PluginMetadata struct {
	Name        string
	Maintainer  string
	Description string
	VCS         string
}

PluginMetadata is the metadata associated with a Plugin.

func (*PluginMetadata) Tag

func (info *PluginMetadata) Tag() string

Tag creates the tag used in the plugin meta information.

type PluginOption

type PluginOption func(*Plugin)

A PluginOption sets optional configurations or functional capabilities for a plugin. This includes things like device identification and device registration behaviors.

func CustomDeviceDataValidator

func CustomDeviceDataValidator(validator DeviceDataValidator) PluginOption

CustomDeviceDataValidator lets you set a custom function for validating the Data field of a device's config. By default, this data is not validated by the SDK, since it is plugin-specific.

func CustomDeviceIdentifier

func CustomDeviceIdentifier(identifier DeviceIdentifier) PluginOption

CustomDeviceIdentifier lets you set a custom function for creating a deterministic identifier for a device using the config data for the device.

func CustomDynamicDeviceConfigRegistration

func CustomDynamicDeviceConfigRegistration(registrar DynamicDeviceConfigRegistrar) PluginOption

CustomDynamicDeviceConfigRegistration lets you set a custom function for dynamically registering DeviceConfig instances using the data from the "dynamic registration" field in the Plugin config.

func CustomDynamicDeviceRegistration

func CustomDynamicDeviceRegistration(registrar DynamicDeviceRegistrar) PluginOption

CustomDynamicDeviceRegistration lets you set a custom function for dynamically registering Device instances using the data from the "dynamic registration" field in the Plugin config.

func DeviceConfigOptional

func DeviceConfigOptional() PluginOption

DeviceConfigOptional is a PluginOption which designates that a Plugin should not require device configurations to be required, as they are by default. This can be the case when a plugin may support dynamic device configuration, so pre-defined device configs may not be specified.

func DynamicConfigRequired

func DynamicConfigRequired() PluginOption

DynamicConfigRequired is a PluginOption which designates that a Plugin requires dynamic device configuration. By default, dynamic device configuration is optional. This can be set if a plugin is designed to only load devices in a dynamic fashion, and not through pre-defined config files.

func PluginConfigRequired

func PluginConfigRequired() PluginOption

PluginConfigRequired is a PluginOption which designates that a Plugin should require a plugin config and will fail if it does not detect one. By default, a Plugin considers them optional and will use a set of default configurations if no config is found.

type ReadContext

type ReadContext struct {
	Device  *Device
	Reading []*output.Reading
}

ReadContext provides the context for a device reading. This context identifies the device being read and associates it with a set of readings at a given time.

A single device can provide more than one reading (e.g. a humidity sensor could provide both a humidity and temperature reading). To accommodate, the ReadContext allows for multiple readings to be associated with the device. Note that the collection of readings in a single ReadContext would correspond to a single Read request.

func NewReadContext

func NewReadContext(device *Device, readings []*output.Reading) *ReadContext

NewReadContext creates a new instance of a ReadContext from the given device and corresponding readings.

type ReadStream

type ReadStream struct {
	// contains filtered or unexported fields
}

ReadStream encapsulates a channel which is used to stream data to a client.

type ScaleTransformer

type ScaleTransformer struct {
	Factor float64
}

ScaleTransformer is a device reading transformer which scales a device's reading(s) by a multiplicative factor.

func NewScaleTransformer

func NewScaleTransformer(factor string) (*ScaleTransformer, error)

NewScaleTransformer creates a new device reading Transformer which is used to scale readings by a multiplicative factor.

The scaling factor is specified as a string, but should resolve to a numeric. By default, it will have a value of 1 (e.g. no-op). Negative values and fractional values are supported. This can be the value itself, e.g. "0.01", or a mathematical representation of the value, e.g. "1e-2". Dividing is the same as multiplying by a fraction (e.g. "/ 2" == "* 0.5").

func (*ScaleTransformer) Apply

func (t *ScaleTransformer) Apply(reading *output.Reading) error

Apply the transformer scaling factor to the given reading value.

func (*ScaleTransformer) Name

func (t *ScaleTransformer) Name() string

Name returns a human-readable name for the scale transformer.

type Tag

type Tag struct {
	Namespace  string
	Annotation string
	Label      string
	// contains filtered or unexported fields
}

Tag represents a group identifier which a Synse device can belong to.

func DeviceSelectorToID

func DeviceSelectorToID(selector *synse.V3DeviceSelector) *Tag

DeviceSelectorToID is a utility which converts a gRPC device selector message into a corresponding ID tag. If the selector has no value in the Id field, this will return nil.

func DeviceSelectorToTags

func DeviceSelectorToTags(selector *synse.V3DeviceSelector) []*Tag

DeviceSelectorToTags is a utility that converts a gRPC device selector message into its corresponding tags.

func NewTag

func NewTag(tag string) (*Tag, error)

NewTag creates a new Tag from a tag string.

func NewTagFromGRPC

func NewTagFromGRPC(tag *synse.V3Tag) *Tag

NewTagFromGRPC creates a new Tag from the gRPC tag message.

func (*Tag) Encode

func (tag *Tag) Encode() *synse.V3Tag

Encode translates the Tag to its corresponding gRPC message.

func (*Tag) HasAnnotation

func (tag *Tag) HasAnnotation() bool

HasAnnotation checks whether the tag has an annotation defined.

func (*Tag) HasNamespace

func (tag *Tag) HasNamespace() bool

HasNamespace checks whether the tag has a namespace defined.

func (*Tag) String

func (tag *Tag) String() string

String prints the Tag in its string representation.

type TagCache

type TagCache struct {
	// contains filtered or unexported fields
}

TagCache is a cache which can be used for looking up devices based on their tags.

func NewTagCache

func NewTagCache() *TagCache

NewTagCache creates a new TagCache instance.

func (*TagCache) Add

func (cache *TagCache) Add(tag *Tag, device *Device)

Add adds a device to the tag cache for the specified tag.

func (*TagCache) GetDevicesFromNamespace

func (cache *TagCache) GetDevicesFromNamespace(namespaces ...string) []*Device

GetDevicesFromNamespace gets the devices for the specified namespaces.

func (*TagCache) GetDevicesFromStrings

func (cache *TagCache) GetDevicesFromStrings(tags ...string) ([]*Device, error)

GetDevicesFromStrings gets the list of Devices which match the given set of tag strings.

func (*TagCache) GetDevicesFromTags

func (cache *TagCache) GetDevicesFromTags(tags ...*Tag) []*Device

GetDevicesFromTags gets the list of Devices which match the given set of tags.

type Transformer

type Transformer interface {

	// Apply the transformation to the given reading value.
	Apply(reading *output.Reading) error

	// Get the name of the transformer.
	Name() string
}

A Transformer is something which transforms a device's raw reading value(s). This transformation is typically a scaling or type conversion. This is generally used more for general-purpose plugins where the device handler is not specific to the device/output and the reading value may need to be coerced into a desired output.

func NewTransformer

func NewTransformer(cfg *config.TransformConfig) (Transformer, error)

NewTransformer creates a new device reading Transformer from the provided TransformConfig. If the configuration is incorrect or specifies unsupported values, an error is returned.

type WriteContext

type WriteContext struct {
	// contains filtered or unexported fields
}

WriteContext describes a single write transaction.

type WriteData

type WriteData synse.V3WriteData

WriteData is an SDK alias for the Synse gRPC WriteData. This is done to make writing new plugins easier.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL