From a2fc7ebf62e1bf5be0396c5ad591cd592a24856d Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Mon, 1 Aug 2022 15:04:33 +0100 Subject: [PATCH 01/17] Add CORS headers to Webpack Dev Server to allow local serving of assets This is for the development environment, so the webpack component can snarf anything from the main react UI app. --- .env.example | 2 +- .env.webcomponent.example | 2 +- config/webpackDevServer.config.js | 5 + public/sense_hat_blob.py | 1605 +++++++++++++++++++++++++++++ public/three | 1 + 5 files changed, 1613 insertions(+), 2 deletions(-) create mode 100644 public/sense_hat_blob.py create mode 120000 public/three diff --git a/.env.example b/.env.example index 3212d94bf..adda168ec 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,4 @@ REACT_APP_AUTHENTICATION_CLIENT_ID='editor-dev' REACT_APP_AUTHENTICATION_URL='http://localhost:9000' REACT_APP_LOGIN_ENABLED='true' REACT_APP_API_ENDPOINT='http://localhost:3009' -REACT_APP_S3_BUCKET='https://editor-images-test.s3.eu-west-2.amazonaws.com' +REACT_APP_S3_BUCKET='http://localhost:3000' diff --git a/.env.webcomponent.example b/.env.webcomponent.example index d14fb044d..f6cbbfc4a 100644 --- a/.env.webcomponent.example +++ b/.env.webcomponent.example @@ -1 +1 @@ -REACT_APP_S3_BUCKET='https://editor-images-test.s3.eu-west-2.amazonaws.com' +REACT_APP_S3_BUCKET='http://localhost:3000' diff --git a/config/webpackDevServer.config.js b/config/webpackDevServer.config.js index 6c43a8a10..24e66a1c7 100644 --- a/config/webpackDevServer.config.js +++ b/config/webpackDevServer.config.js @@ -98,6 +98,11 @@ module.exports = function (proxy, allowedHost) { // See https://github.com/facebook/create-react-app/issues/387. disableDotRule: true, index: paths.publicUrlOrPath, + }, + headers: { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS", + "Access-Control-Allow-Headers": "X-Requested-With, content-type, Authorization" }, public: allowedHost, // `proxy` is run between `before` and `after` `webpack-dev-server` hooks diff --git a/public/sense_hat_blob.py b/public/sense_hat_blob.py new file mode 100644 index 000000000..301c735e3 --- /dev/null +++ b/public/sense_hat_blob.py @@ -0,0 +1,1605 @@ +# from _internal_sense_hat import InputEvent, _wait, _read, _start_stick_thread, _stop_stick_thread, _inspectFunction +import _internal_sense_hat as _ish +import math +import time +import array +from image import Image +from time import sleep + +TEXT_DICT = {';': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '$': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'O': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 's': [[255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], ' ': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], ':': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'y': [[255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'B': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], ')': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '6': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'T': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], 'Q': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], '3': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], 'p': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '/': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0]], '\n': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '0': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'I': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'U': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0]], 'D': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'G': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0]], '>': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '=': [[0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'w': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'r': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'V': [[0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0]], 'C': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'K': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], 'H': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0]], '_': [[255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '"': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '#': [[0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '*': [[0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], ',': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'A': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], '&': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '8': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'g': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], ']': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '@': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], '-': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'L': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'a': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'P': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'Z': [[255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0]], '4': [[0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'j': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '7': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0]], '\\': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'F': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], '9': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'b': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '1': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '2': [[255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], '5': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], 'W': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0]], 'q': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'E': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], '+': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '.': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'N': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0]], 'x': [[255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'l': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '<': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 't': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'd': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0]], '~': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], "'": [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'X': [[255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0]], 'R': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'h': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'f': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0]], '?': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'c': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'm': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '%': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0]], 'k': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'v': [[0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'S': [[255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], 'i': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'M': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0]], '[': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'Y': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0]], '|': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'J': [[0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0]], 'o': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'e': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'z': [[255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'u': [[0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0]], 'n': [[255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '(': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]], '!': [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [255, 255, 255], [0, 0, 0], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [255, 255, 255], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]} + +class GPIOZeroError(Exception): + "Base class for all exceptions in GPIO Zero" + +class GPIODeviceError(GPIOZeroError): + "Base class for errors specific to the GPIODevice hierarchy" + +class GPIOPinInUse(GPIODeviceError): + "Error raised when attempting to use a pin already in use by another device" + +class MotionSensor(object): + + inst = False + + def __init__(self, pull_up=False, active_state=None, queue_len=1, sample_rate=10, threshold=0.5, partial=False, pin_factory=None): + if MotionSensor.inst: + raise GPIOPinInUse() + self._pin = 12 + self.when_motion = None + self.when_no_motion = None + MotionSensor.inst = True + + def wait_for_motion(self, timeout=None): + _ish._waitmotion(timeout, True) + + def wait_for_no_motion(self, timeout=None): + _ish._waitmotion(timeout, False) + + @property + def motion_detected(self): + return _ish.motionRead() == 1 + + @property + def pin(self): + pass + + @property + def value(self): + return _ish.motionRead() + + @property + def when_motion(self): + return self._when_motion + + @when_motion.setter + def when_motion(self, value): + self._when_motion = value + _ish._start_motion(value) + + @property + def when_no_motion(self): + return self._when_no_motion + + @when_no_motion.setter + def when_no_motion(self, value): + self._when_no_motion = value + _ish._stop_motion(value) + +DIRECTION_UP = 'up' +DIRECTION_DOWN = 'down' +DIRECTION_LEFT = 'left' +DIRECTION_RIGHT = 'right' +DIRECTION_MIDDLE = 'middle' + +ACTION_PRESSED = 'pressed' +ACTION_RELEASED = 'released' +ACTION_HELD = 'held' + +class SenseStick(object): + """ + Represents the joystick on the Sense HAT. + """ + SENSE_HAT_EVDEV_NAME = 'Raspberry Pi Sense HAT Joystick' + EVENT_FORMAT = str('llHHI') + #EVENT_SIZE = struct.calcsize(EVENT_FORMAT) + + EV_KEY = 0x01 + + STATE_RELEASE = 0 + STATE_PRESS = 1 + STATE_HOLD = 2 + + KEY_UP = 103 + KEY_LEFT = 105 + KEY_RIGHT = 106 + KEY_DOWN = 108 + KEY_ENTER = 28 + + def __init__(self): + self._callbacks = {} + #self._stick_file = io.open(self._stick_device(), 'rb', buffering=0) + self._callback_thread = False + #self._callback_event = Event() + + def close(self): + pass + + def __enter__(self): + return self + + def __exit__(self, exc_type, exc_value, exc_tb): + self.close() + + def _stick_device(self): + """ + Discovers the filename of the evdev device that represents the Sense + HAT's joystick. + """ + pass + + def _key_to_direction(self, key): + return { + self.KEY_UP: DIRECTION_UP, + self.KEY_DOWN: DIRECTION_DOWN, + self.KEY_LEFT: DIRECTION_LEFT, + self.KEY_RIGHT: DIRECTION_RIGHT, + self.KEY_ENTER: DIRECTION_MIDDLE, + }[key] + + def _state_to_action(self, state): + return { + self.STATE_PRESS: ACTION_PRESSED, + self.STATE_RELEASE: ACTION_RELEASED, + self.STATE_HOLD: ACTION_HELD, + }[state] + + def _read(self): + """ + Reads a single event from the joystick, blocking until one is + available. Returns `None` if a non-key event was read, or an + `InputEvent` tuple describing the event otherwise. + """ + + # Read from internal sense_hat module + event = _read() + (timestamp, key, state, key_type) = event + if key_type == self.EV_KEY: + return InputEvent([ + timestamp, + self._key_to_direction(key), + self._state_to_action(state) + ]) + else: + return None + + def _wait(self, timeout=None): + """ + Waits *timeout* seconds until an event is available from the + joystick. Returns `True` if an event became available, and `False` + if the timeout expired. + """ + # Return from internal sense_hat module + return _wait(timeout) + + def _wrap_callback(self, fn): + return fn + """ + # Shamelessley nicked (with some variation) from GPIO Zero :) + @wraps(fn) + def wrapper(event): + return fn() + + if fn is None: + return None + elif not callable(fn): + raise ValueError('value must be None or a callable') + elif inspect.isbuiltin(fn): + # We can't introspect the prototype of builtins. In this case we + # assume that the builtin has no (mandatory) parameters; this is + # the most reasonable assumption on the basis that pre-existing + # builtins have no knowledge of InputEvent, and the sole parameter + # we would pass is an InputEvent + return wrapper + else: + # Try binding ourselves to the argspec of the provided callable. + # If this works, assume the function is capable of accepting no + # parameters and that we have to wrap it to ignore the event + # parameter + try: + inspect.getcallargs(fn) + return wrapper + except TypeError: + try: + # If the above fails, try binding with a single tuple + # parameter. If this works, return the callback as is + inspect.getcallargs(fn, ()) + return fn + except TypeError: + raise ValueError( + 'value must be a callable which accepts up to one ' + 'mandatory parameter') + """ + + def _start_stop_thread(self): + if self._callbacks and not self._callback_thread: + #self._callback_event.clear() + #self._callback_thread = Thread(target=self._callback_run) + #self._callback_thread.daemon = True + #self._callback_thread.start() + _start_stick_thread(self._callback_run) + self._callback_thread = True + elif not self._callbacks and self._callback_thread: + #self._callback_event.set() + _stop_stick_thread() + self._callback_thread = False + + def _callback_run(self): + # Use this as the internal callback function from our + # JavaScript event based callback system + event = self._read() + if event: + callback = self._callbacks.get(event.direction) + if callback: + argsLength = _inspectFunction(callback) + if argsLength == 1: + callback(event) + else: + callback() + + callback = self._callbacks.get('*') + if callback: + argsLength = _inspectFunction(callback) + if argsLength == 1: + callback(event) + else: + callback() + + def wait_for_event(self, emptybuffer=False): + """ + Waits until a joystick event becomes available. Returns the event, as + an `InputEvent` tuple. + + If *emptybuffer* is `True` (it defaults to `False`), any pending + events will be thrown away first. This is most useful if you are only + interested in "pressed" events. + """ + if emptybuffer: + while self._wait(0): + self._read() + while self._wait(): + event = self._read() + if event: + return event + + def get_events(self): + """ + Returns a list of all joystick events that have occurred since the last + call to `get_events`. The list contains events in the order that they + occurred. If no events have occurred in the intervening time, the + result is an empty list. + """ + result = [] + while self._wait(0): + event = self._read() + if event: + result.append(event) + return result + + @property + def direction_up(self): + """ + The function to be called when the joystick is pushed up. The function + can either take a parameter which will be the `InputEvent` tuple that + has occurred, or the function can take no parameters at all. + """ + return self._callbacks.get(DIRECTION_UP) + + + @direction_up.setter + def direction_up(self, value): + self._callbacks[DIRECTION_UP] = self._wrap_callback(value) + self._start_stop_thread() + + @property + def direction_down(self): + """ + The function to be called when the joystick is pushed down. The + function can either take a parameter which will be the `InputEvent` + tuple that has occurred, or the function can take no parameters at all. + + Assign `None` to prevent this event from being fired. + """ + return self._callbacks.get(DIRECTION_DOWN) + + @direction_down.setter + def direction_down(self, value): + self._callbacks[DIRECTION_DOWN] = self._wrap_callback(value) + self._start_stop_thread() + + @property + def direction_left(self): + """ + The function to be called when the joystick is pushed left. The + function can either take a parameter which will be the `InputEvent` + tuple that has occurred, or the function can take no parameters at all. + + Assign `None` to prevent this event from being fired. + """ + return self._callbacks.get(DIRECTION_LEFT) + + @direction_left.setter + def direction_left(self, value): + self._callbacks[DIRECTION_LEFT] = self._wrap_callback(value) + self._start_stop_thread() + + @property + def direction_right(self): + """ + The function to be called when the joystick is pushed right. The + function can either take a parameter which will be the `InputEvent` + tuple that has occurred, or the function can take no parameters at all. + + Assign `None` to prevent this event from being fired. + """ + return self._callbacks.get(DIRECTION_RIGHT) + + @direction_right.setter + def direction_right(self, value): + self._callbacks[DIRECTION_RIGHT] = self._wrap_callback(value) + self._start_stop_thread() + + @property + def direction_middle(self): + """ + The function to be called when the joystick middle click is pressed. The + function can either take a parameter which will be the `InputEvent` tuple + that has occurred, or the function can take no parameters at all. + + Assign `None` to prevent this event from being fired. + """ + return self._callbacks.get(DIRECTION_MIDDLE) + + @direction_middle.setter + def direction_middle(self, value): + self._callbacks[DIRECTION_MIDDLE] = self._wrap_callback(value) + self._start_stop_thread() + + @property + def direction_any(self): + """ + The function to be called when the joystick is used. The function + can either take a parameter which will be the `InputEvent` tuple that + has occurred, or the function can take no parameters at all. + + This event will always be called *after* events associated with a + specific action. Assign `None` to prevent this event from being fired. + """ + return self._callbacks.get('*') + + @direction_any.setter + def direction_any(self, value): + self._callbacks['*'] = self._wrap_callback(value) + self._start_stop_thread() + +class RTIMU: + """ + https://github.com/richards-tech/RTIMULib2/blob/master/Linux/python/PyRTIMU_RTIMU.cpp + """ + def __init__(self, imu_settings): + self._imu_settings = imu_settings + self._compass_enabled = True + self._gyro_enabled = False + self._accel_enabled = True + self._imu_data = { + 'accel': (0.0, 0.0, 0.0), + 'accelValid': False, + 'compass': (0.0, 0.0, 0.0), + 'compassValid': False, + 'fusionPose': (0.0, 0.0, 0.0), + 'fusionPoseValid': False, + 'fusionQPose': (0.0, 0.0, 0.0, 0.0), + 'fusionQPoseValid': False, + 'gyro': (0.0, 0.0, 0.0), + 'gyroValid': False, + 'humidity': float('nan'), + 'humidityValid': False, + 'pressure': float('nan'), + 'pressureValid': False, + 'temperature': float('nan'), + 'temperatureValid': False, + 'timestamp': 0, + } + + def IMUInit(self): + """ + Set up the IMU + :return: + """ + return True + + def IMUGetPollInterval(self): + """ + Get the recommended poll interval in ms + """ + return 3 + + def IMUGyroBiasValid(self): + raise NotImplementedError + + def IMURead(self): + # ToDo: https://github.com/RPi-Distro/python-sense-emu/blob/master/sense_emu/RTIMU.py#L96 + return 3 # long + + def IMUName(self): + return "LSM9DS1" + + def IMUType(self): + return 6 # 6 in real units + + def getAccel(self): + return self._imu_data['accel'] + + def getAccelCalibrationValid(self): + raise NotImplementedError + + def getAccelResiduals(self): + raise NotImplementedError + + def getCompass(self): + return self._imu_data['compass'] + + def getCompassCalibrationValid(self): + raise NotImplementedError + + def getCompassCalibrationEllipsoidValid(self): + raise NotImplementedError + + def getFusionData(self): + return self._imu_data['fusionPose'] + + def getGyro(self): + return self._imu_data['gyro'] + + def resetFusion(self): + """ + Return true if valid bias + :return: + """ + pass + + def setSlerPower(self): + """ + Enable or disable Gyro reading + :return: + """ + pass + + def getIMUData(self): + # ToDo: https://github.com/RPi-Distro/python-sense-emu/blob/master/sense_emu/RTIMU.py#L153 + """ + https://github.com/richards-tech/RTIMULib2/blob/master/Linux/python/PyRTIMU_RTIMU.cpp#L189 + """ + return { + "timestamp": time.time(), + "fusionPoseValid": True, + "fusionPose": self._getFusionPose(), + "fusionQPoseValid": True, + "gyroValid": True, + "gyro": _ish.gyroRead(), + "accelValid": True, + "accel": _ish.accelRead(), + "compassValid": True, + "compass": _ish.compassRead(), + "pressureValid": True, + "pressure": _ish.pressureRead(), + "temperatureValid": True, + "temperature": _ish.temperatureRead(), + "humidityValid": True, + "humidity": _ish.humidityRead() + } + + def getMeasuredPose(self): + raise NotImplementedError + + def getMeasuredQPose(self): + raise NotImplementedError + + def setCompassEnable(self, enabled): + self._compass_enabled = enabled + + def setGyroEnable(self, enabled): + self._gyro_enabled = enabled + + def setAccelEnable(self, enabled): + self._accel_enabled = enabled + + def _getFusionPose(self): + if self._accel_enabled == False and self._gyro_enabled == False: + # ToDo: special compass only handling for yaw data + return _ish.fusionPoseRead() + + return _ish.fusionPoseRead() + +class RTPressure: + def __init__(self, imu_settings): + self._imu_settings = imu_settings + + def pressureInit(self): + return True + + def pressureRead(self): + # return Py_BuildValue("idid", data.pressureValid, data.pressure, data.temperatureValid, data.temperature); + return _ish.pressureRead(); + + def pressureName(self): + return "LPS25H" + + def pressureType(self): + return 3 # long + +class RTHumidity: + """ + https://github.com/richards-tech/RTIMULib2/blob/master/Linux/python/PyRTIMU_RTHumidity.cpp + """ + def __init__(self, imu_settings): + self._imu_settings = imu_settings + + def humidityInit(self): + """ + Set up the humidity sensor + """ + return True + + def humidityRead(self): + """ + Get current values + """ + #return Py_BuildValue("idid", data.humidityValid, data.humidity, data.temperatureValid, data.temperature); + return _ish.humidityRead(); + + def humidityType(self): + """ + Get the type code of the humidity sensor + """ + return 2 # long + + def humidityName(self): + """ + Get the name of the humidity sensor + """ + return "HTS221" + +class Settings: + def __init__(self, settings_path): + self.settings_path = settings_path + +""" + Custom fb_device implementation that stubs the file descriptor hardware access +""" +class FBDevice: + SENSE_HAT_FB_FBIOGET_GAMMA = 61696 + SENSE_HAT_FB_FBIOSET_GAMMA = 61697 + SENSE_HAT_FB_FBIORESET_GAMMA = 61698 + + def __init__(self): + self.data = [[0, 0, 0] for i in range(0, 64)] # we store the data as 8x8=64 items + self.index = 0 + self.gamma = [0]*32 + + _ish.init() + _ish.setpixels(None, self.data) + + def setpixel(self, index, value): + _ish.setpixel(index, value) + + def getpixel(self, index): + return _ish.getpixel(index) + + def setpixels(self, indexes, values): + _ish.setpixels(indexes, values) + + def getpixels(self): + return _ish.getpixels() + + def ioctl(self, request, arg=0, mutate_flag=True): + if request == FBDevice.SENSE_HAT_FB_FBIOGET_GAMMA: + # mimic the function behavior + if len(arg) != 32: + raise OSError('Getting gamma requires a buffer for 32 values') + + gamma = _ish.getGamma() + + for i in range(0, len(arg)): + arg[i] = gamma[i] + elif request == FBDevice.SENSE_HAT_FB_FBIOSET_GAMMA: + if len(arg) != 32: + raise OSError('Setting gamma requires 32 values') + _ish.setGamma(arg) + # ToDo: add checks if we might need to set low_light + elif request == FBDevice.SENSE_HAT_FB_FBIORESET_GAMMA: + #self.gamma = [arg]*32 + # special case + if arg == 1: + _ish.setGamma([0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10]) + _ish.setLowlight(True) + elif arg == 0: + _ish.setGamma([0]*32) + _ish.setLowlight(False) + else: + # ToDo: check if we need to todo this + raise OSError('Unsupported operation') + + return 0 + +GAIN_MAP = {1: 0x00, 4: 0x01, 16: 0x02, 60: 0x03} # maps gain values to register values +GAIN_INV = dict(zip(GAIN_MAP.values(), GAIN_MAP.keys())) +CLOCK_STEP = 0.0024 # the clock step is 2.4ms +_error_str = "Failed to initialise TCS34725 colour sensor." + +class ColourSensor(object): + """ + Implementation for the TCS34725 Color Sensor + Documentation (including datasheet): https://ams.com/tcs34725#tab/documents + """ + + def __init__(self, gain=1, integration_cycles=1): + self.gain = gain + self.integration_cycles=integration_cycles + self.enabled = 1 + + @property + def enabled(self): + return True + + @enabled.setter + def enabled(self, status): + pass + + @property + def gain(self): + """ + Obtains gain of colour sensor + """ + return GAIN_INV[self._gain] + + @gain.setter + def gain(self, value): + if value in GAIN_MAP: + self._gain = GAIN_MAP[value] + else: + raise RuntimeError("Cannot set gain to {}. Values: {}".format(value,tuple(GAIN_MAP.keys()))) + + @property + def integration_cycles(self): + return 256 - self._atime + + @integration_cycles.setter + def integration_cycles(self, cycles): + if 1 <= cycles <= 256: + self._atime = 256 - cycles + self._integration_time = cycles * CLOCK_STEP + self._max_value = 2**16 if cycles >= 64 else 1024*cycles + self._scaling = self._max_value // 256 + else: + raise RuntimeError("Cannot set integration cycles to {} (1-256)".format(cycles)) + + @property + def integration_time(self): + return self._integration_time + + @property + def max_raw(self): + return self._max_value + + @property + def colour_raw(self): + return _ish.colourRead() + + @property + def red_raw(self): + return self.colour_raw[0] + + @property + def green_raw(self): + return self.colour_raw[1] + + @property + def blue_raw(self): + return self.colour_raw[2] + + @property + def clear_raw(self): + return self.colour_raw[3] + + @property + def colour(self): + #return tuple(reading // self._scaling for reading in self.colour_raw) + return tuple(reading for reading in self.colour_raw) + + @property + def red(self): + return self.colour[0] + + @property + def green(self): + return self.colour[1] + + @property + def blue(self): + return self.colour[2] + + @property + def clear(self): + return self.colour[3] + + color_raw = colour_raw + color = colour + +class SenseHat(object): + + SENSE_HAT_FB_NAME = 'RPi-Sense FB' + SENSE_HAT_FB_FBIOGET_GAMMA = 61696 + SENSE_HAT_FB_FBIOSET_GAMMA = 61697 + SENSE_HAT_FB_FBIORESET_GAMMA = 61698 + SENSE_HAT_FB_GAMMA_DEFAULT = 0 + SENSE_HAT_FB_GAMMA_LOW = 1 + SENSE_HAT_FB_GAMMA_USER = 2 + SETTINGS_HOME_PATH = '.config/sense_hat' + + def __init__(self): + imu_settings_file='RTIMULib' + text_assets='sense_hat_text' + self._fb_device = self._get_fb_device() + if self._fb_device is None: + raise OSError('Cannot detect %s device' % self.SENSE_HAT_FB_NAME) + + # 0 is With B+ HDMI port facing downwards + pix_map0 = [ + [0, 1, 2, 3, 4, 5, 6, 7], + [8, 9, 10, 11, 12, 13, 14, 15], + [16, 17, 18, 19, 20, 21, 22, 23], + [24, 25, 26, 27, 28, 29, 30, 31], + [32, 33, 34, 35, 36, 37, 38, 39], + [40, 41, 42, 43, 44, 45, 46, 47], + [48, 49, 50, 51, 52, 53, 54, 55], + [56, 57, 58, 59, 60, 61, 62, 63] + ] + + # Trinket: hardcoded the values, why should we recalculate them every time + pix_map90 = [[ 7, 15, 23, 31, 39, 47, 55, 63], + [ 6, 14, 22, 30, 38, 46, 54, 62], + [ 5, 13, 21, 29, 37, 45, 53, 61], + [ 4, 12, 20, 28, 36, 44, 52, 60], + [ 3, 11, 19, 27, 35, 43, 51, 59], + [ 2, 10, 18, 26, 34, 42, 50, 58], + [ 1, 9, 17, 25, 33, 41, 49, 57], + [ 0, 8, 16, 24, 32, 40, 48, 56]]# we hardcode these values for now np.rot90(pix_map0) + pix_map180 = [[63, 62, 61, 60, 59, 58, 57, 56], + [55, 54, 53, 52, 51, 50, 49, 48], + [47, 46, 45, 44, 43, 42, 41, 40], + [39, 38, 37, 36, 35, 34, 33, 32], + [31, 30, 29, 28, 27, 26, 25, 24], + [23, 22, 21, 20, 19, 18, 17, 16], + [15, 14, 13, 12, 11, 10, 9, 8], + [ 7, 6, 5, 4, 3, 2, 1, 0]] #np.rot90(pix_map90) + pix_map270 = [[56, 48, 40, 32, 24, 16, 8, 0], + [57, 49, 41, 33, 25, 17, 9, 1], + [58, 50, 42, 34, 26, 18, 10, 2], + [59, 51, 43, 35, 27, 19, 11, 3], + [60, 52, 44, 36, 28, 20, 12, 4], + [61, 53, 45, 37, 29, 21, 13, 5], + [62, 54, 46, 38, 30, 22, 14, 6], + [63, 55, 47, 39, 31, 23, 15, 7]] #np.rot90(pix_map180) + + self._pix_map = { + 0: pix_map0, + 90: pix_map90, + 180: pix_map180, + 270: pix_map270 + } + + self._rotation = 0 + + + # Trinket: we are using an internal dict for that + + # Load text assets + #dir_path = os.path.dirname(__file__) + #self._load_text_assets( + # os.path.join(dir_path, '%s.png' % text_assets), + # os.path.join(dir_path, '%s.txt' % text_assets) + #) + + # Trinket: we do not need to pass any paths as we use hard coded values + self._load_text_assets("", "") + + # Load IMU settings and calibration data + self._imu_settings = self._get_settings_file(imu_settings_file) + self._imu = RTIMU(self._imu_settings) + self._imu_init = False # Will be initialised as and when needed + self._pressure = RTPressure(self._imu_settings) + self._pressure_init = False # Will be initialised as and when needed + self._humidity = RTHumidity(self._imu_settings) + self._humidity_init = False # Will be initialised as and when needed + self._last_orientation = {'pitch': 0, 'roll': 0, 'yaw': 0} + raw = {'x': 0, 'y': 0, 'z': 0} + self._last_compass_raw = raw + self._last_gyro_raw = raw + self._last_accel_raw = raw + self._compass_enabled = False + self._gyro_enabled = False + self._accel_enabled = False + self._stick = SenseStick() + + # initialise the TCS34725 colour sensor (if possible) + try: + self._colour = ColourSensor() + except: + pass + + try: + self._motion = MotionSensor() + except: + pass + + #### + # Text assets + #### + + # Text asset files are rotated right through 90 degrees to allow blocks of + # 40 contiguous pixels to represent one 5 x 8 character. These are stored + # in a 8 x 640 pixel png image with characters arranged adjacently + # Consequently we must rotate the pixel map left through 90 degrees to + # compensate when drawing text + + def _load_text_assets(self, text_image_file, text_file): + """ + Internal. Builds a character indexed dictionary of pixels used by the + show_message function below + """ + + # Trinket: Replaced the loading of the image file with a internal dict. + # This is faster than loading and processing the image on every run. + # Keeping the code, so that we might refactor is later + + #text_pixels = self.load_image(text_image_file, False) + #f = open(text_file, 'r') + #loaded_text = f.read() + #f.close() + #self._text_dict = {} + #for index, s in enumerate(loaded_text): + # start = index * 40 + # end = start + 40 + # char = text_pixels[start:end] + # self._text_dict[s] = char + + # we just load the hardcoded data + self._text_dict = TEXT_DICT + + def _trim_whitespace(self, char): # For loading text assets only + """ + Internal. Trims white space pixels from the front and back of loaded + text characters + """ + + psum = lambda x: sum(sum(x, [])) + if psum(char) > 0: + is_empty = True + while is_empty: # From front + row = char[0:8] + is_empty = psum(row) == 0 + if is_empty: + del char[0:8] + is_empty = True + while is_empty: # From back + row = char[-8:] + is_empty = psum(row) == 0 + if is_empty: + del char[-8:] + return char + + def _get_settings_file(self, imu_settings_file): + """ + Internal. Logic to check for a system wide RTIMU ini file. This is + copied to the home folder if one is not already found there. + """ + + ini_file = '%s.ini' % imu_settings_file + + # Trinket: removed os calls as we do not have a real file system + + return Settings(imu_settings_file) # RTIMU will add .ini internally + + def _get_fb_device(self): + """ + Internal. Finds the correct frame buffer device for the sense HAT + and returns its /dev name. + """ + + device = None + + # Trinket: replace device identification with internal JS bridge + device = FBDevice() + + return device + + #### + # Joystick + #### + @property + def stick(self): + return self._stick + + #### + # Colour sensor + #### + + @property + def colour(self): + """ + Obtains ColourSensor object + """ + + return self._colour + + @property + def color(self): + """ + Obtains ColourSensor + """ + + return self._colour + + def has_colour_sensor(self): + """ + Outputs if colour sensor is present or not + """ + + try: + self._colour + except: + return False + else: + return True + + #### + # Motion sensor + #### + + @property + def motion(self): + """ + Obtains MotionSensor object + """ + + return self._motion + + #### + # LED Matrix + #### + + @property + def rotation(self): + return self._rotation + + @rotation.setter + def rotation(self, r): + self.set_rotation(r, True) + + def set_rotation(self, r=0, redraw=True): + """ + Sets the LED matrix rotation for viewing, adjust if the Pi is upside + down or sideways. 0 is with the Pi HDMI port facing downwards + """ + + if r in self._pix_map.keys(): + if redraw: + pixel_list = self.get_pixels() + self._rotation = r + if redraw: + self.set_pixels(pixel_list) + else: + raise ValueError('Rotation must be 0, 90, 180 or 270 degrees') + + def flip_h(self, redraw=True): + """ + Flip LED matrix horizontal + """ + + pixel_list = self.get_pixels() + flipped = [] + for i in range(8): + offset = i * 8 + flipped.extend(reversed(pixel_list[offset:offset + 8])) + if redraw: + self.set_pixels(flipped) + return flipped + + def flip_v(self, redraw=True): + """ + Flip LED matrix vertical + """ + + pixel_list = self.get_pixels() + flipped = [] + for i in reversed(range(8)): + offset = i * 8 + flipped.extend(pixel_list[offset:offset + 8]) + if redraw: + self.set_pixels(flipped) + return flipped + + def set_pixels(self, pixel_list): + """ + Accepts a list containing 64 smaller lists of [R,G,B] pixels and + updates the LED matrix. R,G,B elements must intergers between 0 + and 255 + """ + + if len(pixel_list) != 64: + raise ValueError('Pixel lists must have 64 elements') + + for index, pix in enumerate(pixel_list): + if len(pix) != 3: + raise ValueError('Pixel at index %d is invalid. Pixels must contain 3 elements: Red, Green and Blue' % index) + + for element in pix: + if element > 255 or element < 0: + raise ValueError('Pixel at index %d is invalid. Pixel elements must be between 0 and 255' % index) + + f = self._fb_device + map = self._pix_map[self._rotation] + indexes = [] + pixels = [] + + for index, pix in enumerate(pixel_list): + indexes.append(map[index // 8][index % 8]) + pixels.append(pix) + + self._fb_device.setpixels(indexes, pixels) + + def get_pixels(self): + """ + Returns a list containing 64 smaller lists of [R,G,B] pixels + representing what is currently displayed on the LED matrix + """ + + pixel_list = [] + f = self._fb_device + map = self._pix_map[self._rotation] + for row in range(8): + for col in range(8): + # Two bytes per pixel in fb memory, 16 bit RGB565 + # Trinket: replace file operations with internal JS bridge + pix = self._fb_device.getpixel(map[row][col]) + pixel_list.append(pix) + + return pixel_list + + def set_pixel(self, x, y, *args): + """ + Updates the single [R,G,B] pixel specified by x and y on the LED matrix + Top left = 0,0 Bottom right = 7,7 + + e.g. ap.set_pixel(x, y, r, g, b) + or + pixel = (r, g, b) + ap.set_pixel(x, y, pixel) + """ + + pixel_error = 'Pixel arguments must be given as (r, g, b) or r, g, b' + + if len(args) == 1: + pixel = args[0] + if len(pixel) != 3: + raise ValueError(pixel_error) + elif len(args) == 3: + pixel = args + else: + raise ValueError(pixel_error) + + if x > 7 or x < 0: + raise ValueError('X position must be between 0 and 7') + + if y > 7 or y < 0: + raise ValueError('Y position must be between 0 and 7') + + for element in pixel: + if element > 255 or element < 0: + raise ValueError('Pixel elements must be between 0 and 255') + + map = self._pix_map[self._rotation] + # Two bytes per pixel in fb memory, 16 bit RGB565 + # Our custom module stores the tuples not the 16bit value + # Trinket: replace file operations with internal JS bridge + self._fb_device.setpixel(map[y][x], pixel) + + def get_pixel(self, x, y): + """ + Returns a list of [R,G,B] representing the pixel specified by x and y + on the LED matrix. Top left = 0,0 Bottom right = 7,7 + """ + + if x > 7 or x < 0: + raise ValueError('X position must be between 0 and 7') + + if y > 7 or y < 0: + raise ValueError('Y position must be between 0 and 7') + + pix = None + + # Trinket: replace file operations with internal JS bridge + map = self._pix_map[self._rotation] + pix = self._fb_device.getpixel(map[y][x]) + + return pix + + def load_image(self, file_path, redraw=True): + """ + Accepts a path to an 8 x 8 image file and updates the LED matrix with + the image + """ + + #if not os.path.exists(file_path): + # raise IOError('%s not found' % file_path) + + # Trinket: Replaced Pillow with skulpt Image module + img = Image(file_path) + pixel_list = list(map(list, img.getData())) + + if redraw: + self.set_pixels(pixel_list) + + return pixel_list + + def clear(self, *args): + """ + Clears the LED matrix with a single colour, default is black / off + + e.g. ap.clear() + or + ap.clear(r, g, b) + or + colour = (r, g, b) + ap.clear(colour) + """ + + black = (0, 0, 0) # default + + if len(args) == 0: + colour = black + elif len(args) == 1: + colour = args[0] + elif len(args) == 3: + colour = args + else: + raise ValueError('Pixel arguments must be given as (r, g, b) or r, g, b') + + self.set_pixels([colour] * 64) + + def _get_char_pixels(self, s): + """ + Internal. Safeguards the character indexed dictionary for the + show_message function below + """ + + if len(s) == 1 and s in self._text_dict.keys(): + return list(self._text_dict[s]) + else: + return list(self._text_dict['?']) + + def show_message( + self, + text_string, + scroll_speed=.1, + text_colour=[255, 255, 255], + back_colour=[0, 0, 0] + ): + """ + Scrolls a string of text across the LED matrix using the specified + speed and colours + """ + + # We must rotate the pixel map left through 90 degrees when drawing + # text, see _load_text_assets + previous_rotation = self._rotation + self._rotation -= 90 + if self._rotation < 0: + self._rotation = 270 + dummy_colour = [None, None, None] + string_padding = [dummy_colour] * 64 + letter_padding = [dummy_colour] * 8 + # Build pixels from dictionary + scroll_pixels = [] + scroll_pixels.extend(string_padding) + for s in text_string: + scroll_pixels.extend(self._trim_whitespace(self._get_char_pixels(s))) + scroll_pixels.extend(letter_padding) + scroll_pixels.extend(string_padding) + # Recolour pixels as necessary + coloured_pixels = [ + text_colour if pixel == [255, 255, 255] else back_colour + for pixel in scroll_pixels + ] + # Shift right by 8 pixels per frame to scroll + scroll_length = len(coloured_pixels) // 8 + for i in range(scroll_length - 8): + start = i * 8 + end = start + 64 + self.set_pixels(coloured_pixels[start:end]) + time.sleep(scroll_speed) + self._rotation = previous_rotation + + def show_letter( + self, + s, + text_colour=[255, 255, 255], + back_colour=[0, 0, 0] + ): + """ + Displays a single text character on the LED matrix using the specified + colours + """ + + if len(s) > 1: + raise ValueError('Only one character may be passed into this method') + # We must rotate the pixel map left through 90 degrees when drawing + # text, see _load_text_assets + previous_rotation = self._rotation + self._rotation -= 90 + if self._rotation < 0: + self._rotation = 270 + dummy_colour = [None, None, None] + pixel_list = [dummy_colour] * 8 + pixel_list.extend(self._get_char_pixels(s)) + pixel_list.extend([dummy_colour] * 16) + coloured_pixels = [ + text_colour if pixel == [255, 255, 255] else back_colour + for pixel in pixel_list + ] + self.set_pixels(coloured_pixels) + self._rotation = previous_rotation + + @property + def gamma(self): + #buffer = array.array('B', [0]*32) + # ToDo: Change this back to array.array + buffer = [0]*32 + self._fb_device.ioctl(SenseHat.SENSE_HAT_FB_FBIOGET_GAMMA, buffer) + return list(buffer) + + @gamma.setter + def gamma(self, buffer): + if len(buffer) is not 32: + raise ValueError('Gamma array must be of length 32') + + if not all(b <= 31 for b in buffer): + raise ValueError('Gamma values must be bewteen 0 and 31') + + # ToDo: trinket, array.array does not support len() right now + #if not isinstance(buffer, array.array): + # buffer = array.array('B', buffer) + + #https://pythonhosted.org/sense-hat/api/#gamma + self._fb_device.ioctl(SenseHat.SENSE_HAT_FB_FBIOSET_GAMMA, buffer) + + def gamma_reset(self): + """ + Resets the LED matrix gamma correction to default + """ + self._fb_device.ioctl(SenseHat.SENSE_HAT_FB_FBIORESET_GAMMA, SenseHat.SENSE_HAT_FB_GAMMA_DEFAULT) + + @property + def low_light(self): + return self.gamma == [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 10] + + @low_light.setter + def low_light(self, value): + cmd = SenseHat.SENSE_HAT_FB_GAMMA_LOW if value else SenseHat.SENSE_HAT_FB_GAMMA_DEFAULT + self._fb_device.ioctl(SenseHat.SENSE_HAT_FB_FBIORESET_GAMMA, cmd) + + #### + # Environmental sensors + #### + + def _init_humidity(self): + """ + Internal. Initialises the humidity sensor via RTIMU + """ + + if not self._humidity_init: + self._humidity_init = self._humidity.humidityInit() + if not self._humidity_init: + raise OSError('Humidity Init Failed, please run as root / use sudo') + + def _init_pressure(self): + """ + Internal. Initialises the pressure sensor via RTIMU + """ + + if not self._pressure_init: + self._pressure_init = self._pressure.pressureInit() + if not self._pressure_init: + raise OSError('Pressure Init Failed, please run as root / use sudo') + + def get_humidity(self): + """ + Returns the percentage of relative humidity + """ + + self._init_humidity() # Ensure humidity sensor is initialised + humidity = 0 + data = self._humidity.humidityRead() + if (data[0]): # Humidity valid + humidity = data[1] + return humidity + + @property + def humidity(self): + return self.get_humidity() + + def get_temperature_from_humidity(self): + """ + Returns the temperature in Celsius from the humidity sensor + """ + + self._init_humidity() # Ensure humidity sensor is initialised + temp = 0 + data = self._humidity.humidityRead() + if (data[2]): # Temp valid + temp = data[3] + return temp + + def get_temperature_from_pressure(self): + """ + Returns the temperature in Celsius from the pressure sensor + """ + + self._init_pressure() # Ensure pressure sensor is initialised + temp = 0 + data = self._pressure.pressureRead() + if (data[2]): # Temp valid + temp = data[3] + return temp + + def get_temperature(self): + """ + Returns the temperature in Celsius + """ + + return self.get_temperature_from_humidity() + + @property + def temp(self): + return self.get_temperature_from_humidity() + + @property + def temperature(self): + return self.get_temperature_from_humidity() + + def get_pressure(self): + """ + Returns the pressure in Millibars + """ + + self._init_pressure() # Ensure pressure sensor is initialised + pressure = 0 + data = self._pressure.pressureRead() + if (data[0]): # Pressure valid + pressure = data[1] + return pressure + + @property + def pressure(self): + return self.get_pressure() + + #### + # IMU Sensor + #### + + def _init_imu(self): + """ + Internal. Initialises the IMU sensor via RTIMU + """ + + if not self._imu_init: + self._imu_init = self._imu.IMUInit() + if self._imu_init: + self._imu_poll_interval = self._imu.IMUGetPollInterval() * 0.001 + # Enable everything on IMU + self.set_imu_config(True, True, True) + else: + # ToDo: implement OSError + raise Error('IMU Init Failed, please run as root / use sudo') + #raise OSError('IMU Init Failed, please run as root / use sudo') + + def set_imu_config(self, compass_enabled, gyro_enabled, accel_enabled): + """ + Enables and disables the gyroscope, accelerometer and/or magnetometer + input to the orientation functions + """ + + # If the consuming code always calls this just before reading the IMU + # the IMU consistently fails to read. So prevent unnecessary calls to + # IMU config functions using state variables + + self._init_imu() # Ensure imu is initialised + + if (not isinstance(compass_enabled, bool) + or not isinstance(gyro_enabled, bool) + or not isinstance(accel_enabled, bool)): + raise TypeError('All set_imu_config parameters must be of boolan type') + + if self._compass_enabled != compass_enabled: + self._compass_enabled = compass_enabled + self._imu.setCompassEnable(self._compass_enabled) + + if self._gyro_enabled != gyro_enabled: + self._gyro_enabled = gyro_enabled + self._imu.setGyroEnable(self._gyro_enabled) + + if self._accel_enabled != accel_enabled: + self._accel_enabled = accel_enabled + self._imu.setAccelEnable(self._accel_enabled) + + def _read_imu(self): + """ + Internal. Tries to read the IMU sensor three times before giving up + """ + + self._init_imu() # Ensure imu is initialised + + attempts = 0 + success = False + + while not success and attempts < 3: + success = self._imu.IMURead() + attempts += 1 + time.sleep(self._imu_poll_interval) + + return success + + def _get_raw_data(self, is_valid_key, data_key): + """ + Internal. Returns the specified raw data from the IMU when valid + """ + + result = None + + if self._read_imu(): + data = self._imu.getIMUData() + if data[is_valid_key]: + raw = data[data_key] + result = { + 'x': raw[0], + 'y': raw[1], + 'z': raw[2] + } + + return result + + def get_orientation_radians(self): + """ + Returns a dictionary object to represent the current orientation in + radians using the aircraft principal axes of pitch, roll and yaw + """ + # orientaiton radians + # yaw: alpha (z), pitch: gamma (y), roll: beta (x) + raw = self._get_raw_data('fusionPoseValid', 'fusionPose') + + if raw is not None: + raw['roll'] = raw.pop('x') + raw['pitch'] = raw.pop('y') + raw['yaw'] = raw.pop('z') + self._last_orientation = raw + + return self._last_orientation + + @property + def orientation_radians(self): + return self.get_orientation_radians() + + def get_orientation_degrees(self): + """ + Returns a dictionary object to represent the current orientation + in degrees, 0 to 360, using the aircraft principal axes of + pitch, roll and yaw + """ + + orientation = self.get_orientation_radians() + for key, val in orientation.items(): + deg = math.degrees(val) # Result is -180 to +180 + orientation[key] = deg + 360 if deg < 0 else deg + return orientation + + def get_orientation(self): + return self.get_orientation_degrees() + + @property + def orientation(self): + return self.get_orientation_degrees() + + def get_compass(self): + """ + Gets the direction of North from the magnetometer in degrees + """ + # Real behavior would be to disable gyro and accel and then reading from fusionPose, + # but the emulator cannot do fusionPose as we derive everything from the orientation. + # Therefore, we a shortcut and read directly from our internal module that applies compass tilt compensation algorithm + # and returns the heading in radians. + + deg = math.degrees(_ish.headingRead()) + + return deg + 360 if deg < 0 else deg + + @property + def compass(self): + return self.get_compass() + + def get_compass_raw(self): + """ + Magnetometer x y z raw data in uT (micro teslas) + """ + + raw = self._get_raw_data('compassValid', 'compass') + + if raw is not None: + self._last_compass_raw = raw + + return self._last_compass_raw + + @property + def compass_raw(self): + return self.get_compass_raw() + + def get_gyroscope(self): + """ + Gets the orientation in degrees from the gyroscope only + """ + + self.set_imu_config(False, True, False) + return self.get_orientation_degrees() + + @property + def gyro(self): + return self.get_gyroscope() + + @property + def gyroscope(self): + return self.get_gyroscope() + + def get_gyroscope_raw(self): + """ + Gyroscope x y z raw data in radians per second + """ + + raw = self._get_raw_data('gyroValid', 'gyro') + + if raw is not None: + self._last_gyro_raw = raw + + return self._last_gyro_raw + + @property + def gyro_raw(self): + return self.get_gyroscope_raw() + + @property + def gyroscope_raw(self): + return self.get_gyroscope_raw() + + def get_accelerometer(self): + """ + Gets the orientation in degrees from the accelerometer only + """ + + self.set_imu_config(False, False, True) + return self.get_orientation_degrees() + + @property + def accel(self): + return self.get_accelerometer() + + @property + def accelerometer(self): + return self.get_accelerometer() + + def get_accelerometer_raw(self): + """ + Accelerometer x y z raw data in Gs + """ + + raw = self._get_raw_data('accelValid', 'accel') + + if raw is not None: + self._last_accel_raw = raw + + return self._last_accel_raw + + @property + def accel_raw(self): + return self.get_accelerometer_raw() + + @property + def accelerometer_raw(self): + return self.get_accelerometer_raw() diff --git a/public/three b/public/three new file mode 120000 index 000000000..e995ee9c8 --- /dev/null +++ b/public/three @@ -0,0 +1 @@ +../node_modules/three \ No newline at end of file From 4c0fdd0e5cdfb311ebe39b90b096fed4023316fa Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 09:27:49 +0100 Subject: [PATCH 02/17] Use PUBLIC_URL over REACT_APP_S3_BUCKET --- .env.example | 2 +- .env.webcomponent.example | 3 ++- .github/workflows/main.yml | 23 ++++++++++++------- src/components/AstroPiModel/FlightCase.js | 10 ++++---- .../Runners/PythonRunner/PythonRunner.js | 8 +++---- 5 files changed, 27 insertions(+), 19 deletions(-) diff --git a/.env.example b/.env.example index adda168ec..b2f8ec3be 100644 --- a/.env.example +++ b/.env.example @@ -2,4 +2,4 @@ REACT_APP_AUTHENTICATION_CLIENT_ID='editor-dev' REACT_APP_AUTHENTICATION_URL='http://localhost:9000' REACT_APP_LOGIN_ENABLED='true' REACT_APP_API_ENDPOINT='http://localhost:3009' -REACT_APP_S3_BUCKET='http://localhost:3000' +PUBLIC_URL='http://localhost:3000' diff --git a/.env.webcomponent.example b/.env.webcomponent.example index f6cbbfc4a..75f823820 100644 --- a/.env.webcomponent.example +++ b/.env.webcomponent.example @@ -1 +1,2 @@ -REACT_APP_S3_BUCKET='http://localhost:3000' +# NB This is the URL of react-ui, rather than the web component +PUBLIC_URL=http://localhost:3000 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 750a43cc7..0d01bde88 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -61,14 +61,6 @@ jobs: with: node-version: 16 cache: 'yarn' - - - name: Install code - run: yarn install --frozen-lock-file - - - name: Build site and WC bundle - run: | - yarn build - yarn build:wc - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 @@ -102,5 +94,20 @@ jobs: fi shell: bash + - name: Set PUBLIC_URL + run: | + public_url=https://${{ env.bucket }}/${{ env.deploy_dir }} + echo "Setting PUBLIC_URL to $public_url" + echo PUBLIC_URL=$public_url >> $GITHUB_ENV + shell: bash + + - name: Install code + run: yarn install --frozen-lock-file + + - name: Build site and WC bundle + run: | + yarn build + yarn build:wc + - name: Deploy site to S3 bucket run: aws s3 sync ./build/ s3://${{ env.bucket }}/${{ env.deploy_dir }} --delete diff --git a/src/components/AstroPiModel/FlightCase.js b/src/components/AstroPiModel/FlightCase.js index 62b2c7621..7eb67b647 100644 --- a/src/components/AstroPiModel/FlightCase.js +++ b/src/components/AstroPiModel/FlightCase.js @@ -9,9 +9,9 @@ import Sk from "skulpt"; const FlightCase = () => { - const gltf = useLoader(GLTFLoader, `${process.env.REACT_APP_S3_BUCKET}/models/raspi-compressed.glb`, loader => { + const gltf = useLoader(GLTFLoader, `${process.env.PUBLIC_URL}/models/raspi-compressed.glb`, loader => { const dracoLoader = new DRACOLoader(); - dracoLoader.setDecoderPath( `${process.env.REACT_APP_S3_BUCKET}/three/examples/js/libs/draco/` ); + dracoLoader.setDecoderPath( `${process.env.PUBLIC_URL}/three/examples/js/libs/draco/` ); loader.setDRACOLoader( dracoLoader ); }) window.mod=gltf.scene @@ -21,17 +21,17 @@ const FlightCase = () => { var y = Math.floor(ledIndex / 8); var newMaterial = new THREE.MeshStandardMaterial({color: `rgb(${r},${g},${b})`}); var object = gltf.scene.getObjectByName(`circle${x}_${7-y}-1`); - + if(object != null){ object.material = newMaterial; } } - + function setPixels(indexes, pix) { if(indexes == null){ indexes = Array.from(Array(8*8).keys()) } - + var i = 0; for (const ledIndex of indexes){ setPixel(ledIndex, pix[i][0], pix[i][1], pix[i][2]) diff --git a/src/components/Editor/Runners/PythonRunner/PythonRunner.js b/src/components/Editor/Runners/PythonRunner/PythonRunner.js index 4d5782ee7..41fdb3dfd 100644 --- a/src/components/Editor/Runners/PythonRunner/PythonRunner.js +++ b/src/components/Editor/Runners/PythonRunner/PythonRunner.js @@ -55,23 +55,23 @@ const PythonRunner = () => { const externalLibraries = { "./pygal/__init__.js": { - path: `${process.env.REACT_APP_S3_BUCKET}/pygal.js`, + path: `${process.env.PUBLIC_URL}/pygal.js`, dependencies: [ 'https://cdnjs.cloudflare.com/ajax/libs/highcharts/6.0.2/highcharts.js', 'https://cdnjs.cloudflare.com/ajax/libs/highcharts/6.0.2/js/highcharts-more.js' ], }, "./p5/__init__.js": { - path: `${process.env.REACT_APP_S3_BUCKET}/p5-shim.js`, + path: `${process.env.PUBLIC_URL}/p5-shim.js`, dependencies: [ 'https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.1/p5.js' ] }, "./_internal_sense_hat/__init__.js": { - path: `${process.env.REACT_APP_S3_BUCKET}/_internal_sense_hat.js` + path: `${process.env.PUBLIC_URL}/_internal_sense_hat.js` }, "./sense_hat.py": { - path: `${process.env.REACT_APP_S3_BUCKET}/sense_hat_blob.py` + path: `${process.env.PUBLIC_URL}/sense_hat_blob.py` } }; From 87a7884d9a864020e6ab5d657f93c5a217692a53 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 09:29:06 +0100 Subject: [PATCH 03/17] Update REACT_APP_S3_BUCKET in circleci too --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3083211cb..9f938be00 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -63,7 +63,7 @@ jobs: name: Start web component server command: yarn run start:wc environment: - REACT_APP_S3_BUCKET: https://editor-images-test.s3.eu-west-2.amazonaws.com + PUBLIC_URL: http://localhost:3000 background: true - run: name: Cypress integration tests From 4627301e5cd8ad5d6fe2b9708b16f520e3a443fa Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 09:30:09 +0100 Subject: [PATCH 04/17] Relax branch restrictions --- .github/workflows/main.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0d01bde88..fd9eb3f64 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,9 +3,7 @@ name: Build and Upload to S3 on: push: branches: - - main - - 'feature/**' - - 'issues/**' + - '*' tags: - '*' From 52a47008600987e8a0be9323571ed19746319d2c Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 09:35:46 +0100 Subject: [PATCH 05/17] Use actual S3 url --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fd9eb3f64..2fdac4581 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -94,7 +94,7 @@ jobs: - name: Set PUBLIC_URL run: | - public_url=https://${{ env.bucket }}/${{ env.deploy_dir }} + public_url=https://${{ env.bucket }}.s3.eu-west-2.amazonaws.com/${{ env.deploy_dir }} echo "Setting PUBLIC_URL to $public_url" echo PUBLIC_URL=$public_url >> $GITHUB_ENV shell: bash From 31bf326777a44d7738cdcb5ab6c322e1f4202ce6 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 11:44:53 +0100 Subject: [PATCH 06/17] Move wc port to 3001; build WC index html with webpack --- README.md | 2 +- cypress/e2e/missionZero-wc.cy.js | 2 +- cypress/e2e/spec-wc.cy.js | 2 +- docker-compose.yml | 2 +- docs/WebComponent.md | 2 +- .../index.html => src/web-component.html | 1 - webpack.component.config.js | 17 +++++++++++------ 7 files changed, 16 insertions(+), 12 deletions(-) rename public/web-component/index.html => src/web-component.html (95%) diff --git a/README.md b/README.md index e25028069..69db39d30 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ The repo includes the Editor Web Component which shares components with the edit ### `yarn stat:wc` Runs the web component in development mode. -Open [http://localhost:9000](http://localhost:9000) to view it in the browser. +Open [http://localhost:3001](http://localhost:3001) to view it in the browser. There is no production build setup for the web component at present. diff --git a/cypress/e2e/missionZero-wc.cy.js b/cypress/e2e/missionZero-wc.cy.js index 89531a4db..b8ee7ddc9 100644 --- a/cypress/e2e/missionZero-wc.cy.js +++ b/cypress/e2e/missionZero-wc.cy.js @@ -1,4 +1,4 @@ -const baseUrl = "http://localhost:9000" +const baseUrl = "http://localhost:3001" beforeEach(() => { cy.visit(baseUrl) diff --git a/cypress/e2e/spec-wc.cy.js b/cypress/e2e/spec-wc.cy.js index 3b62f5d6c..643f5899f 100644 --- a/cypress/e2e/spec-wc.cy.js +++ b/cypress/e2e/spec-wc.cy.js @@ -1,4 +1,4 @@ -const baseUrl = "http://localhost:9000" +const baseUrl = "http://localhost:3001" beforeEach(() => { cy.visit(baseUrl) diff --git a/docker-compose.yml b/docker-compose.yml index 4ec7b994c..3b18e06fa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: build: . command: yarn start:wc ports: - - "9000:9000" + - "3001:3001" container_name: react-ui-wc stdin_open: true volumes: diff --git a/docs/WebComponent.md b/docs/WebComponent.md index 9f21b8b50..6f12eb251 100644 --- a/docs/WebComponent.md +++ b/docs/WebComponent.md @@ -7,7 +7,7 @@ To have the web component be able to use the same React components as the site a There is a custom webpack config file for the component `webpack.component.config.js` and a script in the `package.json`: `start:wc` which will start serving the web component. -In `public/web-component/index.html` the JavaScript output is added and the web-component mounted. Then viewing `http://localhost:9000` will load the page with the web component mounted. +In `public/web-component/index.html` the JavaScript output is added and the web-component mounted. Then viewing `http://localhost:3001` will load the page with the web component mounted. ## WebComponent Class diff --git a/public/web-component/index.html b/src/web-component.html similarity index 95% rename from public/web-component/index.html rename to src/web-component.html index 354eb8195..202289309 100644 --- a/public/web-component/index.html +++ b/src/web-component.html @@ -7,7 +7,6 @@ -

diff --git a/webpack.component.config.js b/webpack.component.config.js index e74308d22..7f0b9ecdd 100644 --- a/webpack.component.config.js +++ b/webpack.component.config.js @@ -1,5 +1,6 @@ const path = require('path'); const Dotenv = require('dotenv-webpack'); +const HtmlWebpackPlugin = require('html-webpack-plugin') module.exports = { entry: path.resolve(__dirname, './src/web-component.js'), @@ -38,21 +39,25 @@ module.exports = { }, output: { path: path.resolve(__dirname, './build'), - filename: 'bundle.js', + filename: 'web-component.js', }, devServer: { - allowedHosts: [ - 'react-ui-test-wc' - ], - contentBase: path.resolve(__dirname, './public/web-component'), + allowedHosts: [ 'react-ui-test-wc' ], + contentBase: path.join(__dirname, 'public'), + index: 'web-component.html', host: "0.0.0.0", - port: 9000, + port: 3001, writeToDisk: true, }, plugins: [ new Dotenv({ path: './.env.webcomponent', systemvars: true + }), + new HtmlWebpackPlugin({ + inject: 'body', + template: 'src/web-component.html', + filename: 'web-component.html', }) ] }; From 01c6e38410b21e3b44731334cb2a02788d3e8e13 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:00:16 +0100 Subject: [PATCH 07/17] Add Cypress test step to github action --- .github/workflows/main.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 2fdac4581..97c57f501 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,6 +7,7 @@ on: tags: - '*' + jobs: lint: runs-on: ubuntu-latest @@ -47,6 +48,25 @@ jobs: - name: Run tests run: yarn test + test:cypress: + runs-on: ubuntu-latest + - name: Checkout + uses: actions/checkout@v1 + + - name: Cache dependencies + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'yarn' + + - name: Cypress run + uses: cypress-io/github-action@v4 + with: + install: false + build: yarn start:wc + start: yarn start & yarn start:wc & wait + wait-on: 'http://localhost:3001' + build-deploy: needs: test runs-on: ubuntu-latest From 75642bdb3d325153530548bb673bd8ece9c91895 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:07:36 +0100 Subject: [PATCH 08/17] Rejig --- .github/workflows/main.yml | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 97c57f501..8ab085f0b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -48,7 +48,7 @@ jobs: - name: Run tests run: yarn test - test:cypress: + test-cypress: runs-on: ubuntu-latest - name: Checkout uses: actions/checkout@v1 @@ -59,16 +59,21 @@ jobs: node-version: 16 cache: 'yarn' + - name: Install code + run: yarn install --frozen-lock-file + - name: Cypress run uses: cypress-io/github-action@v4 with: - install: false - build: yarn start:wc start: yarn start & yarn start:wc & wait wait-on: 'http://localhost:3001' + env: + PUBLIC_URL: 'http://localhost:3000' build-deploy: - needs: test + needs: + - test + - test-cypress runs-on: ubuntu-latest steps: - name: Checkout From 2ec2ef3566657c2cdd1b60af1910c5a89c449902 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:08:53 +0100 Subject: [PATCH 09/17] Add steps --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8ab085f0b..8850928e5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -50,6 +50,7 @@ jobs: test-cypress: runs-on: ubuntu-latest + steps: - name: Checkout uses: actions/checkout@v1 From a2d529668312134b0eb852c6088a9b109438d994 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:09:54 +0100 Subject: [PATCH 10/17] Moar fixes --- .github/workflows/main.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8850928e5..5d5e51056 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -64,12 +64,12 @@ jobs: run: yarn install --frozen-lock-file - name: Cypress run - uses: cypress-io/github-action@v4 - with: - start: yarn start & yarn start:wc & wait - wait-on: 'http://localhost:3001' - env: - PUBLIC_URL: 'http://localhost:3000' + uses: cypress-io/github-action@v4 + with: + start: yarn start & yarn start:wc & wait + wait-on: 'http://localhost:3001' + env: + PUBLIC_URL: 'http://localhost:3000' build-deploy: needs: From 959f03ff77be829cf585a01369b9d790f35387ba Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:11:03 +0100 Subject: [PATCH 11/17] I'll get thiss right eventually --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5d5e51056..61c505b13 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,7 +67,7 @@ jobs: uses: cypress-io/github-action@v4 with: start: yarn start & yarn start:wc & wait - wait-on: 'http://localhost:3001' + wait-on: 'http://localhost:3001' env: PUBLIC_URL: 'http://localhost:3000' From 08db433572c3892f26f225125235585fc0a5461c Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:12:17 +0100 Subject: [PATCH 12/17] Fix env indentation --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 61c505b13..5068d1cea 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -68,8 +68,8 @@ jobs: with: start: yarn start & yarn start:wc & wait wait-on: 'http://localhost:3001' - env: - PUBLIC_URL: 'http://localhost:3000' + env: + PUBLIC_URL: 'http://localhost:3000' build-deploy: needs: From 8f2bceac745ab89e1535a3da7d21c1caec7b3840 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:21:55 +0100 Subject: [PATCH 13/17] start multiple things --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5068d1cea..f6e8dbd55 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,7 +66,7 @@ jobs: - name: Cypress run uses: cypress-io/github-action@v4 with: - start: yarn start & yarn start:wc & wait + start: yarn start, yarn start:wc wait-on: 'http://localhost:3001' env: PUBLIC_URL: 'http://localhost:3000' From 7971217350e08db0927d6cf08ec2070b978cf1ef Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Tue, 2 Aug 2022 17:27:21 +0100 Subject: [PATCH 14/17] Wait on both services to respond --- .github/workflows/main.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f6e8dbd55..515983111 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -66,8 +66,11 @@ jobs: - name: Cypress run uses: cypress-io/github-action@v4 with: - start: yarn start, yarn start:wc - wait-on: 'http://localhost:3001' + install: false + start: | + yarn start + yarn start:wc + wait-on: 'http://localhost:3000, http://localhost:3001' env: PUBLIC_URL: 'http://localhost:3000' From 5a4a7bb8d85e203a531e5d76f6105e871289f4d2 Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Wed, 3 Aug 2022 08:59:28 +0100 Subject: [PATCH 15/17] Add a wait for services in CircleCI --- .circleci/config.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9f938be00..eac5d2a9a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -65,6 +65,9 @@ jobs: environment: PUBLIC_URL: http://localhost:3000 background: true + - run: + name: Wait for services to be ready + command: dockerize -wait http://localhost:3000 -wait http://localhost:3001 - run: name: Cypress integration tests command: yarn exec cypress run From 022bd01503c2804310687fcbc6c1fb7ac1b85ada Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Wed, 3 Aug 2022 09:02:33 +0100 Subject: [PATCH 16/17] Extend timeout to 30s --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index eac5d2a9a..b66df7616 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,7 +67,7 @@ jobs: background: true - run: name: Wait for services to be ready - command: dockerize -wait http://localhost:3000 -wait http://localhost:3001 + command: dockerize -wait http://localhost:3000 -wait http://localhost:3001 -timeout 30s - run: name: Cypress integration tests command: yarn exec cypress run From a7d9da6488eceeed73e52ce3a0e294b22bf04d8e Mon Sep 17 00:00:00 2001 From: "Patrick J. Cherry" Date: Wed, 3 Aug 2022 09:06:34 +0100 Subject: [PATCH 17/17] Update timeout to 120s (!) --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index b66df7616..3d7f4ae39 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -67,7 +67,7 @@ jobs: background: true - run: name: Wait for services to be ready - command: dockerize -wait http://localhost:3000 -wait http://localhost:3001 -timeout 30s + command: dockerize -wait http://localhost:3000 -wait http://localhost:3001 -timeout 120s - run: name: Cypress integration tests command: yarn exec cypress run