unplugged-system/packages/services/Car/tools/telemetry/lua-interpreter/app.py

126 lines
4.2 KiB
Python

"""
Flask server for hosting Lua Interpreter tool.
"""
import ctypes
import json
import os
import flask
app = flask.Flask(__name__)
lua_lib = ctypes.cdll.LoadLibrary('./liblua_engine.so')
lua_lib.NewLuaEngine.argtypes = None
lua_lib.NewLuaEngine.restype = ctypes.c_void_p
lua_lib.ExecuteScript.argtypes = [
ctypes.c_void_p, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_char_p,
ctypes.c_char_p
]
lua_lib.ExecuteScript.restype = ctypes.c_void_p
engine = lua_lib.NewLuaEngine()
class LuaOutput(ctypes.Structure):
"""Python wrapper class around LuaOutput struct as defined in lua_engine.h.
"""
_fields_ = [('output', ctypes.POINTER(ctypes.c_char_p)),
('size', ctypes.c_int), ('saved_state', ctypes.c_char_p)]
@app.route('/')
def index():
"""Renders the main page of the tool.
Returns:
A JSON response with a string of the rendered index.html page.
"""
return flask.render_template('index.html')
@app.route('/get_published_data_file_names_and_content', methods=['POST'])
def get_published_data_file_names_and_content():
"""Returns the list of all the JSON file names under the data
directory without their file extensions. Also returns the
JSON of each published data file under the data directory as a string.
Returns:
A JSON response with file names under the key "file_names" and each
of the published data strings under the key of their file name.
"""
file_path = os.path.join(os.path.dirname(__file__), "data")
all_json_files = filter(lambda file: file.endswith('.json'),
os.listdir(file_path))
file_names = list(
# lamda function leverages splitext, which produces a tuple of
# the file name and the extension.
map(lambda file: os.path.splitext(file)[0], all_json_files))
response = {"file_names": file_names}
for file_name in file_names:
json_file_path = os.path.join(file_path, file_name + '.json')
json_file = open(json_file_path)
response[file_name] = json.dumps(json.load(json_file), indent=2)
json_file.close()
return response
@app.route('/execute_script', methods=['POST'])
def execute_script():
"""Executes the Lua script from the request
re-rendering the home page with the output.
Returns:
A JSON response containing the string of the rendered index.html
page with output, script, published data, and the saved state specified.
"""
script = flask.request.form['script']
function_name = flask.request.form['function-name']
published_data = flask.request.form['published-data']
saved_state = flask.request.form['saved-state']
# ctypes requires that strings are encoded to bytes
lua_output = LuaOutput.from_address(
lua_lib.ExecuteScript(engine, script.encode(), function_name.encode(),
published_data.encode(), saved_state.encode()))
# ctypes encodes strings as bytes so they must be decoded back to strings.
decoded_output = []
for i in range(lua_output.size):
decoded_output.append(prettify_json(lua_output.output[i].decode()))
new_saved_state = prettify_json(
ctypes.string_at(lua_output.saved_state).decode())
saved_state = new_saved_state if new_saved_state else saved_state
lua_lib.FreeLuaOutput(ctypes.byref(lua_output))
return flask.render_template('index.html',
script=script,
output=decoded_output,
function_name=function_name,
published_data=published_data,
saved_state=saved_state)
def prettify_json(string):
"""Prettifies the string if it represents a JSON with an indent of 2.
Args:
string (str): String to prettify
Returns:
A string of the formatted JSON. If the string does not represent a
JSON, the string is returned back with no changes.
"""
try:
# If the string cannot be loaded into a JSON,
# json.loads throws a JSONDecodeError exception.
json_object = json.loads(string)
json_formatted_str = json.dumps(json_object, indent=2)
return json_formatted_str
except json.JSONDecodeError as e:
return string
if __name__ == '__main__':
app.run()