예제 #1
0
def check_printers():
    app.logger.debug("Checking known printers...")
    for raw_printer in printers.get_printers():
        printer = clients.get_printer_instance(raw_printer)
        if printer.hostname is not None:
            current_ip = network.get_avahi_address(printer.hostname)
            if current_ip is not None and current_ip != printer.ip:
                printer.ip = current_ip
                printer.update_network_host()
        else:
            hostname = network.get_avahi_hostname(printer.ip)
            if hostname is not None:
                printer.hostname = hostname
                printer.update_network_host()
        printer.is_alive()
        printers.update_printer(
            uuid=printer.uuid,
            name=printer.name,
            hostname=printer.hostname,
            ip=printer.ip,
            port=printer.port,
            protocol=printer.protocol,
            client=printer.client_name(),
            client_props={
                "version": printer.client_info.version,
                "connected": printer.client_info.connected,
                "access_level": printer.client_info.access_level,
                "api_key": printer.client_info.api_key,
                "webcam": printer.webcam(),
            },
            printer_props=printer.get_printer_props(),
        )
예제 #2
0
def printer_change_connection(uuid):
    # TODO this has to be streamlined, octoprint sometimes cannot handle two connect commands at once
    try:
        uuidmodule.UUID(uuid, version=4)
    except ValueError:
        return abort(make_response("", 400))
    printer = printers.get_printer(uuid)
    if printer is None:
        return abort(make_response("", 404))
    data = request.json
    if not data:
        return abort(make_response("", 400))
    state = data.get("state", None)
    printer_inst = clients.get_printer_instance(printer)
    if state == "online":
        r = printer_inst.connect_printer()
        return (("", 204) if r else
                ("Cannot change printer's connection state to online", 500))
    elif state == "offline":
        r = printer_inst.disconnect_printer()
        return (("", 204) if r else
                ("Cannot change printer's connection state to offline", 500))
    else:
        return abort(make_response("", 400))
    return "", 204
예제 #3
0
def printer_modify_job(uuid):
    # TODO allow users to pause/cancel only their own prints via printjob_id
    # And allow admins to pause/cancel anything
    # but that means creating a new tracking of current jobs on each printer
    # and does not handle prints issued by bypassing Karmen Hub
    # Alternative is to log who modified the current job into an admin-accessible eventlog
    # See https://trello.com/c/uiv0luZ8/142 for details
    try:
        uuidmodule.UUID(uuid, version=4)
    except ValueError:
        return abort(make_response("", 400))
    printer = printers.get_printer(uuid)
    if printer is None:
        return abort(make_response("", 404))
    data = request.json
    if not data:
        return abort(make_response("", 400))
    action = data.get("action", None)
    if not action:
        return abort(make_response("", 400))
    printer_inst = clients.get_printer_instance(printer)
    try:
        if printer_inst.modify_current_job(action):
            user = get_current_user()
            app.logger.info(
                "User %s successfully issued a modification (%s) of current job on printer %s",
                user["uuid"],
                action,
                uuid,
            )
            return "", 204
        return abort(make_response("", 409))
    except clients.utils.PrinterClientException as e:
        return abort(make_response(jsonify(message=str(e)), 400))
예제 #4
0
 def test_returns_octoprint_instance(self):
     octoprinter = get_printer_instance({
         "uuid": "20e91c14-c3e4-4fe9-a066-e69d53324a20",
         "client": "octoprint",
         "hostname": "octoprinter",
         "host": "1.2.3.4",
     })
     self.assertTrue(isinstance(octoprinter, Octoprint))
