Skip to content

Commit b9e1d5d

Browse files
committed
add ability to visualize your topic/ subscriber graph
1 parent 3485cb7 commit b9e1d5d

File tree

9 files changed

+208
-62
lines changed

9 files changed

+208
-62
lines changed

.idea/workspace.xml

+98-14
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

architecture/architecture_relationships.py

+21-19
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
from abc import ABC, abstractmethod
22
import time
33
import json
4-
5-
4+
from typing import Tuple, Any
65

76

87
class Subscriber(ABC):
@@ -38,9 +37,9 @@ def subscriber_periodic(self):
3837
pass
3938

4039
def subscriber_periodic_sim(self):
41-
'''
42-
optional simulation method that one can override if they want different logic than their noirmal periodic
43-
'''
40+
"""
41+
optional simulation method that one can override if they want different logic than their normal periodic
42+
"""
4443
self.subscriber_periodic()
4544

4645
def store_messages(self, topic_name: str, message: 'Message'):
@@ -146,13 +145,14 @@ def is_complete(self):
146145
class DelayCommand(Command):
147146
def __init__(self, delay_time_s, name="Delay Command"):
148147
super().__init__([])
148+
self.start_time = None
149149
self.delay_time = delay_time_s
150150
self.name = name
151-
self.first_run_occured = False
151+
self.first_run_occurred = False
152152

153153
def first_run_behavior(self):
154154
self.start_time = time.time()
155-
self.first_run_occured = True
155+
self.first_run_occurred = True
156156

157157
def periodic(self):
158158
pass
@@ -162,14 +162,14 @@ def is_complete(self):
162162

163163

