数据模型 - Snet Docs

Snet Framework -- 数据模型参考

最后更新: 2026-07-21 命名空间: Snet.Model.data, Snet.Model.@event 源代码: Snet.Model/data/, Snet.Model/event/


架构概览

Snet Framework 定义了 21 个数据模型类,分为四个逻辑区域:事件基础设施、结果模型层次结构、地址系统和配置模型。所有结果模型提供 GetDetails(out ...) 解构器和 CreateSuccessResult/CreateFailureResult 静态工厂方法。每个模型覆盖 ToString() 通过 System.Text.Json 生成 JSON。

EventArgsAsync (CancellationToken)
├── BaseModel (Status, Message, Time)
│   ├── EventInfoResult
│   └── ResultModel (+ResultData)
│       ├── EventDataResult
│       └── OperateResult (+RunTime)
└── EventLanguageResult (Status, Message, Language, Time)

地址系统(独立层次)
├── Address ────────────────── AddressDetails 列表容器
├── AddressDetails ──────────── 单个地址定义
├── AddressValue : AddressDetails ── 带数据的地址
├── AddressValueSimplify [Proto] ── 轻量级传输模型
└── AddressValueSimplifyProtobuf [Proto] ── 批量容器

配置模型
├── WriteModel, WAModel ──────── 设备写入与 WebApi 配置
├── AddressMq, AddressParse ──── MQ 管道与解析管道
├── ResponseModel ──────────────  MQ 响应反序列化
├── BytesModel ─────────────────  字节数组映射
├── LanguageModel ──────────────  多语言资源配置
├── PluginModel ────────────────  插件元数据
└── ParamModel ─────────────────  基于反射的 UI 生成

异常
└── CustomException : Exception ── 自动捕获调用位置

第一部分:事件基础设施

1.1 EventArgsAsync

文件: event/EventArgsAsync.cs 继承层次: 独立的基类

为异步事件处理程序提供数据。持有 CancellationToken(JSON 忽略)和静态 Empty 单例。

成员 类型 描述
CancellationToken CancellationToken(只读) 传播取消通知(JSON 忽略)
Empty static readonly EventArgsAsync 无数据事件的单例
// 带取消感知的静态工厂
var args = EventArgsAsync.CreateOrDefault(token);

// 检查 token 是否可取消
if (args.CancellationToken.CanBeCanceled)
{
    // 处理取消
}

1.2 EventHandlerAsync<TEvent>

文件: event/EventHandlerAsync.cs 类型: 委托

public delegate Task EventHandlerAsync<in TEvent>(
    object? sender, TEvent e) where TEvent : EventArgsAsync;

异步委托,约束为 EventArgsAsync 派生的事件参数。

1.3 EventingWrapperAsync<TEvent>

文件: event/EventingWrapperAsync.cs 类型: struct(线程安全)

线程安全的异步事件包装器,管理订阅、取消订阅和触发。缓存调用列表以提升性能。

方法 描述
AddHandler(handler) 订阅处理程序
RemoveHandler(handler) 取消订阅处理程序
InvokeAsync(sender, parameter) 触发所有处理程序(非 async,避免 struct 拷贝)
Takeover(other) 从另一个包装器转移处理程序

关键行为:

  • 静默捕获 OperationCanceledException
  • 将所有其他异常路由到 _onException 回调
  • 首次调用后缓存 GetInvocationList() 结果以优化后续调用
var wrapper = new EventingWrapperAsync<EventDataResult>(
    "MyContext", OnExceptionHandler);

// 订阅
wrapper.AddHandler(async (sender, e) =>
{
    await ProcessAsync(e);
});

// 触发
await wrapper.InvokeAsync(this, eventData);

第二部分:结果模型层次结构

2.1 BaseModel

文件: data/BaseModel.cs 继承: EventArgsAsync

结果模型层次结构的根节点。包含状态、消息和时间戳。

