WebClient,service,gRPC: More work on TriggerActions

This commit is contained in:
MaMe82 2018-10-01 17:28:24 +02:00
parent 6842c25117
commit 2ee2672171
12 changed files with 958 additions and 380 deletions

View File

@ -8,7 +8,8 @@
grpc.proto
It has these top-level messages:
TriggerActionSettings
TriggerActionSet
TriggerActionSetRequestStorage
TriggerAction
TriggerServiceStarted
TriggerUSBGadgetConnected
@ -344,25 +345,25 @@ func (x EthernetInterfaceSettings_Mode) String() string {
}
// Triggers, Actions and resulting TriggerActions
type TriggerActionSettings struct {
RegisteredTriggerActions []*TriggerAction
type TriggerActionSet struct {
TriggerActions []*TriggerAction
}
// GetRegisteredTriggerActions gets the RegisteredTriggerActions of the TriggerActionSettings.
func (m *TriggerActionSettings) GetRegisteredTriggerActions() (x []*TriggerAction) {
// GetTriggerActions gets the TriggerActions of the TriggerActionSet.
func (m *TriggerActionSet) GetTriggerActions() (x []*TriggerAction) {
if m == nil {
return x
}
return m.RegisteredTriggerActions
return m.TriggerActions
}
// MarshalToWriter marshals TriggerActionSettings to the provided writer.
func (m *TriggerActionSettings) MarshalToWriter(writer jspb.Writer) {
// MarshalToWriter marshals TriggerActionSet to the provided writer.
func (m *TriggerActionSet) MarshalToWriter(writer jspb.Writer) {
if m == nil {
return
}
for _, msg := range m.RegisteredTriggerActions {
for _, msg := range m.TriggerActions {
writer.WriteMessage(1, func() {
msg.MarshalToWriter(writer)
})
@ -371,24 +372,24 @@ func (m *TriggerActionSettings) MarshalToWriter(writer jspb.Writer) {
return
}
// Marshal marshals TriggerActionSettings to a slice of bytes.
func (m *TriggerActionSettings) Marshal() []byte {
// Marshal marshals TriggerActionSet to a slice of bytes.
func (m *TriggerActionSet) Marshal() []byte {
writer := jspb.NewWriter()
m.MarshalToWriter(writer)
return writer.GetResult()
}
// UnmarshalFromReader unmarshals a TriggerActionSettings from the provided reader.
func (m *TriggerActionSettings) UnmarshalFromReader(reader jspb.Reader) *TriggerActionSettings {
// UnmarshalFromReader unmarshals a TriggerActionSet from the provided reader.
func (m *TriggerActionSet) UnmarshalFromReader(reader jspb.Reader) *TriggerActionSet {
for reader.Next() {
if m == nil {
m = &TriggerActionSettings{}
m = &TriggerActionSet{}
}
switch reader.GetFieldNumber() {
case 1:
reader.ReadMessage(func() {
m.RegisteredTriggerActions = append(m.RegisteredTriggerActions, new(TriggerAction).UnmarshalFromReader(reader))
m.TriggerActions = append(m.TriggerActions, new(TriggerAction).UnmarshalFromReader(reader))
})
default:
reader.SkipField()
@ -398,8 +399,90 @@ func (m *TriggerActionSettings) UnmarshalFromReader(reader jspb.Reader) *Trigger
return m
}
// Unmarshal unmarshals a TriggerActionSettings from a slice of bytes.
func (m *TriggerActionSettings) Unmarshal(rawBytes []byte) (*TriggerActionSettings, error) {
// Unmarshal unmarshals a TriggerActionSet from a slice of bytes.
func (m *TriggerActionSet) Unmarshal(rawBytes []byte) (*TriggerActionSet, error) {
reader := jspb.NewReader(rawBytes)
m = m.UnmarshalFromReader(reader)
if err := reader.Err(); err != nil {
return nil, err
}
return m, nil
}
type TriggerActionSetRequestStorage struct {
TemplateName string
Set *TriggerActionSet
}
// GetTemplateName gets the TemplateName of the TriggerActionSetRequestStorage.
func (m *TriggerActionSetRequestStorage) GetTemplateName() (x string) {
if m == nil {
return x
}
return m.TemplateName
}
// GetSet gets the Set of the TriggerActionSetRequestStorage.
func (m *TriggerActionSetRequestStorage) GetSet() (x *TriggerActionSet) {
if m == nil {
return x
}
return m.Set
}
// MarshalToWriter marshals TriggerActionSetRequestStorage to the provided writer.
func (m *TriggerActionSetRequestStorage) MarshalToWriter(writer jspb.Writer) {
if m == nil {
return
}
if len(m.TemplateName) > 0 {
writer.WriteString(1, m.TemplateName)
}
if m.Set != nil {
writer.WriteMessage(2, func() {
m.Set.MarshalToWriter(writer)
})
}
return
}
// Marshal marshals TriggerActionSetRequestStorage to a slice of bytes.
func (m *TriggerActionSetRequestStorage) Marshal() []byte {
writer := jspb.NewWriter()
m.MarshalToWriter(writer)
return writer.GetResult()
}
// UnmarshalFromReader unmarshals a TriggerActionSetRequestStorage from the provided reader.
func (m *TriggerActionSetRequestStorage) UnmarshalFromReader(reader jspb.Reader) *TriggerActionSetRequestStorage {
for reader.Next() {
if m == nil {
m = &TriggerActionSetRequestStorage{}
}
switch reader.GetFieldNumber() {
case 1:
m.TemplateName = reader.ReadString()
case 2:
reader.ReadMessage(func() {
m.Set = m.Set.UnmarshalFromReader(reader)
})
default:
reader.SkipField()
}
}
return m
}
// Unmarshal unmarshals a TriggerActionSetRequestStorage from a slice of bytes.
func (m *TriggerActionSetRequestStorage) Unmarshal(rawBytes []byte) (*TriggerActionSetRequestStorage, error) {
reader := jspb.NewReader(rawBytes)
m = m.UnmarshalFromReader(reader)
@ -5125,6 +5208,12 @@ type P4WNP1Client interface {
DeployStoredWifiSettings(ctx context.Context, in *StringMessage, opts ...grpcweb.CallOption) (*WiFiState, error)
StoreDeployedWifiSettings(ctx context.Context, in *StringMessage, opts ...grpcweb.CallOption) (*Empty, error)
ListStoredWifiSettings(ctx context.Context, in *Empty, opts ...grpcweb.CallOption) (*StringMessageArray, error)
// TriggerActions
ListStoredTriggerActionSets(ctx context.Context, in *Empty, opts ...grpcweb.CallOption) (*StringMessageArray, error)
StoreTriggerActionSets(ctx context.Context, in *TriggerActionSetRequestStorage, opts ...grpcweb.CallOption) (*Empty, error)
GetTriggerActionsState(ctx context.Context, in *Empty, opts ...grpcweb.CallOption) (*TriggerActionSet, error)
DeployTriggerActionSetReplace(ctx context.Context, in *TriggerActionSet, opts ...grpcweb.CallOption) (*TriggerActionSet, error)
DeployTriggerActionSetAdd(ctx context.Context, in *TriggerActionSet, opts ...grpcweb.CallOption) (*TriggerActionSet, error)
}
type p4WNP1Client struct {
@ -5439,3 +5528,48 @@ func (c *p4WNP1Client) ListStoredWifiSettings(ctx context.Context, in *Empty, op
return new(StringMessageArray).Unmarshal(resp)
}
func (c *p4WNP1Client) ListStoredTriggerActionSets(ctx context.Context, in *Empty, opts ...grpcweb.CallOption) (*StringMessageArray, error) {
resp, err := c.client.RPCCall(ctx, "ListStoredTriggerActionSets", in.Marshal(), opts...)
if err != nil {
return nil, err
}
return new(StringMessageArray).Unmarshal(resp)
}
func (c *p4WNP1Client) StoreTriggerActionSets(ctx context.Context, in *TriggerActionSetRequestStorage, opts ...grpcweb.CallOption) (*Empty, error) {
resp, err := c.client.RPCCall(ctx, "StoreTriggerActionSets", in.Marshal(), opts...)
if err != nil {
return nil, err
}
return new(Empty).Unmarshal(resp)
}
func (c *p4WNP1Client) GetTriggerActionsState(ctx context.Context, in *Empty, opts ...grpcweb.CallOption) (*TriggerActionSet, error) {
resp, err := c.client.RPCCall(ctx, "GetTriggerActionsState", in.Marshal(), opts...)
if err != nil {
return nil, err
}
return new(TriggerActionSet).Unmarshal(resp)
}
func (c *p4WNP1Client) DeployTriggerActionSetReplace(ctx context.Context, in *TriggerActionSet, opts ...grpcweb.CallOption) (*TriggerActionSet, error) {
resp, err := c.client.RPCCall(ctx, "DeployTriggerActionSetReplace", in.Marshal(), opts...)
if err != nil {
return nil, err
}
return new(TriggerActionSet).Unmarshal(resp)
}
func (c *p4WNP1Client) DeployTriggerActionSetAdd(ctx context.Context, in *TriggerActionSet, opts ...grpcweb.CallOption) (*TriggerActionSet, error) {
resp, err := c.client.RPCCall(ctx, "DeployTriggerActionSetAdd", in.Marshal(), opts...)
if err != nil {
return nil, err
}
return new(TriggerActionSet).Unmarshal(resp)
}

File diff suppressed because it is too large Load Diff

View File

@ -48,13 +48,26 @@ service P4WNP1 {
rpc DeployStoredWifiSettings(StringMessage) returns (WiFiState) {}
rpc StoreDeployedWifiSettings(StringMessage) returns (Empty) {}
rpc ListStoredWifiSettings(Empty) returns (StringMessageArray) {}
// TriggerActions
rpc ListStoredTriggerActionSets(Empty) returns (StringMessageArray) {}
rpc StoreTriggerActionSets(TriggerActionSetRequestStorage) returns (Empty) {}
rpc GetTriggerActionsState(Empty) returns (TriggerActionSet) {}
rpc DeployTriggerActionSetReplace(TriggerActionSet) returns (TriggerActionSet) {} // Adds a set of additional TriggerActions, returns a set of all Triggeractions registered afterwards
rpc DeployTriggerActionSetAdd(TriggerActionSet) returns (TriggerActionSet) {} // Replaces registered TriggerActions with given set, returns a set ONLY OF ADDED TriggerActions (with assigned IDs)
}
/* Triggers, Actions and resulting TriggerActions */
message TriggerActionSettings {
repeated TriggerAction registeredTriggerActions = 1;
message TriggerActionSet {
repeated TriggerAction TriggerActions = 1;
}
message TriggerActionSetRequestStorage {
string TemplateName = 1;
TriggerActionSet set = 2;
}
message TriggerAction {
uint32 id = 1; // assigned by service, used as identifier to allow deletion of trigger actions
bool oneShot = 2;

10
service/globals.go Normal file
View File

@ -0,0 +1,10 @@
package service
const (
PATH_ROOT = "/usr/local/P4wnP1"
PATH_KEYBOARD_LANGUAGE_MAPS = PATH_ROOT + "/keymaps"
PATH_WEBROOT = PATH_ROOT + "/www"
PATH_BASH_SCRIPTS = PATH_ROOT + "/scripts"
PATH_HID_SCRIPTS = PATH_ROOT + "/HIDScripts"
PATH_DATA_STORE = "/tmp/store"
)

View File

@ -28,7 +28,8 @@ var (
)
const (
cSTORE_PREFIX_WIFI_SETTINGS = "ws_"
cSTORE_PREFIX_WIFI_SETTINGS = "ws_"
cSTORE_PREFIX_TRIGGER_ACTION_SET = "tas_"
)
type server struct {
@ -38,6 +39,46 @@ type server struct {
listenAddrWeb string
}
func (s *server) StoreTriggerActionSets(ctx context.Context, sr *pb.TriggerActionSetRequestStorage) (e *pb.Empty, err error) {
e = &pb.Empty{}
err = s.rootSvc.SubSysDataStore.Put(cSTORE_PREFIX_TRIGGER_ACTION_SET+ sr.TemplateName, sr.Set, true)
return
}
func (s *server) ListStoredTriggerActionSets(ctx context.Context, e *pb.Empty) (tas *pb.StringMessageArray, err error) {
res, err := s.rootSvc.SubSysDataStore.KeysPrefix(cSTORE_PREFIX_TRIGGER_ACTION_SET, true)
if err != nil {
return tas, err
}
tas = &pb.StringMessageArray{
MsgArray: res,
}
return
}
func (s *server) GetTriggerActionsState(context.Context, *pb.Empty) (*pb.TriggerActionSet, error) {
return s.rootSvc.SubSysTriggerActions.GetCurrentTriggerActionSet(), nil
}
func (s *server) DeployTriggerActionSetReplace(ctx context.Context, tas *pb.TriggerActionSet) (resTas *pb.TriggerActionSet, err error) {
// Clear old set, but keep immutables
s.rootSvc.SubSysTriggerActions.ClearTriggerActions(true)
// Add the new set
_,err = s.DeployTriggerActionSetAdd(ctx, tas)
if err != nil { return s.rootSvc.SubSysTriggerActions.GetCurrentTriggerActionSet(),err }
return s.GetTriggerActionsState(ctx, &pb.Empty{})
}
func (s *server) DeployTriggerActionSetAdd(ctx context.Context, tas *pb.TriggerActionSet) (resTas *pb.TriggerActionSet, err error) {
addedTA := make([]*pb.TriggerAction, len(tas.TriggerActions))
for idx,ta := range tas.TriggerActions {
addedTA[idx],err = s.rootSvc.SubSysTriggerActions.AddTriggerAction(ta)
if err != nil { return s.rootSvc.SubSysTriggerActions.GetCurrentTriggerActionSet(),err }
}
return &pb.TriggerActionSet{TriggerActions:addedTA},nil
}
func NewRpcServerService(root *Service) *server {
return &server{
rootSvc:root,

View File

@ -1,3 +1,5 @@
// +build linux,arm
package service
import (
@ -6,28 +8,28 @@ import (
"github.com/mame82/P4wnP1_go/service/datastore"
)
const (
// ToDo: change to non-temporary folder to persist over reboot
pPATH_DATA_STORE = "/tmp/store"
)
func RegisterDefaultTriggerActions(tam *TriggerActionManager) {
// create test trigger
// Trigger to run startup script
serviceUpRunScript := &pb.TriggerAction{
IsActive: true,
Immutable: true,
OneShot: false,
Trigger: &pb.TriggerAction_ServiceStarted{
ServiceStarted: &pb.TriggerServiceStarted{},
},
Action: &pb.TriggerAction_BashScript{
BashScript: &pb.ActionStartBashScript{
ScriptPath: "/usr/local/P4wnP1/scripts/servicestart.sh", // ToDo: use real script path once ready
ScriptName: "servicestart.sh", // ToDo: Remove path component
},
},
}
tam.AddTriggerAction(serviceUpRunScript)
logServiceStart := &pb.TriggerAction{
IsActive: true,
Trigger: &pb.TriggerAction_ServiceStarted{
ServiceStarted: &pb.TriggerServiceStarted{},
},
@ -38,6 +40,7 @@ func RegisterDefaultTriggerActions(tam *TriggerActionManager) {
tam.AddTriggerAction(logServiceStart)
logDHCPLease := &pb.TriggerAction{
IsActive: true,
Trigger: &pb.TriggerAction_DhcpLeaseGranted{
DhcpLeaseGranted: &pb.TriggerDHCPLeaseGranted{},
},
@ -48,6 +51,7 @@ func RegisterDefaultTriggerActions(tam *TriggerActionManager) {
tam.AddTriggerAction(logDHCPLease)
logUSBGadgetConnected := &pb.TriggerAction{
IsActive: true,
Trigger: &pb.TriggerAction_UsbGadgetConnected{
UsbGadgetConnected: &pb.TriggerUSBGadgetConnected{},
},
@ -58,6 +62,7 @@ func RegisterDefaultTriggerActions(tam *TriggerActionManager) {
tam.AddTriggerAction(logUSBGadgetConnected)
logUSBGadgetDisconnected := &pb.TriggerAction{
IsActive: true,
Trigger: &pb.TriggerAction_UsbGadgetDisconnected{
UsbGadgetDisconnected: &pb.TriggerUSBGadgetDisconnected{},
},
@ -68,6 +73,7 @@ func RegisterDefaultTriggerActions(tam *TriggerActionManager) {
tam.AddTriggerAction(logUSBGadgetDisconnected)
logWifiAp := &pb.TriggerAction{
IsActive: true,
Trigger: &pb.TriggerAction_WifiAPStarted{
WifiAPStarted: &pb.TriggerWifiAPStarted{},
},
@ -78,6 +84,7 @@ func RegisterDefaultTriggerActions(tam *TriggerActionManager) {
tam.AddTriggerAction(logWifiAp)
logWifiSta := &pb.TriggerAction{
IsActive: true,
Trigger: &pb.TriggerAction_WifiConnectedAsSta{
WifiConnectedAsSta: &pb.TriggerWifiConnectedAsSta{},
},
@ -88,6 +95,7 @@ func RegisterDefaultTriggerActions(tam *TriggerActionManager) {
tam.AddTriggerAction(logWifiSta)
logSSHLogin := &pb.TriggerAction{
IsActive: true,
Trigger: &pb.TriggerAction_SshLogin{
SshLogin: &pb.TriggerSSHLogin{},
},
@ -118,7 +126,7 @@ type Service struct {
func NewService() (svc *Service, err error) {
svc = &Service{}
svc.SubSysDataStore, err = datastore.Open(pPATH_DATA_STORE)
svc.SubSysDataStore, err = datastore.Open(PATH_DATA_STORE)
if err != nil {
return nil, err
}
@ -152,7 +160,7 @@ func (s *Service) Start() {
s.SubSysEvent.Start()
s.SubSysDwc2ConnectWatcher.Start()
s.SubSysLed.Start()
s.SubSysRPC.StartRpcServerAndWeb("0.0.0.0", "50051", "8000", "/usr/local/P4wnP1/www") //start gRPC service
s.SubSysRPC.StartRpcServerAndWeb("0.0.0.0", "50051", "8000", PATH_WEBROOT) //start gRPC service
s.SubSysTriggerActions.Start()
// Register TriggerActions

View File

@ -1,6 +1,7 @@
package service
import (
"errors"
"fmt"
"github.com/mame82/P4wnP1_go/common"
"github.com/mame82/P4wnP1_go/common_web"
@ -44,8 +45,9 @@ func (tam *TriggerActionManager) fireActionNoArgs(ta *pb.TriggerAction) (err err
switch actionType := ta.Action.(type) {
case *pb.TriggerAction_BashScript:
bs := actionType.BashScript
go common.RunBashScriptEnv(bs.ScriptPath)
fmt.Printf("Fire bash script '%s'\n", bs.ScriptPath)
scriptPath := PATH_BASH_SCRIPTS + "/" + bs.ScriptName
go common.RunBashScriptEnv(scriptPath)
fmt.Printf("Fire bash script '%s'\n", scriptPath)
case *pb.TriggerAction_HidScript:
// ToDo: Implement
hs := actionType.HidScript
@ -70,10 +72,11 @@ func (tam *TriggerActionManager) fireActionSSHLogin(loginUser string, ta *pb.Tri
switch actionType := ta.Action.(type) {
case *pb.TriggerAction_BashScript:
bs := actionType.BashScript
scriptPath := PATH_BASH_SCRIPTS + "/" + bs.ScriptName
envUser := fmt.Sprintf("SSH_LOGIN_USER=%s", loginUser)
go common.RunBashScriptEnv(bs.ScriptPath, envUser)
go common.RunBashScriptEnv(scriptPath, envUser)
//go common.RunBashScript(bs.ScriptPath)
fmt.Printf("Started bash script '%s' (%s)\n", bs.ScriptPath, envUser)
fmt.Printf("Started bash script '%s' (%s)\n", scriptPath, envUser)
case *pb.TriggerAction_HidScript:
// ToDo: Implement
hs := actionType.HidScript
@ -101,12 +104,13 @@ func (tam *TriggerActionManager) fireActionDhcpLeaseGranted(iface string, mac st
switch actionType := ta.Action.(type) {
case *pb.TriggerAction_BashScript:
bs := actionType.BashScript
scriptPath := PATH_BASH_SCRIPTS + "/" + bs.ScriptName
envIface := fmt.Sprintf("DHCP_LEASE_IFACE=%s", iface)
envMac := fmt.Sprintf("DHCP_LEASE_MAC=%s", mac)
envIp := fmt.Sprintf("DHCP_LEASE_IP=%s", ip)
go common.RunBashScriptEnv(bs.ScriptPath, envIface, envMac, envIp)
go common.RunBashScriptEnv(scriptPath, envIface, envMac, envIp)
//go common.RunBashScript(bs.ScriptPath)
fmt.Printf("Started bash script '%s' (%s, %s, %s)\n", bs.ScriptPath, envIface, envMac, envIp)
fmt.Printf("Started bash script '%s' (%s, %s, %s)\n", scriptPath, envIface, envMac, envIp)
case *pb.TriggerAction_HidScript:
// ToDo: Implement
hs := actionType.HidScript
@ -188,53 +192,43 @@ func (tam *TriggerActionManager) dispatchTriggerEvent(evt *pb.Event) {
tam.registeredTriggerActionMutex.Lock()
defer tam.registeredTriggerActionMutex.Unlock()
markedForRemoval := []int{}
for idx,ta := range tam.registeredTriggerAction {
for _,ta := range tam.registeredTriggerAction {
// ToDo: handle errors of fireAction* methods
// ToDo: fire action methods have to return an additional int, indicating if the action has really fired (filter function in action could prevent this, which would cause wrong behavior for OneShot riggerActions)
// skip disabled triggeractions
if !ta.IsActive { continue }
if taTriggerTypeMatchesEvtTriggerType(ta, evt) {
hasFired := true
switch ttEvt := common_web.EvtTriggerType(evt.Values[0].GetTint64()); ttEvt {
case common_web.EVT_TRIGGER_TYPE_SERVICE_STARTED:
tam.fireActionServiceStarted(ta)
if ta.OneShot {
markedForRemoval = append(markedForRemoval,idx)
}
case common_web.EVT_TRIGGER_TYPE_SSH_LOGIN:
loginUser := evt.Values[1].GetTstring()
tam.fireActionSSHLogin(loginUser, ta)
if ta.OneShot {
markedForRemoval = append(markedForRemoval,idx)
}
case common_web.EVT_TRIGGER_TYPE_WIFI_CONNECTED_AS_STA:
tam.fireActionWifiConnectedAsSta(ta)
if ta.OneShot {
markedForRemoval = append(markedForRemoval,idx)
}
case common_web.EVT_TRIGGER_TYPE_WIFI_AP_STARTED:
tam.fireActionWifiApStarted(ta)
if ta.OneShot {
markedForRemoval = append(markedForRemoval,idx)
}
case common_web.EVT_TRIGGER_TYPE_DHCP_LEASE_GRANTED:
// extract iface, mac and ip from event
iface := evt.Values[1].GetTstring()
mac := evt.Values[2].GetTstring()
ip := evt.Values[3].GetTstring()
tam.fireActionDhcpLeaseGranted(iface, mac, ip, ta)
if ta.OneShot {
markedForRemoval = append(markedForRemoval,idx)
}
case common_web.EVT_TRIGGER_TYPE_USB_GADGET_CONNECTED:
tam.fireActionUsbGadgetConnected(ta)
if ta.OneShot {
markedForRemoval = append(markedForRemoval,idx)
}
case common_web.EVT_TRIGGER_TYPE_USB_GADGET_DISCONNECTED:
tam.fireActionUsbGadgetDisconnected(ta)
if ta.OneShot {
markedForRemoval = append(markedForRemoval,idx)
}
default:
hasFired = false
fmt.Println("unhandled trigger: ", ttEvt)
}
if hasFired && ta.OneShot {
//markedForRemoval = append(markedForRemoval,idx)
ta.IsActive = false // don't delete, but deactivate
}
}
}
@ -247,17 +241,82 @@ func (tam *TriggerActionManager) dispatchTriggerEvent(evt *pb.Event) {
return
}
func (tam *TriggerActionManager) AddTriggerAction(ta *pb.TriggerAction) (err error) {
// 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.registeredTriggerAction = append(tam.registeredTriggerAction, ta)
return taAdded,nil
}
var (
ErrTaNotFound = errors.New("Couldn't find given TriggerAction")
ErrTaImmutable = errors.New("Not allowed to change immutable TriggerAction")
)
func (tam *TriggerActionManager) GetTriggerActionByID(Id uint32) (ta *pb.TriggerAction ,err error) {
for _,ta = range tam.registeredTriggerAction {
if ta.Id == Id {
return ta, nil
}
}
return nil, ErrTaNotFound
}
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 }
targetTA.OneShot = srcTa.OneShot
targetTA.IsActive = srcTa.IsActive
targetTA.Immutable = srcTa.Immutable
targetTA.Action = srcTa.Action
targetTA.Trigger = srcTa.Trigger
return nil
}
}
func (tam *TriggerActionManager) ClearTriggerActions(keepImmutable bool) (err error) {
tam.registeredTriggerActionMutex.Lock()
defer tam.registeredTriggerActionMutex.Unlock()
if !keepImmutable {
tam.registeredTriggerAction = []*pb.TriggerAction{}
return
}
newTas := []*pb.TriggerAction{}
for _,ta := range tam.registeredTriggerAction {
if ta.Immutable {
newTas = append(newTas, ta)
}
}
tam.registeredTriggerAction = newTas
return nil
}
func (tam *TriggerActionManager) GetCurrentTriggerActionSet() (ta *pb.TriggerActionSet) {
tam.registeredTriggerActionMutex.Lock()
resTAs := make([]*pb.TriggerAction, len(tam.registeredTriggerAction))
copy(resTAs, tam.registeredTriggerAction)
tam.registeredTriggerActionMutex.Unlock()
return &pb.TriggerActionSet{ TriggerActions: resTAs }
}
func (tam *TriggerActionManager) Start() {
tam.evtRcv = tam.rootSvc.SubSysEvent.RegisterReceiver(common_web.EVT_TRIGGER) // ToDo: change to trigger event type, once defined
go tam.processing_loop()

View File

@ -70,7 +70,7 @@ const (
USB_FUNCTION_HID_RAW_report_desc = "\x06\x00\xff\t\x01\xa1\x01\t\x01\x15\x00&\xff\x00u\x08\x95@\x81\x02\t\x02\x15\x00&\xff\x00u\x08\x95@\x91\x02\xc0"
USB_FUNCTION_HID_RAW_name = "hid.raw"
USB_KEYBOARD_LANGUAGE_MAP_PATH = "/usr/local/P4wnP1/keymaps"
)
@ -674,12 +674,12 @@ func (gm *UsbGadgetManager) DeployGadgetSettings(settings *pb.GadgetSettings) er
devPathMouse := gm.State.DevicePath[USB_FUNCTION_HID_MOUSE_name]
var errH error
gm.HidCtl, errH = hid.NewHIDController(context.Background(), devPathKeyboard, USB_KEYBOARD_LANGUAGE_MAP_PATH, devPathMouse)
gm.HidCtl, errH = hid.NewHIDController(context.Background(), devPathKeyboard, PATH_KEYBOARD_LANGUAGE_MAPS, devPathMouse)
gm.HidCtl.SetEventHandler(gm)
if errH != nil {
log.Printf("ERROR: Couldn't bring up an instance of HIDController for keyboard: '%s', mouse: '%s' and mapping path '%s'\nReason: %v\n", devPathKeyboard, devPathMouse, USB_KEYBOARD_LANGUAGE_MAP_PATH, errH)
log.Printf("ERROR: Couldn't bring up an instance of HIDController for keyboard: '%s', mouse: '%s' and mapping path '%s'\nReason: %v\n", devPathKeyboard, devPathMouse, PATH_KEYBOARD_LANGUAGE_MAPS, errH)
} else {
log.Printf("HIDController for keyboard: '%s', mouse: '%s' and mapping path '%s' initialized\n", devPathKeyboard, devPathMouse, USB_KEYBOARD_LANGUAGE_MAP_PATH)
log.Printf("HIDController for keyboard: '%s', mouse: '%s' and mapping path '%s' initialized\n", devPathKeyboard, devPathMouse, PATH_KEYBOARD_LANGUAGE_MAPS)
}
} else {
if gm.HidCtl != nil { gm.HidCtl.Abort() }

View File

@ -132,6 +132,12 @@ func generateSelectOptionsTemplateTypes() *js.Object {
return tts
}
type TriggerActionCompData struct {
*js.Object
EditMode bool `js:"EditMode"`
}
func InitComponentsTriggerActions() {
hvue.NewComponent(
@ -145,15 +151,50 @@ func InitComponentsTriggerActions() {
data.TriggerAction = NewTriggerAction()
return &data
}),
hvue.Method("addTA",
func(vm *hvue.VM) {
vm.Get("$store").Call("dispatch", VUEX_ACTION_ADD_NEW_TRIGGER_ACTION)
}),
hvue.Mounted(func(vm *hvue.VM) {
vm.Store.Call("dispatch", VUEX_ACTION_UPDATE_TRIGGER_ACTIONS)
}),
)
hvue.NewComponent(
"triggeraction",
hvue.Template(templateTriggerAction),
hvue.Props("ta"),
hvue.DataFunc(func(vm *hvue.VM) interface{} {
data := &TriggerActionCompData{Object: O()}
data.EditMode = false
return data
}),
hvue.PropObj("ta"),
hvue.PropObj(
"overview",
hvue.Types(hvue.PBoolean),
),
hvue.Computed("strTrigger", func(vm *hvue.VM) interface{} {
ta := &jsTriggerAction{Object: vm.Get("ta")}
strTrigger := triggerNames[ta.TriggerType]
return strTrigger
}),
hvue.Computed("strAction", func(vm *hvue.VM) interface{} {
ta := &jsTriggerAction{Object: vm.Get("ta")}
strTrigger := actionNames[ta.ActionType]
return strTrigger
}),
hvue.Mounted(func(vm *hvue.VM) {
data := TriggerActionCompData{Object: vm.Data}
data.EditMode = vm.Get("overview").Bool()
}),
hvue.Method(
"toggleEditMode",
func(vm *hvue.VM) {
data := TriggerActionCompData{Object: vm.Data}
data.EditMode = !data.EditMode
}),
)
hvue.NewComponent(
"trigger",
@ -291,9 +332,28 @@ func InitComponentsTriggerActions() {
//
const templateTriggerAction = `
<q-card class="fit">
<q-card tag="label" link v-if="EditMode">
<q-card-title>
TriggerAction ({{ ta.IsActive ? "active" : "inactive" }})
<span slot="subtitle">
<q-icon name="input"></q-icon>
{{ strTrigger }}
<q-icon name="launch"></q-icon>
{{ strAction }}{{ta.OneShot ? " (only once)" : "" }}
</span>
<q-btn slot="right" icon="more_vert" @click="toggleEditMode" flat></q-btn>
</q-card-title>
</q-card>
<q-card v-else class="fit">
<!-- {{ ta }} -->
<q-card-title>TriggerAction (ID {{ ta.Id }})</q-card-title>
<q-card-title>
TriggerAction
<span slot="subtitle">ID {{ ta.Id }}</span>
<q-btn slot="right" icon="more_vert" @click="toggleEditMode" flat></q-btn>
</q-card-title>
<q-list>
<q-item tag="label" link>
<q-item-side>
@ -512,11 +572,18 @@ const templateAction = `
const templateTriggerActionManager = `
<q-page padding>
<q-card>
<q-card-actions>
<q-btn label="add" @click="addTA" icon="event" />
</q-card-actions>
<div class="row gutter-sm">
<div class="col-12 col-xl-6" v-for="ta in $store.getters.triggerActions">
<triggeraction :key="ta.Id" :ta="ta"></triggeraction>
</div>
<div class="col-12 col-xl-6" v-for="ta in $store.getters.triggerActions">
<triggeraction :key="ta.Id" :ta="ta" overview></triggeraction>
</div>
</div>
</q-card>
</q-page>
`

View File

@ -27,24 +27,24 @@ const (
ActionGroupSend = actionType(5)
)
var triggerNames = map[triggerType]string{
TriggerServiceStarted: "Service started",
TriggerUsbGadgetConnected: "USB Gadget connected to host",
TriggerUsbGadgetDisconnected: "USB Gadget disconnected from host",
TriggerServiceStarted: "service has been started",
TriggerUsbGadgetConnected: "USB gadget has connected to host",
TriggerUsbGadgetDisconnected: "USB Gadget has disconnected from host",
TriggerWifiAPStarted: "WiFi Access Point is up",
TriggerWifiConnectedAsSta: "Connected to existing WiFi",
TriggerSshLogin: "User login via SSH",
TriggerDhcpLeaseGranted: "Client received DHCP lease",
TriggerGPIOIn: "GPIO Pin input",
TriggerGroupReceive: "Group channel received value",
TriggerGroupReceiveSequence: "Group channel received sequence",
TriggerWifiConnectedAsSta: "joined an existing WiFi",
TriggerSshLogin: "a user logged in via SSH",
TriggerDhcpLeaseGranted: "a DHCP lease has been issued",
TriggerGPIOIn: "received input on GPIO",
TriggerGroupReceive: "received a value on a group channel",
TriggerGroupReceiveSequence: "received a sequence of values on a group channel",
}
var actionNames = map[actionType]string{
ActionLog: "Log to internal console",
ActionHidScript: "Start a HIDScript",
ActionDeploySettingsTemplate: "Load and deploy the given settings",
ActionBashScript: "Run the given bash script",
ActionGPIOOut: "GPIO Pin output",
ActionGroupSend: "Send value to group channel",
ActionLog: "write log entry",
ActionHidScript: "start a HIDScript",
ActionDeploySettingsTemplate: "load and deploy settings from a template",
ActionBashScript: "run a bash script",
ActionGPIOOut: "set output on GPIO",
ActionGroupSend: "send a value to a group channel",
}
var availableTriggers = []triggerType{
TriggerServiceStarted,

View File

@ -4,6 +4,7 @@ package main
import (
"github.com/gopherjs/gopherjs/js"
pb "github.com/mame82/P4wnP1_go/proto/gopherjs"
"github.com/mame82/hvue"
"github.com/mame82/mvuex"
"time"
@ -15,7 +16,8 @@ const (
maxLogEntries = 500
VUEX_ACTION_UPDATE_RUNNING_HID_JOBS = "updateRunningHidJobs"
VUEX_ACTION_UPDATE_TRIGGER_ACTIONS = "updateTriggerActions"
VUEX_ACTION_UPDATE_TRIGGER_ACTIONS = "updateTriggerActions"
VUEX_ACTION_ADD_NEW_TRIGGER_ACTION = "addTriggerAction"
VUEX_ACTION_DEPLOY_CURRENT_GADGET_SETTINGS = "deployCurrentGadgetSettings"
VUEX_ACTION_UPDATE_GADGET_SETTINGS_FROM_DEPLOYED = "updateCurrentGadgetSettingsFromDeployed"
VUEX_ACTION_DEPLOY_ETHERNET_INTERFACE_SETTINGS = "deployEthernetInterfaceSettings"
@ -215,33 +217,37 @@ func actionUpdateRunningHidJobs(store *mvuex.Store, context *mvuex.ActionContext
func actionUpdateTriggerActions(store *mvuex.Store, context *mvuex.ActionContext, state *GlobalState) {
go func() {
// ToDo: replace by with RPC, dummy function
tastate,err := RpcClient.GetTriggerActionsState(time.Second * 10)
if err != nil {
QuasarNotifyError("Error fetching deployed TriggerActions", err.Error(), QUASAR_NOTIFICATION_POSITION_TOP)
return
}
//Test Actions
// Generate an array of TriggerActions, which is provided from the Vuex store once the components ViewModel structures are finalized
taList := []*jsTriggerAction{
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
NewTriggerAction(),
}
taList[1].Immutable = true
taList[2].IsActive = false
for idx,ta := range taList {
ta.Id = uint32(idx)
state.TriggerActionList.UpdateEntry(ta)
// ToDo: Clear list berfore adding back elements
for _,ta := range tastate.TriggerActions {
jsTA := NewTriggerAction()
jsTA.fromGo(ta)
state.TriggerActionList.UpdateEntry(jsTA)
}
}()
return
}
func actionAddNewTriggerAction(store *mvuex.Store, context *mvuex.ActionContext, state *GlobalState) {
go func() {
newTA := NewTriggerAction()
newTA.IsActive = false // don't activate by default
RpcClient.DeployTriggerActionsSetAdd(time.Second*10, &pb.TriggerActionSet{TriggerActions:[]*pb.TriggerAction{newTA.toGo()}})
actionUpdateTriggerActions(store, context, state)
}()
return
}
func actionDeployCurrentGadgetSettings(store *mvuex.Store, context *mvuex.ActionContext, state *GlobalState) {
go func() {
@ -334,6 +340,7 @@ func initMVuex() *mvuex.Store {
mvuex.Action(VUEX_ACTION_UPDATE_STORED_WIFI_SETTINGS_LIST, actionUpdateStoredWifiSettingsList),
mvuex.Action(VUEX_ACTION_STORE_WIFI_SETTINGS, actionStoreWifiSettings),
mvuex.Action(VUEX_ACTION_UPDATE_TRIGGER_ACTIONS, actionUpdateTriggerActions),
mvuex.Action(VUEX_ACTION_ADD_NEW_TRIGGER_ACTION, actionAddNewTriggerAction),
mvuex.Getter("triggerActions", func(state *GlobalState) interface{} {
return state.TriggerActionList.TriggerActions

View File

@ -3,14 +3,14 @@
package main
import (
pb "github.com/mame82/P4wnP1_go/proto/gopherjs"
"context"
"sync"
"github.com/johanbrandhorst/protobuf/grpcweb"
"github.com/mame82/P4wnP1_go/common_web"
pb "github.com/mame82/P4wnP1_go/proto/gopherjs"
"io"
"sync"
"time"
"errors"
"github.com/mame82/P4wnP1_go/common_web"
"io"
)
type Rpc struct {
@ -141,6 +141,46 @@ func (rpc *Rpc) RpcGetRunningHidJobStates(timeout time.Duration) (states []*pb.H
return states, nil
}
func (rpc *Rpc) ListStoredTriggerActionSets(timeout time.Duration) (tasNames []string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
msgArray,err := rpc.Client.ListStoredTriggerActionSets(ctx, &pb.Empty{})
if err != nil { return tasNames, err}
return msgArray.MsgArray, nil
}
func (rpc *Rpc) StoreTriggerActionSet(timeout time.Duration, set *pb.TriggerActionSet) (err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
_,err = rpc.Client.StoreTriggerActionSets(ctx, &pb.TriggerActionSetRequestStorage{Set:set})
return err
}
func (rpc *Rpc) GetTriggerActionsState(timeout time.Duration) (res *pb.TriggerActionSet, err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return rpc.Client.GetTriggerActionsState(ctx, &pb.Empty{})
}
func (rpc *Rpc) DeployTriggerActionsSetReplace(timeout time.Duration, set *pb.TriggerActionSet) (res *pb.TriggerActionSet, err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return rpc.Client.DeployTriggerActionSetReplace(ctx, set)
}
func (rpc *Rpc) DeployTriggerActionsSetAdd(timeout time.Duration, set *pb.TriggerActionSet) (res *pb.TriggerActionSet, err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
return rpc.Client.DeployTriggerActionSetAdd(ctx, set)
}
func (rpc *Rpc) RpcGetDeployedGadgetSettings(timeout time.Duration) (*pb.GadgetSettings, error) {
//gs := vue.GetVM(c).Get("gadgetSettings")
println("RpcGetDeployedGadgetSettings called")