Skip to content

Commit e76c3c7

Browse files
committed
Added The Comments in Both concore.py and mkconcore.py
1 parent 4988348 commit e76c3c7

4 files changed

Lines changed: 129 additions & 17 deletions

File tree

concore.py

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,35 @@
55
import re
66
import zmq # Added for ZeroMQ
77

8-
#if windows, create script to kill this process
8+
# if windows, create script to kill this process
99
# because batch files don't provide easy way to know pid of last command
10-
# ignored for posix!=windows, because "concorepid" is handled by script
11-
# ignored for docker (linux!=windows), because handled by docker stop
10+
# ignored for posix != windows, because "concorepid" is handled by script
11+
# ignored for docker (linux != windows), because handled by docker stop
1212
if hasattr(sys, 'getwindowsversion'):
1313
with open("concorekill.bat","w") as fpid:
1414
fpid.write("taskkill /F /PID "+str(os.getpid())+"\n")
1515

16-
# --- ZeroMQ Integration Start ---
16+
# ===================================================================
17+
# ZeroMQ Communication Wrapper
18+
# ===================================================================
1719
class ZeroMQPort:
1820
def __init__(self, port_type, address, zmq_socket_type):
21+
"""
22+
port_type: "bind" or "connect"
23+
address: ZeroMQ address (e.g., "tcp://*:5555")
24+
zmq_socket_type: zmq.REQ, zmq.REP, zmq.PUB, zmq.SUB etc.
25+
"""
1926
self.context = zmq.Context()
2027
self.socket = self.context.socket(zmq_socket_type)
2128
self.port_type = port_type # "bind" or "connect"
2229
self.address = address
2330

24-
self.socket.setsockopt(zmq.RCVTIMEO, 2000)
25-
self.socket.setsockopt(zmq.SNDTIMEO, 2000)
26-
self.socket.setsockopt(zmq.LINGER, 0)
31+
# Configure timeouts & immediate close on failure
32+
self.socket.setsockopt(zmq.RCVTIMEO, 2000) # 2 sec receive timeout
33+
self.socket.setsockopt(zmq.SNDTIMEO, 2000) # 2 sec send timeout
34+
self.socket.setsockopt(zmq.LINGER, 0) # Drop pending messages on close
2735

36+
# Bind or connect
2837
if self.port_type == "bind":
2938
self.socket.bind(address)
3039
print(f"ZMQ Port bound to {address}")
@@ -33,6 +42,7 @@ def __init__(self, port_type, address, zmq_socket_type):
3342
print(f"ZMQ Port connected to {address}")
3443

3544
def send_json_with_retry(self, message):
45+
"""Send JSON message with retries if timeout occurs."""
3646
for attempt in range(5):
3747
try:
3848
self.socket.send_json(message)
@@ -44,6 +54,7 @@ def send_json_with_retry(self, message):
4454
return
4555

4656
def recv_json_with_retry(self):
57+
"""Receive JSON message with retries if timeout occurs."""
4758
for attempt in range(5):
4859
try:
4960
return self.socket.recv_json()
@@ -89,6 +100,9 @@ def terminate_zmq():
89100
print(f"Error while terminating ZMQ port {port.address}: {e}")
90101
# --- ZeroMQ Integration End ---
91102

103+
# ===================================================================
104+
# File & Parameter Handling
105+
# ===================================================================
92106
def safe_literal_eval(filename, defaultValue):
93107
try:
94108
with open(filename, "r") as file:
@@ -97,10 +111,13 @@ def safe_literal_eval(filename, defaultValue):
97111
# Keep print for debugging, but can be made quieter
98112
# print(f"Info: Error reading {filename} or file not found, using default: {e}")
99113
return defaultValue
100-
114+
115+
116+
# Load input/output ports if present
101117
iport = safe_literal_eval("concore.iport", {})
102118
oport = safe_literal_eval("concore.oport", {})
103119

120+
# Global variables
104121
s = ''
105122
olds = ''
106123
delay = 1
@@ -110,14 +127,20 @@ def safe_literal_eval(filename, defaultValue):
110127
simtime = 0
111128

