blob: 4d310b9c9fae6279f6e2e032ce7961b953854b3f [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']:
Matthew Barth06fa7812018-11-20 09:54:30 -0600144 e += "{\"" + member['object'] + "\",\n"
Matthew Barth146b7392018-03-08 16:17:58 -0600145 e += "\"" + member['interface'] + "\",\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600146 e += "\"" + member['property'] + "\"},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600147 e += "},\n"
148
Matthew Barth06fa7812018-11-20 09:54:30 -0600149 e += "ActionData{\n"
150 for d in event['action']:
151 e += "{Group{\n"
152 for g in d['groups']:
153 for m in g['members']:
154 e += "{" + m['object'] + ",\n"
155 e += m['interface'] + ",\n"
156 e += m['property'] + "},\n"
157 e += "},\n"
158 e += "std::vector<Action>{\n"
159 for a in d['actions']:
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600160 e += "make_action(action::" + a['name'] + "(\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600161 for i, p in enumerate(a['parameters']):
162 if (i+1) != len(a['parameters']):
163 e += p + ",\n"
164 else:
165 e += p + "\n"
166 e += ")),\n"
167 e += "}},\n"
168 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600169
Matthew Barth1b4de262018-03-06 13:03:16 -0600170 e += "std::vector<Trigger>{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600171 if ('timer' in event['triggers']) and \
172 (event['triggers']['timer'] is not None):
Matthew Barth1b4de262018-03-06 13:03:16 -0600173 e += "\tmake_trigger(trigger::timer(TimerConf{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600174 e += "\t" + event['triggers']['timer']['interval'] + ",\n"
175 e += "\t" + event['triggers']['timer']['type'] + "\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600176 e += "\t})),\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600177
Matthew Barth016bd242018-03-07 16:06:06 -0600178 if ('signals' in event['triggers']) and \
179 (event['triggers']['signals'] is not None):
180 for s in event['triggers']['signals']:
181 e += "\tmake_trigger(trigger::signal(\n"
182 e += "match::" + s['match'] + "(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500183 for i, mp in enumerate(s['mparams']['params']):
184 if (i+1) != len(s['mparams']['params']):
185 e += "\t\t\t" + s['mparams'][mp] + ",\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600186 else:
Matthew Barth926df662018-10-09 09:51:12 -0500187 e += "\t\t\t" + s['mparams'][mp] + "\n"
188 e += "\t\t),\n"
189 e += "\t\tmake_handler<SignalHandler>(\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600190 if ('type' in s['sparams']) and (s['sparams']['type'] is not None):
191 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600192 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600193 e += s['signal'] + "(\n"
194 for sp in s['sparams']['params']:
195 e += s['sparams'][sp] + ",\n"
196 if ('type' in s['hparams']) and (s['hparams']['type'] is not None):
197 e += ("handler::" + s['handler'] +
198 "<" + s['hparams']['type'] + ">(\n")
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600199 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600200 e += "handler::" + s['handler'] + "(\n)"
201 for i, hp in enumerate(s['hparams']['params']):
202 if (i+1) != len(s['hparams']['params']):
203 e += s['hparams'][hp] + ",\n"
204 else:
205 e += s['hparams'][hp] + "\n"
206 e += "))\n"
Matthew Barth926df662018-10-09 09:51:12 -0500207 e += "\t\t)\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600208 e += "\t)),\n"
209
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600210 if ('init' in event['triggers']):
211 for i in event['triggers']['init']:
212 e += "\tmake_trigger(trigger::init(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500213 if ('method' in i):
214 e += "\t\tmake_handler<MethodHandler>(\n"
215 if ('type' in i['mparams']) and \
216 (i['mparams']['type'] is not None):
217 e += i['method'] + "<" + i['mparams']['type'] + ">(\n"
218 else:
219 e += i['method'] + "(\n"
220 for ip in i['mparams']['params']:
221 e += i['mparams'][ip] + ",\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600222 if ('type' in i['hparams']) and \
223 (i['hparams']['type'] is not None):
224 e += ("handler::" + i['handler'] +
225 "<" + i['hparams']['type'] + ">(\n")
226 else:
227 e += "handler::" + i['handler'] + "(\n)"
228 for i, hp in enumerate(i['hparams']['params']):
229 if (i+1) != len(i['hparams']['params']):
230 e += i['hparams'][hp] + ",\n"
231 else:
232 e += i['hparams'][hp] + "\n"
Matthew Barth926df662018-10-09 09:51:12 -0500233 e += "))\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600234 e += "\t\t)\n"
235 e += "\t)),\n"
236
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600237 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600238
239 e += "}"
240
241 return e
242
243
Matthew Barth6c050692017-12-05 15:30:09 -0600244def getGroups(zNum, zCond, edata, events):
245 """
246 Extract and construct the groups for the given event.
247 """
248 groups = []
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600249 if ('groups' in edata) and (edata['groups'] is not None):
250 for eGroups in edata['groups']:
251 if ('zone_conditions' in eGroups) and \
252 (eGroups['zone_conditions'] is not None):
253 # Zone conditions are optional in the events yaml but skip
254 # if this event's condition is not in this zone's conditions
255 if all('name' in z and z['name'] is not None and
256 not any(c['name'] == z['name'] for c in zCond)
257 for z in eGroups['zone_conditions']):
258 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600259
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600260 # Zone numbers are optional in the events yaml but skip if this
261 # zone's zone number is not in the event's zone numbers
262 if all('zones' in z and z['zones'] is not None and
263 zNum not in z['zones']
264 for z in eGroups['zone_conditions']):
265 continue
266 eGroup = next(g for g in events['groups']
267 if g['name'] == eGroups['name'])
Matthew Barth6c050692017-12-05 15:30:09 -0600268
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600269 group = {}
270 members = []
271 group['name'] = eGroup['name']
272 for m in eGroup['members']:
273 member = {}
274 member['path'] = eGroup['type']
275 member['object'] = (eGroup['type'] + m)
276 member['interface'] = eGroups['interface']
277 member['property'] = eGroups['property']['name']
278 member['type'] = eGroups['property']['type']
279 # Use defined service to note member on zone object
280 if ('service' in eGroup) and \
281 (eGroup['service'] is not None):
282 member['service'] = eGroup['service']
283 # Add expected group member's property value if given
284 if ('value' in eGroups['property']) and \
285 (eGroups['property']['value'] is not None):
286 if isinstance(eGroups['property']['value'], str) or \
287 "string" in str(member['type']).lower():
288 member['value'] = (
289 "\"" + eGroups['property']['value'] + "\"")
290 else:
291 member['value'] = eGroups['property']['value']
292 members.append(member)
293 group['members'] = members
294 groups.append(group)
Matthew Barth6c050692017-12-05 15:30:09 -0600295 return groups
296
297
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600298def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500299 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600300 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500301 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600302 params = {}
303 if ('parameters' in section) and \
304 (section['parameters'] is not None):
305 plist = []
306 for sp in section['parameters']:
307 p = str(sp)
308 if (p != 'type'):
309 plist.append(p)
310 if (p != 'group'):
311 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500312 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600313 params[p] = "Group\n{\n"
314 for g in groups:
315 for m in g['members']:
316 params[p] += (
317 "{\"" + str(m['object']) + "\",\n" +
318 "\"" + str(m['interface']) + "\",\n" +
319 "\"" + str(m['property']) + "\"},\n")
320 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500321 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600322 params[p] = member[p]
323 params['params'] = plist
324 else:
325 params['params'] = []
326 return params
327
328
329def getInit(eGrps, eTrig, events):
330 """
331 Extracts and constructs an init trigger for the event's groups
332 which are required to be of the same type.
333 """
334 method = {}
335 methods = []
Matthew Barth03774012018-10-26 13:25:43 -0500336 if (len(eGrps) > 0):
337 # Use the first group member for retrieving the type
338 member = eGrps[0]['members'][0]
339 if ('method' in eTrig) and \
340 (eTrig['method'] is not None):
341 # Add method parameters
342 eMethod = next(m for m in events['methods']
343 if m['name'] == eTrig['method'])
344 method['method'] = eMethod['name']
345 method['mparams'] = getParameters(
346 member, eGrps, eMethod, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600347
Matthew Barth03774012018-10-26 13:25:43 -0500348 # Add handler parameters
349 eHandler = next(h for h in events['handlers']
350 if h['name'] == eTrig['handler'])
351 method['handler'] = eHandler['name']
352 method['hparams'] = getParameters(
353 member, eGrps, eHandler, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600354
355 methods.append(method)
356
357 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500358
359
Matthew Bartha69465a2018-03-02 13:50:59 -0600360def getSignal(eGrps, eTrig, events):
361 """
362 Extracts and constructs for each group member a signal
363 subscription of each match listed in the trigger.
364 """
365 signals = []
366 for group in eGrps:
367 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600368 signal = {}
369 # Add signal parameters
370 eSignal = next(s for s in events['signals']
371 if s['name'] == eTrig['signal'])
372 signal['signal'] = eSignal['name']
373 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
374
375 # If service not given, subscribe to signal match
376 if ('service' not in member):
377 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600378 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600379 if m['name'] == eSignal['match'])
380 signal['match'] = eMatch['name']
381 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600382
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600383 # Add handler parameters
384 eHandler = next(h for h in events['handlers']
385 if h['name'] == eTrig['handler'])
386 signal['handler'] = eHandler['name']
387 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500388
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600389 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500390
Matthew Bartha69465a2018-03-02 13:50:59 -0600391 return signals
392
393
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600394def getTimer(eTrig):
395 """
396 Extracts and constructs the required parameters for an
397 event timer.
398 """
399 timer = {}
400 timer['interval'] = (
401 "static_cast<std::chrono::microseconds>" +
402 "(" + str(eTrig['interval']) + ")")
403 timer['type'] = "TimerType::" + str(eTrig['type'])
404 return timer
405
406
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600407def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500408 """
409 Extracts and constructs the make_action function call for
410 all the actions within the given event.
411 """
412 action = []
413 for eActions in actions['actions']:
414 actions = {}
415 eAction = next(a for a in events['actions']
416 if a['name'] == eActions['name'])
417 actions['name'] = eAction['name']
Matthew Barth06fa7812018-11-20 09:54:30 -0600418 actions['groups'] = getGroups(zNum, zCond, eActions, events)
Matthew Barth9df74752017-10-11 14:39:31 -0500419 params = []
420 if ('parameters' in eAction) and \
421 (eAction['parameters'] is not None):
422 for p in eAction['parameters']:
423 param = "static_cast<"
424 if type(eActions[p]) is not dict:
425 if p == 'actions':
426 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600427 pActs = getActions(zNum,
428 zCond,
429 edata,
430 eActions,
431 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500432 for a in pActs:
433 if (len(a['parameters']) != 0):
434 param += (
435 "make_action(action::" +
436 a['name'] +
437 "(\n")
438 for i, ap in enumerate(a['parameters']):
439 if (i+1) != len(a['parameters']):
440 param += (ap + ",")
441 else:
442 param += (ap + ")")
443 else:
444 param += ("make_action(action::" + a['name'])
445 param += "),"
446 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600447 elif p == 'defevents' or p == 'altevents':
448 param = "std::vector<SetSpeedEvent>{\n"
449 for i, e in enumerate(eActions[p]):
450 aEvent = getEvent(zNum, zCond, e, events)
451 if not aEvent:
452 continue
453 if (i+1) != len(eActions[p]):
454 param += genEvent(aEvent) + ",\n"
455 else:
456 param += genEvent(aEvent) + "\n"
457 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500458 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600459 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600460 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600461 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600462 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600463 ">(\"" + str(eActions[p]) + "\")")
464 else:
465 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600466 str(eActions[p]['type']).lower() +
467 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500468 else:
469 # Default type to 'size_t' when not given
470 param += ("size_t>(" + str(eActions[p]).lower() + ")")
471 else:
472 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600473 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500474 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600475 "TimerConf{" + t['interval'] + "," +
476 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500477 else:
478 param += (str(eActions[p]['type']).lower() + ">(")
479 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600480 if isinstance(eActions[p]['value'], str) or \
481 "string" in str(eActions[p]['type']).lower():
482 param += \
483 "\"" + str(eActions[p]['value']) + "\")"
484 else:
485 param += \
486 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500487 else:
488 param += (
489 str(eActions[p]['type']).lower() +
490 convertToMap(str(eActions[p]['value'])) + ")")
491 params.append(param)
492 actions['parameters'] = params
493 action.append(actions)
494 return action
495
496
Matthew Barth7f272fd2017-09-12 16:16:56 -0500497def getEvent(zone_num, zone_conditions, e, events_data):
498 """
499 Parses the sections of an event and populates the properties
500 that construct an event within the generated source.
501 """
502 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500503
Matthew Barth621a5772018-11-14 14:55:11 -0600504 # Add set speed event name
505 event['name'] = e['name']
506
Matthew Barth6c050692017-12-05 15:30:09 -0600507 # Add set speed event groups
Matthew Barth03774012018-10-26 13:25:43 -0500508 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500509
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600510 # Add optional set speed actions and function parameters
511 event['action'] = []
512 if ('actions' in e) and \
513 (e['actions'] is not None):
Matthew Barth06fa7812018-11-20 09:54:30 -0600514 # List of dicts containing the list of groups and list of actions
515 sseActions = []
516 eActions = getActions(zone_num, zone_conditions, e, e, events_data)
517 for eAction in eActions:
518 # Skip events that have no groups defined for the event or actions
519 if not event['groups'] and not eAction['groups']:
520 continue
521 # Find group in sseActions
522 grpExists = False
523 for sseDict in sseActions:
524 if eAction['groups'] == sseDict['groups']:
525 # Extend 'actions' list
526 del eAction['groups']
527 sseDict['actions'].append(eAction)
528 grpExists = True
529 break
530 if not grpExists:
531 grps = eAction['groups']
532 del eAction['groups']
533 actList = []
534 actList.append(eAction)
Matthew Bartha6f75162018-11-20 13:50:42 -0600535 sseActions.append({'groups': grps, 'actions': actList})
Matthew Barth06fa7812018-11-20 09:54:30 -0600536 event['action'] = sseActions
Matthew Barth7f272fd2017-09-12 16:16:56 -0500537
Matthew Bartha69465a2018-03-02 13:50:59 -0600538 # Add event triggers
539 event['triggers'] = {}
540 for trig in e['triggers']:
541 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600542 if (trig['name'] == "timer"):
543 event['triggers']['timer'] = getTimer(trig)
544 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600545 if ('signals' not in event['triggers']):
546 event['triggers']['signals'] = []
547 triggers = getSignal(event['groups'], trig, events_data)
548 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600549 elif (trig['name'] == "init"):
550 triggers = getInit(event['groups'], trig, events_data)
551 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500552
Matthew Barth7f272fd2017-09-12 16:16:56 -0500553 return event
554
555
556def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500557 """
558 Parses the precondition section of an event and populates the necessary
559 structures to generate a precondition for a set speed event.
560 """
561 precond = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600562
563 # Add set speed event precondition name
564 precond['pcname'] = event['name']
565
Matthew Barth9af190c2017-08-08 14:20:43 -0500566 # Add set speed event precondition group
Matthew Barth03774012018-10-26 13:25:43 -0500567 precond['pcgrps'] = getGroups(zNum,
568 zCond,
569 event['precondition'],
570 events_data)
Matthew Barth9af190c2017-08-08 14:20:43 -0500571
Matthew Barth7f272fd2017-09-12 16:16:56 -0500572 # Add set speed event precondition actions
573 pc = []
574 pcs = {}
575 pcs['name'] = event['precondition']['name']
576 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500577 if p['name'] == event['precondition']['name'])
578 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500579 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500580 param = {}
581 if p == 'groups':
582 param['type'] = "std::vector<PrecondGroup>"
583 param['open'] = "{"
584 param['close'] = "}"
585 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600586 for group in precond['pcgrps']:
587 for pcgrp in group['members']:
588 value = {}
589 value['value'] = (
590 "PrecondGroup{\"" +
591 str(pcgrp['object']) + "\",\"" +
592 str(pcgrp['interface']) + "\",\"" +
593 str(pcgrp['property']) + "\"," +
594 "static_cast<" +
595 str(pcgrp['type']).lower() + ">")
596 if isinstance(pcgrp['value'], str) or \
597 "string" in str(pcgrp['type']).lower():
598 value['value'] += ("(" + str(pcgrp['value']) + ")}")
599 else:
600 value['value'] += \
601 ("(" + str(pcgrp['value']).lower() + ")}")
602 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500603 param['values'] = values
604 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500605 pcs['params'] = params
606 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500607 precond['pcact'] = pc
608
Matthew Barth7f272fd2017-09-12 16:16:56 -0500609 pcevents = []
610 for pce in event['precondition']['events']:
611 pcevent = getEvent(zNum, zCond, pce, events_data)
612 if not pcevent:
613 continue
614 pcevents.append(pcevent)
615 precond['pcevts'] = pcevents
616
Matthew Barthf20c3212018-03-02 14:42:55 -0600617 # Add precondition event triggers
618 precond['triggers'] = {}
619 for trig in event['precondition']['triggers']:
620 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600621 if (trig['name'] == "timer"):
622 precond['triggers']['pctime'] = getTimer(trig)
623 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600624 if ('pcsigs' not in precond['triggers']):
625 precond['triggers']['pcsigs'] = []
626 triggers = getSignal(precond['pcgrps'], trig, events_data)
627 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600628 elif (trig['name'] == "init"):
629 triggers = getInit(precond['pcgrps'], trig, events_data)
630 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500631
632 return precond
633
634
Gunnar Millsb751f322017-06-06 15:14:11 -0500635def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500636 """
637 Constructs the event entries defined for each zone using the events yaml
638 provided.
639 """
640 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500641
Matthew Barthd4d0f082017-05-16 13:51:10 -0500642 if 'events' in events_data:
643 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500644 event = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600645
Matthew Barth9af190c2017-08-08 14:20:43 -0500646 # Add precondition if given
647 if ('precondition' in e) and \
648 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500649 event['pc'] = addPrecondition(zone_num,
650 zone_conditions,
651 e,
652 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500653 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500654 event = getEvent(zone_num, zone_conditions, e, events_data)
Matthew Bartha6f75162018-11-20 13:50:42 -0600655 # Remove empty events and events that have
656 # no groups defined for the event or any of the actions
657 if not event or \
658 (not event['groups'] and
659 all(not a['groups'] for a in event['action'])):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500660 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500661 events.append(event)
662
663 return events
664
665
Matt Spinler78498c92017-04-11 13:59:46 -0500666def getFansInZone(zone_num, profiles, fan_data):
667 """
668 Parses the fan definition YAML files to find the fans
669 that match both the zone passed in and one of the
670 cooling profiles.
671 """
672
673 fans = []
674
675 for f in fan_data['fans']:
676
677 if zone_num != f['cooling_zone']:
678 continue
679
Gunnar Mills67e95512017-06-02 14:35:18 -0500680 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500681 if f.get('cooling_profile') is None:
682 profile = "all"
683 else:
684 profile = f['cooling_profile']
685
686 if profile not in profiles:
687 continue
688
689 fan = {}
690 fan['name'] = f['inventory']
691 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800692 fan['target_interface'] = f.get(
693 'target_interface',
694 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500695 fans.append(fan)
696
697 return fans
698
699
Matthew Barth7883f582019-02-14 14:24:46 -0600700def getIfacesInZone(zone_ifaces):
701 """
702 Parse given interfaces for a zone for associating a zone with an interface
703 and set any properties listed to defined values upon fan control starting
704 on the zone.
705 """
706
707 ifaces = []
708 for i in zone_ifaces:
709 iface = {}
710 # Interface name not needed yet for fan zones but
711 # may be necessary as more interfaces are extended by the zones
712 iface['name'] = i['name']
713
714 if ('properties' in i) and \
715 (i['properties'] is not None):
716 props = []
717 for p in i['properties']:
718 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600719 prop['name'] = p['name']
720 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600721 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600722 if ('persist' in p):
723 persist = p['persist']
724 if (persist is not None):
725 if (isinstance(persist, bool)):
726 prop['persist'] = 'true' if persist else 'false'
727 else:
728 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600729 vals = []
730 for v in p['values']:
731 val = v['value']
732 if (val is not None):
733 if (isinstance(val, bool)):
734 # Convert True/False to 'true'/'false'
735 val = 'true' if val else 'false'
736 elif (isinstance(val, str)):
737 # Wrap strings with double-quotes
738 val = "\"" + val + "\""
739 vals.append(val)
740 prop['values'] = vals
741 props.append(prop)
742 iface['props'] = props
743 ifaces.append(iface)
744
745 return ifaces
746
747
Gunnar Millsee8a2812017-06-02 14:26:47 -0500748def getConditionInZoneConditions(zone_condition, zone_conditions_data):
749 """
750 Parses the zone conditions definition YAML files to find the condition
751 that match both the zone condition passed in.
752 """
753
754 condition = {}
755
756 for c in zone_conditions_data['conditions']:
757
758 if zone_condition != c['name']:
759 continue
760 condition['type'] = c['type']
761 properties = []
762 for p in c['properties']:
763 property = {}
764 property['property'] = p['property']
765 property['interface'] = p['interface']
766 property['path'] = p['path']
767 property['type'] = p['type'].lower()
768 property['value'] = str(p['value']).lower()
769 properties.append(property)
770 condition['properties'] = properties
771
772 return condition
773
774
775def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500776 """
777 Combines the zone definition YAML and fan
778 definition YAML to create a data structure defining
779 the fan cooling zones.
780 """
781
782 zone_groups = []
783
784 for group in zone_data:
785 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500786 # zone conditions are optional
787 if 'zone_conditions' in group and group['zone_conditions'] is not None:
788 for c in group['zone_conditions']:
789
790 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500791 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500792 "zone_conditions used in zone YAML")
793
794 condition = getConditionInZoneConditions(c['name'],
795 zone_conditions_data)
796
797 if not condition:
798 sys.exit("Missing zone condition " + c['name'])
799
800 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500801
802 zone_group = {}
803 zone_group['conditions'] = conditions
804
805 zones = []
806 for z in group['zones']:
807 zone = {}
808
Gunnar Mills67e95512017-06-02 14:35:18 -0500809 # 'zone' is required
810 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500811 sys.exit("Missing fan zone number in " + zone_yaml)
812
813 zone['num'] = z['zone']
814
815 zone['full_speed'] = z['full_speed']
816
Matthew Barth1de66622017-06-12 13:13:02 -0500817 zone['default_floor'] = z['default_floor']
818
Matthew Bartha9561842017-06-29 11:43:45 -0500819 # 'increase_delay' is optional (use 0 by default)
820 key = 'increase_delay'
821 zone[key] = z.setdefault(key, 0)
822
823 # 'decrease_interval' is optional (use 0 by default)
824 key = 'decrease_interval'
825 zone[key] = z.setdefault(key, 0)
826
Gunnar Mills67e95512017-06-02 14:35:18 -0500827 # 'cooling_profiles' is optional (use 'all' instead)
828 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500829 (z['cooling_profiles'] is None):
830 profiles = ["all"]
831 else:
832 profiles = z['cooling_profiles']
833
Matthew Barth7883f582019-02-14 14:24:46 -0600834 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600835 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600836 if ('interfaces' in z) and \
837 (z['interfaces'] is not None):
838 ifaces = getIfacesInZone(z['interfaces'])
839
Matt Spinler78498c92017-04-11 13:59:46 -0500840 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500841 events = getEventsInZone(z['zone'], group['zone_conditions'],
842 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500843
844 if len(fans) == 0:
845 sys.exit("Didn't find any fans in zone " + str(zone['num']))
846
Matthew Barth7883f582019-02-14 14:24:46 -0600847 if (ifaces):
848 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500849 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500850 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500851 zones.append(zone)
852
853 zone_group['zones'] = zones
854 zone_groups.append(zone_group)
855
856 return zone_groups
857
858
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500859if __name__ == '__main__':
860 parser = ArgumentParser(
861 description="Phosphor fan zone definition parser")
862
863 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
864 default="example/zones.yaml",
865 help='fan zone definitional yaml')
866 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
867 default="example/fans.yaml",
868 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500869 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
870 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500871 parser.add_argument('-c', '--zone_conditions_yaml',
872 dest='zone_conditions_yaml',
873 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500874 parser.add_argument('-o', '--output_dir', dest='output_dir',
875 default=".",
876 help='output directory')
877 args = parser.parse_args()
878
879 if not args.zone_yaml or not args.fan_yaml:
880 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700881 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500882
883 with open(args.zone_yaml, 'r') as zone_input:
884 zone_data = yaml.safe_load(zone_input) or {}
885
886 with open(args.fan_yaml, 'r') as fan_input:
887 fan_data = yaml.safe_load(fan_input) or {}
888
Matthew Barthd4d0f082017-05-16 13:51:10 -0500889 events_data = {}
890 if args.events_yaml:
891 with open(args.events_yaml, 'r') as events_input:
892 events_data = yaml.safe_load(events_input) or {}
893
Gunnar Millsee8a2812017-06-02 14:26:47 -0500894 zone_conditions_data = {}
895 if args.zone_conditions_yaml:
896 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
897 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
898
Matt Spinleree7f6422017-05-09 11:03:14 -0500899 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500900 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500901
902 manager_config = zone_data.get('manager_configuration', {})
903
904 if manager_config.get('power_on_delay') is None:
905 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500906
Matthew Barth702c4a52018-02-28 16:23:11 -0600907 tmpls_dir = os.path.join(
908 os.path.dirname(os.path.realpath(__file__)),
909 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500910 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600911 if sys.version_info < (3, 0):
912 lkup = TemplateLookup(
913 directories=tmpls_dir.split(),
914 disable_unicode=True)
915 else:
916 lkup = TemplateLookup(
917 directories=tmpls_dir.split())
918 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500919 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600920 output.write(tmpl.render(zones=zone_config,
921 mgr_data=manager_config))