From b677556ecee6778916368787b2a4dac57700794b Mon Sep 17 00:00:00 2001 From: Cole Deck Date: Tue, 30 Jul 2024 12:54:13 -0500 Subject: [PATCH] Add counter, MQTT --- config.yml | 4 ++ jukebox-web | 2 +- led_control.py | 2 +- pick_count.pkl | 1 + requirements.txt | 1 + run.py | 96 ++++++++++++++++++++++++++++++++++++++++++++++-- 6 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 pick_count.pkl diff --git a/config.yml b/config.yml index fe4b6a5..4d98384 100644 --- a/config.yml +++ b/config.yml @@ -5,6 +5,10 @@ core: server: Hyper-Vd loopspeed: 60 # fps +mqtt: + enabled: True + server: 172.31.108.4 + arm: ip: 192.168.1.145 tool: diff --git a/jukebox-web b/jukebox-web index 4b5cb19..08eebd4 160000 --- a/jukebox-web +++ b/jukebox-web @@ -1 +1 @@ -Subproject commit 4b5cb19bd87a8aa1f3b43962e54c0336247cb291 +Subproject commit 08eebd400b752488527f917e1b05a11931b1fbba diff --git a/led_control.py b/led_control.py index 837f3d7..497eecf 100755 --- a/led_control.py +++ b/led_control.py @@ -471,7 +471,7 @@ class LEDSystem(): if not self.showtoggle: self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 0,100,0) else: - self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 0,50,100) + self.changecount = self.fadeorder(self.rings[ring][2],self.rings[ring][2]+24, self.changecount, 100,20,100) else: self.changecount = self.animation_time / 2 # 100hz self.showtoggle = not self.showtoggle diff --git a/pick_count.pkl b/pick_count.pkl new file mode 100644 index 0000000..24de7c8 --- /dev/null +++ b/pick_count.pkl @@ -0,0 +1 @@ +€K7. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7081cad..e5aefa5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,6 +22,7 @@ ghostscript pyzbar segno pyModbusTCP +paho-mqtt # Development matplotlib diff --git a/run.py b/run.py index 45d7993..3f1d581 100755 --- a/run.py +++ b/run.py @@ -30,6 +30,8 @@ from search import JukeboxSearch from pyModbusTCP.client import ModbusClient from uptime import uptime import fileserver +import paho.mqtt.client as mqtt +import pickle # set to false to run without real hardware for development real = False @@ -74,6 +76,13 @@ arm_position_process = None start_animation = False failcount = 0 timecount = 0 +secondsclock = 0 +unacked_publish = set() +mqttc = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2) +counter_file = 'pick_count.pkl' + + +mqttc.user_data_set(unacked_publish) def arm_start_callback(res): fprint("Arm action complete.") @@ -121,6 +130,28 @@ def send_data(type, call, data, client_id="*"): out["data"] = data to_server_queue.put((client_id, json.dumps(out))) +def initialize_counter(): + global counter + try: + with open(counter_file, 'rb') as file: + counter = pickle.load(file) + if not isinstance(counter, int): + raise ValueError("Counter is not an integer") + except (FileNotFoundError, ValueError, pickle.PickleError): + counter = 101 + save_counter() + +# Save the counter to the file +def save_counter(): + with open(counter_file, 'wb') as file: + pickle.dump(counter, file) + +# Increment the counter +def increment_counter(): + global counter + counter += 1 + save_counter() + def check_server(): #print("HI") global cable_list @@ -455,11 +486,36 @@ def setup_server(pool, manager): fprint("Starting websocket server...", sendqueue=to_server_queue) websocket_process = server.start_websocket_server(to_server_queue, from_server_queue) - fprint("Starting image file server...", sendqueue=to_server_queue) + fprint("Starting file server...", sendqueue=to_server_queue) image_server_process = Process(target=fileserver.run_server, args=(config["cables"]["port"], config["cables"]["directory"])) image_server_process.start() + + if config["mqtt"]["enabled"]: + global mqttc + global unacked_publish + mqttc.on_publish = on_publish + + mqttc.user_data_set(unacked_publish) + mqttc.connect(config["mqtt"]["server"]) + mqttc.loop_start() + + initialize_counter() + return True +def mqtt_send(msg, name): + global config + if config["mqtt"]["enabled"]: + global mqttc + global unacked_publish + msg_info = mqttc.publish("jukebox/" + name, msg, qos=1) + unacked_publish.add(msg_info.mid) + while len(unacked_publish): + sleep(0.01) + + # Due to race-condition described above, the following way to wait for all publish is safer + msg_info.wait_for_publish() + def handle_error(error): print(error, flush=True) @@ -518,6 +574,24 @@ def get_open_spot(sensordata): return False +def on_publish(client, userdata, mid, reason_code, properties): + # reason_code and properties will only be present in MQTTv5. It's always unset in MQTTv3 + try: + userdata.remove(mid) + except KeyError: + print("on_publish() is called with a mid not present in unacked_publish") + print("This is due to an unavoidable race-condition:") + print("* publish() return the mid of the message sent.") + print("* mid from publish() is added to unacked_publish by the main thread") + print("* on_publish() is called by the loop_start thread") + print("While unlikely (because on_publish() will be called after a network round-trip),") + print(" this is a race-condition that COULD happen") + print("") + print("The best solution to avoid race-condition is using the msg_info from publish()") + print("We could also try using a list of acknowledged mid rather than removing from pending list,") + print("but remember that mid could be re-used !") + + def mainloop_server(pool, manager): # NON-blocking loop global real @@ -548,10 +622,12 @@ def mainloop_server(pool, manager): global start_animation global failcount global timecount + global secondsclock if mode != oldmode: print(" ***** Running mode:", mode, "***** ") oldmode = mode + mqtt_send(mode, "mode") if mode == "Startup": # very first loop pass @@ -561,8 +637,10 @@ def mainloop_server(pool, manager): # check for messages check_server() + # do every loop! checkpoint = None + val = None if not arm_updates.empty(): val = arm_updates.get() @@ -610,6 +688,16 @@ def mainloop_server(pool, manager): else: pass + # every 1 second + if secondsclock >= config["core"]["loopspeed"]: + secondsclock = 1 + + + + + + else: + secondsclock += 1 # if start_animation is False and ring_animation is not None and ledsys.mode != "Idle" and real: # ledsys.mainloop(None, ring_animation, arm_position=arm_position) @@ -801,7 +889,9 @@ def mainloop_server(pool, manager): killme.set(1) else: fprint("Movement requested. Keep clear of the machine!") - + mqtt_send("start", "cycle_start") + increment_counter() + mqtt_send(counter, "pick_count_total") if get_cable > -1: global sensors if action == "pickup": @@ -838,7 +928,7 @@ def mainloop_server(pool, manager): # complete if arm_ready == True: mode = "Idle" - + mqtt_send("start", "cycle_end") else: # getting cable and bringing to tray # led animation