112129
#9/21/22
130+
# ===================================================================
131+
# Parameter Parsing
132+
# ===================================================================
113133
try:
114134
sparams_path = os.path.join(inpath + "1", "concore.params")
115135
if os.path.exists(sparams_path):
116136
with open(sparams_path, "r") as f:
117137
sparams = f.read()
118138
if sparams: # Ensure sparams is not empty
139+
# Windows sometimes keeps quotes
119140
if sparams[0] == '"' and sparams[-1] == '"': #windows keeps "" need to remove
120141
sparams = sparams[1:-1]
142+
143+
# Convert key=value;key2=value2 to Python dict format
121144
if sparams != '{' and not (sparams.startswith('{') and sparams.endswith('}')): # Check if it needs conversion
122145
print("converting sparams: "+sparams)
123146
sparams = "{'"+re.sub(';',",'",re.sub('=',"':",re.sub(' ','',sparams)))+"}"
@@ -137,35 +160,46 @@ def safe_literal_eval(filename, defaultValue):
137160

138161
#9/30/22
139162
def tryparam(n, i):
163+
"""Return parameter `n` from params dict, else default `i`."""
140164
return params.get(n, i)
141165

142166

143167
#9/12/21
168+
# ===================================================================
169+
# Simulation Time Handling
170+
# ===================================================================
144171
def default_maxtime(default):
172+
"""Read maximum simulation time from file or use default."""
145173
global maxtime
146174
maxtime_path = os.path.join(inpath + "1", "concore.maxtime")
147175
maxtime = safe_literal_eval(maxtime_path, default)
148176

149177
default_maxtime(100)
150178

151179
def unchanged():
180+
"""Check if global string `s` is unchanged since last call."""
152181
global olds, s
153182
if olds == s:
154183
s = ''
155184
return True
156185
olds = s
157186
return False
158187

188+
# ===================================================================
189+
# I/O Handling (File + ZMQ)
190+
# ===================================================================
159191
def read(port_identifier, name, initstr_val):
160192
global s, simtime, retrycount
161193

194+
# Default return
162195
default_return_val = initstr_val
163196
if isinstance(initstr_val, str):
164197
try:
165198
default_return_val = literal_eval(initstr_val)
166199
except (SyntaxError, ValueError):
167200
pass
168-
201+
202+
# Case 1: ZMQ port
169203
if isinstance(port_identifier, str) and port_identifier in zmq_ports:
170204
zmq_p = zmq_ports[port_identifier]
171205
try:
@@ -178,6 +212,7 @@ def read(port_identifier, name, initstr_val):
178212
print(f"Unexpected error during ZMQ read on port {port_identifier} (name: {name}): {e}. Returning default.")
179213
return default_return_val
180214

215+
# Case 2: File-based port
181216
try:
182217
file_port_num = int(port_identifier)
183218
except ValueError:
@@ -197,6 +232,7 @@ def read(port_identifier, name, initstr_val):
197232
print(f"Error reading {file_path}: {e}. Using default value.")
198233
return default_return_val
199234

235+
# Retry logic if file is empty
200236
attempts = 0
201237
max_retries = 5
202238
while len(ins) == 0 and attempts < max_retries:
@@ -214,6 +250,8 @@ def read(port_identifier, name, initstr_val):
214250
return default_return_val
215251

216252
s += ins
253+
254+
# Try parsing
217255
try:
218256
inval = literal_eval(ins)
219257
if isinstance(inval, list) and len(inval) > 0:
@@ -230,8 +268,13 @@ def read(port_identifier, name, initstr_val):
230268

231269

232270
def write(port_identifier, name, val, delta=0):
271+
"""
272+
Write data either to ZMQ port or file.
273+
`val` must be list (with simtime prefix) or string.
274+
"""
233275
global simtime
234276

