feat: 完成 issue #29 TDengine 超级表 schema 依据点位字典自动生成
This commit is contained in:
@@ -10,8 +10,10 @@ import _bootstrap # noqa: F401
|
||||
from data_bus.tdengine_schema import (
|
||||
PointSpec,
|
||||
build_batch_insert,
|
||||
generate_schema,
|
||||
generate_subtable_ddls,
|
||||
generate_supertable_ddl,
|
||||
specs_from_point_dict_rows,
|
||||
)
|
||||
from data_bus.templating import TemplateNaming
|
||||
|
||||
@@ -22,6 +24,19 @@ def sample_point(device_id="CLF-01", point_id="CLF-01.TEMP", unit="℃", data_ty
|
||||
return PointSpec(device_id=device_id, point_id=point_id, unit=unit, data_type=data_type)
|
||||
|
||||
|
||||
# 与 edge-gateway 点位字典 CSV 表头对齐的样例行
|
||||
SAMPLE_POINT_DICT_ROWS = [
|
||||
{"device_id": "CLF-01", "point_id": "CLF-01.TEMP", "name": "炉温", "unit": "℃",
|
||||
"dataType": "float", "sampleRate": "1000", "qualityCode": "true", "opcNode": "ns=2;s=CLF.Temp"},
|
||||
{"device_id": "CLF-01", "point_id": "CLF-01.PRES", "name": "炉压", "unit": "kPa",
|
||||
"dataType": "float", "sampleRate": "1000", "qualityCode": "true"},
|
||||
{"device_id": "CLF-02", "point_id": "CLF-02.RUN", "name": "运行状态", "unit": "%",
|
||||
"dataType": "bool", "sampleRate": "1000"},
|
||||
{"device_id": "S7-01", "point_id": "S7-01.PUMP_A", "name": "泵A频率", "unit": "Hz",
|
||||
"dataType": "float", "sampleRate": "1000", "protocol": "s7"},
|
||||
]
|
||||
|
||||
|
||||
class SupertableDDLTest(unittest.TestCase):
|
||||
def test_ddl_shape(self):
|
||||
ddl = generate_supertable_ddl(NAMING)
|
||||
@@ -72,5 +87,49 @@ class BatchInsertTest(unittest.TestCase):
|
||||
self.assertEqual(build_batch_insert(NAMING, []), [])
|
||||
|
||||
|
||||
class PointDictSchemaTest(unittest.TestCase):
|
||||
"""依据点位字典自动生成完整 schema(子任务 #29 交付口径)。"""
|
||||
|
||||
def test_specs_from_point_dict_rows(self):
|
||||
specs = specs_from_point_dict_rows(SAMPLE_POINT_DICT_ROWS)
|
||||
self.assertEqual(len(specs), 4)
|
||||
self.assertEqual(specs[0].device_id, "CLF-01")
|
||||
self.assertEqual(specs[0].point_id, "CLF-01.TEMP")
|
||||
self.assertEqual(specs[0].unit, "℃")
|
||||
self.assertEqual(specs[0].data_type, "float")
|
||||
# 兼容小写 data_type 键
|
||||
specs2 = specs_from_point_dict_rows(
|
||||
[{"device_id": "CLF-01", "point_id": "P1", "unit": "℃", "data_type": "int"}]
|
||||
)
|
||||
self.assertEqual(specs2[0].data_type, "int")
|
||||
# 缺失列宽松兜底
|
||||
specs3 = specs_from_point_dict_rows([{"device_id": "D1", "point_id": "P2"}])
|
||||
self.assertEqual(specs3[0].unit, "")
|
||||
self.assertEqual(specs3[0].data_type, "float")
|
||||
|
||||
def test_generate_schema_full(self):
|
||||
specs = specs_from_point_dict_rows(SAMPLE_POINT_DICT_ROWS)
|
||||
ddls = generate_schema(NAMING, specs)
|
||||
# 第 0 条是超级表,其余是子表;4 个测点 → 1 + 4 条
|
||||
self.assertEqual(len(ddls), 5)
|
||||
self.assertTrue(ddls[0].startswith("CREATE STABLE IF NOT EXISTS ti_cl4_points"))
|
||||
self.assertTrue(all(d.startswith("CREATE TABLE IF NOT EXISTS ti_cl4_pt_") for d in ddls[1:]))
|
||||
# 子表均 USING 同一超级表,且含点位字典标签
|
||||
self.assertIn("USING ti_cl4_points", ddls[1])
|
||||
self.assertIn("'CLF-01'", ddls[1])
|
||||
# bool 点位标签保留 data_type
|
||||
self.assertTrue(any("'bool'" in d for d in ddls))
|
||||
|
||||
def test_generate_schema_retention(self):
|
||||
specs = specs_from_point_dict_rows(SAMPLE_POINT_DICT_ROWS)
|
||||
ddls = generate_schema(NAMING, specs, retention_days=365)
|
||||
self.assertIn("KEEP(365)", ddls[0])
|
||||
|
||||
def test_generate_schema_empty_points(self):
|
||||
ddls = generate_schema(NAMING, [])
|
||||
self.assertEqual(len(ddls), 1) # 只有超级表,无子表
|
||||
self.assertIn("CREATE STABLE", ddls[0])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user