예제 #5
0
def printer_webcam_snapshot(uuid):
    """
    Instead of direct streaming from the end-devices, we are deferring
    the video-like feature to the clients. This (in case of MJPEG) brings
    significant performance gains throughout the whole pipeline.

    This endpoint serves as a redis-backed (due to multithreaded production setup
    with uwsgi) microcache of latest captured image from any
    given printer. New snapshot is requested, but the old one is served to
    the client. In case of the first request, a 202 is returned and the client
    should ask again. By asking periodically for new snapshots, the client
    can emulate a video-like experience. Since we have no sound, this should be
    fine.
    """
    try:
        uuidmodule.UUID(uuid, version=4)
    except ValueError:
        return abort(make_response("", 400))
    printer = printers.get_printer(uuid)
    if printer is None:
        return abort(make_response("", 404))
    printer_inst = clients.get_printer_instance(printer)
    if printer_inst.client_info.webcam is None:
        return abort(make_response("", 404))
    snapshot_url = printer_inst.client_info.webcam.get("snapshot")
    if snapshot_url is None:
        return abort(make_response("", 404))

    # process current future if done
    if FUTURES_MICROCACHE.get(uuid) and FUTURES_MICROCACHE.get(uuid).done():
        WEBCAM_MICROCACHE[uuid] = FUTURES_MICROCACHE[uuid].result()
        try:
            del FUTURES_MICROCACHE[uuid]
        except Exception:
            # that's ok, probably a race condition
            pass
    # issue a new future if not present
    if not FUTURES_MICROCACHE.get(uuid):
        FUTURES_MICROCACHE[uuid] = executor.submit(_get_webcam_snapshot,
                                                   snapshot_url)

    if WEBCAM_MICROCACHE.get(uuid) is not None:
        response = WEBCAM_MICROCACHE.get(uuid)
        return (
            response.content,
            200,
            {
                "Content-Type": response.headers.get("content-type",
                                                     "image/jpeg")
            },
        )
    # There should be a future running, if the client retries, they should
    # eventually get a snapshot.
    # We don't want to end with an error here, so the clients keep retrying
    return "", 202
예제 #6
0
def get_printer_inst(org_uuid, uuid):
    validate_uuid(uuid)
    printer = printers.get_printer(uuid)
    if printer is None or printer.get("organization_uuid") != org_uuid:
        return abort(make_response(jsonify(message="Not found"), 404))
    network_client = network_clients.get_network_client(
        printer["network_client_uuid"])
    printer_data = dict(network_client)
    printer_data.update(dict(printer))
    printer_inst = clients.get_printer_instance(printer_data)
    return printer_inst
예제 #7
0
def check_printer(printer_uuid):
    app.logger.debug("Checking printer %s" % printer_uuid)
    raw_printer = printers.get_printer(printer_uuid)
    # todo: The `get_printer` method should raise an exception instead of returning None
    if raw_printer is None:
        app.logger.info(
            "Printer was deleted after it was scheduled for update.")
        return
    raw_client = network_clients.get_network_client(
        raw_printer["network_client_uuid"])
    printer_data = dict(raw_client)
    printer_data.update(raw_printer)
    printer = clients.get_printer_instance(printer_data)
    # websocket printers are not expected to change
    if printer.protocol in ["http", "https"]:
        if printer.hostname is not None:
            current_ip = network.get_avahi_address(printer.hostname)
            if current_ip is not None and current_ip != printer.ip:
                printer.ip = current_ip
                printer.update_network_base()
        hostname = network.get_avahi_hostname(printer.ip)
        if hostname is not None and hostname != printer.hostname:
            printer.hostname = hostname
            printer.update_network_base()
    now = datetime.now()
    if now.minute % 15 == 0 and printer.client_info.connected:
        printer.sniff()
        printer.karmen_sniff()
    else:
        printer.is_alive()

    if (printer.client_info.pill_info and printer.client_info.pill_info.get(
            "update_status", None) is not None):
        printer.karmen_sniff()

    if printer.hostname != raw_client.get(
            "hostname") or printer.ip != raw_client.get("ip"):
        network_clients.update_network_client(
            uuid=raw_client["uuid"],
            hostname=printer.hostname,
            ip=printer.ip,
        )
    printers.update_printer(
        uuid=printer.uuid,
        client_props={
            "version": printer.client_info.version,
            "connected": printer.client_info.connected,
            "access_level": printer.client_info.access_level,
            "api_key": printer.client_info.api_key,
            "webcam": printer.client_info.webcam,
            "plugins": printer.client_info.plugins,
            "pill_info": printer.client_info.pill_info,
        },
    )
예제 #8
0
 def test_returns_octoprint_instance(self):
     octoprinter = get_printer_instance(
         {
             "uuid": "20e91c14-c3e4-4fe9-a066-e69d53324a20",
             "network_client_uuid": "900c73b8-1f12-4027-941a-e4b29531e8e3",
             "organization_uuid": UUID_ORG,
             "client": "octoprint",
             "hostname": "octoprinter",
             "host": "1.2.3.4",
         }
     )
     self.assertTrue(isinstance(octoprinter, Octoprint))
