blob: f4225222d4f925c8b21c93decaa67981f72871a1 [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:
117 result += ", ".join(map(lambda e: get_cpp_type(e),
118 rest))
119 result += '>'
120
121 return result
122
123
Matthew Barthbb12c922017-06-13 13:57:40 -0500124def convertToMap(listOfDict):
125 """
126 Converts a list of dictionary entries to a std::map initialization list.
127 """
Matthew Barth9a5b6992018-01-23 15:32:26 -0600128 listOfDict = listOfDict.replace('\'', '\"')
Matthew Barthbb12c922017-06-13 13:57:40 -0500129 listOfDict = listOfDict.replace('[', '{')
130 listOfDict = listOfDict.replace(']', '}')
131 listOfDict = listOfDict.replace(':', ',')
132 return listOfDict
133
134
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600135def genEvent(event):
136 """
137 Generates the source code of an event and returns it as a string
138 """
139 e = "SetSpeedEvent{\n"
Matthew Barth621a5772018-11-14 14:55:11 -0600140 e += "\"" + event['name'] + "\",\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600141 e += "Group{\n"
142 for group in event['groups']:
143 for member in group['members']:
144 e += "{\n"
145 e += "\"" + member['object'] + "\",\n"
Matthew Barth146b7392018-03-08 16:17:58 -0600146 e += "\"" + member['interface'] + "\",\n"
147 e += "\"" + member['property'] + "\"\n"
148 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600149 e += "},\n"
150
Matthew Barth75d97352018-11-01 10:34:49 -0500151 e += "{Group{},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600152 e += "std::vector<Action>{\n"
153 for a in event['action']:
154 if len(a['parameters']) != 0:
155 e += "make_action(action::" + a['name'] + "(\n"
156 else:
157 e += "make_action(action::" + a['name'] + "\n"
158 for i, p in enumerate(a['parameters']):
159 if (i+1) != len(a['parameters']):
160 e += p + ",\n"
161 else:
162 e += p + ")\n"
163 e += "),\n"
Matthew Barth75d97352018-11-01 10:34:49 -0500164 e += "}},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600165
Matthew Barth1b4de262018-03-06 13:03:16 -0600166 e += "std::vector<Trigger>{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600167 if ('timer' in event['triggers']) and \
168 (event['triggers']['timer'] is not None):
Matthew Barth1b4de262018-03-06 13:03:16 -0600169 e += "\tmake_trigger(trigger::timer(TimerConf{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600170 e += "\t" + event['triggers']['timer']['interval'] + ",\n"
171 e += "\t" + event['triggers']['timer']['type'] + "\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600172 e += "\t})),\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600173
Matthew Barth016bd242018-03-07 16:06:06 -0600174 if ('signals' in event['triggers']) and \
175 (event['triggers']['signals'] is not None):
176 for s in event['triggers']['signals']:
177 e += "\tmake_trigger(trigger::signal(\n"
178 e += "match::" + s['match'] + "(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500179 for i, mp in enumerate(s['mparams']['params']):
180 if (i+1) != len(s['mparams']['params']):
181 e += "\t\t\t" + s['mparams'][mp] + ",\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600182 else:
Matthew Barth926df662018-10-09 09:51:12 -0500183 e += "\t\t\t" + s['mparams'][mp] + "\n"
184 e += "\t\t),\n"
185 e += "\t\tmake_handler<SignalHandler>(\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600186 if ('type' in s['sparams']) and (s['sparams']['type'] is not None):
187 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600188 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600189 e += s['signal'] + "(\n"
190 for sp in s['sparams']['params']:
191 e += s['sparams'][sp] + ",\n"
192 if ('type' in s['hparams']) and (s['hparams']['type'] is not None):
193 e += ("handler::" + s['handler'] +
194 "<" + s['hparams']['type'] + ">(\n")
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600195 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600196 e += "handler::" + s['handler'] + "(\n)"
197 for i, hp in enumerate(s['hparams']['params']):
198 if (i+1) != len(s['hparams']['params']):
199 e += s['hparams'][hp] + ",\n"
200 else:
201 e += s['hparams'][hp] + "\n"
202 e += "))\n"
Matthew Barth926df662018-10-09 09:51:12 -0500203 e += "\t\t)\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600204 e += "\t)),\n"
205
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600206 if ('init' in event['triggers']):
207 for i in event['triggers']['init']:
208 e += "\tmake_trigger(trigger::init(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500209 if ('method' in i):
210 e += "\t\tmake_handler<MethodHandler>(\n"
211 if ('type' in i['mparams']) and \
212 (i['mparams']['type'] is not None):
213 e += i['method'] + "<" + i['mparams']['type'] + ">(\n"
214 else:
215 e += i['method'] + "(\n"
216 for ip in i['mparams']['params']:
217 e += i['mparams'][ip] + ",\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600218 if ('type' in i['hparams']) and \
219 (i['hparams']['type'] is not None):
220 e += ("handler::" + i['handler'] +
221 "<" + i['hparams']['type'] + ">(\n")
222 else:
223 e += "handler::" + i['handler'] + "(\n)"
224 for i, hp in enumerate(i['hparams']['params']):
225 if (i+1) != len(i['hparams']['params']):
226 e += i['hparams'][hp] + ",\n"
227 else:
228 e += i['hparams'][hp] + "\n"
Matthew Barth926df662018-10-09 09:51:12 -0500229 e += "))\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600230 e += "\t\t)\n"
231 e += "\t)),\n"
232
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600233 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600234
235 e += "}"
236
237 return e
238
239
Matthew Barth6c050692017-12-05 15:30:09 -0600240def getGroups(zNum, zCond, edata, events):
241 """
242 Extract and construct the groups for the given event.
243 """
244 groups = []
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600245 if ('groups' in edata) and (edata['groups'] is not None):
246 for eGroups in edata['groups']:
247 if ('zone_conditions' in eGroups) and \
248 (eGroups['zone_conditions'] is not None):
249 # Zone conditions are optional in the events yaml but skip
250 # if this event's condition is not in this zone's conditions
251 if all('name' in z and z['name'] is not None and
252 not any(c['name'] == z['name'] for c in zCond)
253 for z in eGroups['zone_conditions']):
254 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600255
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600256 # Zone numbers are optional in the events yaml but skip if this
257 # zone's zone number is not in the event's zone numbers
258 if all('zones' in z and z['zones'] is not None and
259 zNum not in z['zones']
260 for z in eGroups['zone_conditions']):
261 continue
262 eGroup = next(g for g in events['groups']
263 if g['name'] == eGroups['name'])
Matthew Barth6c050692017-12-05 15:30:09 -0600264
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600265 group = {}
266 members = []
267 group['name'] = eGroup['name']
268 for m in eGroup['members']:
269 member = {}
270 member['path'] = eGroup['type']
271 member['object'] = (eGroup['type'] + m)
272 member['interface'] = eGroups['interface']
273 member['property'] = eGroups['property']['name']
274 member['type'] = eGroups['property']['type']
275 # Use defined service to note member on zone object
276 if ('service' in eGroup) and \
277 (eGroup['service'] is not None):
278 member['service'] = eGroup['service']
279 # Add expected group member's property value if given
280 if ('value' in eGroups['property']) and \
281 (eGroups['property']['value'] is not None):
282 if isinstance(eGroups['property']['value'], str) or \
283 "string" in str(member['type']).lower():
284 member['value'] = (
285 "\"" + eGroups['property']['value'] + "\"")
286 else:
287 member['value'] = eGroups['property']['value']
288 members.append(member)
289 group['members'] = members
290 groups.append(group)
Matthew Barth6c050692017-12-05 15:30:09 -0600291 return groups
292
293
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600294def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500295 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600296 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500297 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600298 params = {}
299 if ('parameters' in section) and \
300 (section['parameters'] is not None):
301 plist = []
302 for sp in section['parameters']:
303 p = str(sp)
304 if (p != 'type'):
305 plist.append(p)
306 if (p != 'group'):
307 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500308 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600309 params[p] = "Group\n{\n"
310 for g in groups:
311 for m in g['members']:
312 params[p] += (
313 "{\"" + str(m['object']) + "\",\n" +
314 "\"" + str(m['interface']) + "\",\n" +
315 "\"" + str(m['property']) + "\"},\n")
316 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500317 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600318 params[p] = member[p]
319 params['params'] = plist
320 else:
321 params['params'] = []
322 return params
323
324
325def getInit(eGrps, eTrig, events):
326 """
327 Extracts and constructs an init trigger for the event's groups
328 which are required to be of the same type.
329 """
330 method = {}
331 methods = []
Matthew Barth03774012018-10-26 13:25:43 -0500332 if (len(eGrps) > 0):
333 # Use the first group member for retrieving the type
334 member = eGrps[0]['members'][0]
335 if ('method' in eTrig) and \
336 (eTrig['method'] is not None):
337 # Add method parameters
338 eMethod = next(m for m in events['methods']
339 if m['name'] == eTrig['method'])
340 method['method'] = eMethod['name']
341 method['mparams'] = getParameters(
342 member, eGrps, eMethod, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600343
Matthew Barth03774012018-10-26 13:25:43 -0500344 # Add handler parameters
345 eHandler = next(h for h in events['handlers']
346 if h['name'] == eTrig['handler'])
347 method['handler'] = eHandler['name']
348 method['hparams'] = getParameters(
349 member, eGrps, eHandler, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600350
351 methods.append(method)
352
353 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500354
355
Matthew Bartha69465a2018-03-02 13:50:59 -0600356def getSignal(eGrps, eTrig, events):
357 """
358 Extracts and constructs for each group member a signal
359 subscription of each match listed in the trigger.
360 """
361 signals = []
362 for group in eGrps:
363 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600364 signal = {}
365 # Add signal parameters
366 eSignal = next(s for s in events['signals']
367 if s['name'] == eTrig['signal'])
368 signal['signal'] = eSignal['name']
369 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
370
371 # If service not given, subscribe to signal match
372 if ('service' not in member):
373 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600374 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600375 if m['name'] == eSignal['match'])
376 signal['match'] = eMatch['name']
377 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600378
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600379 # Add handler parameters
380 eHandler = next(h for h in events['handlers']
381 if h['name'] == eTrig['handler'])
382 signal['handler'] = eHandler['name']
383 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500384
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600385 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500386
Matthew Bartha69465a2018-03-02 13:50:59 -0600387 return signals
388
389
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600390def getTimer(eTrig):
391 """
392 Extracts and constructs the required parameters for an
393 event timer.
394 """
395 timer = {}
396 timer['interval'] = (
397 "static_cast<std::chrono::microseconds>" +
398 "(" + str(eTrig['interval']) + ")")
399 timer['type'] = "TimerType::" + str(eTrig['type'])
400 return timer
401
402
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600403def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500404 """
405 Extracts and constructs the make_action function call for
406 all the actions within the given event.
407 """
408 action = []
409 for eActions in actions['actions']:
410 actions = {}
411 eAction = next(a for a in events['actions']
412 if a['name'] == eActions['name'])
413 actions['name'] = eAction['name']
414 params = []
415 if ('parameters' in eAction) and \
416 (eAction['parameters'] is not None):
417 for p in eAction['parameters']:
418 param = "static_cast<"
419 if type(eActions[p]) is not dict:
420 if p == 'actions':
421 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600422 pActs = getActions(zNum,
423 zCond,
424 edata,
425 eActions,
426 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500427 for a in pActs:
428 if (len(a['parameters']) != 0):
429 param += (
430 "make_action(action::" +
431 a['name'] +
432 "(\n")
433 for i, ap in enumerate(a['parameters']):
434 if (i+1) != len(a['parameters']):
435 param += (ap + ",")
436 else:
437 param += (ap + ")")
438 else:
439 param += ("make_action(action::" + a['name'])
440 param += "),"
441 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600442 elif p == 'defevents' or p == 'altevents':
443 param = "std::vector<SetSpeedEvent>{\n"
444 for i, e in enumerate(eActions[p]):
445 aEvent = getEvent(zNum, zCond, e, events)
446 if not aEvent:
447 continue
448 if (i+1) != len(eActions[p]):
449 param += genEvent(aEvent) + ",\n"
450 else:
451 param += genEvent(aEvent) + "\n"
452 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500453 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600454 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600455 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600456 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600457 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600458 ">(\"" + str(eActions[p]) + "\")")
459 else:
460 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600461 str(eActions[p]['type']).lower() +
462 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500463 else:
464 # Default type to 'size_t' when not given
465 param += ("size_t>(" + str(eActions[p]).lower() + ")")
466 else:
467 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600468 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500469 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600470 "TimerConf{" + t['interval'] + "," +
471 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500472 else:
473 param += (str(eActions[p]['type']).lower() + ">(")
474 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600475 if isinstance(eActions[p]['value'], str) or \
476 "string" in str(eActions[p]['type']).lower():
477 param += \
478 "\"" + str(eActions[p]['value']) + "\")"
479 else:
480 param += \
481 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500482 else:
483 param += (
484 str(eActions[p]['type']).lower() +
485 convertToMap(str(eActions[p]['value'])) + ")")
486 params.append(param)
487 actions['parameters'] = params
488 action.append(actions)
489 return action
490
491
Matthew Barth7f272fd2017-09-12 16:16:56 -0500492def getEvent(zone_num, zone_conditions, e, events_data):
493 """
494 Parses the sections of an event and populates the properties
495 that construct an event within the generated source.
496 """
497 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500498
Matthew Barth621a5772018-11-14 14:55:11 -0600499 # Add set speed event name
500 event['name'] = e['name']
501
Matthew Barth6c050692017-12-05 15:30:09 -0600502 # Add set speed event groups
Matthew Barth03774012018-10-26 13:25:43 -0500503 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500504
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600505 # Add optional set speed actions and function parameters
506 event['action'] = []
507 if ('actions' in e) and \
508 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600509 event['action'] = getActions(zone_num,
510 zone_conditions,
511 e,
512 e,
513 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500514
Matthew Bartha69465a2018-03-02 13:50:59 -0600515 # Add event triggers
516 event['triggers'] = {}
517 for trig in e['triggers']:
518 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600519 if (trig['name'] == "timer"):
520 event['triggers']['timer'] = getTimer(trig)
521 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600522 if ('signals' not in event['triggers']):
523 event['triggers']['signals'] = []
524 triggers = getSignal(event['groups'], trig, events_data)
525 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600526 elif (trig['name'] == "init"):
527 triggers = getInit(event['groups'], trig, events_data)
528 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500529
Matthew Barth7f272fd2017-09-12 16:16:56 -0500530 return event
531
532
533def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500534 """
535 Parses the precondition section of an event and populates the necessary
536 structures to generate a precondition for a set speed event.
537 """
538 precond = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600539
540 # Add set speed event precondition name
541 precond['pcname'] = event['name']
542
Matthew Barth9af190c2017-08-08 14:20:43 -0500543 # Add set speed event precondition group
Matthew Barth03774012018-10-26 13:25:43 -0500544 precond['pcgrps'] = getGroups(zNum,
545 zCond,
546 event['precondition'],
547 events_data)
Matthew Barth9af190c2017-08-08 14:20:43 -0500548
Matthew Barth7f272fd2017-09-12 16:16:56 -0500549 # Add set speed event precondition actions
550 pc = []
551 pcs = {}
552 pcs['name'] = event['precondition']['name']
553 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500554 if p['name'] == event['precondition']['name'])
555 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500556 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500557 param = {}
558 if p == 'groups':
559 param['type'] = "std::vector<PrecondGroup>"
560 param['open'] = "{"
561 param['close'] = "}"
562 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600563 for group in precond['pcgrps']:
564 for pcgrp in group['members']:
565 value = {}
566 value['value'] = (
567 "PrecondGroup{\"" +
568 str(pcgrp['object']) + "\",\"" +
569 str(pcgrp['interface']) + "\",\"" +
570 str(pcgrp['property']) + "\"," +
571 "static_cast<" +
572 str(pcgrp['type']).lower() + ">")
573 if isinstance(pcgrp['value'], str) or \
574 "string" in str(pcgrp['type']).lower():
575 value['value'] += ("(" + str(pcgrp['value']) + ")}")
576 else:
577 value['value'] += \
578 ("(" + str(pcgrp['value']).lower() + ")}")
579 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500580 param['values'] = values
581 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500582 pcs['params'] = params
583 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500584 precond['pcact'] = pc
585
Matthew Barth7f272fd2017-09-12 16:16:56 -0500586 pcevents = []
587 for pce in event['precondition']['events']:
588 pcevent = getEvent(zNum, zCond, pce, events_data)
589 if not pcevent:
590 continue
591 pcevents.append(pcevent)
592 precond['pcevts'] = pcevents
593
Matthew Barthf20c3212018-03-02 14:42:55 -0600594 # Add precondition event triggers
595 precond['triggers'] = {}
596 for trig in event['precondition']['triggers']:
597 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600598 if (trig['name'] == "timer"):
599 precond['triggers']['pctime'] = getTimer(trig)
600 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600601 if ('pcsigs' not in precond['triggers']):
602 precond['triggers']['pcsigs'] = []
603 triggers = getSignal(precond['pcgrps'], trig, events_data)
604 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600605 elif (trig['name'] == "init"):
606 triggers = getInit(precond['pcgrps'], trig, events_data)
607 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500608
609 return precond
610
611
Gunnar Millsb751f322017-06-06 15:14:11 -0500612def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500613 """
614 Constructs the event entries defined for each zone using the events yaml
615 provided.
616 """
617 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500618
Matthew Barthd4d0f082017-05-16 13:51:10 -0500619 if 'events' in events_data:
620 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500621 event = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600622
Matthew Barth9af190c2017-08-08 14:20:43 -0500623 # Add precondition if given
624 if ('precondition' in e) and \
625 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500626 event['pc'] = addPrecondition(zone_num,
627 zone_conditions,
628 e,
629 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500630 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500631 event = getEvent(zone_num, zone_conditions, e, events_data)
632 if not event:
633 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500634 events.append(event)
635
636 return events
637
638
Matt Spinler78498c92017-04-11 13:59:46 -0500639def getFansInZone(zone_num, profiles, fan_data):
640 """
641 Parses the fan definition YAML files to find the fans
642 that match both the zone passed in and one of the
643 cooling profiles.
644 """
645
646 fans = []
647
648 for f in fan_data['fans']:
649
650 if zone_num != f['cooling_zone']:
651 continue
652
Gunnar Mills67e95512017-06-02 14:35:18 -0500653 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500654 if f.get('cooling_profile') is None:
655 profile = "all"
656 else:
657 profile = f['cooling_profile']
658
659 if profile not in profiles:
660 continue
661
662 fan = {}
663 fan['name'] = f['inventory']
664 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800665 fan['target_interface'] = f.get(
666 'target_interface',
667 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500668 fans.append(fan)
669
670 return fans
671
672
Matthew Barth7883f582019-02-14 14:24:46 -0600673def getIfacesInZone(zone_ifaces):
674 """
675 Parse given interfaces for a zone for associating a zone with an interface
676 and set any properties listed to defined values upon fan control starting
677 on the zone.
678 """
679
680 ifaces = []
681 for i in zone_ifaces:
682 iface = {}
683 # Interface name not needed yet for fan zones but
684 # may be necessary as more interfaces are extended by the zones
685 iface['name'] = i['name']
686
687 if ('properties' in i) and \
688 (i['properties'] is not None):
689 props = []
690 for p in i['properties']:
691 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600692 prop['name'] = p['name']
693 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600694 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600695 if ('persist' in p):
696 persist = p['persist']
697 if (persist is not None):
698 if (isinstance(persist, bool)):
699 prop['persist'] = 'true' if persist else 'false'
700 else:
701 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600702 vals = []
703 for v in p['values']:
704 val = v['value']
705 if (val is not None):
706 if (isinstance(val, bool)):
707 # Convert True/False to 'true'/'false'
708 val = 'true' if val else 'false'
709 elif (isinstance(val, str)):
710 # Wrap strings with double-quotes
711 val = "\"" + val + "\""
712 vals.append(val)
713 prop['values'] = vals
714 props.append(prop)
715 iface['props'] = props
716 ifaces.append(iface)
717
718 return ifaces
719
720
Gunnar Millsee8a2812017-06-02 14:26:47 -0500721def getConditionInZoneConditions(zone_condition, zone_conditions_data):
722 """
723 Parses the zone conditions definition YAML files to find the condition
724 that match both the zone condition passed in.
725 """
726
727 condition = {}
728
729 for c in zone_conditions_data['conditions']:
730
731 if zone_condition != c['name']:
732 continue
733 condition['type'] = c['type']
734 properties = []
735 for p in c['properties']:
736 property = {}
737 property['property'] = p['property']
738 property['interface'] = p['interface']
739 property['path'] = p['path']
740 property['type'] = p['type'].lower()
741 property['value'] = str(p['value']).lower()
742 properties.append(property)
743 condition['properties'] = properties
744
745 return condition
746
747
748def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500749 """
750 Combines the zone definition YAML and fan
751 definition YAML to create a data structure defining
752 the fan cooling zones.
753 """
754
755 zone_groups = []
756
757 for group in zone_data:
758 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500759 # zone conditions are optional
760 if 'zone_conditions' in group and group['zone_conditions'] is not None:
761 for c in group['zone_conditions']:
762
763 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500764 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500765 "zone_conditions used in zone YAML")
766
767 condition = getConditionInZoneConditions(c['name'],
768 zone_conditions_data)
769
770 if not condition:
771 sys.exit("Missing zone condition " + c['name'])
772
773 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500774
775 zone_group = {}
776 zone_group['conditions'] = conditions
777
778 zones = []
779 for z in group['zones']:
780 zone = {}
781
Gunnar Mills67e95512017-06-02 14:35:18 -0500782 # 'zone' is required
783 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500784 sys.exit("Missing fan zone number in " + zone_yaml)
785
786 zone['num'] = z['zone']
787
788 zone['full_speed'] = z['full_speed']
789
Matthew Barth1de66622017-06-12 13:13:02 -0500790 zone['default_floor'] = z['default_floor']
791
Matthew Bartha9561842017-06-29 11:43:45 -0500792 # 'increase_delay' is optional (use 0 by default)
793 key = 'increase_delay'
794 zone[key] = z.setdefault(key, 0)
795
796 # 'decrease_interval' is optional (use 0 by default)
797 key = 'decrease_interval'
798 zone[key] = z.setdefault(key, 0)
799
Gunnar Mills67e95512017-06-02 14:35:18 -0500800 # 'cooling_profiles' is optional (use 'all' instead)
801 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500802 (z['cooling_profiles'] is None):
803 profiles = ["all"]
804 else:
805 profiles = z['cooling_profiles']
806
Matthew Barth7883f582019-02-14 14:24:46 -0600807 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600808 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600809 if ('interfaces' in z) and \
810 (z['interfaces'] is not None):
811 ifaces = getIfacesInZone(z['interfaces'])
812
Matt Spinler78498c92017-04-11 13:59:46 -0500813 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500814 events = getEventsInZone(z['zone'], group['zone_conditions'],
815 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500816
817 if len(fans) == 0:
818 sys.exit("Didn't find any fans in zone " + str(zone['num']))
819
Matthew Barth7883f582019-02-14 14:24:46 -0600820 if (ifaces):
821 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500822 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500823 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500824 zones.append(zone)
825
826 zone_group['zones'] = zones
827 zone_groups.append(zone_group)
828
829 return zone_groups
830
831
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500832if __name__ == '__main__':
833 parser = ArgumentParser(
834 description="Phosphor fan zone definition parser")
835
836 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
837 default="example/zones.yaml",
838 help='fan zone definitional yaml')
839 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
840 default="example/fans.yaml",
841 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500842 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
843 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500844 parser.add_argument('-c', '--zone_conditions_yaml',
845 dest='zone_conditions_yaml',
846 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500847 parser.add_argument('-o', '--output_dir', dest='output_dir',
848 default=".",
849 help='output directory')
850 args = parser.parse_args()
851
852 if not args.zone_yaml or not args.fan_yaml:
853 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700854 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500855
856 with open(args.zone_yaml, 'r') as zone_input:
857 zone_data = yaml.safe_load(zone_input) or {}
858
859 with open(args.fan_yaml, 'r') as fan_input:
860 fan_data = yaml.safe_load(fan_input) or {}
861
Matthew Barthd4d0f082017-05-16 13:51:10 -0500862 events_data = {}
863 if args.events_yaml:
864 with open(args.events_yaml, 'r') as events_input:
865 events_data = yaml.safe_load(events_input) or {}
866
Gunnar Millsee8a2812017-06-02 14:26:47 -0500867 zone_conditions_data = {}
868 if args.zone_conditions_yaml:
869 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
870 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
871
Matt Spinleree7f6422017-05-09 11:03:14 -0500872 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500873 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500874
875 manager_config = zone_data.get('manager_configuration', {})
876
877 if manager_config.get('power_on_delay') is None:
878 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500879
Matthew Barth702c4a52018-02-28 16:23:11 -0600880 tmpls_dir = os.path.join(
881 os.path.dirname(os.path.realpath(__file__)),
882 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500883 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600884 if sys.version_info < (3, 0):
885 lkup = TemplateLookup(
886 directories=tmpls_dir.split(),
887 disable_unicode=True)
888 else:
889 lkup = TemplateLookup(
890 directories=tmpls_dir.split())
891 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500892 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600893 output.write(tmpl.render(zones=zone_config,
894 mgr_data=manager_config))