Skip to content

Commit

Permalink
core(system): build system access with options
Browse files Browse the repository at this point in the history
  • Loading branch information
gen2thomas committed Jan 11, 2025
1 parent 1f2c1dd commit 1e2e392
Show file tree
Hide file tree
Showing 62 changed files with 1,011 additions and 487 deletions.
2 changes: 1 addition & 1 deletion MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ suitable:
Also those findings needs to be replaced, which usually affects developers, but not users:

* `system.WithDigitalPinGpiodAccess()` --> `system.WithDigitalPinCdevAccess()`
* `IsGpiodDigitalPinAccess()` --> `IsCdevDigitalPinAccess()`
* `IsGpiodDigitalPinAccess()` --> `HasDigitalPinCdevAccess()`

### PocketBeagle adaptor goes cdev

Expand Down
3 changes: 3 additions & 0 deletions platforms/adaptors/analogpinsadaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ func NewAnalogPinsAdaptor(sys *system.Accesser, t analogPinTranslator) *AnalogPi
sys: sys,
translate: t,
}

sys.AddAnalogSupport()

return &a
}

Expand Down
146 changes: 32 additions & 114 deletions platforms/adaptors/digitalpinsadaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,12 @@ type (
digitalPinInitializer func(gobot.DigitalPinner) error
)

type digitalPinGpiosForSPI struct {
sclkPin string
ncsPin string
sdoPin string
sdiPin string
}

type digitalPinsDebouncePin struct {
id string
period time.Duration
}

type digitalPinsEventOnEdgePin struct {
id string
handler func(lineOffset int, timestamp time.Duration, detectedEdge string, seqno uint32, lseqno uint32)
}

type digitalPinsPollForEdgeDetectionPin struct {
id string
pollInterval time.Duration
pollQuitChan chan struct{}
}

// digitalPinsConfiguration contains all changeable attributes of the adaptor.
type digitalPinsConfiguration struct {
initialize digitalPinInitializer
useSysfs *bool
gpiosForSPI *digitalPinGpiosForSPI
activeLowPins []string
pullDownPins []string
pullUpPins []string
openDrainPins []string
openSourcePins []string
debouncePins []digitalPinsDebouncePin
eventOnFallingEdgePins []digitalPinsEventOnEdgePin
eventOnRisingEdgePins []digitalPinsEventOnEdgePin
eventOnBothEdgesPins []digitalPinsEventOnEdgePin
pollForEdgeDetectionPins []digitalPinsPollForEdgeDetectionPin
debug bool
initialize digitalPinInitializer
systemOptions []system.AccesserOptionApplier
pinOptions map[string][]func(gobot.DigitalPinOptioner) bool
}

// DigitalPinsAdaptor is a adaptor for digital pins, normally used for composition in platforms.
Expand All @@ -62,7 +30,6 @@ type DigitalPinsAdaptor struct {
digitalPinsCfg *digitalPinsConfiguration
translate digitalPinTranslator
pins map[string]gobot.DigitalPinner
pinOptions map[string][]func(gobot.DigitalPinOptioner) bool
mutex sync.Mutex
}

