From 8b3d149e54e215e25265e9ebd2dae4890b5f2518 Mon Sep 17 00:00:00 2001 From: Juliana Mashon Date: Thu, 18 Jul 2024 13:09:04 -0700 Subject: [PATCH] Added comments--organization --- api_connect.py | 6 ++++ api_functions.py | 23 ++++++++++++-- broker_connect.py | 46 +++++++++------------------ broker_functions.py | 65 +++++++++++++++++++++++++++++++++----- main.py | 77 ++++++++++++++++++++++++++++++--------------- 5 files changed, 152 insertions(+), 65 deletions(-) diff --git a/api_connect.py b/api_connect.py index 513fb4d..a8ee7da 100644 --- a/api_connect.py +++ b/api_connect.py @@ -7,6 +7,8 @@ def __init__(self): self.token = None self.error = None + ## ERROR HANDLING + def token_handling(self, response): """Handle errors relating to bad user auth token requests.""" @@ -54,6 +56,8 @@ def request_handling(self, response): return 0 + ## FUNCTIONS + def get_token(self, email, password, server): """Fetch user authentication token via API.""" @@ -95,6 +99,8 @@ def request(self, method, endpoint, id, payload): else: return self.error + ## REQUEST METHODS + def get(self, endpoint, id): """METHOD: 'get' allows user to view endpoint data.""" return self.request('GET', endpoint, id, payload=None) diff --git a/api_functions.py b/api_functions.py index 83fe537..0db9c6c 100644 --- a/api_functions.py +++ b/api_functions.py @@ -5,6 +5,25 @@ def __init__(self): self.api_connect = ApiConnect() self.token = None + self.echo = True + self.verbose = True + + def return_config(self, return_value): + """Configure echo and verbosity of function returns.""" + + if self.echo is True and self.verbose is True: + print('-' * 100) + print(f'FUNCTION: {return_value}\n') + return print(return_value) + elif self.echo is True and self.verbose is False: + print('-' * 100) + return print(return_value) + elif self.echo is False and self.verbose is False: + return return_value + else: + print('-' * 100) + return print("ERROR: Incompatible return configuration.") + def get_token(self, email, password, server='https://my.farm.bot'): token_str = self.api_connect.get_token(email, password, server) return token_str @@ -67,8 +86,8 @@ def garden_size(self): f'\ty length = {length_y:.2f}\n' f'\tarea = {area:.2f}') - def group(self, id): + def group(self, id): ## MAKE ID OPTIONAL RETURN FULL TREE W/O ID return self.get_info('point_groups', id) - def curve(self, id): + def curve(self, id): ## MAKE ID OPTIONAL RETURN FULL TREE W/O ID return self.get_info('curves', id) diff --git a/broker_connect.py b/broker_connect.py index 6d1434c..9dc67c3 100644 --- a/broker_connect.py +++ b/broker_connect.py @@ -11,8 +11,13 @@ def __init__(self): self.last_message = None + ## ERROR HANDLING + + ## FUNCTIONS -- SENDING MESSAGES + def connect(self): """Establish persistent connection with message broker.""" + self.client = mqtt.Client() self.client.username_pw_set( username=self.token['token']['unencoded']['bot'], @@ -29,12 +34,14 @@ def connect(self): def disconnect(self): """Disconnect from the message broker.""" + if self.client is not None: self.client.loop_stop() self.client.disconnect() def publish(self, message): """Send Celery Script messages via message broker.""" + if self.client is None: self.connect() @@ -43,48 +50,25 @@ def publish(self, message): payload=json.dumps(message) ) - # def on_connect(self, *_args): - # # Subscribe to all channels - # self.client.subscribe(f"bot/{self.token['token']['unencoded']['bot']}/#") - - # def on_message(self, _client, _userdata, msg): - # print('-' * 100) - # # print channel - # print(f'{msg.topic} ({datetime.now().strftime("%Y-%m-%d %H:%M:%S")})\n') - # # print message - # print(json.dumps(json.loads(msg.payload), indent=4)) - - # def listen(self): - # if self.client is None: - # self.connect() - - # self.client.on_connect = self.on_connect - # self.client.on_message = self.on_message - - # # Start loop in a separate thread - # self.client.loop_start() - - # # Sleep for five seconds to listen for messages - # time.sleep(5) - - # # Stop loop and disconnect after five seconds - # self.client.loop_stop() - # self.client.disconnect() + ## FUNCTIONS -- RECEIVING MESSAGES def on_connect(self, _client, _userdata, _flags, _rc, channel): - # Subscribe to specified channel + """Subscribe to specified broker response channel.""" self.client.subscribe(f"bot/{self.token['token']['unencoded']['bot']}/{channel}") def on_message(self, _client, _userdata, msg): - message = json.loads(msg.payload) + """Update message queue with latest broker response.""" - self.last_message = message # Update last_message + new_message = json.loads(msg.payload) + self.last_message = new_message def listen(self, duration, channel='#'): + """Listen to messages via message broker.""" + if self.client is None: self.connect() - # Wrap on_connect and on_message methods to pass channel argument + # Wrap on_connect to pass channel argument self.client.on_connect = lambda client, userdata, flags, rc: self.on_connect(client, userdata, flags, rc, channel) self.client.on_message = self.on_message diff --git a/broker_functions.py b/broker_functions.py index 387c3c4..459998f 100644 --- a/broker_functions.py +++ b/broker_functions.py @@ -18,7 +18,28 @@ def __init__(self): self.token = None self.client = None + self.echo = True + self.verbose = True + + def return_config(self, return_value): + """Configure echo and verbosity of function returns.""" + + if self.echo is True and self.verbose is True: + print('-' * 100) + print(f'FUNCTION: {return_value}\n') + return print(return_value) + elif self.echo is True and self.verbose is False: + print('-' * 100) + return print(return_value) + elif self.echo is False and self.verbose is False: + return return_value + else: + print('-' * 100) + return print("ERROR: Incompatible return configuration.") + def read_status(self): + """Get Farmbot device status tree via message broker.""" + status_message = { **RPC_REQUEST, "body": { @@ -35,6 +56,8 @@ def read_status(self): return status_tree def read_sensor(self, id): + """Get sensor data via message broker.""" + peripheral_str = self.api.get_info('peripherals', id) mode = peripheral_str['mode'] @@ -59,6 +82,8 @@ def read_sensor(self, id): # return ... def message(self, message, type=None, channel=None): + """Send log message via message broker.""" + message_message = { **RPC_REQUEST, "body": { @@ -80,28 +105,34 @@ def message(self, message, type=None, channel=None): # return ... def debug(self, message): + """Send 'debug' type log message via message broker.""" self.message(message, 'debug') # return ... def toast(self, message): + """Send 'toast' type log message via message broker.""" self.message(message, 'toast') # return ... - def wait(self, time): + def wait(self, duration): + """Send wait command to device via message broker.""" + wait_message = { **RPC_REQUEST, "body": { "kind": "wait", "args": { - "milliseconds": time + "milliseconds": duration } } } self.broker_connect.publish(wait_message) - return print("Waiting for "+str(time)+" milliseconds...") + return print("Waiting for "+str(duration)+" milliseconds...") def e_stop(self): + """Send emergency stop command to device via message broker.""" + e_stop_message = { **RPC_REQUEST, "body": { @@ -114,6 +145,8 @@ def e_stop(self): return print("Triggered device emergency stop.") def unlock(self): + """Send unlock command to device via message broker.""" + unlock_message = { **RPC_REQUEST, "body": { @@ -126,6 +159,8 @@ def unlock(self): return print("Triggered device unlock.") def reboot(self): + """Send reboot command to device via message broker.""" + reboot_message = { **RPC_REQUEST, "body": { @@ -140,6 +175,8 @@ def reboot(self): return print("Triggered device reboot.") def shutdown(self): + """Send shutdown command to device via message broker.""" + shutdown_message = { **RPC_REQUEST, "body": { @@ -151,7 +188,7 @@ def shutdown(self): self.broker_connect.publish(shutdown_message) return print("Triggered device shutdown.") - def calibrate_camera(self): + def calibrate_camera(self): ## JULIANA FIX THIS calibrate_message = { **RPC_REQUEST, "body": { @@ -172,8 +209,7 @@ def calibrate_camera(self): self.broker_connect.publish(calibrate_message) # return ... - # photo_grid() --> sequence (broker message) - def photo_grid(self): + def photo_grid(self): ## JULIANA FIX THIS photo_grid_message = { **RPC_REQUEST, "body": { @@ -232,6 +268,8 @@ def control_peripheral(self, id, value, mode=None): # return ... def toggle_peripheral(self, id): + """Toggle peripheral off/on via message broker.""" + toggle_peripheral_message = { **RPC_REQUEST, "body": [{ @@ -252,6 +290,8 @@ def toggle_peripheral(self, id): # return ... def on(self, id): + """Toggle peripheral ON via message broker.""" + peripheral_str = self.api.get_info('peripherals', id) mode = peripheral_str['mode'] @@ -263,10 +303,14 @@ def on(self, id): # return ... def off(self, id): + """Toggle peripheral OFF via message broker.""" + self.control_peripheral(id, 0) # return ... def take_photo(self): + """Send photo command to camera via message broker.""" + take_photo_message = { **RPC_REQUEST, "body": { @@ -279,6 +323,8 @@ def take_photo(self): # return ... def soil_height(self): + """Execute script to check soil height via message broker.""" + soil_height_message = { **RPC_REQUEST, "body": { @@ -293,6 +339,8 @@ def soil_height(self): # return ... def detect_weeds(self): + """Execute script to detect weeds via message broker.""" + detect_weeds_message = { **RPC_REQUEST, "body": { @@ -307,6 +355,8 @@ def detect_weeds(self): # return ... def get_xyz(self): + """Get current x, y, z coordinate of device via message broker.""" + tree_data = self.read_status() position = tree_data["position"] @@ -320,9 +370,10 @@ def get_xyz(self): f'\ty = {y_val:.2f}\n' f'\tz = {z_val:.2f}') - # check_position() --> requires read_status() --> LUA + # check_position(coordinate, tolerance) USE COORDS? def move(self, x, y, z): + """Move to new x, y, z position via message broker.""" def axis_overwrite(axis, value): return { "kind": "axis_overwrite", diff --git a/main.py b/main.py index 91cb055..d15ed8f 100644 --- a/main.py +++ b/main.py @@ -8,7 +8,11 @@ def __init__(self): self.token = None + ## SETUP + def get_token(self, email, password, server="https://my.farm.bot"): + """Fetch user authentication token via API.""" + token_data = self.api.get_token(email, password, server) self.token = token_data @@ -23,6 +27,17 @@ def get_token(self, email, password, server="https://my.farm.bot"): return token_data + def connect_broker(self): + """Establish persistent connection with message broker.""" + + def disconnect_broker(self): + """Disconnect from the message broker.""" + + def listen_broker(self, duration, channel): + """Listen to messages via message broker.""" + + ## INFORMATION + def get_info(self, endpoint, id=None): return self.api.get_info(endpoint, id) @@ -32,15 +47,6 @@ def set_info(self, endpoint, field, value, id=None): def env(self, id=None, field=None, new_val=None): return self.api.env(id, field, new_val) - def log(self, message, type=None, channel=None): - return self.api.log(message, type, channel) - - def safe_z(self): - return self.api.safe_z() - - def garden_size(self): - return self.api.garden_size() - def group(self, id): return self.api.group(id) @@ -53,6 +59,17 @@ def read_status(self): def read_sensor(self, id): return self.broker.read_sensor(id) + def safe_z(self): + return self.api.safe_z() + + def garden_size(self): + return self.api.garden_size() + + ## MESSAGING + + def log(self, message, type=None, channel=None): + return self.api.log(message, type, channel) + def message(self, message, type=None, channel=None): return self.broker.message(message, type, channel) @@ -62,6 +79,8 @@ def debug(self, message): def toast(self, message): return self.broker.toast(message) + ## BASIC COMMANDS + def wait(self, time): return self.broker.wait(time) @@ -77,11 +96,21 @@ def reboot(self): def shutdown(self): return self.broker.shutdown() - def calibrate_camera(self): - return self.broker.calibrate_camera() + ## MOVEMENT - def control_servo(self, pin, angle): - return self.broker.control_servo(pin, angle) + def move(self, x, y, z): + return self.broker.move(x, y, z) + + def set_home(self, axis='all'): + return self.broker.set_home(axis) + + def find_home(self, axis='all', speed=100): + return self.broker.find_home(axis, speed) + + def axis_length(self, axis='all'): + return self.broker.axis_length(axis) + + ## PERIPHERALS def control_peripheral(self, id, value, mode=None): return self.broker.control_peripheral(id, value, mode) @@ -95,6 +124,16 @@ def on(self, id): def off(self, id): return self.broker.off(id) + ## API COMMANDS + + ## BROKER COMMANDS + + def calibrate_camera(self): + return self.broker.calibrate_camera() + + def control_servo(self, pin, angle): + return self.broker.control_servo(pin, angle) + def take_photo(self): return self.broker.take_photo() @@ -104,18 +143,6 @@ def soil_height(self): def detect_weeds(self): return self.broker.detect_weeds() - def move(self, x, y, z): - return self.broker.move(x, y, z) - - def set_home(self, axis='all'): - return self.broker.set_home(axis) - - def find_home(self, axis='all', speed=100): - return self.broker.find_home(axis, speed) - - def axis_length(self, axis='all'): - return self.broker.axis_length(axis) - def mount_tool(self, x, y, z): return self.broker.mount_tool(x, y, z)