277+
# Case 1: ZMQ port
235278
if isinstance(port_identifier, str) and port_identifier in zmq_ports:
236279
zmq_p = zmq_ports[port_identifier]
237280
try:
@@ -240,7 +283,8 @@ def write(port_identifier, name, val, delta=0):
240283
print(f"ZMQ write error on port {port_identifier} (name: {name}): {e}")
241284
except Exception as e:
242285
print(f"Unexpected error during ZMQ write on port {port_identifier} (name: {name}): {e}")
243-
286+
287+
# Case 2: File-based port
244288
try:
245289
if isinstance(port_identifier, str) and port_identifier in zmq_ports:
246290
file_path = os.path.join("../"+port_identifier, name)
@@ -251,8 +295,9 @@ def write(port_identifier, name, val, delta=0):
251295
print(f"Error: Invalid port identifier '{port_identifier}' for file operation. Must be integer or ZMQ name.")
252296
return
253297

298+
# File writing rules
254299
if isinstance(val, str):
255-
time.sleep(2 * delay)
300+
time.sleep(2 * delay) # string writes wait longer
256301
elif not isinstance(val, list):
257302
print(f"File write to {file_path} must have list or str value, got {type(val)}")
258303
return
@@ -269,6 +314,10 @@ def write(port_identifier, name, val, delta=0):
269314
print(f"Error writing to {file_path}: {e}")
270315

271316
def initval(simtime_val_str):
317+
"""
318+
Initialize simtime from string containing a list.
319+
Example: "[10, 'foo', 'bar']" → simtime=10, returns ['foo','bar']
320+
"""
272321
global simtime
273322
try:
274323
val = literal_eval(simtime_val_str)

measurements/Throughput/maximumThroughputComaprison.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
# Add plot details
1717
plt.ylabel('Throughput (Messages/Second)', fontsize=14)
18-
plt.title('Figure 6: Maximum Throughput Comparison', fontsize=18, pad=25)
1918
plt.xticks(fontsize=12)
2019
plt.yscale('log') # log scale for large differences
2120
plt.grid(axis='y', linestyle='--', alpha=0.7)
-1.31 KB
Binary file not shown.