Expand All @@ -76,15 +43,27 @@ func NewDigitalPinsAdaptor(
t digitalPinTranslator,
opts ...DigitalPinsOptionApplier,
) *DigitalPinsAdaptor {
a := &DigitalPinsAdaptor{
sys: sys,
digitalPinsCfg: &digitalPinsConfiguration{initialize: func(pin gobot.DigitalPinner) error { return pin.Export() }},
translate: t,
a := DigitalPinsAdaptor{
sys: sys,
digitalPinsCfg: &digitalPinsConfiguration{
initialize: func(pin gobot.DigitalPinner) error { return pin.Export() },
pinOptions: make(map[string][]func(gobot.DigitalPinOptioner) bool),
},
translate: t,
}

for _, o := range opts {
o.apply(a.digitalPinsCfg)
}
return a

a.sys.AddDigitalPinSupport(a.digitalPinsCfg.systemOptions...)

return &a
}

// WithDigitalPinDebug can be used to switch on debugging for SPI implementation.
func WithDigitalPinDebug() digitalPinsDebugOption {
return digitalPinsDebugOption(true)
}

// WithDigitalPinInitializer can be used to substitute the default initializer.
Expand All @@ -104,18 +83,6 @@ func WithGpioSysfsAccess() digitalPinsSystemSysfsOption {
return digitalPinsSystemSysfsOption(true)
}

// WithSpiGpioAccess can be used to switch the default SPI implementation to GPIO usage.
func WithSpiGpioAccess(sclkPin, ncsPin, sdoPin, sdiPin string) digitalPinsForSystemSpiOption {
o := digitalPinsForSystemSpiOption{
sclkPin: sclkPin,
ncsPin: ncsPin,
sdoPin: sdoPin,
sdiPin: sdiPin,
}

return o
}

// WithGpiosActiveLow prepares the given pins for inverse reaction on next initialize.
// This is working for inputs and outputs.
func WithGpiosActiveLow(pin string, otherPins ...string) digitalPinsActiveLowOption {
Expand Down Expand Up @@ -196,69 +163,16 @@ func (a *DigitalPinsAdaptor) Connect() error {
a.mutex.Lock()
defer a.mutex.Unlock()

if a.pinOptions != nil {
return fmt.Errorf("digital pin adaptor already connected, please call Finalize() for re-connect")
}

cfg := a.digitalPinsCfg

if cfg.useSysfs != nil {
if *cfg.useSysfs {
system.WithDigitalPinSysfsAccess()(a.sys)
} else {
system.WithDigitalPinCdevAccess()(a.sys)
}
}

if cfg.gpiosForSPI != nil {
system.WithSpiGpioAccess(a, cfg.gpiosForSPI.sclkPin, cfg.gpiosForSPI.ncsPin, cfg.gpiosForSPI.sdoPin,
cfg.gpiosForSPI.sdiPin)(a.sys)
if a.digitalPinsCfg.debug {
fmt.Println("connect the digital pins adaptor")
}

a.pinOptions = make(map[string][]func(gobot.DigitalPinOptioner) bool)

for _, pin := range cfg.activeLowPins {
a.pinOptions[pin] = append(a.pinOptions[pin], system.WithPinActiveLow())
}

for _, pin := range cfg.pullDownPins {
a.pinOptions[pin] = append(a.pinOptions[pin], system.WithPinPullDown())
}

for _, pin := range cfg.pullUpPins {
a.pinOptions[pin] = append(a.pinOptions[pin], system.WithPinPullUp())
}

for _, pin := range cfg.openDrainPins {
a.pinOptions[pin] = append(a.pinOptions[pin], system.WithPinOpenDrain())
}

for _, pin := range cfg.openSourcePins {
a.pinOptions[pin] = append(a.pinOptions[pin], system.WithPinOpenSource())
}

for _, pin := range cfg.debouncePins {
a.pinOptions[pin.id] = append(a.pinOptions[pin.id], system.WithPinDebounce(pin.period))
}

for _, pin := range cfg.eventOnFallingEdgePins {
a.pinOptions[pin.id] = append(a.pinOptions[pin.id], system.WithPinEventOnFallingEdge(pin.handler))
}

for _, pin := range cfg.eventOnRisingEdgePins {
a.pinOptions[pin.id] = append(a.pinOptions[pin.id], system.WithPinEventOnRisingEdge(pin.handler))
}

for _, pin := range cfg.eventOnBothEdgesPins {
a.pinOptions[pin.id] = append(a.pinOptions[pin.id], system.WithPinEventOnBothEdges(pin.handler))
}

for _, pin := range cfg.pollForEdgeDetectionPins {
a.pinOptions[pin.id] = append(a.pinOptions[pin.id],
system.WithPinPollForEdgeDetection(pin.pollInterval, pin.pollQuitChan))
if a.pins != nil {
return fmt.Errorf("digital pin adaptor already connected, please call Finalize() for re-connect")
}

a.pins = make(map[string]gobot.DigitalPinner)

return nil
}

Expand All @@ -267,6 +181,10 @@ func (a *DigitalPinsAdaptor) Finalize() error {
a.mutex.Lock()
defer a.mutex.Unlock()

if a.digitalPinsCfg.debug {
fmt.Println("finalize the digital pins adaptor")
}

var err error
for _, pin := range a.pins {
if pin != nil {
Expand All @@ -276,7 +194,7 @@ func (a *DigitalPinsAdaptor) Finalize() error {
}
}
a.pins = nil
a.pinOptions = nil

return err
}

Expand Down Expand Up @@ -321,7 +239,7 @@ func (a *DigitalPinsAdaptor) digitalPin(
return nil, fmt.Errorf("not connected for pin %s", id)
}

o := append(a.pinOptions[id], opts...)
o := append(a.digitalPinsCfg.pinOptions[id], opts...)
pin := a.pins[id]

if pin == nil {
Expand Down
7 changes: 4 additions & 3 deletions platforms/adaptors/digitalpinsadaptor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,17 @@ func TestNewDigitalPinsAdaptor(t *testing.T) {
// arrange
translate := func(string) (string, int, error) { return "", 0, nil }
sys := system.NewAccesser()
// arrange for cdev needed
sys.UseMockFilesystem([]string{"/dev/gpiochip"})
// act
a := NewDigitalPinsAdaptor(sys, translate)
// assert
assert.IsType(t, &DigitalPinsAdaptor{}, a)
assert.NotNil(t, a.sys)
assert.NotNil(t, a.digitalPinsCfg)
assert.NotNil(t, a.translate)
assert.Nil(t, a.pins) // will be created on connect
assert.Nil(t, a.pinOptions) // will be created on connect
assert.True(t, a.sys.IsCdevDigitalPinAccess())
assert.Nil(t, a.pins) // will be created on connect
assert.True(t, a.sys.HasDigitalPinCdevAccess())
}

func TestDigitalPinsConnect(t *testing.T) {
Expand Down
69 changes: 41 additions & 28 deletions platforms/adaptors/digitalpinsadaptoroptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package adaptors

import (
"time"

"gobot.io/x/gobot/v2/system"
)

// DigitalPinsOptionApplier is the interface for digital adaptors options. This provides the possibility for change the
Expand All @@ -11,16 +13,16 @@ type DigitalPinsOptionApplier interface {
apply(cfg *digitalPinsConfiguration)
}

// digitalPinsDebugOption is the type to switch on digital pin related debug messages.
type digitalPinsDebugOption bool

// digitalPinInitializeOption is the type for applying another than the default initializer
type digitalPinsInitializeOption digitalPinInitializer

// digitalPinsSystemSysfsOption is the type to change the default character device implementation to the legacy
// sysfs Kernel ABI
type digitalPinsSystemSysfsOption bool

// digitalPinsForSystemSpiOption is the type to switch the default SPI implementation to GPIO usage
type digitalPinsForSystemSpiOption digitalPinGpiosForSPI

// digitalPinsActiveLowOption is the type to prepare the given pins for inverse reaction on next initialize
type digitalPinsActiveLowOption []string

Expand Down Expand Up @@ -75,6 +77,10 @@ type digitalPinsPollForEdgeDetectionOption struct {
pollQuitChan chan struct{}
}

func (o digitalPinsDebugOption) String() string {
return "switch on debugging for digital pins option"
}

func (o digitalPinsInitializeOption) String() string {
return "pin initializer option for digital IO's"
}
Expand All @@ -83,10 +89,6 @@ func (o digitalPinsSystemSysfsOption) String() string {
return "use sysfs vs. cdev system access option for digital pins"
}

func (o digitalPinsForSystemSpiOption) String() string {
return "use digital pins for SPI option"
}

func (o digitalPinsActiveLowOption) String() string {
return "digital pins set to active low option"
}
Expand Down Expand Up @@ -127,61 +129,72 @@ func (o digitalPinsPollForEdgeDetectionOption) String() string {
return "discrete polling function for edge detection on digital pin option"
}

func (o digitalPinsDebugOption) apply(cfg *digitalPinsConfiguration) {
cfg.debug = bool(o)
cfg.systemOptions = append(cfg.systemOptions, system.WithDigitalPinDebug())
}

func (o digitalPinsInitializeOption) apply(cfg *digitalPinsConfiguration) {
cfg.initialize = digitalPinInitializer(o)
}

func (o digitalPinsSystemSysfsOption) apply(cfg *digitalPinsConfiguration) {
c := bool(o)
cfg.useSysfs = &c
}
useSysFs := bool(o)

func (o digitalPinsForSystemSpiOption) apply(cfg *digitalPinsConfiguration) {
c := digitalPinGpiosForSPI(o)
cfg.gpiosForSPI = &c
if useSysFs {
cfg.systemOptions = append(cfg.systemOptions, system.WithDigitalPinSysfsAccess())
} else {
cfg.systemOptions = append(cfg.systemOptions, system.WithDigitalPinCdevAccess())
}
}

func (o digitalPinsActiveLowOption) apply(cfg *digitalPinsConfiguration) {
cfg.activeLowPins = append(cfg.activeLowPins, o...)
for _, pin := range o {
cfg.pinOptions[pin] = append(cfg.pinOptions[pin], system.WithPinActiveLow())
}
}

func (o digitalPinsPullDownOption) apply(cfg *digitalPinsConfiguration) {
cfg.pullDownPins = append(cfg.pullDownPins, o...)
for _, pin := range o {
cfg.pinOptions[pin] = append(cfg.pinOptions[pin], system.WithPinPullDown())
}
}

func (o digitalPinsPullUpOption) apply(cfg *digitalPinsConfiguration) {
cfg.pullUpPins = append(cfg.pullUpPins, o...)
for _, pin := range o {
cfg.pinOptions[pin] = append(cfg.pinOptions[pin], system.WithPinPullUp())
}
}

func (o digitalPinsOpenDrainOption) apply(cfg *digitalPinsConfiguration) {
cfg.openDrainPins = append(cfg.openDrainPins, o...)
for _, pin := range o {
cfg.pinOptions[pin] = append(cfg.pinOptions[pin], system.WithPinOpenDrain())
}
}

func (o digitalPinsOpenSourceOption) apply(cfg *digitalPinsConfiguration) {
cfg.openSourcePins = append(cfg.openSourcePins, o...)
for _, pin := range o {
cfg.pinOptions[pin] = append(cfg.pinOptions[pin], system.WithPinOpenSource())
}
}

func (o digitalPinsDebounceOption) apply(cfg *digitalPinsConfiguration) {
pinCfg := digitalPinsDebouncePin(o)
cfg.debouncePins = append(cfg.debouncePins, pinCfg)
cfg.pinOptions[o.id] = append(cfg.pinOptions[o.id], system.WithPinDebounce(o.period))
}

func (o digitalPinsEventOnFallingEdgeOption) apply(cfg *digitalPinsConfiguration) {
pinCfg := digitalPinsEventOnEdgePin(o)
cfg.eventOnFallingEdgePins = append(cfg.eventOnFallingEdgePins, pinCfg)
cfg.pinOptions[o.id] = append(cfg.pinOptions[o.id], system.WithPinEventOnFallingEdge(o.handler))
}

func (o digitalPinsEventOnRisingEdgeOption) apply(cfg *digitalPinsConfiguration) {
pinCfg := digitalPinsEventOnEdgePin(o)
cfg.eventOnRisingEdgePins = append(cfg.eventOnRisingEdgePins, pinCfg)
cfg.pinOptions[o.id] = append(cfg.pinOptions[o.id], system.WithPinEventOnRisingEdge(o.handler))
}

func (o digitalPinsEventOnBothEdgesOption) apply(cfg *digitalPinsConfiguration) {
pinCfg := digitalPinsEventOnEdgePin(o)
cfg.eventOnBothEdgesPins = append(cfg.eventOnBothEdgesPins, pinCfg)
cfg.pinOptions[o.id] = append(cfg.pinOptions[o.id], system.WithPinEventOnBothEdges(o.handler))
}

func (o digitalPinsPollForEdgeDetectionOption) apply(cfg *digitalPinsConfiguration) {
pinCfg := digitalPinsPollForEdgeDetectionPin(o)
cfg.pollForEdgeDetectionPins = append(cfg.pollForEdgeDetectionPins, pinCfg)
cfg.pinOptions[o.id] = append(cfg.pinOptions[o.id],
system.WithPinPollForEdgeDetection(o.pollInterval, o.pollQuitChan))
}
Loading

0 comments on commit 1e2e392

Please sign in to comment.