Skip to content

Commit 27d324c

Browse files
committed
- Added Python scripts to execute msl-simulations in parallel and obtain statistics.
1 parent 41c9ce3 commit 27d324c

File tree

2 files changed

+526
-0
lines changed

2 files changed

+526
-0
lines changed
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
"""
2+
Synopsis:
3+
This script classifies the simulation results according to their status (see Groups) and updates
4+
the statistics file, which holds the latest status and the change date (of the status) for all simulations in the set.
5+
6+
Assumptions:
7+
1) This script will be executed in the folder containing the simulations:
8+
- Search for result files (MODEL.omc.txt) will be performed in the working directory.
9+
- Statistics file will be created and updated in the working directory.
10+
11+
12+
Authors: martin.flehmig@tu-dresden.de, Christian Schubert
13+
"""
14+
15+
import logging
16+
import sys
17+
import os.path
18+
import datetime
19+
import json
20+
21+
NOGROUP = '_NoGroup'
22+
23+
# GroupName, Words that must occur, Words that must not occur
24+
Groups = [('_Translation', 'g++', None), \
25+
('_Translation', 'Error building simulator', None), \
26+
('_Update', 'Please update the test', None), \
27+
('_Failed', 'failed', None), \
28+
('_NotEqual', 'Files not Equal!', None), \
29+
('_OK', 'Frontend succeeded', 'failed'), \
30+
('_OK', 'Translation succeeded', 'failed'), \
31+
('_OK', 'Compilation succeeded', 'failed'), \
32+
('_OK', 'Files Equal!', 'failed'), \
33+
('_OK', 'SimpleSimulation', ['failed', 'did not'])]
34+
35+
36+
class SimulationStatistics(object):
37+
"""
38+
Holds the information and statistics for a particular simulation model.
39+
"""
40+
41+
def __init__(self, name="", resfile="", status="", change_date=""):
42+
"""
43+
:param name: Name of the simulation model.
44+
:param status: Status of the simulation.
45+
:param change_date: Date of status change.
46+
"""
47+
self.sim_name = name
48+
self.resfile = resfile
49+
self.status = status
50+
self.change_date = change_date # Last change of the simulation result.
51+
52+
def set_status(self, status):
53+
"""
54+
Set member 'status' of object.
55+
:param status: New status of the object.
56+
"""
57+
self.status = status
58+
59+
def set_date(self, date):
60+
"""
61+
Set member 'date' of object.
62+
:param date: New date of the object.
63+
"""
64+
self.change_date = date
65+
66+
def get_status(self, groups):
67+
# Return immediately if resfile is not set.
68+
if self.resfile is None:
69+
return NOGROUP
70+
71+
# Check File Size first (ignore > 128Kb)
72+
if os.path.getsize(self.resfile) > 128 * 1024:
73+
print "Skipping %s. Because file is too large!" % self.resfile
74+
return NOGROUP
75+
76+
# Open, Read and Close file
77+
f = open(self.resfile, 'r')
78+
content = f.read()
79+
f.close()
80+
81+
# run through all groups
82+
for g in groups:
83+
group_name = g[0]
84+
include = g[1]
85+
exclude = g[2]
86+
if include is None: include = list()
87+
if not isinstance(include, (list, tuple)): include = [include]
88+
if exclude is None: exclude = list()
89+
if not isinstance(exclude, (list, tuple)): exclude = [exclude]
90+
91+
# test for include
92+
found = True
93+
for i in include:
94+
if content.find(i) < 0:
95+
found = False
96+
break
97+
if found is False:
98+
continue
99+
100+
# test for exclude
101+
found = False
102+
for e in exclude:
103+
if content.find(e) >= 0:
104+
found = True
105+
break
106+
if found is True:
107+
continue
108+
109+
# we found the group
110+
return group_name
111+
112+
# we did not find a matching group
113+
return None
114+
115+
def get_date(self):
116+
"""
117+
:return date2: Date and time of last modification of the result file.
118+
:rtype string
119+
"""
120+
date1 = datetime.datetime.fromtimestamp(os.path.getmtime(self.resfile))
121+
date2 = date1.strftime("%d/%m/%Y (%H:%M)") # 29 09 2015 (10:26)
122+
return date2
123+
124+
125+
def obj_dict(obj):
126+
"""
127+
Returns the object converted into a dictionary.
128+
This method is used for json.dump.
129+
:param obj:
130+
"""
131+
return obj.__dict__
132+
133+
134+
class HandleSimulationStatistics(object):
135+
"""
136+
Class that holds statistics about simulations, e.g., MSL32_cpp.
137+
Name of simulations are read from current directory.
138+
"""
139+
140+
def __init__(self):
141+
"""
142+
Initializes object of this class.
143+
:return: Nuescht.
144+
"""
145+
# Working directory.
146+
self.wdir = os.getcwd()
147+
# Statistics file.
148+
self.stat_file = self.wdir + os.path.sep + "statistics.json"
149+
# Create list of simulation statistics objects.
150+
self.sim_stats = []
151+
152+
def get_sims(self):
153+
"""
154+
Reads names of simulation models from current directory.
155+
"""
156+
for mosfile in os.listdir(self.wdir):
157+
if mosfile.endswith(".mos"):
158+
# Check, if there is a output/result file for this simulation (, i.e., if Model.mos.txt is present).
159+
# If so, create SimulationStatistics object. Else, skipp.
160+
resfile = mosfile + ".txt"
161+
if os.path.isfile(resfile):
162+
self.sim_stats.append(SimulationStatistics(mosfile, resfile))
163+
else:
164+
logging.info('No result file for model %s found.', mosfile)
165+
166+
# print "Number of simulations is ", len(self.sim_stats)
167+
# print self.sim_stats
168+
169+
def create_statistics(self):
170+
"""
171+
Creates statistics for all models.
172+
"""
173+
# Get simulations.
174+
self.get_sims()
175+
176+
for sim in self.sim_stats:
177+
group = sim.get_status(Groups)
178+
sim.set_status(group)
179+
180+
date = sim.get_date()
181+
sim.set_date(date)
182+
183+
def create_statistics_file(self):
184+
"""
185+
Creates statistics file and writes statistics. If a statistics file is already present, return without any changes.
186+
"""
187+
if os.path.isfile(self.stat_file):
188+
logging.info('Statistics file %s exists already. Skip method create_statistics_file(self).', self.stat_file)
189+
return
190+
else:
191+
logging.info('No statistics file found. Statistics file %s is created.', self.stat_file)
192+
with open(self.stat_file, 'w') as file:
193+
json.dump(self.sim_stats, file, sort_keys=True, indent=4, ensure_ascii=False, default=obj_dict)
194+
195+
def update_statistics_file(self):
196+
"""
197+
Updates the statistics file.
198+
"""
199+
# Loaded data are of type dict! I.e., stats_in_file is a list of dict.
200+
with open(self.stat_file, 'r') as file:
201+
stats_in_file = json.load(file)
202+
if not file.closed:
203+
logging.error('Statistics file could not be closed. Exit.')
204+
sys.exit(1)
205+
206+
# print "Number of simulations in statistics file is ", len(stats_in_file['sim_name'])
207+
num_of_ups = 0
208+
# Iterate over all created statistics.
209+
for stat in self.sim_stats:
210+
# Find simulation in loaded data. If found, compare attributes and update statistics if possible.
211+
try:
212+
it = next(stat_f for stat_f in stats_in_file if stat_f['sim_name'] == stat.sim_name)
213+
214+
# Compare old status with new status of a simulation.
215+
if it['status'] != unicode(stat.status):
216+
num_of_ups += 1
217+
logging.debug('Statistics for simulation %s needs to be updated', stat.sim_name)
218+
logging.debug('Old: change date: %s \t status: %s', it['change_date'], it['status'])
219+
logging.debug('New: change date: %s \t status: %s', stat.change_date, stat.status)
220+
221+
# Update status and change_date.
222+
it['status'] = stat.status
223+
it['change_date'] = stat.change_date
224+
except StopIteration:
225+
# Statistics for simulation 'stat' could not be found in statistics file. Maybe because this is a
226+
# new model or for some other reason. Therefore, statistics will be added to the statistics file.
227+
stats_in_file.append(stat)
228+
num_of_ups += 1
229+
logging.debug('New statistics for simulation %s added to statistics file.', stat.sim_name)
230+
231+
# Write updated statistics to file (The file will be completely overridden. Can you manipulate json files
232+
# 'in place'?)
233+
print " %d changes/updates were found." % num_of_ups
234+
if num_of_ups > 0:
235+
print " Update statistics file..."
236+
with open(self.stat_file, 'w+') as file:
237+
json.dump(stats_in_file, file, sort_keys=True, indent=4, ensure_ascii=False, default=obj_dict)
238+
if not file.closed:
239+
logging.error('Statistics file could not be closed. Exit.')
240+
sys.exit(1)
241+
print " Done."
242+
else:
243+
print " No updates necessary."
244+
245+
def run(self):
246+
"""
247+
Run preprocessing.
248+
"""
249+
# Create statistics from simulation results.
250+
self.create_statistics()
251+
252+
# If statistics file does not exist, create and write it.
253+
if not os.path.isfile(self.stat_file):
254+
self.create_statistics_file()
255+
else:
256+
# Else, update statistics to file.
257+
self.update_statistics_file()
258+
259+
260+
def main():
261+
# Logging level is set to error. Possible values are: debug, info, warning, error and critical.
262+
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)
263+
264+
# Parse command line arguments
265+
# parser = create_argument_parser()
266+
# args = parser.parse_args() # will raise an error if the arguments are invalid and terminate the program immediately
267+
268+
handle_sim_stats = HandleSimulationStatistics()
269+
handle_sim_stats.run()
270+
271+
272+
if __name__ == '__main__':
273+
main()

0 commit comments

Comments
 (0)