blob: 994ad6b79366d9d74016e46c4565f7fd45b517ea [file] [log] [blame]
Matt Spinlerd08dbe22017-04-11 13:52:54 -05001#!/usr/bin/env python
2
3"""
4This script reads in fan definition and zone definition YAML
5files and generates a set of structures for use by the fan control code.
6"""
7
8import os
9import sys
10import yaml
11from argparse import ArgumentParser
12from mako.template import Template
Matthew Barth702c4a52018-02-28 16:23:11 -060013from mako.lookup import TemplateLookup
Matt Spinlerd08dbe22017-04-11 13:52:54 -050014
Matt Spinler78498c92017-04-11 13:59:46 -050015
Matthew Barth7883f582019-02-14 14:24:46 -060016def parse_cpp_type(typeName):
17 """
18 Take a list of dbus types from YAML and convert it to a recursive cpp
19 formed data structure. Each entry of the original list gets converted
20 into a tuple consisting of the type name and a list with the params
21 for this type,
22 e.g.
23 ['dict', ['string', 'dict', ['string', 'int64']]]
24 is converted to
25 [('dict', [('string', []), ('dict', [('string', []),
26 ('int64', [])]]]
27 """
28
29 if not typeName:
30 return None
31
32 # Type names are _almost_ valid YAML. Insert a , before each [
33 # and then wrap it in a [ ] and it becomes valid YAML (assuming
34 # the user gave us a valid typename).
35 typeArray = yaml.safe_load("[" + ",[".join(typeName.split("[")) + "]")
36 typeTuple = preprocess_yaml_type_array(typeArray).pop(0)
37 return get_cpp_type(typeTuple)
38
39
40def preprocess_yaml_type_array(typeArray):
41 """
42 Flattens an array type into a tuple list that can be used to get the
43 supported cpp type from each element.
44 """
45
46 result = []
47
48 for i in range(len(typeArray)):
49 # Ignore lists because we merge them with the previous element
50 if type(typeArray[i]) is list:
51 continue
52
53 # If there is a next element and it is a list, merge it with the
54 # current element.
55 if i < len(typeArray)-1 and type(typeArray[i+1]) is list:
56 result.append(
57 (typeArray[i],
58 preprocess_yaml_type_array(typeArray[i+1])))
59 else:
60 result.append((typeArray[i], []))
61
62 return result
63
64
65def get_cpp_type(typeTuple):
66 """
67 Take a list of dbus types and perform validity checking, such as:
68 [ variant [ dict [ int32, int32 ], double ] ]
69 This function then converts the type-list into a C++ type string.
70 """
71
72 propertyMap = {
73 'byte': {'cppName': 'uint8_t', 'params': 0},
74 'boolean': {'cppName': 'bool', 'params': 0},
75 'int16': {'cppName': 'int16_t', 'params': 0},
76 'uint16': {'cppName': 'uint16_t', 'params': 0},
77 'int32': {'cppName': 'int32_t', 'params': 0},
78 'uint32': {'cppName': 'uint32_t', 'params': 0},
79 'int64': {'cppName': 'int64_t', 'params': 0},
80 'uint64': {'cppName': 'uint64_t', 'params': 0},
81 'double': {'cppName': 'double', 'params': 0},
82 'string': {'cppName': 'std::string', 'params': 0},
83 'array': {'cppName': 'std::vector', 'params': 1},
84 'dict': {'cppName': 'std::map', 'params': 2}}
85
86 if len(typeTuple) != 2:
87 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
88
89 first = typeTuple[0]
90 entry = propertyMap[first]
91
92 result = entry['cppName']
93
94 # Handle 0-entry parameter lists.
95 if (entry['params'] == 0):
96 if (len(typeTuple[1]) != 0):
97 raise RuntimeError("Invalid typeTuple %s" % typeTuple)
98 else:
99 return result
100
101 # Get the parameter list
102 rest = typeTuple[1]
103
104 # Confirm parameter count matches.
105 if (entry['params'] != -1) and (entry['params'] != len(rest)):
106 raise RuntimeError("Invalid entry count for %s : %s" %
107 (first, rest))
108
109 # Parse each parameter entry, if appropriate, and create C++ template
110 # syntax.
111 result += '<'
112 if entry.get('noparse'):
113 # Do not parse the parameter list, just use the first element
114 # of each tuple and ignore possible parameters
115 result += ", ".join([e[0] for e in rest])
116 else:
117 result += ", ".join(map(lambda e: get_cpp_type(e),
118 rest))
119 result += '>'
120
121 return result
122
123
Matthew Barthbb12c922017-06-13 13:57:40 -0500124def convertToMap(listOfDict):
125 """
126 Converts a list of dictionary entries to a std::map initialization list.
127 """
Matthew Barth9a5b6992018-01-23 15:32:26 -0600128 listOfDict = listOfDict.replace('\'', '\"')
Matthew Barthbb12c922017-06-13 13:57:40 -0500129 listOfDict = listOfDict.replace('[', '{')
130 listOfDict = listOfDict.replace(']', '}')
131 listOfDict = listOfDict.replace(':', ',')
132 return listOfDict
133
134
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600135def genEvent(event):
136 """
137 Generates the source code of an event and returns it as a string
138 """
139 e = "SetSpeedEvent{\n"
Matthew Barth621a5772018-11-14 14:55:11 -0600140 e += "\"" + event['name'] + "\",\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600141 e += "Group{\n"
142 for group in event['groups']:
143 for member in group['members']:
144 e += "{\n"
145 e += "\"" + member['object'] + "\",\n"
Matthew Barth146b7392018-03-08 16:17:58 -0600146 e += "\"" + member['interface'] + "\",\n"
147 e += "\"" + member['property'] + "\"\n"
148 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600149 e += "},\n"
150
151 e += "std::vector<Action>{\n"
152 for a in event['action']:
153 if len(a['parameters']) != 0:
154 e += "make_action(action::" + a['name'] + "(\n"
155 else:
156 e += "make_action(action::" + a['name'] + "\n"
157 for i, p in enumerate(a['parameters']):
158 if (i+1) != len(a['parameters']):
159 e += p + ",\n"
160 else:
161 e += p + ")\n"
162 e += "),\n"
163 e += "},\n"
164
Matthew Barth1b4de262018-03-06 13:03:16 -0600165 e += "std::vector<Trigger>{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600166 if ('timer' in event['triggers']) and \
167 (event['triggers']['timer'] is not None):
Matthew Barth1b4de262018-03-06 13:03:16 -0600168 e += "\tmake_trigger(trigger::timer(TimerConf{\n"
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600169 e += "\t" + event['triggers']['timer']['interval'] + ",\n"
170 e += "\t" + event['triggers']['timer']['type'] + "\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600171 e += "\t})),\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600172
Matthew Barth016bd242018-03-07 16:06:06 -0600173 if ('signals' in event['triggers']) and \
174 (event['triggers']['signals'] is not None):
175 for s in event['triggers']['signals']:
176 e += "\tmake_trigger(trigger::signal(\n"
177 e += "match::" + s['match'] + "(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500178 for i, mp in enumerate(s['mparams']['params']):
179 if (i+1) != len(s['mparams']['params']):
180 e += "\t\t\t" + s['mparams'][mp] + ",\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600181 else:
Matthew Barth926df662018-10-09 09:51:12 -0500182 e += "\t\t\t" + s['mparams'][mp] + "\n"
183 e += "\t\t),\n"
184 e += "\t\tmake_handler<SignalHandler>(\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600185 if ('type' in s['sparams']) and (s['sparams']['type'] is not None):
186 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600187 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600188 e += s['signal'] + "(\n"
189 for sp in s['sparams']['params']:
190 e += s['sparams'][sp] + ",\n"
191 if ('type' in s['hparams']) and (s['hparams']['type'] is not None):
192 e += ("handler::" + s['handler'] +
193 "<" + s['hparams']['type'] + ">(\n")
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600194 else:
Matthew Barth016bd242018-03-07 16:06:06 -0600195 e += "handler::" + s['handler'] + "(\n)"
196 for i, hp in enumerate(s['hparams']['params']):
197 if (i+1) != len(s['hparams']['params']):
198 e += s['hparams'][hp] + ",\n"
199 else:
200 e += s['hparams'][hp] + "\n"
201 e += "))\n"
Matthew Barth926df662018-10-09 09:51:12 -0500202 e += "\t\t)\n"
Matthew Barth016bd242018-03-07 16:06:06 -0600203 e += "\t)),\n"
204
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600205 if ('init' in event['triggers']):
206 for i in event['triggers']['init']:
207 e += "\tmake_trigger(trigger::init(\n"
Matthew Barth926df662018-10-09 09:51:12 -0500208 if ('method' in i):
209 e += "\t\tmake_handler<MethodHandler>(\n"
210 if ('type' in i['mparams']) and \
211 (i['mparams']['type'] is not None):
212 e += i['method'] + "<" + i['mparams']['type'] + ">(\n"
213 else:
214 e += i['method'] + "(\n"
215 for ip in i['mparams']['params']:
216 e += i['mparams'][ip] + ",\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600217 if ('type' in i['hparams']) and \
218 (i['hparams']['type'] is not None):
219 e += ("handler::" + i['handler'] +
220 "<" + i['hparams']['type'] + ">(\n")
221 else:
222 e += "handler::" + i['handler'] + "(\n)"
223 for i, hp in enumerate(i['hparams']['params']):
224 if (i+1) != len(i['hparams']['params']):
225 e += i['hparams'][hp] + ",\n"
226 else:
227 e += i['hparams'][hp] + "\n"
Matthew Barth926df662018-10-09 09:51:12 -0500228 e += "))\n"
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600229 e += "\t\t)\n"
230 e += "\t)),\n"
231
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600232 e += "},\n"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600233
234 e += "}"
235
236 return e
237
238
Matthew Barth6c050692017-12-05 15:30:09 -0600239def getGroups(zNum, zCond, edata, events):
240 """
241 Extract and construct the groups for the given event.
242 """
243 groups = []
244 for eGroups in edata['groups']:
245 if ('zone_conditions' in eGroups) and \
246 (eGroups['zone_conditions'] is not None):
247 # Zone conditions are optional in the events yaml but skip
248 # if this event's condition is not in this zone's conditions
249 if all('name' in z and z['name'] is not None and
250 not any(c['name'] == z['name'] for c in zCond)
251 for z in eGroups['zone_conditions']):
252 continue
253
254 # Zone numbers are optional in the events yaml but skip if this
255 # zone's zone number is not in the event's zone numbers
256 if all('zones' in z and z['zones'] is not None and
257 zNum not in z['zones']
258 for z in eGroups['zone_conditions']):
259 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600260 eGroup = next(g for g in events['groups']
261 if g['name'] == eGroups['name'])
262
263 group = {}
264 members = []
265 group['name'] = eGroup['name']
266 for m in eGroup['members']:
267 member = {}
268 member['path'] = eGroup['type']
269 member['object'] = (eGroup['type'] + m)
270 member['interface'] = eGroups['interface']
271 member['property'] = eGroups['property']['name']
272 member['type'] = eGroups['property']['type']
Matthew Barth18c91032019-01-29 15:36:00 -0600273 # Use defined service to note member on zone object
274 if ('service' in eGroup) and \
275 (eGroup['service'] is not None):
276 member['service'] = eGroup['service']
Matthew Barth6c050692017-12-05 15:30:09 -0600277 # Add expected group member's property value if given
278 if ('value' in eGroups['property']) and \
279 (eGroups['property']['value'] is not None):
280 if isinstance(eGroups['property']['value'], str) or \
281 "string" in str(member['type']).lower():
282 member['value'] = (
283 "\"" + eGroups['property']['value'] + "\"")
284 else:
285 member['value'] = eGroups['property']['value']
286 members.append(member)
287 group['members'] = members
288 groups.append(group)
289 return groups
290
291
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600292def getParameters(member, groups, section, events):
Matthew Barth73379f92018-03-15 11:37:10 -0500293 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600294 Extracts and constructs a section's parameters
Matthew Barth73379f92018-03-15 11:37:10 -0500295 """
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600296 params = {}
297 if ('parameters' in section) and \
298 (section['parameters'] is not None):
299 plist = []
300 for sp in section['parameters']:
301 p = str(sp)
302 if (p != 'type'):
303 plist.append(p)
304 if (p != 'group'):
305 params[p] = "\"" + member[p] + "\""
Matthew Barth73379f92018-03-15 11:37:10 -0500306 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600307 params[p] = "Group\n{\n"
308 for g in groups:
309 for m in g['members']:
310 params[p] += (
311 "{\"" + str(m['object']) + "\",\n" +
312 "\"" + str(m['interface']) + "\",\n" +
313 "\"" + str(m['property']) + "\"},\n")
314 params[p] += "}"
Matthew Barth73379f92018-03-15 11:37:10 -0500315 else:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600316 params[p] = member[p]
317 params['params'] = plist
318 else:
319 params['params'] = []
320 return params
321
322
323def getInit(eGrps, eTrig, events):
324 """
325 Extracts and constructs an init trigger for the event's groups
326 which are required to be of the same type.
327 """
328 method = {}
329 methods = []
Matthew Barth03774012018-10-26 13:25:43 -0500330 if (len(eGrps) > 0):
331 # Use the first group member for retrieving the type
332 member = eGrps[0]['members'][0]
333 if ('method' in eTrig) and \
334 (eTrig['method'] is not None):
335 # Add method parameters
336 eMethod = next(m for m in events['methods']
337 if m['name'] == eTrig['method'])
338 method['method'] = eMethod['name']
339 method['mparams'] = getParameters(
340 member, eGrps, eMethod, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600341
Matthew Barth03774012018-10-26 13:25:43 -0500342 # Add handler parameters
343 eHandler = next(h for h in events['handlers']
344 if h['name'] == eTrig['handler'])
345 method['handler'] = eHandler['name']
346 method['hparams'] = getParameters(
347 member, eGrps, eHandler, events)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600348
349 methods.append(method)
350
351 return methods
Matthew Barth73379f92018-03-15 11:37:10 -0500352
353
Matthew Bartha69465a2018-03-02 13:50:59 -0600354def getSignal(eGrps, eTrig, events):
355 """
356 Extracts and constructs for each group member a signal
357 subscription of each match listed in the trigger.
358 """
359 signals = []
360 for group in eGrps:
361 for member in group['members']:
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600362 signal = {}
363 # Add signal parameters
364 eSignal = next(s for s in events['signals']
365 if s['name'] == eTrig['signal'])
366 signal['signal'] = eSignal['name']
367 signal['sparams'] = getParameters(member, eGrps, eSignal, events)
368
369 # If service not given, subscribe to signal match
370 if ('service' not in member):
371 # Add signal match parameters
Matthew Bartha69465a2018-03-02 13:50:59 -0600372 eMatch = next(m for m in events['matches']
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600373 if m['name'] == eSignal['match'])
374 signal['match'] = eMatch['name']
375 signal['mparams'] = getParameters(member, eGrps, eMatch, events)
Matthew Bartha69465a2018-03-02 13:50:59 -0600376
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600377 # Add handler parameters
378 eHandler = next(h for h in events['handlers']
379 if h['name'] == eTrig['handler'])
380 signal['handler'] = eHandler['name']
381 signal['hparams'] = getParameters(member, eGrps, eHandler, events)
Matthew Barth73379f92018-03-15 11:37:10 -0500382
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600383 signals.append(signal)
Matthew Barth73379f92018-03-15 11:37:10 -0500384
Matthew Bartha69465a2018-03-02 13:50:59 -0600385 return signals
386
387
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600388def getTimer(eTrig):
389 """
390 Extracts and constructs the required parameters for an
391 event timer.
392 """
393 timer = {}
394 timer['interval'] = (
395 "static_cast<std::chrono::microseconds>" +
396 "(" + str(eTrig['interval']) + ")")
397 timer['type'] = "TimerType::" + str(eTrig['type'])
398 return timer
399
400
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600401def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500402 """
403 Extracts and constructs the make_action function call for
404 all the actions within the given event.
405 """
406 action = []
407 for eActions in actions['actions']:
408 actions = {}
409 eAction = next(a for a in events['actions']
410 if a['name'] == eActions['name'])
411 actions['name'] = eAction['name']
412 params = []
413 if ('parameters' in eAction) and \
414 (eAction['parameters'] is not None):
415 for p in eAction['parameters']:
416 param = "static_cast<"
417 if type(eActions[p]) is not dict:
418 if p == 'actions':
419 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600420 pActs = getActions(zNum,
421 zCond,
422 edata,
423 eActions,
424 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500425 for a in pActs:
426 if (len(a['parameters']) != 0):
427 param += (
428 "make_action(action::" +
429 a['name'] +
430 "(\n")
431 for i, ap in enumerate(a['parameters']):
432 if (i+1) != len(a['parameters']):
433 param += (ap + ",")
434 else:
435 param += (ap + ")")
436 else:
437 param += ("make_action(action::" + a['name'])
438 param += "),"
439 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600440 elif p == 'defevents' or p == 'altevents':
441 param = "std::vector<SetSpeedEvent>{\n"
442 for i, e in enumerate(eActions[p]):
443 aEvent = getEvent(zNum, zCond, e, events)
444 if not aEvent:
445 continue
446 if (i+1) != len(eActions[p]):
447 param += genEvent(aEvent) + ",\n"
448 else:
449 param += genEvent(aEvent) + "\n"
450 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500451 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600452 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600453 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600454 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600455 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600456 ">(\"" + str(eActions[p]) + "\")")
457 else:
458 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600459 str(eActions[p]['type']).lower() +
460 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500461 else:
462 # Default type to 'size_t' when not given
463 param += ("size_t>(" + str(eActions[p]).lower() + ")")
464 else:
465 if p == 'timer':
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600466 t = getTimer(eActions[p])
Matthew Barth9df74752017-10-11 14:39:31 -0500467 param = (
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600468 "TimerConf{" + t['interval'] + "," +
469 t['type'] + "}")
Matthew Barth9df74752017-10-11 14:39:31 -0500470 else:
471 param += (str(eActions[p]['type']).lower() + ">(")
472 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600473 if isinstance(eActions[p]['value'], str) or \
474 "string" in str(eActions[p]['type']).lower():
475 param += \
476 "\"" + str(eActions[p]['value']) + "\")"
477 else:
478 param += \
479 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500480 else:
481 param += (
482 str(eActions[p]['type']).lower() +
483 convertToMap(str(eActions[p]['value'])) + ")")
484 params.append(param)
485 actions['parameters'] = params
486 action.append(actions)
487 return action
488
489
Matthew Barth7f272fd2017-09-12 16:16:56 -0500490def getEvent(zone_num, zone_conditions, e, events_data):
491 """
492 Parses the sections of an event and populates the properties
493 that construct an event within the generated source.
494 """
495 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500496
Matthew Barth621a5772018-11-14 14:55:11 -0600497 # Add set speed event name
498 event['name'] = e['name']
499
Matthew Barth6c050692017-12-05 15:30:09 -0600500 # Add set speed event groups
Matthew Barth03774012018-10-26 13:25:43 -0500501 event['groups'] = getGroups(zone_num, zone_conditions, e, events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500502
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600503 # Add optional set speed actions and function parameters
504 event['action'] = []
505 if ('actions' in e) and \
506 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600507 event['action'] = getActions(zone_num,
508 zone_conditions,
509 e,
510 e,
511 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500512
Matthew Bartha69465a2018-03-02 13:50:59 -0600513 # Add event triggers
514 event['triggers'] = {}
515 for trig in e['triggers']:
516 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600517 if (trig['name'] == "timer"):
518 event['triggers']['timer'] = getTimer(trig)
519 elif (trig['name'] == "signal"):
Matthew Bartha69465a2018-03-02 13:50:59 -0600520 if ('signals' not in event['triggers']):
521 event['triggers']['signals'] = []
522 triggers = getSignal(event['groups'], trig, events_data)
523 event['triggers']['signals'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600524 elif (trig['name'] == "init"):
525 triggers = getInit(event['groups'], trig, events_data)
526 event['triggers']['init'] = triggers
Matthew Barth7f272fd2017-09-12 16:16:56 -0500527
Matthew Barth7f272fd2017-09-12 16:16:56 -0500528 return event
529
530
531def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500532 """
533 Parses the precondition section of an event and populates the necessary
534 structures to generate a precondition for a set speed event.
535 """
536 precond = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600537
538 # Add set speed event precondition name
539 precond['pcname'] = event['name']
540
Matthew Barth9af190c2017-08-08 14:20:43 -0500541 # Add set speed event precondition group
Matthew Barth03774012018-10-26 13:25:43 -0500542 precond['pcgrps'] = getGroups(zNum,
543 zCond,
544 event['precondition'],
545 events_data)
Matthew Barth9af190c2017-08-08 14:20:43 -0500546
Matthew Barth7f272fd2017-09-12 16:16:56 -0500547 # Add set speed event precondition actions
548 pc = []
549 pcs = {}
550 pcs['name'] = event['precondition']['name']
551 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500552 if p['name'] == event['precondition']['name'])
553 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500554 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500555 param = {}
556 if p == 'groups':
557 param['type'] = "std::vector<PrecondGroup>"
558 param['open'] = "{"
559 param['close'] = "}"
560 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600561 for group in precond['pcgrps']:
562 for pcgrp in group['members']:
563 value = {}
564 value['value'] = (
565 "PrecondGroup{\"" +
566 str(pcgrp['object']) + "\",\"" +
567 str(pcgrp['interface']) + "\",\"" +
568 str(pcgrp['property']) + "\"," +
569 "static_cast<" +
570 str(pcgrp['type']).lower() + ">")
571 if isinstance(pcgrp['value'], str) or \
572 "string" in str(pcgrp['type']).lower():
573 value['value'] += ("(" + str(pcgrp['value']) + ")}")
574 else:
575 value['value'] += \
576 ("(" + str(pcgrp['value']).lower() + ")}")
577 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500578 param['values'] = values
579 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500580 pcs['params'] = params
581 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500582 precond['pcact'] = pc
583
Matthew Barth7f272fd2017-09-12 16:16:56 -0500584 pcevents = []
585 for pce in event['precondition']['events']:
586 pcevent = getEvent(zNum, zCond, pce, events_data)
587 if not pcevent:
588 continue
589 pcevents.append(pcevent)
590 precond['pcevts'] = pcevents
591
Matthew Barthf20c3212018-03-02 14:42:55 -0600592 # Add precondition event triggers
593 precond['triggers'] = {}
594 for trig in event['precondition']['triggers']:
595 triggers = []
Matthew Barthd0b90fc2018-03-05 09:38:45 -0600596 if (trig['name'] == "timer"):
597 precond['triggers']['pctime'] = getTimer(trig)
598 elif (trig['name'] == "signal"):
Matthew Barthf20c3212018-03-02 14:42:55 -0600599 if ('pcsigs' not in precond['triggers']):
600 precond['triggers']['pcsigs'] = []
601 triggers = getSignal(precond['pcgrps'], trig, events_data)
602 precond['triggers']['pcsigs'].extend(triggers)
Matthew Barthcd3bfbc2018-03-07 16:26:03 -0600603 elif (trig['name'] == "init"):
604 triggers = getInit(precond['pcgrps'], trig, events_data)
605 precond['triggers']['init'] = triggers
Matthew Barth9af190c2017-08-08 14:20:43 -0500606
607 return precond
608
609
Gunnar Millsb751f322017-06-06 15:14:11 -0500610def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500611 """
612 Constructs the event entries defined for each zone using the events yaml
613 provided.
614 """
615 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500616
Matthew Barthd4d0f082017-05-16 13:51:10 -0500617 if 'events' in events_data:
618 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500619 event = {}
Matthew Barth621a5772018-11-14 14:55:11 -0600620
Matthew Barth9af190c2017-08-08 14:20:43 -0500621 # Add precondition if given
622 if ('precondition' in e) and \
623 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500624 event['pc'] = addPrecondition(zone_num,
625 zone_conditions,
626 e,
627 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500628 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500629 event = getEvent(zone_num, zone_conditions, e, events_data)
630 if not event:
631 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500632 events.append(event)
633
634 return events
635
636
Matt Spinler78498c92017-04-11 13:59:46 -0500637def getFansInZone(zone_num, profiles, fan_data):
638 """
639 Parses the fan definition YAML files to find the fans
640 that match both the zone passed in and one of the
641 cooling profiles.
642 """
643
644 fans = []
645
646 for f in fan_data['fans']:
647
648 if zone_num != f['cooling_zone']:
649 continue
650
Gunnar Mills67e95512017-06-02 14:35:18 -0500651 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500652 if f.get('cooling_profile') is None:
653 profile = "all"
654 else:
655 profile = f['cooling_profile']
656
657 if profile not in profiles:
658 continue
659
660 fan = {}
661 fan['name'] = f['inventory']
662 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800663 fan['target_interface'] = f.get(
664 'target_interface',
665 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500666 fans.append(fan)
667
668 return fans
669
670
Matthew Barth7883f582019-02-14 14:24:46 -0600671def getIfacesInZone(zone_ifaces):
672 """
673 Parse given interfaces for a zone for associating a zone with an interface
674 and set any properties listed to defined values upon fan control starting
675 on the zone.
676 """
677
678 ifaces = []
679 for i in zone_ifaces:
680 iface = {}
681 # Interface name not needed yet for fan zones but
682 # may be necessary as more interfaces are extended by the zones
683 iface['name'] = i['name']
684
685 if ('properties' in i) and \
686 (i['properties'] is not None):
687 props = []
688 for p in i['properties']:
689 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600690 prop['name'] = p['name']
691 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600692 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600693 if ('persist' in p):
694 persist = p['persist']
695 if (persist is not None):
696 if (isinstance(persist, bool)):
697 prop['persist'] = 'true' if persist else 'false'
698 else:
699 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600700 vals = []
701 for v in p['values']:
702 val = v['value']
703 if (val is not None):
704 if (isinstance(val, bool)):
705 # Convert True/False to 'true'/'false'
706 val = 'true' if val else 'false'
707 elif (isinstance(val, str)):
708 # Wrap strings with double-quotes
709 val = "\"" + val + "\""
710 vals.append(val)
711 prop['values'] = vals
712 props.append(prop)
713 iface['props'] = props
714 ifaces.append(iface)
715
716 return ifaces
717
718
Gunnar Millsee8a2812017-06-02 14:26:47 -0500719def getConditionInZoneConditions(zone_condition, zone_conditions_data):
720 """
721 Parses the zone conditions definition YAML files to find the condition
722 that match both the zone condition passed in.
723 """
724
725 condition = {}
726
727 for c in zone_conditions_data['conditions']:
728
729 if zone_condition != c['name']:
730 continue
731 condition['type'] = c['type']
732 properties = []
733 for p in c['properties']:
734 property = {}
735 property['property'] = p['property']
736 property['interface'] = p['interface']
737 property['path'] = p['path']
738 property['type'] = p['type'].lower()
739 property['value'] = str(p['value']).lower()
740 properties.append(property)
741 condition['properties'] = properties
742
743 return condition
744
745
746def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500747 """
748 Combines the zone definition YAML and fan
749 definition YAML to create a data structure defining
750 the fan cooling zones.
751 """
752
753 zone_groups = []
754
755 for group in zone_data:
756 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500757 # zone conditions are optional
758 if 'zone_conditions' in group and group['zone_conditions'] is not None:
759 for c in group['zone_conditions']:
760
761 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500762 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500763 "zone_conditions used in zone YAML")
764
765 condition = getConditionInZoneConditions(c['name'],
766 zone_conditions_data)
767
768 if not condition:
769 sys.exit("Missing zone condition " + c['name'])
770
771 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500772
773 zone_group = {}
774 zone_group['conditions'] = conditions
775
776 zones = []
777 for z in group['zones']:
778 zone = {}
779
Gunnar Mills67e95512017-06-02 14:35:18 -0500780 # 'zone' is required
781 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500782 sys.exit("Missing fan zone number in " + zone_yaml)
783
784 zone['num'] = z['zone']
785
786 zone['full_speed'] = z['full_speed']
787
Matthew Barth1de66622017-06-12 13:13:02 -0500788 zone['default_floor'] = z['default_floor']
789
Matthew Bartha9561842017-06-29 11:43:45 -0500790 # 'increase_delay' is optional (use 0 by default)
791 key = 'increase_delay'
792 zone[key] = z.setdefault(key, 0)
793
794 # 'decrease_interval' is optional (use 0 by default)
795 key = 'decrease_interval'
796 zone[key] = z.setdefault(key, 0)
797
Gunnar Mills67e95512017-06-02 14:35:18 -0500798 # 'cooling_profiles' is optional (use 'all' instead)
799 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500800 (z['cooling_profiles'] is None):
801 profiles = ["all"]
802 else:
803 profiles = z['cooling_profiles']
804
Matthew Barth7883f582019-02-14 14:24:46 -0600805 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600806 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600807 if ('interfaces' in z) and \
808 (z['interfaces'] is not None):
809 ifaces = getIfacesInZone(z['interfaces'])
810
Matt Spinler78498c92017-04-11 13:59:46 -0500811 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500812 events = getEventsInZone(z['zone'], group['zone_conditions'],
813 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500814
815 if len(fans) == 0:
816 sys.exit("Didn't find any fans in zone " + str(zone['num']))
817
Matthew Barth7883f582019-02-14 14:24:46 -0600818 if (ifaces):
819 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500820 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500821 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500822 zones.append(zone)
823
824 zone_group['zones'] = zones
825 zone_groups.append(zone_group)
826
827 return zone_groups
828
829
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500830if __name__ == '__main__':
831 parser = ArgumentParser(
832 description="Phosphor fan zone definition parser")
833
834 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
835 default="example/zones.yaml",
836 help='fan zone definitional yaml')
837 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
838 default="example/fans.yaml",
839 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500840 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
841 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500842 parser.add_argument('-c', '--zone_conditions_yaml',
843 dest='zone_conditions_yaml',
844 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500845 parser.add_argument('-o', '--output_dir', dest='output_dir',
846 default=".",
847 help='output directory')
848 args = parser.parse_args()
849
850 if not args.zone_yaml or not args.fan_yaml:
851 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700852 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500853
854 with open(args.zone_yaml, 'r') as zone_input:
855 zone_data = yaml.safe_load(zone_input) or {}
856
857 with open(args.fan_yaml, 'r') as fan_input:
858 fan_data = yaml.safe_load(fan_input) or {}
859
Matthew Barthd4d0f082017-05-16 13:51:10 -0500860 events_data = {}
861 if args.events_yaml:
862 with open(args.events_yaml, 'r') as events_input:
863 events_data = yaml.safe_load(events_input) or {}
864
Gunnar Millsee8a2812017-06-02 14:26:47 -0500865 zone_conditions_data = {}
866 if args.zone_conditions_yaml:
867 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
868 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
869
Matt Spinleree7f6422017-05-09 11:03:14 -0500870 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500871 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500872
873 manager_config = zone_data.get('manager_configuration', {})
874
875 if manager_config.get('power_on_delay') is None:
876 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500877
Matthew Barth702c4a52018-02-28 16:23:11 -0600878 tmpls_dir = os.path.join(
879 os.path.dirname(os.path.realpath(__file__)),
880 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500881 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600882 if sys.version_info < (3, 0):
883 lkup = TemplateLookup(
884 directories=tmpls_dir.split(),
885 disable_unicode=True)
886 else:
887 lkup = TemplateLookup(
888 directories=tmpls_dir.split())
889 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500890 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600891 output.write(tmpl.render(zones=zone_config,
892 mgr_data=manager_config))