属性 类型 默认值 描述
Status bool false 操作成功指示符
Message string? null 描述/错误消息
Time DateTime DateTime.Now 响应时间戳
方法 签名 描述
GetDetails (out string? message) -> bool 解构获取消息
GetDetails (out BaseModel result) -> bool 解构获取自身
ToString () -> string JSON 序列化
var result = new BaseModel(true, "操作完成");

// 解构模式
if (result.GetDetails(out string? message))
{
    Console.WriteLine($"成功: {message}");
}
else
{
    Console.WriteLine($"失败: {message}");
}

2.2 EventInfoResult

文件: data/EventInfoResult.cs 继承: BaseModel

用于通过 IEvent.OnInfoEvent 传递通信状态事件。仅包含状态和消息(无数据负载)。

静态工厂方法 签名
CreateSuccessResult (string successMessage) -> EventInfoResult
CreateFailureResult (string failureMessage) -> EventInfoResult
// 发布连接状态
var info = EventInfoResult.CreateSuccessResult("PLC 已连接");
info.GetDetails(out EventInfoResult result);

2.3 ResultModel

文件: data/ResultModel.cs 继承: BaseModel

扩展 BaseModel,增加通用结果数据负载。提供 8 个 GetDetails 重载用于灵活的数据解构。

属性 类型 默认值 描述
ResultData object? null 结果数据负载
关键方法 签名
GetSource<T> () -> T?
GetDetails (out object? resultData) -> bool
GetDetails<T> (out T? resultData) -> bool
GetDetails (out object? resultData, out string? message) -> bool
GetDetails<T> (out T? resultData, out string? message) -> bool
GetDetails (out string? message, out object? resultData) -> bool
GetDetails<T> (out string? message, out T? resultData) -> bool
var result = new ResultModel(true, "完成", new { Value = 42 });

// 类型化提取
if (result.GetDetails<dynamic>(out var data, out string? msg))
{
    Console.WriteLine(data.Value); // 42
}

// 泛型源提取
var source = result.GetSource<MyDto>();

2.4 EventDataResult

文件: data/EventDataResult.cs 继承: ResultModel

用于通过 IEvent.OnDataEvent 传递数据采集事件。包装状态、消息和数据结果。

静态工厂方法 签名
CreateSuccessResult (string successMessage) -> EventDataResult
CreateSuccessResult<T> (string successMessage, T resultData) -> EventDataResult
CreateFailureResult (string failureMessage) -> EventDataResult
// 发布采集数据
var data = EventDataResult.CreateSuccessResult(
    "读取完成", new float[] { 1.0f, 2.5f, 3.7f });

data.GetDetails(out EventDataResult result);

2.5 OperateResult

文件: data/OperateResult.cs 继承: ResultModel

统一的操作返回模型。增加了毫秒级执行时间追踪。

属性 类型 默认值 描述
RunTime int 0 执行时间(毫秒)
静态工厂方法 签名 默认 RunTime
CreateSuccessResult (string msg, int? runTime) -> OperateResult 8ms
CreateSuccessResult<T> (string msg, T data, int? runTime) -> OperateResult 8ms
CreateFailureResult (string msg, int? runTime) -> OperateResult 8ms
var sw = Stopwatch.StartNew();
// ... 执行操作 ...
sw.Stop();

var result = OperateResult.CreateSuccessResult(
    "批量写入完成", affectedRows, (int)sw.ElapsedMilliseconds);

Console.WriteLine($"状态: {result.Status}, 耗时: {result.RunTime}ms");

2.6 EventLanguageResult

文件: data/EventLanguageResult.cs 继承: EventArgsAsync(不是 BaseModel -- 平行分支)

用于通过 IEvent.OnLanguageEvent 传递语言变更通知。拥有独立的 StatusMessageLanguageTime 属性,不继承 BaseModel