164164
class Topic(Subscriber):
165-
'''
166-
These could be various sensor readings or commands.
167-
For example, you might have a SpeedCommand topic that the MotorController subscribes to.
168-
Whenever a new message is published on this topic,
169-
the MotorController would update the robot's speed accordingly.
170-
Similarly, sensor topics can publish data
165+
"""
166+
These could be various sensor readings or commands.
167+
For example, you might have a SpeedCommand topic that the MotorController subscribes to.
168+
Whenever a new message is published on this topic,
169+
the MotorController would update the robot's speed accordingly.
170+
Similarly, sensor topics can publish data
171171
such as images from a camera, distance from an ultrasonic sensor, etc.
172-
'''
172+
"""
173173

174174
def __init__(self, name="Abstract Topic", is_sim=False):
175175
super().__init__(is_sim, name)
@@ -178,9 +178,10 @@ def __init__(self, name="Abstract Topic", is_sim=False):
178178
self.__current_time = time.time()
179179
self.__previous_time = time.time()
180180
self.delta_time_seconds = self.__current_time - self.__previous_time
181-
# when true, if we are simulating, we will publish the message that was in the log file instead of generating a new one
182-
# important if this topic is just used for reading data from a physical sensor that the simulation does not have access to read.
183-
# should be kept false if the topic can be simulated or is calculated based on data from other topics. Odometry and PID for example SHOULD be simulated
181+
# when true, if we are simulating, we will publish the message that was in the log file instead of generating
182+
# a new one important if this topic is just used for reading data from a physical sensor that the simulation
183+
# does not have access to read. should be kept false if the topic can be simulated or is calculated based on
184+
# data from other topics. Odometry and PID for example SHOULD be simulated
184185
self.replace_message_with_log = False
185186

186187
def subscriber_periodic(self):
@@ -197,7 +198,7 @@ def generate_messages_periodic(self):
197198
'''
198199
pass
199200

200-
def publish_periodic(self) -> Message:
201+
def publish_periodic(self) -> Tuple[Message, float, float]:
201202
self.__current_time = time.time()
202203
self.delta_time_seconds = self.__current_time - self.__previous_time
203204
self.message_body = self.generate_messages_periodic()
@@ -206,7 +207,8 @@ def publish_periodic(self) -> Message:
206207
self.notify_subscribers(msg)
207208
return msg, self.__current_time, self.delta_time_seconds
208209

209-
def publish_periodic_from_log(self, message_from_log: 'Message', current_time, delta_time_seconds) -> Message:
210+
def publish_periodic_from_log(self, message_from_log: 'Message', current_time, delta_time_seconds) -> \
211+
Tuple[Message, Any, Any]:
210212
self.__current_time = current_time
211213
self.delta_time_seconds = delta_time_seconds
212214
self.__previous_time = self.__current_time

architecture/scheduler.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from architecture.architecture_relationships import Command, Subscriber, Topic, Message
2-
from pyros_math.graph_theory import dependecy_sort, cycle_is_present_in_any
2+
from pyros_math.graph_theory import dependency_sort, cycle_is_present_in_any
33
from pyros_exceptions.pyros_exceptions import TopicCircularDependency, TopicNameCollision, SubscriberNameCollision
44
from architecture.OnRobotUDP import start_client, send_data_to_server
55
import time
@@ -31,7 +31,7 @@ def initialize(self):
3131

3232
self.has_initialize_been_called = True
3333

34-
self.topics = dependecy_sort(self.topics)
34+
self.topics = dependency_sort(self.topics)
3535

3636
if cycle_is_present_in_any(self.topics):
3737
raise TopicCircularDependency("There is a circular dependency in the topics, aborting init")
@@ -122,7 +122,7 @@ def periodic(self):
122122
for topic in self.topics:
123123
topic.periodic()
124124

125-
if True: # self.num_runs >= self.num_runs_per_transmission:
125+
if self.num_runs >= self.num_runs_per_transmission:
126126
if self.enable_coms:
127127
send_data_to_server(self.client_socket, self.server_address, stored_messages_txt)
128128
self.num_runs = 0
@@ -178,3 +178,5 @@ def check_topic_name_collision(self):
178178
for sub in self.subscribers:
179179
if topic.name == sub.name:
180180
raise RuntimeError("Topic and Subscriber cannot have the same name: {}".format(topic.name))
181+
182+

example/MotorControllerExample/MotorHardware.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
class Motor(Subscriber):
88
def __init__(self, subscriber_name="Motor", is_sim=False):
9-
super().__init__(subscriber_name, is_sim)
9+
super().__init__(is_sim, subscriber_name)
1010
self.voltage_hardware = 0
1111

1212
def subscriber_periodic(self):

networkx_test.py

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import networkx as nx
2+
import matplotlib.pyplot as plt
3+
4+
G = nx.DiGraph()
5+
G.add_edges_from(
6+
[('A', 'B'), ('A', 'C'), ('D', 'B'), ('E', 'C'), ('E', 'F'),
7+
('B', 'H'), ('B', 'G'), ('B', 'F'), ('C', 'G')])
8+
9+
val_map = {'A': 1.0,
10+
'D': 0.5714285714285714,
11+
'H': 0.0}
12+
13+
values = [val_map.get(node, 0.25) for node in G.nodes()]
14+
15+
# Specify the edges you want here
16+
red_edges = [('A', 'C'), ('E', 'C')]
17+
edge_colours = ['black' if not edge in red_edges else 'red'
18+
for edge in G.edges()]
19+
black_edges = [edge for edge in G.edges() if edge not in red_edges]
20+
21+
# Need to create a layout when doing
22+
# separate calls to draw nodes and edges
23+
pos = nx.spring_layout(G)
24+
nx.draw_networkx_nodes(G, pos, cmap=plt.get_cmap('jet'),
25+
node_color=values, node_size=500)
26+
nx.draw_networkx_labels(G, pos)
27+
nx.draw_networkx_edges(G, pos, edgelist=red_edges, edge_color='r', arrows=True)
28+
nx.draw_networkx_edges(G, pos, edgelist=black_edges, arrows=False)
29+
plt.show()

pyROS_visualizer.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,6 @@ def main():
8181
server_thread = threading.Thread(target=main)
8282
server_thread.start()
8383

84-
# ani = FuncAnimation(fig, update, blit=False, interval=10, repeat=False, cache_frame_data=False)
85-
# plt.show()
86-
# server_socket.close()
84+
ani = FuncAnimation(fig, update, blit=True, interval=10, repeat=False, cache_frame_data=False)
85+
plt.show()
86+
server_socket.close()

0 commit comments

Comments
 (0)