66from datetime import datetime
77from typing import Dict
88
9+ import time
10+ import os
11+ from functools import wraps
12+ import sys
13+ import inspect
14+ import atexit
15+
916from cimgraph .databases import ConnectionParameters
1017from cimgraph .databases .gridappsd import GridappsdConnection
1118from cimgraph .models import FeederModel
1724from gridappsd_field_bus .field_interface .gridappsd_field_bus import GridAPPSDMessageBus
1825from gridappsd_field_bus .field_interface .interfaces import (FieldMessageBus , MessageBusDefinition , MessageBusFactory )
1926
27+
2028CIM_PROFILE = None
2129IEC61970_301 = None
2230cim = None
2331
2432_log = logging .getLogger (__name__ )
25-
33+ decorator_logger = logging .getLogger ("decorator_logger" )
34+ decorator_logger .setLevel (logging .INFO )
35+ file_handler = logging .FileHandler ("compute_req_log.txt" ) # Log file name
36+ formatter = logging .Formatter ('[COMPUTE_REQ] %(asctime)s - %(message)s' )
37+ file_handler .setFormatter (formatter )
38+ decorator_logger .addHandler (file_handler )
2639
2740def set_cim_profile (cim_profile : str , iec61970_301 : int ):
2841 global CIM_PROFILE
@@ -41,6 +54,147 @@ class AgentRegistrationDetails:
4154 upstream_message_bus_id : FieldMessageBus .id
4255 downstream_message_bus_id : FieldMessageBus .id
4356
57+ @atexit .register
58+ def call_counter_report ():
59+ decorator_logger .info ("Function call counts summary:" )
60+ for func_name , count in function_call_counts .items ():
61+ decorator_logger .info (f"{ func_name } was called { count } time(s)" )
62+
63+ @atexit .register
64+ def message_size_report ():
65+ decorator_logger .info ("Total message size summary:" )
66+ for func_name , total_size in message_size_totals .items ():
67+ decorator_logger .info (f"{ func_name } total message size: { total_size } bytes" )
68+
69+ def compute_req (cls ):
70+ functions = [
71+ '__init__' ,
72+ #'on_measurement',
73+ 'on_upstream_message' ,
74+ 'on_downstream_message' ,
75+ 'on_request' ,
76+ 'publish_upstream' ,
77+ 'publish_downstream' ,
78+ 'send_control_command'
79+ ]
80+
81+ def call_counter (func ):
82+ name = func .__qualname__
83+
84+ @wraps (func )
85+ def wrapper (* args , ** kwargs ):
86+ if args [0 ].agent_id + '.' + name not in function_call_counts :
87+ function_call_counts [args [0 ].agent_id + '.' + name ] = 0
88+ function_call_counts [args [0 ].agent_id + '.' + name ] += 1
89+ #decorator_logger.info(f"{name} called {function_call_counts[name]} times")
90+ return func (* args , ** kwargs )
91+ return wrapper
92+
93+ def timed (func ):
94+ @wraps (func )
95+ def wrapper (* args , ** kwargs ):
96+ start = time .perf_counter ()
97+ result = func (* args , ** kwargs )
98+ end = time .perf_counter ()
99+ class_name = args [0 ].__class__ .__name__ if args else ""
100+ if func .__name__ == '__init__' :
101+ decorator_logger .info (f"{ class_name } .{ func .__name__ } .{ args [0 ].agent_id } took: { end - start :.6f} seconds" )
102+ return result
103+ return wrapper
104+
105+ def get_deep_size (func ):
106+ @wraps (func )
107+ def wrapper (* args , ** kwargs ):
108+ result = func (* args , ** kwargs )
109+
110+ def deep_size (obj , seen = None ):
111+ if seen is None :
112+ seen = set ()
113+ obj_id = id (obj )
114+ if obj_id in seen :
115+ return 0
116+ seen .add (obj_id )
117+ size = sys .getsizeof (obj )
118+ if isinstance (obj , dict ):
119+ size += sum (deep_size (k , seen ) + deep_size (v , seen ) for k , v in obj .items ())
120+ elif isinstance (obj , (list , tuple , set , frozenset )):
121+ size += sum (deep_size (i , seen ) for i in obj )
122+ elif hasattr (obj , '__dict__' ):
123+ for attr_name , attr_value in vars (obj ).items ():
124+ if attr_name in ['feeder_area' , 'switch_area' , 'secondary_area' ]:
125+ continue
126+ size += deep_size (attr_value , seen )
127+ elif hasattr (obj , '__slots__' ):
128+ size += sum (deep_size (getattr (obj , slot ), seen ) for slot in obj .__slots__ if hasattr (obj , slot ))
129+ return size
130+
131+ self = args [0 ]
132+ obj_size = deep_size (self )
133+ decorator_logger .info (f"{ self .__class__ .__name__ } .{ func .__name__ } .{ args [0 ].agent_id } size is: { obj_size } bytes" )
134+
135+ return result
136+ return wrapper
137+
138+ def get_graph_size (func ):
139+ @wraps (func )
140+ def wrapper (* args , ** kwargs ):
141+ self = args [0 ]
142+ result = func (* args , ** kwargs )
143+ area_names = ['feeder_area' , 'switch_area' , 'secondary_area' ]
144+ area_found = False
145+ for name in area_names :
146+ area_dict = getattr (self , name , None )
147+ if area_dict is not None and hasattr (area_dict , 'graph' ):
148+ graph_keys = [key .__name__ for key in list (area_dict .graph .keys ())]
149+ size = len (area_dict .graph .keys ())
150+ decorator_logger .info (f"{ self .__class__ .__name__ } .{ func .__name__ } .{ args [0 ].agent_id } length of graph: { size } " )
151+ decorator_logger .info (f"{ self .__class__ .__name__ } .{ name } .{ args [0 ].agent_id } graph keys: { graph_keys } " )
152+ area_found = True
153+ break
154+
155+ if not area_found :
156+ decorator_logger .error (f"{ class_name } .{ func .__name__ } .{ args [0 ].agent_id } No area dictionary (feeder/switch/secondary) found in { self .__class__ .__name__ } " )
157+ return result
158+ return wrapper
159+
160+ def log_message_size (func ):
161+ name = func .__qualname__
162+
163+ @wraps (func )
164+ def wrapper (* args , ** kwargs ):
165+ sig = inspect .signature (func )
166+ bound_args = sig .bind (* args , ** kwargs )
167+ bound_args .apply_defaults ()
168+
169+ if 'message' in bound_args .arguments :
170+ msg = bound_args .arguments ['message' ]
171+ size = sys .getsizeof (msg )
172+ if args [0 ].agent_id + '.' + name not in message_size_totals :
173+ message_size_totals [args [0 ].agent_id + '.' + name ] = 0
174+ message_size_totals [args [0 ].agent_id + '.' + name ] += size
175+
176+ if 'differenceBuilder' in bound_args .arguments :
177+ msg = bound_args .arguments ['differenceBuilder' ]
178+ size = sys .getsizeof (msg )
179+ if args [0 ].agent_id + '.' + name not in message_size_totals :
180+ message_size_totals [args [0 ].agent_id + '.' + name ] = 0
181+ message_size_totals [args [0 ].agent_id + '.' + name ] += size
182+
183+ return func (* args , ** kwargs )
184+ return wrapper
185+
186+ # Decorate the relevant functions
187+ for attr_name in functions :
188+ if hasattr (cls , attr_name ):
189+ original_func = getattr (cls , attr_name )
190+ if callable (original_func ):
191+ if attr_name == '__init__' :
192+ decorated = get_deep_size (get_graph_size (timed (original_func )))
193+ else :
194+ decorated = call_counter (log_message_size (timed (original_func )))
195+ setattr (cls , attr_name , decorated )
196+
197+ return cls
44198
45199class DistributedAgent :
46200
@@ -61,10 +215,7 @@ def __init__(self,
61215 self .simulation_id = simulation_id
62216 self .context = None
63217
64- # TODO: Change params and connection to local connection
65- self .params = ConnectionParameters (cim_profile = CIM_PROFILE , iec61970_301 = IEC61970_301 )
66-
67- self .connection = GridappsdConnection (self .params )
218+ self .connection = GridappsdConnection ()
68219 self .connection .cim_profile = cim_profile
69220
70221 self .app_id = agent_config ['app_id' ]
@@ -79,14 +230,10 @@ def __init__(self,
79230 self .agent_area_dict = agent_area_dict
80231
81232 if upstream_message_bus_def is not None :
82- if upstream_message_bus_def .is_ot_bus :
83- self .upstream_message_bus = MessageBusFactory .create (upstream_message_bus_def )
84- # else:
85- # self.upstream_message_bus = VolttronMessageBus(upstream_message_bus_def)
86-
233+ self .upstream_message_bus = MessageBusFactory .create (upstream_message_bus_def )
234+
87235 if downstream_message_bus_def is not None :
88- if downstream_message_bus_def .is_ot_bus :
89- self .downstream_message_bus = MessageBusFactory .create (downstream_message_bus_def )
236+ self .downstream_message_bus = MessageBusFactory .create (downstream_message_bus_def )
90237
91238 if self .downstream_message_bus is None and self .upstream_message_bus is None :
92239 raise ValueError ("Must have at least a downstream and/or upstream message bus specified" )
0 commit comments