예제 #9
0
def printjob_create():
    data = request.json
    if not data:
        return abort(make_response("", 400))
    gcode_id = data.get("gcode", None)
    printer_uuid = data.get("printer", None)
    if not gcode_id or not printer_uuid:
        return abort(make_response("", 400))
    printer = printers.get_printer(printer_uuid)
    if printer is None:
        return abort(make_response("", 404))
    gcode = gcodes.get_gcode(gcode_id)
    if gcode is None:
        return abort(make_response("", 404))
    try:
        printer_inst = clients.get_printer_instance(printer)
        uploaded = printer_inst.upload_and_start_job(gcode["absolute_path"],
                                                     gcode["path"])
        if not uploaded:
            return abort(
                make_response(
                    jsonify(message="Cannot upload the g-code to the printer"),
                    500))
        printjob_id = printjobs.add_printjob(
            gcode_id=gcode["id"],
            printer_uuid=printer["uuid"],
            user_uuid=get_current_user()["uuid"],
            gcode_data={
                "id": gcode["id"],
                "filename": gcode["filename"],
                "size": gcode["size"],
                "available": True,
            },
            printer_data={
                "ip": printer["ip"],
                "port": printer["port"],
                "hostname": printer["hostname"],
                "name": printer["name"],
                "client": printer["client"],
            },
        )
        return (
            jsonify({
                "id": printjob_id,
                "user_uuid": get_current_user()["uuid"]
            }),
            201,
        )
    except clients.utils.PrinterClientException:
        return abort(make_response("", 409))
예제 #10
0
def check_printer(printer_uuid):
    app.logger.debug("Checking printer %s" % printer_uuid)
    raw_printer = printers.get_printer(printer_uuid)
    raw_client = network_clients.get_network_client(
        raw_printer["network_client_uuid"])
    printer_data = dict(raw_client)
    printer_data.update(raw_printer)
    printer = clients.get_printer_instance(printer_data)
    # websocket printers are not expected to change
    if printer.protocol in ["http", "https"]:
        if printer.hostname is not None:
            current_ip = network.get_avahi_address(printer.hostname)
            if current_ip is not None and current_ip != printer.ip:
                printer.ip = current_ip
                printer.update_network_base()
        hostname = network.get_avahi_hostname(printer.ip)
        if hostname is not None and hostname != printer.hostname:
            printer.hostname = hostname
            printer.update_network_base()
    now = datetime.now()
    if now.minute % 15 == 0 and printer.client_info.connected:
        printer.sniff()
    else:
        printer.is_alive()

    if printer.hostname != raw_client.get(
            "hostname") or printer.ip != raw_client.get("ip"):
        network_clients.update_network_client(
            uuid=raw_client["uuid"],
            hostname=printer.hostname,
            ip=printer.ip,
        )

    printers.update_printer(
        uuid=printer.uuid,
        client_props={
            "version": printer.client_info.version,
            "connected": printer.client_info.connected,
            "access_level": printer.client_info.access_level,
            "api_key": printer.client_info.api_key,
            "webcam": printer.client_info.webcam,
            "plugins": printer.client_info.plugins,
        },
    )
예제 #11
0
def sniff_printer(hostname, ip):
    app.logger.info("Sniffing printer on %s (%s) - trying http" %
                    (ip, hostname))
    printer = clients.get_printer_instance({
        "uuid": uuid.uuid4(),
        "hostname": hostname,
        "ip": ip,
        "client": "octoprint",  # TODO not only octoprint
        "protocol": "http",
    })

    printer.sniff()
    # Let's try a secured connection
    if not printer.client_info.connected:
        printer.protocol = "https"
        app.logger.info("Sniffing printer on %s (%s) - trying https" %
                        (ip, hostname))
        printer.sniff()

    # Not even on https, no reason to do anything
    if not printer.client_info.connected:
        app.logger.info("Sniffing printer on %s (%s) - no luck" %
                        (ip, hostname))
        return

    app.logger.info("Sniffing printer on %s (%s) - success" % (ip, hostname))
    save_printer_data(
        uuid=printer.uuid,
        name=hostname or ip,
        hostname=hostname,
        ip=ip,
        protocol=printer.protocol,
        client=printer.client_name(),
        client_props={
            "version": printer.client_info.version,
            "connected": printer.client_info.connected,
            "access_level": printer.client_info.access_level,
            "api_key": printer.client_info.api_key,
            "webcam": printer.webcam(),
        },
        printer_props=printer.get_printer_props(),
    )
