blob: e875e32652c4c84537af3c056174b643f432c570 [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"
171 for s in event['signals']:
172 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 Bartha1aef7a2019-01-16 11:02:57 -0600260def getActions(zNum, zCond, edata, actions, events):
Matthew Barth9df74752017-10-11 14:39:31 -0500261 """
262 Extracts and constructs the make_action function call for
263 all the actions within the given event.
264 """
265 action = []
266 for eActions in actions['actions']:
267 actions = {}
268 eAction = next(a for a in events['actions']
269 if a['name'] == eActions['name'])
270 actions['name'] = eAction['name']
271 params = []
272 if ('parameters' in eAction) and \
273 (eAction['parameters'] is not None):
274 for p in eAction['parameters']:
275 param = "static_cast<"
276 if type(eActions[p]) is not dict:
277 if p == 'actions':
278 param = "std::vector<Action>{"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600279 pActs = getActions(zNum,
280 zCond,
281 edata,
282 eActions,
283 events)
Matthew Barth9df74752017-10-11 14:39:31 -0500284 for a in pActs:
285 if (len(a['parameters']) != 0):
286 param += (
287 "make_action(action::" +
288 a['name'] +
289 "(\n")
290 for i, ap in enumerate(a['parameters']):
291 if (i+1) != len(a['parameters']):
292 param += (ap + ",")
293 else:
294 param += (ap + ")")
295 else:
296 param += ("make_action(action::" + a['name'])
297 param += "),"
298 param += "}"
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600299 elif p == 'defevents' or p == 'altevents':
300 param = "std::vector<SetSpeedEvent>{\n"
301 for i, e in enumerate(eActions[p]):
302 aEvent = getEvent(zNum, zCond, e, events)
303 if not aEvent:
304 continue
305 if (i+1) != len(eActions[p]):
306 param += genEvent(aEvent) + ",\n"
307 else:
308 param += genEvent(aEvent) + "\n"
309 param += "\t}"
Matthew Barth9df74752017-10-11 14:39:31 -0500310 elif p == 'property':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600311 if isinstance(eActions[p], str) or \
Matthew Barth6c050692017-12-05 15:30:09 -0600312 "string" in str(eActions[p]['type']).lower():
Matthew Barth9a5b6992018-01-23 15:32:26 -0600313 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600314 str(eActions[p]['type']).lower() +
Matthew Barth9a5b6992018-01-23 15:32:26 -0600315 ">(\"" + str(eActions[p]) + "\")")
316 else:
317 param += (
Matthew Barth6c050692017-12-05 15:30:09 -0600318 str(eActions[p]['type']).lower() +
319 ">(" + str(eActions[p]['value']).lower() + ")")
Matthew Barth9df74752017-10-11 14:39:31 -0500320 else:
321 # Default type to 'size_t' when not given
322 param += ("size_t>(" + str(eActions[p]).lower() + ")")
323 else:
324 if p == 'timer':
325 param = (
William A. Kennington III122b8432018-10-30 18:39:21 -0700326 "TimerConf{static_cast<std::chrono::seconds>(" +
Matthew Barth9df74752017-10-11 14:39:31 -0500327 str(eActions[p]['delay']) + "), " +
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700328 "TimerType::" +
Matthew Barth9df74752017-10-11 14:39:31 -0500329 str(eActions[p]['type']) + "}")
330 else:
331 param += (str(eActions[p]['type']).lower() + ">(")
332 if p != 'map':
Matthew Barth9a5b6992018-01-23 15:32:26 -0600333 if isinstance(eActions[p]['value'], str) or \
334 "string" in str(eActions[p]['type']).lower():
335 param += \
336 "\"" + str(eActions[p]['value']) + "\")"
337 else:
338 param += \
339 str(eActions[p]['value']).lower() + ")"
Matthew Barth9df74752017-10-11 14:39:31 -0500340 else:
341 param += (
342 str(eActions[p]['type']).lower() +
343 convertToMap(str(eActions[p]['value'])) + ")")
344 params.append(param)
345 actions['parameters'] = params
346 action.append(actions)
347 return action
348
349
Matthew Barth7f272fd2017-09-12 16:16:56 -0500350def getEvent(zone_num, zone_conditions, e, events_data):
351 """
352 Parses the sections of an event and populates the properties
353 that construct an event within the generated source.
354 """
355 event = {}
Matthew Barth7f272fd2017-09-12 16:16:56 -0500356
Matthew Barth6c050692017-12-05 15:30:09 -0600357 # Add set speed event groups
358 grps = getGroups(zone_num, zone_conditions, e, events_data)
359 if not grps:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500360 return
Matthew Barth6c050692017-12-05 15:30:09 -0600361 event['groups'] = grps
Matthew Barth7f272fd2017-09-12 16:16:56 -0500362
Matthew Barthe3d1c4a2018-01-11 13:53:49 -0600363 # Add optional set speed actions and function parameters
364 event['action'] = []
365 if ('actions' in e) and \
366 (e['actions'] is not None):
Matthew Bartha1aef7a2019-01-16 11:02:57 -0600367 event['action'] = getActions(zone_num,
368 zone_conditions,
369 e,
370 e,
371 events_data)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500372
Matthew Barth67967f92017-09-22 12:43:57 -0500373 # Add signal handlers
374 signals = []
Matthew Barth6c050692017-12-05 15:30:09 -0600375 for group in event['groups']:
376 for member in group['members']:
377 for eMatches in e['matches']:
378 signal = {}
379 eMatch = next(m for m in events_data['matches']
380 if m['name'] == eMatches['name'])
Matthew Barth18c91032019-01-29 15:36:00 -0600381 # If service not given, subscribe to signal match
382 if ('service' not in member):
383 signal['match'] = eMatch['name']
384 params = []
385 if ('parameters' in eMatch) and \
386 (eMatch['parameters'] is not None):
387 for p in eMatch['parameters']:
388 params.append(member[str(p)])
389 signal['mparams'] = params
390
Matthew Barthe7d87052018-03-21 13:58:31 -0500391 if ('parameters' in eMatch['signal']) and \
392 (eMatch['signal']['parameters'] is not None):
393 eSignal = eMatch['signal']
394 else:
395 eSignal = next(s for s in events_data['signals']
396 if s['name'] == eMatch['signal'])
Matthew Barth6c050692017-12-05 15:30:09 -0600397 signal['signal'] = eSignal['name']
398 sparams = {}
399 if ('parameters' in eSignal) and \
400 (eSignal['parameters'] is not None):
401 splist = []
402 for p in eSignal['parameters']:
403 sp = str(p)
404 if (sp != 'type'):
405 splist.append(sp)
406 if (sp != 'group'):
407 sparams[sp] = "\"" + member[sp] + "\""
408 else:
409 sparams[sp] = "Group{\n"
410 for m in group['members']:
411 sparams[sp] += (
412 "{\n" +
413 "\"" + str(m['object']) + "\",\n" +
414 "{\"" + str(m['interface']) + "\"," +
415 "\"" + str(m['property']) + "\"}\n" +
416 "},\n")
417 sparams[sp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500418 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600419 sparams[sp] = member[sp]
420 sparams['params'] = splist
421 signal['sparams'] = sparams
422 # Add signal handler
423 eHandler = next(h for h in events_data['handlers']
424 if h['name'] == eSignal['handler'])
425 signal['handler'] = eHandler['name']
426 hparams = {}
427 if ('parameters' in eHandler) and \
428 (eHandler['parameters'] is not None):
429 hplist = []
430 for p in eHandler['parameters']:
431 hp = str(p)
432 if (hp != 'type'):
433 hplist.append(hp)
434 if (hp != 'group'):
435 hparams[hp] = "\"" + member[hp] + "\""
436 else:
437 hparams[hp] = "Group{\n"
438 for m in group['members']:
439 hparams[hp] += (
440 "{\n" +
441 "\"" + str(m['object']) + "\",\n" +
442 "{\"" + str(m['interface']) + "\"," +
443 "\"" + str(m['property']) + "\"}\n" +
444 "},\n")
445 hparams[hp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500446 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600447 hparams[hp] = member[hp]
448 hparams['params'] = hplist
449 signal['hparams'] = hparams
450 signals.append(signal)
Matthew Barth67967f92017-09-22 12:43:57 -0500451 event['signals'] = signals
Matthew Barth7f272fd2017-09-12 16:16:56 -0500452
453 # Add optional action call timer
454 timer = {}
455 interval = "static_cast<std::chrono::seconds>"
456 if ('timer' in e) and \
457 (e['timer'] is not None):
458 timer['interval'] = (interval +
459 "(" +
460 str(e['timer']['interval']) +
461 ")")
462 else:
463 timer['interval'] = (interval +
464 "(" + str(0) + ")")
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700465 timer['type'] = "TimerType::repeating"
Matthew Barth7f272fd2017-09-12 16:16:56 -0500466 event['timer'] = timer
467
468 return event
469
470
471def addPrecondition(zNum, zCond, event, events_data):
Matthew Barth9af190c2017-08-08 14:20:43 -0500472 """
473 Parses the precondition section of an event and populates the necessary
474 structures to generate a precondition for a set speed event.
475 """
476 precond = {}
477 # Add set speed event precondition group
Matthew Barth6c050692017-12-05 15:30:09 -0600478 grps = getGroups(zNum, zCond, event['precondition'], events_data)
479 if not grps:
480 return
481 precond['pcgrps'] = grps
Matthew Barth9af190c2017-08-08 14:20:43 -0500482
Matthew Barth7f272fd2017-09-12 16:16:56 -0500483 # Add set speed event precondition actions
484 pc = []
485 pcs = {}
486 pcs['name'] = event['precondition']['name']
487 epc = next(p for p in events_data['preconditions']
Matthew Barth9af190c2017-08-08 14:20:43 -0500488 if p['name'] == event['precondition']['name'])
489 params = []
Matthew Barth7f272fd2017-09-12 16:16:56 -0500490 for p in epc['parameters']:
Matthew Barth9af190c2017-08-08 14:20:43 -0500491 param = {}
492 if p == 'groups':
493 param['type'] = "std::vector<PrecondGroup>"
494 param['open'] = "{"
495 param['close'] = "}"
496 values = []
Matthew Barth6c050692017-12-05 15:30:09 -0600497 for group in precond['pcgrps']:
498 for pcgrp in group['members']:
499 value = {}
500 value['value'] = (
501 "PrecondGroup{\"" +
502 str(pcgrp['object']) + "\",\"" +
503 str(pcgrp['interface']) + "\",\"" +
504 str(pcgrp['property']) + "\"," +
505 "static_cast<" +
506 str(pcgrp['type']).lower() + ">")
507 if isinstance(pcgrp['value'], str) or \
508 "string" in str(pcgrp['type']).lower():
509 value['value'] += ("(" + str(pcgrp['value']) + ")}")
510 else:
511 value['value'] += \
512 ("(" + str(pcgrp['value']).lower() + ")}")
513 values.append(value)
Matthew Barth9af190c2017-08-08 14:20:43 -0500514 param['values'] = values
515 params.append(param)
Matthew Barth7f272fd2017-09-12 16:16:56 -0500516 pcs['params'] = params
517 pc.append(pcs)
Matthew Barth9af190c2017-08-08 14:20:43 -0500518 precond['pcact'] = pc
519
Matthew Barth7f272fd2017-09-12 16:16:56 -0500520 pcevents = []
521 for pce in event['precondition']['events']:
522 pcevent = getEvent(zNum, zCond, pce, events_data)
523 if not pcevent:
524 continue
525 pcevents.append(pcevent)
526 precond['pcevts'] = pcevents
527
Matthew Barth67967f92017-09-22 12:43:57 -0500528 # Add precondition signal handlers
529 signals = []
Matthew Barth6c050692017-12-05 15:30:09 -0600530 for group in precond['pcgrps']:
531 for member in group['members']:
532 for eMatches in event['precondition']['matches']:
533 signal = {}
534 eMatch = next(m for m in events_data['matches']
535 if m['name'] == eMatches['name'])
Matthew Barth18c91032019-01-29 15:36:00 -0600536 # If service not given, subscribe to signal match
537 if ('service' not in member):
538 signal['match'] = eMatch['name']
539 params = []
540 if ('parameters' in eMatch) and \
541 (eMatch['parameters'] is not None):
542 for p in eMatch['parameters']:
543 params.append(member[str(p)])
544 signal['mparams'] = params
545
Matthew Barthe7d87052018-03-21 13:58:31 -0500546 if ('parameters' in eMatch['signal']) and \
547 (eMatch['signal']['parameters'] is not None):
548 eSignal = eMatch['signal']
549 else:
550 eSignal = next(s for s in events_data['signals']
551 if s['name'] == eMatch['signal'])
Matthew Barth6c050692017-12-05 15:30:09 -0600552 signal['signal'] = eSignal['name']
553 sparams = {}
554 if ('parameters' in eSignal) and \
555 (eSignal['parameters'] is not None):
556 splist = []
557 for p in eSignal['parameters']:
558 sp = str(p)
559 if (sp != 'type'):
560 splist.append(sp)
561 if (sp != 'group'):
562 sparams[sp] = "\"" + member[sp] + "\""
563 else:
564 sparams[sp] = "Group{\n"
565 for m in group:
566 sparams[sp] += (
567 "{\n" +
568 "\"" + str(m['object']) + "\",\n" +
569 "{\"" + str(m['interface']) + "\"," +
570 "\"" + str(m['property']) + "\"}\n" +
571 "},\n")
572 sparams[sp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500573 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600574 sparams[sp] = member[sp]
575 sparams['params'] = splist
576 signal['sparams'] = sparams
577 # Add signal handler
578 eHandler = next(h for h in events_data['handlers']
579 if h['name'] == eSignal['handler'])
580 signal['handler'] = eHandler['name']
581 hparams = {}
582 if ('parameters' in eHandler) and \
583 (eHandler['parameters'] is not None):
584 hplist = []
585 for p in eHandler['parameters']:
586 hp = str(p)
587 if (hp != 'type'):
588 hplist.append(hp)
589 if (hp != 'group'):
590 hparams[hp] = "\"" + member[hp] + "\""
591 else:
592 hparams[hp] = "Group{\n"
593 for m in group:
594 hparams[hp] += (
595 "{\n" +
596 "\"" + str(m['object']) + "\",\n" +
597 "{\"" + str(m['interface']) + "\"," +
598 "\"" + str(m['property']) + "\"}\n" +
599 "},\n")
600 hparams[hp] += "}"
Matthew Barth9f964bf2017-10-02 15:07:00 -0500601 else:
Matthew Barth6c050692017-12-05 15:30:09 -0600602 hparams[hp] = member[hp]
603 hparams['params'] = hplist
604 signal['hparams'] = hparams
605 signals.append(signal)
Matthew Barth67967f92017-09-22 12:43:57 -0500606 precond['pcsigs'] = signals
Matthew Barth9af190c2017-08-08 14:20:43 -0500607
Matthew Barth90149802017-08-15 10:51:37 -0500608 # Add optional action call timer
609 timer = {}
610 interval = "static_cast<std::chrono::seconds>"
611 if ('timer' in event['precondition']) and \
612 (event['precondition']['timer'] is not None):
613 timer['interval'] = (interval +
614 "(" +
615 str(event['precondition']['timer']['interval']) +
616 ")")
617 else:
618 timer['interval'] = (interval +
619 "(" + str(0) + ")")
William A. Kennington III0ce353e2018-10-30 18:30:36 -0700620 timer['type'] = "TimerType::repeating"
Matthew Barth90149802017-08-15 10:51:37 -0500621 precond['pctime'] = timer
622
Matthew Barth9af190c2017-08-08 14:20:43 -0500623 return precond
624
625
Gunnar Millsb751f322017-06-06 15:14:11 -0500626def getEventsInZone(zone_num, zone_conditions, events_data):
Matthew Barthd4d0f082017-05-16 13:51:10 -0500627 """
628 Constructs the event entries defined for each zone using the events yaml
629 provided.
630 """
631 events = []
Matthew Barthba102b32017-05-16 16:13:56 -0500632
Matthew Barthd4d0f082017-05-16 13:51:10 -0500633 if 'events' in events_data:
634 for e in events_data['events']:
Matthew Barthd4d0f082017-05-16 13:51:10 -0500635 event = {}
Matthew Barth9af190c2017-08-08 14:20:43 -0500636 # Add precondition if given
637 if ('precondition' in e) and \
638 (e['precondition'] is not None):
Matthew Barth7f272fd2017-09-12 16:16:56 -0500639 event['pc'] = addPrecondition(zone_num,
640 zone_conditions,
641 e,
642 events_data)
Matthew Barth90149802017-08-15 10:51:37 -0500643 else:
Matthew Barth7f272fd2017-09-12 16:16:56 -0500644 event = getEvent(zone_num, zone_conditions, e, events_data)
645 if not event:
646 continue
Matthew Barthd4d0f082017-05-16 13:51:10 -0500647 events.append(event)
648
649 return events
650
651
Matt Spinler78498c92017-04-11 13:59:46 -0500652def getFansInZone(zone_num, profiles, fan_data):
653 """
654 Parses the fan definition YAML files to find the fans
655 that match both the zone passed in and one of the
656 cooling profiles.
657 """
658
659 fans = []
660
661 for f in fan_data['fans']:
662
663 if zone_num != f['cooling_zone']:
664 continue
665
Gunnar Mills67e95512017-06-02 14:35:18 -0500666 # 'cooling_profile' is optional (use 'all' instead)
Matt Spinler78498c92017-04-11 13:59:46 -0500667 if f.get('cooling_profile') is None:
668 profile = "all"
669 else:
670 profile = f['cooling_profile']
671
672 if profile not in profiles:
673 continue
674
675 fan = {}
676 fan['name'] = f['inventory']
677 fan['sensors'] = f['sensors']
Lei YU069e4402018-01-31 16:47:37 +0800678 fan['target_interface'] = f.get(
679 'target_interface',
680 'xyz.openbmc_project.Control.FanSpeed')
Matt Spinler78498c92017-04-11 13:59:46 -0500681 fans.append(fan)
682
683 return fans
684
685
Matthew Barth7883f582019-02-14 14:24:46 -0600686def getIfacesInZone(zone_ifaces):
687 """
688 Parse given interfaces for a zone for associating a zone with an interface
689 and set any properties listed to defined values upon fan control starting
690 on the zone.
691 """
692
693 ifaces = []
694 for i in zone_ifaces:
695 iface = {}
696 # Interface name not needed yet for fan zones but
697 # may be necessary as more interfaces are extended by the zones
698 iface['name'] = i['name']
699
700 if ('properties' in i) and \
701 (i['properties'] is not None):
702 props = []
703 for p in i['properties']:
704 prop = {}
705 prop['name'] = str(p['name']).lower()
706 prop['type'] = parse_cpp_type(p['type'])
707 vals = []
708 for v in p['values']:
709 val = v['value']
710 if (val is not None):
711 if (isinstance(val, bool)):
712 # Convert True/False to 'true'/'false'
713 val = 'true' if val else 'false'
714 elif (isinstance(val, str)):
715 # Wrap strings with double-quotes
716 val = "\"" + val + "\""
717 vals.append(val)
718 prop['values'] = vals
719 props.append(prop)
720 iface['props'] = props
721 ifaces.append(iface)
722
723 return ifaces
724
725
Gunnar Millsee8a2812017-06-02 14:26:47 -0500726def getConditionInZoneConditions(zone_condition, zone_conditions_data):
727 """
728 Parses the zone conditions definition YAML files to find the condition
729 that match both the zone condition passed in.
730 """
731
732 condition = {}
733
734 for c in zone_conditions_data['conditions']:
735
736 if zone_condition != c['name']:
737 continue
738 condition['type'] = c['type']
739 properties = []
740 for p in c['properties']:
741 property = {}
742 property['property'] = p['property']
743 property['interface'] = p['interface']
744 property['path'] = p['path']
745 property['type'] = p['type'].lower()
746 property['value'] = str(p['value']).lower()
747 properties.append(property)
748 condition['properties'] = properties
749
750 return condition
751
752
753def buildZoneData(zone_data, fan_data, events_data, zone_conditions_data):
Matt Spinler78498c92017-04-11 13:59:46 -0500754 """
755 Combines the zone definition YAML and fan
756 definition YAML to create a data structure defining
757 the fan cooling zones.
758 """
759
760 zone_groups = []
761
762 for group in zone_data:
763 conditions = []
Gunnar Millsee8a2812017-06-02 14:26:47 -0500764 # zone conditions are optional
765 if 'zone_conditions' in group and group['zone_conditions'] is not None:
766 for c in group['zone_conditions']:
767
768 if not zone_conditions_data:
Gunnar Millsb751f322017-06-06 15:14:11 -0500769 sys.exit("No zone_conditions YAML file but " +
Gunnar Millsee8a2812017-06-02 14:26:47 -0500770 "zone_conditions used in zone YAML")
771
772 condition = getConditionInZoneConditions(c['name'],
773 zone_conditions_data)
774
775 if not condition:
776 sys.exit("Missing zone condition " + c['name'])
777
778 conditions.append(condition)
Matt Spinler78498c92017-04-11 13:59:46 -0500779
780 zone_group = {}
781 zone_group['conditions'] = conditions
782
783 zones = []
784 for z in group['zones']:
785 zone = {}
786
Gunnar Mills67e95512017-06-02 14:35:18 -0500787 # 'zone' is required
788 if ('zone' not in z) or (z['zone'] is None):
Matt Spinler78498c92017-04-11 13:59:46 -0500789 sys.exit("Missing fan zone number in " + zone_yaml)
790
791 zone['num'] = z['zone']
792
793 zone['full_speed'] = z['full_speed']
794
Matthew Barth1de66622017-06-12 13:13:02 -0500795 zone['default_floor'] = z['default_floor']
796
Matthew Bartha9561842017-06-29 11:43:45 -0500797 # 'increase_delay' is optional (use 0 by default)
798 key = 'increase_delay'
799 zone[key] = z.setdefault(key, 0)
800
801 # 'decrease_interval' is optional (use 0 by default)
802 key = 'decrease_interval'
803 zone[key] = z.setdefault(key, 0)
804
Gunnar Mills67e95512017-06-02 14:35:18 -0500805 # 'cooling_profiles' is optional (use 'all' instead)
806 if ('cooling_profiles' not in z) or \
Matt Spinler78498c92017-04-11 13:59:46 -0500807 (z['cooling_profiles'] is None):
808 profiles = ["all"]
809 else:
810 profiles = z['cooling_profiles']
811
Matthew Barth7883f582019-02-14 14:24:46 -0600812 # 'interfaces' is optional (no default)
Matthew Barth64099cd2019-02-18 09:43:12 -0600813 ifaces = []
Matthew Barth7883f582019-02-14 14:24:46 -0600814 if ('interfaces' in z) and \
815 (z['interfaces'] is not None):
816 ifaces = getIfacesInZone(z['interfaces'])
817
Matt Spinler78498c92017-04-11 13:59:46 -0500818 fans = getFansInZone(z['zone'], profiles, fan_data)
Gunnar Millsb751f322017-06-06 15:14:11 -0500819 events = getEventsInZone(z['zone'], group['zone_conditions'],
820 events_data)
Matt Spinler78498c92017-04-11 13:59:46 -0500821
822 if len(fans) == 0:
823 sys.exit("Didn't find any fans in zone " + str(zone['num']))
824
Matthew Barth7883f582019-02-14 14:24:46 -0600825 if (ifaces):
826 zone['ifaces'] = ifaces
Matt Spinler78498c92017-04-11 13:59:46 -0500827 zone['fans'] = fans
Matthew Barthd4d0f082017-05-16 13:51:10 -0500828 zone['events'] = events
Matt Spinler78498c92017-04-11 13:59:46 -0500829 zones.append(zone)
830
831 zone_group['zones'] = zones
832 zone_groups.append(zone_group)
833
834 return zone_groups
835
836
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500837if __name__ == '__main__':
838 parser = ArgumentParser(
839 description="Phosphor fan zone definition parser")
840
841 parser.add_argument('-z', '--zone_yaml', dest='zone_yaml',
842 default="example/zones.yaml",
843 help='fan zone definitional yaml')
844 parser.add_argument('-f', '--fan_yaml', dest='fan_yaml',
845 default="example/fans.yaml",
846 help='fan definitional yaml')
Matthew Barthd4d0f082017-05-16 13:51:10 -0500847 parser.add_argument('-e', '--events_yaml', dest='events_yaml',
848 help='events to set speeds yaml')
Gunnar Millsee8a2812017-06-02 14:26:47 -0500849 parser.add_argument('-c', '--zone_conditions_yaml',
850 dest='zone_conditions_yaml',
851 help='conditions to determine zone yaml')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500852 parser.add_argument('-o', '--output_dir', dest='output_dir',
853 default=".",
854 help='output directory')
855 args = parser.parse_args()
856
857 if not args.zone_yaml or not args.fan_yaml:
858 parser.print_usage()
William A. Kennington III3e781062018-10-19 17:18:34 -0700859 sys.exit(1)
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500860
861 with open(args.zone_yaml, 'r') as zone_input:
862 zone_data = yaml.safe_load(zone_input) or {}
863
864 with open(args.fan_yaml, 'r') as fan_input:
865 fan_data = yaml.safe_load(fan_input) or {}
866
Matthew Barthd4d0f082017-05-16 13:51:10 -0500867 events_data = {}
868 if args.events_yaml:
869 with open(args.events_yaml, 'r') as events_input:
870 events_data = yaml.safe_load(events_input) or {}
871
Gunnar Millsee8a2812017-06-02 14:26:47 -0500872 zone_conditions_data = {}
873 if args.zone_conditions_yaml:
874 with open(args.zone_conditions_yaml, 'r') as zone_conditions_input:
875 zone_conditions_data = yaml.safe_load(zone_conditions_input) or {}
876
Matt Spinleree7f6422017-05-09 11:03:14 -0500877 zone_config = buildZoneData(zone_data.get('zone_configuration', {}),
Gunnar Millsee8a2812017-06-02 14:26:47 -0500878 fan_data, events_data, zone_conditions_data)
Matt Spinleree7f6422017-05-09 11:03:14 -0500879
880 manager_config = zone_data.get('manager_configuration', {})
881
882 if manager_config.get('power_on_delay') is None:
883 manager_config['power_on_delay'] = 0
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500884
Matthew Barth702c4a52018-02-28 16:23:11 -0600885 tmpls_dir = os.path.join(
886 os.path.dirname(os.path.realpath(__file__)),
887 "templates")
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500888 output_file = os.path.join(args.output_dir, "fan_zone_defs.cpp")
Matthew Barth702c4a52018-02-28 16:23:11 -0600889 if sys.version_info < (3, 0):
890 lkup = TemplateLookup(
891 directories=tmpls_dir.split(),
892 disable_unicode=True)
893 else:
894 lkup = TemplateLookup(
895 directories=tmpls_dir.split())
896 tmpl = lkup.get_template('fan_zone_defs.mako.cpp')
Matt Spinlerd08dbe22017-04-11 13:52:54 -0500897 with open(output_file, 'w') as output:
Matthew Barth702c4a52018-02-28 16:23:11 -0600898 output.write(tmpl.render(zones=zone_config,
899 mgr_data=manager_config))