v1.0.9 changes from 1.1.1
This commit is contained in:
@@ -1,52 +1,43 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of the Buildbotics firmware. #
|
||||
# #
|
||||
# Copyright (c) 2015 - 2018, Buildbotics LLC #
|
||||
# All rights reserved. #
|
||||
# #
|
||||
# This file ("the software") is free software: you can redistribute it #
|
||||
# and/or modify it under the terms of the GNU General Public License, #
|
||||
# version 2 as published by the Free Software Foundation. You should #
|
||||
# have received a copy of the GNU General Public License, version 2 #
|
||||
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# The software is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
# Lesser General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU Lesser General Public #
|
||||
# License along with the software. If not, see #
|
||||
# <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# For information regarding this software email: #
|
||||
# "Joseph Coffland" <joseph@buildbotics.com> #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import bbctrl
|
||||
import glob
|
||||
import html
|
||||
import tornado
|
||||
from tornado import gen
|
||||
from tornado.web import HTTPError
|
||||
from tornado.escape import url_unescape
|
||||
|
||||
|
||||
def safe_remove(path):
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError: pass
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
@tornado.web.stream_request_body
|
||||
class FileHandler(bbctrl.APIHandler):
|
||||
def prepare(self): pass
|
||||
def prepare(self):
|
||||
if self.request.method == 'PUT':
|
||||
self.request.connection.set_max_body_size(2 ** 30)
|
||||
|
||||
filename = self.request.path.split('/')[-1]
|
||||
self.uploadFilename = url_unescape(filename) \
|
||||
.replace('\\', '/') \
|
||||
.replace('#', '-') \
|
||||
.replace('?', '-')
|
||||
|
||||
self.uploadFile = tempfile.NamedTemporaryFile("wb")
|
||||
|
||||
def data_received(self, data):
|
||||
if self.request.method == 'PUT':
|
||||
self.uploadFile.write(data)
|
||||
|
||||
def delete_ok(self, filename):
|
||||
if not filename:
|
||||
# Delete everything
|
||||
for path in glob.glob(self.get_upload('*')): safe_remove(path)
|
||||
for path in glob.glob(self.get_upload('*')):
|
||||
safe_remove(path)
|
||||
self.get_ctrl().preplanner.delete_all_plans()
|
||||
self.get_ctrl().state.clear_files()
|
||||
|
||||
@@ -57,26 +48,29 @@ class FileHandler(bbctrl.APIHandler):
|
||||
self.get_ctrl().preplanner.delete_plans(filename)
|
||||
self.get_ctrl().state.remove_file(filename)
|
||||
|
||||
|
||||
def put_ok(self, *args):
|
||||
gcode = self.request.files['gcode'][0]
|
||||
filename = os.path.basename(gcode['filename'].replace('\\', '/'))
|
||||
filename = filename.replace('#', '-').replace('?', '-')
|
||||
if not os.path.exists(self.get_upload()):
|
||||
os.mkdir(self.get_upload())
|
||||
|
||||
if not os.path.exists(self.get_upload()): os.mkdir(self.get_upload())
|
||||
filename = self.get_upload(self.uploadFilename).encode('utf8')
|
||||
safe_remove(filename)
|
||||
os.link(self.uploadFile.name, filename)
|
||||
|
||||
with open(self.get_upload(filename).encode('utf8'), 'wb') as f:
|
||||
f.write(gcode['body'])
|
||||
os.sync()
|
||||
self.uploadFile.close()
|
||||
|
||||
self.get_ctrl().preplanner.invalidate(filename)
|
||||
self.get_ctrl().state.add_file(filename)
|
||||
self.get_log('FileHandler').info('GCode received: ' + filename)
|
||||
del (self.uploadFile)
|
||||
|
||||
self.get_ctrl().preplanner.invalidate(self.uploadFilename)
|
||||
self.get_ctrl().state.add_file(self.uploadFilename)
|
||||
self.get_log('FileHandler').info(
|
||||
'GCode received: ' + self.uploadFilename)
|
||||
|
||||
del (self.uploadFilename)
|
||||
|
||||
@gen.coroutine
|
||||
def get(self, filename):
|
||||
if not filename: raise HTTPError(400, 'Missing filename')
|
||||
if not filename:
|
||||
raise HTTPError(400, 'Missing filename')
|
||||
filename = os.path.basename(filename)
|
||||
|
||||
try:
|
||||
@@ -84,6 +78,7 @@ class FileHandler(bbctrl.APIHandler):
|
||||
self.write(f.read())
|
||||
except Exception:
|
||||
self.get_ctrl().state.select_file('')
|
||||
raise HTTPError(400, "Unable to read file - doesn't appear to be GCode.")
|
||||
raise HTTPError(
|
||||
400, "Unable to read file - doesn't appear to be GCode.")
|
||||
|
||||
self.get_ctrl().state.select_file(filename)
|
||||
|
||||
@@ -1,30 +1,3 @@
|
||||
################################################################################
|
||||
# #
|
||||
# This file is part of the Buildbotics firmware. #
|
||||
# #
|
||||
# Copyright (c) 2015 - 2018, Buildbotics LLC #
|
||||
# All rights reserved. #
|
||||
# #
|
||||
# This file ("the software") is free software: you can redistribute it #
|
||||
# and/or modify it under the terms of the GNU General Public License, #
|
||||
# version 2 as published by the Free Software Foundation. You should #
|
||||
# have received a copy of the GNU General Public License, version 2 #
|
||||
# along with the software. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# The software is distributed in the hope that it will be useful, but #
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU #
|
||||
# Lesser General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU Lesser General Public #
|
||||
# License along with the software. If not, see #
|
||||
# <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# For information regarding this software email: #
|
||||
# "Joseph Coffland" <joseph@buildbotics.com> #
|
||||
# #
|
||||
################################################################################
|
||||
|
||||
import os
|
||||
import json
|
||||
import tornado
|
||||
@@ -34,9 +7,9 @@ import subprocess
|
||||
import socket
|
||||
from tornado.web import HTTPError
|
||||
from tornado import gen
|
||||
|
||||
import re
|
||||
import bbctrl
|
||||
|
||||
from urllib.request import urlopen
|
||||
|
||||
|
||||
def call_get_output(cmd):
|
||||
@@ -75,7 +48,7 @@ def check_password(password):
|
||||
class RebootHandler(bbctrl.APIHandler):
|
||||
def put_ok(self):
|
||||
self.get_ctrl().lcd.goodbye('Rebooting...')
|
||||
subprocess.Popen('reboot')
|
||||
subprocess.Popen(['reboot'])
|
||||
|
||||
class ShutdownHandler(bbctrl.APIHandler):
|
||||
def put_ok(self):
|
||||
@@ -153,6 +126,30 @@ class HostnameHandler(bbctrl.APIHandler):
|
||||
raise HTTPError(400, 'Failed to set hostname')
|
||||
|
||||
|
||||
class NetworkData(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
try:
|
||||
ipAddresses = subprocess.P(
|
||||
"ip -4 addr | grep -oE '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'", shell=True).decode().split()
|
||||
ipAddresses.remove("127.0.0.1")
|
||||
regex = re.compile(r'/255$/')
|
||||
filtered = [i for i in ipAddresses if not regex.match(i)]
|
||||
ipAddresses = filtered[0]
|
||||
except:
|
||||
ipAddresses = "Not Connected"
|
||||
try:
|
||||
wifi = subprocess.check_output(
|
||||
"sudo iw dev wlan0 info | grep ssid", shell=True).decode().split()
|
||||
wifi.pop(0)
|
||||
wifiName = " ".join(wifi)
|
||||
except:
|
||||
wifi = "not connected"
|
||||
self.write_json({
|
||||
'ipAddresses': ipAddresses,
|
||||
'wifi': wifiName
|
||||
})
|
||||
|
||||
class WifiHandler(bbctrl.APIHandler):
|
||||
def get(self):
|
||||
data = {'ssid': '', 'channel': 0}
|
||||
@@ -189,6 +186,7 @@ class WifiHandler(bbctrl.APIHandler):
|
||||
raise HTTPError(400, 'Failed to configure wifi')
|
||||
|
||||
|
||||
|
||||
class UsernameHandler(bbctrl.APIHandler):
|
||||
def get(self): self.write_json(get_username())
|
||||
|
||||
@@ -407,6 +405,97 @@ class JogHandler(bbctrl.APIHandler):
|
||||
self.get_ctrl().mach.jog(self.json)
|
||||
|
||||
|
||||
class ScreenRotationHandler(bbctrl.APIHandler):
|
||||
|
||||
@gen.coroutine
|
||||
def get(self):
|
||||
with open("/boot/config.txt", 'rt') as config:
|
||||
lines = config.readlines()
|
||||
for line in lines:
|
||||
if line.startswith('display_rotate'):
|
||||
self.write_json({
|
||||
'rotated':
|
||||
int(displayRotatePattern.search(line).group(1)) != 0
|
||||
})
|
||||
return
|
||||
|
||||
self.write_json({'rotated': False})
|
||||
return
|
||||
|
||||
@gen.coroutine
|
||||
def put_ok(self):
|
||||
rotated = self.json['rotated']
|
||||
|
||||
subprocess.Popen([
|
||||
'/usr/local/bin/edit-boot-config',
|
||||
'display_rotate={}'.format(2 if rotated else 0)
|
||||
])
|
||||
|
||||
with open("/usr/share/X11/xorg.conf.d/40-libinput.conf",
|
||||
'rt') as config:
|
||||
text = config.read()
|
||||
text = transformationMatrixPattern.sub(r'\1\2\3\5', text)
|
||||
if rotated:
|
||||
text = matchIsTouchscreenPattern.sub(
|
||||
r'\1\2\3\2Option "TransformationMatrix" "-1 0 1 0 -1 1 0 0 1"\1\4',
|
||||
text)
|
||||
with open("/usr/share/X11/xorg.conf.d/40-libinput.conf",
|
||||
'wt') as config:
|
||||
config.write(text)
|
||||
|
||||
subprocess.run('reboot')
|
||||
|
||||
|
||||
class TimeHandler(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
timeinfo = call_get_output(['timedatectl'])
|
||||
timezones = call_get_output(
|
||||
['timedatectl', 'list-timezones', '--no-pager'])
|
||||
self.get_log('TimeHandler').info('Time stuff: {}, {}'.format(
|
||||
timeinfo, timezones))
|
||||
|
||||
self.write_json({'timeinfo': timeinfo, 'timezones': timezones})
|
||||
|
||||
def put_ok(self):
|
||||
datetime = self.json['datetime']
|
||||
timezone = self.json['timezone']
|
||||
subprocess.Popen(['timedatectl', 'set-time', datetime])
|
||||
subprocess.Popen(['timedatectl', 'set-timezone', timezone])
|
||||
|
||||
|
||||
class RemoteDiagnosticsHandler(bbctrl.APIHandler):
|
||||
|
||||
def get(self):
|
||||
code = self.get_query_argument("code", "")
|
||||
command = self.get_query_argument("command", "")
|
||||
|
||||
log = self.get_log('RemoteDiagnostics')
|
||||
|
||||
if command == "disconnect":
|
||||
subprocess.Popen(['killall', 'ngrok'])
|
||||
self.write_json({'message': "Succesfully disconnected"})
|
||||
|
||||
if command == "connect":
|
||||
try:
|
||||
url = 'https://tinyurl.com/1f-remote?code={}'.format(code)
|
||||
with urlopen(url) as response:
|
||||
body = response.read()
|
||||
|
||||
os.makedirs("/tmp/ngrok", exist_ok=True)
|
||||
with open("/tmp/ngrok/1f-ngrok.sh", 'wb') as f:
|
||||
f.write(body)
|
||||
|
||||
subprocess.Popen(['/bin/bash', "/tmp/ngrok/1f-ngrok.sh"])
|
||||
self.write_json({'success': True})
|
||||
except Exception as e:
|
||||
log.info("Failed: {}".format(str(e)))
|
||||
self.write_json({
|
||||
'success': False,
|
||||
'code': e.code or None,
|
||||
'message': e.reason or "Unknown"
|
||||
})
|
||||
|
||||
# Base class for Web Socket connections
|
||||
class ClientConnection(object):
|
||||
def __init__(self, app):
|
||||
@@ -517,7 +606,8 @@ class Web(tornado.web.Application):
|
||||
(r'/api/reboot', RebootHandler),
|
||||
(r'/api/shutdown', ShutdownHandler),
|
||||
(r'/api/hostname', HostnameHandler),
|
||||
(r'/api/wifi', WifiHandler),
|
||||
(r'/api/wifi', NetworkData),
|
||||
(r'/api/network', WifiHandler),
|
||||
(r'/api/remote/username', UsernameHandler),
|
||||
(r'/api/remote/password', PasswordHandler),
|
||||
(r'/api/config/load', ConfigLoadHandler),
|
||||
@@ -544,6 +634,9 @@ class Web(tornado.web.Application):
|
||||
(r'/api/modbus/write', ModbusWriteHandler),
|
||||
(r'/api/jog', JogHandler),
|
||||
(r'/api/video', bbctrl.VideoHandler),
|
||||
(r'/api/screen-rotation', ScreenRotationHandler),
|
||||
(r'/api/time', TimeHandler),
|
||||
(r'/api/remote-diagnostics', RemoteDiagnosticsHandler),
|
||||
(r'/(.*)', StaticFileHandler,
|
||||
{'path': bbctrl.get_resource('http/'),
|
||||
'default_filename': 'index.html'}),
|
||||
|
||||
Reference in New Issue
Block a user