예제 #12
0
def make_printer_response(printer, fields):
    if not isinstance(printer, clients.utils.PrinterClient):
        printer_inst = clients.get_printer_instance(printer)
    else:
        printer_inst = printer
    data = {
        "uuid": printer_inst.uuid,
        "client": {
            "name":
            printer_inst.client_name(),
            "version":
            printer_inst.client_info.version,
            "connected":
            bool(printer_inst.client_info.connected),
            "access_level":
            printer_inst.client_info.access_level,
            "api_key":
            printer_inst.client_info.api_key[0:2] +
            "*" * 30  # Add star padding to 32 characters
            if printer_inst.client_info.api_key else None,
        },
        "printer_props": printer_inst.get_printer_props(),
        "name": printer_inst.name,
        "hostname": printer_inst.hostname,
        "ip": printer_inst.ip,
        "port": printer_inst.port,
        "protocol": printer_inst.protocol,
    }
    if "status" in fields:
        data["status"] = printer_inst.status()
    if "webcam" in fields:
        webcam_data = printer_inst.client_info.webcam or {}
        data["webcam"] = {
            "url": "/printers/%s/webcam-snapshot" % (printer_inst.uuid, ),
            "flipHorizontal": webcam_data.get("flipHorizontal"),
            "flipVertical": webcam_data.get("flipVertical"),
            "rotate90": webcam_data.get("rotate90"),
        }
    if "job" in fields:
        data["job"] = printer_inst.job()
    return data
예제 #13
0
def printer_create(org_uuid):
    data = request.json
    if not data:
        return abort(make_response(jsonify(message="Missing payload"), 400))
    uuid = guid.uuid4()
    ip = data.get("ip", None)
    port = data.get("port", None)
    hostname = data.get("hostname", None)
    name = data.get("name", None)
    api_key = data.get("api_key", None)
    protocol = data.get("protocol", "http")
    path = data.get("path", "")
    token = data.get("token", None)

    if token is not None and token != "":
        if not app.config.get("CLOUD_MODE"):
            return abort(make_response(jsonify(message="Missing token"), 400))
        existing_network_client = network_clients.get_network_client_by_socket_token(
            token)
        if existing_network_client:
            existing_printer = printers.get_printer_by_network_client_uuid(
                org_uuid, existing_network_client.get("uuid"))
            if existing_printer is not None:
                return abort(
                    make_response(jsonify(message="Printer exists"), 409))
        path = ""
        hostname = ""
        ip = ""
        protocol = ""
        port = 0
    else:
        if app.config.get("CLOUD_MODE"):
            return abort(
                make_response(
                    jsonify(
                        message=
                        "Cannot add printer without a token in CLOUD_MODE"),
                    500,
                ))
        if ((not ip and not hostname) or not name
                or protocol not in ["http", "https"]
                or (hostname
                    and re.match(r"^[0-9a-zA-Z.-]+\.local$", hostname) is None)
                or
            (ip
             and re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip) is None)
                or (port and re.match(r"^\d{0,5}$", str(port)) is None)):
            return abort(
                make_response(
                    jsonify(
                        message="Missing some network data about the printer"),
                    400))

        if hostname and not ip:
            ip = network.get_avahi_address(hostname)
            if not ip:
                return abort(
                    make_response(
                        jsonify(message="Cannot resolve %s with mDNS" %
                                hostname), 500))
        if ip and not hostname:
            hostname = network.get_avahi_hostname(ip)

        existing_network_client = network_clients.get_network_client_by_props(
            hostname, ip, port, path)
        if existing_network_client:
            existing_printer = printers.get_printer_by_network_client_uuid(
                org_uuid, existing_network_client.get("uuid"))
            if existing_printer is not None:
                return abort(
                    make_response(jsonify(message="Printer exists"), 409))

    if not existing_network_client:
        existing_network_client = {
            "uuid": guid.uuid4(),
            "hostname": hostname,
            "client": "octoprint",  # TODO make this more generic
            "protocol": protocol,
            "ip": ip,
            "port": port,
            "path": path,
            "token": token,
        }
        network_clients.add_network_client(**existing_network_client)

    printer = clients.get_printer_instance({
        "uuid":
        uuid,
        "network_client_uuid":
        existing_network_client["uuid"],
        "organization_uuid":
        org_uuid,
        "name":
        name,
        "client":
        existing_network_client["client"],
        "protocol":
        existing_network_client["protocol"],
        "hostname":
        existing_network_client["hostname"],
        "ip":
        existing_network_client["ip"],
        "port":
        existing_network_client["port"],
        "path":
        existing_network_client["path"],
        "token":
        existing_network_client["token"],
    })
    printer.add_api_key(api_key)
    printer.update_network_base()
    printer.sniff()
    printers.add_printer(
        uuid=uuid,
        network_client_uuid=existing_network_client["uuid"],
        organization_uuid=org_uuid,
        name=name,
        client=printer.client_name(),
        client_props={
            "version": printer.client_info.version,
            "connected": printer.client_info.connected,
            "access_level": printer.client_info.access_level,
            "api_key": printer.client_info.api_key,
            "webcam": printer.client_info.webcam,
            "plugins": printer.client_info.plugins,
        },
    )
    # TODO cache webcam, job, status for a small amount of time in this client
    return jsonify(make_printer_response(printer,
                                         ["status", "webcam", "job"])), 201
