-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
system(gpio): add edge polling function
- Loading branch information
1 parent
ee4368b
commit 0efa9fe
Showing
8 changed files
with
326 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
package system | ||
|
||
import ( | ||
"fmt" | ||
"sync" | ||
"time" | ||
) | ||
|
||
// TODO write tests | ||
|
||
func startEdgePolling( | ||
pinLabel string, | ||
pinReadFunc func() (int, error), | ||
pollInterval time.Duration, | ||
wantedEdge int, | ||
eventHandler func(offset int, t time.Duration, et string, sn uint32, lsn uint32), | ||
quitChan chan struct{}, | ||
) error { | ||
if eventHandler == nil { | ||
return fmt.Errorf("an event handler is mandatory for edge polling") | ||
} | ||
if quitChan == nil { | ||
return fmt.Errorf("the quit channel is mandatory for edge polling") | ||
} | ||
|
||
const allEdges = "all" | ||
|
||
triggerEventOn := "none" | ||
switch wantedEdge { | ||
case digitalPinEventOnFallingEdge: | ||
triggerEventOn = DigitalPinEventFallingEdge | ||
case digitalPinEventOnRisingEdge: | ||
triggerEventOn = DigitalPinEventRisingEdge | ||
case digitalPinEventOnBothEdges: | ||
triggerEventOn = allEdges | ||
default: | ||
return fmt.Errorf("unsupported edge type %d for edge polling", wantedEdge) | ||
} | ||
|
||
wg := sync.WaitGroup{} | ||
wg.Add(1) | ||
|
||
go func() { | ||
var oldState int | ||
var readStart time.Time | ||
var firstLoopDone bool | ||
for { | ||
select { | ||
case <-quitChan: | ||
return | ||
default: | ||
// note: pure reading takes between 30us and 1ms on rasperry Pi1, typically 50us, with sysfs also 500us | ||
// can happen, so we use the time stamp before start of reading to reduce random duration offset | ||
readStart = time.Now() | ||
readedValue, err := pinReadFunc() | ||
if err != nil { | ||
fmt.Printf("edge polling error occurred while reading the pin %s: %v", pinLabel, err) | ||
readedValue = oldState // keep the value | ||
} | ||
if readedValue != oldState { | ||
detectedEdge := DigitalPinEventRisingEdge | ||
if readedValue < oldState { | ||
detectedEdge = DigitalPinEventFallingEdge | ||
} | ||
if firstLoopDone && (triggerEventOn == allEdges || triggerEventOn == detectedEdge) { | ||
eventHandler(0, time.Duration(readStart.UnixNano()), detectedEdge, 0, 0) | ||
} | ||
oldState = readedValue | ||
} | ||
// the real poll interval is increased by the reading time, see also note above | ||
// negative or zero duration causes no sleep | ||
time.Sleep(pollInterval - time.Since(readStart)) | ||
if !firstLoopDone { | ||
wg.Done() | ||
firstLoopDone = true | ||
} | ||
} | ||
} | ||
}() | ||
|
||
wg.Wait() | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package system | ||
|
||
import ( | ||
"sync" | ||
"testing" | ||
"time" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func Test_startEdgePolling(t *testing.T) { | ||
tests := map[string]struct { | ||
eventOnEdge int | ||
quitChan chan struct{} | ||
simulateReturnValues []int | ||
simulateNoEventHandler bool | ||
wantEdgeTypes []string | ||
wantErr string | ||
}{ | ||
"edge_falling": { | ||
eventOnEdge: digitalPinEventOnFallingEdge, | ||
quitChan: make(chan struct{}), | ||
simulateReturnValues: []int{1, 0, 1, 0, 0}, | ||
wantEdgeTypes: []string{DigitalPinEventFallingEdge, DigitalPinEventFallingEdge}, | ||
}, | ||
"no_edge_falling": { | ||
eventOnEdge: digitalPinEventOnFallingEdge, | ||
quitChan: make(chan struct{}), | ||
simulateReturnValues: []int{0, 1, 1}, | ||
wantEdgeTypes: nil, | ||
}, | ||
"edge_rising": { | ||
eventOnEdge: digitalPinEventOnRisingEdge, | ||
quitChan: make(chan struct{}), | ||
simulateReturnValues: []int{0, 1, 0, 1, 1}, | ||
wantEdgeTypes: []string{DigitalPinEventRisingEdge, DigitalPinEventRisingEdge}, | ||
}, | ||
"no_edge_rising": { | ||
eventOnEdge: digitalPinEventOnRisingEdge, | ||
quitChan: make(chan struct{}), | ||
simulateReturnValues: []int{1, 0, 0}, | ||
wantEdgeTypes: nil, | ||
}, | ||
"edge_both": { | ||
eventOnEdge: digitalPinEventOnBothEdges, | ||
quitChan: make(chan struct{}), | ||
simulateReturnValues: []int{0, 1, 0, 1, 1}, | ||
wantEdgeTypes: []string{DigitalPinEventRisingEdge, DigitalPinEventFallingEdge, DigitalPinEventRisingEdge}, | ||
}, | ||
"no_edges_low": { | ||
eventOnEdge: digitalPinEventOnBothEdges, | ||
quitChan: make(chan struct{}), | ||
simulateReturnValues: []int{0, 0, 0}, | ||
wantEdgeTypes: nil, | ||
}, | ||
"no_edges_high": { | ||
eventOnEdge: digitalPinEventOnBothEdges, | ||
quitChan: make(chan struct{}), | ||
simulateReturnValues: []int{1, 1, 1}, | ||
wantEdgeTypes: nil, | ||
}, | ||
"error_no_eventhandler": { | ||
quitChan: make(chan struct{}), | ||
wantErr: "event handler is mandatory", | ||
}, | ||
"error_no_quitchannel": { | ||
wantErr: "quit channel is mandatory", | ||
}, | ||
"error_unsupported_edgetype_none": { | ||
eventOnEdge: digitalPinEventNone, | ||
quitChan: make(chan struct{}), | ||
wantErr: "unsupported edge type 0", | ||
}, | ||
} | ||
for name, tc := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
// arrange | ||
defer func() { | ||
if tc.quitChan != nil { | ||
close(tc.quitChan) | ||
} | ||
}() | ||
pinLabel := "test_pin" | ||
pollInterval := time.Microsecond // zero is possible, just to show usage | ||
// arrange reads | ||
numCallsRead := 0 | ||
wg := sync.WaitGroup{} | ||
if tc.simulateReturnValues != nil { | ||
wg.Add(1) | ||
} | ||
readFunc := func() (int, error) { | ||
numCallsRead++ | ||
retVal := tc.simulateReturnValues[numCallsRead-1] | ||
if numCallsRead >= len(tc.simulateReturnValues) { | ||
close(tc.quitChan) // ensure no further read call | ||
tc.quitChan = nil // lets skip defer routine | ||
wg.Done() // release assertions | ||
} | ||
return retVal, nil | ||
} | ||
// arrange event handler | ||
var edgeTypes []string | ||
eventHandler := func(offset int, t time.Duration, et string, sn uint32, lsn uint32) { | ||
edgeTypes = append(edgeTypes, et) | ||
} | ||
if tc.simulateNoEventHandler { | ||
eventHandler = nil | ||
} | ||
// act | ||
err := startEdgePolling(pinLabel, readFunc, pollInterval, tc.eventOnEdge, eventHandler, tc.quitChan) | ||
wg.Wait() | ||
// assert | ||
if tc.wantErr != "" { | ||
assert.Error(t, err, tc.wantErr) | ||
} | ||
assert.Equal(t, len(tc.simulateReturnValues), numCallsRead) | ||
assert.Equal(t, tc.wantEdgeTypes, edgeTypes) | ||
}) | ||
} | ||
} |
Oops, something went wrong.