Modified boilerplate code for web-client to allow changing USB gadget settings

This commit is contained in:
mame82 2018-07-16 17:15:25 +00:00
parent b7d0885fd6
commit 579c2cb2ca
12 changed files with 19733 additions and 32148 deletions

View File

@ -51,6 +51,7 @@ LOGGING:
OTHER:
- extend installer to movee HIDScripts to a fixed absolute path
- proper error extraction from gRPC calls
TO FIX:
- debug out of HIDScript puts way to much CPU load on journaling daemon (floods logs)

View File

@ -1,4 +1,4 @@
#!/bin/bash
# dependencies for the web app
gopherjs build -o ../www/webapp.js main.go
gopherjs build -o ../www/webapp.js #main.go

View File

@ -1,8 +1,18 @@
<html>
<head>
<title>gRPC test</title>
<head>
<title>P4wnP1 by MaMe82</title>
<link rel="stylesheet" type="text/css" href="p4wnp1.css">
</head>
<body>
<script src="main.js"></script>
</body>
</html>
<body>
<h1>P4wnP1 service - USB settings</h1>
<p>See Javascript console for details, entry script is webapp.js (generated by gopherjs)</p>
<div id="app">
<usb-settings></usb-settings>
</div>
<script type="text/javascript" src="webapp.js"></script>
</body>
</html>

View File