예제 #14
0
def make_printer_response(printer, fields):
    if not isinstance(printer, clients.utils.PrinterClient):
        network_client = network_clients.get_network_client(
            printer["network_client_uuid"])
        printer_data = dict(network_client)
        printer_data.update(dict(printer))
        printer_inst = clients.get_printer_instance(printer_data)
    else:
        printer_inst = printer
    data = {
        "uuid":
        printer_inst.uuid,
        "client": {
            "name":
            printer_inst.client_name(),
            "version":
            printer_inst.client_info.version,
            "connected":
            bool(printer_inst.client_info.connected),
            "access_level":
            printer_inst.client_info.access_level,
            "api_key":
            printer_inst.client_info.api_key[0:2] +
            "*" * 30  # Add star padding to 32 characters
            if printer_inst.client_info.api_key else None,
            "plugins":
            printer_inst.client_info.plugins,
        },
        "printer_props":
        printer_inst.get_printer_props(),
        "name":
        printer_inst.name,
        "protocol":
        printer_inst.protocol,
        "token":
        printer_inst.token[0:2] + "*" * 30  # Add star padding to 32 characters
        if printer_inst.token else None,
        "hostname":
        printer_inst.hostname,
        "ip":
        printer_inst.ip,
        "port":
        printer_inst.port,
        "path":
        printer_inst.path,
    }

    if "status" in fields:
        data["status"] = printer_inst.status()
    if "webcam" in fields:
        webcam_data = printer_inst.client_info.webcam or {}
        data["webcam"] = {
            "url":
            "/organizations/%s/printers/%s/webcam-snapshot" % (
                printer_inst.organization_uuid,
                printer_inst.uuid,
            ),
            "flipHorizontal":
            webcam_data.get("flipHorizontal"),
            "flipVertical":
            webcam_data.get("flipVertical"),
            "rotate90":
            webcam_data.get("rotate90"),
        }
    if "job" in fields:
        data["job"] = printer_inst.job()
    if "lights" in fields:
        try:
            data["lights"] = "on" if printer_inst.are_lights_on() else "off"
        except PrinterClientException:
            data["lights"] = "unavailable"
    return data
