Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core(system): split system accesser features #1121

Merged
merged 1 commit into from
Jan 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading