blob: 2cc93fcdc00927d42d08f5d26dd8c0174289e55c [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)
535 sseActions.append({'groups':grps, 'actions':actList})
536 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)
655 if not event:
656 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500657 events.append(event)
658
659 return events
660
661
Matt Spinler78498c92017-04-11 13:59:46 -0500662def getFansInZone(zone_num, profiles, fan_data):
663 """
664 Parses the fan definition YAML files to find the fans
665 that match both the zone passed in and one of the
666 cooling profiles.
667 """
668
669 fans = []
670
671 for f in fan_data['fans']:
672
673 if zone_num != f['cooling_zone']:
674 continue
675
Gunnar Mills67e95512017-06-02 14:35:18 -0500676 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500677 if f.get('cooling_profile') is None:
678 profile = "all"
679 else:
680 profile = f['cooling_profile']
681
682 if profile not in profiles:
683 continue
684
685 fan = {}
686 fan['name'] = f['inventory']
687 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800688 fan['target_interface'] = f.get(
689 'target_interface',
690 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500691 fans.append(fan)
692
693 return fans
694
695
Matthew Barth7883f582019-02-14 14:24:46 -0600696def getIfacesInZone(zone_ifaces):
697 """
698 Parse given interfaces for a zone for associating a zone with an interface
699 and set any properties listed to defined values upon fan control starting
700 on the zone.
701 """
702
703 ifaces = []
704 for i in zone_ifaces:
705 iface = {}
706 # Interface name not needed yet for fan zones but
707 # may be necessary as more interfaces are extended by the zones
708 iface['name'] = i['name']
709
710 if ('properties' in i) and \
711 (i['properties'] is not None):
712 props = []
713 for p in i['properties']:
714 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600715 prop['name'] = p['name']
716 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600717 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600718 if ('persist' in p):
719 persist = p['persist']
720 if (persist is not None):
721 if (isinstance(persist, bool)):
722 prop['persist'] = 'true' if persist else 'false'
723 else:
724 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600725 vals = []
726 for v in p['values']:
727 val = v['value']
728 if (val is not None):
729 if (isinstance(val, bool)):
730 # Convert True/False to 'true'/'false'
731 val = 'true' if val else 'false'
732 elif (isinstance(val, str)):
733 # Wrap strings with double-quotes
734 val = "\"" + val + "\""
735 vals.append(val)
736 prop['values'] = vals
737 props.append(prop)
738 iface['props'] = props
739 ifaces.append(iface)
740
741 return ifaces
742
743
Gunnar Millsee8a2812017-06-02 14:26:47 -0500744def getConditionInZoneConditions(zone_condition, zone_conditions_data):
745 """
746 Parses the zone conditions definition YAML files to find the condition
747 that match both the zone condition passed in.
748 """
749
750 condition = {}
751
752 for c in zone_conditions_data['conditions']:
753
754 if zone_condition != c['name']:
755 continue
756 condition['type'] = c['type']
757 properties = []
758 for p in c['properties']:
759 property = {}
760 property['property'] = p['property']
761 property['interface'] = p['interface']
762 property['path'] = p['path']
763 property['type'] = p['type'].lower()
764 property['value'] = str(p['value']).lower()
765 properties.append(property)
766 condition['properties'] = properties
767
768 return condition
769
770
771def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500772 """
773 Combines the zone definition YAML and fan
774 definition YAML to create a data structure defining
775 the fan cooling zones.
776 """
777
778 zone_groups = []
779
780 for group in zone_data:
781 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500782 # zone conditions are optional
783 if 'zone_conditions' in group and group['zone_conditions'] is not None:
784 for c in group['zone_conditions']:
785
786 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500787 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500788 "zone_conditions used in zone YAML")
789
790 condition = getConditionInZoneConditions(c['name'],
791 zone_conditions_data)
792
793 if not condition:
794 sys.exit("Missing zone condition " + c['name'])
795
796 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500797
798 zone_group = {}
799 zone_group['conditions'] = conditions
800
801 zones = []
802 for z in group['zones']:
803 zone = {}
804
Gunnar Mills67e95512017-06-02 14:35:18 -0500805 # 'zone' is required
806 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500807 sys.exit("Missing fan zone number in " + zone_yaml)
808
809 zone['num'] = z['zone']
810
811 zone['full_speed'] = z['full_speed']
812
Matthew Barth1de66622017-06-12 13:13:02 -0500813 zone['default_floor'] = z['default_floor']
814
Matthew Bartha9561842017-06-29 11:43:45 -0500815 # 'increase_delay' is optional (use 0 by default)
816 key = 'increase_delay'
817 zone[key] = z.setdefault(key, 0)
818
819 # 'decrease_interval' is optional (use 0 by default)
820 key = 'decrease_interval'
821 zone[key] = z.setdefault(key, 0)
822
Gunnar Mills67e95512017-06-02 14:35:18 -0500823 # 'cooling_profiles' is optional (use 'all' instead)
824 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500825 (z['cooling_profiles'] is None):
826 profiles = ["all"]
827 else:
828 profiles = z['cooling_profiles']
829
Matthew Barth7883f582019-02-14 14:24:46 -0600830 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600831 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600832 if ('interfaces' in z) and \
833 (z['interfaces'] is not None):
834 ifaces = getIfacesInZone(z['interfaces'])
835
Matt Spinler78498c92017-04-11 13:59:46 -0500836 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500837 events = getEventsInZone(z['zone'], group['zone_conditions'],
838 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500839
840 if len(fans) == 0:
841 sys.exit("Didn't find any fans in zone " + str(zone['num']))
842
Matthew Barth7883f582019-02-14 14:24:46 -0600843 if (ifaces):
844 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500845 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500846 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500847 zones.append(zone)
848
849 zone_group['zones'] = zones
850 zone_groups.append(zone_group)
851
852 return zone_groups
853
854
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500855if __name__ == '__main__':
856 parser = ArgumentParser(
857 description="Phosphor fan zone definition parser")
858
859 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
860 default="example/zones.yaml",
861 help='fan zone definitional yaml')
862 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
863 default="example/fans.yaml",
864 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500865 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
866 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500867 parser.add_argument('-c', '--zone_conditions_yaml',
868 dest='zone_conditions_yaml',
869 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500870 parser.add_argument('-o', '--output_dir', dest='output_dir',
871 default=".",
872 help='output directory')
873 args = parser.parse_args()
874
875 if not args.zone_yaml or not args.fan_yaml:
876 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700877 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500878
879 with open(args.zone_yaml, 'r') as zone_input:
880 zone_data = yaml.safe_load(zone_input) or {}
881
882 with open(args.fan_yaml, 'r') as fan_input:
883 fan_data = yaml.safe_load(fan_input) or {}
884
Matthew Barthd4d0f082017-05-16 13:51:10 -0500885 events_data = {}
886 if args.events_yaml:
887 with open(args.events_yaml, 'r') as events_input:
888 events_data = yaml.safe_load(events_input) or {}
889
Gunnar Millsee8a2812017-06-02 14:26:47 -0500890 zone_conditions_data = {}
891 if args.zone_conditions_yaml:
892 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
893 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
894
Matt Spinleree7f6422017-05-09 11:03:14 -0500895 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500896 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500897
898 manager_config = zone_data.get('manager_configuration', {})
899
900 if manager_config.get('power_on_delay') is None:
901 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500902
Matthew Barth702c4a52018-02-28 16:23:11 -0600903 tmpls_dir = os.path.join(
904 os.path.dirname(os.path.realpath(__file__)),
905 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500906 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600907 if sys.version_info < (3, 0):
908 lkup = TemplateLookup(
909 directories=tmpls_dir.split(),
910 disable_unicode=True)
911 else:
912 lkup = TemplateLookup(
913 directories=tmpls_dir.split())
914 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500915 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600916 output.write(tmpl.render(zones=zone_config,
917 mgr_data=manager_config))