예제 #15
0
def printer_patch(uuid):
    try:
        uuidmodule.UUID(uuid, version=4)
    except ValueError:
        return abort(make_response("", 400))
    printer = printers.get_printer(uuid)
    if printer is None:
        return abort(make_response("", 404))
    data = request.json
    if not data:
        return abort(make_response("", 400))
    name = data.get("name", printer["name"])
    protocol = data.get("protocol", printer["protocol"])
    api_key = data.get("api_key", printer["client_props"].get("api_key", None))
    printer_props = data.get("printer_props", {})

    # TODO it might be necessary to update ip, hostname, port as well eventually
    if not name or protocol not in ["http", "https"]:
        return abort(make_response("", 400))
    printer_inst = clients.get_printer_instance(printer)
    printer_inst.add_api_key(api_key)
    if data.get("api_key", "-1") != "-1" and data.get(
            "api_key", "-1") != printer["client_props"].get("api_key", None):
        printer_inst.sniff(
        )  # this can probably be offloaded to check_printer task
    if printer_props:
        if not printer_inst.get_printer_props():
            printer_inst.printer_props = {}
        # This is effectively the only place where printer_props "validation" happens
        printer_inst.get_printer_props().update({
            k: printer_props[k]
            for k in [
                "filament_type",
                "filament_color",
                "bed_type",
                "tool0_diameter",
                "note",
            ] if k in printer_props
        })
    printers.update_printer(
        uuid=printer_inst.uuid,
        name=name,
        hostname=printer_inst.hostname,
        ip=printer_inst.ip,
        port=printer_inst.port,
        protocol=protocol,
        client=printer_inst.client_name(),
        client_props={
            "version": printer_inst.client_info.version,
            "connected": printer_inst.client_info.connected,
            "access_level": printer_inst.client_info.access_level,
            "api_key": printer_inst.client_info.api_key,
            "webcam": printer_inst.webcam(),
        },
        printer_props=printer_inst.get_printer_props(),
    )
    # TODO cache webcam, job, status for a small amount of time in client
    return (
        jsonify(
            make_printer_response(printer_inst, ["status", "webcam", "job"])),
        200,
    )
