import sys import flask import os from flask import make_response, request, jsonify, session from flask_socketio import SocketIO, send, emit from flask_cors import CORS from engineio.payload import Payload import json import time import argparse import logging import math # interface for odrive GUI to get data from odrivetool # Flush stdout by default # Source: # https://stackoverflow.com/questions/230751/how-to-flush-output-of-python-print old_print = print def print(*args, **kwargs): kwargs.pop('flush', False) old_print(*args, **kwargs) file = kwargs.get('file', sys.stdout) file.flush() if file is not None else sys.stdout.flush() app = flask.Flask(__name__) # disable logging, very noisy! log = logging.getLogger('werkzeug') log.disabled = True app.config['SECRET_KEY'] = 'secret' app.config.update( SESSION_COOKIE_SECURE=True, SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SAMESITE='None' ) CORS(app, support_credentials=True) Payload.max_decode_packets = 500 socketio = SocketIO(app, cors_allowed_origins="*", async_mode = "threading") #def get_odrive(): # globals()['odrives'] = [] # globals()['odrives'].append(odrive.find_any()) # globals()['odrives'][0].__channel__._channel_broken.subscribe(lambda: handle_disconnect()) # print("odrives found") # socketio.emit('odrive-found') def discovered_device(device): # when device is discovered, add it to list of serial numbers and global odrive list # shamelessly lifted from odrive python package serial_number = '{:012X}'.format(device.serial_number) if hasattr(device, 'serial_number') else "[unknown serial number]" if serial_number in globals()['discovered_devices']: index = globals()['discovered_devices'].index(serial_number) else: globals()['discovered_devices'].append(serial_number) index = len(globals()['discovered_devices']) - 1 odrive_name = "odrive" + str(index) # add to list of odrives while globals()['inUse']: time.sleep(0.1) globals()['odrives'][odrive_name] = device globals()['odrives_status'][odrive_name] = True print("Found " + str(serial_number)) print("odrive list: " + str([key for key in globals()['odrives'].keys()])) # tell GUI the status of known ODrives (previously connected and then disconnected ODrives will be "False") socketio.emit('odrives-status', json.dumps(globals()['odrives_status'])) # triggers a getODrives socketio message socketio.emit('odrive-found') def start_discovery(): print("starting disco loop...") log = fibre.Logger(verbose = False) shutdown = fibre.Event() fibre.find_all("usb", None, discovered_device, shutdown, shutdown, log) def handle_disconnect(odrive_name): print("lost odrive") globals()['odrives_status'][odrive_name] = False # emit the whole list of odrive statuses # in the GUI, mark and use status as ODrive state. socketio.emit('odrives-status', json.dumps(globals()['odrives_status'])) @socketio.on('findODrives') def getODrives(message): print("looking for odrive") start_discovery() @socketio.on('enableSampling') def enableSampling(message): print("sampling enabled") session['samplingEnabled'] = True emit('samplingEnabled') @socketio.on('stopSampling') def stopSampling(message): session['samplingEnabled'] = False emit('samplingDisabled') @socketio.on('sampledVarNames') def sampledVarNames(message): session['sampledVars'] = message print(session['sampledVars']) @socketio.on('startSampling') def sendSamples(message): print(session['samplingEnabled']) while session['samplingEnabled']: emit('sampledData', json.dumps(getSampledData(session['sampledVars']))) time.sleep(0.02) @socketio.on('message') def handle_message(message): print(message) emit('response', 'hello from server!') @socketio.on('getODrives') def get_odrives(data): # spinlock while globals()['inUse']: time.sleep(0.1) globals()['inUse'] = True odriveDict = {} #for (index, odrv) in enumerate(globals()['odrives']): # odriveDict["odrive" + str(index)] = dictFromRO(odrv) for key in globals()['odrives_status'].keys(): if globals()['odrives_status'][key] == True: odriveDict[key] = dictFromRO(globals()['odrives'][key]) globals()['inUse'] = False emit('odrives', json.dumps(odriveDict)) @socketio.on('getProperty') def get_property(message): # message is dict natively # will be {"path": "odriveX.axisY.blah.blah"} while globals()['inUse']: time.sleep(0.1) if globals()['odrives_status'][message["path"].split('.')[0]]: globals()['inUse'] = True val = getVal(globals()['odrives'], message["path"].split('.')) globals()['inUse'] = False emit('ODriveProperty', json.dumps({"path": message["path"], "val": val})) @socketio.on('setProperty') def set_property(message): # message is {"path":, "val":, "type":} while globals()['inUse']: time.sleep(0.1) globals()['inUse'] = True print("From setProperty event handler: " + str(message)) postVal(globals()['odrives'], message["path"].split('.'), message["val"], message["type"]) val = getVal(globals()['odrives'], message["path"].split('.')) globals()['inUse'] = False emit('ODriveProperty', json.dumps({"path": message["path"], "val": val})) @socketio.on('callFunction') def call_function(message): # message is {"path"}, no args yet (do we know which functions accept arguments from the odrive tree directly?) while globals()['inUse']: time.sleep(0.1) print("From callFunction event handler: " + str(message)) globals()['inUse'] = True callFunc(globals()['odrives'], message["path"].split('.')) globals()['inUse'] = False @app.route('/', methods=['GET']) def home(): return "