Several improvements and rearranged files

This commit is contained in:
Samuel Sloniker 2021-06-17 14:33:40 -07:00
parent d88766ddc0
commit b0329fd8ce
10 changed files with 280 additions and 158 deletions

View File

@ -2,3 +2,9 @@
HamClock Remote Access - access HamClock remotely
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
View File

@ -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()

View File

@ -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
View 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
View 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
View 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
View 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')

View File

@ -11,11 +11,6 @@ def get_img():
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:
crops = json.load(f)
@ -47,7 +42,7 @@ def get_full_img():
return 'full.jpg'
def touch(x, y, w):
def touch(x, y, w, is_long):
x = round(800 * x / 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}')

View File

@ -33,21 +33,33 @@ class Client:
self.server = server
self.queue = queue.Queue()
self.items = {}
self.lock = threading.Lock()
self.good = True
def send(self, item, name):
with self.lock:
self.items[name] = item
def ack(self):
with self.lock:
try:
self.server.send_message(self.client, 'ack')
self.good = False
except BrokenPipeError:
pass
def cycle(self):
with self.lock:
try:
while not self.good:
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)
del self.items[name]
except BrokenPipeError:
pass
def run(self):
while True:
@ -55,9 +67,7 @@ class Client:
clients = {}
def do_img(imgname):
#server.send_message_to_all(img(imgname))
for client in clients.values():
print(client)
client.send(img(imgname), imgname)
@ -70,6 +80,12 @@ def img(imgname):
return response
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:
imgname = hcapi.get_full_img()
except Exception as e:
@ -91,17 +107,24 @@ def do_touch(client, server, message):
if action == 'ack':
clients[client['id']].good = True
else:
_, password, x, y, w = message.split(' ')
_, password, x, y, w, is_long = message.split(' ')
if password == 'password':
x, y, w = int(x), int(y), int(w)
hcapi.touch(x, y, w)
x, y, w, is_long = int(x), int(y), int(w), bool(is_long)
hcapi.touch(x, y, w, is_long)
else:
clients[client['id']].send(f'badpass', 'BADPASS')
def client_left(client, server):
clients[client['id']].good = False
del clients[client['id']]
def do_cycles():
while True:
if clients:
cycle()
time.sleep(0.2)
time.sleep(0.4)
else:
time.sleep(1)
tmp = tempfile.mkdtemp(prefix="HCRA-")
try:
@ -112,6 +135,7 @@ try:
threading.Thread(target=do_cycles).start()
server.set_fn_new_client(new_client)
server.set_fn_message_received(do_touch)
server.set_fn_client_left(client_left)
server.run_forever()
finally:
os.chdir('/')