Several improvements and rearranged files
This commit is contained in:
parent
d88766ddc0
commit
b0329fd8ce
|
@ -2,3 +2,9 @@
|
||||||
HamClock Remote Access - access HamClock remotely
|
HamClock Remote Access - access HamClock remotely
|
||||||
|
|
||||||
This is currently in early development.
|
This is currently in early development.
|
||||||
|
|
||||||
|
## Client
|
||||||
|
The client is in the `client` directory.
|
||||||
|
|
||||||
|
## Server
|
||||||
|
The server is in the `server` directory.
|
||||||
|
|
11
app.py
11
app.py
|
@ -1,11 +0,0 @@
|
||||||
from flask import Flask, request, Response
|
|
||||||
import os
|
|
||||||
import requests
|
|
||||||
import base64
|
|
||||||
#import hcapi
|
|
||||||
app = Flask(__name__)
|
|
||||||
|
|
||||||
@app.route('/client')
|
|
||||||
def client():
|
|
||||||
with open('client.html') as f:
|
|
||||||
return f.read()
|
|
125
client.html
125
client.html
|
@ -1,125 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>HamClock</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
background-color: black;
|
|
||||||
}
|
|
||||||
#errorbox {
|
|
||||||
color: white;
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 200%;
|
|
||||||
text-align: center
|
|
||||||
}
|
|
||||||
#holder {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
text-align: center
|
|
||||||
}
|
|
||||||
#holder * {
|
|
||||||
margin: 0 auto;
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id=login>
|
|
||||||
<h1>HamClock Remote Access</h1>
|
|
||||||
<form>
|
|
||||||
<label for=url>Server URL:</label><input name=url id=url><br>
|
|
||||||
<label for=password>Password:</label><input name=password id=password>
|
|
||||||
<button id=start>Start</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<div id=holder style="display:none">
|
|
||||||
<div id=errorbox style="width: 800px; height: 480px">Loading...</div>
|
|
||||||
<canvas id=canvas width=800 height=480 style="width: 800px; height: 480px; display: none"></canvas>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
let canvas = document.querySelector('canvas')
|
|
||||||
let errorbox = document.querySelector('#errorbox')
|
|
||||||
let login = document.querySelector('#login')
|
|
||||||
let holder = document.querySelector('#holder')
|
|
||||||
let url_el = document.querySelector('#url')
|
|
||||||
let password_el = document.querySelector('#password')
|
|
||||||
let start_button = document.querySelector('#start')
|
|
||||||
let displayWidth, displayHeight
|
|
||||||
function resize() {
|
|
||||||
let bodyWidth = window.innerWidth
|
|
||||||
let maxWidth = Math.min(bodyWidth - 60, 800)
|
|
||||||
let bodyHeight = window.innerHeight
|
|
||||||
let maxHeight = Math.min(bodyHeight - 60, 480)
|
|
||||||
let equivalentHeight = maxWidth * 0.6
|
|
||||||
if (equivalentHeight >= maxHeight) {
|
|
||||||
displayHeight = Math.round(maxHeight)
|
|
||||||
displayWidth = Math.round(maxHeight * (5/3))
|
|
||||||
} else {
|
|
||||||
displayWidth = Math.round(maxWidth)
|
|
||||||
displayHeight = Math.round(maxWidth * 0.6)
|
|
||||||
}
|
|
||||||
canvas.style.width = displayWidth + 'px'
|
|
||||||
canvas.style.height = displayHeight + 'px'
|
|
||||||
//canvas.style.marginTop = '-' + displayHeight/2 + 'px'
|
|
||||||
errorbox.style.width = displayWidth + 'px'
|
|
||||||
errorbox.style.height = displayHeight + 'px'
|
|
||||||
//errorbox.style.marginTop = '-' + displayHeight/2 + 'px'
|
|
||||||
}
|
|
||||||
resize()
|
|
||||||
window.onresize = resize
|
|
||||||
let ctx = canvas.getContext("2d")
|
|
||||||
canvas.addEventListener('click', function (e) {
|
|
||||||
x = e.layerX
|
|
||||||
y = e.layerY
|
|
||||||
w = displayWidth
|
|
||||||
ws.send(localStorage.getItem('password') + ' ' + x + ' ' + y + ' ' + w)
|
|
||||||
})
|
|
||||||
let ws
|
|
||||||
function setup() {
|
|
||||||
ws = new WebSocket(localStorage.getItem('url'))
|
|
||||||
ws.onmessage = function(e) {
|
|
||||||
let type, pos, x, y, data, img, code, msg
|
|
||||||
type = e.data.split('%')[0]
|
|
||||||
if ( type == 'pic' ) {
|
|
||||||
pos = e.data.split('%')[1]
|
|
||||||
x = Number(pos.split('x')[0])
|
|
||||||
y = Number(pos.split('x')[1])
|
|
||||||
data = e.data.split('%')[2]
|
|
||||||
img = new Image()
|
|
||||||
img.src = data
|
|
||||||
img.addEventListener("load", function(){
|
|
||||||
ctx.drawImage(img,x,y)
|
|
||||||
})
|
|
||||||
errorbox.style.display = 'none'
|
|
||||||
canvas.style.display = 'block'
|
|
||||||
} else if ( type == 'err' ) {
|
|
||||||
code = e.data.split('%')[1]
|
|
||||||
msg = e.data.split('%')[2]
|
|
||||||
errorbox.textContent = msg
|
|
||||||
errorbox.style.display = 'block'
|
|
||||||
canvas.style.display = 'none'
|
|
||||||
console.error('Server reported error: ' + code + ': ' + msg)
|
|
||||||
} else if (type == 'ack') {
|
|
||||||
ws.send('ack')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ws.onerror = setup
|
|
||||||
ws.onclose = setup
|
|
||||||
}
|
|
||||||
start.onclick = function(e) {
|
|
||||||
e.preventDefault()
|
|
||||||
holder.style.display = 'block'
|
|
||||||
login.style.display = 'none'
|
|
||||||
localStorage.setItem('url', url_el.value)
|
|
||||||
localStorage.setItem('password', password_el.value)
|
|
||||||
setup()
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
48
client/client.css
Normal file
48
client/client.css
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
body {
|
||||||
|
background-color: black;
|
||||||
|
color: white;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 16pt;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
background-color: #222222;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
font-size: 16pt;
|
||||||
|
text-align: left !important;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
background-color: #00FF00;
|
||||||
|
border: none;
|
||||||
|
font-size: 16pt;
|
||||||
|
}
|
||||||
|
.loginline {
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
form {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
form * {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
#errorbox {
|
||||||
|
font-size: 200%;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
#holder {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
text-align: center
|
||||||
|
}
|
||||||
|
#holder * {
|
||||||
|
margin: 0 auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
#errorbox * {
|
||||||
|
position: static;
|
||||||
|
}
|
22
client/client.html
Normal file
22
client/client.html
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>HamClock</title>
|
||||||
|
<link rel=stylesheet href=client.css>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id=login>
|
||||||
|
<h1>HamClock Remote Access</h1>
|
||||||
|
<form>
|
||||||
|
<div class=loginline><label for=url>Server URL:</label> <input name=url id=url></div>
|
||||||
|
<div class=loginline><label for=password>Password:</label> <input name=password id=password type=password></div>
|
||||||
|
<div class=loginline><button id=start>Start</button></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div id=holder style="display:none">
|
||||||
|
<div id=errorbox style="width: 800px; height: 480px">Loading...</div>
|
||||||
|
<canvas id=canvas width=800 height=480 style="width: 800px; height: 480px; display: none"></canvas>
|
||||||
|
</div>
|
||||||
|
<script src=client.js></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
122
client/client.js
Normal file
122
client/client.js
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
function resize() {
|
||||||
|
let bodyWidth = window.innerWidth
|
||||||
|
let maxWidth = Math.min(bodyWidth - 60, 800)
|
||||||
|
let bodyHeight = window.innerHeight
|
||||||
|
let maxHeight = Math.min(bodyHeight - 60, 480)
|
||||||
|
let equivalentHeight = maxWidth * 0.6
|
||||||
|
if (equivalentHeight >= maxHeight) {
|
||||||
|
displayHeight = Math.round(maxHeight)
|
||||||
|
displayWidth = Math.round(maxHeight * (5/3))
|
||||||
|
} else {
|
||||||
|
displayWidth = Math.round(maxWidth)
|
||||||
|
displayHeight = Math.round(maxWidth * 0.6)
|
||||||
|
}
|
||||||
|
canvas.style.width = displayWidth + 'px'
|
||||||
|
canvas.style.height = displayHeight + 'px'
|
||||||
|
errorbox.style.width = displayWidth + 'px'
|
||||||
|
errorbox.style.height = displayHeight + 'px'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
ws = new WebSocket(localStorage.getItem('url'))
|
||||||
|
ws.onmessage = function(e) {
|
||||||
|
window.m = e
|
||||||
|
let type, pos, x, y, data, img, code, msg
|
||||||
|
type = e.data.split('%')[0]
|
||||||
|
if ( type == 'pic' ) {
|
||||||
|
pos = e.data.split('%')[1]
|
||||||
|
x = Number(pos.split('x')[0])
|
||||||
|
y = Number(pos.split('x')[1])
|
||||||
|
data = e.data.split('%')[2]
|
||||||
|
img = new Image()
|
||||||
|
img.src = data
|
||||||
|
img.addEventListener("load", function(){
|
||||||
|
ctx.drawImage(img,x,y)
|
||||||
|
})
|
||||||
|
errorbox.style.display = 'none'
|
||||||
|
canvas.style.display = 'block'
|
||||||
|
} else if ( type == 'err' ) {
|
||||||
|
code = e.data.split('%')[1]
|
||||||
|
msg = e.data.split('%')[2]
|
||||||
|
errorbox.textContent = msg
|
||||||
|
errorbox.style.display = 'block'
|
||||||
|
canvas.style.display = 'none'
|
||||||
|
console.error('Server reported error: ' + code + ': ' + msg)
|
||||||
|
if (code[0] == '*') {
|
||||||
|
errorbox.innerHTML = msg + '<br>Please refresh page'
|
||||||
|
ws.onclose = function(e){}
|
||||||
|
ws.onerror = function(e){}
|
||||||
|
}
|
||||||
|
} else if (type == 'ack') {
|
||||||
|
ws.send('ack')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ws.onerror = function(e){
|
||||||
|
errorbox.innerHTML = 'Connection lost<br>Please refresh page'
|
||||||
|
errorbox.style.display = 'block'
|
||||||
|
canvas.style.display = 'none'
|
||||||
|
}
|
||||||
|
ws.onclose = function(e){
|
||||||
|
errorbox.innerHTML = 'Connection lost<br>Please refresh page'
|
||||||
|
errorbox.style.display = 'block'
|
||||||
|
canvas.style.display = 'none'
|
||||||
|
}
|
||||||
|
setInterval(function(){ws.send('ack')}, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function touch(e) {
|
||||||
|
click_timeout = setTimeout(function() {
|
||||||
|
is_short = false
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function release(e) {
|
||||||
|
clearTimeout(click_timeout)
|
||||||
|
x = e.layerX
|
||||||
|
y = e.layerY
|
||||||
|
w = displayWidth
|
||||||
|
length = is_short?0:1
|
||||||
|
ws.send('touch ' + localStorage.getItem('password') + ' ' + x + ' ' + y + ' ' + w + ' ' + length)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function connect(e) {
|
||||||
|
e.preventDefault()
|
||||||
|
holder.style.display = 'block'
|
||||||
|
login.style.display = 'none'
|
||||||
|
localStorage.setItem('url', url_el.value)
|
||||||
|
localStorage.setItem('password', password_el.value)
|
||||||
|
setup()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let canvas = document.querySelector('canvas')
|
||||||
|
let errorbox = document.querySelector('#errorbox')
|
||||||
|
|
||||||
|
let login = document.querySelector('#login')
|
||||||
|
let holder = document.querySelector('#holder')
|
||||||
|
|
||||||
|
let url_el = document.querySelector('#url')
|
||||||
|
let password_el = document.querySelector('#password')
|
||||||
|
let start_button = document.querySelector('#start')
|
||||||
|
|
||||||
|
let is_short = true
|
||||||
|
let click_timeout
|
||||||
|
|
||||||
|
let ctx = canvas.getContext("2d")
|
||||||
|
|
||||||
|
let displayWidth, displayHeight
|
||||||
|
|
||||||
|
let ws
|
||||||
|
|
||||||
|
|
||||||
|
resize()
|
||||||
|
window.onresize = resize
|
||||||
|
|
||||||
|
canvas.onmousedown = touch
|
||||||
|
canvas.onmouseup = release
|
||||||
|
|
||||||
|
start.onclick = connect
|
41
server/hcapi.py
Normal file
41
server/hcapi.py
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import os
|
||||||
|
import requests
|
||||||
|
import threading
|
||||||
|
import json
|
||||||
|
import time
|
||||||
|
|
||||||
|
def get_img():
|
||||||
|
os.system('xwd -root -silent | convert xwd:- img.bmp')
|
||||||
|
|
||||||
|
with open('crops.json') as f:
|
||||||
|
crops = json.load(f)
|
||||||
|
|
||||||
|
threads = []
|
||||||
|
for crop in crops:
|
||||||
|
threads.append(threading.Thread(target=os.system, args=(f'convert img.bmp -crop {crop} pieces/{crop.split("+", 1)[1].replace("+", "x")}.jpg',)))
|
||||||
|
threads[-1].start()
|
||||||
|
for thread in threads:
|
||||||
|
thread.join()
|
||||||
|
|
||||||
|
os.system("md5sum -c oldlist 2>/dev/null | grep FAILED > newlist")
|
||||||
|
changed = []
|
||||||
|
with open('newlist') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
for line in lines:
|
||||||
|
changed.append(line.split(": ")[0].split('.')[0].split('/')[1])
|
||||||
|
os.system("md5sum pieces/* > oldlist")
|
||||||
|
return changed
|
||||||
|
|
||||||
|
def get_full_img():
|
||||||
|
os.system('xwd -root -silent | convert xwd:- full.jpg')
|
||||||
|
return 'full.jpg'
|
||||||
|
|
||||||
|
def touch(x, y, w, is_long):
|
||||||
|
x = round(800 * x / w)
|
||||||
|
y = round(800 * y / w)
|
||||||
|
if is_long:
|
||||||
|
os.system(f'xdotool mousemove {x} {y} mousedown 1')
|
||||||
|
time.sleep(2)
|
||||||
|
os.system(f'xdotool mouseup 1')
|
||||||
|
else:
|
||||||
|
os.system(f'xdotool mousemove {x} {y} click 1')
|
|
@ -11,11 +11,6 @@ def get_img():
|
||||||
|
|
||||||
os.system('convert full_img.bmp -resize 800x480 img.bmp')
|
os.system('convert full_img.bmp -resize 800x480 img.bmp')
|
||||||
|
|
||||||
#crops = []
|
|
||||||
#for n in range(10):
|
|
||||||
# for m in range(6):
|
|
||||||
# crops.append(f'80x80+{n*80}+{m*80}')
|
|
||||||
#print(crops)
|
|
||||||
with open('crops.json') as f:
|
with open('crops.json') as f:
|
||||||
crops = json.load(f)
|
crops = json.load(f)
|
||||||
|
|
||||||
|
@ -47,7 +42,7 @@ def get_full_img():
|
||||||
|
|
||||||
return 'full.jpg'
|
return 'full.jpg'
|
||||||
|
|
||||||
def touch(x, y, w):
|
def touch(x, y, w, is_long):
|
||||||
x = round(800 * x / w)
|
x = round(800 * x / w)
|
||||||
y = round(800 * y / w)
|
y = round(800 * y / w)
|
||||||
requests.get(f'http://localhost:8080/set_touch?x={x}&y={y}&hold={0}')
|
requests.get(f'http://localhost:8080/set_touch?x={x}&y={y}&hold={1 if is_long else 0}')
|
|
@ -33,21 +33,33 @@ class Client:
|
||||||
self.server = server
|
self.server = server
|
||||||
self.queue = queue.Queue()
|
self.queue = queue.Queue()
|
||||||
self.items = {}
|
self.items = {}
|
||||||
|
self.lock = threading.Lock()
|
||||||
self.good = True
|
self.good = True
|
||||||
|
|
||||||
def send(self, item, name):
|
def send(self, item, name):
|
||||||
|
with self.lock:
|
||||||
self.items[name] = item
|
self.items[name] = item
|
||||||
|
|
||||||
def ack(self):
|
def ack(self):
|
||||||
|
with self.lock:
|
||||||
|
try:
|
||||||
self.server.send_message(self.client, 'ack')
|
self.server.send_message(self.client, 'ack')
|
||||||
self.good = False
|
self.good = False
|
||||||
|
except BrokenPipeError:
|
||||||
|
pass
|
||||||
|
|
||||||
def cycle(self):
|
def cycle(self):
|
||||||
|
with self.lock:
|
||||||
|
try:
|
||||||
while not self.good:
|
while not self.good:
|
||||||
pass
|
pass
|
||||||
for name, item in self.items.items():
|
for name, item in list(self.items.items()):
|
||||||
|
if not self.good:
|
||||||
|
break
|
||||||
self.server.send_message(self.client, item)
|
self.server.send_message(self.client, item)
|
||||||
del self.items[name]
|
del self.items[name]
|
||||||
|
except BrokenPipeError:
|
||||||
|
pass
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while True:
|
while True:
|
||||||
|
@ -55,9 +67,7 @@ class Client:
|
||||||
clients = {}
|
clients = {}
|
||||||
|
|
||||||
def do_img(imgname):
|
def do_img(imgname):
|
||||||
#server.send_message_to_all(img(imgname))
|
|
||||||
for client in clients.values():
|
for client in clients.values():
|
||||||
print(client)
|
|
||||||
client.send(img(imgname), imgname)
|
client.send(img(imgname), imgname)
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,6 +80,12 @@ def img(imgname):
|
||||||
return response
|
return response
|
||||||
|
|
||||||
def new_client(client, server):
|
def new_client(client, server):
|
||||||
|
if clients:
|
||||||
|
server.send_message(client, 'err%*inuse%Server already in use')
|
||||||
|
client['handler'].send_text("", opcode=0x8)
|
||||||
|
return
|
||||||
|
|
||||||
|
clients[client['id']] = Client(client, server)
|
||||||
try:
|
try:
|
||||||
imgname = hcapi.get_full_img()
|
imgname = hcapi.get_full_img()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -91,17 +107,24 @@ def do_touch(client, server, message):
|
||||||
if action == 'ack':
|
if action == 'ack':
|
||||||
clients[client['id']].good = True
|
clients[client['id']].good = True
|
||||||
else:
|
else:
|
||||||
_, password, x, y, w = message.split(' ')
|
_, password, x, y, w, is_long = message.split(' ')
|
||||||
if password == 'password':
|
if password == 'password':
|
||||||
x, y, w = int(x), int(y), int(w)
|
x, y, w, is_long = int(x), int(y), int(w), bool(is_long)
|
||||||
hcapi.touch(x, y, w)
|
hcapi.touch(x, y, w, is_long)
|
||||||
else:
|
else:
|
||||||
clients[client['id']].send(f'badpass', 'BADPASS')
|
clients[client['id']].send(f'badpass', 'BADPASS')
|
||||||
|
|
||||||
|
def client_left(client, server):
|
||||||
|
clients[client['id']].good = False
|
||||||
|
del clients[client['id']]
|
||||||
|
|
||||||
def do_cycles():
|
def do_cycles():
|
||||||
while True:
|
while True:
|
||||||
|
if clients:
|
||||||
cycle()
|
cycle()
|
||||||
time.sleep(0.2)
|
time.sleep(0.4)
|
||||||
|
else:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
tmp = tempfile.mkdtemp(prefix="HCRA-")
|
tmp = tempfile.mkdtemp(prefix="HCRA-")
|
||||||
try:
|
try:
|
||||||
|
@ -112,6 +135,7 @@ try:
|
||||||
threading.Thread(target=do_cycles).start()
|
threading.Thread(target=do_cycles).start()
|
||||||
server.set_fn_new_client(new_client)
|
server.set_fn_new_client(new_client)
|
||||||
server.set_fn_message_received(do_touch)
|
server.set_fn_message_received(do_touch)
|
||||||
|
server.set_fn_client_left(client_left)
|
||||||
server.run_forever()
|
server.run_forever()
|
||||||
finally:
|
finally:
|
||||||
os.chdir('/')
|
os.chdir('/')
|
Loading…
Reference in New Issue
Block a user