blob: 733a29b3c334ef185a32ce18aee787e23c8d28cc [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
Matthew Barth005ff2f2020-02-18 11:17:41 -0600789 # Allow zone_conditions to not be in yaml (since its optional)
790 if not isinstance(zone_data, list) and zone_data != {}:
791 zone_data = [zone_data]
Matt Spinler78498c92017-04-11 13:59:46 -0500792 for group in zone_data:
793 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500794 # zone conditions are optional
795 if 'zone_conditions' in group and group['zone_conditions'] is not None:
796 for c in group['zone_conditions']:
797
798 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500799 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500800 "zone_conditions used in zone YAML")
801
802 condition = getConditionInZoneConditions(c['name'],
803 zone_conditions_data)
804
805 if not condition:
806 sys.exit("Missing zone condition " + c['name'])
807
808 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500809
810 zone_group = {}
811 zone_group['conditions'] = conditions
812
813 zones = []
814 for z in group['zones']:
815 zone = {}
816
Gunnar Mills67e95512017-06-02 14:35:18 -0500817 # 'zone' is required
818 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500819 sys.exit("Missing fan zone number in " + zone_yaml)
820
821 zone['num'] = z['zone']
822
823 zone['full_speed'] = z['full_speed']
824
Matthew Barth1de66622017-06-12 13:13:02 -0500825 zone['default_floor'] = z['default_floor']
826
Matthew Bartha9561842017-06-29 11:43:45 -0500827 # 'increase_delay' is optional (use 0 by default)
828 key = 'increase_delay'
829 zone[key] = z.setdefault(key, 0)
830
831 # 'decrease_interval' is optional (use 0 by default)
832 key = 'decrease_interval'
833 zone[key] = z.setdefault(key, 0)
834
Gunnar Mills67e95512017-06-02 14:35:18 -0500835 # 'cooling_profiles' is optional (use 'all' instead)
836 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500837 (z['cooling_profiles'] is None):
838 profiles = ["all"]
839 else:
840 profiles = z['cooling_profiles']
841
Matthew Barth7883f582019-02-14 14:24:46 -0600842 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600843 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600844 if ('interfaces' in z) and \
845 (z['interfaces'] is not None):
846 ifaces = getIfacesInZone(z['interfaces'])
847
Matt Spinler78498c92017-04-11 13:59:46 -0500848 fans = getFansInZone(z['zone'], profiles, fan_data)
Matthew Barth005ff2f2020-02-18 11:17:41 -0600849 events = getEventsInZone(z['zone'],
850 group.get('zone_conditions', {}),
Gunnar Millsb751f322017-06-06 15:14:11 -0500851 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500852
853 if len(fans) == 0:
854 sys.exit("Didn't find any fans in zone " + str(zone['num']))
855
Matthew Barth7883f582019-02-14 14:24:46 -0600856 if (ifaces):
857 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500858 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500859 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500860 zones.append(zone)
861
862 zone_group['zones'] = zones
863 zone_groups.append(zone_group)
864
865 return zone_groups
866
867
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500868if __name__ == '__main__':
869 parser = ArgumentParser(
870 description="Phosphor fan zone definition parser")
871
872 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
873 default="example/zones.yaml",
874 help='fan zone definitional yaml')
875 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
876 default="example/fans.yaml",
877 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500878 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
879 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500880 parser.add_argument('-c', '--zone_conditions_yaml',
881 dest='zone_conditions_yaml',
882 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500883 parser.add_argument('-o', '--output_dir', dest='output_dir',
884 default=".",
885 help='output directory')
886 args = parser.parse_args()
887
888 if not args.zone_yaml or not args.fan_yaml:
889 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700890 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500891
892 with open(args.zone_yaml, 'r') as zone_input:
893 zone_data = yaml.safe_load(zone_input) or {}
894
895 with open(args.fan_yaml, 'r') as fan_input:
896 fan_data = yaml.safe_load(fan_input) or {}
897
Matthew Barthd4d0f082017-05-16 13:51:10 -0500898 events_data = {}
899 if args.events_yaml:
900 with open(args.events_yaml, 'r') as events_input:
901 events_data = yaml.safe_load(events_input) or {}
902
Gunnar Millsee8a2812017-06-02 14:26:47 -0500903 zone_conditions_data = {}
904 if args.zone_conditions_yaml:
905 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
906 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
907
Matt Spinleree7f6422017-05-09 11:03:14 -0500908 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500909 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500910
911 manager_config = zone_data.get('manager_configuration', {})
912
913 if manager_config.get('power_on_delay') is None:
914 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500915
Matthew Barth702c4a52018-02-28 16:23:11 -0600916 tmpls_dir = os.path.join(
917 os.path.dirname(os.path.realpath(__file__)),
918 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500919 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600920 if sys.version_info < (3, 0):
921 lkup = TemplateLookup(
922 directories=tmpls_dir.split(),
923 disable_unicode=True)
924 else:
925 lkup = TemplateLookup(
926 directories=tmpls_dir.split())
927 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500928 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600929 output.write(tmpl.render(zones=zone_config,
930 mgr_data=manager_config))