예제 #1
0
def register_device():
    metadata = request.get_json()
    device_id = metadata.pop('id')

    if device_id is None:
        return jsonify(success=False, error_code="MISSING_DEVICE_ID"), 400

    if not isinstance(device_id, int):
        return jsonify(success=False,
                       error_code="DEVICE_ID_NOT_AN_INTEGER"), 400

    # check if the device is already registered
    if db.get_device(device_id=device_id):
        return jsonify(success=False,
                       error_code="DEVICE_ALREADY_REGISTERED"), 400

    smart_plug_key = metadata.pop("smart_plug_key")
    if not smart_plug_key:
        return jsonify(success=False,
                       error_code="MISSING_DEVICE_SMART_PLUG_KEY"), 400

    db.create_device(device_id=device_id,
                     smart_plug_key=smart_plug_key,
                     metadata=metadata)
    return jsonify(success=True, device_id=device_id)
예제 #2
0
def test_connect():
    device_id = request.args.get("device_id", type=int)
    device = db.get_device(device_id=device_id)
    if not device:
        # Unknown device tried to connect
        print("Rejecting unknown device connection")
        return False
    print(f"Device id={device_id} has connected")
예제 #3
0
def job_update_status(job_id):
    body = request.get_json()

    assert "device_id" in body
    assert "status" in body

    device_id = body['device_id']
    status = body['status']

    result = body.get("result")

    device = db.get_device(device_id)
    if not device:
        return jsonify(success=False, error_code="INVALID_DEVICE_ID"), 400

    job = db.get_job(job_id)
    if not job:
        return jsonify(success=False, error_code="INVALID_JOB_ID"), 400

    if not job.assigned_device:
        return jsonify(success=False,
                       error_code="CANNOT_UPDATE_UNASSIGNED_JOB")

    if job.assigned_device.id != device_id:
        return jsonify(success=False,
                       error_code="JOB_ASSIGNED_TO_ANOTHER_DEVICE"), 400

    job.status = status

    if status == db.Job.SUCCEEDED or status == db.Job.FAILED:
        # This job has finished, so it's no longer assigned to a device
        job.assigned_device = None

        if result:
            print(f"\n\n[JOB RESULT] Received output for job id {job_id}:")
            print(result, "\n\n")

    job.save()
    return jsonify(success=True)
예제 #4
0
def device_heartbeat(device_id):
    device = db.get_device(device_id=device_id)
    if not device:
        return jsonify(success=False, error_code="DEVICE_NOT_FOUND"), 400

    # metadata stores a json object of arbitrary device related data (such as battery life, cpu mem, etc.)
    device.metadata = request.get_json()
    device.update_metadata_history()

    device.last_heartbeat = datetime.utcnow()
    device.save()

    # TODO: need to add charging logic based on phone's battery level on heartbeat
    # possibly something like phone.metadata.charge < 20 -> phone.start_charging() ...
    if device.needs_to_start_charging() and not device.decommissioned:
        device.start_charging()
    elif device.needs_to_stop_charging() and not device.decommissioned:
        device.stop_charging()
    elif device.decommissioned:  # if the device is behaving badly, stop charging it.
        device.stop_charging()

    return jsonify(success=True)
예제 #5
0
    def check_jobs(self):
        if self.stopped:
            # stops recursively generating threads.
            return
        while not self.stopped:
            print("[CHECKING JOBS]")
            jobs = db.get_all_jobs()

            # ==== for each job ====
            for job in jobs:
                job_json = job.to_json()
                job_max_secs = job_json["resource_requirements"][
                    "max_runtime_secs"]

                # Check if job timed out, if so then try to cancel it and reschedule it.
                if job_max_secs > 0 and job_json[
                        "status"] == job.ASSIGNED and job_json[
                            "assigned_device_id"] is not None:
                    time_updated = datetime.strptime(job_json["time_updated"],
                                                     '%Y-%m-%d %H:%M:%S.%f')
                    timeout_datetime = timedelta(
                        seconds=job_max_secs) + time_updated
                    if timeout_datetime < datetime.utcnow():

                        # update device's num_failed_jobs
                        device = db.get_device(job_json["assigned_device_id"])
                        device.num_failed_jobs += 1
                        device.save()

                        print("[JOB TIMEOUT]: Device", device.id, "on Job",
                              job.id)

                        self.cancel_and_reschedule_job(job.id)

                # For unassigned jobs.
                if job_json["status"] == job.UNASSIGNED:
                    # Check if the phone has not acknowledged the job for 10 seconds. If so, then increase the num_failed_acks and reschedule the job
                    time_updated = datetime.strptime(job_json["time_updated"],
                                                     '%Y-%m-%d %H:%M:%S.%f')
                    timeout_datetime = timedelta(
                        seconds=ACK_TIMEOUT) + time_updated
                    if timeout_datetime < datetime.utcnow(
                    ) and job.id in self.pending_job_acks:

                        # update device's num_failed_acks
                        device = db.get_device(self.pending_job_acks[job.id])
                        device.num_failed_acks += 1
                        device.save()

                        print("[NO DEVICE ACK]: Device", device.id, "on Job",
                              job.id)

                        self.remove_pending_acknowledgement(job.id)
                        self.cancel_and_reschedule_job(job.id)

                    # For jobs that are just unassigned in general, hence there is no pending ack's:
                    elif job.id not in self.pending_job_acks and job.can_be_retried:
                        schedule_job(job)

                # For failed jobs: retry them.
                if job_json["status"] == job.FAILED and job.can_be_retried:
                    schedule_job(job)

            # ==== END of "for each job" section ====

            # Start another thread in a few seconds
            # threading.Timer(CHECK_JOBS_INTERVAL_SEC, self.check_jobs).start()
            time.sleep(CHECK_JOBS_INTERVAL_SEC)