sgbaird commited on
Commit
2af30d4
1 Parent(s): 726195c

create backup for filesystem style handling

Browse files
Files changed (1) hide show
  1. app_filesystem_version.py +227 -0
app_filesystem_version.py ADDED
@@ -0,0 +1,227 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ from pathlib import Path
4
+
5
+ import paho.mqtt.client as mqtt
6
+ import streamlit as st
7
+ import matplotlib.pyplot as plt
8
+ import numpy as np
9
+ from matplotlib.patches import Rectangle
10
+
11
+ import secrets
12
+
13
+ from time import time, sleep
14
+
15
+ # Initialize Streamlit app
16
+ st.title("Light-mixing Control Panel")
17
+
18
+ # Description and context
19
+ st.markdown(
20
+ """
21
+ This application accesses a public test demo located in Toronto, ON, Canada (as of 2024-07-27).
22
+ For more context, you can refer to this [Colab notebook](https://colab.research.google.com/github/sparks-baird/self-driving-lab-demo/blob/main/notebooks/4.2-paho-mqtt-colab-sdl-demo-test.ipynb)
23
+ and the [self-driving-lab-demo project](https://github.com/sparks-baird/self-driving-lab-demo).
24
+ You may also be interested in the Acceleration Consortium's ["Hello World" microcourse](https://ac-microcourses.readthedocs.io/en/latest/courses/hello-world/index.html) for self-driving labs.
25
+ """
26
+ )
27
+
28
+ max_power = 0.35
29
+ max_value = round(max_power * 255)
30
+
31
+ with st.form("mqtt_form"):
32
+
33
+ # MQTT Configuration
34
+ HIVEMQ_HOST = st.text_input(
35
+ "Enter your HiveMQ host:",
36
+ "248cc294c37642359297f75b7b023374.s2.eu.hivemq.cloud",
37
+ type="password",
38
+ )
39
+ HIVEMQ_USERNAME = st.text_input("Enter your HiveMQ username:", "sgbaird")
40
+ HIVEMQ_PASSWORD = st.text_input(
41
+ "Enter your HiveMQ password:", "D.Pq5gYtejYbU#L", type="password"
42
+ )
43
+ PORT = st.number_input(
44
+ "Enter the port number:", min_value=1, max_value=65535, value=8883
45
+ )
46
+
47
+ # User input for the Pico ID
48
+ PICO_ID = st.text_input("Enter your Pico ID:", "test", type="password")
49
+
50
+ # Information about the maximum power reduction
51
+ st.info(
52
+ f"The upper limit for RGB power levels has been set to {max_value} instead of 255. NeoPixels are bright 😎"
53
+ )
54
+
55
+ # Sliders for RGB values
56
+ R = st.slider("Select the Red value:", min_value=0, max_value=max_value, value=0)
57
+ G = st.slider("Select the Green value:", min_value=0, max_value=max_value, value=0)
58
+ B = st.slider("Select the Blue value:", min_value=0, max_value=max_value, value=0)
59
+
60
+ submit_button = st.form_submit_button(label="Send RGB Command")
61
+
62
+ command_topic = f"sdl-demo/picow/{PICO_ID}/GPIO/28/"
63
+ sensor_data_topic = f"sdl-demo/picow/{PICO_ID}/as7341/"
64
+
65
+
66
+ # random session id to keep track of the session and filter out old data
67
+ experiment_id = secrets.token_hex(4) # 4 bytes = 8 characters
68
+ sensor_data_file = f"sensor_data-{experiment_id}.json"
69
+
70
+ # TODO: Session ID using st.session_state to have history of commands and sensor data
71
+
72
+ # file_path = Path(sensor_data_file)
73
+ # file_path.unlink(missing_ok=True)
74
+
75
+
76
+ # Singleton: https://docs.streamlit.io/develop/api-reference/caching-and-state/st.cache_resource
77
+ # (on_message to be set later since filename is dynamic)
78
+ @st.cache_resource
79
+ def get_paho_client(
80
+ sensor_data_topic, hostname, username, password=None, port=8883, tls=True
81
+ ):
82
+ client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, protocol=mqtt.MQTTv5)
83
+
84
+ def on_connect(client, userdata, flags, rc, properties=None):
85
+ if rc != 0:
86
+ print("Connected with result code " + str(rc))
87
+ client.subscribe(sensor_data_topic, qos=1)
88
+
89
+ client.on_connect = on_connect
90
+
91
+ if tls:
92
+ client.tls_set(tls_version=mqtt.ssl.PROTOCOL_TLS_CLIENT)
93
+ client.username_pw_set(username, password)
94
+ client.connect(hostname, port)
95
+ client.loop_start() # Use a non-blocking loop
96
+
97
+ return client
98
+
99
+
100
+ def send_and_receive(client, command_topic, msg, queue_timeout=15):
101
+ print("Sending command...")
102
+ result = client.publish(command_topic, json.dumps(msg), qos=2)
103
+ result.wait_for_publish() # Ensure the message is sent
104
+
105
+ if result.rc == mqtt.MQTT_ERR_SUCCESS:
106
+ print(f"Command sent: {msg} to topic {command_topic}")
107
+ else:
108
+ print(f"Failed to send command: {result.rc}")
109
+
110
+ timeout = time() + queue_timeout # Set timeout
111
+
112
+ while True:
113
+ if time() > timeout:
114
+ st.error("No sensor data received within the timeout period.")
115
+ return None
116
+ if os.path.exists(sensor_data_file):
117
+ with open(sensor_data_file, "r") as f:
118
+ sensor_data = json.load(f)
119
+
120
+ file_path = Path(sensor_data_file)
121
+ file_path.unlink(missing_ok=True)
122
+
123
+ return sensor_data
124
+
125
+
126
+ # Helper function to plot discrete spectral sensor data
127
+ def plot_spectra(sensor_data):
128
+ """https://chatgpt.com/share/210d2fee-ca64-45a5-866e-e6df6e56bd1c"""
129
+ wavelengths = np.array([410, 440, 470, 510, 550, 583, 620, 670])
130
+ intensities = np.array(
131
+ [
132
+ sensor_data["ch410"],
133
+ sensor_data["ch440"],
134
+ sensor_data["ch470"],
135
+ sensor_data["ch510"],
136
+ sensor_data["ch550"],
137
+ sensor_data["ch583"],
138
+ sensor_data["ch620"],
139
+ sensor_data["ch670"],
140
+ ]
141
+ )
142
+
143
+ fig, ax = plt.subplots(figsize=(10, 6))
144
+
145
+ num_points = 100 # for "fake" color bar effect
146
+
147
+ # Adding rectangles for color bar effect
148
+ dense_wavelengths = np.linspace(wavelengths.min(), wavelengths.max(), num_points)
149
+ rect_height = max(intensities) * 0.02 # Height of the rectangles
150
+
151
+ for dw in dense_wavelengths:
152
+ rect = Rectangle(
153
+ (
154
+ dw - (wavelengths.max() - wavelengths.min()) / num_points / 2,
155
+ -rect_height * 2,
156
+ ),
157
+ (wavelengths.max() - wavelengths.min()) / num_points,
158
+ rect_height * 3,
159
+ color=plt.cm.rainbow(
160
+ (dw - wavelengths.min()) / (wavelengths.max() - wavelengths.min())
161
+ ),
162
+ edgecolor="none",
163
+ )
164
+ ax.add_patch(rect)
165
+
166
+ # Main scatter plot
167
+ scatter = ax.scatter(
168
+ wavelengths, intensities, c=wavelengths, cmap="rainbow", edgecolor="k"
169
+ )
170
+
171
+ # Adding vertical lines from the x-axis to each point
172
+ for wavelength, intensity in zip(wavelengths, intensities):
173
+ ax.vlines(wavelength, 0, intensity, color="gray", linestyle="--", linewidth=1)
174
+
175
+ # Adjust limits and labels with larger font size
176
+ ax.set_xlim(wavelengths.min() - 10, wavelengths.max() + 10)
177
+ ax.set_ylim(
178
+ 0, max(intensities) + 15
179
+ ) # Ensure the lower y limit is 0 and add buffer
180
+ ax.set_xticks(wavelengths)
181
+ ax.set_xlabel("Wavelength (nm)", fontsize=14)
182
+ ax.set_ylabel("Intensity", fontsize=14)
183
+ ax.set_title("Spectral Intensity vs. Wavelength", fontsize=16)
184
+ ax.tick_params(axis="both", which="major", labelsize=12)
185
+
186
+ st.pyplot(fig)
187
+
188
+
189
+ # Publish button
190
+ if submit_button:
191
+ if not PICO_ID or not HIVEMQ_HOST or not HIVEMQ_USERNAME or not HIVEMQ_PASSWORD:
192
+ st.error("Please enter all required fields.")
193
+ else:
194
+ st.info(
195
+ f"Please wait while the command {R, G, B} for experiment {experiment_id} is sent..."
196
+ )
197
+
198
+ client = get_paho_client(
199
+ sensor_data_topic,
200
+ HIVEMQ_HOST,
201
+ HIVEMQ_USERNAME,
202
+ password=HIVEMQ_PASSWORD,
203
+ port=int(PORT),
204
+ tls=True,
205
+ )
206
+
207
+ def on_message(client, userdata, msg):
208
+ with open(sensor_data_file, "w") as f:
209
+ json.dump(json.loads(msg.payload), f)
210
+
211
+ client.on_message = on_message
212
+
213
+ command_msg = {"R": R, "G": G, "B": B}
214
+ sensor_data = send_and_receive(
215
+ client, command_topic, command_msg, queue_timeout=15
216
+ )
217
+
218
+ if sensor_data:
219
+ received_cmd = sensor_data["_input_message"]
220
+ R1 = received_cmd["R"]
221
+ G1 = received_cmd["G"]
222
+ B1 = received_cmd["B"]
223
+ st.success(
224
+ f"Command {R1, G1, B1} for experiment {experiment_id} sent successfully!"
225
+ )
226
+ plot_spectra(sensor_data)
227
+ st.write("Sensor Data Received:", sensor_data)