地址系统 - Snet Docs

地址系统 (Address System)

程序集: Snet.Model / Snet.Core 命名空间: Snet.Model.data, Snet.Core.handler, Snet.Core.virtualAddress, Snet.Core.extend


概述

地址系统是 Snet 框架中所有读/写/订阅操作的核心数据模型。它定义了数据点如何被标识、类型化、传输和处理——从原始 PLC 寄存器值,经过类型转换、自定义解析和 MQ 发布,最终成为轻量级的 Protobuf 传输对象。


架构

Address ────── (容器: SN + List<AddressDetails> + CreationTime)
  |
  +── AddressDetails[] ─── (每个数据点一个实例)
  |     |-- AddressName          (PLC 地址, 如 "DB100.DBD0")
  |     |-- AddressDataType       (DataType 枚举, 默认 String)
  |     |-- AddressType           (Reality / VirtualStatic / VirtualDynamic_*)
  |     |-- Length                (ushort, 1 = 单值)
  |     |-- EncodingType          (默认 ANSI, 基于 CodePage)
  |     |-- AddressMqParam        (MQ 发布配置, 可选)
  |     +-- AddressParseParam     (基于反射的自定义解析, 可选)
  |
  +── AddressValue : AddressDetails ─── (运行时读取结果)
  |     |-- Quality               (QualityType 枚举)
  |     |-- ResultValue           (解析后的最终值)
  |     |-- OriginalValue         (解析前的原始值)
  |     |-- Message               (详细信息/错误消息)
  |     |-- Time                  (DateTime 时间戳)
  |     +-- .GetSimplify()        (---> AddressValueSimplify)
  |
  +── AddressValueSimplify        ([ProtoContract], 9 个 ProtoMember 字段)
  +── AddressValueSimplifyProtobuf ([ProtoContract], 批量传输包装器)

数据流

PLC/设备 --原始字节--> [驱动] --字符串值--> AddressHandler.ExecuteDispose
                                                     |
                            +--> DataTypeConvert (字符串 -> .NET 类型值)
                            +--> Parse (基于反射的自定义转换)
                            +--> Produce (如配置则发布到 MQ)
                                                     |
                                                AddressValue
                                               /            \
                                       GetSimplify()        直接使用
                                          |
                               AddressValueSimplify
                                          |
                   [通过 GetSimplifyProtobuf/SelectSimplifyProtobuf 批量处理]
                                          |
                               AddressValueSimplifyProtobuf
                                          |
                               [Protobuf 二进制传输]

核心类

Address

顶层容器。用共享标识符将相关的数据点分组。

属性 类型 默认值 描述
SN string Guid.NewGuid().ToUpperNString() 组标识符(工位/产线/车间/工厂)
AddressArray List<AddressDetails> -- 组内所有地址详情
CreationTime DateTime DateTime.Now 组创建时间戳

关键方法:

  • GetAddressInfo(addressName?, addressAnotherName?) -- 按名称/别名查找 AddressDetails
  • CheckAddress() -- 验证所有 AddressName 是否非空
  • RemoveVirtualAddress() -- 过滤仅保留 Reality 类型(用于订阅场景)
  • GetRealityAddressCount() -- 计算非虚拟地址数量

AddressDetails

每个数据点一个实例。定义标识、类型、编码以及可选的 MQ/解析行为。