mkconcore.py

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,67 @@
1+
# The script handles different environments: Docker, POSIX (macOS/Ubuntu), and Windows.
2+
# It reads the graph nodes (representing computational tasks) and edges (representing data flow).
3+
# Based on this information, it generates a directory structure and a set of helper scripts
4+
# (build, run, stop, clear, maxtime, params, unlock) to manage the workflow.
5+
# It also includes logic to handle "script specialization" for ZMQ-based communication,
6+
# where it modifies source files to include specific port and port-name information.
7+
8+
# The script does the following:
9+
# 1. Initial Setup and Argument Parsing:
10+
# - Defines global constants for tool names (g++, iverilog, python3, matlab, etc.) and paths.
11+
# - Parses command-line arguments for the GraphML file, source directory, output directory, and execution type (posix, windows, docker).
12+
# - Checks for the existence of input/output directories and creates the output structure.
13+
# - Logs the configuration details.
14+
15+
# 2. Graph Parsing and Adjacency Matrix Creation:
16+
# - Uses BeautifulSoup to parse the input GraphML file.
17+
# - Identifies nodes and edges, storing them in dictionaries.
18+
# - Creates a simple adjacency matrix (m) and a reachability matrix (ms) from the graph,
19+
# detecting any unreachable nodes and logging a warning.
20+
21+
# 3. Script Specialization (Aggregation and Execution):
22+
# - This is a key part of the logic that handles ZMQ connections.
23+
# - It iterates through the edges, specifically looking for ones with labels in the format "0x<hex_port>_<port_name>".
24+
# - It aggregates these port parameters for each node.
25+
# - It then uses an external script `copy_with_port_portname.py` to "specialize" the original source files. This means it creates
26+
# new versions of the scripts, injecting the ZMQ port information directly into the code.
27+
# - The `nodes_dict` is then updated to point to these newly created, specialized scripts.
28+
29+
# 4. Port Mapping and File Generation:
30+
# - Generates `.iport` (input port) and `.oport` (output port) mapping files for each node.
31+
# - These files are simple dictionaries that map volume names (for file-based communication) or port names (for ZMQ)
32+
# to their corresponding port numbers or indices. This allows the individual scripts to know how to connect to their
33+
# peers in the graph.
34+
35+
# 5. File Copying and Script Generation:
36+
# - Copies all necessary source files (`.py`, `.cpp`, `.m`, etc.) from the source directory to the `outdir/src` directory.
37+
# - Handles cases where specialized scripts were created, ensuring the new files are copied instead of the originals.
38+
# - Copies a set of standard `concore` files (`.py`, `.hpp`, `.v`, `.m`, `mkcompile`) into the `src` directory.
39+
40+
# 6. Environment-Specific Scripting (Main Logic Branches):
41+
# - This is the largest and most complex part, where the script's behavior diverges based on the `concoretype`.
42+
43+
# a. Docker:
44+
# - Generates `Dockerfile`s for each node's container. If a custom Dockerfile exists in the source directory, it's used.
45+
# Otherwise, it generates a default one based on the file extension (`.py`, `.cpp`, etc.).
46+
# - Creates `build.bat` (Windows) or `build` (POSIX) scripts to build the Docker images for each node.
47+
# - Creates `run.bat`/`run` scripts to launch the containers, setting up the necessary shared volumes (`-v`) for data transfer.
48+
# - Creates `stop.bat`/`stop` and `clear.bat`/`clear` scripts to manage the containers and clean up the volumes.
49+
# - Creates helper scripts like `maxtime.bat`/`maxtime`, `params.bat`/`params`, and `unlock.bat`/`unlock` to
50+
# pass runtime parameters or API keys to the containers.
51+
52+
# b. POSIX (Linux/macOS) and Windows:
53+
# - These branches handle direct execution on the host machine without containers.
54+
# - Creates a separate directory for each node inside the output directory.
55+
# - Uses the `build` script to copy source files and create symbolic links (`ln -s` on POSIX, `mklink` on Windows)
56+
# between the node directories and the shared data directories (representing graph edges).
57+
# - Generates `run` and `debug` scripts to execute the programs. It uses platform-specific commands
58+
# like `start /B` for Windows and `xterm -e` or `osascript` for macOS to run the processes.
59+
# - The `stop` and `clear` scripts use `kill` or `del` commands to manage the running processes and files.
60+
# - Generates `maxtime`, `params`, and `unlock` scripts that directly write files to the shared directories.
61+
62+
# 7. Permissions:
63+
# - Sets the executable permission (`stat.S_IRWXU`) for the generated scripts on POSIX systems.
64+
165
from bs4 import BeautifulSoup
266
import logging
367
import re
@@ -17,13 +81,13 @@
1781
CPPEXE = "g++" #Ubuntu/macOS C++ 6/22/21
1882
VWIN = "iverilog" #Windows verilog 6/25/21
1983
VEXE = "iverilog" #Ubuntu/macOS verilog 6/25/21
20-
PYTHONEXE = "python3" #Ubuntu/macOS python 3
21-
PYTHONWIN = "python" #Windows python 3
84+
PYTHONEXE = "python3" #Ubuntu/macOS python3
85+
PYTHONWIN = "python" #Windows python3
2286
MATLABEXE = "matlab" #Ubuntu/macOS matlab
2387
MATLABWIN = "matlab" #Windows matlab
24-
OCTAVEEXE = "octave" #Ubuntu/macOS octave
25-
OCTAVEWIN = "octave" #Windows octave
26-
M_IS_OCTAVE = False #treat .m as octave
88+
OCTAVEEXE = "octave" #Ubuntu/macOS octave
89+
OCTAVEWIN = "octave" #Windows octave
90+
M_IS_OCTAVE = False #treat .m as octave
2791
MCRPATH = "~/MATLAB/R2021a" #path to local Ubunta Matlab Compiler Runtime
2892
DOCKEREXE = "sudo docker"#assume simple docker install
2993
DOCKEREPO = "markgarnold"#where pulls come from 3/28/21

0 commit comments

Comments
 (0)