using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json.Linq; using Xunit; using CncModels.Entity; using CncCollector.Core; namespace CncCollector.Tests { /// /// DataParser 单元测试 /// 覆盖:正常解析、空JSON、缺失字段、非法数值、多品牌映射 /// public class DataParserTests { /// 创建标准 FANUC 品牌配置 private static Brand CreateFanucBrand() { return new Brand { DeviceField = "device", TagsPath = "tags" }; } /// 创建16条标准 FANUC 字段映射 private static List CreateFanucMappings() { return new List { new BrandFieldMapping { StandardField = "program_name", FieldName = "Tag5", DataType = "string" }, new BrandFieldMapping { StandardField = "part_count", FieldName = "Tag8", DataType = "number" }, new BrandFieldMapping { StandardField = "runtime", FieldName = "Tag9", DataType = "number" }, new BrandFieldMapping { StandardField = "start_value", FieldName = "Tag11", DataType = "number" }, new BrandFieldMapping { StandardField = "angle", FieldName = "Tag14", DataType = "number" }, new BrandFieldMapping { StandardField = "feed", FieldName = "Tag17", DataType = "number" }, new BrandFieldMapping { StandardField = "rpm", FieldName = "Tag18", DataType = "number" }, new BrandFieldMapping { StandardField = "spindle", FieldName = "Tag19", DataType = "number" }, new BrandFieldMapping { StandardField = "speed", FieldName = "Tag20", DataType = "number" }, new BrandFieldMapping { StandardField = "temperature", FieldName = "Tag21", DataType = "number" }, new BrandFieldMapping { StandardField = "mass", FieldName = "Tag22", DataType = "number" }, new BrandFieldMapping { StandardField = "height", FieldName = "Tag23", DataType = "number" }, new BrandFieldMapping { StandardField = "width", FieldName = "Tag24", DataType = "number" }, new BrandFieldMapping { StandardField = "length", FieldName = "Tag25", DataType = "number" }, new BrandFieldMapping { StandardField = "custom_code", FieldName = "Tag26", DataType = "string" }, new BrandFieldMapping { StandardField = "io_status", FieldName = "_io_status", DataType = "number" } }; } /// 构造标准 FANUC 设备 JSON 对象 private static JObject BuildFanucDeviceJson() { var json = @"[ { ""device"": ""CNC-A001"", ""desc"": ""测试机床"", ""tags"": [ { ""id"": ""_io_status"", ""value"": ""1.00000"" }, { ""id"": ""Tag5"", ""value"": ""O0001"" }, { ""id"": ""Tag8"", ""value"": ""1219.00000"" }, { ""id"": ""Tag9"", ""value"": ""3.00000"" }, { ""id"": ""Tag11"", ""value"": ""1.00000"" }, { ""id"": ""Tag14"", ""value"": ""100.00000"" }, { ""id"": ""Tag17"", ""value"": ""300.00000"" }, { ""id"": ""Tag18"", ""value"": ""60.00000"" }, { ""id"": ""Tag19"", ""value"": ""450.00000"" }, { ""id"": ""Tag20"", ""value"": ""60.00000"" }, { ""id"": ""Tag21"", ""value"": ""25.00000"" }, { ""id"": ""Tag22"", ""value"": ""23558160.00000"" }, { ""id"": ""Tag23"", ""value"": ""18224.00000"" }, { ""id"": ""Tag24"", ""value"": ""6848959.00000"" }, { ""id"": ""Tag25"", ""value"": ""699.00000"" }, { ""id"": ""Tag26"", ""value"": ""G01"" } ] } ]"; var arr = JArray.Parse(json); return arr[0] as JObject; } // ===== 正常解析测试 ===== [Fact] public void ParseDevice_正常FANUC数据_解析出所有映射字段() { var deviceObj = BuildFanucDeviceJson(); var brand = CreateFanucBrand(); var mappings = CreateFanucMappings(); var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.NotNull(result); Assert.Equal(16, result.Count); // 验证字符串字段 Assert.Equal("O0001", result["program_name"].StringValue); Assert.Equal("G01", result["custom_code"].StringValue); // 验证数值字段 Assert.True(result["part_count"].NumericValue.HasValue); Assert.Equal(1219m, result["part_count"].NumericValue.Value); Assert.True(result["runtime"].NumericValue.HasValue); Assert.Equal(3m, result["runtime"].NumericValue.Value); // 验证所有映射字段都存在 foreach (var key in mappings.Select(m => m.StandardField)) { Assert.True(result.ContainsKey(key), $"字段 {key} 未在结果中找到"); } } [Fact] public void ParseDevice_数值型字段_正确解析为decimal() { var deviceObj = BuildFanucDeviceJson(); var brand = CreateFanucBrand(); var mappings = new List { new BrandFieldMapping { StandardField = "part_count", FieldName = "Tag8", DataType = "number" } }; var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.True(result["part_count"].NumericValue.HasValue); Assert.Equal(1219m, result["part_count"].NumericValue.Value); Assert.Equal("1219.00000", result["part_count"].StringValue); } [Fact] public void ParseDevice_字符串型字段_保留原始值() { var deviceObj = BuildFanucDeviceJson(); var brand = CreateFanucBrand(); var mappings = new List { new BrandFieldMapping { StandardField = "program_name", FieldName = "Tag5", DataType = "string" } }; var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.Equal("O0001", result["program_name"].StringValue); } // ===== 空值和null 测试 ===== [Fact] public void ParseDevice_deviceObj为null_返回空字典() { var brand = CreateFanucBrand(); var mappings = CreateFanucMappings(); var result = DataParser.ParseDevice(null, brand, mappings); Assert.NotNull(result); Assert.Empty(result); } [Fact] public void ParseDevice_mappings为null_返回空字典() { var deviceObj = BuildFanucDeviceJson(); var brand = CreateFanucBrand(); var result = DataParser.ParseDevice(deviceObj, brand, null); Assert.NotNull(result); Assert.Empty(result); } [Fact] public void ParseDevice_mappings为空列表_返回空字典() { var deviceObj = BuildFanucDeviceJson(); var brand = CreateFanucBrand(); var result = DataParser.ParseDevice(deviceObj, brand, new List()); Assert.NotNull(result); Assert.Empty(result); } // ===== 缺失字段测试 ===== [Fact] public void ParseDevice_tags路径不存在_返回空字典() { var deviceObj = BuildFanucDeviceJson(); var brand = new Brand { DeviceField = "device", TagsPath = "nonexistent_path" }; var mappings = CreateFanucMappings(); var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.Empty(result); } [Fact] public void ParseDevice_部分字段不存在于tags_只返回存在的字段() { var deviceObj = BuildFanucDeviceJson(); var brand = CreateFanucBrand(); var mappings = new List { new BrandFieldMapping { StandardField = "program_name", FieldName = "Tag5", DataType = "string" }, new BrandFieldMapping { StandardField = "missing_field", FieldName = "Tag999", DataType = "string" }, new BrandFieldMapping { StandardField = "part_count", FieldName = "Tag8", DataType = "number" } }; var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.Equal(2, result.Count); Assert.True(result.ContainsKey("program_name")); Assert.True(result.ContainsKey("part_count")); Assert.False(result.ContainsKey("missing_field")); } // ===== 非法数值测试 ===== [Fact] public void ParseDevice_数值字段值非法_NumericValue为null() { var json = @"[{ ""device"": ""D1"", ""tags"": [{ ""id"": ""Tag8"", ""value"": ""not_a_number"" }] }]"; var deviceObj = JArray.Parse(json)[0] as JObject; var brand = new Brand { DeviceField = "device", TagsPath = "tags" }; var mappings = new List { new BrandFieldMapping { StandardField = "part_count", FieldName = "Tag8", DataType = "number" } }; var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.True(result.ContainsKey("part_count")); Assert.Null(result["part_count"].NumericValue); Assert.Equal("not_a_number", result["part_count"].StringValue); } [Fact] public void ParseDevice_数值字段为空字符串_NumericValue为null() { var json = @"[{ ""device"": ""D1"", ""tags"": [{ ""id"": ""Tag8"", ""value"": """" }] }]"; var deviceObj = JArray.Parse(json)[0] as JObject; var brand = new Brand { DeviceField = "device", TagsPath = "tags" }; var mappings = new List { new BrandFieldMapping { StandardField = "part_count", FieldName = "Tag8", DataType = "number" } }; var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.True(result.ContainsKey("part_count")); Assert.Null(result["part_count"].NumericValue); } // ===== ExtractDeviceCode 测试 ===== [Fact] public void ExtractDeviceCode_正常JSON_返回device值() { var json = @"{ ""device"": ""fanake_1.8"" }"; var obj = JObject.Parse(json); var code = DataParser.ExtractDeviceCode(obj); Assert.Equal("fanake_1.8", code); } [Fact] public void ExtractDeviceCode_自定义字段名_返回正确值() { var json = @"{ ""name"": ""myDevice"" }"; var obj = JObject.Parse(json); var code = DataParser.ExtractDeviceCode(obj, "name"); Assert.Equal("myDevice", code); } [Fact] public void ExtractDeviceCode_null对象_返回null() { var code = DataParser.ExtractDeviceCode(null); Assert.Null(code); } [Fact] public void ExtractDeviceCode_无device字段_返回null() { var json = @"{ ""other"": ""x"" }"; var obj = JObject.Parse(json); var code = DataParser.ExtractDeviceCode(obj); Assert.Null(code); } // ===== 多品牌/不同结构测试 ===== [Fact] public void ParseDevice_不同TagsPath_正确解析() { var json = @"[{ ""device"": ""M1"", ""data"": [{ ""id"": ""prog"", ""value"": ""P001"" }] }]"; var deviceObj = JArray.Parse(json)[0] as JObject; var brand = new Brand { DeviceField = "device", TagsPath = "data" }; var mappings = new List { new BrandFieldMapping { StandardField = "program_name", FieldName = "prog", DataType = "string" } }; var result = DataParser.ParseDevice(deviceObj, brand, mappings); Assert.Single(result); Assert.Equal("P001", result["program_name"].StringValue); } } }