Added interface for callback events to HID

This commit is contained in:
MaMe82 2018-07-29 02:40:23 +02:00
parent 286b8b9b46
commit 8f553db5c3
3 changed files with 215 additions and 13 deletions

View File

@ -57,13 +57,29 @@ func (job *AsyncOttoJob) ResultJsonString() (string, error) {
return string(json),nil
}
type AsyncOttoVM struct {
vm *otto.Otto
isWorking bool
sync.Mutex
Id int
eventHandler EventHandler
}
func (avm *AsyncOttoVM) HandleEvent(event Event) {
fmt.Printf("!!! AsyncOtto Event: %+v\n", event)
}
func (avm *AsyncOttoVM) SetEventHandler(handler EventHandler) {
avm.eventHandler = handler
}
func (avm *AsyncOttoVM) SetDefaultHandler() {
avm.SetEventHandler(avm)
}
func (avm *AsyncOttoVM) emitEvent(event Event) {
if avm.eventHandler == nil { return }
avm.eventHandler.HandleEvent(event)
}
func NewAsyncOttoVM(vm *otto.Otto) *AsyncOttoVM {
@ -73,6 +89,7 @@ func NewAsyncOttoVM(vm *otto.Otto) *AsyncOttoVM {
vm: vm,
Id: vmNum,
}
res.SetDefaultHandler()
vmNum++
return res
}
@ -133,7 +150,7 @@ func (avm *AsyncOttoVM) RunAsync(ctx context.Context, src interface{}) (job *Asy
}(avm)
go func(avm *AsyncOttoVM) {
defer func() { //runs after avm.vm.Run() returns (because script finished a was interrupted)
defer func() { //runs after avm.vm.Run() returns (because script finished or was interrupted)
defer avm.SetWorking(false)
if caught := recover(); caught != nil {
fmt.Printf("VM %d CAUGHT INTERRUPT, ENDING JOB %d\n", avm.Id, job.Id)
@ -157,17 +174,26 @@ func (avm *AsyncOttoVM) RunAsync(ctx context.Context, src interface{}) (job *Asy
job.ResultValue, job.ResultErr = avm.vm.Run(job.Source) //store result
job.SetFinished() // signal job finished
//DEBUG
//Emit event + print DEBUG
evRes := Event{ Vm: avm, Job: job }
//evRes.ScriptSource,_ = job.Source.(string) //if string, attach source to event
if job.ResultErr == nil {
jRes,jErr := job.ResultJsonString()
if jErr == nil {
fmt.Printf("JOB %d on VM %d SUCCEEDED WITH RESULT: %s\n", job.Id, avm.Id, jRes)
evRes.Type = EventType_JOB_SUCCEEDED
evRes.Message = fmt.Sprintf("JOB %d on VM %d SUCCEEDED WITH RESULT: %s", job.Id, avm.Id, jRes)
fmt.Println(evRes.Message)
} else {
fmt.Printf("JOB %d on VM %d SUCCEEDED BUT RESULT COULDN'T BE MARSHALED TO JSON: %v\n", job.Id, avm.Id, jErr)
evRes.Type = EventType_JOB_SUCCEEDED
evRes.Message = fmt.Sprintf("JOB %d on VM %d SUCCEEDED BUT RESULT COULDN'T BE MARSHALED TO JSON: %v", job.Id, avm.Id, jErr)
fmt.Println(evRes.Message)
}
} else {
fmt.Printf("JOB %d on VM %d FAILED: %v\n", job.Id, avm.Id, job.ResultErr)
evRes.Type = EventType_JOB_FAILED
evRes.Message = fmt.Sprintf("JOB %d on VM %d FAILED: %v", job.Id, avm.Id, job.ResultErr)
fmt.Println(evRes.Message)
}
avm.emitEvent(evRes)
}(avm)

46
hid/HIDEvent.go Normal file
View File

@ -0,0 +1,46 @@
package hid
type EventType int32
const (
EventType_JOB_STARTED EventType = 0
EventType_JOB_STOPPED EventType = 1
EventType_CONTROLLER_ABORTED EventType = 2
EventType_JOB_CANCELLED EventType = 3
EventType_JOB_SUCCEEDED EventType = 4
EventType_JOB_SUCCEEDED_NO_RESULT EventType = 5
EventType_JOB_FAILED EventType = 6
EventType_JOB_WAIT_LED_FINISHED EventType = 7
EventType_JOB_WAIT_LED_REPEATED_FINISHED EventType = 8
EventType_JOB_NO_FREE_VM EventType = 9
)
/*
var EventType_name = map[int32]string{
0: "JOB_STARTED",
1: "JOB_STOPPED",
2: "CONTROLLER_ABORTED",
3: "JOB_CANCELLED",
}
var EventType_value = map[string]int32{
"JOB_STARTED": 0,
"JOB_STOPPED": 1,
"CONTROLLER_ABORTED": 2,
"JOB_CANCELLED": 3,
}
*/
type Event struct {
Type EventType
Job *AsyncOttoJob
Vm *AsyncOttoVM
Message string
//ScriptSource string
}
type EventHandler interface {
HandleEvent(event Event)
}

View File

@ -59,6 +59,7 @@ type HIDController struct {
vmMaster *otto.Otto
ctx context.Context
cancel context.CancelFunc
eventHandler EventHandler
}
func NewHIDController(ctx context.Context, keyboardDevicePath string, keyboardMapPath string, mouseDevicePath string) (ctl *HIDController, err error) {
@ -98,9 +99,30 @@ func NewHIDController(ctx context.Context, keyboardDevicePath string, keyboardMa
if err != nil { return nil, err }
}
hidControllerReuse.SetDefaultHandler()
return hidControllerReuse, nil
}
func (ctl *HIDController) SetEventHandler(handler EventHandler) {
ctl.eventHandler = handler
// set same handler for all child VMs
for _,vm := range ctl.vmPool { vm.SetEventHandler(handler) }
}
func (ctl *HIDController) HandleEvent(event Event) {
fmt.Printf("!!! HID Controller Event: %+v\n", event)
}
func (ctl *HIDController) SetDefaultHandler() {
ctl.SetEventHandler(ctl)
}
func (ctl *HIDController) emitEvent(event Event) {
if ctl.eventHandler == nil { return }
ctl.eventHandler.HandleEvent(event)
}
func (ctl *HIDController) Abort() {
// stop the old LED reader if present
// !! Keyboard.Close() has to be called before sending IRQ to VMs (there're JS function which register
@ -113,6 +135,13 @@ func (ctl *HIDController) Abort() {
// Interrupt all VMs already running
//hidControllerReuse.CancelAllBackgroundJobs()
hidControllerReuse.CancelAllBackgroundJobs()
ctl.emitEvent(Event{
Type: EventType_CONTROLLER_ABORTED,
Message: "Called abort on HID Controller",
Job: nil,
Vm: nil,
})
}
func (ctl *HIDController) NextUnusedVM() (vm *AsyncOttoVM, err error) {
@ -161,15 +190,52 @@ func (ctl *HIDController) GetBackgroundJobByID(id int) (job *AsyncOttoJob, err e
}
}
func (ctl *HIDController) retrieveJobFromOtto(vm *otto.Otto) (job *AsyncOttoJob, runVM *AsyncOttoVM, err error) {
found := false
globalJobListMutex.Lock()
for cJob,_ := range globalJobList {
vme := cJob.executingVM
if vme == nil { continue }
if vme.vm == vm {
found = true
job = cJob
runVM = vme
}
}
globalJobListMutex.Unlock()
if found {
return
} else {
return nil,nil,errors.New("Couldn't retrieve job of this Otto VM")
}
}
func (ctl *HIDController) StartScriptAsBackgroundJob(ctx context.Context,script string) (job *AsyncOttoJob, err error) {
//fetch next free vm from pool
avm,err := ctl.NextUnusedVM()
if err != nil { return nil, err }
if err != nil {
ctl.emitEvent(Event{
Job:job,
Message:"No free Java VM available to run the script",
Type:EventType_JOB_NO_FREE_VM,
})
return nil, err
}
//try to run script async
job,err = avm.RunAsync(ctx,script)
if err != nil { return nil, err }
ctl.emitEvent(Event{
Job:job,
Vm:avm,
Type:EventType_JOB_STARTED,
Message:"Script started",
//ScriptSource:script,
})
//add job to global list
globalJobListMutex.Lock()
globalJobList[job] = true
@ -177,11 +243,19 @@ func (ctl *HIDController) StartScriptAsBackgroundJob(ctx context.Context,script
//ad go routine which monitors context of the job, to remove it from global list, once done or interrupted
go func() {
fmt.Printf("StartScriptAsBackgroundJob: started finish watcher for job %d\n",job.Id)
//fmt.Printf("StartScriptAsBackgroundJob: started finish watcher for job %d\n",job.Id)
select {
case <- job.ctx.Done():
fmt.Printf("StartScriptAsBackgroundJob: Job %d finished or interrupted, removing from global list\n",job.Id)
ctl.emitEvent(Event{
Job:job,
Vm:avm,
Type:EventType_JOB_STOPPED,
Message:"Script stopped",
//ScriptSource:script,
})
//remove job from global list after result has been retrieved
globalJobListMutex.Lock()
delete(globalJobList,job)
@ -223,6 +297,12 @@ 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()
@ -419,7 +499,7 @@ func (ctl *HIDController) jsWaitLED(call otto.FunctionCall) (res otto.Value) {
errStr := ""
if err != nil {errStr = fmt.Sprintf("%v",err)}
res,_ = call.Otto.ToValue(struct{
resStruct := struct{
ERROR bool
ERRORTEXT string
TIMEOUT bool
@ -437,7 +517,32 @@ func (ctl *HIDController) jsWaitLED(call otto.FunctionCall) (res otto.Value) {
SCROLL: err == nil && changed.ScrollLock,
COMPOSE: err == nil && changed.Compose,
KANA: err == nil && changed.Kana,
})
}
res,_ = call.Otto.ToValue(resStruct)
//Event generation
resText := "WaitLED ended:"
if resStruct.ERROR { resText = " ERROR " + errStr} else {
if resStruct.NUM { resText += " NUM" }
if resStruct.CAPS { resText += " CAPS" }
if resStruct.SCROLL { resText += " SCROLL" }
if resStruct.COMPOSE { resText += " COMPOSE" }
if resStruct.KANA { resText += " KANA" }
}
sJob, sVM, err := ctl.retrieveJobFromOtto(call.Otto)
if err == nil {
ctl.emitEvent(Event{
Type: EventType_JOB_WAIT_LED_FINISHED,
Job: sJob,
Vm: sVM,
Message: resText,
})
} else {
ctl.emitEvent(Event{
Type: EventType_JOB_WAIT_LED_FINISHED,
Message: resText + " (unknown Job)",
})
}
return
}
@ -530,7 +635,7 @@ func (ctl *HIDController) jsWaitLEDRepeat(call otto.FunctionCall) (res otto.Valu
errStr := ""
if err != nil {errStr = fmt.Sprintf("%v",err)}
res,_ = call.Otto.ToValue(struct{
resStruct := struct{
ERROR bool
ERRORTEXT string
TIMEOUT bool
@ -548,7 +653,32 @@ func (ctl *HIDController) jsWaitLEDRepeat(call otto.FunctionCall) (res otto.Valu
SCROLL: err == nil && changed.ScrollLock,
COMPOSE: err == nil && changed.Compose,
KANA: err == nil && changed.Kana,
})
}
res,_ = call.Otto.ToValue(resStruct)
//Event generation
resText := "WaitLED ended:"
if resStruct.ERROR { resText = " ERROR " + errStr} else {
if resStruct.NUM { resText += " NUM" }
if resStruct.CAPS { resText += " CAPS" }
if resStruct.SCROLL { resText += " SCROLL" }
if resStruct.COMPOSE { resText += " COMPOSE" }
if resStruct.KANA { resText += " KANA" }
}
sJob, sVM, err := ctl.retrieveJobFromOtto(call.Otto)
if err == nil {
ctl.emitEvent(Event{
Type: EventType_JOB_WAIT_LED_REPEATED_FINISHED,
Job: sJob,
Vm: sVM,
Message: resText,
})
} else {
ctl.emitEvent(Event{
Type: EventType_JOB_WAIT_LED_REPEATED_FINISHED,
Message: resText + " (unknown Job)",
})
}
return
}