[{"title":"从 Web 到 .NET 桌面:2016—2021 公开仓库技术轨迹","url":"/2021/12/31/2016-2021-public-repositories-review/","content":"GitHub 上最早的一批仓库始于 2016 年。现在回看,它们更像一份持续多年的技术实验记录:前端页面、播放器、博客主题、Java Web、CMS、爬虫、WPF 工具和 C# 公共类都曾是阶段性重点。\n\n\n2016:先把 Web 做出来早期项目集中在 HTML、JavaScript、Bootstrap、媒体播放、Hexo 和 Java Web。FrontEndGuide、mediaelement、video.js、WebJava、WebDemo、cms 等仓库反映了当时最直接的目标:快速理解页面、组件、后台和部署之间的完整关系。\n这一阶段最大的收获并不是掌握了某个框架,而是形成了“可运行结果优先”的习惯。页面必须能打开,数据必须能展示,部署后必须能访问。后来做桌面、设备和服务端系统时,这个习惯仍然有效。\n2017—2019:从功能代码走向工具沉淀Common.Utility 汇总了文件、网络、序列化、加密、图像、文档、任务和扩展方法等常用能力。它代表了一个明显变化:不再满足于完成单个功能,而是开始考虑如何让代码在下一项目中复用。\n同一时期的爬虫、音乐、定时器和桌面界面项目,则让网络请求、异步任务、UI 状态和异常处理逐渐成为日常问题。\n2020—2021:重点转向 C#、WPF 与 Windows随着业务对桌面能力和系统集成的要求增加,技术重心逐步转向 C#/.NET。WPF 运行时检查、Inline Hook、桌面 UI 和各种 Windows 工具类,使关注点从“网页功能”扩展到进程、窗口、消息、系统 API 和运行时状态。\n这段经历后来直接影响了几个长期方向:\n\nUI 自动化不能只依赖控件表面,需要理解窗口句柄、进程边界和可访问性树。\nWindows API 调用必须考虑权限、超时和不可中断阻塞。\n桌面软件的错误恢复比一次成功更重要。\n公共能力要从复制代码升级为有边界、有测试、可观察的组件。\n\n今天再看这些仓库早期仓库使用的框架和写法并不都适合今天的新项目,但它们仍然有价值:它们解释了当前技术偏好从哪里来,也提醒我避免只追逐框架名称。真正长期有效的能力仍是问题拆解、边界识别、失败处理和完整交付。\n","categories":["项目复盘"],"tags":["Web","CSharp","WPF"]},{"title":"从 Windows 工具到远程设备链路:2022—2024 的系统集成实践","url":"/2024/12/31/2022-2024-windows-usb-network-road/","content":"进入系统集成领域后,问题不再只发生在应用进程内部。一次远程设备连接可能依次经过业务服务、网络、硬件控制器、USB Hub、Windows PnP、设备驱动和客户端映射,任何节点异常都会表现为“设备连不上”。\n\n\n网络可达不等于设备可用SuperNAT 以及 USB/IP 相关仓库的研究,让我逐渐把“连接成功”拆成多层状态:\n\nTCP 或隧道是否建立;\n远端服务是否能枚举设备;\nWindows 是否完成驱动绑定;\n设备接口是否进入可用状态;\n客户端是否获得独占权并完成映射;\n上层业务协议是否真正收到正确响应。\n\n只检查 Ping 或端口会制造大量误判。可靠系统必须给每一层定义可观测信号、超时和错误码。\nWindows 设备状态具有异步性USB 上电后,PnP 枚举、驱动加载、接口创建和上层服务发现不是一个原子动作。设备管理器里出现名称,也不代表服务已经可以安全使用它。反过来,服务列表暂时缺少设备,也不一定意味着枚举最终失败。\n因此链路控制不能堆固定延时,而应采用“状态轮询 + 总超时 + 稳定窗口”:只有关键状态连续满足一段时间,才进入下一步;失败时记录停在哪个节点,而不是只返回一个布尔值。\n恢复操作也可能失败禁用/启用设备、重启服务、释放串口和端口断电都属于恢复动作,但恢复动作本身可能阻塞、超时或引起设备重新编号。更安全的设计是:\n\n将可能卡住的系统调用隔离到独立进程;\n给外层编排设置硬超时,超时后可终止隔离进程;\n通过设备实例路径和硬件标识重新发现设备,不长期依赖 COM 号;\n限制自动恢复频率,避免形成电源和枚举风暴;\n保留操作前后的完整快照,方便定位真正原因。\n\n形成链路化思维这阶段最大的技术变化,是从“写一个控制设备的方法”转向“设计一条可验证、可恢复的连接状态机”。它后来也延伸到串口稳定性、MQTT 推送、定时任务和跨平台服务中:不要只关注成功路径,要把失败路径当作系统的一等公民。\n","categories":["项目复盘"],"tags":["Windows","USB","Networking"]},{"title":"2026 技术实践回顾:基础平台、电子墨水屏与本地 AI","url":"/2026/08/27/2026-engineering-focus/","content":"2026 年公开项目的重点可以归纳为三个方向:构建可复用的基础平台、让电子墨水屏具备完整服务能力,以及把本地 AI 接入真实工程流程。\n\n\n基础平台:减少重复开发MyBaseSystem 把通用后台能力重新按 DDD 与 Clean Architecture 组织。这里关注的不是再做一套用户角色页面,而是建立后续项目都能遵守的边界、接口规范和目录结构。\n墨水屏:软硬件必须一起设计CrossMux 负责设备侧中文阅读、文件同步和 MQTT 智能待机;AirPageSystem 负责服务端数据、模板、预览、调度和设备推送。两者把频繁变化的模板与数据留在服务端,把低功耗显示和必要校验留在设备端。\n这种分工同样适用于其他资源受限设备:服务端承担复杂计算,设备端保持协议简单、状态明确、失败可恢复。\n本地 AI:性能只是一个维度本地大模型、YOLO、OCR 和 TTS 的实践让 AI 从在线接口变成可以控制的基础能力,但工程问题也随之增加:显存与上下文如何分配、长文档怎样分块、推理速度为什么随上下文下降、模型输出怎样进入有状态任务,以及失败后如何续跑。\n真正可用的 AI 系统需要模型之外的任务队列、数据版本、进度、日志、人工确认和结果校验。Agent 也不应该获得无限权限,而应通过明确工具完成可审计操作。\n三个方向的共同点看似不同的项目最终都指向同一组原则:\n\n用明确边界隔离变化;\n用状态和证据替代盲目重试;\n把安全与恢复设计进正常流程;\n先完成真实闭环,再提炼可复用能力;\n让自动化减少重复劳动,而不是隐藏错误。\n\n这些原则会继续成为后续项目的技术主线。\n","categories":["项目复盘"],"tags":["DotNet10","Embedded","LocalAI"]},{"title":"AirPageSystem:从一张图片到可运营的墨水屏推送平台","url":"/2026/08/26/airpage-system-design/","content":"AirPageSystem 基于 .NET 10 与 Vue 3,用于采集行情、服务器状态和自定义 JSON 数据,生成适合电子墨水屏的图片并按计划推送。\n\n\n推送图片只是最后一步一个可长期使用的平台至少需要处理数据源、模板、预览、设备、计划任务、权限、历史记录和失败重试。如果这些能力直接写在一个定时方法里,新增一种面板就会牵动整个系统。\n项目将流程拆成稳定的几个阶段:采集数据、标准化模型、模板渲染、设备格式编码、推送、记录结果。新增数据源或模板时,只需要扩展相应阶段。\n墨水屏格式是硬约束目标设备使用 528×792、四级灰度和 2-bit BMP,并有文件大小限制。普通浏览器预览图即使看起来正确,也不代表固件一定能解码。\n因此系统同时生成用于预览的 PNG 与固件兼容 BMP,并在推送前检查尺寸、调色板、位深、扫描方向和大小。格式校验必须位于服务端边界,不能把错误文件发送后再依赖设备兜底。\n调度系统要留下执行证据Cron 到点未执行通常不是一句“调度器有问题”就能解释。任务可能被禁用、时区错误、进程当时未运行、前一次仍在执行,或者执行了但在数据采集阶段失败。\n可靠调度至少应记录计划时间、实际开始时间、完成时间、触发来源、执行节点、状态和错误摘要。测试执行与计划执行应复用同一条执行管线,避免“测试正常、定时失败”成为两个不同系统。\n自定义能力需要安全边界允许用户配置 HTTP JSON 数据源会引入 SSRF 风险。系统默认禁止访问环回和 RFC1918 私网,只有明确配置后才允许局域网监控。模板使用受限映射,而不是执行任意脚本。\n可扩展并不等于无限执行能力。好的模板系统应该允许用户组合数据和布局,同时保持网络、凭据和代码执行边界可控。\n","categories":[".NET"],"tags":["DotNet10","Vue3","EInk","Scheduler"]},{"title":"CrossMux:给小型墨水屏阅读器增加中文、同步与智能待机","url":"/2026/08/25/crossmux-embedded-eink/","content":"CrossMux 是基于 CrossPoint Reader 演进的社区固件,面向 ESP32-C3 的 Xteink X3/X4。项目目标不是简单增加几个菜单,而是在有限内存、存储、刷新速度和电量预算下,让设备具备更完整的中文阅读与联网能力。\n\n\n中文支持不是替换几段文案电子书中的中文涉及字体体积、字形缓存、换行规则和渲染速度。固件需要在 Flash、内存和排版质量之间取舍:字体不能无限增大,文本布局不能假设以空格断词,刷新区域也要尽量稳定。\n因此中文化的核心是把 CJK 当作排版能力,而不是只把 UI 字符串翻译为中文。\n文件同步要避免留下半文件1.5.8 引入自托管同步服务。设备根据 SHA-256 跳过未变化文件,并使用临时文件和备份文件完成替换。这个设计解决的不是“怎样下载”,而是弱网络和断电条件下怎样保证文件系统仍然一致。\n服务端使用 .NET 提供上传、清单和下载能力;固件端负责增量判断、安全落盘和目录约束。职责边界清晰后,两端都更容易测试。\nMQTT 智能待机的正确边界1.5.9 的智能待机由服务端生成天气、行情、歌词或自定义数据面板,设备通过 MQTT 接收更新。选择服务端渲染而不是让固件解释复杂模板,有三个原因:\n\n固件内存有限,不适合承载完整排版引擎;\n模板和数据源变化频繁,服务端升级成本更低;\n最终位图可在推送前预览和校验,降低设备端失败概率。\n\n设备端仍需处理断线重连、消息去重、图片校验和睡眠策略。网络功能不能无限阻止自动休眠,持续连接失败也必须主动让出资源。\n桌面模拟器的价值墨水屏设备编译、烧录和刷新都比普通 Web 调试慢。桌面模拟器让菜单布局、输入逻辑和部分渲染可以在主机上快速验证,把硬件测试集中到真正依赖设备的环节。这是嵌入式项目提升迭代速度最有效的工程投入之一。\n","categories":["嵌入式"],"tags":["EInk","CrossMux","ESP32","MQTT"]},{"title":"MyBaseSystem:用 DDD 与整洁架构构建可复用的 .NET 10 基础系统","url":"/2026/08/27/my-base-system-ddd-clean-architecture/","content":"MyBaseSystem 的目标,是把登录、用户、角色、权限、菜单、审计和基础配置等重复工作沉淀为长期复用的平台。前端基于 Vue 3 Monorepo,后端使用 .NET 10、DDD 与 Clean Architecture。\n\n\n通用系统最容易变成“大杂烩”基础系统功能多、关联广,很容易让控制器直接访问数据库,让权限判断散落在页面和接口里,最终每次复用都需要大幅裁剪。真正的复用不是拥有更多公共方法,而是拥有稳定的边界。\n后端依赖方向始终指向内层:\nApi -> Application -> DomainInfrastructure -> Application / Domain\n\nDomain 不依赖 EF Core 或 ASP.NET Core;Application 编排用例并定义端口;Infrastructure 实现存储、JWT 和外部服务;Api 只负责 HTTP 映射、认证与统一响应。\n前后端目录必须明确分离所有前端代码放在 frontend/,所有 .NET 解决方案、源码和测试放在 backend/。这不是单纯为了目录美观,而是为了让工具链、依赖缓存、容器构建和 CI 任务拥有清晰作用域。\n前端业务应用与通用组件继续分层;后端则按照领域、应用、基础设施和 API 分层。一个新业务模块应作为限界上下文接入,而不是继续向 SystemService 中堆方法。\n权限是服务端契约菜单用于导航,权限码用于授权,两者不能混为一谈。权限采用 领域:资源:动作 的形式,例如 system:user:update。前端可以据此隐藏按钮,但服务端仍必须执行最终授权。\n用户获得角色后,菜单树应从权限结果动态生成;角色维护则同时展示菜单名称和代码,避免配置人员面对无法理解的标识符。\n开箱即用与生产安全之间的平衡SQLite 和管理员种子数据让项目可以快速启动,但生产环境必须更换 JWT 密钥和初始密码,并按目标数据库增加 Infrastructure Provider。领域层和应用层不应因为数据库从 SQLite 切换到 PostgreSQL 或 SQL Server 而改变。\n基础系统的价值不在于功能列表有多长,而在于新项目接入后能否继续保持边界清楚、行为可测试、升级可控。\n","categories":[".NET"],"tags":["DotNet10","Vue3","DDD","CleanArchitecture"]},{"title":"远程 USB 连接为什么难:一条可观测链路的设计方法","url":"/2026/07/12/remote-usb-diagnostics/","content":"远程 USB 最难处理的不是正常连接,而是偶发失败:同样的设备、相同的指令和相近的网络条件,有时几秒成功,有时一直停在“可见但不可用”。要解决这类问题,首先需要停止把整条链路看成一个接口调用。\n\n\n五层检查模型1. 硬件与供电层确认端口上电状态、工作电压、电流、过流标志和 Hub 上游连接。多个端口同时启动时应分批上电,避免瞬时压降导致整个 Hub 重新枚举。\n2. Windows 枚举层使用设备实例 ID、父子拓扑和问题码判断设备是否完成枚举。只按 VID/PID 匹配不够,因为多个同型号设备可能共享这些字段。\n3. 共享服务层检查服务是否运行、枚举列表是否刷新、目标设备是否空闲,以及服务看到的地址是否稳定。这里要区分“服务无响应”和“服务响应但没有设备”。\n4. 网络与映射层记录 DNS、TCP、认证、设备绑定和客户端驱动创建各阶段耗时。网络延迟较低时,固定几秒等待往往来自应用策略或枚举稳定窗口,而不是链路 RTT。\n5. 业务验证层设备出现在设备管理器并不是终点。必须执行一个只读、低风险的业务探针,例如读取设备信息或发送协议握手,确认上层确实可用。\n状态机优于固定延时每一阶段都应包含进入条件、成功条件、软超时、硬超时和恢复策略。固定 Task.Delay(3000) 无法解释为什么等三秒,也无法在设备提前就绪时缩短耗时。\n建议为一次连接生成统一 CorrelationId,把硬件控制、Windows 事件、服务日志、客户端日志和业务响应关联起来。这样才能回答“慢在哪里”,而不只是知道“最后失败了”。\n自动恢复的边界自动恢复应从轻到重:重新查询、刷新枚举、释放占用、重启单设备、端口断电重启,最后才考虑重启服务或主机。每一级都要限制次数并设置冷却时间。连续失败后应停止自动操作并告警,避免把原本局部的异常扩大成整机 USB 风暴。\n远程 USB 的稳定性不是靠更多重试获得的,而是靠更细的状态、更准确的证据和有边界的恢复策略获得的。\n","categories":["USB与硬件"],"tags":["Windows","USB","USBIP","Diagnostics"]}]