blob: 511e910c018d90c2a42fd2ed39dd24804ace09e7 [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"
140
141 e += "Group{\n"
142 for group in event['groups']:
143 for member in group['members']:
144 e += "{\n"
145 e += "\"" + member['object'] + "\",\n"
146 e += "{\"" + member['interface'] + "\",\n"
147 e += " \"" + member['property'] + "\"}\n"
148 e += "},\n"
149 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
165 e += "TimerConf{\n"
166 e += "\t" + event['timer']['interval'] + ",\n"
167 e += "\t" + event['timer']['type'] + "\n"
168 e += "},\n"
169
170 e += "std::vector<Signal>{\n"
Matthew Bartha69465a2018-03-02 13:50:59 -0600171 for s in event['triggers']['signals']:
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600172 e += "Signal{\n"
173 e += "match::" + s['match'] + "(\n"
174 for i, mp in enumerate(s['mparams']):
175 if (i+1) != len(s['mparams']):
176 e += "\"" + mp + "\",\n"
177 else:
178 e += "\"" + mp + "\"\n"
179 e += "),\n"
180 e += "make_handler(\n"
181 if ('type' in s['sparams']) and (s['sparams']['type'] is not None):
182 e += s['signal'] + "<" + s['sparams']['type'] + ">(\n"
183 else:
184 e += s['signal'] + "(\n"
185 for sp in s['sparams']['params']:
186 e += s['sparams'][sp] + ",\n"
187 if ('type' in s['hparams']) and (s['hparams']['type'] is not None):
188 e += ("handler::" + s['handler'] +
189 "<" + s['hparams']['type'] + ">(\n")
190 else:
191 e += "handler::" + s['handler'] + "(\n)"
192 for i, hp in enumerate(s['hparams']['params']):
193 if (i+1) != len(s['hparams']['params']):
194 e += s['hparams'][hp] + ",\n"
195 else:
196 e += s['hparams'][hp] + "\n"
197 e += "))\n"
198 e += ")\n"
199 e += "},\n"
200 e += "}\n"
201
202 e += "}"
203
204 return e
205
206
Matthew Barth6c050692017-12-05 15:30:09 -0600207def getGroups(zNum, zCond, edata, events):
208 """
209 Extract and construct the groups for the given event.
210 """
211 groups = []
212 for eGroups in edata['groups']:
213 if ('zone_conditions' in eGroups) and \
214 (eGroups['zone_conditions'] is not None):
215 # Zone conditions are optional in the events yaml but skip
216 # if this event's condition is not in this zone's conditions
217 if all('name' in z and z['name'] is not None and
218 not any(c['name'] == z['name'] for c in zCond)
219 for z in eGroups['zone_conditions']):
220 continue
221
222 # Zone numbers are optional in the events yaml but skip if this
223 # zone's zone number is not in the event's zone numbers
224 if all('zones' in z and z['zones'] is not None and
225 zNum not in z['zones']
226 for z in eGroups['zone_conditions']):
227 continue
Matthew Barth6c050692017-12-05 15:30:09 -0600228 eGroup = next(g for g in events['groups']
229 if g['name'] == eGroups['name'])
230
231 group = {}
232 members = []
233 group['name'] = eGroup['name']
234 for m in eGroup['members']:
235 member = {}
236 member['path'] = eGroup['type']
237 member['object'] = (eGroup['type'] + m)
238 member['interface'] = eGroups['interface']
239 member['property'] = eGroups['property']['name']
240 member['type'] = eGroups['property']['type']
Matthew Barth18c91032019-01-29 15:36:00 -0600241 # Use defined service to note member on zone object
242 if ('service' in eGroup) and \
243 (eGroup['service'] is not None):
244 member['service'] = eGroup['service']
Matthew Barth6c050692017-12-05 15:30:09 -0600245 # Add expected group member's property value if given
246 if ('value' in eGroups['property']) and \
247 (eGroups['property']['value'] is not None):
248 if isinstance(eGroups['property']['value'], str) or \
249 "string" in str(member['type']).lower():
250 member['value'] = (
251 "\"" + eGroups['property']['value'] + "\"")
252 else:
253 member['value'] = eGroups['property']['value']
254 members.append(member)
255 group['members'] = members
256 groups.append(group)
257 return groups
258
259
Matthew Bartha69465a2018-03-02 13:50:59 -0600260def getSignal(eGrps, eTrig, events):
261 """
262 Extracts and constructs for each group member a signal
263 subscription of each match listed in the trigger.
264 """
265 signals = []
266 for group in eGrps:
267 for member in group['members']:
268 for eMatches in eTrig['matches']:
269 signal = {}
270 eMatch = next(m for m in events['matches']
271 if m['name'] == eMatches['name'])
272 # If service not given, subscribe to signal match
273 if ('service' not in member):
274 signal['match'] = eMatch['name']
275 params = []
276 if ('parameters' in eMatch) and \
277 (eMatch['parameters'] is not None):
278 for p in eMatch['parameters']:
279 params.append(member[str(p)])
280 signal['mparams'] = params
281
282 if ('parameters' in eMatch['signal']) and \
283 (eMatch['signal']['parameters'] is not None):
284 eSignal = eMatch['signal']
285 else:
286 eSignal = next(s for s in events['signals']
287 if s['name'] == eMatch['signal'])
288 signal['signal'] = eSignal['name']
289 sparams = {}
290 if ('parameters' in eSignal) and \
291 (eSignal['parameters'] is not None):
292 splist = []
293 for p in eSignal['parameters']:
294 sp = str(p)
295 if (sp != 'type'):
296 splist.append(sp)
297 if (sp != 'group'):
298 sparams[sp] = "\"" + member[sp] + "\""
299 else:
300 sparams[sp] = "Group{\n"
301 for m in group['members']:
302 sparams[sp] += (
303 "{\n" +
304 "\"" + str(m['object']) + "\",\n" +
305 "{\"" + str(m['interface']) + "\"," +
306 "\"" + str(m['property']) + "\"}\n" +
307 "},\n")
308 sparams[sp] += "}"
309 else:
310 sparams[sp] = member[sp]
311 sparams['params'] = splist
312 signal['sparams'] = sparams
313 # Add signal handler
314 eHandler = next(h for h in events['handlers']
315 if h['name'] == eSignal['handler'])
316 signal['handler'] = eHandler['name']
317 hparams = {}
318 if ('parameters' in eHandler) and \
319 (eHandler['parameters'] is not None):
320 hplist = []
321 for p in eHandler['parameters']:
322 hp = str(p)
323 if (hp != 'type'):
324 hplist.append(hp)
325 if (hp != 'group'):
326 hparams[hp] = "\"" + member[hp] + "\""
327 else:
328 hparams[hp] = "Group{\n"
329 for m in group['members']:
330 hparams[hp] += (
331 "{\n" +
332 "\"" + str(m['object']) + "\",\n" +
333 "{\"" + str(m['interface']) + "\"," +
334 "\"" + str(m['property']) + "\"}\n" +
335 "},\n")
336 hparams[hp] += "}"
337 else:
338 hparams[hp] = member[hp]
339 hparams['params'] = hplist
340 signal['hparams'] = hparams
341 signals.append(signal)
342 return signals
343
344
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600345def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500346 """
347 Extracts and constructs the make_action function call for
348 all the actions within the given event.
349 """
350 action = []
351 for eActions in actions['actions']:
352 actions = {}
353 eAction = next(a for a in events['actions']
354 if a['name'] == eActions['name'])
355 actions['name'] = eAction['name']
356 params = []
357 if ('parameters' in eAction) and \
358 (eAction['parameters'] is not None):
359 for p in eAction['parameters']:
360 param = "static_cast<"
361 if type(eActions[p]) is not dict:
362 if p == 'actions':
363 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600364 pActs = getActions(zNum,
365 zCond,
366 edata,
367 eActions,
368 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500369 for a in pActs:
370 if (len(a['parameters']) != 0):
371 param += (
372 "make_action(action::" +
373 a['name'] +
374 "(\n")
375 for i, ap in enumerate(a['parameters']):
376 if (i+1) != len(a['parameters']):
377 param += (ap + ",")
378 else:
379 param += (ap + ")")
380 else:
381 param += ("make_action(action::" + a['name'])
382 param += "),"
383 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600384 elif p == 'defevents' or p == 'altevents':
385 param = "std::vector<SetSpeedEvent>{\n"
386 for i, e in enumerate(eActions[p]):
387 aEvent = getEvent(zNum, zCond, e, events)
388 if not aEvent:
389 continue
390 if (i+1) != len(eActions[p]):
391 param += genEvent(aEvent) + ",\n"
392 else:
393 param += genEvent(aEvent) + "\n"
394 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500395 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600396 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600397 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600398 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600399 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600400 ">(\"" + str(eActions[p]) + "\")")
401 else:
402 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600403 str(eActions[p]['type']).lower() +
404 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500405 else:
406 # Default type to 'size_t' when not given
407 param += ("size_t>(" + str(eActions[p]).lower() + ")")
408 else:
409 if p == 'timer':
410 param = (
William A. Kennington III122b8432018-10-30 18:39:21 -0700411 "TimerConf{static_cast<std::chrono::seconds>(" +
Matthew Barth9df74752017-10-11 14:39:31 -0500412 str(eActions[p]['delay']) + "), " +
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700413 "TimerType::" +
Matthew Barth9df74752017-10-11 14:39:31 -0500414 str(eActions[p]['type']) + "}")
415 else:
416 param += (str(eActions[p]['type']).lower() + ">(")
417 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600418 if isinstance(eActions[p]['value'], str) or \
419 "string" in str(eActions[p]['type']).lower():
420 param += \
421 "\"" + str(eActions[p]['value']) + "\")"
422 else:
423 param += \
424 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500425 else:
426 param += (
427 str(eActions[p]['type']).lower() +
428 convertToMap(str(eActions[p]['value'])) + ")")
429 params.append(param)
430 actions['parameters'] = params
431 action.append(actions)
432 return action
433
434
Matthew Barth7f272fd2017-09-12 16:16:56 -0500435def getEvent(zone_num, zone_conditions, e, events_data):
436 """
437 Parses the sections of an event and populates the properties
438 that construct an event within the generated source.
439 """
440 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500441
Matthew Barth6c050692017-12-05 15:30:09 -0600442 # Add set speed event groups
443 grps = getGroups(zone_num, zone_conditions, e, events_data)
444 if not grps:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500445 return
Matthew Barth6c050692017-12-05 15:30:09 -0600446 event['groups'] = grps
Matthew Barth7f272fd2017-09-12 16:16:56 -0500447
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600448 # Add optional set speed actions and function parameters
449 event['action'] = []
450 if ('actions' in e) and \
451 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600452 event['action'] = getActions(zone_num,
453 zone_conditions,
454 e,
455 e,
456 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500457
Matthew Bartha69465a2018-03-02 13:50:59 -0600458 # Add event triggers
459 event['triggers'] = {}
460 for trig in e['triggers']:
461 triggers = []
462 if (trig['name'] == "signal"):
463 if ('signals' not in event['triggers']):
464 event['triggers']['signals'] = []
465 triggers = getSignal(event['groups'], trig, events_data)
466 event['triggers']['signals'].extend(triggers)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500467
468 # Add optional action call timer
469 timer = {}
470 interval = "static_cast<std::chrono::seconds>"
471 if ('timer' in e) and \
472 (e['timer'] is not None):
473 timer['interval'] = (interval +
474 "(" +
475 str(e['timer']['interval']) +
476 ")")
477 else:
478 timer['interval'] = (interval +
479 "(" + str(0) + ")")
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700480 timer['type'] = "TimerType::repeating"
Matthew Barth7f272fd2017-09-12 16:16:56 -0500481 event['timer'] = timer
482
483 return event
484
485
486def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500487 """
488 Parses the precondition section of an event and populates the necessary
489 structures to generate a precondition for a set speed event.
490 """
491 precond = {}
492 # Add set speed event precondition group
Matthew Barth6c050692017-12-05 15:30:09 -0600493 grps = getGroups(zNum, zCond, event['precondition'], events_data)
494 if not grps:
495 return
496 precond['pcgrps'] = grps
Matthew Barth9af190c2017-08-08 14:20:43 -0500497
Matthew Barth7f272fd2017-09-12 16:16:56 -0500498 # Add set speed event precondition actions
499 pc = []
500 pcs = {}
501 pcs['name'] = event['precondition']['name']
502 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500503 if p['name'] == event['precondition']['name'])
504 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500505 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500506 param = {}
507 if p == 'groups':
508 param['type'] = "std::vector<PrecondGroup>"
509 param['open'] = "{"
510 param['close'] = "}"
511 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600512 for group in precond['pcgrps']:
513 for pcgrp in group['members']:
514 value = {}
515 value['value'] = (
516 "PrecondGroup{\"" +
517 str(pcgrp['object']) + "\",\"" +
518 str(pcgrp['interface']) + "\",\"" +
519 str(pcgrp['property']) + "\"," +
520 "static_cast<" +
521 str(pcgrp['type']).lower() + ">")
522 if isinstance(pcgrp['value'], str) or \
523 "string" in str(pcgrp['type']).lower():
524 value['value'] += ("(" + str(pcgrp['value']) + ")}")
525 else:
526 value['value'] += \
527 ("(" + str(pcgrp['value']).lower() + ")}")
528 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500529 param['values'] = values
530 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500531 pcs['params'] = params
532 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500533 precond['pcact'] = pc
534
Matthew Barth7f272fd2017-09-12 16:16:56 -0500535 pcevents = []
536 for pce in event['precondition']['events']:
537 pcevent = getEvent(zNum, zCond, pce, events_data)
538 if not pcevent:
539 continue
540 pcevents.append(pcevent)
541 precond['pcevts'] = pcevents
542
Matthew Barth67967f92017-09-22 12:43:57 -0500543 # Add precondition signal handlers
544 signals = []
Matthew Barth6c050692017-12-05 15:30:09 -0600545 for group in precond['pcgrps']:
546 for member in group['members']:
547 for eMatches in event['precondition']['matches']:
548 signal = {}
549 eMatch = next(m for m in events_data['matches']
550 if m['name'] == eMatches['name'])
Matthew Barth18c91032019-01-29 15:36:00 -0600551 # If service not given, subscribe to signal match
552 if ('service' not in member):
553 signal['match'] = eMatch['name']
554 params = []
555 if ('parameters' in eMatch) and \
556 (eMatch['parameters'] is not None):
557 for p in eMatch['parameters']:
558 params.append(member[str(p)])
559 signal['mparams'] = params
560
Matthew Barthe7d87052018-03-21 13:58:31 -0500561 if ('parameters' in eMatch['signal']) and \
562 (eMatch['signal']['parameters'] is not None):
563 eSignal = eMatch['signal']
564 else:
565 eSignal = next(s for s in events_data['signals']
566 if s['name'] == eMatch['signal'])
Matthew Barth6c050692017-12-05 15:30:09 -0600567 signal['signal'] = eSignal['name']
568 sparams = {}
569 if ('parameters' in eSignal) and \
570 (eSignal['parameters'] is not None):
571 splist = []
572 for p in eSignal['parameters']:
573 sp = str(p)
574 if (sp != 'type'):
575 splist.append(sp)
576 if (sp != 'group'):
577 sparams[sp] = "\"" + member[sp] + "\""
578 else:
579 sparams[sp] = "Group{\n"
580 for m in group:
581 sparams[sp] += (
582 "{\n" +
583 "\"" + str(m['object']) + "\",\n" +
584 "{\"" + str(m['interface']) + "\"," +
585 "\"" + str(m['property']) + "\"}\n" +
586 "},\n")
587 sparams[sp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500588 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600589 sparams[sp] = member[sp]
590 sparams['params'] = splist
591 signal['sparams'] = sparams
592 # Add signal handler
593 eHandler = next(h for h in events_data['handlers']
594 if h['name'] == eSignal['handler'])
595 signal['handler'] = eHandler['name']
596 hparams = {}
597 if ('parameters' in eHandler) and \
598 (eHandler['parameters'] is not None):
599 hplist = []
600 for p in eHandler['parameters']:
601 hp = str(p)
602 if (hp != 'type'):
603 hplist.append(hp)
604 if (hp != 'group'):
605 hparams[hp] = "\"" + member[hp] + "\""
606 else:
607 hparams[hp] = "Group{\n"
608 for m in group:
609 hparams[hp] += (
610 "{\n" +
611 "\"" + str(m['object']) + "\",\n" +
612 "{\"" + str(m['interface']) + "\"," +
613 "\"" + str(m['property']) + "\"}\n" +
614 "},\n")
615 hparams[hp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500616 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600617 hparams[hp] = member[hp]
618 hparams['params'] = hplist
619 signal['hparams'] = hparams
620 signals.append(signal)
Matthew Barth67967f92017-09-22 12:43:57 -0500621 precond['pcsigs'] = signals
Matthew Barth9af190c2017-08-08 14:20:43 -0500622
Matthew Barth90149802017-08-15 10:51:37 -0500623 # Add optional action call timer
624 timer = {}
625 interval = "static_cast<std::chrono::seconds>"
626 if ('timer' in event['precondition']) and \
627 (event['precondition']['timer'] is not None):
628 timer['interval'] = (interval +
629 "(" +
630 str(event['precondition']['timer']['interval']) +
631 ")")
632 else:
633 timer['interval'] = (interval +
634 "(" + str(0) + ")")
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700635 timer['type'] = "TimerType::repeating"
Matthew Barth90149802017-08-15 10:51:37 -0500636 precond['pctime'] = timer
637
Matthew Barth9af190c2017-08-08 14:20:43 -0500638 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 Barth9af190c2017-08-08 14:20:43 -0500651 # Add precondition if given
652 if ('precondition' in e) and \
653 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500654 event['pc'] = addPrecondition(zone_num,
655 zone_conditions,
656 e,
657 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500658 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500659 event = getEvent(zone_num, zone_conditions, e, events_data)
660 if not event:
661 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500662 events.append(event)
663
664 return events
665
666
Matt Spinler78498c92017-04-11 13:59:46 -0500667def getFansInZone(zone_num, profiles, fan_data):
668 """
669 Parses the fan definition YAML files to find the fans
670 that match both the zone passed in and one of the
671 cooling profiles.
672 """
673
674 fans = []
675
676 for f in fan_data['fans']:
677
678 if zone_num != f['cooling_zone']:
679 continue
680
Gunnar Mills67e95512017-06-02 14:35:18 -0500681 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500682 if f.get('cooling_profile') is None:
683 profile = "all"
684 else:
685 profile = f['cooling_profile']
686
687 if profile not in profiles:
688 continue
689
690 fan = {}
691 fan['name'] = f['inventory']
692 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800693 fan['target_interface'] = f.get(
694 'target_interface',
695 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500696 fans.append(fan)
697
698 return fans
699
700
Matthew Barth7883f582019-02-14 14:24:46 -0600701def getIfacesInZone(zone_ifaces):
702 """
703 Parse given interfaces for a zone for associating a zone with an interface
704 and set any properties listed to defined values upon fan control starting
705 on the zone.
706 """
707
708 ifaces = []
709 for i in zone_ifaces:
710 iface = {}
711 # Interface name not needed yet for fan zones but
712 # may be necessary as more interfaces are extended by the zones
713 iface['name'] = i['name']
714
715 if ('properties' in i) and \
716 (i['properties'] is not None):
717 props = []
718 for p in i['properties']:
719 prop = {}
Matthew Barth59096e52019-02-18 12:23:38 -0600720 prop['name'] = p['name']
721 prop['func'] = str(p['name']).lower()
Matthew Barth7883f582019-02-14 14:24:46 -0600722 prop['type'] = parse_cpp_type(p['type'])
Matthew Barth59096e52019-02-18 12:23:38 -0600723 if ('persist' in p):
724 persist = p['persist']
725 if (persist is not None):
726 if (isinstance(persist, bool)):
727 prop['persist'] = 'true' if persist else 'false'
728 else:
729 prop['persist'] = 'false'
Matthew Barth7883f582019-02-14 14:24:46 -0600730 vals = []
731 for v in p['values']:
732 val = v['value']
733 if (val is not None):
734 if (isinstance(val, bool)):
735 # Convert True/False to 'true'/'false'
736 val = 'true' if val else 'false'
737 elif (isinstance(val, str)):
738 # Wrap strings with double-quotes
739 val = "\"" + val + "\""
740 vals.append(val)
741 prop['values'] = vals
742 props.append(prop)
743 iface['props'] = props
744 ifaces.append(iface)
745
746 return ifaces
747
748
Gunnar Millsee8a2812017-06-02 14:26:47 -0500749def getConditionInZoneConditions(zone_condition, zone_conditions_data):
750 """
751 Parses the zone conditions definition YAML files to find the condition
752 that match both the zone condition passed in.
753 """
754
755 condition = {}
756
757 for c in zone_conditions_data['conditions']:
758
759 if zone_condition != c['name']:
760 continue
761 condition['type'] = c['type']
762 properties = []
763 for p in c['properties']:
764 property = {}
765 property['property'] = p['property']
766 property['interface'] = p['interface']
767 property['path'] = p['path']
768 property['type'] = p['type'].lower()
769 property['value'] = str(p['value']).lower()
770 properties.append(property)
771 condition['properties'] = properties
772
773 return condition
774
775
776def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500777 """
778 Combines the zone definition YAML and fan
779 definition YAML to create a data structure defining
780 the fan cooling zones.
781 """
782
783 zone_groups = []
784
785 for group in zone_data:
786 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500787 # zone conditions are optional
788 if 'zone_conditions' in group and group['zone_conditions'] is not None:
789 for c in group['zone_conditions']:
790
791 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500792 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500793 "zone_conditions used in zone YAML")
794
795 condition = getConditionInZoneConditions(c['name'],
796 zone_conditions_data)
797
798 if not condition:
799 sys.exit("Missing zone condition " + c['name'])
800
801 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500802
803 zone_group = {}
804 zone_group['conditions'] = conditions
805
806 zones = []
807 for z in group['zones']:
808 zone = {}
809
Gunnar Mills67e95512017-06-02 14:35:18 -0500810 # 'zone' is required
811 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500812 sys.exit("Missing fan zone number in " + zone_yaml)
813
814 zone['num'] = z['zone']
815
816 zone['full_speed'] = z['full_speed']
817
Matthew Barth1de66622017-06-12 13:13:02 -0500818 zone['default_floor'] = z['default_floor']
819
Matthew Bartha9561842017-06-29 11:43:45 -0500820 # 'increase_delay' is optional (use 0 by default)
821 key = 'increase_delay'
822 zone[key] = z.setdefault(key, 0)
823
824 # 'decrease_interval' is optional (use 0 by default)
825 key = 'decrease_interval'
826 zone[key] = z.setdefault(key, 0)
827
Gunnar Mills67e95512017-06-02 14:35:18 -0500828 # 'cooling_profiles' is optional (use 'all' instead)
829 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500830 (z['cooling_profiles'] is None):
831 profiles = ["all"]
832 else:
833 profiles = z['cooling_profiles']
834
Matthew Barth7883f582019-02-14 14:24:46 -0600835 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600836 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600837 if ('interfaces' in z) and \
838 (z['interfaces'] is not None):
839 ifaces = getIfacesInZone(z['interfaces'])
840
Matt Spinler78498c92017-04-11 13:59:46 -0500841 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500842 events = getEventsInZone(z['zone'], group['zone_conditions'],
843 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500844
845 if len(fans) == 0:
846 sys.exit("Didn't find any fans in zone " + str(zone['num']))
847
Matthew Barth7883f582019-02-14 14:24:46 -0600848 if (ifaces):
849 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500850 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500851 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500852 zones.append(zone)
853
854 zone_group['zones'] = zones
855 zone_groups.append(zone_group)
856
857 return zone_groups
858
859
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500860if __name__ == '__main__':
861 parser = ArgumentParser(
862 description="Phosphor fan zone definition parser")
863
864 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
865 default="example/zones.yaml",
866 help='fan zone definitional yaml')
867 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
868 default="example/fans.yaml",
869 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500870 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
871 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500872 parser.add_argument('-c', '--zone_conditions_yaml',
873 dest='zone_conditions_yaml',
874 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500875 parser.add_argument('-o', '--output_dir', dest='output_dir',
876 default=".",
877 help='output directory')
878 args = parser.parse_args()
879
880 if not args.zone_yaml or not args.fan_yaml:
881 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700882 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500883
884 with open(args.zone_yaml, 'r') as zone_input:
885 zone_data = yaml.safe_load(zone_input) or {}
886
887 with open(args.fan_yaml, 'r') as fan_input:
888 fan_data = yaml.safe_load(fan_input) or {}
889
Matthew Barthd4d0f082017-05-16 13:51:10 -0500890 events_data = {}
891 if args.events_yaml:
892 with open(args.events_yaml, 'r') as events_input:
893 events_data = yaml.safe_load(events_input) or {}
894
Gunnar Millsee8a2812017-06-02 14:26:47 -0500895 zone_conditions_data = {}
896 if args.zone_conditions_yaml:
897 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
898 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
899
Matt Spinleree7f6422017-05-09 11:03:14 -0500900 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500901 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500902
903 manager_config = zone_data.get('manager_configuration', {})
904
905 if manager_config.get('power_on_delay') is None:
906 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500907
Matthew Barth702c4a52018-02-28 16:23:11 -0600908 tmpls_dir = os.path.join(
909 os.path.dirname(os.path.realpath(__file__)),
910 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500911 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600912 if sys.version_info < (3, 0):
913 lkup = TemplateLookup(
914 directories=tmpls_dir.split(),
915 disable_unicode=True)
916 else:
917 lkup = TemplateLookup(
918 directories=tmpls_dir.split())
919 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500920 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600921 output.write(tmpl.render(zones=zone_config,
922 mgr_data=manager_config))