属性 类型 默认值 描述
SN string Guid.NewGuid().ToUpperNString() 唯一点标识符
AddressName string -- PLC 地址(如 "DB100.DBD0", "M100", "40001"
AddressAnotherName string? -- 别名/显示名称
AddressPropertyName string? -- 实体属性名称,用于 ORM 映射
AddressDescribe string? -- 人类可读的描述
AddressDataType DataType DataType.String 期望的 .NET 类型
AddressType AddressType AddressType.Reality Reality 或 Virtual* 变体
Length ushort 1 数组长度(Array 数据类型时 >1)
EncodingType EncodingType EncodingType.ANSI 字符串编码(ANSI=0, UTF8=65001 等)
IsEnable bool true 启用/禁用此地址
AddressExtendParam object? -- 扩展通信参数
AddressMqParam AddressMq? -- MQ 发布配置
AddressParseParam AddressParse? -- 基于反射的自定义解析配置

AddressValue

继承 AddressDetails,扩展运行时读/写结果数据。

属性 类型 默认值 描述
Quality QualityType QualityType.None 数据质量指示器
ResultValue object? -- 解析后的最终值
OriginalValue object? -- 解析前的原始值
Message string -- 详细信息或错误消息
Time DateTime DateTime.Now 读取/更新时间戳

关键方法:

  • SET(AddressDetails) -- 从 AddressDetails 复制所有基础属性
  • SET(value, source, time, bytesModel, msg?, quality?) -- 从驱动级 BytesModel 设置
  • GetSimplify() -- 转换为轻量级 AddressValueSimplify 用于传输
  • GetDetails(out resultValue, out originalValue, out message, out time) -- 解构

QualityType 枚举

名称 含义
-1 None 尚未处理
0 Exception 异常 / 空值
1 Normal 数据有效且类型正确
2 DataTypeError 类型转换失败
3 ParseUnknown 解析成功,正确性未知
4 ParseError 自定义解析抛出异常

AddressType 枚举

描述
Reality 实际硬件地址
VirtualStatic 固定随机值(一次性生成)
VirtualDynamic_Random 随机值,每 {interval}ms 变化
VirtualDynamic_RandomScope 在 {min,max} 范围内的随机值
VirtualDynamic_Order 按 {step} 步长顺序递增
VirtualDynamic_OrderScope 在 {min,max} 内顺序递增,超出后循环

AddressValueSimplify

轻量级 [ProtoContract] 传输模型,共 9 个 ProtoMember 字段:

ProtoMember 字段 类型 描述
1 SN string? 唯一标识符
2 Add string? 点位地址名称
3 L ushort 数据长度
4 ENC EncodingType 编码类型
5 Q QualityType 质量指示器
6 Msg string? 详细信息
7 VAL string? 值(JSON 序列化)
8 Ts long 时间戳(DateTime.Ticks)
9 DT DataType 数据类型

AddressValueSimplifyProtobuf

批量包装器 [ProtoContract],用于 Protobuf 传输。

ProtoMember 字段 类型 描述
1 Ts long 时间戳(DateTime.UtcNow.Ticks)
2 ITM IEnumerable<AddressValueSimplify> 精简值集合

辅助类

AddressMq

配置数据采集后的 MQ 发布。

属性 类型 描述
Topic string MQ 主题
ContentFormat string? 格式字符串,{0} 为数据占位符;null = 原始数据
ISns List<string>? 目标 MQ 实例 SN;null = 所有活跃实例

AddressParse

配置基于反射的自定义解析。

属性 类型 描述
ReflectionParam object[]? [0] = ReflectionData.Basics(程序集),[1] = 方法 SN

方法签名: Func<string, string, string> -- 接收 (addressName, value),返回解析后的字符串。


处理管道: AddressHandler.ExecuteDispose

主要的数据处理入口点。管道阶段:

1. 空值检查
   value 为 null 或空白 --> QualityType.Exception, 返回

2. DataTypeConvert (字符串 -> .NET 类型值)
   处理 30+ 种 DataType 变体:
     Bool, String, Char, Byte, Double, Float/Single, Short/Int16, Int/Int32
     Long/Int64, Ushort/UInt16, Uint/UInt32, Ulong/UInt64
     DateTime/Date/Time
     ByteArray, BoolArray, DoubleArray, FloatArray/SingleArray
     ShortArray/Int16Array, UshortArray/UInt16Array
     IntArray/Int32Array, UintArray/UInt32Array
     LongArray/Int64Array, UlongArray/UInt64Array
   数组类型通过 ToJsonEntity<T[]> 从 JSON 反序列化。

3. Parse (基于反射的自定义转换, 可选)
   如果 AddressParseParam 配置了有效的 ReflectionParam[0] 和 [1],
   则通过反射执行外部方法。
   结果质量 -> ParseUnknown (成功) 或 ParseError (失败)。

4. Produce (MQ 发布, 可选)
   如果配置了 AddressMqParam:
     - 应用 ContentFormat (使用 {0} 占位符的 string.Format)
     - 通过线程池任务异步发布到 MQ 主题 (非阻塞)

5. 返回 AddressValue

传输助手 (CoreExtend 扩展方法)

静态类 Snet.Core.extend.CoreExtend:

方法 签名 描述
GetSimplifyArray ConcurrentDictionary<string,AddressValue> -> IEnumerable<AddressValueSimplify>? 将所有值转换为精简形式
GetSimplifyProtobuf ConcurrentDictionary<string,AddressValue> -> AddressValueSimplifyProtobuf? 批量包装用于 Protobuf
SelectSimplifyArray (dict, List<string> addressNames) -> IEnumerable<AddressValueSimplify>? 筛选的精简值
SelectSimplifyProtobuf (dict, List<string> addressNames) -> AddressValueSimplifyProtobuf? 筛选的 Protobuf 批量数据
GetArray ConcurrentDictionary<string,AddressValue> -> IEnumerable<AddressValue>? 所有原始 AddressValue
SelectArray (dict, List<string> addressNames) -> IEnumerable<AddressValue>? 筛选的原始值
CheckAddress Address -> bool 验证 AddressName 是否非空
GetEncoding EncodingType? -> Encoding 将编码枚举转换为 System.Text.Encoding
ReadGetLength DataType -> ushort 类型字节大小 (8/4/2/0)
WriteGetBytes object -> byte[] 数值转字节 (BitConverter)
WriteGetBytes string + Encoding -> byte[] 字符串转编码字节
GetDefaultEncodingWrite ConcurrentDictionary<string,object> + EncodingType? -> ConcurrentDictionary<string,(object,EncodingType?)> 为写入值附加编码
MessageStandard string -> string 标准化 [TAG] 消息格式

虚拟地址系统

虚拟地址无需真实硬件即可生成模拟数据——用于测试和开发。

VirtualAddressManage

管理器类 (IDisposable, IAsyncDisposable),使用 ConcurrentDictionary<string, (VirtualAddress, AddressType, DataType)>

  • InitVirtualAddress(AddressDetails, out bool IsVA) -- 如果是虚拟类型则初始化
  • IsVirtualAddress(AddressDetails) -> bool -- 检查地址类型
  • IsVirtualAddress(string addressName) -> bool -- 按名称检查
  • Read(AddressDetails) -> object? / Read(string) -> object? -- 读取当前值
  • Write(AddressDetails, object) -> bool / Write(string, object) -> bool -- 写入值

VirtualAddress

实例类 (CoreUnify<VirtualAddress, VirtualAddressData>):

  • 通过 AddressName 配置: 参数嵌入在花括号中
  • 线程安全: 使用 lock(_syncLock) 保护读写,CancellationTokenSource 管理任务生命周期
  • ThreadLocal Random: 每个线程独立的 Random 实例,避免锁竞争

配置格式

类型 语法 示例
VirtualStatic {点名称} "TestPoint" -- 生成一个随机值
VirtualDynamic_Random {点名称}{间隔} "MyPoint{500}" -- 每 500ms 变化
VirtualDynamic_RandomScope {点名称}{间隔,最小值^最大值} "MyPoint{200,0^100}" -- 每 200ms 在 [0,100] 内随机
VirtualDynamic_Order {点名称}{间隔,步长} "MyPoint{1000,0.5}" -- 每 1000ms 递增 0.5
VirtualDynamic_OrderScope {点名称}{间隔,步长,最小值^最大值} "MyPoint{500,1,0^100}" -- 0->1->2... 到 100 后循环

所有间隔值经过校验,最低为 10ms,防止 CPU 空转。


代码示例

创建包含多个数据点的 Address

using Snet.Model.data;
using Snet.Model.@enum;

var details = new List<AddressDetails>
{
    new AddressDetails("DB100.DBD0", DataType.Double, length: 1, encodingType: EncodingType.ANSI)
    {
        AddressDescribe = "温度传感器读数",
        AddressType = AddressType.Reality,
        AddressMqParam = new AddressMq("sensors/temperature", "{0}", null)
    },
    new AddressDetails("M100", DataType.Bool)
    {
        AddressDescribe = "急停状态"
    },
    new AddressDetails("VirtualRandom{500,0^1000}", DataType.Int)
    {
        AddressDescribe = "模拟计数器",
        AddressType = AddressType.VirtualDynamic_RandomScope
    }
};

var address = new Address("Line1_Station3", details, DateTime.Now);

读写数据流

using Snet.Core.handler;
using Snet.Core.virtualAddress;

var vaManager = new VirtualAddressManage();

// 初始化虚拟地址
foreach (var detail in address.AddressArray)
{
    vaManager.InitVirtualAddress(detail, out bool isVA);
    if (isVA) Console.WriteLine($"虚拟地址: {detail.AddressName}");
}

// 处理来自硬件的值
string hardwareValue = "23.5";
AddressValue? result = address.AddressArray[0].ExecuteDispose(hardwareValue, "读取成功");

if (result != null)
{
    Console.WriteLine($"质量: {result.Quality}");          // Normal
    Console.WriteLine($"值: {result.ResultValue}");       // 23.5 (double)
    Console.WriteLine($"原始值: {result.OriginalValue}");  // 23.5 (double)
}

// 转换为传输格式
var simplify = result.GetSimplify();
// simplify.VAL == "\"23.5\"", simplify.Q == Normal

// 批量传输
var dict = new ConcurrentDictionary<string, AddressValue>();
dict.TryAdd(result.AddressName, result);
var proto = dict.GetSimplifyProtobuf();
byte[] bytes = proto.ToProtobuf(); // Protobuf 二进制传输

虚拟地址读写

// 向虚拟地址写入值
vaManager.Write("VirtualRandom{500,0^1000}", 42);

// 读取当前值
object? currentValue = vaManager.Read("VirtualRandom{500,0^1000}");

// 动态虚拟地址在后台任务中自动更新
// 间隔为 500ms 的 VirtualDynamic_Random 点位将通过
// Task.Run + Task.Delay 循环持续生成新的随机值