P4wnP1_aloa/service/SubSysTriggerAction.go
2018-12-07 00:59:54 +01:00

836 lines
30 KiB
Go

// +build linux
package service
import (
"context"
"errors"
"fmt"
"github.com/mame82/P4wnP1_aloa/common"
"github.com/mame82/P4wnP1_aloa/common_web"
pb "github.com/mame82/P4wnP1_aloa/proto"
"github.com/mame82/P4wnP1_aloa/service/util"
"io/ioutil"
"sync"
)
var (
ErrTaNotFound = errors.New("Couldn't find given TriggerAction")
ErrTaImmutable = errors.New("Not allowed to change immutable TriggerAction")
)
type triggerType int
const (
triggerTypeServiceStarted triggerType = iota
triggerTypeUsbGadgetConnected
triggerTypeUsbGadgetDisconnected
triggerTypeWifiAPStarted
triggerTypeWifiConnectedAsSta
triggerTypeSshLogin
triggerTypeDhcpLeaseGranted
triggerTypeGroupReceive
triggerTypeGroupReceiveMulti
triggerTypeGpioIn
)
var triggerTypeString = map[triggerType]string {
triggerTypeServiceStarted: "TRIGGER_SERVICE_STARTED",
triggerTypeUsbGadgetConnected: "TRIGGER_USB_GADGET_CONNECTED",
triggerTypeUsbGadgetDisconnected: "TRIGGER_USB_GADGET_DISCONNECTED",
triggerTypeWifiAPStarted: "TRIGGER_WIFI_AP_STARTED",
triggerTypeWifiConnectedAsSta: "TRIGGER_WIFI_CONNECTED_AS_STA",
triggerTypeSshLogin: "TRIGGER_SSH_LOGIN",
triggerTypeDhcpLeaseGranted: "TRIGGER_DHCP_LEASE_GRANTED",
triggerTypeGroupReceive: "TRIGGER_GROUP_RECEIVE",
triggerTypeGroupReceiveMulti: "TRIGGER_GROUP_RECEIVE_MULTI",
triggerTypeGpioIn: "TRIGGER_GPIO_IN",
}
type actionType int
const (
actionTypeBashScript actionType = iota
actionTypeHidScript
actionTypeDeploySettingsTemplate
actionTypeLog
actionTypeGpioOut
actionTypeGroupSend
)
var actionTypeString = map[actionType]string {
actionTypeBashScript: "ACTION_BASH_SCRIPT",
actionTypeHidScript: "ACTION_HID_SCRIPT",
actionTypeDeploySettingsTemplate: "ACTION_DEPLOY_SETTINGS_TEMPLATE",
actionTypeLog: "ACTION_LOG",
actionTypeGpioOut: "ACTION_GPIO_OUT",
actionTypeGroupSend: "ACTION_GROUP_SEND",
}
func retrieveTriggerActionTypes(ta *pb.TriggerAction) (ttype triggerType, atype actionType) {
// Trigger
switch x := ta.Trigger.(type) {
case *pb.TriggerAction_ServiceStarted:
ttype = triggerTypeServiceStarted
case *pb.TriggerAction_UsbGadgetConnected:
ttype = triggerTypeUsbGadgetConnected
case *pb.TriggerAction_UsbGadgetDisconnected:
ttype = triggerTypeUsbGadgetDisconnected
case *pb.TriggerAction_WifiAPStarted:
ttype = triggerTypeWifiAPStarted
case *pb.TriggerAction_WifiConnectedAsSta:
ttype = triggerTypeWifiConnectedAsSta
case *pb.TriggerAction_SshLogin:
ttype = triggerTypeSshLogin
case *pb.TriggerAction_DhcpLeaseGranted:
ttype = triggerTypeDhcpLeaseGranted
case *pb.TriggerAction_GroupReceive:
ttype = triggerTypeGroupReceive
case *pb.TriggerAction_GroupReceiveMulti:
ttype = triggerTypeGroupReceiveMulti
case *pb.TriggerAction_GpioIn:
ttype = triggerTypeGpioIn
case nil:
default:
panic(fmt.Sprintf("unexpected trigger type %T", x))
}
// Action
switch x := ta.Action.(type) {
case *pb.TriggerAction_BashScript:
atype = actionTypeBashScript
case *pb.TriggerAction_HidScript:
atype = actionTypeHidScript
case *pb.TriggerAction_DeploySettingsTemplate:
atype = actionTypeDeploySettingsTemplate
case *pb.TriggerAction_Log:
atype = actionTypeLog
case *pb.TriggerAction_GpioOut:
atype = actionTypeGpioOut
case *pb.TriggerAction_GroupSend:
atype = actionTypeGroupSend
case nil:
default:
panic(fmt.Sprintf("unexpected action type %T", x))
}
return
}
type TriggerActionManager struct {
rootSvc *Service
evtRcv *EventReceiver
registeredTriggerActionMutex *sync.Mutex
//registeredTriggerAction []*pb.TriggerAction
registeredTriggerActions pb.TriggerActionSet
groupReceiveSequenceCheckers map[*pb.TriggerAction]*util.ValueSequenceChecker
groupReceiveSequenceCheckersMutex *sync.Mutex
nextID uint32
}
func (tam *TriggerActionManager) processing_loop() {
fmt.Println("TAM processing loop started")
Outer:
for {
select {
case evt := <- tam.evtRcv.EventQueue:
// avoid consuming empty messages, because channel is closed
if evt == nil {
break Outer // abort loop on "nil" event, as this indicates the EventQueue channel has been closed
}
//fmt.Println("TriggerActionManager received unfiltered event", evt)
tam.processTriggerEvent(evt)
// check if relevant and dispatch to triggers
case <- tam.evtRcv.Ctx.Done():
// evvent Receiver cancelled or unregistered
break Outer
}
}
fmt.Println("TAM processing loop finished")
}
// iterates over registered trigger actions
// if event matches a trigger, pass execution to respective on{Event} method, along with the arguments
// from the respective events
//
// Tasks of on{event} method:
// - decide if the given trigger fires, based on the event arguments
// - disable the TriggerAction, in case the trigger has fired
// - call the execute{Action} method, according to the Action defined in the trigger Action, in case the trigger fires
//
// Note: a event doesn't necessarily map to a trigger (f.e. a TRIGGER_EVT_TYPE_GROUP_RECEIVE carries a single value to a
// group, but a triggerTypeGroupReceive doesn't trigger if it is the wrong value)
//
func (tam *TriggerActionManager) processTriggerEvent(evt *pb.Event) {
//fmt.Printf("Remaining triggerActions: %+v\n", tam.registeredTriggerAction)
//fmt.Printf("TriggerActionManager Received event: %+v\n", evt)
tam.registeredTriggerActionMutex.Lock()
defer tam.registeredTriggerActionMutex.Unlock()
for _,ta := range tam.registeredTriggerActions.TriggerActions {
// skip disabled triggeractions
if !ta.IsActive { continue }
ttype,atype := retrieveTriggerActionTypes(ta)
if taTriggerTypeMatchesEvtTriggerType(ttype, evt) {
switch ttEvt := common_web.EvtTriggerType(evt.Values[0].GetTint64()); ttEvt {
case common_web.TRIGGER_EVT_TYPE_SERVICE_STARTED:
tam.onServiceStarted(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_SSH_LOGIN:
tam.onSSHLogin(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_WIFI_CONNECTED_AS_STA:
tam.onWifiConnectedAsSta(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_WIFI_AP_STARTED:
tam.onWifiApStarted(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_DHCP_LEASE_GRANTED:
// extract iface, mac and ip from event
tam.onDhcpLeaseGranted(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_USB_GADGET_CONNECTED:
tam.onUsbGadgetConnected(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_USB_GADGET_DISCONNECTED:
tam.onUsbGadgetDisconnected(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_GPIO_IN:
tam.onGpioIn(evt, ta, ttype, atype)
case common_web.TRIGGER_EVT_TYPE_GROUP_RECEIVE:
tam.onGroupReceive(evt, ta, ttype, atype)
default:
fmt.Println("unhandled trigger: ", ttEvt)
}
}
}
return
}
func (tam *TriggerActionManager) onServiceStarted(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
// always triggers
tam.executeAction(evt, ta, tt, at)
return nil
}
func (tam *TriggerActionManager) onSSHLogin(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
//ToDo: allow filtering by login user
// always triggers
tam.executeAction(evt, ta, tt, at)
return nil
}
func (tam *TriggerActionManager) onWifiConnectedAsSta(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
//ToDo: filter by AP name
// always triggers
tam.executeAction(evt, ta, tt, at)
return nil
}
func (tam *TriggerActionManager) onWifiApStarted(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
//ToDo: provide AP name with event and hand it over to the action
// always triggers
tam.executeAction(evt, ta, tt, at)
return nil
}
func (tam *TriggerActionManager) onDhcpLeaseGranted(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
//ToDo: filter by source interface, mac, IP
// always triggers
tam.executeAction(evt, ta, tt, at)
return nil
}
func (tam *TriggerActionManager) onUsbGadgetConnected(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
// always triggers
tam.executeAction(evt, ta, tt, at)
return nil
}
func (tam *TriggerActionManager) onUsbGadgetDisconnected(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
// always triggers
tam.executeAction(evt, ta, tt, at)
return nil
}
func (tam *TriggerActionManager) onGpioIn(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
evtGpioName, evtGpioLevel,err := DeconstructEventTriggerGpioIn(evt)
if err != nil { return err }
taGpioName := ta.Trigger.(*pb.TriggerAction_GpioIn).GpioIn.GpioName
if taGpioName != evtGpioName {
return nil // ignore
}
tam.executeAction(evt, ta, tt, at)
fmt.Printf("Gpio in trigger '%s' new state: %v\n", evtGpioName, evtGpioLevel)
return nil
}
func (tam *TriggerActionManager) onGroupReceive(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
evGroupName,evValue,err := DeconstructEventTriggerGroupReceive(evt)
if err != nil { return err }
switch tt {
case triggerTypeGroupReceive:
triggerVal := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.Value
triggerGroupName := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.GroupName
if evGroupName != triggerGroupName {
return nil // don't handle on group mismatch, but return without error
}
if evValue != triggerVal {
return nil // don't handle on value mismatch, but return without error
}
tam.executeAction(evt, ta, tt, at) // fire action
return nil
case triggerTypeGroupReceiveMulti:
// fmt.Println("### Processing GroupReceive event for trigger type GroupReceiveSequence")
triggerGroupName := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.GroupName
if evGroupName != triggerGroupName {
return nil
}
// retrieve the sequence checker
if sc,exists := tam.groupReceiveSequenceCheckers[ta]; exists {
if sc.Check(evValue) {
tam.executeAction(evt, ta, tt, at) // fire action
}
// fmt.Printf("GrpRcvSeq '%s' received '%d': %s\n", triggerGroupName, evValue, sc)
}
return nil // don't handle on group mismatch, but return without error
default:
return errors.New("Wrong trigger for onGroupReceive event")
}
return nil
}
func (tam *TriggerActionManager) executeAction(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType) error {
if ta.OneShot { ta.IsActive = false }
switch actionType := ta.Action.(type) {
case *pb.TriggerAction_BashScript:
go tam.executeActionBashScript(evt, ta, tt, at, actionType.BashScript)
case *pb.TriggerAction_HidScript:
go tam.executeActionStartHidScript(evt, ta, tt, at, actionType.HidScript)
case *pb.TriggerAction_Log:
go tam.executeActionLog(evt, ta, tt, at, actionType.Log)
case *pb.TriggerAction_DeploySettingsTemplate:
go tam.executeActionDeploySettingsTemplate(evt, ta, tt, at, actionType.DeploySettingsTemplate)
case *pb.TriggerAction_GroupSend:
tam.executeActionGroupSend(evt, ta, tt, at, actionType.GroupSend)
case *pb.TriggerAction_GpioOut:
tam.executeActionGPIOOut(evt, ta, tt, at, actionType.GpioOut)
}
return nil
}
func (tam *TriggerActionManager) executeActionDeploySettingsTemplate(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType, action *pb.ActionDeploySettingsTemplate) {
triggerName := triggerTypeString[tt]
actionName := actionTypeString[at]
templateTypeName := pb.ActionDeploySettingsTemplate_TemplateType_name[int32(action.Type)]
fmt.Printf("Trigger '%s' fired -> executing action '%s' (%s: '%s')\n", triggerName, actionName, templateTypeName, action.TemplateName)
switch action.Type {
case pb.ActionDeploySettingsTemplate_FULL_SETTINGS:
_,err := tam.rootSvc.SubSysRPC.DeployStoredMasterTemplate(context.Background(), &pb.StringMessage{Msg: action.TemplateName})
if err == nil {
fmt.Println("... stored settings deployed")
} else {
fmt.Println("... deploying stored settings failed: ", err.Error())
}
case pb.ActionDeploySettingsTemplate_NETWORK:
_,err := tam.rootSvc.SubSysRPC.DeployStoredEthernetInterfaceSettings(context.Background(), &pb.StringMessage{Msg: action.TemplateName})
if err == nil {
fmt.Println("... stored settings deployed")
} else {
fmt.Println("... deploying stored settings failed: ", err.Error())
}
case pb.ActionDeploySettingsTemplate_USB:
_,err := tam.rootSvc.SubSysRPC.DeployStoredUSBSettings(context.Background(), &pb.StringMessage{Msg: action.TemplateName})
if err == nil {
fmt.Println("... stored settings deployed")
} else {
fmt.Println("... deploying stored settings failed: ", err.Error())
}
case pb.ActionDeploySettingsTemplate_WIFI:
_,err := tam.rootSvc.SubSysRPC.DeployStoredWifiSettings(context.Background(), &pb.StringMessage{Msg: action.TemplateName})
if err == nil {
fmt.Println("... stored settings deployed")
} else {
fmt.Println("... deploying stored settings failed: ", err.Error())
}
case pb.ActionDeploySettingsTemplate_BLUETOOTH:
_,err := tam.rootSvc.SubSysRPC.DeployStoredBluetoothSettings(context.Background(), &pb.StringMessage{Msg: action.TemplateName})
if err == nil {
fmt.Println("... stored settings deployed")
} else {
fmt.Println("... deploying stored settings failed: ", err.Error())
}
case pb.ActionDeploySettingsTemplate_TRIGGER_ACTIONS:
_,err := tam.rootSvc.SubSysRPC.DeployStoredTriggerActionSetReplace(context.Background(), &pb.StringMessage{Msg: action.TemplateName})
if err == nil {
fmt.Println("... stored settings deployed")
} else {
fmt.Println("... deploying stored settings failed: ", err.Error())
}
}
}
func (tam *TriggerActionManager) executeActionGPIOOut(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType, action *pb.ActionGPIOOut) {
triggerName := triggerTypeString[tt]
actionName := actionTypeString[at]
gpioNumName := action.GpioName
fmt.Printf("Trigger '%s' fired -> executing action '%s' ('%s')\n", triggerName, actionName, gpioNumName)
tam.rootSvc.SubSysGpio.FireGpioAction(action)
}
func (tam *TriggerActionManager) executeActionGroupSend(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType, action *pb.ActionGroupSend) {
triggerName := triggerTypeString[tt]
actionName := actionTypeString[at]
groupName := action.GroupName
value := action.Value
fmt.Printf("Trigger '%s' fired -> executing action '%s' ('%s': %d)\n", triggerName, actionName, groupName, value)
tam.rootSvc.SubSysEvent.Emit(ConstructEventTriggerGroupReceive(groupName, value))
}
func (tam *TriggerActionManager) executeActionStartHidScript(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType, action *pb.ActionStartHIDScript) {
triggerName := triggerTypeString[tt]
actionName := actionTypeString[at]
fmt.Printf("Trigger '%s' fired -> executing action '%s' ('%s')\n", triggerName, actionName, action.ScriptName)
scriptPath := common.PATH_HID_SCRIPTS + "/" + action.ScriptName
preScript := fmt.Sprintf("var TRIGGER='%s';\n", triggerName)
switch tt {
case triggerTypeGpioIn:
gpioPinName := ta.Trigger.(*pb.TriggerAction_GpioIn).GpioIn.GpioName
preScript += fmt.Sprintf("var GPIO_PIN='%s';\n", gpioPinName)
_,level,_ := DeconstructEventTriggerGpioIn(evt)
if level {
preScript += fmt.Sprintf("var GPIO_LEVEL=true;\n")
} else {
preScript += fmt.Sprintf("var GPIO_LEVEL=false;\n")
}
case triggerTypeGroupReceiveMulti:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.GroupName
values := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.Values
rtype := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.Type
// create bash array of values
jsArray := "["
for idx,v := range values {
if idx >= len(values) - 1 {
jsArray += fmt.Sprintf("%d", v)
} else {
jsArray += fmt.Sprintf("%d, ", v)
}
}
jsArray += "]"
preScript += fmt.Sprintf("var GROUP='%s';\n", groupName)
preScript += fmt.Sprintf("var VALUES=%s;\n", jsArray)
preScript += fmt.Sprintf("var MULTI_TYPE='%s';\n", pb.GroupReceiveMultiType_name[int32(rtype)])
case triggerTypeGroupReceive:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.GroupName
value := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.Value
preScript += fmt.Sprintf("var GROUP='%s';\n", groupName)
preScript += fmt.Sprintf("var VALUE=%d;\n", value)
case triggerTypeDhcpLeaseGranted:
iface := evt.Values[1].GetTstring()
mac := evt.Values[2].GetTstring()
ip := evt.Values[3].GetTstring()
host := evt.Values[4].GetTstring()
preScript += fmt.Sprintf("var DHCP_LEASE_IFACE='%s';\n", iface)
preScript += fmt.Sprintf("var DHCP_LEASE_MAC='%s';\n", mac)
preScript += fmt.Sprintf("var DHCP_LEASE_IP='%s';\n", ip)
preScript += fmt.Sprintf("var DHCP_LEASE_HOST='%s';\n", host)
case triggerTypeSshLogin:
loginUser := evt.Values[1].GetTstring()
preScript += fmt.Sprintf("var SSH_LOGIN_USER='%s';\n", loginUser)
}
err := tam.rootSvc.SubSysUSB.HidScriptUsable()
if err != nil {
fmt.Printf("Couldn't start HIDScript: %v\n", err)
return
}
scriptFile, err := ioutil.ReadFile(scriptPath)
if err != nil {
fmt.Printf("Couldn't load HIDScript '%s': %v\n", scriptPath, err)
return
}
newScriptFile := preScript + string(scriptFile)
_,err = tam.rootSvc.SubSysUSB.HidScriptStartBackground(context.Background(), newScriptFile)
if err != nil {
fmt.Printf("Couldn't start HIDScript as background job'%s': %v\n", action.ScriptName, err)
return
}
return
}
func (tam *TriggerActionManager) executeActionBashScript(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType, action *pb.ActionStartBashScript) {
triggerName := triggerTypeString[tt]
actionName := actionTypeString[at]
scriptPath := common.PATH_BASH_SCRIPTS + "/" + action.ScriptName
env := []string{
fmt.Sprintf("TRIGGER=%s", triggerName),
}
switch tt {
case triggerTypeGpioIn:
gpioPinName := ta.Trigger.(*pb.TriggerAction_GpioIn).GpioIn.GpioName
env = append(env, fmt.Sprintf("GPIO_PIN='%s'", gpioPinName))
_,level,_ := DeconstructEventTriggerGpioIn(evt)
if level {
env = append(env, fmt.Sprintf("GPIO_LEVEL=HIGH"))
} else {
env = append(env, fmt.Sprintf("GPIO_LEVEL=LOW"))
}
case triggerTypeGroupReceiveMulti:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.GroupName
values := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.Values
rtype := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.Type
// create bash array of values
bashArray := "("
for _,v := range values { bashArray += fmt.Sprintf("%d ", v)}
bashArray += ")"
env = append(env,
fmt.Sprintf("GROUP=%s", groupName),
fmt.Sprintf("VALUES=%s", bashArray),
fmt.Sprintf("MULTI_TYPE=%s", pb.GroupReceiveMultiType_name[int32(rtype)]),
)
case triggerTypeGroupReceive:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.GroupName
value := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.Value
env = append(env,
fmt.Sprintf("GROUP=%s", groupName),
fmt.Sprintf("VALUE=%d", value),
)
case triggerTypeDhcpLeaseGranted:
iface := evt.Values[1].GetTstring()
mac := evt.Values[2].GetTstring()
ip := evt.Values[3].GetTstring()
host := evt.Values[4].GetTstring()
env = append(env,
fmt.Sprintf("DHCP_LEASE_IFACE=%s", iface),
fmt.Sprintf("DHCP_LEASE_MAC=%s", mac),
fmt.Sprintf("DHCP_LEASE_IP=%s", ip),
fmt.Sprintf("DHCP_LEASE_HOST=\"%s\"", host),
)
case triggerTypeSshLogin:
loginUser := evt.Values[1].GetTstring()
env = append(env,
fmt.Sprintf("SSH_LOGIN_USER=%s", loginUser),
)
}
fmt.Printf("Trigger '%s' fired -> executing action '%s' ('%s')\n", triggerName, actionName, scriptPath)
common.RunBashScriptEnv(scriptPath, env...)
}
func (tam *TriggerActionManager) executeActionLog(evt *pb.Event, ta *pb.TriggerAction, tt triggerType, at actionType, action *pb.ActionLog) {
triggerName := triggerTypeString[tt]
actionName := actionTypeString[at]
logMessage := fmt.Sprintf("Trigger fired: %s", triggerName)
switch tt {
case triggerTypeGpioIn:
gpioPinName := ta.Trigger.(*pb.TriggerAction_GpioIn).GpioIn.GpioName
_,level,_ := DeconstructEventTriggerGpioIn(evt)
logMessage += fmt.Sprintf(" (GPIO_PIN=%s GPIO_HIGH=%v)", gpioPinName, level)
case triggerTypeGroupReceiveMulti:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.GroupName
values := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.Values
logMessage += fmt.Sprintf(" (GROUP='%s', VALUES=%v)", groupName, values)
case triggerTypeGroupReceive:
groupName := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.GroupName
values := ta.Trigger.(*pb.TriggerAction_GroupReceive).GroupReceive.Value
typeName := pb.GroupReceiveMultiType_name[int32(ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti).GroupReceiveMulti.Type)]
logMessage += fmt.Sprintf(" (GROUP='%s', VALUES=%+v, VALUE='%d')", groupName, values, typeName)
case triggerTypeDhcpLeaseGranted:
iface := evt.Values[1].GetTstring()
mac := evt.Values[2].GetTstring()
ip := evt.Values[3].GetTstring()
host := evt.Values[4].GetTstring()
logMessage += fmt.Sprintf(" (DHCP_LEASE_IFACE=%s, DHCP_LEASE_MAC=%s, DHCP_LEASE_IP=%s, DHCP_LEASE_HOST='%s')", iface, mac, ip, host)
case triggerTypeSshLogin:
loginUser := evt.Values[1].GetTstring()
logMessage += fmt.Sprintf(" (SSH_LOGIN_USER=%s)", loginUser)
}
fmt.Printf("Trigger '%s' fired -> executing action '%s'\n", triggerName, actionName)
tam.rootSvc.SubSysEvent.Emit(ConstructEventLog("TriggerAction", LOG_LEVEL_INFORMATION, logMessage))
}
// checks if the triggerType of the given event (if trigger event at all), matches the TriggerType of the TriggerAction
func taTriggerTypeMatchesEvtTriggerType(ttype triggerType, evt *pb.Event) bool {
if evt.Type != common_web.EVT_TRIGGER { return false }
triggerTypeEvt := common_web.EvtTriggerType(evt.Values[0].GetTint64())
switch triggerTypeEvt {
case common_web.TRIGGER_EVT_TYPE_SERVICE_STARTED:
if ttype == triggerTypeServiceStarted {
return true
}
case common_web.TRIGGER_EVT_TYPE_DHCP_LEASE_GRANTED:
if ttype == triggerTypeDhcpLeaseGranted {
return true
}
case common_web.TRIGGER_EVT_TYPE_WIFI_AP_STARTED:
if ttype == triggerTypeWifiAPStarted {
return true
}
case common_web.TRIGGER_EVT_TYPE_WIFI_CONNECTED_AS_STA:
if ttype == triggerTypeWifiConnectedAsSta {
return true
}
case common_web.TRIGGER_EVT_TYPE_USB_GADGET_CONNECTED:
if ttype == triggerTypeUsbGadgetConnected {
return true
}
case common_web.TRIGGER_EVT_TYPE_USB_GADGET_DISCONNECTED:
if ttype == triggerTypeUsbGadgetDisconnected {
return true
}
case common_web.TRIGGER_EVT_TYPE_SSH_LOGIN:
if ttype == triggerTypeSshLogin {
return true
}
case common_web.TRIGGER_EVT_TYPE_GROUP_RECEIVE:
if ttype == triggerTypeGroupReceive || ttype == triggerTypeGroupReceiveMulti {
return true
}
case common_web.TRIGGER_EVT_TYPE_GPIO_IN:
if ttype == triggerTypeGpioIn {
return true
}
default:
return false
}
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) {
tam.registeredTriggerActionMutex.Lock()
defer tam.registeredTriggerActionMutex.Unlock()
ta.Id = tam.nextID
tam.nextID++
tam.registeredTriggerActions.TriggerActions = append(tam.registeredTriggerActions.TriggerActions, ta)
taAdded = ta
//if new ta trigger is GroupReceiveSequence, add a SequenceChecker
if triggerGrpRcv,match := ta.Trigger.(*pb.TriggerAction_GroupReceiveMulti); match {
tam.groupReceiveSequenceCheckersMutex.Lock()
//fmt.Printf("##### New val checker %+v\n", triggerGrpRcv.GroupReceiveSequence.Values)
switch triggerGrpRcv.GroupReceiveMulti.Type {
case pb.GroupReceiveMultiType_AND:
tam.groupReceiveSequenceCheckers[ta] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_AND)
case pb.GroupReceiveMultiType_OR:
tam.groupReceiveSequenceCheckers[ta] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_OR)
case pb.GroupReceiveMultiType_SEQUENCE:
tam.groupReceiveSequenceCheckers[ta] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_SEQUENCE)
case pb.GroupReceiveMultiType_EXACT_SEQUENCE:
tam.groupReceiveSequenceCheckers[ta] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_EXACT_SEQUENCE)
}
tam.groupReceiveSequenceCheckersMutex.Unlock()
}
//if trigger is GpioIn, configure GPIO
if triggerGpioIn,match := ta.Trigger.(*pb.TriggerAction_GpioIn); match {
tam.rootSvc.SubSysGpio.DeployGpioTrigger(triggerGpioIn.GpioIn)
}
return taAdded,nil
}
func (tam *TriggerActionManager) UpdateTriggerAction(srcTa *pb.TriggerAction, addIfMissing bool) (err error) {
tam.registeredTriggerActionMutex.Lock()
defer tam.registeredTriggerActionMutex.Unlock()
if targetTA,err := tam.GetTriggerActionByID(srcTa.Id); err != nil {
if addIfMissing {
_,err = tam.AddTriggerAction(srcTa)
return err
} else {
return ErrTaNotFound
}
} else {
if targetTA.Immutable { return ErrTaImmutable }
//if target ta trigger had a sequenceChecker assigned, remove it
if _,match := targetTA.Trigger.(*pb.TriggerAction_GroupReceiveMulti); match {
tam.groupReceiveSequenceCheckersMutex.Lock()
delete(tam.groupReceiveSequenceCheckers, targetTA)
tam.groupReceiveSequenceCheckersMutex.Unlock()
}
targetTA.OneShot = srcTa.OneShot
targetTA.IsActive = srcTa.IsActive
targetTA.Immutable = srcTa.Immutable
targetTA.Action = srcTa.Action
targetTA.Trigger = srcTa.Trigger
//if new ta trigger is GroupReceiveSequence, add a SequenceChecker
if triggerGrpRcv,match := targetTA.Trigger.(*pb.TriggerAction_GroupReceiveMulti); match {
tam.groupReceiveSequenceCheckersMutex.Lock()
switch triggerGrpRcv.GroupReceiveMulti.Type {
case pb.GroupReceiveMultiType_AND:
tam.groupReceiveSequenceCheckers[targetTA] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_AND)
case pb.GroupReceiveMultiType_OR:
tam.groupReceiveSequenceCheckers[targetTA] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_OR)
case pb.GroupReceiveMultiType_SEQUENCE:
tam.groupReceiveSequenceCheckers[targetTA] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_SEQUENCE)
case pb.GroupReceiveMultiType_EXACT_SEQUENCE:
tam.groupReceiveSequenceCheckers[targetTA] = util.NewValueSequenceChecker(triggerGrpRcv.GroupReceiveMulti.Values, util.ValueSeqType_EXACT_SEQUENCE)
}
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
}
}
func (tam *TriggerActionManager) ClearTriggerActions(keepImmutable bool) (err error) {
tam.registeredTriggerActionMutex.Lock()
defer tam.registeredTriggerActionMutex.Unlock()
if !keepImmutable {
tam.registeredTriggerActions.TriggerActions = []*pb.TriggerAction{}
return
}
newTas := []*pb.TriggerAction{}
for _,ta := range tam.registeredTriggerActions.TriggerActions {
if ta.Immutable {
newTas = append(newTas, ta)
}
}
tam.registeredTriggerActions.TriggerActions = newTas
tam.groupReceiveSequenceCheckersMutex.Lock()
defer tam.groupReceiveSequenceCheckersMutex.Unlock()
tam.groupReceiveSequenceCheckers = make(map[*pb.TriggerAction]*util.ValueSequenceChecker)
return nil
}
func (tam *TriggerActionManager) GetCurrentTriggerActionSet() (ta *pb.TriggerActionSet) {
/*
tam.registeredTriggerActionMutex.Lock()
resTAs := make([]*pb.TriggerAction, len(tam.registeredTriggerActions.TriggerActions))
copy(resTAs, tam.registeredTriggerActions.TriggerActions)
tam.registeredTriggerActionMutex.Unlock()
return &pb.TriggerActionSet{ TriggerActions: resTAs }
*/
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()
}
func (tam *TriggerActionManager) Stop() {
tam.rootSvc.SubSysEvent.UnregisterReceiver(tam.evtRcv) // should end the processing loop, as the context of the event receiver is closed
}
func NewTriggerActionManager(rootService *Service) (tam *TriggerActionManager) {
tam = &TriggerActionManager{
registeredTriggerActions: pb.TriggerActionSet{
Name: "DeployedTriggerActions",
TriggerActions: []*pb.TriggerAction{},
},
registeredTriggerActionMutex: &sync.Mutex{},
rootSvc: rootService,
groupReceiveSequenceCheckers: make(map[*pb.TriggerAction]*util.ValueSequenceChecker),
groupReceiveSequenceCheckersMutex: &sync.Mutex{},
}
return tam
}