属性 类型 描述
Status bool 操作成功
Message string? 描述
Language LanguageType? 当前语言
Time DateTime(默认: DateTime.Now 时间戳
方法 签名
GetDetails (out string? message) -> bool
GetDetails (out string? message, out LanguageType? language) -> bool
GetDetails (out LanguageType? language) -> bool
GetDetails (out EventLanguageResult result) -> bool
静态工厂方法 签名
CreateSuccessResult (string msg) -> EventLanguageResult
CreateSuccessResult (string msg, LanguageType? lang) -> EventLanguageResult
CreateFailureResult (string msg) -> EventLanguageResult
CreateFailureResult (string msg, LanguageType? lang) -> EventLanguageResult
// 通知语言切换
var langResult = EventLanguageResult.CreateSuccessResult(
    "已切换为英文", LanguageType.en);

langResult.GetDetails(out string? msg, out LanguageType? lang);

第三部分:地址系统

3.1 Address

文件: data/Address.cs 继承层次: 独立类

AddressDetails 集合的容器。通过 SN 标识(可用于存储机台号、组名、车间、工厂)。

属性 类型 默认值 描述
SN string Guid.NewGuid().ToUpperNString() 唯一标识符
AddressArray List<AddressDetails> null 地址集合
CreationTime DateTime DateTime.Now 创建时间戳
方法 签名 描述
GetAddressInfo (string? name, string? anotherName) -> AddressDetails? 按名称/别名查找(两者均为 null 返回 null)
CheckAddress () -> bool 验证所有地址均已设置 AddressName
RemoveVirtualAddress () -> List<AddressDetails> 筛选实际地址(返回新列表)
GetRealityAddressCount () -> int 统计实际地址数量(无中间分配)
var address = new Address("Machine-01", detailsList, DateTime.Now);

// 查找特定地址
var detail = address.GetAddressInfo(AddressName: "D100");

// 过滤虚拟地址用于订阅
var realOnly = address.RemoveVirtualAddress();

// 使用前验证
if (address.CheckAddress())
{
    // 所有地址有效
}

3.2 AddressDetails

文件: data/AddressDetails.cs 继承层次: 独立类

定义单个地址点,包含完整元数据:名称、类型、编码、长度、MQ 参数和解析参数。

属性 类型 默认值 描述
SN string Guid.NewGuid().ToUpperNString() 唯一标识符
AddressName string null PLC 地址名称(唯一)
Length ushort 1 数组类型时的数组长度
EncodingType EncodingType ANSI 字符串编码
AddressAnotherName string? null 地址别名
AddressPropertyName string? null 实体属性名称映射
AddressDescribe string? null 可读描述
AddressExtendParam object? null 扩展通信参数
IsEnable bool true 地址是否启用
AddressMqParam AddressMq? null MQ 发布配置
AddressParseParam AddressParse? null 数据解析配置
AddressDataType DataType String 读写数据类型
AddressType AddressType Reality 实际地址或虚拟地址
// 最小定义
var detail = new AddressDetails("D100", DataType.Float);

// 完整定义
var detail = new AddressDetails(
    sn: "Machine-01",
    addressName: "D200",
    addressAnotherName: "TempSensor",
    addressPropertyName: "Temperature",
    addressDescribe: "炉温",
    addressExtendParam: null,
    addressMqParam: new AddressMq("snet/data", "{{temp:{0}}}", null),
    addressParseParam: null,
    addressDataType: DataType.Short,
    addressType: AddressType.Reality,
    isEnable: true,
    length: 1,
    encodingType: EncodingType.ANSI
);

3.3 AddressValue

文件: data/AddressValue.cs 继承: AddressDetails

扩展了采集数据的 AddressDetails。包含质量、结果值、原始值、消息和时间戳。

属性 类型 默认值 描述
Quality QualityType None (-1) 数据质量状态
Message string null 详细信息
ResultValue object? null 解析后的值(解析后)
OriginalValue object? null 原始值(解析前)
Time DateTime DateTime.Now 采集/更新时间
方法 签名 描述
GetDetails (out object? resultValue, out object? originalValue, out string? message, out DateTime? time) -> QualityType 完整解构
GetSimplify () -> AddressValueSimplify 转换为轻量级传输模型
SET (AddressDetails details) -> AddressValue 从父类复制属性
SET (object? value, byte[] source, DateTime time, BytesModel param, string? msg, QualityType quality) -> AddressValue 从字节解析结果设置
var av = new AddressValue(QualityType.Normal, 42.5f, new byte[] { 0x42, 0x2A }, "正常");

// 解构
var quality = av.GetDetails(
    out object? resultValue,
    out object? originalValue,
    out string? message,
    out DateTime? time);

// 转换为轻量级模型
AddressValueSimplify simple = av.GetSimplify();

3.4 AddressValueSimplify

文件: data/AddressValueSimplify.cs 标记: [ProtoContract](Protobuf-net)

轻量级传输模型,使用短属性名以实现紧凑的网络序列化。包含 9 个 ProtoMember 字段。

属性 类型 ProtoMember 默认值 描述
SN string? 1 null 唯一标识符
Add string? 2 null 点位地址
L ushort 3 1 数据长度
ENC EncodingType 4 ANSI 编码类型
Q QualityType 5 None 数据质量
Msg string? 6 null 详细信息
VAL string? 7 null 采集数值(JSON 字符串)
Ts long 8 DateTime.UtcNow.Ticks 采集时间戳(ticks)
DT DataType 9 String 数据类型
// 从 AddressValue 构造
var simple = addressValue.GetSimplify();

// 手动构造
var simple = new AddressValueSimplify(
    sn: "M01", add: "D100", l: 1, enc: EncodingType.ANSI,
    q: QualityType.Normal, val: "42.5", msg: "正常",
    ts: DateTime.UtcNow.Ticks, dt: DataType.Float
);

3.5 AddressValueSimplifyProtobuf

文件: data/AddressValueSimplifyProtobuf.cs 标记: [ProtoContract]

用于批量 Protobuf 序列化的容器,包含多个 AddressValueSimplify 实例。

属性 类型 ProtoMember 默认值 描述
Ts long 1 DateTime.UtcNow.Ticks 批次时间戳
ITM IEnumerable<AddressValueSimplify> 2 [] 数据项集合
var batch = new AddressValueSimplifyProtobuf(simplifyList);

// 通过 protobuf-net 序列化
using var stream = new MemoryStream();
Serializer.Serialize(stream, batch);
byte[] data = stream.ToArray();

第四部分:配置模型

4.1 WriteModel

文件: data/WriteModel.cs

定义向设备地址写入数据的参数。配合 IWrite 接口使用。

属性 类型 默认值 描述
Value object null 要写入的值(不能为空)
AddressDataType DataType String 目标数据类型
EncodingType EncodingType ANSI 字符串编码
var write = new WriteModel(42.5f, DataType.Float, EncodingType.ANSI);
await writer.WriteAsync("D100", write);

4.2 WAModel

文件: data/WAModel.cs

配置数据采集引擎内置的 WebApi 服务器。

属性 类型 默认值 描述
IpAddress string "127.0.0.1" 监听 IP 地址
Port int 6688 监听端口
CrossDomain bool false 启用 CORS
var wa = new WAModel("0.0.0.0", 6688, crossDomain: true);

4.3 AddressMq

文件: data/AddressMq.cs

配置采集数据通过消息队列发布。每个地址可选择性地发布到 MQ。

属性 类型 默认值 描述
Topic string null MQ 主题
ContentFormat string? null 格式字符串({0} = 数据占位符)
ISns List<string>? null MQ 实例 SN(null = 所有打开的实例)
var mqParam = new AddressMq(
    topic: "snet/device/data",
    contentFormat: "{{\"address\":\"{0}\",\"value\":\"{0}\"}}",
    iSns: null  // 发布到所有可用的 MQ 实例
);

4.4 AddressParse

文件: data/AddressParse.cs

通过反射对采集数据进行后处理的配置。每个地址最多有一个解析配置。

属性 类型 默认值 描述
ReflectionParam object[]? null [0] = 基础数据, [1] = 方法 SN

反射机制调用 <方法>(string 地址名, string 地址值) -> string 来转换原始值。

var parse = new AddressParse(new object[] { baseData, "MethodGUID" });

4.5 ResponseModel

文件: data/ResponseModel.cs

当配置 ResponseType.ContentWithTopic 时使用。从包含主题和内容的 MQ 响应中反序列化。

属性 类型 描述
Topic string 消息主题
Content object 消息内容
var response = JsonSerializer.Deserialize<ResponseModel>(mqMessage);
ProcessMessage(response.Topic, response.Content);

4.6 BytesModel

文件: data/BytesModel.cs

将字节数组映射到结构化数据。定义地址、偏移量、长度、数据类型、编码和字节序。同时支持 System.Text.JsonNewtonsoft.Json 枚举字符串序列化。

属性 类型 默认值 描述
Address string null 地址名称
Describe string null 描述
StartBit int 0 起始位偏移
Length ushort 1 数据长度
BoolIndex int 0 布尔位索引(每字节 0-7)
DataType DataType Bool 数据类型
EncodingType EncodingType UTF8 字符串编码
DataFormat DataFormat DCBA 字节序
var bm = new BytesModel(
    address: "DB100.DBD0",
    describe: "电机转速",
    startBit: 0,
    length: 2,           // 2 字节 = 16 位
    dataType: DataType.Short,
    encodingType: EncodingType.UTF8,
    dataFormat: DataFormat.DCBA,
    boolIndex: -1         // -1 表示非布尔位
);

4.7 LanguageModel

文件: data/LanguageModel.cs

配置多语言资源加载。指向解决方案、资源字典和程序集文件。

属性 类型 描述
Source string 存放资源字典的解决方案名称
Dictionary string 资源文件名称
AssemblyFile string 包含程序集清单的文件名称或路径
var lang = new LanguageModel(
    source: "Snet.Resources",
    dictionary: "Strings.zh-CN.xaml",
    assemblyFile: "Snet.Resources.dll"
);

4.8 PluginModel

文件: data/PluginModel.cs

插件加载器使用的插件元数据。描述名称、命名空间、配置模板、路径、类型和版本。

属性 类型 描述
Name string 插件名称
Namespace string 插件命名空间
ConfigFormat string 配置文件格式模板
Path string 插件所在目录路径
Type PluginType 插件类别(Daq=0, Mq=1)
Version string 插件版本
var plugin = new PluginModel(
    name: "SiemensS7",
    @namespace: "Snet.Plugin.Daq.Siemens",
    configFormat: "{\"ip\":\"{ip}\",\"rack\":{rack},\"slot\":{slot}}",
    path: "./plugins/SiemensS7/",
    version: "2.1.0",
    type: PluginType.Daq
);

4.9 ParamModel

文件: data/ParamModel.cs

基于反射的 UI 生成模型。允许插件声明式地定义其配置界面。包含嵌套类型层次结构,用于定义表单字段及其约束。

嵌套类型

ParamModel
├── dataCate (枚举) ─────────── 前端控件类型
│   ├── text (0) ─────────────── 文本框
│   ├── select (1) ───────────── 下拉框
│   ├── radio (2) ────────────── 单选框
│   ├── unmber (3) ───────────── 整数框
│   └── upload (4) ───────────── 上传
├── subset
│   ├── Name ─────────────────── 分组名称
│   ├── Description ──────────── 分组描述
│   └── Propertie: List<propertie>
├── propertie
│   ├── DataCate ─────────────── 控件类型
│   ├── Default ──────────────── 默认值
│   ├── Initial ──────────────── 初始值
│   ├── Description ──────────── 字段描述
│   ├── PropertyName ─────────── 绑定的属性名称
│   ├── Show ─────────────────── 可见性(true/false)
│   ├── Use ──────────────────── 可编辑(false = 只读)
│   ├── MustFillIn ───────────── 必填项
│   ├── DetailsTips ──────────── 提示文本
│   ├── Pattern ──────────────── 正则校验
│   ├── FailTips ─────────────── 校验失败提示
│   └── Options: List<options>
├── options
│   ├── Key ──────────────────── 选项键
│   └── Value ────────────────── 选项值
└── (精简版变体)
    ├── subsetSimplify ────────── 精简版分组(Name, Description, Propertie)
    └── propertieSimplify ─────── 精简版属性(Description, PropertyName)
ParamModel 属性 类型 描述
Code string(只读) Name 的别名
Name string 参数模型名称
Description string 描述
Subset List<subset> 参数分组
var param = new ParamModel
{
    Name = "SiemensConfig",
    Description = "西门子 PLC 连接参数",
    Subset = new List<ParamModel.subset>
    {
        new()
        {
            Name = "Connection",
            Description = "网络设置",
            Propertie = new List<ParamModel.propertie>
            {
                new()
                {
                    DataCate = ParamModel.dataCate.text,
                    PropertyName = "IpAddress",
                    Description = "PLC IP 地址",
                    Default = "192.168.0.1",
                    Show = true,
                    Use = true,
                    MustFillIn = true,
                    Pattern = @"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$",
                    FailTips = "IP 地址格式无效"
                },
                new()
                {
                    DataCate = ParamModel.dataCate.select,
                    PropertyName = "Protocol",
                    Description = "通信协议",
                    Default = "S7",
                    Show = true,
                    Use = true,
                    Options = new List<ParamModel.options>
                    {
                        new() { Key = "S7", Value = "S7" },
                        new() { Key = "S7-200", Value = "S7-200" }
                    }
                }
            }
        }
    }
};

第五部分:异常模型

5.1 CustomException

文件: data/CustomException.cs 继承: Exception

使用 [CallerFilePath][CallerMemberName][CallerLineNumber] 自动捕获调用者的文件路径、方法名和行号。提供同步和异步工厂方法。

静态工厂方法 签名 描述
Create (string message) -> CustomException 同步创建
Create (string message, Exception inner) -> CustomException 同步创建(含内部异常)
CreateAsync (string message, CancellationToken token) -> Task<CustomException> 异步创建
CreateAsync (string message, Exception inner, CancellationToken token) -> Task<CustomException> 异步创建(含内部异常)

消息格式: file {文件名} the {方法名} method {行号} line exception: {消息}

// 同步抛出
throw CustomException.Create("无效的寄存器地址");

// 异步创建
var ex = await CustomException.CreateAsync("超时", cts.Token);
// 输出: "file DeviceReader.cs the ReadAsync method 42 line exception: 超时"

跨领域设计模式

ToString() / JSON 序列化

每个模型覆盖 ToString(),通过 Snet.Utility 扩展方法 ToJson(true) 返回 JSON:

var result = OperateResult.CreateSuccessResult("完成", data, 15);
string json = result.ToString();
// {"Status":true,"Message":"完成","ResultData":{...},"Time":"...","RunTime":15}

GetDetails() 解构模式

所有结果类型遵循一致的解构模式,支持 C# out 变量:

// 模式: 方法返回 bool(状态),属性通过 out 参数输出
if (result.GetDetails(out var data, out string? message))
{
    // 成功路径
}
else
{
    // 失败路径 -- message 包含错误信息
}

工厂方法模式

所有结果类型提供 CreateSuccessResultCreateFailureResult 静态工厂方法,支持单表达式构造,无需使用 new

// 替代: new OperateResult(false, "错误", 0)
return OperateResult.CreateFailureResult("错误");