예제 #16
0
def printer_create():
    data = request.json
    if not data:
        return abort(make_response("", 400))
    uuid = uuidmodule.uuid4()
    ip = data.get("ip", None)
    port = data.get("port", None)
    hostname = data.get("hostname", None)
    name = data.get("name", None)
    api_key = data.get("api_key", None)
    protocol = data.get("protocol", "http")

    if ((not ip and not hostname) or not name
            or protocol not in ["http", "https"] or
        (hostname and re.match(r"^[0-9a-zA-Z.-]+\.local$", hostname) is None)
            or
        (ip and re.match(r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$", ip) is None)
            or (port and re.match(r"^\d{0,5}$", str(port)) is None)):
        return abort(make_response("", 400))

    if hostname and not ip:
        ip = network.get_avahi_address(hostname)
        if not ip:
            return abort(
                make_response("Cannot resolve %s with mDNS" % hostname, 500))
    if ip and not hostname:
        hostname = network.get_avahi_hostname(ip)

    if printers.get_printer_by_network_props(hostname, ip, port) is not None:
        return abort(make_response("", 409))

    printer = clients.get_printer_instance({
        "uuid": uuid,
        "hostname": hostname,
        "ip": ip,
        "port": port,
        "name": name,
        "protocol": protocol,
        "client": "octoprint",  # TODO make this more generic
    })
    printer.add_api_key(api_key)
    printer.sniff()
    printers.add_printer(
        uuid=uuid,
        name=name,
        hostname=hostname,
        ip=ip,
        port=port,
        protocol=printer.protocol,
        client=printer.client_name(),
        client_props={
            "version": printer.client_info.version,
            "connected": printer.client_info.connected,
            "access_level": printer.client_info.access_level,
            "api_key": printer.client_info.api_key,
            "webcam": printer.webcam(),
        },
    )
    # TODO cache webcam, job, status for a small amount of time in client
    return jsonify(make_printer_response(printer,
                                         ["status", "webcam", "job"])), 201
예제 #17
0
 def test_throws_on_unknown_client(self):
     with self.assertRaises(RuntimeError) as context:
         get_printer_instance({"client": "unknown"})
         self.assertTrue("unknown client unknown" in str(context.exception))
예제 #18
0
def printjob_create(org_uuid):
    data = request.json
    if not data:
        return abort(make_response(jsonify(message="Missing payload"), 400))
    gcode_uuid = data.get("gcode", None)
    printer_uuid = data.get("printer",
                            None)  # FIXME: this should be part of the path
    if not gcode_uuid or not printer_uuid:
        return abort(
            make_response(
                jsonify(message="Missing gcode_uuid or printer_uuid"), 400))

    printer = printers.get_printer(printer_uuid)
    if not printer or printer['organization_uuid'] != org_uuid:
        raise http_exceptions.UnprocessableEntity(
            f"Invalid printer {printer_uuid} - does not exist.")

    gcode = gcodes.get_gcode(gcode_uuid)
    if not gcode:
        raise http_exceptions.UnprocessableEntity(
            "Invalid gcode {gcode_uuid} - does not exist.")

    network_client = network_clients.get_network_client(
        printer["network_client_uuid"])
    printer_data = dict(network_client)
    printer_data.update(dict(printer))
    printer_inst = clients.get_printer_instance(printer_data)
    try:
        printer_inst.upload_and_start_job(gcode["absolute_path"],
                                          gcode["path"])
    except DeviceInvalidState as e:
        raise http_exceptions.Conflict(*e.args)
    except DeviceCommunicationError as e:
        raise http_exceptions.GatewayTimeout(*e.args)
    # TODO: robin - add_printjob should be method of printer and printer a
    #               method of organization
    printjob_uuid = printjobs.add_printjob(
        gcode_uuid=gcode["uuid"],
        organization_uuid=org_uuid,
        printer_uuid=printer["uuid"],
        user_uuid=get_current_user()["uuid"],
        gcode_data={
            "uuid": gcode["uuid"],
            "filename": gcode["filename"],
            "size": gcode["size"],
            "available": True,
        },
        # FIXME: printer data should be kept in printer object only
        printer_data={
            "ip": printer_inst.ip,
            "port": printer_inst.port,
            "hostname": printer_inst.hostname,
            "name": printer_inst.name,
            "client": printer_inst.client,
        },
    )
    return (
        jsonify({
            "uuid": printjob_uuid,
            "user_uuid": get_current_user()["uuid"]
        }),
        201,
    )
예제 #19
0
 def test_throws_on_none(self):
     with self.assertRaises(RuntimeError) as context:
         get_printer_instance(None)
         self.assertTrue("no client defined" in str(context.exception))
예제 #20
0
def printjob_create(org_uuid):
    data = request.json
    if not data:
        return abort(make_response(jsonify(message="Missing payload"), 400))
    gcode_uuid = data.get("gcode", None)
    printer_uuid = data.get("printer", None)
    if not gcode_uuid or not printer_uuid:
        return abort(
            make_response(jsonify(message="Missing gcode_uuid or printer_uuid"), 400)
        )
    validate_uuid(gcode_uuid)
    validate_uuid(printer_uuid)
    printer = printers.get_printer(printer_uuid)
    if printer is None or printer["organization_uuid"] != org_uuid:
        return abort(make_response(jsonify(message="Not found"), 404))
    gcode = gcodes.get_gcode(gcode_uuid)
    if gcode is None:
        return abort(make_response(jsonify(message="Not found"), 404))
    try:
        network_client = network_clients.get_network_client(
            printer["network_client_uuid"]
        )
        printer_data = dict(network_client)
        printer_data.update(dict(printer))
        printer_inst = clients.get_printer_instance(printer_data)
        uploaded = printer_inst.upload_and_start_job(
            gcode["absolute_path"], gcode["path"]
        )
        if not uploaded:
            return abort(
                make_response(
                    jsonify(message="Cannot upload the g-code to the printer"), 500
                )
            )
        printjob_uuid = guid.uuid4()
        printjobs.add_printjob(
            uuid=printjob_uuid,
            gcode_uuid=gcode["uuid"],
            organization_uuid=org_uuid,
            printer_uuid=printer["uuid"],
            user_uuid=get_current_user()["uuid"],
            gcode_data={
                "uuid": gcode["uuid"],
                "filename": gcode["filename"],
                "size": gcode["size"],
                "available": True,
            },
            printer_data={
                "ip": printer_inst.ip,
                "port": printer_inst.port,
                "hostname": printer_inst.hostname,
                "name": printer_inst.name,
                "client": printer_inst.client,
            },
        )
        return (
            jsonify({"uuid": printjob_uuid, "user_uuid": get_current_user()["uuid"]}),
            201,
        )
    except clients.utils.PrinterClientException as e:
        app.logger.error(e)
        return abort(
            make_response(
                jsonify(message="Cannot schedule a printjob: %s" % str(e)), 409
            )
        )