blob: f30a1a7b9aca3743848c76f574b4e2585e61af63 [file] [log] [blame]
Matt Spinlerd08dbe22017-04-11 13:52:54 -05001#!/usr/bin/env python
2
3"""
4This script reads in fan definition and zone definition YAML
5files and generates a set of structures for use by the fan control code.
6"""
7
8import os
9import sys
10import yaml
11from argparse import ArgumentParser
12from mako.template import Template
Matthew Barth702c4a52018-02-28 16:23:11 -060013from mako.lookup import TemplateLookup
Matt Spinlerd08dbe22017-04-11 13:52:54 -050014
Matt Spinler78498c92017-04-11 13:59:46 -050015
Matthew Barth7883f582019-02-14 14:24:46 -060016def parse_cpp_type(typeName):
17 """
18 Take a list of dbus types from YAML and convert it to a recursive cpp
19 formed data structure. Each entry of the original list gets converted
20 into a tuple consisting of the type name and a list with the params
21 for this type,
22 e.g.
23 ['dict', ['string', 'dict', ['string', 'int64']]]
24 is converted to
25 [('dict', [('string', []), ('dict', [('string', []),
26 ('int64', [])]]]
27 """
28
29 if not typeName:
30 return None
31
32 # Type names are _almost_ valid YAML. Insert a , before each [
33 # and then wrap it in a [ ] and it becomes valid YAML (assuming
34 # the user gave us a valid typename).
35 typeArray = yaml.safe_load("[" + ",[".join(typeName.split("[")) + "]")
36 typeTuple = preprocess_yaml_type_array(typeArray).pop(0)
37 return get_cpp_type(typeTuple)
38
39
40def preprocess_yaml_type_array(typeArray):
41 """
42 Flattens an array type into a tuple list that can be used to get the
43 supported cpp type from each element.
44 """
45
46 result = []
47
48 for i in range(len(typeArray)):
49 # Ignore lists because we merge them with the previous element
50 if type(typeArray[i]) is list:
51 continue
52
53 # If there is a next element and it is a list, merge it with the
54 # current element.
55 if i < len(typeArray)-1 and type(typeArray[i+1]) is list:
56 result.append(
57 (typeArray[i],
58 preprocess_yaml_type_array(typeArray[i+1])))
59 else:
60 result.append((typeArray[i], []))
61
62 return result
63
64
65def get_cpp_type(typeTuple):
66 """
67 Take a list of dbus types and perform validity checking, such as:
68 [ variant [ dict [ int32, int32 ], double ] ]
69 This function then converts the type-list into a C++ type string.
70 """
71
72 propertyMap = {
73 'byte': {'cppName': 'uint8_t', 'params': 0},
74 'boolean': {'cppName': 'bool', 'params': 0},
75 'int16': {'cppName': 'int16_t', 'params': 0},
76 'uint16': {'cppName': 'uint16_t', 'params': 0},
77 'int32': {'cppName': 'int32_t', 'params': 0},
78 'uint32': {'cppName': 'uint32_t', 'params': 0},
79 'int64': {'cppName': 'int64_t', 'params': 0},
80 'uint64': {'cppName': 'uint64_t', 'params': 0},
81 'double': {'cppName': 'double', 'params': 0},
82 'string': {'cppName': 'std::string', 'params': 0},
83 'array': {'cppName': 'std::vector', 'params': 1},
84 'dict': {'cppName': 'std::map', 'params': 2}}
85
86 if len(typeTuple) != 2:
87 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
88
89 first = typeTuple[0]
90 entry = propertyMap[first]
91
92 result = entry['cppName']
93
94 # Handle 0-entry parameter lists.
95 if (entry['params'] == 0):
96 if (len(typeTuple[1]) != 0):
97 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
98 else:
99 return result
100
101 # Get the parameter list
102 rest = typeTuple[1]
103
104 # Confirm parameter count matches.
105 if (entry['params'] != -1) and (entry['params'] != len(rest)):
106 raise RuntimeError("Invalid entry count for %s : %s" %
107 (first, rest))
108
109 # Parse each parameter entry, if appropriate, and create C++ template
110 # syntax.
111 result += '<'
112 if entry.get('noparse'):
113 # Do not parse the parameter list, just use the first element
114 # of each tuple and ignore possible parameters
115 result += ", ".join([e[0] for e in rest])
116 else:
Matthew Barth867a31c2020-02-13 13:13:44 -0600117 result += ", ".join([get_cpp_type(e) for e in rest])
Matthew Barth7883f582019-02-14 14:24:46 -0600118 result += '>'
119
120 return result
121
122
Matthew Barthbb12c922017-06-13 13:57:40 -0500123def convertToMap(listOfDict):
124 """
125 Converts a list of dictionary entries to a std::map initialization list.
126 """
Matthew Barth9a5b6992018-01-23 15:32:26 -0600127 listOfDict = listOfDict.replace('\'', '\"')
Matthew Barthbb12c922017-06-13 13:57:40 -0500128 listOfDict = listOfDict.replace('[', '{')
129 listOfDict = listOfDict.replace(']', '}')
130 listOfDict = listOfDict.replace(':', ',')
131 return listOfDict
132
133
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600134def genEvent(event):
135 """
136 Generates the source code of an event and returns it as a string
137 """
138 e = "SetSpeedEvent{\n"
Matthew Barth621a5772018-11-14 14:55:11 -0600139 e += "\"" + event['name'] + "\",\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600140 e += "Group{\n"
141 for group in event['groups']:
142 for member in group['members']:
Matthew Barth06fa7812018-11-20 09:54:30 -0600143 e += "{\"" + member['object'] + "\",\n"
Matthew Barth146b7392018-03-08 16:17:58 -0600144 e += "\"" + member['interface'] + "\",\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600145 e += "\"" + member['property'] + "\"},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600146 e += "},\n"
147
Matthew Barth06fa7812018-11-20 09:54:30 -0600148 e += "ActionData{\n"
149 for d in event['action']:
150 e += "{Group{\n"
151 for g in d['groups']:
152 for m in g['members']:
Matthew Barth6db547d2019-10-08 14:57:22 -0500153 e += "{\"" + m['object'] + "\",\n"
154 e += "\"" + m['interface'] + "\",\n"
155 e += "\"" + m['property'] + "\"},\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600156 e += "},\n"
157 e += "std::vector<Action>{\n"
158 for a in d['actions']:
Matthew Barth8a697b62018-12-14 13:23:47 -0600159 if len(a['parameters']) != 0:
160 e += "make_action(action::" + a['name'] + "(\n"
161 else:
162 e += "make_action(action::" + a['name'] + "\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600163 for i, p in enumerate(a['parameters']):
164 if (i+1) != len(a['parameters']):
165 e += p + ",\n"
166 else:
167 e += p + "\n"
Matthew Barth8a697b62018-12-14 13:23:47 -0600168 if len(a['parameters']) != 0:
169 e += ")),\n"
170 else:
171 e += "),\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600172 e += "}},\n"
173 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600174
Matthew Barth1b4de262018-03-06 13:03:16 -0600175 e += "std::vector<Trigger>{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600176 if ('timer' in event['triggers']) and \
177 (event['triggers']['timer'] is not None):
Matthew Barth1b4de262018-03-06 13:03:16 -0600178 e += "\tmake_trigger(trigger::timer(TimerConf{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600179 e += "\t" + event['triggers']['timer']['interval'] + ",\n"
180 e += "\t" + event['triggers']['timer']['type'] + "\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600181 e += "\t})),\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600182
Matthew Barth016bd242018-03-07 16:06:06 -0600183 if ('signals' in event['triggers']) and \
184 (event['triggers']['signals'] is not None):
185 for s in event['triggers']['signals']:
186 e += "\tmake_trigger(trigger::signal(\n"
187 e += "match::" + s['match'] + "(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500188 for i, mp in enumerate(s['mparams']['params']):
189 if (i+1) != len(s['mparams']['params']):
190 e += "\t\t\t" + s['mparams'][mp] + ",\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600191 else:
Matthew Barth926df662018-10-09 09:51:12 -0500192 e += "\t\t\t" + s['mparams'][mp] + "\n"
193 e += "\t\t),\n"
194 e += "\t\tmake_handler<SignalHandler>(\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600195 if ('type' in s['sparams']) and (s['sparams']['type'] is not None):
196 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600197 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600198 e += s['signal'] + "(\n"
199 for sp in s['sparams']['params']:
200 e += s['sparams'][sp] + ",\n"
201 if ('type' in s['hparams']) and (s['hparams']['type'] is not None):
202 e += ("handler::" + s['handler'] +
203 "<" + s['hparams']['type'] + ">(\n")
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600204 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600205 e += "handler::" + s['handler'] + "(\n)"
206 for i, hp in enumerate(s['hparams']['params']):
207 if (i+1) != len(s['hparams']['params']):
208 e += s['hparams'][hp] + ",\n"
209 else:
210 e += s['hparams'][hp] + "\n"
211 e += "))\n"
Matthew Barth926df662018-10-09 09:51:12 -0500212 e += "\t\t)\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600213 e += "\t)),\n"
214
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600215 if ('init' in event['triggers']):
216 for i in event['triggers']['init']:
217 e += "\tmake_trigger(trigger::init(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500218 if ('method' in i):
219 e += "\t\tmake_handler<MethodHandler>(\n"
220 if ('type' in i['mparams']) and \
221 (i['mparams']['type'] is not None):
222 e += i['method'] + "<" + i['mparams']['type'] + ">(\n"
223 else:
224 e += i['method'] + "(\n"
225 for ip in i['mparams']['params']:
226 e += i['mparams'][ip] + ",\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600227 if ('type' in i['hparams']) and \
228 (i['hparams']['type'] is not None):
229 e += ("handler::" + i['handler'] +
230 "<" + i['hparams']['type'] + ">(\n")
231 else:
232 e += "handler::" + i['handler'] + "(\n)"
233 for i, hp in enumerate(i['hparams']['params']):
234 if (i+1) != len(i['hparams']['params']):
235 e += i['hparams'][hp] + ",\n"
236 else:
237 e += i['hparams'][hp] + "\n"
Matthew Barth926df662018-10-09 09:51:12 -0500238 e += "))\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600239 e += "\t\t)\n"
240 e += "\t)),\n"
241
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600242 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600243
244 e += "}"
245
246 return e
247
248
Matthew Barth6c050692017-12-05 15:30:09 -0600249def getGroups(zNum, zCond, edata, events):
250 """
251 Extract and construct the groups for the given event.
252 """
253 groups = []
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600254 if ('groups' in edata) and (edata['groups'] is not None):
255 for eGroups in edata['groups']:
256 if ('zone_conditions' in eGroups) and \
257 (eGroups['zone_conditions'] is not None):
258 # Zone conditions are optional in the events yaml but skip
259 # if this event's condition is not in this zone's conditions
260 if all('name' in z and z['name'] is not None and
261 not any(c['name'] == z['name'] for c in zCond)
262 for z in eGroups['zone_conditions']):
263 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600264
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600265 # Zone numbers are optional in the events yaml but skip if this
266 # zone's zone number is not in the event's zone numbers
267 if all('zones' in z and z['zones'] is not None and
268 zNum not in z['zones']
269 for z in eGroups['zone_conditions']):
270 continue
271 eGroup = next(g for g in events['groups']
272 if g['name'] == eGroups['name'])
Matthew Barth6c050692017-12-05 15:30:09 -0600273
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600274 group = {}
275 members = []
276 group['name'] = eGroup['name']
277 for m in eGroup['members']:
278 member = {}
279 member['path'] = eGroup['type']
280 member['object'] = (eGroup['type'] + m)
281 member['interface'] = eGroups['interface']
282 member['property'] = eGroups['property']['name']
283 member['type'] = eGroups['property']['type']
284 # Use defined service to note member on zone object
285 if ('service' in eGroup) and \
286 (eGroup['service'] is not None):
287 member['service'] = eGroup['service']
288 # Add expected group member's property value if given
289 if ('value' in eGroups['property']) and \
290 (eGroups['property']['value'] is not None):
291 if isinstance(eGroups['property']['value'], str) or \
292 "string" in str(member['type']).lower():
293 member['value'] = (
294 "\"" + eGroups['property']['value'] + "\"")
295 else:
296 member['value'] = eGroups['property']['value']
297 members.append(member)
298 group['members'] = members
299 groups.append(group)
Matthew Barth6c050692017-12-05 15:30:09 -0600300 return groups
301
302
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600303def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500304 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600305 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500306 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600307 params = {}
308 if ('parameters' in section) and \
309 (section['parameters'] is not None):
310 plist = []
311 for sp in section['parameters']:
312 p = str(sp)
313 if (p != 'type'):
314 plist.append(p)
315 if (p != 'group'):
316 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500317 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600318 params[p] = "Group\n{\n"
319 for g in groups:
320 for m in g['members']:
321 params[p] += (
322 "{\"" + str(m['object']) + "\",\n" +
323 "\"" + str(m['interface']) + "\",\n" +
324 "\"" + str(m['property']) + "\"},\n")
325 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500326 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600327 params[p] = member[p]
328 params['params'] = plist
329 else:
330 params['params'] = []
331 return params
332
333
334def getInit(eGrps, eTrig, events):
335 """
336 Extracts and constructs an init trigger for the event's groups
337 which are required to be of the same type.
338 """
339 method = {}
340 methods = []
Matthew Barth03774012018-10-26 13:25:43 -0500341 if (len(eGrps) > 0):
342 # Use the first group member for retrieving the type
343 member = eGrps[0]['members'][0]
344 if ('method' in eTrig) and \
345 (eTrig['method'] is not None):
346 # Add method parameters
347 eMethod = next(m for m in events['methods']
348 if m['name'] == eTrig['method'])
349 method['method'] = eMethod['name']
350 method['mparams'] = getParameters(
351 member, eGrps, eMethod, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600352
Matthew Barth03774012018-10-26 13:25:43 -0500353 # Add handler parameters
354 eHandler = next(h for h in events['handlers']
355 if h['name'] == eTrig['handler'])
356 method['handler'] = eHandler['name']
357 method['hparams'] = getParameters(
358 member, eGrps, eHandler, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600359
360 methods.append(method)
361
362 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500363
364
Matthew Bartha69465a2018-03-02 13:50:59 -0600365def getSignal(eGrps, eTrig, events):
366 """
367 Extracts and constructs for each group member a signal
368 subscription of each match listed in the trigger.
369 """
370 signals = []
371 for group in eGrps:
372 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600373 signal = {}
374 # Add signal parameters
375 eSignal = next(s for s in events['signals']
376 if s['name'] == eTrig['signal'])
377 signal['signal'] = eSignal['name']
378 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
379
380 # If service not given, subscribe to signal match
381 if ('service' not in member):
382 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600383 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600384 if m['name'] == eSignal['match'])
385 signal['match'] = eMatch['name']
386 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600387
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600388 # Add handler parameters
389 eHandler = next(h for h in events['handlers']
390 if h['name'] == eTrig['handler'])
391 signal['handler'] = eHandler['name']
392 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500393
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600394 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500395
Matthew Bartha69465a2018-03-02 13:50:59 -0600396 return signals
397
398
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600399def getTimer(eTrig):
400 """
401 Extracts and constructs the required parameters for an
402 event timer.
403 """
404 timer = {}
405 timer['interval'] = (
406 "static_cast<std::chrono::microseconds>" +
407 "(" + str(eTrig['interval']) + ")")
408 timer['type'] = "TimerType::" + str(eTrig['type'])
409 return timer
410
411
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600412def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500413 """
414 Extracts and constructs the make_action function call for
415 all the actions within the given event.
416 """
417 action = []
418 for eActions in actions['actions']:
419 actions = {}
420 eAction = next(a for a in events['actions']
421 if a['name'] == eActions['name'])
422 actions['name'] = eAction['name']
Matthew Barth06fa7812018-11-20 09:54:30 -0600423 actions['groups'] = getGroups(zNum, zCond, eActions, events)
Matthew Barth9df74752017-10-11 14:39:31 -0500424 params = []
425 if ('parameters' in eAction) and \
426 (eAction['parameters'] is not None):
427 for p in eAction['parameters']:
428 param = "static_cast<"
429 if type(eActions[p]) is not dict:
430 if p == 'actions':
431 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600432 pActs = getActions(zNum,
433 zCond,
434 edata,
435 eActions,
436 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500437 for a in pActs:
438 if (len(a['parameters']) != 0):
439 param += (
440 "make_action(action::" +
441 a['name'] +
442 "(\n")
443 for i, ap in enumerate(a['parameters']):
444 if (i+1) != len(a['parameters']):
445 param += (ap + ",")
446 else:
447 param += (ap + ")")
448 else:
449 param += ("make_action(action::" + a['name'])
450 param += "),"
451 param += "}"
Matthew Barth799cdf72019-10-08 15:00:10 -0500452 elif p == 'defevents' or p == 'altevents' or p == 'events':
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600453 param = "std::vector<SetSpeedEvent>{\n"
454 for i, e in enumerate(eActions[p]):
455 aEvent = getEvent(zNum, zCond, e, events)
456 if not aEvent:
457 continue
458 if (i+1) != len(eActions[p]):
459 param += genEvent(aEvent) + ",\n"
460 else:
461 param += genEvent(aEvent) + "\n"
462 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500463 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600464 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600465 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600466 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600467 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600468 ">(\"" + str(eActions[p]) + "\")")
469 else:
470 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600471 str(eActions[p]['type']).lower() +
472 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500473 else:
474 # Default type to 'size_t' when not given
475 param += ("size_t>(" + str(eActions[p]).lower() + ")")
476 else:
477 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600478 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500479 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600480 "TimerConf{" + t['interval'] + "," +
481 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500482 else:
483 param += (str(eActions[p]['type']).lower() + ">(")
484 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600485 if isinstance(eActions[p]['value'], str) or \
486 "string" in str(eActions[p]['type']).lower():
487 param += \
488 "\"" + str(eActions[p]['value']) + "\")"
489 else:
490 param += \
491 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500492 else:
493 param += (
494 str(eActions[p]['type']).lower() +
495 convertToMap(str(eActions[p]['value'])) + ")")
496 params.append(param)
497 actions['parameters'] = params
498 action.append(actions)
499 return action
500
501
Matthew Barth7f272fd2017-09-12 16:16:56 -0500502def getEvent(zone_num, zone_conditions, e, events_data):
503 """
504 Parses the sections of an event and populates the properties
505 that construct an event within the generated source.
506 """
507 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500508
Matthew Barth621a5772018-11-14 14:55:11 -0600509 # Add set speed event name
510 event['name'] = e['name']
511
Matthew Barth6c050692017-12-05 15:30:09 -0600512 # Add set speed event groups
Matthew Barth03774012018-10-26 13:25:43 -0500513 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500514
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600515 # Add optional set speed actions and function parameters
516 event['action'] = []
517 if ('actions' in e) and \
518 (e['actions'] is not None):
Matthew Barth06fa7812018-11-20 09:54:30 -0600519 # List of dicts containing the list of groups and list of actions
520 sseActions = []
521 eActions = getActions(zone_num, zone_conditions, e, e, events_data)
522 for eAction in eActions:
523 # Skip events that have no groups defined for the event or actions
524 if not event['groups'] and not eAction['groups']:
525 continue
526 # Find group in sseActions
527 grpExists = False
528 for sseDict in sseActions:
529 if eAction['groups'] == sseDict['groups']:
530 # Extend 'actions' list
531 del eAction['groups']
532 sseDict['actions'].append(eAction)
533 grpExists = True
534 break
535 if not grpExists:
536 grps = eAction['groups']
537 del eAction['groups']
538 actList = []
539 actList.append(eAction)
Matthew Bartha6f75162018-11-20 13:50:42 -0600540 sseActions.append({'groups': grps, 'actions': actList})
Matthew Barth06fa7812018-11-20 09:54:30 -0600541 event['action'] = sseActions
Matthew Barth7f272fd2017-09-12 16:16:56 -0500542
Matthew Bartha69465a2018-03-02 13:50:59 -0600543 # Add event triggers
544 event['triggers'] = {}
545 for trig in e['triggers']:
546 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600547 if (trig['name'] == "timer"):
548 event['triggers']['timer'] = getTimer(trig)
549 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600550 if ('signals' not in event['triggers']):
551 event['triggers']['signals'] = []
552 triggers = getSignal(event['groups'], trig, events_data)
553 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600554 elif (trig['name'] == "init"):
555 triggers = getInit(event['groups'], trig, events_data)
556 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500557
Matthew Barth7f272fd2017-09-12 16:16:56 -0500558 return event
559
560
561def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500562 """
563 Parses the precondition section of an event and populates the necessary
564 structures to generate a precondition for a set speed event.
565 """
566 precond = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600567
568 # Add set speed event precondition name
569 precond['pcname'] = event['name']
570
Matthew Barth9af190c2017-08-08 14:20:43 -0500571 # Add set speed event precondition group
Matthew Barth03774012018-10-26 13:25:43 -0500572 precond['pcgrps'] = getGroups(zNum,
573 zCond,
574 event['precondition'],
575 events_data)
Matthew Barth9af190c2017-08-08 14:20:43 -0500576
Matthew Barth7f272fd2017-09-12 16:16:56 -0500577 # Add set speed event precondition actions
578 pc = []
579 pcs = {}
580 pcs['name'] = event['precondition']['name']
581 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500582 if p['name'] == event['precondition']['name'])
583 params = []
Matthew Barth8a697b62018-12-14 13:23:47 -0600584 for p in epc['parameters'] or []:
Matthew Barth9af190c2017-08-08 14:20:43 -0500585 param = {}
586 if p == 'groups':
587 param['type'] = "std::vector<PrecondGroup>"
588 param['open'] = "{"
589 param['close'] = "}"
590 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600591 for group in precond['pcgrps']:
592 for pcgrp in group['members']:
593 value = {}
594 value['value'] = (
595 "PrecondGroup{\"" +
596 str(pcgrp['object']) + "\",\"" +
597 str(pcgrp['interface']) + "\",\"" +
598 str(pcgrp['property']) + "\"," +
599 "static_cast<" +
600 str(pcgrp['type']).lower() + ">")
601 if isinstance(pcgrp['value'], str) or \
602 "string" in str(pcgrp['type']).lower():
603 value['value'] += ("(" + str(pcgrp['value']) + ")}")
604 else:
605 value['value'] += \
606 ("(" + str(pcgrp['value']).lower() + ")}")
607 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500608 param['values'] = values
609 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500610 pcs['params'] = params
611 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500612 precond['pcact'] = pc
613
Matthew Barth7f272fd2017-09-12 16:16:56 -0500614 pcevents = []
615 for pce in event['precondition']['events']:
616 pcevent = getEvent(zNum, zCond, pce, events_data)
617 if not pcevent:
618 continue
619 pcevents.append(pcevent)
620 precond['pcevts'] = pcevents
621
Matthew Barthf20c3212018-03-02 14:42:55 -0600622 # Add precondition event triggers
623 precond['triggers'] = {}
624 for trig in event['precondition']['triggers']:
625 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600626 if (trig['name'] == "timer"):
627 precond['triggers']['pctime'] = getTimer(trig)
628 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600629 if ('pcsigs' not in precond['triggers']):
630 precond['triggers']['pcsigs'] = []
631 triggers = getSignal(precond['pcgrps'], trig, events_data)
632 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600633 elif (trig['name'] == "init"):
634 triggers = getInit(precond['pcgrps'], trig, events_data)
635 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500636
637 return precond
638
639
Gunnar Millsb751f322017-06-06 15:14:11 -0500640def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500641 """
642 Constructs the event entries defined for each zone using the events yaml
643 provided.
644 """
645 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500646
Matthew Barthd4d0f082017-05-16 13:51:10 -0500647 if 'events' in events_data:
648 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500649 event = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600650
Matthew Barth9af190c2017-08-08 14:20:43 -0500651 # Add precondition if given
652 if ('precondition' in e) and \
653 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500654 event['pc'] = addPrecondition(zone_num,
655 zone_conditions,
656 e,
657 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500658 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500659 event = getEvent(zone_num, zone_conditions, e, events_data)
Matthew Bartha6f75162018-11-20 13:50:42 -0600660 # Remove empty events and events that have
661 # no groups defined for the event or any of the actions
662 if not event or \
663 (not event['groups'] and
664 all(not a['groups'] for a in event['action'])):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500665 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500666 events.append(event)
667
668 return events
669
670
Matt Spinler78498c92017-04-11 13:59:46 -0500671def getFansInZone(zone_num, profiles, fan_data):
672 """
673 Parses the fan definition YAML files to find the fans
674 that match both the zone passed in and one of the
675 cooling profiles.
676 """
677
678 fans = []
679
680 for f in fan_data['fans']:
681
682 if zone_num != f['cooling_zone']:
683 continue
684
Gunnar Mills67e95512017-06-02 14:35:18 -0500685 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500686 if f.get('cooling_profile') is None:
687 profile = "all"
688 else:
689 profile = f['cooling_profile']
690
691 if profile not in profiles:
692 continue
693
694 fan = {}
695 fan['name'] = f['inventory']
696 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800697 fan['target_interface'] = f.get(
698 'target_interface',
699 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500700 fans.append(fan)
701
702 return fans
703
704
Matthew Barth7883f582019-02-14 14:24:46 -0600705def getIfacesInZone(zone_ifaces):
706 """
707 Parse given interfaces for a zone for associating a zone with an interface
708 and set any properties listed to defined values upon fan control starting
709 on the zone.
710 """
711
712 ifaces = []
713 for i in zone_ifaces:
714 iface = {}
715 # Interface name not needed yet for fan zones but
716 # may be necessary as more interfaces are extended by the zones
717 iface['name'] = i['name']
718
719 if ('properties' in i) and \
720 (i['properties'] is not None):
721 props = []
722 for p in i['properties']:
723 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600724 prop['name'] = p['name']
725 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600726 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600727 if ('persist' in p):
728 persist = p['persist']
729 if (persist is not None):
730 if (isinstance(persist, bool)):
731 prop['persist'] = 'true' if persist else 'false'
732 else:
733 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600734 vals = []
735 for v in p['values']:
736 val = v['value']
737 if (val is not None):
738 if (isinstance(val, bool)):
739 # Convert True/False to 'true'/'false'
740 val = 'true' if val else 'false'
741 elif (isinstance(val, str)):
742 # Wrap strings with double-quotes
743 val = "\"" + val + "\""
744 vals.append(val)
745 prop['values'] = vals
746 props.append(prop)
747 iface['props'] = props
748 ifaces.append(iface)
749
750 return ifaces
751
752
Gunnar Millsee8a2812017-06-02 14:26:47 -0500753def getConditionInZoneConditions(zone_condition, zone_conditions_data):
754 """
755 Parses the zone conditions definition YAML files to find the condition
756 that match both the zone condition passed in.
757 """
758
759 condition = {}
760
761 for c in zone_conditions_data['conditions']:
762
763 if zone_condition != c['name']:
764 continue
765 condition['type'] = c['type']
766 properties = []
767 for p in c['properties']:
768 property = {}
769 property['property'] = p['property']
770 property['interface'] = p['interface']
771 property['path'] = p['path']
772 property['type'] = p['type'].lower()
773 property['value'] = str(p['value']).lower()
774 properties.append(property)
775 condition['properties'] = properties
776
777 return condition
778
779
780def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500781 """
782 Combines the zone definition YAML and fan
783 definition YAML to create a data structure defining
784 the fan cooling zones.
785 """
786
787 zone_groups = []
788
789 for group in zone_data:
790 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500791 # zone conditions are optional
792 if 'zone_conditions' in group and group['zone_conditions'] is not None:
793 for c in group['zone_conditions']:
794
795 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500796 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500797 "zone_conditions used in zone YAML")
798
799 condition = getConditionInZoneConditions(c['name'],
800 zone_conditions_data)
801
802 if not condition:
803 sys.exit("Missing zone condition " + c['name'])
804
805 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500806
807 zone_group = {}
808 zone_group['conditions'] = conditions
809
810 zones = []
811 for z in group['zones']:
812 zone = {}
813
Gunnar Mills67e95512017-06-02 14:35:18 -0500814 # 'zone' is required
815 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500816 sys.exit("Missing fan zone number in " + zone_yaml)
817
818 zone['num'] = z['zone']
819
820 zone['full_speed'] = z['full_speed']
821
Matthew Barth1de66622017-06-12 13:13:02 -0500822 zone['default_floor'] = z['default_floor']
823
Matthew Bartha9561842017-06-29 11:43:45 -0500824 # 'increase_delay' is optional (use 0 by default)
825 key = 'increase_delay'
826 zone[key] = z.setdefault(key, 0)
827
828 # 'decrease_interval' is optional (use 0 by default)
829 key = 'decrease_interval'
830 zone[key] = z.setdefault(key, 0)
831
Gunnar Mills67e95512017-06-02 14:35:18 -0500832 # 'cooling_profiles' is optional (use 'all' instead)
833 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500834 (z['cooling_profiles'] is None):
835 profiles = ["all"]
836 else:
837 profiles = z['cooling_profiles']
838
Matthew Barth7883f582019-02-14 14:24:46 -0600839 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600840 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600841 if ('interfaces' in z) and \
842 (z['interfaces'] is not None):
843 ifaces = getIfacesInZone(z['interfaces'])
844
Matt Spinler78498c92017-04-11 13:59:46 -0500845 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500846 events = getEventsInZone(z['zone'], group['zone_conditions'],
847 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500848
849 if len(fans) == 0:
850 sys.exit("Didn't find any fans in zone " + str(zone['num']))
851
Matthew Barth7883f582019-02-14 14:24:46 -0600852 if (ifaces):
853 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500854 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500855 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500856 zones.append(zone)
857
858 zone_group['zones'] = zones
859 zone_groups.append(zone_group)
860
861 return zone_groups
862
863
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500864if __name__ == '__main__':
865 parser = ArgumentParser(
866 description="Phosphor fan zone definition parser")
867
868 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
869 default="example/zones.yaml",
870 help='fan zone definitional yaml')
871 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
872 default="example/fans.yaml",
873 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500874 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
875 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500876 parser.add_argument('-c', '--zone_conditions_yaml',
877 dest='zone_conditions_yaml',
878 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500879 parser.add_argument('-o', '--output_dir', dest='output_dir',
880 default=".",
881 help='output directory')
882 args = parser.parse_args()
883
884 if not args.zone_yaml or not args.fan_yaml:
885 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700886 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500887
888 with open(args.zone_yaml, 'r') as zone_input:
889 zone_data = yaml.safe_load(zone_input) or {}
890
891 with open(args.fan_yaml, 'r') as fan_input:
892 fan_data = yaml.safe_load(fan_input) or {}
893
Matthew Barthd4d0f082017-05-16 13:51:10 -0500894 events_data = {}
895 if args.events_yaml:
896 with open(args.events_yaml, 'r') as events_input:
897 events_data = yaml.safe_load(events_input) or {}
898
Gunnar Millsee8a2812017-06-02 14:26:47 -0500899 zone_conditions_data = {}
900 if args.zone_conditions_yaml:
901 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
902 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
903
Matt Spinleree7f6422017-05-09 11:03:14 -0500904 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500905 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500906
907 manager_config = zone_data.get('manager_configuration', {})
908
909 if manager_config.get('power_on_delay') is None:
910 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500911
Matthew Barth702c4a52018-02-28 16:23:11 -0600912 tmpls_dir = os.path.join(
913 os.path.dirname(os.path.realpath(__file__)),
914 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500915 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600916 if sys.version_info < (3, 0):
917 lkup = TemplateLookup(
918 directories=tmpls_dir.split(),
919 disable_unicode=True)
920 else:
921 lkup = TemplateLookup(
922 directories=tmpls_dir.split())
923 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500924 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600925 output.write(tmpl.render(zones=zone_config,
926 mgr_data=manager_config))