硬件自发现解析流程
硬件自发现主要分为两大流程:加载和解析。加载流程详见《硬件自发现CSR选择策略》文档,本文主要介绍自发现加载到CSR数据后如何解析数据,以及数据在每一个流程中会发生什么样的变化。
前言
在介绍整体的解析流程前,先简单介绍解析流程中会用到的数据以及它们在代码中的命名。
整个解析流程主要需要两个对象数据:解析后的Connector的数据和下级CSR数据。解析后的Connector的数据将会作为下一级CSR解析的上下文,辅助下级CSR解析。
自发现解析总体流程
自发现解析流程总体分为五步:match(匹配连接器与总线)→ rename(对象重命名)→ append(天池标准组件生成EEPROM对象及引用关系)→ arrange(对象排列)→ analyse(对象解析)。下面将按顺序逐步介绍解析的细节。
注意:解析platform.sr不包含match和append步骤。
1. match 匹配连接器与总线
简要介绍:
这一步主要是将Connector里的Buses和Connector对应的CSR的Buses建立映射关系,不会修改CSR数据。
主要步骤有校验Anchor数据和建立buses映射表。
1.1 校验Anchor数据
在建立映射关系前,会先校验CSR内是否配置了ManagementTopology:{Anchor:{Buses:{xxx}}},如果没有配置,则终止解析并抛出错误(可以配空,但不能不配)。
然后校验上级Connector里的Buses数量和CSR里Buses数量是否一致,不一致则会打印WARNING级别日志:position: %s, unmatched anchor buses, parent number: %d, current number: %d,并继续解析。
注意:如果是解析root.sr则不存在上级Connector,Buses映射表实际是自己映射到自己。
1.2 建立buses映射表
CSR和上级Connector的Buses可以不一致,因此分3种情况介绍映射表建立。
假设CSR配置为:
"ManagementTopology": {
"Anchor": {
"Buses": [
"I2c_1",
"I2c_2",
"I2c_3"
]
}
}1.2.1 CSR和上级Connector的Buses数量一致
假设上级Connector配置为:
"Connector_EXU_1": {
"Buses": [
"I2c_A",
"I2c_B",
"I2c_C"
]
}数量一致,按顺序将Connector和CSR的Buses一一映射,存于buses_map中,用于后续解析。
buses_map = {
I2c_1 = 'I2c_A',
I2c_2 = 'I2c_B',
I2c_3 = 'I2c_C',
}1.2.2 上级Connector的Buses数量大
假设上级Connector配置为:
"Connector_EXU_1": {
"Buses": [
"I2c_A",
"I2c_B",
"I2c_C",
"I2c_D"
]
}数量不一致且上级Connector的Buses数量大,按顺序将上级Connector和CSR的Buses一一映射,存于buses_map中,用于后续解析。上级Connector内多余的总线不记录于映射表中。这种情况建议修改上级Connector,保持和下级CSR一致。
buses_map = {
I2c_1 = 'I2c_A',
I2c_2 = 'I2c_B',
I2c_3 = 'I2c_C',
}1.2.3 CSR的Buses数量大
假设上级Connector配置为:
"Connector_EXU_1": {
"Buses": [
"I2c_A"
]
}数量不一致且CSR的Buses数量大,按顺序将上级Connector和CSR的Buses一一映射,存于buses_map中,用于后续解析。CSR内多余的总线记录于extra_buses中,CSR内配置于多余总线下的Chip和Connector对象在后续解析时被丢弃,进而导致相关的所有对象都会不解析,其他组件也将拿不到对应的对象。这种情况建议修改CSR,保证上级Connector和下级CSR总线一致。
buses_map = {
I2c_1 = 'I2c_A'
}extra_buses = {'I2C_2', 'I2C_3', 'I2C_4', 'I2C_5', 'I2C_6'}2. rename 对象重命名
简要介绍:
从这一步开始会逐步修改CSR数据。
这一步主要是将CSR内Object里的对象、对象属性同步、引用的其他对象以及需要重命名的下级属性加上Position后缀,并在bus_map中将CSR内ManagementTopology里Chip底下的Buses添加进去,即把通过9545之类分路出来的新bus对象添加进映射表里。
主要步骤有重命名topology拓扑结构、重命名objects对象结构和重命名对象属性。
2.1 重命名topology拓扑结构
这一步主要是重命名ManagementTopology下的Chip、Connector对象,添加Position后缀,将9545之类分路出来的新bus对象添加进映射表里,且通过上级Connector传递下来的bus对象不添加Position后缀;分路出的新对象添加Position后缀;以及根据buses_map替换Connector里的Buses(假设Connector的Position为01)。
假设CSR的ManagementTopology和Connector为:
"ManagementTopology": {
"Anchor": {
"Buses": [
"I2c_2"
]
},
"I2c_2": {
"Chips": [
"Pca9545_i2c2_chip"
],
"Connectors": [
"Connector_Exu_1"
]
},
"Pca9545_i2c2_chip": {
"Buses": [
"I2cMux_pca9545_chan1",
"I2cMux_pca9545_chan2"
]
}
},
"Connector_Exu_1": {
"Buses": [
"I2cMux_pca9545_chan1",
"I2c_2"
]
}match传递下来的buses_map为:
buses_map = {
I2c_2 = 'I2c_A'
}那么,经过重命名后,ManagementTopology、Connector和buses_map分别为:
"ManagementTopology": {
"Anchor": {
"Buses": [
"I2c_A"
]
},
"I2c_A": {
"Chips": [
"Pca9545_i2c2_chip_01"
],
"Connectors": [
"Connector_Exu_1_01"
]
},
"Pca9545_i2c2_chip_01": {
"Buses": [
"I2cMux_pca9545_chan1_01",
"I2cMux_pca9545_chan2_01"
]
}
},
"Connector_Exu_1": {
"Buses": [
"I2cMux_pca9545_chan1_01",
"I2c_A"
]
}buses_map = {
I2c_2 = 'I2c_A',
I2cMux_pca9545_chan1 = 'I2cMux_pca9545_chan1_01',
I2cMux_pca9545_chan2 = 'I2cMux_pca9545_chan2_01'
}2.2 重命名objects对象结构
这一步主要是重命名Object下对象以及对象属性引用、同步的对象,添加Position后缀,并将上级Connector对象的属性值作为参数,替换下级加载组件的CSR中对象的变量。
目前支持替换的动态参数有:${Slot}、${SystemId}、${ManagerId}、${Container}、${GroupId}、${ChassisId}、${GroupPosition}、${SilkText}、${DataVersion}、${FormatVersion}。
假设CSR里Object对象为:
"Connector_Exu_1": {
"Bom": "14100513",
"Slot": "\${GroupId}",
"Position": 1,
"Presence": 0,
"Id": "#/NewClass_1.NewProperty1",
"AuxId": "<=/NewClass_1.NewProperty2",
"Buses": [
"I2cMux_pca9545_chan1_01",
"I2c_2"
],
"SystemId": "<=/::Connector_Exu_2.SystemId",
"ManagerId": "<=/Pca9545_i2c2_chip.HealthStatus",
"SilkText": "#/Connector_Exu_1.Bom",
"IdentifyMode": 2,
"@Default": {
"ManagerId": "0"
},
"NewProperty1": "#/NewClass_1.NewProperty1",
"NewProperty2": "<=/NewClass_1.NewProperty2",
"NewProperty3": "#/Connector_Exu_1.Bom",
"NewProperty4": "<=/Connector_Exu_1.Bom"
}经过重命名后,对象数据变为:
"Connector_Exu_1_01": {
"Bom": "14100513",
"Slot": 2,
"Position": 1,
"Presence": 0,
"Id": "#/NewClass_1_01.NewProperty1",
"AuxId": "<=/NewClass_1_01.NewProperty2",
"Buses": [
"I2cMux_pca9545_chan1_01",
"I2c_2"
],
"SystemId": "<=/::Connector_Exu_2_01.SystemId",
"ManagerId": "<=/Pca9545_i2c2_chip_01.HealthStatus",
"SilkText": "#/Connector_Exu_2_01.Bom",
"IdentifyMode": 2,
"@Default": {
"ManagerId": "0"
},
"NewProperty1": "#/NewClass_1_01.NewProperty1",
"NewProperty2": "<=/NewClass_1_01.NewProperty2",
"NewProperty3": "#/Connector_Exu_2_01.Bom",
"NewProperty4": "<=/Connector_Exu_2_01.Bom"
}2.3 重命名对象属性
这一步主要是重命名的下级属性,目前仅支持重命名SmcDfxInfo对象下Mapping里的对象名。
假设SmcDfxInfo对象配置为:
"SmcDfxInfo_exu": {
"Chip": "#/Smc_ExpBoardSMC",
"Offset": 7424,
"Size": 2,
"Period": 400,
"SmcVersion": 1,
"Config": {
"1": {"cpld_ver": 255},
"2": {"test_for_dxf": 1}
},
"Mapping": {
"Scanner_Dftenabled": {"Value": "expr($test_for_dxf)"}
}
}重命名后,对象数据变为:
"SmcDfxInfo_exu_01": {
"Chip": "#/Smc_ExpBoardSMC_01",
"Offset": 7424,
"Size": 2,
"Period": 400,
"SmcVersion": 1,
"Config": {
"1": {"cpld_ver": 255},
"2": {"test_for_dxf": 1}
},
"Mapping": {
"Scanner_Dftenabled_01": {"Value": "expr($test_for_dxf)"}
}
}3. append 天池标准组件生成EEPROM对象及引用关系
简要介绍:
这一步主要是为天池标准组件的Connector生成对应的EEPROM对象,用于加载下级CSR时可以根据这个EEPROM对象读取对应的EEPROM,从而读取对应的CSR信息。
根据Connector配置于哪路总线下,对应的EEPROM也会生成在哪路总线下,并为Connector添加引用关系。并对Connector对象数据预处理,添加GroupId和GroupPosition(唯一标识)。
假设CSR配置为:
"ManagementTopology": {
"Anchor": {
"Buses": [
"I2c_2"
]
},
"I2c_2": {
"Connectors": [
"Connector_Eep_1"
]
}
}
"Connector_Eep_1": {
"Bom": "14100513",
"Slot": 1,
"Position": 2,
"Presence": 0,
"Id": "",
"AuxId": "",
"Buses": [
"I2c_2"
],
"SystemId": 1,
"IdentifyMode": 3
}添加对象及引用关系后,对象数据变为:
"ManagementTopology": {
"Anchor": {
"Buses": [
"I2c_2"
]
},
"I2c_2": {
"Connectors": [
"Connector_Eep_1"
],
"Chips": [
"Eeprom_3_2_01"
]
}
},
"Connector_Eep_1": {
"Bom": "14100513",
"Slot": 1,
"Position": 2,
"Presence": 0,
"Id": "",
"AuxId": "",
"Buses": [
"I2c_2"
],
"SystemId": 1,
"IdentifyMode": 3,
"CSRVersion": "#/Eeprom_3_2_01.CSRVersion",
"Chip": "#/Eeprom_3_2_01",
"GroupId": 2,
"GroupPosition": "0102"
},
"Eeprom_3_2_01": {
"OffsetWidth": 2,
"Address": 174,
"AddrWidth": 1,
"RwBlockSize": 32,
"WriteInterval": 20,
"ReadTmout": 100,
"WriteTmout": 100
}4. arrange 对象排列
简要介绍:
这一步主要检查CSR对象中是否存在不合理的配置,检查内容包括对象存在多个父层关系,对象属性同步/引用自身,对象属性同步/引用、对象引用成环,这些不合理的配置都会导致CSR解析失败。下面将举例说明这些不合理的配置。
4.1 对象存在多个父层关系
CSR配置举例,一个对象配置两个不同的@Parent,这种配置是不合理的。
"Objects": {
"ParentTest_1": {},
"ParentTest_2": {},
"ChildTest_2": {
"@Parent": "ParentTest_1",
"@Parent": "ParentTest_2",
"Test": 1
}
}4.2 对象属性引用自身
CSR配置举例,对象属性同步/引用自己,这个配置是不合理的。
"Objects": {
"ObjectA": {
"Relation": "#/ObjectA.Relation"
}
}4.3 对象同步/引用关系存在环
CSR配置举例,对象属性同步/引用、对象引用成环,这些配置是不合理的。
"Objects": {
"ObjectA": {
"Relation": "#/ObjectB"
},
"ObjectB": {
"Relation": "#/ObjectC"
},
"ObjectC": {
"Relation": "#/ObjectA"
},
"SMC_2": {
"ChipModel": "SMC",
"Address": 96
},
"Scanner_SMC_2_0": {
"Reading": "<=/Scanner_SMC_2_1.Value | <=/Scanner_SMC_2_2.Value"
},
"Scanner_SMC_2_1": {
"Chip": "#/SMC_2",
"Size": 2,
"Value": "<=/Scanner_SMC_2_3.Value"
},
"Scanner_SMC_2_2": {
"Chip": "#/SMC_2",
"Size": 2,
"Value": "<=/Scanner_SMC_2_1.Value"
},
"Scanner_SMC_2_3": {
"Chip": "#/SMC_2",
"Size": 2,
"Value": "<=/Scanner_SMC_2_0.Reading"
}
}5. analyse 对象解析
简要介绍:
这一步主要是将CSR对象解析成代码可理解的形式,并为每个对象打上所属组件标记等。
5.1 删除非法总线下的Chip对象
根据总线匹配时匹配到的非法总线(extra_buses),将非法总线下的Chip和Connector对象剔除出解析队列。
5.2 预解析对象
这里主要是生成object_info和object_child两个表,object_info记录每个对象的app、object_name和path(私有对象没有path)。
比如Connector_Eep_1_01,对应的object_info如下:
"object_info" = {
"Connector_Eep_1_01" = {
"app" = "hwdiscovery",
"object_name" = "Connector_Eep_1_01",
"path" = "/bmc/kepler/Connector/Connector_Eep_1_01"
}
}object_child记录对象的层级关系,这个是配置了@Parent属性才会用到。
如果对象所属的app不存在,或者具有层级的对象的上层对象不存在,则会忽略该对象,将对象剔除出解析队列。
5.3 解析对象
这一步主要处理属性的同步、引用和表达式语法和封装对象,将对象的各种语法解析为软件可以理解的语言,在后续框架处理时可以根据解析的结果处理属性,并将对象进一步封装,方便使用。
5.3.1 同步属性
假设CSR配置为:
"Object_A": {
"Presence": "<=/Scanner_A.Value"
}经过解析后:
"Object_A_01": {
"Presence": "$sync:{\"properties\":[{\"Service\": \"bmc.kepler.hwproxy\", \"Path\": \"/bmc/kepler/Scanner/Scanner_A_01\", \"Interface\": \"bmc.kepler.Scanner\", \"Property\": \"Value\"}]}"
}$sync表示属性值同步其他属性,同步对象为hwproxy组件下Scanner_A_01对象的bmc.kepler.Scanner接口下的Value属性,当被同步的属性发生变化后,框架会更新同步属性的值。
5.3.2 引用属性
假设CSR配置为:
"Object_A": {
"CSRVersion": "#/Eeprom_A.CSRVersion"
}经过解析后:
"Object_A_01": {
"CSRVersion": "$ref:{\"properties\":[{\"Service\": \"bmc.kepler.hwproxy\", \"Path\": \"/bmc/kepler/Chip/Eeprom/Eeprom_A_01\", \"Interface\": \"bmc.kepler.EepromData\", \"Property\": \"CSRVersion\"}]}"
}$ref表示属性值引用其他属性,引用对象为hwproxy组件下Eeprom_A_01对象的bmc.kepler.EepromData接口下的CSRVersion属性,引用属性只有主动访问属性才会更新属性。
5.3.3 引用对象
假设CSR配置为:
"Object_A": {
"Chip": "#/Eeprom_A"
}经过解析后:
"Object_A_01": {
"Chip": "$ref:{\"Service\": \"bmc.kepler.hwproxy\", \"Path\": \"/bmc/kepler/Chip/Eeprom/Eeprom_A_01\"}"
}$ref表示属性值引用其他对象,引用对象为hwproxy组件下Eeprom_A_01对象。
5.3.4 表达式语法
假设CSR配置为:
"Object_A": {
"Reading": "<=/CPU_1.MaxMemoryTemperatureCelsius |> expr($1 == 32768 ? 1 : 0)",
"Slot": "100 |> expr($1 * 2 + 2) |> expr(($1 + 1) * 2) |> expr(($1 + 1) * 2)",
"Presence": "#/Accessor_Test.Size |> expr(($1 + 1) * 2) |> expr(($1 + 1) * 2)"
}经过解析后:
"Object_A_01": {
"Reading": "$sync:{\"properties\":[{\"Service\": \"bmc.kepler.compute\", \"Path\": \"/bmc/kepler/Systems/1/Processors/CPU/CPU_1_01\", \"Interface\": \"bmc.kepler.Systems.Processor.CPU\", \"Property\": \"MaxMemoryTemperatureCelsius\"}],\"expressions\":[\"expr($1 == 32768 ? 1 : 0)\"]}",
"Slot": "$const:{\"properties\":[100],\"expressions\":[\"expr($1 * 2 + 2)\", \"expr(($1 + 1) * 2)\", \"expr(($1 + 1) * 2)\"]}",
"Presence": "$ref:{\"properties\":[{\"Path\": \"/bmc/kepler/Accessor/Accessor_Test\", \"Interface\": \"bmc.kepler.Accessor\", \"Property\": \"Size\"}],\"expressions\":[\"expr(($1 + 1) * 2)\", \"expr(($1 + 1) * 2)\"]}"
}表达式语法的解析主要是将多个表达式收集起来,存于expressions字段中,方便后续框架计算表达式。解析时如果遇到同步、引用语法就根据上面介绍的方式解析。
5.3.5 对象封装
将上面预解析和解析的结果归一,封装到对象数据,存在表里。
假设CSR配置为:
"Connector_Exu_1": {
"Bom": "14100513",
"Slot": 1,
"Position": 1,
"Presence": 0,
"AuxId": "",
"SystemId": 1,
"ManagerId": "2",
"SilkText": "EXU",
"IdentifyMode": 3
}经过解析后的Lua数据:
{
app_name = 'hwdiscovery',
class_name = 'Connector',
object_name = 'Connector_Exu_1_01',
object_props = {
AuxId = '',
Bom = '14100513',
Chip = '$ref:{"Service": "bmc.kepler.hwproxy", "Path": "/bmc/kepler/Eeprom/Eeprom_3_1_01"}',
Id = '',
Position = 1,
Presence = 0,
SilkText = 'EXU',
Slot = 1,
SystemId = 1,
ManagerId = '2',
IdentifyMode = 3,
GroupId = 5,
GroupPosition = '0101'
},
path = '/bmc/kepler/Connector/Connector_Exu_1_01',
extra_props = {
Path = '/bmc/kepler/Connector/Connector_Exu_1_01',
},
position = '01'
}5.4 对象解析后处理
这里主要是将上面解析完的一个个对象整合到一个表内,作为对象组数据。若是框架对象则会在extra_props添加"Framework = true"标记。
假设CSR配置为:
"Objects": {
"Connector_Exu_1": {
"Bom": "14100513",
"Slot": 1,
"Position": 1,
"Presence": 0,
"AuxId": "",
"SystemId": 1,
"ManagerId": "2",
"SilkText": "EXU",
"IdentifyMode": 3
},
"Eeprom_3_100_1": {
"AddrWidth": 1,
"Address": 174,
"OffsetWidth": 2,
"ReadTmout": 100,
"WriteTmout": 100,
"RwBlockSize": 32,
"WriteInterval": 20
}
}经过解析后的Lua数据:
{
[1] = {
app_name = 'hwproxy',
class_name = 'Eeprom',
object_name = 'Eeprom_3_1_01',
object_props = {
AddrWidth = 1,
Address = 174,
OffsetWidth = 2,
ReadTmout = 100,
WriteTmout = 100,
RwBlockSize = 32,
WriteInterval = 20
},
path = '/bmc/kepler/Eeprom/Eeprom_3_1_01',
extra_props = {
Path = '/bmc/kepler/Eeprom/Eeprom_3_1_01',
Framework = true
},
position = '01'
},
[2] = {
app_name = 'hwdiscovery',
class_name = 'Connector',
object_name = 'Connector_Exu_1_01',
object_props = {
AuxId = '',
Bom = '14100513',
Chip = '$ref:{"Service": "bmc.kepler.hwproxy", "Path": "/bmc/kepler/Eeprom/Eeprom_3_1_01"}',
Id = '',
Position = 1,
Presence = 0,
SilkText = 'EXU',
Slot = 1,
SystemId = 1,
ManagerId = '2',
IdentifyMode = 3,
GroupId = 5,
GroupPosition = '0101'
},
path = '/bmc/kepler/Connector/Connector_Exu_1_01',
extra_props = {
Path = '/bmc/kepler/Connector/Connector_Exu_1_01',
Framework = true
},
position = '0005'
}
}至此,CSR数据从原始配置到最终提供给组件的数据,中间经历的解析流程全部梳理完毕,组件收到对象后可以根据业务使用对象。