requestrules

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package requestrules implements a declarative engine for polling on-demand NMEA 2000 data: send a request when a matching device comes online (or at startup), keep the data fresh, and re-request when a trigger PGN is seen — without duplicating ad-hoc request code per device/PGN.

The engine is transport-agnostic and has no dependency on the root lplex package (it operates on DeviceView, returns Request values). A driver (the broker) feeds it device/frame events and a clock tick, and transmits the Requests it returns.

Timing safety: every rule has a MinInterval — the floor between successive requests for the same (device, datum). The engine will never emit a request for the same target more often than MinInterval, regardless of how many triggers fire. An optional global floor caps the rate across all rules.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// GlobalMinInterval optionally caps the rate across ALL rules: no two
	// requests are emitted closer together than this. 0 = no global cap.
	GlobalMinInterval time.Duration
	// Clock is injectable for tests; nil uses time.Now.
	Clock func() time.Time
}

Config configures a new Engine.

type DeviceView

type DeviceView struct {
	Bus              string
	Source           uint8
	NAMEHex          string
	Manufacturer     string
	ManufacturerCode uint16
	DeviceClass      uint8
	DeviceFunction   uint8
	DeviceInstance   uint8
	ModelID          string
	ProductCode      uint16
}

DeviceView is the minimal device identity the engine matches against. The driver builds it from its own device registry (avoids an import cycle).

type Engine

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

Engine evaluates rules against device/frame events. All methods are safe for concurrent use, but in practice the driver calls them from one goroutine.

func New

func New(cfg Config) *Engine

New creates an Engine. Add rules with AddRule.

func (*Engine) AddRule

func (e *Engine) AddRule(r Rule) error

AddRule registers a rule. Returns an error if the rule is invalid.

func (*Engine) InterestingPGNs

func (e *Engine) InterestingPGNs() map[uint32]struct{}

InterestingPGNs returns the set of PGNs the engine cares about in OnFrame: the PGNs that mark a want fresh, plus the invalidation triggers. A driver (e.g. the broker) can check membership lock-free on its hot path and only call OnFrame for relevant frames, avoiding per-frame engine work.

func (*Engine) OnDeviceOffline

func (e *Engine) OnDeviceOffline(bus string, src uint8)

OnDeviceOffline forgets a device so its freshness state is reset.

func (*Engine) OnDeviceOnline

func (e *Engine) OnDeviceOnline(d DeviceView) []Request

OnDeviceOnline records a device as present and returns any requests triggered by OnOnline rules. Call when a device is first seen or its identity updates.

func (*Engine) OnFrame

func (e *Engine) OnFrame(bus string, src uint8, pgn uint32, data []byte) []Request

OnFrame processes an incoming frame: it marks wanted data fresh (so the engine knows the datum is now available as state) and applies invalidation triggers, returning any re-requests. data may be nil if not needed.

func (*Engine) Tick

func (e *Engine) Tick() []Request

Tick drives MaxAge-based refresh. The driver calls it periodically (e.g. 1 Hz).

type Match

type Match struct {
	Manufacturer     string
	ManufacturerCode uint16 // 0 = any
	ModelID          string // exact, or "Prefix*"
	DeviceClass      *uint8
	DeviceFunction   *uint8
	Name             string // CAN NAME hex, exact (case-insensitive)
	Source           *uint8
	Bus              string
}

Match selects which devices a rule applies to. All set fields must match (logical AND); zero/empty fields are wildcards. ModelID matches case-insensitively and supports a single trailing '*' for prefix matching.

type Request

type Request struct {
	Bus      string
	Dst      uint8
	Via      Via
	PGN      uint32 // ViaISORequest: PGN to request; ViaFrame: the frame's PGN
	Data     []byte // ViaFrame: the payload to send
	Priority uint8
	Rule     string // originating rule name (for logging)
	Want     Want
}

Request is an action the driver should transmit.

type Rule

type Rule struct {
	Name  string
	Match Match
	Wants []Want
	Via   Via

	// Destination. If ToDevice is true the request is sent to the matched
	// device's source address; otherwise to Dst (0 means 0xFF broadcast).
	ToDevice bool
	Dst      uint8

	// ViaFrame parameters.
	FramePGN       uint32
	FramePriority  uint8
	FrameTemplate  []byte // copied per request; SubKey written in if SubKeyWriteLen>0
	SubKeyWriteOff int    // byte offset in template to write the want's SubKey (LE)
	SubKeyWriteLen int    // 0 = don't write a subkey into the frame
	SubKeyReadOff  int    // byte offset in a response to read the subkey (LE)
	SubKeyReadLen  int    // 0 = responses are not sub-keyed

	// Timing.
	MinInterval  time.Duration // REQUIRED: floor between requests for the same (device, want)
	MaxAge       time.Duration // refresh when the datum is older than this; 0 = request once
	OnOnline     bool          // request when a matching device appears / at startup
	InvalidateOn []uint32      // PGNs that, when seen from the device, mark wants stale
}

Rule is a declarative request rule. Construct directly (Go API) or via config.

type Via

type Via int

Via is how a request is transmitted.

const (
	// ViaISORequest sends an ISO Request (PGN 59904) for the wanted PGN.
	ViaISORequest Via = iota
	// ViaFrame sends a templated frame, substituting the want's SubKey.
	ViaFrame
)

type Want

type Want struct {
	PGN       uint32
	SubKey    uint32
	HasSubKey bool
}

Want is one datum a rule keeps available. For ViaISORequest only PGN is used. For ViaFrame, SubKey identifies the parameter (e.g. a Victron register id): it is written into the request frame template and matched against responses so distinct sub-keyed values are tracked independently.

Jump to

Keyboard shortcuts

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