blob: dc28d1ab1ff8338aad147fa76742773107c66253 [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 Barth8a697b62018-12-14 13:23:47 -0600160 if len(a['parameters']) != 0:
161 e += "make_action(action::" + a['name'] + "(\n"
162 else:
163 e += "make_action(action::" + a['name'] + "\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600164 for i, p in enumerate(a['parameters']):
165 if (i+1) != len(a['parameters']):
166 e += p + ",\n"
167 else:
168 e += p + "\n"
Matthew Barth8a697b62018-12-14 13:23:47 -0600169 if len(a['parameters']) != 0:
170 e += ")),\n"
171 else:
172 e += "),\n"
Matthew Barth06fa7812018-11-20 09:54:30 -0600173 e += "}},\n"
174 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600175
Matthew Barth1b4de262018-03-06 13:03:16 -0600176 e += "std::vector<Trigger>{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600177 if ('timer' in event['triggers']) and \
178 (event['triggers']['timer'] is not None):
Matthew Barth1b4de262018-03-06 13:03:16 -0600179 e += "\tmake_trigger(trigger::timer(TimerConf{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600180 e += "\t" + event['triggers']['timer']['interval'] + ",\n"
181 e += "\t" + event['triggers']['timer']['type'] + "\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600182 e += "\t})),\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600183
Matthew Barth016bd242018-03-07 16:06:06 -0600184 if ('signals' in event['triggers']) and \
185 (event['triggers']['signals'] is not None):
186 for s in event['triggers']['signals']:
187 e += "\tmake_trigger(trigger::signal(\n"
188 e += "match::" + s['match'] + "(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500189 for i, mp in enumerate(s['mparams']['params']):
190 if (i+1) != len(s['mparams']['params']):
191 e += "\t\t\t" + s['mparams'][mp] + ",\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600192 else:
Matthew Barth926df662018-10-09 09:51:12 -0500193 e += "\t\t\t" + s['mparams'][mp] + "\n"
194 e += "\t\t),\n"
195 e += "\t\tmake_handler<SignalHandler>(\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600196 if ('type' in s['sparams']) and (s['sparams']['type'] is not None):
197 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600198 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600199 e += s['signal'] + "(\n"
200 for sp in s['sparams']['params']:
201 e += s['sparams'][sp] + ",\n"
202 if ('type' in s['hparams']) and (s['hparams']['type'] is not None):
203 e += ("handler::" + s['handler'] +
204 "<" + s['hparams']['type'] + ">(\n")
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600205 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600206 e += "handler::" + s['handler'] + "(\n)"
207 for i, hp in enumerate(s['hparams']['params']):
208 if (i+1) != len(s['hparams']['params']):
209 e += s['hparams'][hp] + ",\n"
210 else:
211 e += s['hparams'][hp] + "\n"
212 e += "))\n"
Matthew Barth926df662018-10-09 09:51:12 -0500213 e += "\t\t)\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600214 e += "\t)),\n"
215
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600216 if ('init' in event['triggers']):
217 for i in event['triggers']['init']:
218 e += "\tmake_trigger(trigger::init(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500219 if ('method' in i):
220 e += "\t\tmake_handler<MethodHandler>(\n"
221 if ('type' in i['mparams']) and \
222 (i['mparams']['type'] is not None):
223 e += i['method'] + "<" + i['mparams']['type'] + ">(\n"
224 else:
225 e += i['method'] + "(\n"
226 for ip in i['mparams']['params']:
227 e += i['mparams'][ip] + ",\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600228 if ('type' in i['hparams']) and \
229 (i['hparams']['type'] is not None):
230 e += ("handler::" + i['handler'] +
231 "<" + i['hparams']['type'] + ">(\n")
232 else:
233 e += "handler::" + i['handler'] + "(\n)"
234 for i, hp in enumerate(i['hparams']['params']):
235 if (i+1) != len(i['hparams']['params']):
236 e += i['hparams'][hp] + ",\n"
237 else:
238 e += i['hparams'][hp] + "\n"
Matthew Barth926df662018-10-09 09:51:12 -0500239 e += "))\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600240 e += "\t\t)\n"
241 e += "\t)),\n"
242
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600243 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600244
245 e += "}"
246
247 return e
248
249
Matthew Barth6c050692017-12-05 15:30:09 -0600250def getGroups(zNum, zCond, edata, events):
251 """
252 Extract and construct the groups for the given event.
253 """
254 groups = []
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600255 if ('groups' in edata) and (edata['groups'] is not None):
256 for eGroups in edata['groups']:
257 if ('zone_conditions' in eGroups) and \
258 (eGroups['zone_conditions'] is not None):
259 # Zone conditions are optional in the events yaml but skip
260 # if this event's condition is not in this zone's conditions
261 if all('name' in z and z['name'] is not None and
262 not any(c['name'] == z['name'] for c in zCond)
263 for z in eGroups['zone_conditions']):
264 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600265
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600266 # Zone numbers are optional in the events yaml but skip if this
267 # zone's zone number is not in the event's zone numbers
268 if all('zones' in z and z['zones'] is not None and
269 zNum not in z['zones']
270 for z in eGroups['zone_conditions']):
271 continue
272 eGroup = next(g for g in events['groups']
273 if g['name'] == eGroups['name'])
Matthew Barth6c050692017-12-05 15:30:09 -0600274
Matthew Barthf7e1cb32018-11-19 11:19:09 -0600275 group = {}
276 members = []
277 group['name'] = eGroup['name']
278 for m in eGroup['members']:
279 member = {}
280 member['path'] = eGroup['type']
281 member['object'] = (eGroup['type'] + m)
282 member['interface'] = eGroups['interface']
283 member['property'] = eGroups['property']['name']
284 member['type'] = eGroups['property']['type']
285 # Use defined service to note member on zone object
286 if ('service' in eGroup) and \
287 (eGroup['service'] is not None):
288 member['service'] = eGroup['service']
289 # Add expected group member's property value if given
290 if ('value' in eGroups['property']) and \
291 (eGroups['property']['value'] is not None):
292 if isinstance(eGroups['property']['value'], str) or \
293 "string" in str(member['type']).lower():
294 member['value'] = (
295 "\"" + eGroups['property']['value'] + "\"")
296 else:
297 member['value'] = eGroups['property']['value']
298 members.append(member)
299 group['members'] = members
300 groups.append(group)
Matthew Barth6c050692017-12-05 15:30:09 -0600301 return groups
302
303
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600304def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500305 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600306 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500307 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600308 params = {}
309 if ('parameters' in section) and \
310 (section['parameters'] is not None):
311 plist = []
312 for sp in section['parameters']:
313 p = str(sp)
314 if (p != 'type'):
315 plist.append(p)
316 if (p != 'group'):
317 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500318 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600319 params[p] = "Group\n{\n"
320 for g in groups:
321 for m in g['members']:
322 params[p] += (
323 "{\"" + str(m['object']) + "\",\n" +
324 "\"" + str(m['interface']) + "\",\n" +
325 "\"" + str(m['property']) + "\"},\n")
326 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500327 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600328 params[p] = member[p]
329 params['params'] = plist
330 else:
331 params['params'] = []
332 return params
333
334
335def getInit(eGrps, eTrig, events):
336 """
337 Extracts and constructs an init trigger for the event's groups
338 which are required to be of the same type.
339 """
340 method = {}
341 methods = []
Matthew Barth03774012018-10-26 13:25:43 -0500342 if (len(eGrps) > 0):
343 # Use the first group member for retrieving the type
344 member = eGrps[0]['members'][0]
345 if ('method' in eTrig) and \
346 (eTrig['method'] is not None):
347 # Add method parameters
348 eMethod = next(m for m in events['methods']
349 if m['name'] == eTrig['method'])
350 method['method'] = eMethod['name']
351 method['mparams'] = getParameters(
352 member, eGrps, eMethod, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600353
Matthew Barth03774012018-10-26 13:25:43 -0500354 # Add handler parameters
355 eHandler = next(h for h in events['handlers']
356 if h['name'] == eTrig['handler'])
357 method['handler'] = eHandler['name']
358 method['hparams'] = getParameters(
359 member, eGrps, eHandler, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600360
361 methods.append(method)
362
363 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500364
365
Matthew Bartha69465a2018-03-02 13:50:59 -0600366def getSignal(eGrps, eTrig, events):
367 """
368 Extracts and constructs for each group member a signal
369 subscription of each match listed in the trigger.
370 """
371 signals = []
372 for group in eGrps:
373 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600374 signal = {}
375 # Add signal parameters
376 eSignal = next(s for s in events['signals']
377 if s['name'] == eTrig['signal'])
378 signal['signal'] = eSignal['name']
379 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
380
381 # If service not given, subscribe to signal match
382 if ('service' not in member):
383 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600384 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600385 if m['name'] == eSignal['match'])
386 signal['match'] = eMatch['name']
387 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600388
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600389 # Add handler parameters
390 eHandler = next(h for h in events['handlers']
391 if h['name'] == eTrig['handler'])
392 signal['handler'] = eHandler['name']
393 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500394
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600395 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500396
Matthew Bartha69465a2018-03-02 13:50:59 -0600397 return signals
398
399
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600400def getTimer(eTrig):
401 """
402 Extracts and constructs the required parameters for an
403 event timer.
404 """
405 timer = {}
406 timer['interval'] = (
407 "static_cast<std::chrono::microseconds>" +
408 "(" + str(eTrig['interval']) + ")")
409 timer['type'] = "TimerType::" + str(eTrig['type'])
410 return timer
411
412
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600413def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500414 """
415 Extracts and constructs the make_action function call for
416 all the actions within the given event.
417 """
418 action = []
419 for eActions in actions['actions']:
420 actions = {}
421 eAction = next(a for a in events['actions']
422 if a['name'] == eActions['name'])
423 actions['name'] = eAction['name']
Matthew Barth06fa7812018-11-20 09:54:30 -0600424 actions['groups'] = getGroups(zNum, zCond, eActions, events)
Matthew Barth9df74752017-10-11 14:39:31 -0500425 params = []
426 if ('parameters' in eAction) and \
427 (eAction['parameters'] is not None):
428 for p in eAction['parameters']:
429 param = "static_cast<"
430 if type(eActions[p]) is not dict:
431 if p == 'actions':
432 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600433 pActs = getActions(zNum,
434 zCond,
435 edata,
436 eActions,
437 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500438 for a in pActs:
439 if (len(a['parameters']) != 0):
440 param += (
441 "make_action(action::" +
442 a['name'] +
443 "(\n")
444 for i, ap in enumerate(a['parameters']):
445 if (i+1) != len(a['parameters']):
446 param += (ap + ",")
447 else:
448 param += (ap + ")")
449 else:
450 param += ("make_action(action::" + a['name'])
451 param += "),"
452 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600453 elif p == 'defevents' or p == 'altevents':
454 param = "std::vector<SetSpeedEvent>{\n"
455 for i, e in enumerate(eActions[p]):
456 aEvent = getEvent(zNum, zCond, e, events)
457 if not aEvent:
458 continue
459 if (i+1) != len(eActions[p]):
460 param += genEvent(aEvent) + ",\n"
461 else:
462 param += genEvent(aEvent) + "\n"
463 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500464 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600465 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600466 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600467 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600468 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600469 ">(\"" + str(eActions[p]) + "\")")
470 else:
471 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600472 str(eActions[p]['type']).lower() +
473 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500474 else:
475 # Default type to 'size_t' when not given
476 param += ("size_t>(" + str(eActions[p]).lower() + ")")
477 else:
478 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600479 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500480 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600481 "TimerConf{" + t['interval'] + "," +
482 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500483 else:
484 param += (str(eActions[p]['type']).lower() + ">(")
485 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600486 if isinstance(eActions[p]['value'], str) or \
487 "string" in str(eActions[p]['type']).lower():
488 param += \
489 "\"" + str(eActions[p]['value']) + "\")"
490 else:
491 param += \
492 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500493 else:
494 param += (
495 str(eActions[p]['type']).lower() +
496 convertToMap(str(eActions[p]['value'])) + ")")
497 params.append(param)
498 actions['parameters'] = params
499 action.append(actions)
500 return action
501
502
Matthew Barth7f272fd2017-09-12 16:16:56 -0500503def getEvent(zone_num, zone_conditions, e, events_data):
504 """
505 Parses the sections of an event and populates the properties
506 that construct an event within the generated source.
507 """
508 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500509
Matthew Barth621a5772018-11-14 14:55:11 -0600510 # Add set speed event name
511 event['name'] = e['name']
512
Matthew Barth6c050692017-12-05 15:30:09 -0600513 # Add set speed event groups
Matthew Barth03774012018-10-26 13:25:43 -0500514 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500515
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600516 # Add optional set speed actions and function parameters
517 event['action'] = []
518 if ('actions' in e) and \
519 (e['actions'] is not None):
Matthew Barth06fa7812018-11-20 09:54:30 -0600520 # List of dicts containing the list of groups and list of actions
521 sseActions = []
522 eActions = getActions(zone_num, zone_conditions, e, e, events_data)
523 for eAction in eActions:
524 # Skip events that have no groups defined for the event or actions
525 if not event['groups'] and not eAction['groups']:
526 continue
527 # Find group in sseActions
528 grpExists = False
529 for sseDict in sseActions:
530 if eAction['groups'] == sseDict['groups']:
531 # Extend 'actions' list
532 del eAction['groups']
533 sseDict['actions'].append(eAction)
534 grpExists = True
535 break
536 if not grpExists:
537 grps = eAction['groups']
538 del eAction['groups']
539 actList = []
540 actList.append(eAction)
Matthew Bartha6f75162018-11-20 13:50:42 -0600541 sseActions.append({'groups': grps, 'actions': actList})
Matthew Barth06fa7812018-11-20 09:54:30 -0600542 event['action'] = sseActions
Matthew Barth7f272fd2017-09-12 16:16:56 -0500543
Matthew Bartha69465a2018-03-02 13:50:59 -0600544 # Add event triggers
545 event['triggers'] = {}
546 for trig in e['triggers']:
547 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600548 if (trig['name'] == "timer"):
549 event['triggers']['timer'] = getTimer(trig)
550 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600551 if ('signals' not in event['triggers']):
552 event['triggers']['signals'] = []
553 triggers = getSignal(event['groups'], trig, events_data)
554 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600555 elif (trig['name'] == "init"):
556 triggers = getInit(event['groups'], trig, events_data)
557 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500558
Matthew Barth7f272fd2017-09-12 16:16:56 -0500559 return event
560
561
562def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500563 """
564 Parses the precondition section of an event and populates the necessary
565 structures to generate a precondition for a set speed event.
566 """
567 precond = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600568
569 # Add set speed event precondition name
570 precond['pcname'] = event['name']
571
Matthew Barth9af190c2017-08-08 14:20:43 -0500572 # Add set speed event precondition group
Matthew Barth03774012018-10-26 13:25:43 -0500573 precond['pcgrps'] = getGroups(zNum,
574 zCond,
575 event['precondition'],
576 events_data)
Matthew Barth9af190c2017-08-08 14:20:43 -0500577
Matthew Barth7f272fd2017-09-12 16:16:56 -0500578 # Add set speed event precondition actions
579 pc = []
580 pcs = {}
581 pcs['name'] = event['precondition']['name']
582 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500583 if p['name'] == event['precondition']['name'])
584 params = []
Matthew Barth8a697b62018-12-14 13:23:47 -0600585 for p in epc['parameters'] or []:
Matthew Barth9af190c2017-08-08 14:20:43 -0500586 param = {}
587 if p == 'groups':
588 param['type'] = "std::vector<PrecondGroup>"
589 param['open'] = "{"
590 param['close'] = "}"
591 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600592 for group in precond['pcgrps']:
593 for pcgrp in group['members']:
594 value = {}
595 value['value'] = (
596 "PrecondGroup{\"" +
597 str(pcgrp['object']) + "\",\"" +
598 str(pcgrp['interface']) + "\",\"" +
599 str(pcgrp['property']) + "\"," +
600 "static_cast<" +
601 str(pcgrp['type']).lower() + ">")
602 if isinstance(pcgrp['value'], str) or \
603 "string" in str(pcgrp['type']).lower():
604 value['value'] += ("(" + str(pcgrp['value']) + ")}")
605 else:
606 value['value'] += \
607 ("(" + str(pcgrp['value']).lower() + ")}")
608 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500609 param['values'] = values
610 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500611 pcs['params'] = params
612 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500613 precond['pcact'] = pc
614
Matthew Barth7f272fd2017-09-12 16:16:56 -0500615 pcevents = []
616 for pce in event['precondition']['events']:
617 pcevent = getEvent(zNum, zCond, pce, events_data)
618 if not pcevent:
619 continue
620 pcevents.append(pcevent)
621 precond['pcevts'] = pcevents
622
Matthew Barthf20c3212018-03-02 14:42:55 -0600623 # Add precondition event triggers
624 precond['triggers'] = {}
625 for trig in event['precondition']['triggers']:
626 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600627 if (trig['name'] == "timer"):
628 precond['triggers']['pctime'] = getTimer(trig)
629 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600630 if ('pcsigs' not in precond['triggers']):
631 precond['triggers']['pcsigs'] = []
632 triggers = getSignal(precond['pcgrps'], trig, events_data)
633 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600634 elif (trig['name'] == "init"):
635 triggers = getInit(precond['pcgrps'], trig, events_data)
636 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500637
638 return precond
639
640
Gunnar Millsb751f322017-06-06 15:14:11 -0500641def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500642 """
643 Constructs the event entries defined for each zone using the events yaml
644 provided.
645 """
646 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500647
Matthew Barthd4d0f082017-05-16 13:51:10 -0500648 if 'events' in events_data:
649 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500650 event = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600651
Matthew Barth9af190c2017-08-08 14:20:43 -0500652 # Add precondition if given
653 if ('precondition' in e) and \
654 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500655 event['pc'] = addPrecondition(zone_num,
656 zone_conditions,
657 e,
658 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500659 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500660 event = getEvent(zone_num, zone_conditions, e, events_data)
Matthew Bartha6f75162018-11-20 13:50:42 -0600661 # Remove empty events and events that have
662 # no groups defined for the event or any of the actions
663 if not event or \
664 (not event['groups'] and
665 all(not a['groups'] for a in event['action'])):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500666 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500667 events.append(event)
668
669 return events
670
671
Matt Spinler78498c92017-04-11 13:59:46 -0500672def getFansInZone(zone_num, profiles, fan_data):
673 """
674 Parses the fan definition YAML files to find the fans
675 that match both the zone passed in and one of the
676 cooling profiles.
677 """
678
679 fans = []
680
681 for f in fan_data['fans']:
682
683 if zone_num != f['cooling_zone']:
684 continue
685
Gunnar Mills67e95512017-06-02 14:35:18 -0500686 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500687 if f.get('cooling_profile') is None:
688 profile = "all"
689 else:
690 profile = f['cooling_profile']
691
692 if profile not in profiles:
693 continue
694
695 fan = {}
696 fan['name'] = f['inventory']
697 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800698 fan['target_interface'] = f.get(
699 'target_interface',
700 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500701 fans.append(fan)
702
703 return fans
704
705
Matthew Barth7883f582019-02-14 14:24:46 -0600706def getIfacesInZone(zone_ifaces):
707 """
708 Parse given interfaces for a zone for associating a zone with an interface
709 and set any properties listed to defined values upon fan control starting
710 on the zone.
711 """
712
713 ifaces = []
714 for i in zone_ifaces:
715 iface = {}
716 # Interface name not needed yet for fan zones but
717 # may be necessary as more interfaces are extended by the zones
718 iface['name'] = i['name']
719
720 if ('properties' in i) and \
721 (i['properties'] is not None):
722 props = []
723 for p in i['properties']:
724 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600725 prop['name'] = p['name']
726 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600727 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600728 if ('persist' in p):
729 persist = p['persist']
730 if (persist is not None):
731 if (isinstance(persist, bool)):
732 prop['persist'] = 'true' if persist else 'false'
733 else:
734 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600735 vals = []
736 for v in p['values']:
737 val = v['value']
738 if (val is not None):
739 if (isinstance(val, bool)):
740 # Convert True/False to 'true'/'false'
741 val = 'true' if val else 'false'
742 elif (isinstance(val, str)):
743 # Wrap strings with double-quotes
744 val = "\"" + val + "\""
745 vals.append(val)
746 prop['values'] = vals
747 props.append(prop)
748 iface['props'] = props
749 ifaces.append(iface)
750
751 return ifaces
752
753
Gunnar Millsee8a2812017-06-02 14:26:47 -0500754def getConditionInZoneConditions(zone_condition, zone_conditions_data):
755 """
756 Parses the zone conditions definition YAML files to find the condition
757 that match both the zone condition passed in.
758 """
759
760 condition = {}
761
762 for c in zone_conditions_data['conditions']:
763
764 if zone_condition != c['name']:
765 continue
766 condition['type'] = c['type']
767 properties = []
768 for p in c['properties']:
769 property = {}
770 property['property'] = p['property']
771 property['interface'] = p['interface']
772 property['path'] = p['path']
773 property['type'] = p['type'].lower()
774 property['value'] = str(p['value']).lower()
775 properties.append(property)
776 condition['properties'] = properties
777
778 return condition
779
780
781def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500782 """
783 Combines the zone definition YAML and fan
784 definition YAML to create a data structure defining
785 the fan cooling zones.
786 """
787
788 zone_groups = []
789
790 for group in zone_data:
791 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500792 # zone conditions are optional
793 if 'zone_conditions' in group and group['zone_conditions'] is not None:
794 for c in group['zone_conditions']:
795
796 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500797 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500798 "zone_conditions used in zone YAML")
799
800 condition = getConditionInZoneConditions(c['name'],
801 zone_conditions_data)
802
803 if not condition:
804 sys.exit("Missing zone condition " + c['name'])
805
806 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500807
808 zone_group = {}
809 zone_group['conditions'] = conditions
810
811 zones = []
812 for z in group['zones']:
813 zone = {}
814
Gunnar Mills67e95512017-06-02 14:35:18 -0500815 # 'zone' is required
816 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500817 sys.exit("Missing fan zone number in " + zone_yaml)
818
819 zone['num'] = z['zone']
820
821 zone['full_speed'] = z['full_speed']
822
Matthew Barth1de66622017-06-12 13:13:02 -0500823 zone['default_floor'] = z['default_floor']
824
Matthew Bartha9561842017-06-29 11:43:45 -0500825 # 'increase_delay' is optional (use 0 by default)
826 key = 'increase_delay'
827 zone[key] = z.setdefault(key, 0)
828
829 # 'decrease_interval' is optional (use 0 by default)
830 key = 'decrease_interval'
831 zone[key] = z.setdefault(key, 0)
832
Gunnar Mills67e95512017-06-02 14:35:18 -0500833 # 'cooling_profiles' is optional (use 'all' instead)
834 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500835 (z['cooling_profiles'] is None):
836 profiles = ["all"]
837 else:
838 profiles = z['cooling_profiles']
839
Matthew Barth7883f582019-02-14 14:24:46 -0600840 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600841 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600842 if ('interfaces' in z) and \
843 (z['interfaces'] is not None):
844 ifaces = getIfacesInZone(z['interfaces'])
845
Matt Spinler78498c92017-04-11 13:59:46 -0500846 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500847 events = getEventsInZone(z['zone'], group['zone_conditions'],
848 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500849
850 if len(fans) == 0:
851 sys.exit("Didn't find any fans in zone " + str(zone['num']))
852
Matthew Barth7883f582019-02-14 14:24:46 -0600853 if (ifaces):
854 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500855 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500856 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500857 zones.append(zone)
858
859 zone_group['zones'] = zones
860 zone_groups.append(zone_group)
861
862 return zone_groups
863
864
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500865if __name__ == '__main__':
866 parser = ArgumentParser(
867 description="Phosphor fan zone definition parser")
868
869 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
870 default="example/zones.yaml",
871 help='fan zone definitional yaml')
872 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
873 default="example/fans.yaml",
874 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500875 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
876 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500877 parser.add_argument('-c', '--zone_conditions_yaml',
878 dest='zone_conditions_yaml',
879 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500880 parser.add_argument('-o', '--output_dir', dest='output_dir',
881 default=".",
882 help='output directory')
883 args = parser.parse_args()
884
885 if not args.zone_yaml or not args.fan_yaml:
886 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700887 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500888
889 with open(args.zone_yaml, 'r') as zone_input:
890 zone_data = yaml.safe_load(zone_input) or {}
891
892 with open(args.fan_yaml, 'r') as fan_input:
893 fan_data = yaml.safe_load(fan_input) or {}
894
Matthew Barthd4d0f082017-05-16 13:51:10 -0500895 events_data = {}
896 if args.events_yaml:
897 with open(args.events_yaml, 'r') as events_input:
898 events_data = yaml.safe_load(events_input) or {}
899
Gunnar Millsee8a2812017-06-02 14:26:47 -0500900 zone_conditions_data = {}
901 if args.zone_conditions_yaml:
902 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
903 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
904
Matt Spinleree7f6422017-05-09 11:03:14 -0500905 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500906 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500907
908 manager_config = zone_data.get('manager_configuration', {})
909
910 if manager_config.get('power_on_delay') is None:
911 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500912
Matthew Barth702c4a52018-02-28 16:23:11 -0600913 tmpls_dir = os.path.join(
914 os.path.dirname(os.path.realpath(__file__)),
915 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500916 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600917 if sys.version_info < (3, 0):
918 lkup = TemplateLookup(
919 directories=tmpls_dir.split(),
920 disable_unicode=True)
921 else:
922 lkup = TemplateLookup(
923 directories=tmpls_dir.split())
924 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500925 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600926 output.write(tmpl.render(zones=zone_config,
927 mgr_data=manager_config))