webclient,service,hid: Fixes of HIDScript state events; fix timestamp displaying

This commit is contained in:
MaMe82 2018-10-30 15:36:10 +01:00
parent 5657a98c17
commit 32846e652c
15 changed files with 258 additions and 143 deletions

View File

@ -167,6 +167,13 @@ func (avm *AsyncOttoVM) RunAsync(ctx context.Context, src interface{}, anonymous
if caught == haltirq {
job.ResultErr = errors.New(fmt.Sprintf("Execution of job %d on VM %d interrupted\n", job.Id, avm.Id))
avm.emitEvent(Event{
Job:job,
Vm:job.executingVM,
Type:EventType_JOB_CANCELLED,
Message:"Script execution cancelled",
})
// signal Job finished
job.SetFinished()
return

View File

@ -1,3 +1,4 @@
// +build linux
package hid
@ -301,12 +302,14 @@ func (ctl *HIDController) CancelAllBackgroundJobs() {
for job,_ := range oldList {
fmt.Printf("Cancelling Job %d\n", job.Id)
job.Cancel()
/*
ctl.emitEvent(Event{
Job:job,
Vm:job.executingVM,
Type:EventType_JOB_CANCELLED,
Message:"Script execution cancelled",
})
*/
}
globalJobList = make(map[*AsyncOttoJob]bool) //Create new empty list
globalJobListMutex.Unlock()

View File

@ -1,15 +1,15 @@
package service
import (
"errors"
"fmt"
pb "github.com/mame82/P4wnP1_go/proto"
"context"
"fmt"
"github.com/mame82/P4wnP1_go/common_web"
"github.com/mame82/P4wnP1_go/hid"
pb "github.com/mame82/P4wnP1_go/proto"
"log"
"sync"
"time"
"log"
"github.com/mame82/P4wnP1_go/hid"
"github.com/mame82/P4wnP1_go/common_web"
"errors"
)
type EventManager struct {
@ -53,7 +53,7 @@ func (em *EventManager) Emit(event *pb.Event) {
}
func (em *EventManager) Write(p []byte) (n int, err error) {
ev := ConstructEventLog("logWriter", 1, string(p))
ev := ConstructEventLog("logWriter", LOG_LEVEL_INFORMATION, string(p))
em.Emit(ev)
return len(p), nil
}
@ -165,8 +165,32 @@ func ConstructEventNotifyStateChange(stateType common_web.EvtStateChangeType) *p
}
}
func ConstructEventLog(source string, level int, message string) *pb.Event {
tJson, _ := time.Now().MarshalJSON()
/*
case 1:
return prefix + "critical"
case 2:
return prefix + "error"
case 3:
return prefix + "warning"
case 4:
return prefix + "information"
case 5:
return prefix + "verbose"
*/
type LogLevel int
const (
LOG_LEVEL_UNDEFINED LogLevel = iota
LOG_LEVEL_CRITICAL
LOG_LEVEL_ERROR
LOG_LEVEL_WARNING
LOG_LEVEL_INFORMATION
LOG_LEVEL_VERBOSE
)
func ConstructEventLog(source string, level LogLevel, message string) *pb.Event {
//tJson, _ := time.Now().MarshalJSON()
unixTimeMillis := time.Now().UnixNano() / 1e6
return &pb.Event{
Type: common_web.EVT_LOG,
@ -174,7 +198,7 @@ func ConstructEventLog(source string, level int, message string) *pb.Event {
{Val: &pb.EventValue_Tstring{Tstring: source}},
{Val: &pb.EventValue_Tint64{Tint64: int64(level)}},
{Val: &pb.EventValue_Tstring{Tstring: message}},
{Val: &pb.EventValue_Tstring{Tstring: string(tJson)}},
{Val: &pb.EventValue_Tint64{Tint64: unixTimeMillis}}, //retrieve time in nano second accuracy and scale down to milliseconds
},
}
}
@ -266,7 +290,8 @@ func ConstructEventHID(hidEvent hid.Event) *pb.Event {
vmID = eVM.Id
}
tJson, _ := time.Now().MarshalJSON()
unixTimeMillis := time.Now().UnixNano() / 1e6
return &pb.Event{
Type: common_web.EVT_HID, //Type
@ -278,7 +303,7 @@ func ConstructEventHID(hidEvent hid.Event) *pb.Event {
{Val: &pb.EventValue_Tstring{Tstring: resString}}, //result String
{Val: &pb.EventValue_Tstring{Tstring: errString}}, //error String (message in case of error)
{Val: &pb.EventValue_Tstring{Tstring: message}}, //Mesage text of event
{Val: &pb.EventValue_Tstring{Tstring: string(tJson)}}, //Timestamp of event genration
{Val: &pb.EventValue_Tint64{Tint64: unixTimeMillis}}, //Timestamp of event genration
},
}
}

View File

@ -547,7 +547,7 @@ func (tam *TriggerActionManager) executeActionLog(evt *pb.Event, ta *pb.TriggerA
}
fmt.Printf("Trigger '%s' fired -> executing action '%s'\n", triggerName, actionName)
tam.rootSvc.SubSysEvent.Emit(ConstructEventLog("TriggerAction", 0, logMessage))
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

View File

@ -3,12 +3,9 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
"crypto/md5"
"encoding/hex"
"time"
pb "github.com/mame82/P4wnP1_go/proto/gopherjs"
"context"
"github.com/gopherjs/gopherjs/js"
)
func O() *js.Object {
@ -27,6 +24,14 @@ func StringToMD5(input string) string {
return string(dst)
}
func BytesToMD5(input []byte) string {
sum := md5.Sum(input)
dst := make([]byte, hex.EncodedLen(len(sum)))
hex.Encode(dst, sum[:])
return string(dst)
}
/*
func UploadHIDScript(filename string, content string) (err error) {
ctx,cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
@ -65,4 +70,5 @@ func RunHIDScript(filename string, timeoutSeconds uint32) (job *pb.HIDScriptJob,
},
)
}
}
*/

View File

@ -29,8 +29,11 @@ func InitCompHIDEvents() {
func(vm *hvue.VM) interface{} {
return vm.Get("$store").Get("state").Get("EventProcessor").Get("eventHidArray")
}),
hvue.Method("formatDate", func(vm *hvue.VM, timestamp *js.Object) interface{} {
return js.Global.Get("Quasar").Get("utils").Get("date").Call("formatDate", timestamp, "YYYY-MM-DD HH:mm:ss Z")
}),
hvue.Method("evIdToString", func(vm *hvue.VM, evID int64) (res string) {
println("EvID", evID)
//println("EvID", evID)
return common_web.EventTypeHIDName[evID]
}),
)
@ -45,12 +48,16 @@ const (
<div>
<q-table
:data="events"
:columns="[{name:'type', field: 'evtype', label: 'Event Type', align: 'left'}, {name:'vmid', field: 'vmId', label: 'VM ID', align: 'left'}, {name:'jobid', field: 'jobId', label: 'Job ID', align: 'left'}, {name:'haserror', field: 'hasError', label: 'Has error', align: 'left'}, {name:'res', field: 'result', label: 'Result', align: 'left'}, {name:'errormsg', field: 'error', label: 'Error', align: 'left'}, {name:'msg', field: 'message', label: 'Message', align: 'left'}, {name:'timestamp', field: 'time', label: 'Time', align: 'left'}]"
:columns="[{name:'timestamp', field: 'time', label: 'Time', align: 'left'}, {name:'type', field: 'evtype', label: 'Event Type', align: 'left'}, {name:'vmid', field: 'vmId', label: 'VM ID', align: 'left'}, {name:'jobid', field: 'jobId', label: 'Job ID', align: 'left'}, {name:'haserror', field: 'hasError', label: 'Has error', align: 'left'}, {name:'res', field: 'result', label: 'Result', align: 'left'}, {name:'errormsg', field: 'error', label: 'Error', align: 'left'}, {name:'msg', field: 'message', label: 'Message', align: 'left'}]"
row-key="name"
:pagination="pagination"
hide-bottom
>
<q-td slot="body-cell-timestamp" slot-scope="props" :props="props">
{{ formatDate(props.value) }}
</q-td>
<q-td slot="body-cell-type" slot-scope="props" :props="props">
{{ evIdToString(props.value) }}
</q-td>

View File

@ -123,13 +123,16 @@ ScriptSource string `js:"textSource"`
<q-popover>
{{ job.textResult }}
</q-popover>
<q-tooltip>
show job result
</q-tooltip>
</q-btn>
</q-item-side>
<q-item-side right v-if="!job.hasSucceeded && !job.hasFailed">
<q-btn flat round dense color="negative" icon="cancel" @click="cancel">
<q-popover>
<q-tooltip>
cancel HIDScript job {{ job.id }}
</q-popover>
</q-tooltip>
</q-btn>
</q-item-side>
</q-item>
@ -141,15 +144,31 @@ ScriptSource string `js:"textSource"`
compHIDJobOverviewTemplate = `
<q-card class="full-height">
<q-list>
<q-list-header>Running</q-list-header>
<hid-job-overview-item v-for="job in $store.getters.hidjobsRunning" :job="job" :key="job.id"></hid-job-overview-item>
<q-collapsible opened icon-toggle>
<template slot="header">
<q-item-main label="Running jobs" :sublabel="'(' + $store.getters.hidjobsRunning.length + ' running jobs)'"/>
<q-item-side v-if="$store.getters.hidjobsRunning.length > 0" right>
<q-btn icon="cancel" color="red" @click="$store.dispatch('cancelAllHIDJobs')" round inverted flat>
<q-tooltip>
cancel all running HIDScript jobs
</q-tooltip>
</q-btn>
</q-item-side>
</template>
<hid-job-overview-item v-for="job in $store.getters.hidjobsRunning" :job="job" :key="job.id"></hid-job-overview-item>
</q-collapsible>
</q-list>
<q-list>
<q-collapsible opened icon-toggle>
<q-collapsible opened icon-toggle>
<template slot="header">
<q-item-main label="Succeeded" :sublabel="'(' + $store.getters.hidjobsSucceeded.length + ' successful jobs)'"/>
<q-item-side v-if="$store.getters.hidjobsSucceeded.length > 0" right>
<q-btn icon="delete" color="red" @click="$store.dispatch('removeSucceededHidJobs')" round inverted flat />
<q-btn icon="delete" color="red" @click="$store.dispatch('removeSucceededHidJobs')" round inverted flat>
<q-tooltip>
delete succeeded HID jobs from list
</q-tooltip>
</q-btn>
</q-item-side>
</template>
<hid-job-overview-item v-for="job in $store.getters.hidjobsSucceeded" :job="job" :key="job.id"></hid-job-overview-item>
@ -160,7 +179,12 @@ compHIDJobOverviewTemplate = `
<template slot="header">
<q-item-main label="Failed" :sublabel="'(' + $store.getters.hidjobsFailed.length + ' failed jobs)'"/>
<q-item-side v-if="$store.getters.hidjobsFailed.length > 0" right>
<q-btn icon="delete" color="red" @click="$store.dispatch('removeFailedHidJobs')" round inverted flat />
<q-btn icon="delete" color="red" @click="$store.dispatch('removeFailedHidJobs')" round inverted flat>
<q-tooltip>
delete failed HID jobs from list
</q-tooltip>
</q-btn>
</q-item-side>
</template>
<hid-job-overview-item v-for="job in $store.getters.hidjobsFailed" :job="job" :key="job.id"></hid-job-overview-item>

View File

@ -5,7 +5,6 @@ package main
import (
"github.com/gopherjs/gopherjs/js"
"github.com/mame82/hvue"
"strconv"
)
type CompHIDScriptCodeEditorData struct {
@ -13,28 +12,6 @@ type CompHIDScriptCodeEditorData struct {
CodeMirrorOptions *CodeMirrorOptionsType `js:"codemirrorOptions"`
}
// ToDo: Change into action of vuex store
func SendAndRun(vm *hvue.VM) {
sourceCode := vm.Get("$store").Get("state").Get("currentHIDScriptSource").String()
md5 := StringToMD5(sourceCode) //Calculate MD5 hexstring of current script content
go func() {
timeout := uint32(0)
err := UploadHIDScript(md5, sourceCode)
if err != nil {
QuasarNotifyError("Error uploading script", err.Error(), QUASAR_NOTIFICATION_POSITION_TOP)
return
}
job,err := RunHIDScript(md5, timeout)
if err != nil {
QuasarNotifyError("Error starting script as background job", err.Error(), QUASAR_NOTIFICATION_POSITION_TOP)
return
}
QuasarNotifySuccess("Script started successfully", "Job ID " + strconv.Itoa(int(job.Id)), QUASAR_NOTIFICATION_POSITION_TOP)
}()
}
type CodeMirrorMode struct {
*js.Object
Name string `js:"name"`
@ -80,7 +57,6 @@ func InitComponentsHIDScript() {
"hid-script-code-editor",
hvue.Template(compHIDScriptCodeEditorTemplate),
hvue.DataFunc(newCompHIDScriptCodeEditorData),
hvue.Method("SendAndRun", SendAndRun),
hvue.ComputedWithGetSet(
"scriptContent",
func(vm *hvue.VM) interface{} {
@ -133,7 +109,10 @@ func InitComponentsHIDScript() {
vm.Get("$q").Call("notify", "store " + name.String())
vm.Store.Call("dispatch", VUEX_ACTION_STORE_CURRENT_HID_SCRIPT_SOURCE_TO_REMOTE_FILE, name)
}),
hvue.Method("SendAndRun", SendAndRun),
hvue.Method("SendAndRun", func (vm *hvue.VM) {
sourceCode := vm.Get("$store").Get("state").Get("currentHIDScriptSource").String()
vm.Store.Call("dispatch", VUEX_ACTION_AND_AND_RUN_HID_SCRIPT, sourceCode)
}),
)
}

View File

@ -3,6 +3,7 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
"github.com/mame82/hvue"
)
@ -30,22 +31,25 @@ func InitCompLogger() {
hvue.NewComponent(
"logger",
hvue.Template(compLoggerTemplate),
// hvue.DataFunc(NewLoggerData),
// hvue.MethodsOf(&CompLoggerData{}),
hvue.DataFunc(func(vm *hvue.VM) interface{} {
data := &struct {
*js.Object
Pagination *jsDataTablePagination `js:"pagination"`
}{Object:O()}
data.Pagination = newPagination(0, 1)
return data
}),
hvue.Method("logLevelClass", LogLevelClass),
hvue.PropObj("max-entries", hvue.Types(hvue.PNumber), hvue.Default(5)),
hvue.Created(func(vm *hvue.VM) {
println("OnCreated")
// vm.Call("StartListening")
}),
hvue.Destroyed(func(vm *hvue.VM) {
println("OnDestroyed")
// vm.Call("StopListening")
}),
hvue.Computed("classFromLevel", func(vm *hvue.VM) interface{} {
return "info"
}),
hvue.Method("formatDate", func(vm *hvue.VM, timestamp *js.Object) interface{} {
return js.Global.Get("Quasar").Get("utils").Get("date").Call("formatDate", timestamp, "YYYY-MM-DD HH:mm:ss Z")
}),
hvue.Computed("logArray",
func(vm *hvue.VM) interface{} {
return vm.Get("$store").Get("state").Get("EventProcessor").Get("logArray")
@ -57,7 +61,24 @@ func InitCompLogger() {
const (
compLoggerTemplate = `
<q-page>
<q-page padding>
<q-card>
<div>
<q-table
:data="logArray"
:columns="[{name:'logTime', field: 'time', label: 'Time', align: 'left'}, {name:'logSource', field: 'source', label: 'Source', align: 'left'}, {name:'logLevel', field: 'level', label: 'Level', align: 'left'}, {name:'logMessage', field: 'message', label: 'Message', align: 'left'}]"
row-key="name"
:pagination="pagination"
hide-bottom
>
<q-td slot="body-cell-logTime" slot-scope="props" :props="props">
{{ formatDate(props.value) }}
</q-td>
</q-table>
</div>
</q-card>
<!--
<div class="logger">
<table class="log-entries">
<tr>
@ -74,6 +95,7 @@ const (
</tr>
</table>
</div>
-->
</q-page>
`
)

View File

@ -115,7 +115,7 @@ const (
<div class="col-12">
<q-card>
<q-card-title>
USB Gadget Settings
USB Gadget Settings ({{ deploying }})
</q-card-title>
<q-card-main>

View File

@ -359,19 +359,23 @@ const templateBluetoothPage = `
</q-card>
</div>
<!--
<div class="col-12">
{{ CurrentControllerInfo }}
</div>
-->
<div class="col-12 col-lg">
<bluetooth-controller :controllerInfo="CurrentControllerInfo"></bluetooth-controller>
</div>
<div class="col-12 col-lg">
<bluetooth-controller-network-services :controllerInfo="CurrentControllerInfo"></bluetooth-controller-network-services>
</div>
<div class="col-12 col-lg">
<bluetooth-agent :bluetoothAgent="CurrentBluetoothAgentSettings"></bluetooth-agent>
<div class="row gutter-y-sm">
<div class="col-12">
<bluetooth-controller-network-services :controllerInfo="CurrentControllerInfo"></bluetooth-controller-network-services>
</div>
<div class="col-12">
<bluetooth-agent :bluetoothAgent="CurrentBluetoothAgentSettings"></bluetooth-agent>
</div>
</div>
</div>
</div>
</q-page>

View File

@ -234,7 +234,7 @@ type jsLogEvent struct {
EvLogSource string `js:"source"`
EvLogLevel int `js:"level"`
EvLogMessage string `js:"message"`
EvLogTime string `js:"time"`
EvLogTime int64 `js:"time"`
}
//HID event
@ -247,7 +247,7 @@ type jsHidEvent struct {
Result string `js:"result"`
Error string `js:"error"`
Message string `js:"message"`
EvLogTime string `js:"time"`
EvLogTime int64 `js:"time"`
}
func (jsEv *jsEvent) toLogEvent() (res *jsLogEvent, err error) {
@ -273,10 +273,11 @@ func (jsEv *jsEvent) toLogEvent() (res *jsLogEvent, err error) {
return nil, eNoLogEvent
}
res.EvLogTime, ok = jsEv.Values[3].(string)
res.EvLogTime, ok = jsEv.Values[3].(int64)
if !ok {
return nil, eNoLogEvent
}
println("EvLogTime", res.EvLogTime)
return res, nil
}
@ -323,7 +324,7 @@ func (jsEv *jsEvent) toHidEvent() (res *jsHidEvent, err error) {
return nil, eNoHidEvent
}
res.EvLogTime, ok = jsEv.Values[7].(string)
res.EvLogTime, ok = jsEv.Values[7].(int64)
if !ok {
return nil, eNoHidEvent
}
@ -341,7 +342,7 @@ type jsHidJobState struct {
LastMessage string `js:"lastMessage"`
TextResult string `js:"textResult"`
// TextError string `js:"textError"`
LastUpdateTime string `js:"lastUpdateTime"` //JSON timestamp from server
LastUpdateTime int64 `js:"lastUpdateTime"` //JSON timestamp from server
ScriptSource string `js:"textSource"`
}
@ -392,7 +393,7 @@ func NewHIDJobStateList() *jsHidJobStateList {
// state list, directly, but instead uses the "Vue.set()" method to update the object, while making vue aware of it.
// This means: THE "UpdateEntry" METHOD RELIES ON THE PRESENCE OF THE "Vue" OBJECT IN JAVASCRIPT GLOBAL SCOPE. This again
// means Vue.JS has to be loaded, BEFORE THIS METHOD IS CALLED"
func (jl *jsHidJobStateList) UpdateEntry(id, vmId int64, hasFailed, hasSucceeded bool, message, textResult, lastUpdateTime, scriptSource string) {
func (jl *jsHidJobStateList) UpdateEntry(id, vmId int64, hasFailed, hasSucceeded bool, message string, textResult string, lastUpdateTime int64, scriptSource string) {
key := strconv.Itoa(int(id))
//Check if job exists, update existing one if already present
@ -998,29 +999,10 @@ func (data *jsEventProcessor) handleHidEvent(hEv *jsHidEvent) {
data.JobList.UpdateEntry(hEv.JobId, hEv.VMId, hEv.HasError, false, hEv.Message, hEv.Error, hEv.EvLogTime, "")
QuasarNotifyError("HIDScript job " + strconv.Itoa(int(hEv.JobId)) + " failed", hEv.Error, QUASAR_NOTIFICATION_POSITION_TOP)
/*
notification := &QuasarNotification{Object: O()}
notification.Message = "HIDScript job " + strconv.Itoa(int(hEv.JobId)) + " failed"
notification.Detail = hEv.Error
notification.Position = QUASAR_NOTIFICATION_POSITION_TOP
notification.Type = QUASAR_NOTIFICATION_TYPE_NEGATIVE
notification.Timeout = 5000
QuasarNotify(notification)
*/
case common_web.HidEventType_JOB_SUCCEEDED:
data.JobList.UpdateEntry(hEv.JobId, hEv.VMId, hEv.HasError, true, hEv.Message, hEv.Result, hEv.EvLogTime, "")
QuasarNotifySuccess("HIDScript job " + strconv.Itoa(int(hEv.JobId)) + " succeeded", hEv.Result, QUASAR_NOTIFICATION_POSITION_TOP)
/*
notification := &QuasarNotification{Object: O()}
notification.Message = "HIDScript job " + strconv.Itoa(int(hEv.JobId)) + " succeeded"
notification.Detail = hEv.Result
notification.Position = QUASAR_NOTIFICATION_POSITION_TOP
notification.Type = QUASAR_NOTIFICATION_TYPE_POSITIVE
notification.Timeout = 5000
QuasarNotify(notification)
*/
case common_web.HidEventType_JOB_CANCELLED:
data.JobList.UpdateEntry(hEv.JobId, hEv.VMId, true, false, hEv.Message, hEv.Message, hEv.EvLogTime, "")
default:

View File

@ -47,19 +47,18 @@ func Router(router *js.Object) hvue.ComponentOption {
func main() {
println(GetBaseURL())
println("====================---------")
store := InitGlobalState() //sets Vuex store in JS window.store
store.Dispatch(VUEX_ACTION_START_EVENT_LISTEN)
// RpcClient.StartListening() //Start event listening after global state is initiated (contains the event handlers)
// ToDo: delete because debug
// RpcClient.GetAllDeployedEthernetInterfaceSettings(time.Second*10)
router := NewVueRouter("/usb",
VueRouterRoute("/usb","", "<usb-settings></usb-settings>"),
router := NewVueRouter(
"/usb",
// route below could be used for an easter egg
//VueRouterRoute("/","", "<usb-settings></usb-settings>"),
VueRouterRoute("/usb","", "<usb-settings></usb-settings>"),
VueRouterRoute("/hid","", "<hid-script></hid-script>"),
VueRouterRoute("/hidjobs","", "<hid-job-event-overview></hid-job-event-overview>"),
VueRouterRoute("/logger","", "<logger :max-entries='7'></logger>"),
@ -88,25 +87,7 @@ println("====================---------")
vm := hvue.NewVM(
hvue.El("#app"),
hvue.Template(templateMainApp),
/*
//add "testString" to data
hvue.DataFunc(func(vm *hvue.VM) interface{} {
data := struct{
*js.Object
TestString string `js:"testString"`
SelectedTab string `js:"selectedTab"`
}{Object: O()}
data.SelectedTab = "USB"
data.TestString = "type('hello');"
return &data
}),
*/
//add console to app as computed property, to allow debug output on vue events
hvue.Computed(
"console",
func(vm *hvue.VM) interface{} {
return js.Global.Get("console")
}),
hvue.Computed("state", func(vm *hvue.VM) interface{} {
return vm.Get("$store").Get("state") //works only with Vuex store option added
}),
@ -118,7 +99,6 @@ println("====================---------")
)
// ToDo: remove next lines, debug code
js.Global.Set("vm",vm)
js.Global.Set("rpc", RpcClient)
}
const templateMainApp = `
@ -132,13 +112,13 @@ const templateMainApp = `
</q-toolbar>
<q-tabs>
<q-route-tab default slot="title" to="usb" name="tab-usb" icon="usb" label="USB Settings"></q-route-tab>
<q-route-tab slot="title" to="hid" name="tab-hid-script" icon="code" label="HIDScript"></q-route-tab>
<q-route-tab slot="title" to="hidjobs" name="tab-hid-jobs" icon="schedule" label="HID Events"></q-route-tab>
<q-route-tab slot="title" to="logger" name="tab-logger" icon="message" label="Event Log"></q-route-tab>
<q-route-tab slot="title" to="network" name="tab-network" icon="settings_ethernet" label="Network settings"></q-route-tab>
<q-route-tab slot="title" to="wifi" name="tab-wifi" icon="wifi" label="WiFi settings"></q-route-tab>
<q-route-tab slot="title" to="triggeractions" name="tab-triggeraction" icon="whatshot" label="Trigger Actions"></q-route-tab>
<q-route-tab slot="title" to="bluetooth" name="tab-bluetooth" icon="bluetooth" label="Bluetooth"></q-route-tab>
<q-route-tab slot="title" to="network" name="tab-network" icon="settings_ethernet" label="Network settings"></q-route-tab>
<q-route-tab slot="title" to="triggeractions" name="tab-triggeraction" icon="whatshot" label="Trigger Actions"></q-route-tab>
<q-route-tab slot="title" to="hid" name="tab-hid-script" icon="keyboard" label="HIDScript"></q-route-tab>
<!-- <q-route-tab slot="title" to="hidjobs" name="tab-hid-jobs" icon="schedule" label="HID Events"></q-route-tab> -->
<q-route-tab slot="title" to="logger" name="tab-logger" icon="message" label="Event Log"></q-route-tab>
</q-tabs>
</q-layout-header>
@ -155,14 +135,6 @@ const templateMainApp = `
<router-view></router-view>
<disconnect-modal :value="!$store.getters.isConnected"></disconnect-modal>
<!--
<q-modal v-model="!$store.getters.isConnected" minimized no-route-dismiss no-esc-dismiss no-backdrop-dismiss>
<div style="padding: 50px">
<div class="q-display-1 q-mb-md">No connection to server</div>
<p>Trying to reconnect ... (attempt {{ $store.state.failedConnectionAttempts }})</p>
</div>
</q-modal>
-->
</q-page-container>

View File

@ -12,6 +12,7 @@ import (
"github.com/pkg/errors"
"io"
"path/filepath"
"strconv"
"strings"
"time"
)
@ -45,12 +46,14 @@ const (
//HIDScripts and jobs
VUEX_ACTION_UPDATE_RUNNING_HID_JOBS = "updateRunningHidJobs"
VUEX_ACTION_REMOVE_SUCCEEDED_HID_JOBS = "removeSucceededHidJobs"
VUEX_ACTION_REMOVE_FAILED_HID_JOBS = "removeFailedHidJobs"
VUEX_ACTION_REMOVE_SUCCEEDED_HID_JOBS = "removeSucceededHidJobs"
VUEX_ACTION_REMOVE_FAILED_HID_JOBS = "removeFailedHidJobs"
VUEX_ACTION_UPDATE_STORED_HID_SCRIPTS_LIST = "updateStoredHIDScriptsList"
VUEX_ACTION_UPDATE_CURRENT_HID_SCRIPT_SOURCE_FROM_REMOTE_FILE = "updateCurrentHidScriptSourceFromRemoteFile"
VUEX_ACTION_STORE_CURRENT_HID_SCRIPT_SOURCE_TO_REMOTE_FILE = "storeCurrentHidScriptSourceToRemoteFile"
VUEX_ACTION_CANCEL_HID_JOB = "cancelHIDJob"
VUEX_ACTION_CANCEL_HID_JOB = "cancelHIDJob"
VUEX_ACTION_CANCEL_ALL_HID_JOBS = "cancelAllHIDJobs"
VUEX_ACTION_AND_AND_RUN_HID_SCRIPT = "sendAndRunHIDScript"
VUEX_MUTATION_SET_CURRENT_HID_SCRIPT_SOURCE_TO = "setCurrentHIDScriptSource"
VUEX_MUTATION_SET_STORED_HID_SCRIPTS_LIST = "setStoredHIDScriptsList"
@ -155,6 +158,7 @@ func createGlobalStateStruct() GlobalState {
state.Title = "P4wnP1 by MaMe82"
state.CurrentHIDScriptSource = initHIDScript
state.CurrentGadgetSettings = NewUSBGadgetSettings()
state.CurrentlyDeployingGadgetSettings = false
state.CurrentlyDeployingWifiSettings = false
state.HidJobList = NewHIDJobStateList()
state.TriggerActionList = NewTriggerActionSet()
@ -204,7 +208,7 @@ func processEvent(evt *pb.Event, store *mvuex.Store, state *GlobalState) {
case common_web.STATE_CHANGE_EVT_TYPE_NETWORK:
store.Dispatch(VUEX_ACTION_UPDATE_ALL_ETHERNET_INTERFACE_SETTINGS)
case common_web.STATE_CHANGE_EVT_TYPE_HID:
store.Dispatch(VUEX_ACTION_UPDATE_RUNNING_HID_JOBS) // handled by dedicated listener
//store.Dispatch(VUEX_ACTION_UPDATE_RUNNING_HID_JOBS) // handled by dedicated listener
case common_web.STATE_CHANGE_EVT_TYPE_WIFI:
store.Dispatch(VUEX_ACTION_UPDATE_WIFI_STATE)
case common_web.STATE_CHANGE_EVT_TYPE_TRIGGER_ACTIONS:
@ -254,9 +258,34 @@ func actionUpdateAllStates(store *mvuex.Store, context *mvuex.ActionContext, sta
}
func actionSendAndRunHIDScript(store *mvuex.Store, context *mvuex.ActionContext, state *GlobalState, scriptContent *js.Object) {
go func() {
strScriptContent := scriptContent.String()
println("Send and run HIDScript job")
//fetch deployed gadget settings
filename,err := RpcClient.UploadContentToTempFile(defaultTimeout, []byte(strScriptContent))
if err != nil {
println("Couldn't upload HIDScript job", err)
QuasarNotifyError("Error uploading script", err.Error(), QUASAR_NOTIFICATION_POSITION_TOP)
return
}
job,err := RpcClient.RunHIDScriptJob(defaultTimeout, "/tmp/" + filename)
if err != nil {
println("Couldn't start HIDScript job", err)
QuasarNotifyError("Error starting script as background job", err.Error(), QUASAR_NOTIFICATION_POSITION_TOP)
return
}
QuasarNotifySuccess("Script started successfully", "Job ID " + strconv.Itoa(int(job.Id)), QUASAR_NOTIFICATION_POSITION_TOP)
// ToDo: update HIDScriptJob list (should be done event based)
}()
return
}
func actionCancelHidJob(store *mvuex.Store, context *mvuex.ActionContext, state *GlobalState, jobID *js.Object) {
go func() {
id := uint32(jobID.Int())
println("Cancel HIDScript job", id)
@ -269,8 +298,21 @@ func actionCancelHidJob(store *mvuex.Store, context *mvuex.ActionContext, state
// ToDo: update HIDScriptJob list (should be done event based)
}()
return
}
func actionCancelAllHidJobs(store *mvuex.Store, context *mvuex.ActionContext, state *GlobalState) {
go func() {
println("Cancel all HIDScript jobs")
//fetch deployed gadget settings
err := RpcClient.CancelAllHIDScriptJobs(defaultTimeout)
if err != nil {
println("Couldn't cancel all HIDScript jobs", err)
return
}
// ToDo: update HIDScriptJob list (should be done event based)
}()
return
}
@ -932,7 +974,8 @@ func actionUpdateRunningHidJobs(store *mvuex.Store, context *mvuex.ActionContext
for _, jobstate := range jobstates {
println("updateing jobstate", jobstate)
state.HidJobList.UpdateEntry(jobstate.Id, jobstate.VmId, false, false, "initial job state", "", time.Now().String(), jobstate.Source)
timeNowUnixMilli := time.Now().UnixNano()/1e6
state.HidJobList.UpdateEntry(jobstate.Id, jobstate.VmId, false, false, "initial job state", "", timeNowUnixMilli, jobstate.Source)
}
}()
@ -1115,6 +1158,7 @@ func actionDeployCurrentGadgetSettings(store *mvuex.Store, context *mvuex.Action
notification.Timeout = 2000
QuasarNotify(notification)
return
}()
return
@ -1267,9 +1311,12 @@ func initMVuex() *mvuex.Store {
mvuex.Action(VUEX_ACTION_START_EVENT_LISTEN, actionStartEventListen),
mvuex.Action(VUEX_ACTION_STOP_EVENT_LISTEN, actionStopEventListen),
mvuex.Action(VUEX_ACTION_REMOVE_SUCCEEDED_HID_JOBS, actionRemoveSucceededHidJobs),
mvuex.Action(VUEX_ACTION_REMOVE_FAILED_HID_JOBS, actionRemoveFailedHidJobs),
mvuex.Action(VUEX_ACTION_CANCEL_HID_JOB, actionCancelHidJob),
mvuex.Action(VUEX_ACTION_CANCEL_ALL_HID_JOBS, actionCancelAllHidJobs),
mvuex.Action(VUEX_ACTION_AND_AND_RUN_HID_SCRIPT, actionSendAndRunHIDScript),
mvuex.Getter("triggerActions", func(state *GlobalState) interface{} {
@ -1327,6 +1374,9 @@ func initMVuex() *mvuex.Store {
}),
)
store.Dispatch(VUEX_ACTION_START_EVENT_LISTEN)
/*
// fetch deployed gadget settings
store.Dispatch(VUEX_ACTION_UPDATE_CURRENT_USB_SETTINGS)
@ -1335,14 +1385,14 @@ func initMVuex() *mvuex.Store {
// Update WiFi state
store.Dispatch(VUEX_ACTION_UPDATE_WIFI_STATE)
// propagate Vuex store to global scope to allow injecting it to Vue by setting the "store" option
js.Global.Set("store", store)
*/
return store
}
func InitGlobalState() *mvuex.Store {
return initMVuex()
store := initMVuex()
// propagate Vuex store to global scope to allow injecting it to Vue by setting the "store" option
js.Global.Set("store", store)
return store
}

View File

@ -29,6 +29,40 @@ func NewRpcClient(addr string) Rpc {
return rcl
}
func (rpc *Rpc) UploadContentToTempFile(timeout time.Duration, content []byte) (filename string, err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
//create hex string of content MD5 sum
filename = BytesToMD5(content)
//upload file to `/tmp/{md5_hash_hex}`
_,err = rpc.Client.FSWriteFile(ctx,
&pb.WriteFileRequest{
Data:content,
Append:false,
Filename:filename,
Folder: pb.AccessibleFolder_TMP,
MustNotExist:false,
})
return
}
func (rpc *Rpc) RunHIDScriptJob(timeout time.Duration, filepath string) (job *pb.HIDScriptJob, err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
//upload file to `/tmp/{md5_hash_hex}`
return rpc.Client.HIDRunScriptJob(
ctx,
&pb.HIDScriptRequest{
ScriptPath: filepath,
TimeoutSeconds: uint32(0),
},
)
}
func (rpc *Rpc) CancelHIDScriptJob(timeout time.Duration, jobID uint32) (err error) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()