mirror of
https://github.com/RoganDawes/P4wnP1_aloa.git
synced 2025-11-15 08:32:06 +01:00
GPIO,service,webclient: debounce GPIO,update SINGLE TriggerAction, WebClient allows loading bluetooth and master settings via TriggerAction
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
// +build linux
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/mame82/P4wnP1_go/service/pgpio"
|
||||
"periph.io/x/periph"
|
||||
"periph.io/x/periph/conn/gpio"
|
||||
"periph.io/x/periph/conn/gpio/gpioreg"
|
||||
@@ -10,28 +13,25 @@ import (
|
||||
"periph.io/x/periph/host"
|
||||
"periph.io/x/periph/host/rpi"
|
||||
"sync"
|
||||
|
||||
"time"
|
||||
"errors"
|
||||
pb "github.com/mame82/P4wnP1_go/proto"
|
||||
)
|
||||
|
||||
/*
|
||||
ToDo: Entprellen + StopAllEdgeDetecting (when new TriggerActionSet is deployed)
|
||||
ToDo: If a single item of the trigger action set changes, all GPIOTriggers have to be re-enumearted, to assure that there's no edge detection running for a trigger which doesn't exist anymore
|
||||
*/
|
||||
|
||||
var (
|
||||
EGpioNotAvailable = errors.New("sub system GPIO not available")
|
||||
EGpioPinInvalid = errors.New("invalid GPIO pin")
|
||||
)
|
||||
|
||||
type GpioManager struct {
|
||||
availableGpioPins []gpio.PinIO
|
||||
availableGpioPins []*pgpio.P4wnp1PinIO
|
||||
availableGpioPinsMap map[string]*pgpio.P4wnp1PinIO
|
||||
availableGpioNames []string
|
||||
|
||||
rootSvc *Service
|
||||
|
||||
edgeDetectingMutex *sync.Mutex
|
||||
edgeDetecting map[gpio.PinIO]bool
|
||||
edgeDetecting map[gpio.PinIO]bool
|
||||
|
||||
IsUsable bool
|
||||
|
||||
@@ -45,12 +45,11 @@ func (gm *GpioManager) Start() {
|
||||
func (gm *GpioManager) Stop() {
|
||||
}
|
||||
|
||||
|
||||
func NewGpioManager(rootSvc *Service) (res *GpioManager) {
|
||||
gm := &GpioManager{
|
||||
rootSvc: rootSvc,
|
||||
}
|
||||
state,err := host.Init()
|
||||
state, err := host.Init()
|
||||
if err != nil {
|
||||
gm.IsUsable = false
|
||||
return
|
||||
@@ -59,39 +58,107 @@ func NewGpioManager(rootSvc *Service) (res *GpioManager) {
|
||||
gm.State = state
|
||||
gm.IsUsable = rpi.Present()
|
||||
|
||||
gm.availableGpioPinsMap = make(map[string]*pgpio.P4wnp1PinIO)
|
||||
gpios := gpioreg.All()
|
||||
for _,g := range gpios {
|
||||
for _, g := range gpios {
|
||||
if pinreg.IsConnected(g) {
|
||||
gm.availableGpioPins = append(gm.availableGpioPins,g)
|
||||
ppin := pgpio.NewP4wnp1PinIO(g)
|
||||
gm.availableGpioPins = append(gm.availableGpioPins, ppin)
|
||||
gm.availableGpioPinsMap[g.Name()] = ppin
|
||||
gm.availableGpioNames = append(gm.availableGpioNames, g.Name())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
gm.edgeDetecting = make(map[gpio.PinIO]bool)
|
||||
gm.edgeDetectingMutex = &sync.Mutex{}
|
||||
|
||||
return gm
|
||||
}
|
||||
|
||||
func (gm GpioManager) GetAvailableGpios() (res []gpio.PinIO, err error) {
|
||||
func (gm *GpioManager) GetAvailableGpios() (res []*pgpio.P4wnp1PinIO, err error) {
|
||||
if gm.IsUsable {
|
||||
return gm.availableGpioPins,nil
|
||||
return gm.availableGpioPins, nil
|
||||
}
|
||||
return res,EGpioNotAvailable
|
||||
return res, EGpioNotAvailable
|
||||
}
|
||||
|
||||
func (gm GpioManager) GetAvailableGpioNames() (res []string, err error) {
|
||||
func (gm *GpioManager) GetAvailableGpioNames() (res []string, err error) {
|
||||
if gm.IsUsable {
|
||||
return gm.availableGpioNames,nil
|
||||
return gm.availableGpioNames, nil
|
||||
}
|
||||
return res,EGpioNotAvailable
|
||||
return res, EGpioNotAvailable
|
||||
}
|
||||
|
||||
|
||||
|
||||
func (gm *GpioManager) DeployGpioTrigger(in *pb.TriggerGPIOIn) (err error) {
|
||||
if !gm.IsUsable {
|
||||
return EGpioNotAvailable
|
||||
}
|
||||
|
||||
p,present := gm.availableGpioPinsMap[in.GpioName]
|
||||
if !present {
|
||||
return EGpioPinInvalid
|
||||
}
|
||||
|
||||
fmt.Printf("Deploying trigger for GPIO: %+v\n", p)
|
||||
|
||||
pull := gpio.Float
|
||||
switch in.PullUpDown {
|
||||
case pb.GPIOInPullUpDown_DOWN:
|
||||
pull = gpio.PullDown
|
||||
case pb.GPIOInPullUpDown_UP:
|
||||
pull = gpio.PullUp
|
||||
}
|
||||
|
||||
edge := gpio.BothEdges
|
||||
switch in.GpioInEdge {
|
||||
case pb.GPIOInEdge_FALLING:
|
||||
edge = gpio.FallingEdge
|
||||
case pb.GPIOInEdge_RISING:
|
||||
edge = gpio.RisingEdge
|
||||
}
|
||||
|
||||
p.In(pull, edge)
|
||||
|
||||
debounceDelay := time.Duration(in.DebounceMillis) * time.Millisecond
|
||||
//ToDo: remove next line, as this is a fixed test delay of 100 ms
|
||||
debounceDelay = 100 * time.Millisecond
|
||||
|
||||
go func() {
|
||||
fmt.Println("Starting edge detection for pin " + p.Name())
|
||||
detectErr := error(nil)
|
||||
for detectErr == nil {
|
||||
var detectedLevel gpio.Level
|
||||
detectedLevel,detectErr = p.ExtWaitForEdge(context.Background(), debounceDelay)
|
||||
|
||||
fmt.Printf("... done wait for edge %s level: %v\n", p.Name(), detectedLevel)
|
||||
|
||||
//Edge detected, check if still edge detecting before consuming
|
||||
|
||||
switch detectedLevel {
|
||||
case gpio.High:
|
||||
fmt.Println("Gpio " + p.Name() + " changed to high")
|
||||
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.High)))
|
||||
|
||||
case gpio.Low:
|
||||
fmt.Println("Gpio " + p.Name() + " changed to low")
|
||||
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.Low)))
|
||||
}
|
||||
}
|
||||
fmt.Println("!!!! STOPPED edge loop for pin " + p.Name())
|
||||
}()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
/*
|
||||
func (gm *GpioManager) DeployGpioTriggerOld(in *pb.TriggerGPIOIn) (err error) {
|
||||
if !gm.IsUsable {
|
||||
return EGpioNotAvailable
|
||||
}
|
||||
|
||||
p := gpioreg.ByName(in.GpioName)
|
||||
if p == nil {
|
||||
return EGpioPinInvalid
|
||||
@@ -124,19 +191,46 @@ func (gm *GpioManager) DeployGpioTrigger(in *pb.TriggerGPIOIn) (err error) {
|
||||
gm.edgeDetecting[p] = true
|
||||
gm.edgeDetectingMutex.Unlock()
|
||||
|
||||
debounceDelay := time.Duration(in.DebounceMillis) * time.Millisecond
|
||||
//ToDo: remove next line, as this is a fixed test delay of 100 ms
|
||||
debounceDelay = 100 *time.Millisecond
|
||||
|
||||
go func() {
|
||||
fmt.Println("Starting edge detection for pin " + p.Name())
|
||||
lastHit := time.Now()
|
||||
for gm.isEdgeDetecting(p) {
|
||||
p.WaitForEdge(-1)
|
||||
fmt.Println("Wait for edge " + p.Name() + " ...")
|
||||
waitSuccess := p.WaitForEdge(-1)
|
||||
fmt.Printf("... done wait for edge %s waitSucces: %v\n", p.Name(), waitSuccess)
|
||||
if !waitSuccess {
|
||||
break
|
||||
}
|
||||
now := time.Now()
|
||||
bounce := true
|
||||
sinceLastHit := now.Sub(lastHit)
|
||||
if sinceLastHit > debounceDelay {
|
||||
bounce = false
|
||||
// could do `lastHit = now` if timer only should be reset for "no bounce"
|
||||
}
|
||||
lastHit = now
|
||||
|
||||
//Edge detected, check if still edge detecting before consuming
|
||||
if gm.isEdgeDetecting(p) {
|
||||
switch p.Read() {
|
||||
case gpio.High:
|
||||
fmt.Println("Gpio " + p.Name() + " changed to high")
|
||||
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.High)))
|
||||
if !bounce {
|
||||
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.High)))
|
||||
} else {
|
||||
fmt.Println("... ignored, as in debounceDelay")
|
||||
}
|
||||
case gpio.Low:
|
||||
fmt.Println("Gpio " + p.Name() + " changed to low")
|
||||
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.Low)))
|
||||
if !bounce {
|
||||
gm.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGpioIn(p.Name(), bool(gpio.Low)))
|
||||
} else {
|
||||
fmt.Println("... ignored, as in debounceDelay")
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -144,6 +238,7 @@ func (gm *GpioManager) DeployGpioTrigger(in *pb.TriggerGPIOIn) (err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Println("STOPPED edge detection for pin " + p.Name())
|
||||
gm.edgeDetectingMutex.Lock()
|
||||
delete(gm.edgeDetecting, p)
|
||||
gm.edgeDetectingMutex.Unlock()
|
||||
@@ -152,23 +247,19 @@ func (gm *GpioManager) DeployGpioTrigger(in *pb.TriggerGPIOIn) (err error) {
|
||||
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
func (gm *GpioManager) FireGpioAction(out *pb.ActionGPIOOut) (err error) {
|
||||
fmt.Println("FireGPIOAction for", out.GpioName)
|
||||
if !gm.IsUsable {
|
||||
return EGpioNotAvailable
|
||||
}
|
||||
|
||||
p := gpioreg.ByName(out.GpioName)
|
||||
if p == nil {
|
||||
p,present := gm.availableGpioPinsMap[out.GpioName]
|
||||
if !present {
|
||||
return EGpioPinInvalid
|
||||
}
|
||||
|
||||
// If edge detection is already running, stop it
|
||||
if gm.isEdgeDetecting(p) {
|
||||
gm.stopEdgeDetectionForPin(p)
|
||||
}
|
||||
|
||||
|
||||
level := gpio.Low
|
||||
switch out.Value {
|
||||
case pb.GPIOOutValue_HIGH:
|
||||
@@ -179,15 +270,28 @@ func (gm *GpioManager) FireGpioAction(out *pb.ActionGPIOOut) (err error) {
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("Setting %s to out level %v...\n", p.Name(), level)
|
||||
|
||||
p.Out(level)
|
||||
fmt.Println("..setting level done")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gm *GpioManager) ResetPins() {
|
||||
fmt.Println("Resetting all pins")
|
||||
for _, pin := range gm.availableGpioPins {
|
||||
if pin.Edge() != gpio.NoEdge {
|
||||
pin.In(gpio.Float, gpio.NoEdge)
|
||||
}
|
||||
fmt.Println("... halting pin " + pin.Name())
|
||||
pin.Halt()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
func (gm *GpioManager) isEdgeDetecting(p gpio.PinIO) bool {
|
||||
fmt.Println("Check edge detection for " + p.Name())
|
||||
gm.edgeDetectingMutex.Lock()
|
||||
defer gm.edgeDetectingMutex.Unlock()
|
||||
if _,exists := gm.edgeDetecting[p]; exists && gm.edgeDetecting[p] {
|
||||
@@ -197,20 +301,39 @@ func (gm *GpioManager) isEdgeDetecting(p gpio.PinIO) bool {
|
||||
fmt.Println("Edge detection for " + p.Name() + " not running")
|
||||
return false
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
func (gm *GpioManager) stopEdgeDetectionForPin(p gpio.PinIO) {
|
||||
fmt.Println("Stopping edge detection for pin " + p.Name())
|
||||
gm.edgeDetectingMutex.Lock()
|
||||
if _,exists := gm.edgeDetecting[p]; exists {
|
||||
gm.edgeDetecting[p] = false
|
||||
}
|
||||
gm.edgeDetectingMutex.Unlock()
|
||||
|
||||
|
||||
//p.Halt()
|
||||
p.In(gpio.Float, gpio.BothEdges)
|
||||
|
||||
isNotDeleted := func() bool {
|
||||
gm.edgeDetectingMutex.Lock()
|
||||
defer gm.edgeDetectingMutex.Unlock()
|
||||
_,exists := gm.edgeDetecting[p]
|
||||
return exists
|
||||
}
|
||||
|
||||
|
||||
//write high/low till gpio is deleted from map, to assure pending edge detection ended
|
||||
for gm.isEdgeDetecting(p) {
|
||||
p.Out(gpio.High)
|
||||
if gm.isEdgeDetecting(p) {
|
||||
p.Out(gpio.Low)
|
||||
for isNotDeleted() {
|
||||
fmt.Println("... stopping " + p.Name() + " setting PullDown to trigger final edge")
|
||||
p.In(gpio.PullDown, gpio.BothEdges)
|
||||
if isNotDeleted() {
|
||||
fmt.Println("... stopping " + p.Name() + " setting PullUp to trigger final edge")
|
||||
p.In(gpio.PullUp, gpio.BothEdges)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
p.In(gpio.Float, gpio.NoEdge)
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -628,6 +628,40 @@ func taTriggerTypeMatchesEvtTriggerType(ttype triggerType, evt *pb.Event) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// removes the given TriggerAction
|
||||
// ToDo: for now only the ID is compared, to assure we don't remove a TriggerAction which has been changed meanwhile, we should deep-compare the whole object
|
||||
func (tam *TriggerActionManager) RemoveTriggerAction(removeTa *pb.TriggerAction) (taRemoved *pb.TriggerAction, err error) {
|
||||
tam.registeredTriggerActionMutex.Lock()
|
||||
defer tam.registeredTriggerActionMutex.Unlock()
|
||||
|
||||
|
||||
for idx,ta := range tam.registeredTriggerActions.TriggerActions {
|
||||
if ta.Id == removeTa.Id {
|
||||
// remove element (not a problem for running `for`-loop, as it is interrupted here)
|
||||
tam.registeredTriggerActions.TriggerActions = append(tam.registeredTriggerActions.TriggerActions[:idx], tam.registeredTriggerActions.TriggerActions[idx+1:]...)
|
||||
|
||||
//if target ta trigger had a sequenceChecker assigned, remove it
|
||||
if _,match := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti); match {
|
||||
tam.groupReceiveSequenceCheckersMutex.Lock()
|
||||
delete(tam.groupReceiveSequenceCheckers, ta)
|
||||
tam.groupReceiveSequenceCheckersMutex.Unlock()
|
||||
}
|
||||
|
||||
return ta, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrTaNotFound
|
||||
|
||||
}
|
||||
|
||||
func (tam *TriggerActionManager) GetTriggerActionByID(Id uint32) (ta *pb.TriggerAction ,err error) {
|
||||
for _,ta = range tam.registeredTriggerActions.TriggerActions {
|
||||
if ta.Id == Id {
|
||||
return ta, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrTaNotFound
|
||||
}
|
||||
|
||||
// returns the TriggerAction with assigned ID
|
||||
func (tam *TriggerActionManager) AddTriggerAction(ta *pb.TriggerAction) (taAdded *pb.TriggerAction, err error) {
|
||||
@@ -664,40 +698,6 @@ func (tam *TriggerActionManager) AddTriggerAction(ta *pb.TriggerAction) (taAdded
|
||||
return taAdded,nil
|
||||
}
|
||||
|
||||
// removes the given TriggerAction
|
||||
// ToDo: for now only the ID is compared, to assure we don't remove a TriggerAction which has been changed meanwhile, we should deep-compare the whole object
|
||||
func (tam *TriggerActionManager) RemoveTriggerAction(removeTa *pb.TriggerAction) (taRemoved *pb.TriggerAction, err error) {
|
||||
tam.registeredTriggerActionMutex.Lock()
|
||||
defer tam.registeredTriggerActionMutex.Unlock()
|
||||
|
||||
|
||||
for idx,ta := range tam.registeredTriggerActions.TriggerActions {
|
||||
if ta.Id == removeTa.Id {
|
||||
// remove element (not a problem for running `for`-loop, as it is interrupted here)
|
||||
tam.registeredTriggerActions.TriggerActions = append(tam.registeredTriggerActions.TriggerActions[:idx], tam.registeredTriggerActions.TriggerActions[idx+1:]...)
|
||||
|
||||
//if target ta trigger had a sequenceChecker assigned, remove it
|
||||
if _,match := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti); match {
|
||||
tam.groupReceiveSequenceCheckersMutex.Lock()
|
||||
delete(tam.groupReceiveSequenceCheckers, ta)
|
||||
tam.groupReceiveSequenceCheckersMutex.Unlock()
|
||||
}
|
||||
|
||||
return ta, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrTaNotFound
|
||||
|
||||
}
|
||||
|
||||
func (tam *TriggerActionManager) GetTriggerActionByID(Id uint32) (ta *pb.TriggerAction ,err error) {
|
||||
for _,ta = range tam.registeredTriggerActions.TriggerActions {
|
||||
if ta.Id == Id {
|
||||
return ta, nil
|
||||
}
|
||||
}
|
||||
return nil, ErrTaNotFound
|
||||
}
|
||||
|
||||
func (tam *TriggerActionManager) UpdateTriggerAction(srcTa *pb.TriggerAction, addIfMissing bool) (err error) {
|
||||
tam.registeredTriggerActionMutex.Lock()
|
||||
@@ -742,6 +742,12 @@ func (tam *TriggerActionManager) UpdateTriggerAction(srcTa *pb.TriggerAction, ad
|
||||
}
|
||||
tam.groupReceiveSequenceCheckersMutex.Unlock()
|
||||
}
|
||||
|
||||
//if trigger is GpioIn, configure GPIO
|
||||
if triggerGpioIn,match := targetTA.Trigger.(*pb.TriggerAction_GpioIn); match {
|
||||
tam.rootSvc.SubSysGpio.DeployGpioTrigger(triggerGpioIn.GpioIn)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -785,6 +791,19 @@ func (tam *TriggerActionManager) GetCurrentTriggerActionSet() (ta *pb.TriggerAct
|
||||
return &tam.registeredTriggerActions
|
||||
}
|
||||
|
||||
func (tam *TriggerActionManager) redeployGpioForAllTas() {
|
||||
tam.registeredTriggerActionMutex.Lock()
|
||||
defer tam.registeredTriggerActionMutex.Unlock()
|
||||
tam.rootSvc.SubSysGpio.ResetPins()
|
||||
for _,ta := range tam.registeredTriggerActions.TriggerActions {
|
||||
ttype, _ := retrieveTriggerActionTypes(ta)
|
||||
if ttype == triggerTypeGpioIn {
|
||||
gpioIn := ta.Trigger.(*pb.TriggerAction_GpioIn).GpioIn
|
||||
tam.rootSvc.SubSysGpio.DeployGpioTrigger(gpioIn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (tam *TriggerActionManager) Start() {
|
||||
tam.evtRcv = tam.rootSvc.SubSysEvent.RegisterReceiver(common_web.EVT_TRIGGER)
|
||||
go tam.processing_loop()
|
||||
|
||||
@@ -55,11 +55,15 @@ func (s *server) DeployTriggerActionSetUpdate(ctx context.Context, updateTas *pb
|
||||
defer s.rootSvc.SubSysEvent.Emit(ConstructEventNotifyStateChange(common_web.STATE_CHANGE_EVT_TYPE_TRIGGER_ACTIONS))
|
||||
for _, updateTa := range updateTas.TriggerActions {
|
||||
// try to find the trigger action to update by ID
|
||||
fmt.Printf("Updating TriggerAction %d ...\n", updateTa.Id)
|
||||
if s.rootSvc.SubSysTriggerActions.UpdateTriggerAction(updateTa, false) != nil {
|
||||
fmt.Printf("Updating TriggerAction %d failed: %v\n", updateTa.Id, err)
|
||||
// coudln't find the given action, return with error
|
||||
return &s.rootSvc.SubSysTriggerActions.registeredTriggerActions,errors.New(fmt.Sprintf("Couldn't find trigger action with id %d", updateTa.Id))
|
||||
}
|
||||
fmt.Printf("Updating TriggerAction %d succeeded\n", updateTa.Id)
|
||||
}
|
||||
|
||||
resultingTas = &s.rootSvc.SubSysTriggerActions.registeredTriggerActions
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user