Visitar URL original
GitHub - xiaomuwing/UniversalUtilities: 日常积累的静态帮助类,随时更新 · GitHub
Skip to content

About

日常积累的静态帮助类,随时更新

Resources

Stars

5 stars

Watchers

0 watching

Forks

Repository files navigation

UniversalUtilities

.NET Framework 4.8 的通用工具库。解决方案里的主工程输出类型是 WinExe,但 Program.Main 目前是空壳, 实际是当类库用的:Modbus 从站/主站、跨平台高精度定时器、多媒体时钟、数字滤波与曲线拟合、异步日志、 FTP 与 Windows Shell 文件操作、DataGridView 增强、CPU 拓扑与线程亲和性,以及字节/时间/IP 等基础转换。

项目 说明
UniversalUtilities 主工程(.NET Framework 4.8,C# 12)。Debug 为 x86(含 unsafe 指针代码),Release 为 AnyCPU
UniversalUtilities.Tests MSTest 3.11.1 测试工程(必须同为 x86,否则运行期 BadImageFormatException)

目录结构

路径 说明
UniversalUtilities\ModbusServer.cs Modbus 从站(服务端),基于 NModbus 3.0.83;TCP / UDP / RTU,保留原有对外 API 与地址语义
UniversalUtilities\ModbusClient_TCP.cs Modbus 主站(客户端),基于 NModbus;只读保持寄存器,float 字节序与旧实现逐字节一致
UniversalUtilities\CrossPlatformTimer.cs 跨平台高精度定时器:Windows 用可等待定时器 + 自适应 busy-spin,Linux 用 timerfd/eventfd
UniversalUtilities\HighResTimer.cs Windows 专用高精度定时器(MMCSS "Pro Audio" + 可选线程亲和性绑定)
UniversalUtilities\MMTimer.cs winmm 多媒体时钟(timeSetEvent/timeKillEvent),带同步对象封送与优雅停止
UniversalUtilities\Filters.cs 均值 / 中位数 / 奥林匹克 / 高保真中位数滤波(滑动有序窗口,O(N))
UniversalUtilities\GaussianFilter.cs 高斯滤波(偶数核自动取奇数,避免半样本偏移)
UniversalUtilities\SavitzkyGolayFilter.cs Savitzky-Golay 平滑(最小二乘卷积系数)
UniversalUtilities\Fitting.cs 最小二乘多项式拟合(法方程 + 列主元高斯消元)
UniversalUtilities\ArrayManipulator.cs 数组统计:最值、均值、绝对均值、峰峰值、半峰值(含 unsafe 指针重载)
UniversalUtilities\Logger.cs 异步日志:入队即返回,后台线程落盘,按日期分片、单文件 8 MB 轮转
UniversalUtilities\FTPClient.cs FluentFTP 封装(连接、列目录、上传下载)
UniversalUtilities\FileOperateProxy.cs Windows Shell(SHFileOperation)文件操作:删除到回收站、移动、复制、重名自动改名
UniversalUtilities\CustomDataGridView.cs、ColumnStruct.cs DataGridView 增强:列显示/列宽持久化、字号缩放、行高调整、滚动与拖拽
UniversalUtilities\CpuTopology.cs CPU Set / P 核 E 核查询、逻辑处理器索引映射、把线程绑定到指定 CPU
UniversalUtilities\NativeMethods.cs Win32 P/Invoke:内存拷贝、winmm 定时器、comctl32 图像列表拖拽
UniversalUtilities\Utilities.cs 通用工具:内存拷贝、字节序与十六进制/BCD、CRC16、时间戳与 OLE 自动化日期互转、IP↔整数、枚举显示名、压缩解压等
UniversalUtilities\Exceptions.cs Modbus 相关异常类型(连接、CRC、功能码、数量、起始地址等)
UniversalUtilities\Program.cs 程序入口(当前为空壳,Application.Run 被注释)
UniversalUtilities\Properties\ 程序集信息与资源(*.Designer.cs 为自动生成,不要手工改)
UniversalUtilities.Tests\ 测试工程、测试用例与一键运行脚本(见 UniversalUtilities.Tests\README.md)
docs\ 代码审查报告(历史缺陷与修复记录)、测试覆盖率报告

依赖

通过 packages.config 还原到仓库内的 packages\ 目录(旧的 NuGet 方式,不生成 PackageReference):

FluentFTP 55.0.0、NModbus 3.0.83、MathNet.Numerics 5.0.0、Newtonsoft.Json 13.0.4、 System.Memory 4.6.3、System.Buffers 4.6.1、System.Numerics.Vectors 4.6.1、 System.Runtime.CompilerServices.Unsafe 6.1.2、System.IO.Compression.ZipFile 4.3.0;测试用 MSTest 3.11.1。

容易踩的语义约定

  • Modbus 地址映射:Modbus 地址 A 对应 localArray[A + 1],四个数据容器(保持寄存器/输入寄存器/线圈/离散输入) 的下标 0 一律保留不用。
  • 单元标识:unit id 等于 UnitIdentifier,或 unit id = 0(广播寻址)时才响应,其它请求直接丢弃(不返回异常)。
  • 功能码开关:FunctionCodeXxxDisabled = true 时,对应功能码请求返回异常响应(异常码 1:非法功能)。
  • 浮点寄存器:SetHoldingRegisterValue(address, value) 把 float 的高 16 位写到 localArray[address]、 低 16 位写到 localArray[address + 1];主站读回的 float 与该布局逐字节一致。
  • 日志落盘:{程序目录}\logs\{前缀}_{yyyyMMdd}({编号}).log,UTF-8(无 BOM),单文件超过 8 MB 自动切下一个编号。 队列上限 100000;队列满或已经停止接收时,日志被静默丢弃并计入 Log.DroppedCount,写入方法不抛异常、不阻塞业务线程。
  • 定时器回调:InvokeHandlersOnThreadPool 默认为 false,即在定时器线程上同步回调(回调耗时会长会拖慢后续周期); 自旋窗口会按实测漂移自适应调整,CPUAffinity 打开时把定时线程绑到前两个逻辑 CPU。
  • 文件操作返回值:FileOperateProxy 的删除/移动/复制返回 0 表示成功,其它非零值是 Shell 错误码, -200 表示托管异常(异常消息写入 errorMsg)。

使用示例

// Modbus 从站:监听 505 端口,数据容器索引 0 保留(索引 1 对应 Modbus 地址 0)
using (var server = new ModbusServer { Port = 505, UnitIdentifier = 1 })
{
    server.holdingRegisters[1] = 1234;      // 写保持寄存器
    short value = server.holdingRegisters[1];
    server.SetHoldingRegisterValue(10, 3.14f);   // float 拆成两个寄存器
    server.HoldingRegistersChanged += (register, count) => { /* 主站写入后的处理 */ };
    server.Listen();                        // TCP(UDPFlag / SerialFlag 可切换到 UDP / RTU)
    // ... 运行中 ...
    server.StopListening();
}

// Modbus 主站:读保持寄存器(只支持 FC3)
using (var client = new ModbusClient_TCP("127.0.0.1", 505, demo: false))
{
    client.Connect();
    float[] values = client.ReadHoldingRegisters(startingAddress: 0, quantity: 10);
    client.Disconnect();
}

// 异步日志:入队即返回,后台线程落盘到 {程序目录}\logs\{前缀}_{日期}({编号}).log
Log.WriteLog("设备已连接", source: "Main", preText: "app");
Log.WriteUnifiedLog("标定完成", category: "校准", source: "Calib");
Log.Flush();                               // 退出前确保落盘(或 ShutdownAndFlush)

// 滤波与拟合
double[] smooth = Filters.MeanFilter(data, 5);
double[] gauss = GaussianFilter.Gaussian(data, stdDev: 2.0, kernelSize: 5);
double[] coefficients = Fitting.GetCoefficient(xs, ys, xs.Length, dimension: 2);  // 按升幂排列
var sg = new SavitzkyGolayFilter(sidePoints: 5, polynomialOrder: 2);              // 构造时算好系数,实例可复用
double[] filtered = sg.Process(data);

// 高精度周期定时器(回调默认在定时器线程上同步执行)
using (var timer = new CrossPlatformTimer(intervalMs: 10, spinWindowMs: 2))
{
    timer.InvokeHandlersOnThreadPool = true;   // 回调耗时较长时建议打开
    timer.Elapsed += (s, e) => { /* 周期任务 */ };
    timer.Start();
    // ...
    timer.Stop();
}

构建

推荐用 Visual Studio 打开 UniversalUtilities.sln 直接生成;命令行方式(在 VS 开发者命令提示符里 msbuild 即在 PATH 上):

# 编译整个解决方案
msbuild UniversalUtilities.sln /p:Configuration=Debug /t:Rebuild /v:m /nologo

# 只编译主工程,并顺带生成 XML 文档(工程默认不生成文档文件)
msbuild UniversalUtilities\UniversalUtilities.csproj /p:Configuration=Debug /p:DocumentationFile=bin\Debug\UniversalUtilities.xml

注意:/p:DocumentationFile 请只对主工程使用。若在解决方案级别传入,测试工程也会跟着生成文档, 而测试用例的公开成员没有文档注释,会刷出一批 CS1591 警告。

测试

# 一键:编译 → 跑全部用例 → 采集并汇总覆盖率(Debug,默认)
powershell -ExecutionPolicy Bypass -File UniversalUtilities.Tests\RunTests.ps1

# Release 配置 / 列出未覆盖行 / 跳过编译
powershell -ExecutionPolicy Bypass -File UniversalUtilities.Tests\RunTests.ps1 -Configuration Release -ListUncovered

# 只跑某一类(脚本内部用命名互斥体串行化 MSBuild,适合多人同时改代码)
pwsh -NoProfile -File UniversalUtilities.Tests\RunOne.ps1 -Filter 'FullyQualifiedName~ModbusTests'

当前基线(2026-09-24 实测,Debug):242 个用例全部通过,行覆盖率 86.7%(3823/4407)、 分支点覆盖率 93.5%(550/588)。未覆盖部分主要是 Linux-only 分支、真实串口/公网 FTP、以及需要极端并发才能触发的防御代码, 逐条原因见 docs\UniversalUtilities-测试覆盖率报告.md。

代码约定

  • 公共可见的类型与成员都有中文 XML 文档注释(<summary> / <param> / <returns> / <exception>), 编译时 CS1591(缺少文档注释)保持为 0;提交前请勿引入新的文档告警。
  • 文档注释里出现 <、>、& 必须转义为 &lt;、&gt;、&amp;;结束标签不要写错(例如 </summary>)。
  • 字段名不加下划线前缀(private readonly object syncRoot; 这种形式);唯一例外是 C# 保留字, 锁对象字段写作 @lock。
  • 成员级说明用 ///,行内说明用 //;Properties\*.Designer.cs、AssemblyInfo.cs 由工具生成,不要手工加注释。
  • 已知遗留告警:16 条 CS8632(CrossPlatformTimer.cs / HighResTimer.cs 在未开启 #nullable 的上下文里使用 ? 可空注解), 属历史代码,未做改动。

相关文档

文档 内容
docs\UniversalUtilities-代码审查报告.md 历史缺陷清单与修复记录(含旧手写 Modbus 实现的问题:FC5/FC6 地址越界、FC15/FC16 字段不匹配、定时器句柄泄漏等)
docs\UniversalUtilities-测试覆盖率报告.md 覆盖率明细、未覆盖行号与原因
UniversalUtilities.Tests\README.md 测试工程的使用说明、测试文件一览与约定

About

日常积累的静态帮助类,随时更新

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages