11import time
2+ import logging
23import os
34from ast import literal_eval
45import sys
56import re
6- import zmq # Added for ZeroMQ
7+ import zmq
8+ import numpy as np
9+ logging .basicConfig (
10+ level = logging .INFO ,
11+ format = '%(levelname)s - %(message)s'
12+ ) # Added for ZeroMQ
713
814# if windows, create script to kill this process
915# because batch files don't provide easy way to know pid of last command
@@ -36,10 +42,10 @@ def __init__(self, port_type, address, zmq_socket_type):
3642 # Bind or connect
3743 if self .port_type == "bind" :
3844 self .socket .bind (address )
39- print (f"ZMQ Port bound to { address } " )
45+ logging . info (f"ZMQ Port bound to { address } " )
4046 else :
4147 self .socket .connect (address )
42- print (f"ZMQ Port connected to { address } " )
48+ logging . info (f"ZMQ Port connected to { address } " )
4349
4450 def send_json_with_retry (self , message ):
4551 """Send JSON message with retries if timeout occurs."""
@@ -48,9 +54,9 @@ def send_json_with_retry(self, message):
4854 self .socket .send_json (message )
4955 return
5056 except zmq .Again :
51- print (f"Send timeout (attempt { attempt + 1 } /5)" )
57+ logging . warning (f"Send timeout (attempt { attempt + 1 } /5)" )
5258 time .sleep (0.5 )
53- print ("Failed to send after retries." )
59+ logging . error ("Failed to send after retries." )
5460 return
5561
5662 def recv_json_with_retry (self ):
@@ -59,9 +65,9 @@ def recv_json_with_retry(self):
5965 try :
6066 return self .socket .recv_json ()
6167 except zmq .Again :
62- print (f"Receive timeout (attempt { attempt + 1 } /5)" )
68+ logging . warning (f"Receive timeout (attempt { attempt + 1 } /5)" )
6369 time .sleep (0.5 )
64- print ("Failed to receive after retries." )
70+ logging . error ("Failed to receive after retries." )
6571 return None
6672
6773# Global ZeroMQ ports registry
@@ -76,30 +82,48 @@ def init_zmq_port(port_name, port_type, address, socket_type_str):
7682 socket_type_str (str): String representation of ZMQ socket type (e.g., "REQ", "REP", "PUB", "SUB").
7783 """
7884 if port_name in zmq_ports :
79- print (f"ZMQ Port { port_name } already initialized." )
85+ logging . info (f"ZMQ Port { port_name } already initialized." )
8086 return # Avoid reinitialization
8187
8288 try :
8389 # Map socket type string to actual ZMQ constant (e.g., zmq.REQ, zmq.REP)
8490 zmq_socket_type = getattr (zmq , socket_type_str .upper ())
8591 zmq_ports [port_name ] = ZeroMQPort (port_type , address , zmq_socket_type )
86- print (f"Initialized ZMQ port: { port_name } ({ socket_type_str } ) on { address } " )
92+ logging . info (f"Initialized ZMQ port: { port_name } ({ socket_type_str } ) on { address } " )
8793 except AttributeError :
88- print (f"Error: Invalid ZMQ socket type string '{ socket_type_str } '." )
94+ logging . error (f"Error: Invalid ZMQ socket type string '{ socket_type_str } '." )
8995 except zmq .error .ZMQError as e :
90- print (f"Error initializing ZMQ port { port_name } on { address } : { e } " )
96+ logging . error (f"Error initializing ZMQ port { port_name } on { address } : { e } " )
9197 except Exception as e :
92- print (f"An unexpected error occurred during ZMQ port initialization for { port_name } : { e } " )
98+ logging . error (f"An unexpected error occurred during ZMQ port initialization for { port_name } : { e } " )
9399
94100def terminate_zmq ():
95101 for port in zmq_ports .values ():
96102 try :
97103 port .socket .close ()
98104 port .context .term ()
99105 except Exception as e :
100- print (f"Error while terminating ZMQ port { port .address } : { e } " )
106+ logging . error (f"Error while terminating ZMQ port { port .address } : { e } " )
101107# --- ZeroMQ Integration End ---
102108
109+
110+ # NumPy Type Conversion Helper
111+ def convert_numpy_to_python (obj ):
112+ #Recursively convert numpy types to native Python types.
113+ #This is necessary because literal_eval cannot parse numpy representations
114+ #like np.float64(1.0), but can parse native Python types like 1.0.
115+ if isinstance (obj , np .generic ):
116+ # Convert numpy scalar types to Python native types
117+ return obj .item ()
118+ elif isinstance (obj , list ):
119+ return [convert_numpy_to_python (item ) for item in obj ]
120+ elif isinstance (obj , tuple ):
121+ return tuple (convert_numpy_to_python (item ) for item in obj )
122+ elif isinstance (obj , dict ):
123+ return {key : convert_numpy_to_python (value ) for key , value in obj .items ()}
124+ else :
125+ return obj
126+
103127# ===================================================================
104128# File & Parameter Handling
105129# ===================================================================
@@ -125,13 +149,15 @@ def safe_literal_eval(filename, defaultValue):
125149inpath = "./in" #must be rel path for local
126150outpath = "./out"
127151simtime = 0
152+ concore_params_file = os .path .join (inpath + "1" , "concore.params" )
153+ concore_maxtime_file = os .path .join (inpath + "1" , "concore.maxtime" )
128154
129155#9/21/22
130156# ===================================================================
131157# Parameter Parsing
132158# ===================================================================
133159try :
134- sparams_path = os . path . join ( inpath + "1" , "concore.params" )
160+ sparams_path = concore_params_file
135161 if os .path .exists (sparams_path ):
136162 with open (sparams_path , "r" ) as f :
137163 sparams = f .read ()
@@ -142,13 +168,13 @@ def safe_literal_eval(filename, defaultValue):
142168
143169 # Convert key=value;key2=value2 to Python dict format
144170 if sparams != '{' and not (sparams .startswith ('{' ) and sparams .endswith ('}' )): # Check if it needs conversion
145- print ("converting sparams: " + sparams )
171+ logging . debug ("converting sparams: " + sparams )
146172 sparams = "{'" + re .sub (';' ,",'" ,re .sub ('=' ,"':" ,re .sub (' ' ,'' ,sparams )))+ "}"
147- print ("converted sparams: " + sparams )
173+ logging . debug ("converted sparams: " + sparams )
148174 try :
149175 params = literal_eval (sparams )
150176 except Exception as e :
151- print (f"bad params content: { sparams } , error: { e } " )
177+ logging . warning (f"bad params content: { sparams } , error: { e } " )
152178 params = dict ()
153179 else :
154180 params = dict ()
@@ -171,8 +197,7 @@ def tryparam(n, i):
171197def default_maxtime (default ):
172198 """Read maximum simulation time from file or use default."""
173199 global maxtime
174- maxtime_path = os .path .join (inpath + "1" , "concore.maxtime" )
175- maxtime = safe_literal_eval (maxtime_path , default )
200+ maxtime = safe_literal_eval (concore_maxtime_file , default )
176201
177202default_maxtime (100 )
178203
@@ -206,30 +231,31 @@ def read(port_identifier, name, initstr_val):
206231 message = zmq_p .recv_json_with_retry ()
207232 return message
208233 except zmq .error .ZMQError as e :
209- print (f"ZMQ read error on port { port_identifier } (name: { name } ): { e } . Returning default." )
234+ logging . error (f"ZMQ read error on port { port_identifier } (name: { name } ): { e } . Returning default." )
210235 return default_return_val
211236 except Exception as e :
212- print (f"Unexpected error during ZMQ read on port { port_identifier } (name: { name } ): { e } . Returning default." )
237+ logging . error (f"Unexpected error during ZMQ read on port { port_identifier } (name: { name } ): { e } . Returning default." )
213238 return default_return_val
214239
215240 # Case 2: File-based port
216241 try :
217242 file_port_num = int (port_identifier )
218243 except ValueError :
219- print (f"Error: Invalid port identifier '{ port_identifier } ' for file operation. Must be integer or ZMQ name." )
244+ logging . error (f"Error: Invalid port identifier '{ port_identifier } ' for file operation. Must be integer or ZMQ name." )
220245 return default_return_val
221246
222247 time .sleep (delay )
223- file_path = os .path .join (inpath + str (file_port_num ), name )
248+ file_path = os .path .join (inpath + str (file_port_num ), name )
224249 ins = ""
225250
226251 try :
227252 with open (file_path , "r" ) as infile :
228253 ins = infile .read ()
229254 except FileNotFoundError :
230255 ins = str (initstr_val )
256+ s += ins # Update s to break unchanged() loop
231257 except Exception as e :
232- print (f"Error reading { file_path } : { e } . Using default value." )
258+ logging . error (f"Error reading { file_path } : { e } . Using default value." )
233259 return default_return_val
234260
235261 # Retry logic if file is empty
@@ -241,12 +267,12 @@ def read(port_identifier, name, initstr_val):
241267 with open (file_path , "r" ) as infile :
242268 ins = infile .read ()
243269 except Exception as e :
244- print (f"Retry { attempts + 1 } : Error reading { file_path } - { e } " )
270+ logging . warning (f"Retry { attempts + 1 } : Error reading { file_path } - { e } " )
245271 attempts += 1
246272 retrycount += 1
247273
248274 if len (ins ) == 0 :
249- print (f"Max retries reached for { file_path } , using default value." )
275+ logging . error (f"Max retries reached for { file_path } , using default value." )
250276 return default_return_val
251277
252278 s += ins
@@ -260,10 +286,10 @@ def read(port_identifier, name, initstr_val):
260286 simtime = max (simtime , current_simtime_from_file )
261287 return inval [1 :]
262288 else :
263- print (f"Warning: Unexpected data format in { file_path } : { ins } . Returning raw content or default." )
289+ logging . warning (f"Warning: Unexpected data format in { file_path } : { ins } . Returning raw content or default." )
264290 return inval
265291 except Exception as e :
266- print (f"Error parsing content from { file_path } ('{ ins } '): { e } . Returning default." )
292+ logging . error (f"Error parsing content from { file_path } ('{ ins } '): { e } . Returning default." )
267293 return default_return_val
268294
269295
@@ -280,39 +306,38 @@ def write(port_identifier, name, val, delta=0):
280306 try :
281307 zmq_p .send_json_with_retry (val )
282308 except zmq .error .ZMQError as e :
283- print (f"ZMQ write error on port { port_identifier } (name: { name } ): { e } " )
309+ logging . error (f"ZMQ write error on port { port_identifier } (name: { name } ): { e } " )
284310 except Exception as e :
285- print (f"Unexpected error during ZMQ write on port { port_identifier } (name: { name } ): { e } " )
311+ logging . error (f"Unexpected error during ZMQ write on port { port_identifier } (name: { name } ): { e } " )
286312 return
287313
288314 # Case 2: File-based port
289315 try :
290- if isinstance (port_identifier , str ) and port_identifier in zmq_ports :
291- file_path = os .path .join ("../" + port_identifier , name )
292- else :
293- file_port_num = int (port_identifier )
294- file_path = os .path .join (outpath + str (file_port_num ), name )
316+ file_port_num = int (port_identifier )
317+ file_path = os .path .join (outpath + str (file_port_num ), name )
295318 except ValueError :
296- print (f"Error: Invalid port identifier '{ port_identifier } ' for file operation. Must be integer or ZMQ name." )
319+ logging . error (f"Error: Invalid port identifier '{ port_identifier } ' for file operation. Must be integer or ZMQ name." )
297320 return
298321
299322 # File writing rules
300323 if isinstance (val , str ):
301324 time .sleep (2 * delay ) # string writes wait longer
302325 elif not isinstance (val , list ):
303- print (f"File write to { file_path } must have list or str value, got { type (val )} " )
326+ logging . error (f"File write to { file_path } must have list or str value, got { type (val )} " )
304327 return
305328
306329 try :
307330 with open (file_path , "w" ) as outfile :
308331 if isinstance (val , list ):
309- data_to_write = [simtime + delta ] + val
332+ # Convert numpy types to native Python types
333+ val_converted = convert_numpy_to_python (val )
334+ data_to_write = [simtime + delta ] + val_converted
310335 outfile .write (str (data_to_write ))
311336 simtime += delta
312337 else :
313338 outfile .write (val )
314339 except Exception as e :
315- print (f"Error writing to { file_path } : { e } " )
340+ logging . error (f"Error writing to { file_path } : { e } " )
316341
317342def initval (simtime_val_str ):
318343 """
@@ -328,12 +353,12 @@ def initval(simtime_val_str):
328353 simtime = first_element
329354 return val [1 :]
330355 else :
331- print (f"Error: First element in initval string '{ simtime_val_str } ' is not a number. Using data part as is or empty." )
356+ logging . error (f"Error: First element in initval string '{ simtime_val_str } ' is not a number. Using data part as is or empty." )
332357 return val [1 :] if len (val ) > 1 else []
333358 else :
334- print (f"Error: initval string '{ simtime_val_str } ' is not a list or is empty. Returning empty list." )
359+ logging . error (f"Error: initval string '{ simtime_val_str } ' is not a list or is empty. Returning empty list." )
335360 return []
336361
337362 except Exception as e :
338- print (f"Error parsing simtime_val_str '{ simtime_val_str } ': { e } . Returning empty list." )
363+ logging . error (f"Error parsing simtime_val_str '{ simtime_val_str } ': { e } . Returning empty list." )
339364 return []
0 commit comments