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);
}
}
}