@ -8,35 +8,52 @@ import (
pb "../proto/gopherjs"
dom "honnef.co/go/js/dom"
"github.com/oskca/gopherjs-vue"
"github.com/gopherjs/gopherjs/js"
)
var (
document = dom.GetWindow().Document().(dom.HTMLDocument)
serverAddr = "http://raspberrypi.local"
serverAddr = GetBaseURL()
Client = pb.NewP4WNP1Client(
serverAddr + ":80",
)
GS *pb.GadgetSettings
)
func main() {
fmt.Println("Hello")
func GetBaseURL() string {
document := js.Global.Get("window").Get("document")
location := document.Get("location")
port := location.Get("port").String()
url := location.Get("protocol").String() + "//" + location.Get("hostname").String()
if len(port) > 0 {
url = url + ":" + port
}
return url
}
func main() {
println(GetBaseURL())
client := pb.NewP4WNP1Client(
"http://raspberrypi.local:80",
)
fmt.Printf("Address %v\n", strings.TrimSuffix(document.BaseURI(), "/"))
fmt.Printf("Client %v\n", client)
fmt.Printf("Client %v\n", Client)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
gs, err := client.GetDeployedGadgetSetting(ctx, &pb.Empty{})
gs, err := Client.GetDeployedGadgetSetting(ctx, &pb.Empty{})
if err == nil {
str:=fmt.Sprintf("Gs: %+v\n", gs)
fmt.Println(str)
div_cont:= dom.GetWindow().Document().GetElementByID("content").(*dom.HTMLDivElement)
new_div := dom.GetWindow().Document().CreateElement("div").(*dom.HTMLDivElement)
new_div.SetTextContent(fmt.Sprintf("Result of GetDeployedGadgetSetting gRPC-web call:\n%s ",str))
div_cont.AppendChild(new_div)
//export Gadget setting
js.Global.Set("gs", gs)
GS = gs
} else {
fmt.Printf("Error rpc call: %v\n", err)
}
vue.NewComponent(New, template).Register("usb-settings")
vm := vue.New("#app", new(controller))
js.Global.Set("vm", vm)
println("vm:", vm)
}

150
web_client/p4wnp1.css Normal file
View File

@ -0,0 +1,150 @@
/*
<label class="toggle-switch">
<input type="checkbox">
<div>
<span class="on">On</span>
<span class="off">Off</span>
</div>
<span class="toggle-switch-slider"></span>
</label>
*/
.toggle-switch {
position: relative;
display: block;
width: 80px;
height: 30px;
background-color: gray;
background: linear-gradient(to top, #f4f4f4, #9e9e9e 20%);
border-radius: 99999px;
/* box-shadow: h-offset v-offset blur spread color inset initial inherit */
box-shadow: 0 2px 0 0 #FFFFFF, 0 -2px 0 0 #f4f4f4; /* 2px offset gray to top, white to bottom */
}
.toggle-switch input[type="checkbox"] {
display: none;
}
.toggle-switch div {
top: 50%;
left: 50%;
width: 80%;
height: 60%;
border-radius: 99999px;
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
position: absolute;
background-color: #707070;
box-shadow: inset 0 0 10px 0 rgba(0, 0, 0, 0.7);
}
.toggle-switch input[type="checkbox"]:checked ~ div {
/* box-shadow: h-offset v-offset blur spread color inset initial inherit */
box-shadow: inset 0 0 9px 0 rgba(0, 100, 0, 0.8),0 0 11px 1px rgb(150,255,0);
background-color: rgb(150,250,0);
}
.toggle-switch input[type="checkbox"] ~ .toggle-switch-slider {
display: block;
width: 30px;
height: 30px;
position: absolute;
background: linear-gradient(to top, #9e9e9e 20%, #f4f4f4);
border-radius: 50%;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.7);
top: 0;
left: 0;
/*
transition: all 0.3s ease-in 0s;
*/
transition: .25s;
}
.toggle-switch input[type="checkbox"] ~ .toggle-switch-slider:after {
content: "";
display: block;
left: 15%;
top: 15%;
width: 70%;
height: 70%;
border-radius: 50%;
position: absolute;
background: linear-gradient(to top, #f4f4f4, #9e9e9e);
/*
z-index: 1;
*/
}
.toggle-switch input[type="checkbox"]:checked ~ .toggle-switch-slider {
-moz-transform: translate(-100%, 0);
-ms-transform: translate(-100%, 0);
-webkit-transform: translate(-100%, 0);
transform: translate(-100%, 0);
left: 100%;
}
.toggle-switch div > .on,.off {
position: absolute;
/*
background-color: #34A7C1;
*/
top: 50%;
text-transform: uppercase;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
font-size: 0.8em;
font-weight: 600;
z-index: 2;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
letter-spacing: 1px;
transition: .25s;
}
.on {
left: 10%;
color: transparent;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0);
}
.toggle-switch input[type="checkbox"]:checked ~ div > .on {
color: rgb(0,154,0);
text-shadow: 0px 1px 0px rgba(27,76,37, 0.5);
}
.off {
right: 10%;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
}
.toggle-switch input[type="checkbox"]:checked + div > .off {
color: transparent;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0);
}

View File

@ -0,0 +1,333 @@
package main
import (
"github.com/gopherjs/gopherjs/js"
pb "../proto/gopherjs"
"fmt"
"time"
"context"
"google.golang.org/grpc/status"
)
type VGadgetSettings struct {
*js.Object
Enabled bool `js:"Enabled"`
Vid string `js:"Vid"`
Pid string `js:"Pid"`
Manufacturer string `js:"Manufacturer"`
Product string `js:"Product"`
Serial string `js:"Serial"`
Use_CDC_ECM bool `js:"Use_CDC_ECM"`
Use_RNDIS bool `js:"Use_RNDIS"`
Use_HID_KEYBOARD bool `js:"Use_HID_KEYBOARD"`
Use_HID_MOUSE bool `js:"Use_HID_MOUSE"`
Use_HID_RAW bool `js:"Use_HID_RAW"`
Use_UMS bool `js:"Use_UMS"`
Use_SERIAL bool `js:"Use_SERIAL"`
RndisSettings *VGadgetSettingsEthernet `js:"RndisSettings"`
CdcEcmSettings *VGadgetSettingsEthernet `js:"CdcEcmSettings"`
UmsSettings *VGadgetSettingsUMS `js:"UmsSettings"`
}
type VGadgetSettingsEthernet struct {
*js.Object
HostAddr string `js:"HostAddr"`
DevAddr string `js:"DevAddr"`
}
type VGadgetSettingsUMS struct {
*js.Object
Cdrom bool `js:"Cdrom"`
File string `js:"File"`
}
func (vGS VGadgetSettings) toGS() (gs *pb.GadgetSettings) {
return &pb.GadgetSettings{
Serial: vGS.Serial,
Use_SERIAL: vGS.Use_SERIAL,
Use_UMS: vGS.Use_UMS,
Use_HID_RAW: vGS.Use_HID_RAW,
Use_HID_MOUSE: vGS.Use_HID_MOUSE,
Use_HID_KEYBOARD: vGS.Use_HID_KEYBOARD,
Use_RNDIS: vGS.Use_RNDIS,
Use_CDC_ECM: vGS.Use_CDC_ECM,
Product: vGS.Product,
Manufacturer: vGS.Manufacturer,
Vid: vGS.Vid,
Pid: vGS.Pid,
Enabled: vGS.Enabled,
UmsSettings: &pb.GadgetSettingsUMS{
Cdrom: vGS.UmsSettings.Cdrom,
File: vGS.UmsSettings.File,
},
CdcEcmSettings: &pb.GadgetSettingsEthernet{
DevAddr: vGS.CdcEcmSettings.DevAddr,
HostAddr: vGS.CdcEcmSettings.HostAddr,
},
RndisSettings: &pb.GadgetSettingsEthernet{
DevAddr: vGS.RndisSettings.DevAddr,
HostAddr: vGS.RndisSettings.HostAddr,
},
}
}
func (vGS *VGadgetSettings) fromGS(gs *pb.GadgetSettings) {
vGS.Enabled = gs.Enabled
vGS.Vid = gs.Vid
vGS.Pid = gs.Pid
vGS.Manufacturer = gs.Manufacturer
vGS.Product = gs.Product
vGS.Serial = gs.Serial
vGS.Use_CDC_ECM = gs.Use_CDC_ECM
vGS.Use_RNDIS = gs.Use_RNDIS
vGS.Use_HID_KEYBOARD = gs.Use_HID_KEYBOARD
vGS.Use_HID_MOUSE = gs.Use_HID_MOUSE
vGS.Use_HID_RAW = gs.Use_HID_RAW
vGS.Use_UMS = gs.Use_UMS
vGS.Use_SERIAL = gs.Use_SERIAL
vGS.RndisSettings = &VGadgetSettingsEthernet{
Object: js.Global.Get("Object").New(),
}
if gs.RndisSettings != nil {
vGS.RndisSettings.HostAddr = gs.RndisSettings.HostAddr
vGS.RndisSettings.DevAddr = gs.RndisSettings.DevAddr
}
vGS.CdcEcmSettings = &VGadgetSettingsEthernet{
Object: js.Global.Get("Object").New(),
}
if gs.CdcEcmSettings != nil {
vGS.CdcEcmSettings.HostAddr = gs.CdcEcmSettings.HostAddr
vGS.CdcEcmSettings.DevAddr = gs.CdcEcmSettings.DevAddr
}
vGS.UmsSettings = &VGadgetSettingsUMS{
Object: js.Global.Get("Object").New(),
}
if gs.UmsSettings != nil {
vGS.UmsSettings.File = gs.UmsSettings.File
vGS.UmsSettings.Cdrom = gs.UmsSettings.Cdrom
}
}
// Note: internalize wouldn't work on this, as the nested structs don't translate back
type Com struct {
*js.Object
GadgetSettings *VGadgetSettings `js:"gadgetSettings"`
}
func (c *Com) UodateToDeployedGadgetSettings() {
//gs := vue.GetVM(c).Get("gadgetSettings")
println("Trying to fetch deployed GadgetSettings")
go func() {
ctx,cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
fmt.Printf("Before client + request\n")
deployedGs, err := pb.NewP4WNP1Client(serverAddr).GetDeployedGadgetSetting(ctx, &pb.Empty{})
if err != nil { fmt.Println(err); return }
newGs := &VGadgetSettings{
Object: js.Global.Get("Object").New(),
}
newGs.fromGS(deployedGs)
c.GadgetSettings = newGs
}()
}
func (c *Com) ApplyGadgetSettings() {
//gs := vue.GetVM(c).Get("gadgetSettings")
println("Trying to deploy GadgetSettings: " + fmt.Sprintf("%+v",c.GadgetSettings.toGS()))
gs:=c.GadgetSettings.toGS()
go func() {
//ToDo: set apply button to inactive
//ToDo: defer set apply button to active
ctx,cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
client := pb.NewP4WNP1Client(serverAddr)
//Set gadget settings
settedGs, err := client.SetGadgetSettings(ctx, gs)
if err != nil {
js.Global.Call("alert", "Error setting given gadget settings: " + status.Convert(err).Message())
fmt.Println(err)
c.UodateToDeployedGadgetSettings()
return
}
println(fmt.Sprintf("The following GadgetSettings have been set: %+v", settedGs))
//deploy the settings
deployedGs,err := client.DeployGadgetSetting(ctx, &pb.Empty{})
if err != nil {
js.Global.Call("alert", "Error deploying gadget settings: " + status.Convert(err).Message())
fmt.Println(err)
c.UodateToDeployedGadgetSettings()
return
}
println(fmt.Sprintf("The following GadgetSettings have been deployed: %+v", deployedGs))
js.Global.Call("alert", "New USB gadget settings deployed ")
newGs := &VGadgetSettings{
Object: js.Global.Get("Object").New(),
}
newGs.fromGS(deployedGs)
c.GadgetSettings = newGs
}()
}
func New() interface{} {
cc := &Com{
Object: js.Global.Get("Object").New(),
}
cc.GadgetSettings = &VGadgetSettings{
Object: js.Global.Get("Object").New(),
}
cc.UodateToDeployedGadgetSettings()
fmt.Printf("Client: %+v\n", Client)
fmt.Printf("cc.gadgetSettings: %+v\n", cc.GadgetSettings)
fmt.Printf("GS.Vid: %+v\n", GS.Vid)
fmt.Printf("cc.gadgetSettings.Vid: %+v\n", cc.GadgetSettings.Vid)
return cc
}
type controller struct {
*js.Object
}
const (
template = `
<div>
<table>
<tr> <td>USB gadget settings</td><td><button @click="ApplyGadgetSettings">Apply</button></td> </tr>
<tr>
<td>Gadget enabled</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Enabled">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
<tr> <td>Vendor ID</td><td><input v-model="gadgetSettings.Vid"/></td> </tr>
<tr> <td>Product ID</td><td><input v-model="gadgetSettings.Pid"/></td> </tr>
<tr> <td>Manufacturer Name</td><td><input v-model="gadgetSettings.Manufacturer"/></td> </tr>
<tr> <td>Product Name</td><td><input v-model="gadgetSettings.Product"/></td> </tr>
<tr> <td>Serial number</td><td><input v-model="gadgetSettings.Serial"/></td> </tr>
<tr>
<td>CDC ECM</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_CDC_ECM">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
<tr>
<td>RNDIS</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_RNDIS">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
<tr>
<td>HID Keyboard</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_HID_KEYBOARD">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
<tr>
<td>HID Mouse</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_HID_MOUSE">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
<tr>
<td>HID Raw</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_HID_RAW">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
<tr>
<td>Serial</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_SERIAL">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
<tr>
<td>Mass Storage</td>
<td>
<label class="toggle-switch">
<input type="checkbox" v-model="gadgetSettings.Use_UMS">
<div><span class="on">On</span><span class="off">Off</span></div>
<span class="toggle-switch-slider"></span>
</label>
</td>
</tr>
</table>
`
)

View File

@ -1,11 +1,18 @@
<html>
<head><title>Hello</title></head>
<head>
<title>P4wnP1 by MaMe82</title>
<link rel="stylesheet" type="text/css" href="p4wnp1.css">
</head>
<body>
<script src="webapp.js"></script>
<h1>Testpage for P4wnP1 service</h1>
<h1>P4wnP1 service - USB settings</h1>
<p>See Javascript console for details, entry script is webapp.js (generated by gopherjs)</p>
<div id="content"></div>
<div id="app">
<usb-settings></usb-settings>
</div>
<script type="text/javascript" src="webapp.js"></script>
</body>
</html>

194
www/p4wnp1.css Normal file
View File

@ -0,0 +1,194 @@
/*
<label class="toggle-switch">
<input type="checkbox">
<div>
<span class="on">On</span>
<span class="off">Off</span>
</div>
<span class="toggle-switch-slider"></span>
</label>
*/
html {
height: 100%;
}
body {
font-family: "Open Sans", sans-serif;
font-size: 1em;
line-height: 1.6;
background: linear-gradient(45deg, #9e9e9e, #c4c4c4 66%, #9e9e9e);
}
input {
/*border-radius: 99999px;*/
background-color: #707070;
background: linear-gradient(to top, #9e9e9e, #f4f4f4);
height: 30px;
box-shadow: inset 0 0 10px 0 rgba(0, 0, 0, 0.7);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
font-size: 0.9em;
font-weight: 600;
}
button {
border-radius: 99999px;
background-color: #707070;
background: linear-gradient(to top, #f4f4f4, #9e9e9e);
height: 30px;
width: 100px;
text-transform: uppercase;
font-size: 0.9em;
font-weight: 800;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
letter-spacing: 1px;
}
.toggle-switch {
position: relative;
display: block;
width: 80px;
height: 30px;
background-color: gray;
background: linear-gradient(to top, #f4f4f4, #9e9e9e 20%);
border-radius: 99999px;
/* box-shadow: h-offset v-offset blur spread color inset initial inherit */
box-shadow: 1px 1px 1px 0 #3f3f3f, -1px -1px 1px 0 #ffffff; /* 2px offset gray to top, white to bottom */
cursor: pointer;
}
.toggle-switch input[type="checkbox"] {
display: none;
}
.toggle-switch div {
top: 50%;
left: 50%;
width: 80%;
height: 60%;
border-radius: 99999px;
-moz-transform: translate(-50%, -50%);
-ms-transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
position: absolute;
background-color: #707070;
box-shadow: inset 0 0 10px 0 rgba(0, 0, 0, 0.7);
}
.toggle-switch input[type="checkbox"]:checked ~ div {
/* box-shadow: h-offset v-offset blur spread color inset initial inherit */
box-shadow: inset 0 0 9px 0 rgba(0, 100, 0, 0.8),0 0 11px 1px rgb(150,255,0);
background-color: rgb(150,250,0);
}
.toggle-switch input[type="checkbox"] ~ .toggle-switch-slider {
display: block;
width: 30px;
height: 30px;
position: absolute;
background: linear-gradient(to top, #9e9e9e 20%, #f4f4f4);
border-radius: 50%;
box-shadow: 0 5px 10px 0 rgba(0, 0, 0, 0.7);
top: 0;
left: 0;
/*
transition: all 0.3s ease-in 0s;
*/
transition: .25s;
}
.toggle-switch input[type="checkbox"] ~ .toggle-switch-slider:after {
content: "";
display: block;
left: 15%;
top: 15%;
width: 70%;
height: 70%;
border-radius: 50%;
position: absolute;
background: linear-gradient(to top, #f4f4f4, #9e9e9e);
/*
z-index: 1;
*/
}
.toggle-switch input[type="checkbox"]:checked ~ .toggle-switch-slider {
-moz-transform: translate(-100%, 0);
-ms-transform: translate(-100%, 0);
-webkit-transform: translate(-100%, 0);
transform: translate(-100%, 0);
left: 100%;
}
.toggle-switch div > .on,.off {
position: absolute;
/*
background-color: #34A7C1;
*/
top: 50%;
text-transform: uppercase;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
font-size: 0.8em;
font-weight: 600;
z-index: 2;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
letter-spacing: 1px;
transition: .25s;
}
.on {
left: 10%;
color: transparent;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0);
}
.toggle-switch input[type="checkbox"]:checked ~ div > .on {
color: rgb(0,154,0);
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
}
.off {
right: 10%;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.3);
}
.toggle-switch input[type="checkbox"]:checked + div > .off {
color: transparent;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0);
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long