def handle_ping(self): """ Request connection check """ command = tr50.create_diag_ping() message_desc = "Connected" message = defs.OutMessage(command, message_desc) status = self.send(message) return constants.STATUS_SUCCESS
def handle_time(self): """ Request time from the cloud """ command = tr50.create_diag_time() message_desc = "Retrieving Time.." message = defs.OutMessage(command, message_desc) status = self.send(message) return constants.STATUS_SUCCESS
def action_progress_update(self, request_id, message): """ Update message for action (method) request in Cloud """ cmd = tr50.create_mailbox_update(request_id, message) message = defs.OutMessage(cmd, "Update Action Progress " "{} \"{}\"".format(request_id, message)) return self.send(message)
def action_acknowledge(self, request_id, error_code, error_message): """ Send acknowledgement for action (method) request by the cloud """ cmd = tr50.create_mailbox_ack(request_id, error_code, error_message) message = defs.OutMessage( cmd, "Action Acknowledge " "{} {}: \"{}\"".format(request_id, error_code, error_message)) return self.send(message)
def request_download(self, file_name, file_dest, blocking=False, callback=None, timeout=0, file_global=False): """ Request a C2D file transfer """ current_time = datetime.utcnow() end_time = current_time + timedelta(seconds=timeout) self.logger.info("Request download of %s", file_name) # is file_dest the full path or the parent directory? if os.path.isdir(file_dest): file_dest = os.path.join(file_dest, file_name) # File Transfer object for tracking progress transfer = defs.FileTransfer(file_name, file_dest, self.client, callback=callback) # Generate and send message to request file transfer command = tr50.create_file_get(self.config.key, file_name, file_global) message = defs.OutMessage(command, "Download {}".format(file_name), data=transfer) status = self.send(message) # If blocking is set, wait for result of file transfer if status == constants.STATUS_SUCCESS and blocking: while ((timeout == 0 or current_time < end_time) and transfer.status is None): sleep(0.1) current_time = datetime.utcnow() if transfer.status is None: status = constants.STATUS_TIMED_OUT else: status = transfer.status return status
def handle_publish(self): """ Publish any pending publishes in the publish queue, or the cloud logger """ status = constants.STATUS_SUCCESS # Collect all pending publishes in publish queue to_publish = [] while not self.publish_queue.empty(): try: to_publish.append(self.publish_queue.get()) except queue.Empty: break if to_publish: # If pending publishes are found, parse into list for sending messages = [] for pub in to_publish: # Create publish command for an alarm if pub.type == "PublishAlarm": command = tr50.create_alarm_publish(self.config.key, pub.name, pub.state, message=pub.message, timestamp=pub.timestamp) message_desc = "Alarm Publish {}".format(pub.name) message_desc += " : {}".format(pub.state) message = defs.OutMessage(command, message_desc) # Create publish command for strings elif pub.type == "PublishAttribute": command = tr50.create_attribute_publish(self.config.key, pub.name, pub.value, timestamp=pub.timestamp) message_desc = "Attribute Publish {}".format(pub.name) message_desc += " : \"{}\"".format(pub.value) message = defs.OutMessage(command, message_desc) # Create publish command for numbers elif pub.type == "PublishTelemetry": command = tr50.create_property_publish(self.config.key, pub.name, pub.value, timestamp=pub.timestamp) message_desc = "Property Publish {}".format(pub.name) message_desc += " : {}".format(pub.value) message = defs.OutMessage(command, message_desc) # Create publish command for location elif pub.type == "PublishLocation": command = tr50.create_location_publish(self.config.key, pub.latitude, pub.longitude, heading=pub.heading, altitude=pub.altitude, speed=pub.speed, fix_accuracy=pub.accuracy, fix_type=pub.fix_type, timestamp=pub.timestamp) message_desc = "Location Publish {}".format(str(pub)) message = defs.OutMessage(command, message_desc) # Create publish command for a log elif pub.type == "PublishLog": command = tr50.create_log_publish(self.config.key, pub.message, timestamp=pub.timestamp) message_desc = "Log Publish {}".format(pub.message) message = defs.OutMessage(command, message_desc) messages.append(message) # Send all publishes if messages: status = self.send(messages) return status
def handle_message(self, mqtt_message): """ Handle messages received from Cloud """ status = constants.STATUS_NOT_SUPPORTED msg_json = mqtt_message.json if "notify/" in mqtt_message.topic: # Received a notification if mqtt_message.topic[len("notify/"):] == "mailbox_activity": # Mailbox activity, send a request to check the mailbox self.logger.info("Recevied notification of mailbox activity") mailbox_check = tr50.create_mailbox_check(auto_complete=False) to_send = defs.OutMessage(mailbox_check, "Mailbox Check") self.send(to_send) status = constants.STATUS_SUCCESS elif "reply/" in mqtt_message.topic: # Received a reply to a previous message topic_num = mqtt_message.topic[len("reply/"):] for command_num in msg_json: reply = msg_json[command_num] # Retrieve the sent message that this is a reply for, removing # it from being tracked self.lock.acquire() try: sent_message = self.reply_tracker.pop_message(topic_num, command_num) except KeyError as error: self.logger.error(error.message) continue finally: self.lock.release() sent_command_type = sent_message.command.get("command") # Log success status of reply if reply.get("success"): self.logger.info("Received success for %s-%s - %s", topic_num, command_num, sent_message) else: self.logger.error("Received failure for %s-%s - %s", topic_num, command_num, sent_message) self.logger.error(".... %s", str(reply)) # Check what kind of message this is a reply to if sent_command_type == TR50Command.file_get: # Recevied a reply for a file download request if reply.get("success"): file_id = reply["params"].get("fileId") file_checksum = reply["params"].get("crc32") file_transfer = sent_message.data file_transfer.file_id = file_id file_transfer.file_checksum = file_checksum work = defs.Work(constants.WORK_DOWNLOAD, file_transfer) self.queue_work(work) else: if -90008 in reply.get("errorCodes", []): sent_message.data.status = constants.STATUS_NOT_FOUND else: sent_message.data.status = constants.STATUS_FAILURE elif sent_command_type == TR50Command.file_put: # Received a reply for a file upload request if reply.get("success"): file_id = reply["params"].get("fileId") file_transfer = sent_message.data file_transfer.file_id = file_id work = defs.Work(constants.WORK_UPLOAD, file_transfer) self.queue_work(work) else: sent_message.data.status = constants.STATUS_FAILURE elif sent_command_type == TR50Command.mailbox_check: # Received a reply for a mailbox check if reply.get("success"): try: for mail in reply["params"]["messages"]: mail_command = mail.get("command") if mail_command == "method.exec": # Action execute request in mailbox mail_id = mail.get("id") action_name = mail["params"].get("method") action_params = mail["params"].get("params") action_request = defs.ActionRequest(mail_id, action_name, action_params) work = defs.Work(constants.WORK_ACTION, action_request) self.queue_work(work) except: pass elif sent_command_type == TR50Command.diag_time: # Recevied a reply for a ping request if reply.get("success"): mill = reply["params"].get("time") print (datetime.fromtimestamp(mill/1000.0)) else: if -90008 in reply.get("errorCodes", []): sent_message.data.status = constants.STATUS_NOT_FOUND else: sent_message.data.status = constants.STATUS_FAILURE elif sent_command_type == TR50Command.diag_ping: # Recevied a reply for a ping request if reply.get("success"): print ('*Connection Okay* \n') else: if -90008 in reply.get("errorCodes", []): sent_message.data.status = constants.STATUS_NOT_FOUND else: sent_message.data.status = constants.STATUS_FAILURE status = constants.STATUS_SUCCESS return status
def handle_action(self, action_request): """ Handle action execution requests from Cloud """ result_code = -1 result_args = {"mail_id":action_request.request_id} action_result = None action_failed = False try: # Execute callback action_result = self.callbacks.execute_action(action_request) except Exception as error: # Error with action execution. Might not have been registered. action_failed = True self.logger.error("Action %s execution failed", action_request.name) self.logger.error(".... %s", str(error)) result_code = constants.STATUS_FAILURE result_args["error_message"] = "ERROR: {}".format(str(error)) if action_request.name not in self.callbacks: result_code = constants.STATUS_NOT_FOUND else: self.logger.exception("Exception:") # Action execution did not raise an error if not action_failed: # Handle returning a tuple or just a status code if action_result.__class__.__name__ == "tuple": result_code = action_result[0] if len(action_result) >= 2: result_args["error_message"] = str(action_result[1]) if len(action_result) >= 3: result_args["params"] = action_result[2] else: result_code = action_result if not is_valid_status(result_code): # Returned 'status' is not a valid status error_string = ("Invalid return status: " + str(result_code)) self.logger.error(error_string) result_code = constants.STATUS_BAD_PARAMETER result_args["error_message"] = "ERROR: " + error_string # Return status to Cloud # Check for invoked status. If so, return mail box update not # ack. Ack is the final notification. This breaks triggers # etc because it doesn't update the status. result_args["error_code"] = tr50.translate_error_code(result_code) if result_code == constants.STATUS_INVOKED: update_args = {"mail_id":action_request.request_id} update_args["message"] = "Invoked" mailbox_ack = tr50.create_mailbox_update(**update_args) else: mailbox_ack = tr50.create_mailbox_ack(**result_args) message_desc = "Action Complete \"{}\"".format(action_request.name) message_desc += " result : {}({})".format(result_code, status_string(result_code)) if result_args.get("error_message"): message_desc += " \"{}\"".format(result_args["error_message"]) if result_args.get("params"): message_desc += " \"{}\"".format(str(result_args["params"])) message = defs.OutMessage(mailbox_ack, message_desc) status = self.send(message) return status
def request_upload(self, file_path, upload_name=None, blocking=False, callback=None, timeout=0, file_global=False): """ Request a D2C file transfer """ status = constants.STATUS_SUCCESS current_time = datetime.utcnow() end_time = current_time + timedelta(seconds=timeout) transfer = None self.logger.info("Request upload of %s", file_path) # Path must be absolute if not os.path.isabs(file_path): self.logger.error("Path must be absolute \"%s\"", file_path) status = constants.STATUS_NOT_FOUND if status == constants.STATUS_SUCCESS: # Check if file exists if not os.path.isfile(file_path): # No file to upload self.logger.error("Cannot find file %s. " "Upload cancelled.", file_path) status = constants.STATUS_NOT_FOUND else: transfer = None file_name = os.path.basename(file_path) if not upload_name: upload_name = file_name # Get file crc32 checksum checksum = 0 with open(file_path, "rb") as up_file: for chunk in up_file: checksum = crc32(chunk, checksum) checksum = checksum & 0xffffffff # File Transfer object for tracking progress transfer = defs.FileTransfer(upload_name, file_path, self.client, callback=callback) # Generate and send message to request file transfer command = tr50.create_file_put(self.config.key, upload_name, crc32=checksum, file_global=file_global) message_desc = "Upload {} as {}".format(file_name, upload_name) message = defs.OutMessage(command, message_desc, data=transfer) status = self.send(message) # If blocking is set, wait for result of file transfer if status == constants.STATUS_SUCCESS and blocking: while ((timeout == 0 or current_time < end_time) and not self.to_quit and transfer.status is None): sleep(0.1) current_time = datetime.utcnow() if transfer.status is None: status = constants.STATUS_TIMED_OUT else: status = transfer.status return status