diff options
| author | Lex Neva <lexelby@users.noreply.github.com> | 2020-04-28 12:34:05 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-04-28 18:34:05 +0200 |
| commit | cb2b4e3522cb7f426ba5ad3e68deb9e6ccc581ec (patch) | |
| tree | 2a2f38823c3c9f0b5439ce2aa7c9040299109292 /lib | |
| parent | eb526927e16954390d06929535d6f5c3766b5f6c (diff) | |
electron simulator (#531)
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/api/__init__.py | 1 | ||||
| -rw-r--r-- | lib/api/server.py | 110 | ||||
| -rw-r--r-- | lib/api/simulator.py | 3 | ||||
| -rw-r--r-- | lib/api/stitch_plan.py | 17 | ||||
| -rw-r--r-- | lib/extensions/__init__.py | 6 | ||||
| -rw-r--r-- | lib/extensions/simulator.py | 17 | ||||
| -rw-r--r-- | lib/stitch_plan/stitch.py | 3 | ||||
| -rw-r--r-- | lib/stitch_plan/stitch_plan.py | 11 | ||||
| -rw-r--r-- | lib/threads/color.py | 19 | ||||
| -rw-r--r-- | lib/utils/geometry.py | 3 | ||||
| -rw-r--r-- | lib/utils/json.py | 15 |
11 files changed, 200 insertions, 5 deletions
diff --git a/lib/api/__init__.py b/lib/api/__init__.py new file mode 100644 index 00000000..f93e59a9 --- /dev/null +++ b/lib/api/__init__.py @@ -0,0 +1 @@ +from .server import APIServer
\ No newline at end of file diff --git a/lib/api/server.py b/lib/api/server.py new file mode 100644 index 00000000..c57a1785 --- /dev/null +++ b/lib/api/server.py @@ -0,0 +1,110 @@ +import errno +import logging +import socket +from threading import Thread +import time + +from flask import Flask, request, g +import requests + +from ..utils.json import InkStitchJSONEncoder +from .simulator import simulator +from .stitch_plan import stitch_plan + + +class APIServer(Thread): + def __init__(self, *args, **kwargs): + self.extension = args[0] + Thread.__init__(self, *args[1:], **kwargs) + self.daemon = True + self.shutting_down = False + self.app = None + self.host = None + self.port = None + self.ready = False + + self.__setup_app() + + def __setup_app(self): # noqa: C901 + self.app = Flask(__name__) + self.app.json_encoder = InkStitchJSONEncoder + + self.app.register_blueprint(simulator, url_prefix="/simulator") + self.app.register_blueprint(stitch_plan, url_prefix="/stitch_plan") + + @self.app.before_request + def store_extension(): + # make the InkstitchExtension object available to the view handling + # this request + g.extension = self.extension + + @self.app.route('/shutdown', methods=['POST']) + def shutdown(): + self.shutting_down = True + request.environ.get('werkzeug.server.shutdown')() + return "shutting down" + + @self.app.route('/ping') + def ping(): + return "pong" + + def stop(self): + # for whatever reason, shutting down only seems possible in + # the context of a flask request, so we'll just make one + requests.post("http://%s:%s/shutdown" % (self.host, self.port)) + + def disable_logging(self): + logging.getLogger('werkzeug').setLevel(logging.ERROR) + + def run(self): + self.disable_logging() + + self.host = "127.0.0.1" + self.port = 5000 + + while True: + try: + self.app.run(self.host, self.port, threaded=True) + except socket.error as e: + if e.errno == errno.EADDRINUSE: + self.port += 1 + continue + else: + raise + else: + break + + def ready_checker(self): + """Wait until the server is started. + + Annoyingly, there's no way to get a callback to be run when the Flask + server starts. Instead, we'll have to poll. + """ + + while True: + if self.port: + try: + response = requests.get("http://%s:%s/ping" % (self.host, self.port)) + if response.status_code == 200: + break + except socket.error, e: + if e.errno == errno.ECONNREFUSED: + pass + else: + raise + + time.sleep(0.1) + + def start_server(self): + """Start the API server. + + returns: port (int) -- the port that the server is listening on + (on localhost) + """ + + checker = Thread(target=self.ready_checker) + checker.start() + self.start() + checker.join() + + return self.port diff --git a/lib/api/simulator.py b/lib/api/simulator.py new file mode 100644 index 00000000..d11bd308 --- /dev/null +++ b/lib/api/simulator.py @@ -0,0 +1,3 @@ +from flask import Blueprint + +simulator = Blueprint('simulator', __name__) diff --git a/lib/api/stitch_plan.py b/lib/api/stitch_plan.py new file mode 100644 index 00000000..fd6bf9c9 --- /dev/null +++ b/lib/api/stitch_plan.py @@ -0,0 +1,17 @@ +from flask import Blueprint, g, jsonify + +from ..stitch_plan import patches_to_stitch_plan + + +stitch_plan = Blueprint('stitch_plan', __name__) + + +@stitch_plan.route('') +def get_stitch_plan(): + if not g.extension.get_elements(): + return dict(colors=[], stitch_blocks=[], commands=[]) + + patches = g.extension.elements_to_patches(g.extension.elements) + stitch_plan = patches_to_stitch_plan(patches) + + return jsonify(stitch_plan) diff --git a/lib/extensions/__init__.py b/lib/extensions/__init__.py index 6f9ef5c4..3c67700b 100644 --- a/lib/extensions/__init__.py +++ b/lib/extensions/__init__.py @@ -16,7 +16,7 @@ from output import Output from params import Params from print_pdf import Print from remove_embroidery_settings import RemoveEmbroiderySettings -from simulate import Simulate +from simulator import Simulator from stitch_plan_preview import StitchPlanPreview from zip import Zip @@ -25,7 +25,6 @@ __all__ = extensions = [Embroider, Install, Params, Print, - Simulate, Input, Output, Zip, @@ -40,4 +39,5 @@ __all__ = extensions = [Embroider, Troubleshoot, RemoveEmbroiderySettings, BreakApart, - ImportThreadlist] + ImportThreadlist, + Simulator] diff --git a/lib/extensions/simulator.py b/lib/extensions/simulator.py new file mode 100644 index 00000000..1c0627ba --- /dev/null +++ b/lib/extensions/simulator.py @@ -0,0 +1,17 @@ +from ..api import APIServer +from ..gui import open_url + +from .base import InkstitchExtension + + +class Simulator(InkstitchExtension): + def __init__(self): + InkstitchExtension.__init__(self) + + def effect(self): + api_server = APIServer(self) + port = api_server.start_server() + electron = open_url("/simulator?port=%d" % port) + electron.wait() + api_server.stop() + api_server.join() diff --git a/lib/stitch_plan/stitch.py b/lib/stitch_plan/stitch.py index 462634cb..ccbea12e 100644 --- a/lib/stitch_plan/stitch.py +++ b/lib/stitch_plan/stitch.py @@ -31,3 +31,6 @@ class Stitch(Point): def copy(self): return Stitch(self.x, self.y, self.color, self.jump, self.stop, self.trim, self.color_change, self.no_ties) + + def __json__(self): + return vars(self) diff --git a/lib/stitch_plan/stitch_plan.py b/lib/stitch_plan/stitch_plan.py index ac6b3b8d..0b2f75cd 100644 --- a/lib/stitch_plan/stitch_plan.py +++ b/lib/stitch_plan/stitch_plan.py @@ -98,6 +98,14 @@ class StitchPlan(object): def __repr__(self): return "StitchPlan(%s)" % ", ".join(repr(cb) for cb in self.color_blocks) + def __json__(self): + return dict(color_blocks=self.color_blocks, + num_stops=self.num_stops, + num_trims=self.num_trims, + num_stitches=self.num_stitches, + bounding_box=self.bounding_box + ) + @property def num_colors(self): """Number of unique colors in the stitch plan.""" @@ -175,6 +183,9 @@ class ColorBlock(object): def __delitem__(self, item): del self.stitches[item] + def __json__(self): + return dict(color=self.color, stitches=self.stitches) + def has_color(self): return self._color is not None diff --git a/lib/threads/color.py b/lib/threads/color.py index 8c596e44..ea9dab06 100644 --- a/lib/threads/color.py +++ b/lib/threads/color.py @@ -1,7 +1,8 @@ -import simplestyle -import re import colorsys from pyembroidery.EmbThread import EmbThread +import re + +import simplestyle class ThreadColor(object): @@ -27,6 +28,20 @@ class ThreadColor(object): self.number = number self.manufacturer = manufacturer + def __json__(self): + jsonified = self._as_dict() + jsonified["visible_on_white"] = self.visible_on_white._as_dict() + + return jsonified + + def _as_dict(self): + return dict(name=self.name, + number=self.number, + manufacturer=self.manufacturer, + rgb=self.rgb, + hex=self.to_hex_str(), + ) + def __eq__(self, other): if isinstance(other, ThreadColor): return self.rgb == other.rgb diff --git a/lib/utils/geometry.py b/lib/utils/geometry.py index 647636c8..bae32c32 100644 --- a/lib/utils/geometry.py +++ b/lib/utils/geometry.py @@ -63,6 +63,9 @@ class Point: self.x = x self.y = y + def __json__(self): + return vars(self) + def __add__(self, other): return Point(self.x + other.x, self.y + other.y) diff --git a/lib/utils/json.py b/lib/utils/json.py new file mode 100644 index 00000000..c670a46b --- /dev/null +++ b/lib/utils/json.py @@ -0,0 +1,15 @@ +from flask.json import JSONEncoder + + +class InkStitchJSONEncoder(JSONEncoder): + """JSON encoder class that runs __json__ on an object if available. + + The __json__ method should return a JSON-compatible representation of the + object. + """ + + def default(self, obj): + try: + return obj.__json__() + except AttributeError: + return JSONEncoder.default(self, obj) |
