228 lines
15 KiB
Go
228 lines
15 KiB
Go
package platform
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
)
|
||
|
||
// gb32960FieldReference is the platform copy of the authoritative Chinese
|
||
// terminology supplied for GB/T 32960 and the Guangdong fuel-cell extension.
|
||
// It enriches protocol values at read time without changing the stored RAW
|
||
// evidence or replacing the parser's stable source-field keys.
|
||
type gb32960FieldReference struct {
|
||
Label string
|
||
Unit string
|
||
Description string
|
||
ValueMappings []MetricValueMapping
|
||
}
|
||
|
||
func enumMappings(values ...string) []MetricValueMapping {
|
||
result := make([]MetricValueMapping, 0, len(values)/3)
|
||
for index := 0; index+2 < len(values); index += 3 {
|
||
result = append(result, MetricValueMapping{Value: values[index], Label: values[index+1], Description: values[index+2]})
|
||
}
|
||
return result
|
||
}
|
||
|
||
var gb32960PositionMappings = func() []MetricValueMapping {
|
||
result := make([]MetricValueMapping, 0, 8)
|
||
for value := 0; value < 8; value++ {
|
||
parts := []string{"有效定位", "北纬", "东经"}
|
||
if value&1 != 0 {
|
||
parts[0] = "无效定位"
|
||
}
|
||
if value&2 != 0 {
|
||
parts[1] = "南纬"
|
||
}
|
||
if value&4 != 0 {
|
||
parts[2] = "西经"
|
||
}
|
||
result = append(result, MetricValueMapping{
|
||
Value: strconv.Itoa(value),
|
||
Label: strings.Join(parts, " · "),
|
||
Description: "bit0 表示定位有效性,bit1 表示纬度方向,bit2 表示经度方向。",
|
||
})
|
||
}
|
||
return result
|
||
}()
|
||
|
||
var gb32960FieldReferences = map[string]gb32960FieldReference{
|
||
"gb32960.vehicle.vehicle_status": {Label: "车辆状态", Description: "车辆运行、停止等状态编码。", ValueMappings: enumMappings("1", "启动", "车辆处于可行驶启动状态。", "2", "熄火", "车辆处于熄火状态。", "3", "其他", "车辆处于其他状态。", "254", "异常", "协议异常值。", "255", "无效", "协议无效值。")},
|
||
"gb32960.vehicle.charge_status": {Label: "充电状态", Description: "车辆充电状态编码。", ValueMappings: enumMappings("1", "停车充电", "车辆停车充电。", "2", "行驶充电", "车辆行驶充电。", "3", "未充电", "车辆未充电。", "4", "充电完成", "车辆充电完成。", "254", "异常", "协议异常值。", "255", "无效", "协议无效值。")},
|
||
"gb32960.vehicle.running_mode": {Label: "运行模式", Description: "纯电、混合动力、燃料电池等运行模式编码。", ValueMappings: enumMappings("1", "纯电", "纯电驱动模式。", "2", "混合动力", "混合动力驱动模式。", "3", "燃料电池", "燃料电池驱动模式。", "254", "异常", "协议异常值。", "255", "无效", "协议无效值。")},
|
||
"gb32960.vehicle.speed_kmh": {Label: "车速", Unit: "km/h", Description: "车辆当前速度。"},
|
||
"gb32960.vehicle.total_mileage_km": {Label: "累计里程", Unit: "km", Description: "车辆累计行驶里程。"},
|
||
"gb32960.vehicle.total_voltage_v": {Label: "总电压", Unit: "V", Description: "动力系统总电压。"},
|
||
"gb32960.vehicle.total_current_a": {Label: "总电流", Unit: "A", Description: "动力系统总电流。"},
|
||
"gb32960.vehicle.soc_percent": {Label: "SOC", Unit: "%", Description: "动力电池荷电状态。"},
|
||
"gb32960.vehicle.dc_dc_status": {Label: "DC/DC 状态", Description: "DC/DC 工作状态编码。", ValueMappings: enumMappings("1", "工作", "DC/DC 处于工作状态。", "2", "断开", "DC/DC 处于断开状态。", "254", "异常", "协议异常值。", "255", "无效", "协议无效值。")},
|
||
"gb32960.vehicle.gear": {Label: "挡位原始值", Description: "挡位、驱动力和制动力组合原始编码。"},
|
||
"gb32960.vehicle.insulation_kohm": {Label: "绝缘电阻", Unit: "kΩ", Description: "高压系统绝缘电阻。"},
|
||
"gb32960.vehicle.accelerator_pct": {Label: "加速踏板行程", Unit: "%", Description: "2016 版字段,加速踏板开度。"},
|
||
"gb32960.vehicle.brake_pct": {Label: "制动踏板状态", Unit: "%", Description: "2016 版字段,制动踏板状态或开度。"},
|
||
|
||
"gb32960.drive_motor.serial_no": {Label: "驱动电机序号", Description: "电机编号。"},
|
||
"gb32960.drive_motor.state": {Label: "驱动电机状态", Description: "电机工作状态编码。", ValueMappings: enumMappings("1", "耗电", "驱动电机耗电。", "2", "发电", "驱动电机发电。", "3", "关闭", "驱动电机关闭。", "4", "准备", "驱动电机准备。", "254", "异常", "协议异常值。", "255", "无效", "协议无效值。")},
|
||
"gb32960.drive_motor.controller_temperature_c": {Label: "控制器温度", Unit: "℃", Description: "驱动电机控制器温度。"},
|
||
"gb32960.drive_motor.speed_rpm": {Label: "转速", Unit: "rpm", Description: "驱动电机转速。"},
|
||
"gb32960.drive_motor.torque_nm": {Label: "转矩", Unit: "N·m", Description: "驱动电机输出转矩。"},
|
||
"gb32960.drive_motor.motor_temperature_c": {Label: "电机温度", Unit: "℃", Description: "驱动电机温度。"},
|
||
"gb32960.drive_motor.controller_voltage_v": {Label: "控制器输入电压", Unit: "V", Description: "电机控制器直流母线输入电压。"},
|
||
"gb32960.drive_motor.controller_current_a": {Label: "控制器直流母线电流", Unit: "A", Description: "电机控制器直流母线电流。"},
|
||
|
||
"gb32960.fuel_cell.fuel_cell_voltage_v": {Label: "燃料电池电压", Unit: "V", Description: "燃料电池系统输出电压。"},
|
||
"gb32960.fuel_cell.fuel_cell_current_a": {Label: "燃料电池电流", Unit: "A", Description: "燃料电池系统输出电流。"},
|
||
"gb32960.fuel_cell.hydrogen_consumption_kg_per_100km": {Label: "氢耗", Unit: "kg/100km", Description: "燃料电池氢气消耗率。"},
|
||
"gb32960.fuel_cell.temperature_probe_values_c": {Label: "探针温度", Unit: "℃", Description: "燃料电池温度探针列表。"},
|
||
"gb32960.fuel_cell.max_hydrogen_temperature_c": {Label: "氢系统最高温度", Unit: "℃", Description: "氢系统最高温度。"},
|
||
"gb32960.fuel_cell.max_hydrogen_concentration_percent": {Label: "最高氢浓度", Unit: "%", Description: "氢气浓度最高值。"},
|
||
"gb32960.fuel_cell.max_hydrogen_pressure_mpa": {Label: "最高氢压力", Unit: "MPa", Description: "氢系统最高压力。"},
|
||
"gb32960.fuel_cell.dc_dc_status": {Label: "高压 DC/DC 状态", Description: "燃料电池高压 DC/DC 状态。"},
|
||
"gb32960.fuel_cell.max_hydrogen_temperature_probe_id": {Label: "最高氢温探针编号", Description: "检测到氢系统最高温度的探针编号。"},
|
||
"gb32960.fuel_cell.max_hydrogen_concentration_probe_id": {Label: "最高氢浓度探针编号", Description: "检测到最高氢浓度的探针编号。"},
|
||
"gb32960.fuel_cell.max_hydrogen_pressure_probe_id": {Label: "最高氢压探针编号", Description: "检测到最高氢压力的探针编号。"},
|
||
|
||
"gb32960.position.position_status": {Label: "定位状态标志", Description: "定位有效性及经纬度方向标志。", ValueMappings: gb32960PositionMappings},
|
||
"gb32960.position.coordinate_system": {Label: "坐标系", Description: "2025 版新增坐标系编码。", ValueMappings: enumMappings("1", "WGS84", "WGS84 坐标系。", "2", "GCJ-02", "GCJ-02 坐标系。", "3", "其他", "其他坐标系。")},
|
||
"gb32960.position.longitude": {Label: "经度", Unit: "°", Description: "车辆经度。"},
|
||
"gb32960.position.latitude": {Label: "纬度", Unit: "°", Description: "车辆纬度。"},
|
||
|
||
"gb32960.alarm.max_alarm_level": {Label: "最高报警等级", Description: "当前最高报警等级。", ValueMappings: enumMappings("0", "无故障", "当前无报警故障。", "1", "一级故障", "一级报警故障。", "2", "二级故障", "二级报警故障。", "3", "三级故障", "三级报警故障。")},
|
||
"gb32960.alarm.general_alarm_flag": {Label: "通用报警标志", Description: "通用报警位图。"},
|
||
"gb32960.alarm.battery_faults": {Label: "可充电储能装置故障码", Description: "动力电池相关故障码列表。"},
|
||
"gb32960.alarm.motor_faults": {Label: "驱动电机故障码", Description: "驱动电机相关故障码列表。"},
|
||
"gb32960.alarm.engine_faults": {Label: "发动机故障码", Description: "发动机相关故障码列表。"},
|
||
"gb32960.alarm.other_faults": {Label: "其他故障码", Description: "其他故障码列表。"},
|
||
|
||
"gb32960.gd_fc_stack.stack_count": {Label: "电堆数量", Description: "本包包含的电堆数量。"},
|
||
"gb32960.gd_fc_stack.engine_work_state": {Label: "发动机工作状态", Description: "燃料电池发动机工作状态。"},
|
||
"gb32960.gd_fc_stack.stack_water_outlet_temp_c": {Label: "电堆出水温度", Unit: "℃", Description: "电堆冷却水出口温度。"},
|
||
"gb32960.gd_fc_stack.hydrogen_inlet_pressure_kpa": {Label: "氢气入口压力", Unit: "kPa", Description: "电堆氢气入口压力。"},
|
||
"gb32960.gd_fc_stack.air_inlet_pressure_kpa": {Label: "空气入口压力", Unit: "kPa", Description: "电堆空气入口压力。"},
|
||
"gb32960.gd_fc_stack.air_inlet_temp_c": {Label: "空气入口温度", Unit: "℃", Description: "电堆空气入口温度。"},
|
||
"gb32960.gd_fc_stack.max_cell_voltage_v": {Label: "单体最高电压", Unit: "V", Description: "本电堆单体最高电压。"},
|
||
"gb32960.gd_fc_stack.min_cell_voltage_v": {Label: "单体最低电压", Unit: "V", Description: "本电堆单体最低电压。"},
|
||
"gb32960.gd_fc_stack.avg_cell_voltage_v": {Label: "单体平均电压", Unit: "V", Description: "本电堆单体平均电压。"},
|
||
"gb32960.gd_fc_stack.cell_count": {Label: "单体总数", Description: "电堆单体总数量。"},
|
||
"gb32960.gd_fc_stack.frame_cell_start": {Label: "本帧单体起始序号", Description: "当前分段电压起始单体序号。"},
|
||
"gb32960.gd_fc_stack.frame_cell_count": {Label: "本帧单体数量", Description: "当前分段包含的单体数量。"},
|
||
"gb32960.gd_fc_stack.frame_cell_voltages_v": {Label: "本帧单体电压列表", Unit: "V", Description: "当前分段的单体电压数组;未跨帧合并时不代表完整电堆快照。"},
|
||
|
||
"gb32960.gd_fc_dcdc.input_voltage_v": {Label: "输入电压", Unit: "V", Description: "DC/DC 输入电压。"},
|
||
"gb32960.gd_fc_dcdc.input_current_a": {Label: "输入电流", Unit: "A", Description: "DC/DC 输入电流。"},
|
||
"gb32960.gd_fc_dcdc.output_voltage_v": {Label: "输出电压", Unit: "V", Description: "DC/DC 输出电压。"},
|
||
"gb32960.gd_fc_dcdc.output_current_a": {Label: "输出电流", Unit: "A", Description: "DC/DC 输出电流。"},
|
||
"gb32960.gd_fc_dcdc.controller_temp_c": {Label: "控制器温度", Unit: "℃", Description: "DC/DC 控制器温度。"},
|
||
|
||
"gb32960.gd_fc_air_conditioner.status": {Label: "空调状态", Description: "空调工作状态。", ValueMappings: enumMappings("0", "关闭", "空调关闭。", "1", "启动", "空调启动。", "254", "异常", "协议异常值。", "255", "无效", "协议无效值。")},
|
||
"gb32960.gd_fc_air_conditioner.power_kw": {Label: "空调功率", Unit: "kW", Description: "空调消耗功率。"},
|
||
"gb32960.gd_fc_air_conditioner.compressor_input_voltage_v": {Label: "压缩机输入电压", Unit: "V", Description: "空调压缩机输入电压。"},
|
||
|
||
"gb32960.gd_fc_vehicle_info.collision_alarm": {Label: "碰撞报警", Description: "碰撞报警状态。", ValueMappings: enumMappings("0", "无碰撞报警", "当前无碰撞报警。", "1", "有碰撞报警", "当前有碰撞报警。", "254", "异常", "协议异常值。", "255", "无效", "协议无效值。")},
|
||
"gb32960.gd_fc_vehicle_info.ambient_temp_c": {Label: "环境温度", Unit: "℃", Description: "车辆环境温度。"},
|
||
"gb32960.gd_fc_vehicle_info.ambient_pressure_kpa": {Label: "环境压力", Unit: "kPa", Description: "车辆环境压力。"},
|
||
"gb32960.gd_fc_vehicle_info.hydrogen_mass_kg": {Label: "车载氢量", Unit: "kg", Description: "当前车载氢气质量。"},
|
||
}
|
||
|
||
func canonicalGB32960ReferenceKey(sourceField string) string {
|
||
sourceField = strings.ToLower(strings.TrimSpace(sourceField))
|
||
if !strings.HasPrefix(sourceField, "gb32960.") {
|
||
return sourceField
|
||
}
|
||
if strings.HasPrefix(sourceField, "gb32960.drive_motor.") {
|
||
return "gb32960.drive_motor." + lastSourcePart(sourceField)
|
||
}
|
||
if strings.HasPrefix(sourceField, "gb32960.gd_fc_stack.") {
|
||
return "gb32960.gd_fc_stack." + lastSourcePart(sourceField)
|
||
}
|
||
return sourceField
|
||
}
|
||
|
||
func lastSourcePart(sourceField string) string {
|
||
if index := strings.LastIndexByte(sourceField, '.'); index >= 0 && index+1 < len(sourceField) {
|
||
return sourceField[index+1:]
|
||
}
|
||
return sourceField
|
||
}
|
||
|
||
func gb32960ReferenceForSource(sourceField string) (gb32960FieldReference, bool) {
|
||
reference, ok := gb32960FieldReferences[canonicalGB32960ReferenceKey(sourceField)]
|
||
if !ok {
|
||
return gb32960FieldReference{}, false
|
||
}
|
||
reference.ValueMappings = append([]MetricValueMapping(nil), reference.ValueMappings...)
|
||
return reference, true
|
||
}
|
||
|
||
func protocolDisplayValue(sourceField string, value any) string {
|
||
reference, ok := gb32960ReferenceForSource(sourceField)
|
||
if !ok || len(reference.ValueMappings) == 0 || value == nil {
|
||
return ""
|
||
}
|
||
raw := metricMappingValue(value)
|
||
for _, mapping := range reference.ValueMappings {
|
||
if mapping.Value == raw {
|
||
return fmt.Sprintf("%s(%s)", mapping.Label, raw)
|
||
}
|
||
}
|
||
return fmt.Sprintf("未知状态(%s)", raw)
|
||
}
|
||
|
||
func metricMappingValue(value any) string {
|
||
switch typed := value.(type) {
|
||
case json.Number:
|
||
return typed.String()
|
||
case float64:
|
||
if typed == float64(int64(typed)) {
|
||
return strconv.FormatInt(int64(typed), 10)
|
||
}
|
||
return strconv.FormatFloat(typed, 'f', -1, 64)
|
||
case float32:
|
||
return strconv.FormatFloat(float64(typed), 'f', -1, 32)
|
||
case int:
|
||
return strconv.Itoa(typed)
|
||
case int8:
|
||
return strconv.FormatInt(int64(typed), 10)
|
||
case int16:
|
||
return strconv.FormatInt(int64(typed), 10)
|
||
case int32:
|
||
return strconv.FormatInt(int64(typed), 10)
|
||
case int64:
|
||
return strconv.FormatInt(typed, 10)
|
||
case uint:
|
||
return strconv.FormatUint(uint64(typed), 10)
|
||
case uint8:
|
||
return strconv.FormatUint(uint64(typed), 10)
|
||
case uint16:
|
||
return strconv.FormatUint(uint64(typed), 10)
|
||
case uint32:
|
||
return strconv.FormatUint(uint64(typed), 10)
|
||
case uint64:
|
||
return strconv.FormatUint(typed, 10)
|
||
default:
|
||
return strings.TrimSpace(fmt.Sprint(value))
|
||
}
|
||
}
|
||
|
||
func enrichMetricDefinitions(definitions []MetricDefinition) []MetricDefinition {
|
||
result := make([]MetricDefinition, len(definitions))
|
||
copy(result, definitions)
|
||
for index := range result {
|
||
definition := &result[index]
|
||
sourceField := definition.SourceFields["GB32960"]
|
||
reference, ok := gb32960ReferenceForSource(sourceField)
|
||
if !ok {
|
||
continue
|
||
}
|
||
if len(definition.Protocols) == 1 && strings.EqualFold(definition.Protocols[0], "GB32960") {
|
||
definition.Label = reference.Label
|
||
definition.Description = reference.Description
|
||
definition.Unit = reference.Unit
|
||
}
|
||
definition.ValueMappings = reference.ValueMappings
|
||
}
|
||
return result
|
||
}
|