SMARTSHELF-API-AND-DOTNET8-MIGRATION.md 26.5 KB

SmartShelf 接口、功能与 .NET 8 迁移概要

本文档描述 SmartShelf(料架/库位灯控边缘服务) 的 HTTP 接口、核心业务与硬件/数据契约,便于重做 .NET 8 版本时对齐行为。(不含 Web 前端、SPA、模板页面。)


1. 项目定位

设备侧服务承担三类事:

  1. 解析库位表:从 CSV 建立「库位编码 → 灯条通道 + 像素序号」的映射。
  2. 驱动硬件:两路 WS281x 灯条;六路 GPIO 数字输出驱动 A/B 两面各绿/黄/红三色「灯塔」状态灯。
  3. 与上位交互:HTTP API 直连;并可按需启动后台线程,以固定周期 POST {服务器基址}/service/store/communication,根据响应 JSON 里的 data 批量改灯、控塔灯、闪烁定时等。

运行身份(写死):本服务在树莓派上 必须以 root 身份运行(例如 systemd User=root,或直接 sudo 启动)。不要求也不支持以普通用户 + udev/capability 等折中方式作为正式部署路径——WS281x DMA、GPIO 及若干运维接口均按 root 可用 为前提设计。


2. HTTP 接口清单

2.1 配置与库位列表

方法 路径 响应 / 行为
GET /config_state JSON:true / false(库位 CSV 是否成功加载)
GET /positionlist JSON 数组:全部库位编码(与 CSV 第一列一致,不含表头)
POST /upload/ 上传文件到约定目录后重新加载库位表
POST /setconfig JSON 与运行时 货架配置 同名字段合并后持久化

货架配置(默认值可被持久化覆盖):

字段 含义
LED_ColorSort 灯条颜色分量顺序:RGBGRB(与 rpi_ws281x 的 Color 构造一致)
LED_COUNT 单条灯带逻辑像素个数(初始化 PixelStrip 时使用)
TOWER_CUSTCONTROL 非 0 时:关闭「运行中有库位亮灯则自动点亮该侧黄塔灯」「启动自检里自动拉绿色塔灯」等默认塔灯联动

2.2 状态灯 / GPIO(逻辑面:channel1 = A,channel2 = B)

方法 路径 Body(JSON)
POST /workinglight workchannelworkcolorgreen \
POST /workingoff 同上 → 置低

2.3 单库位 WS281x(库位名须与 /positionlist 中一致)

方法 路径 Body(JSON)
POST /ledopen light_ledlight_led_color
POST /ledoff off_led
POST /resetled 两路灯条全黑并清空内部状态缓存

2.4 整条灯条测试

方法 路径 Body(JSON)
POST /lineledon channel_numchannel1 / channel2channel_color
POST /lineledoff 同上,整条条灭
POST /opAll opon(现实现几乎不拉亮条,仅返回)/ off(两路全黑)

2.5 聚合 JSON API

方法 路径 Body 响应
POST /api/open params:分号分隔项,如 statusa=green;5=red;statusb=yellow status:0 成功;status:1 且带 failed 字符串
POST /api/close 同上风格;可含仅数字的灭灯项、statusa / statusb 关多色等 同上

说明:statusa / statusb塔灯 GPIO;纯数字 key 表示 /positionlist 顺序下的 1-based 索引(不是 CSV 行号);越界项进入失败列表。

2.6 REST v1(GET,响应为纯文本)

方法 路径 查询 成功 / 失败文本
GET /rest/api/v1/shelf/posOn posId=库位@颜色;… posOn OK / posOn FAIL
GET /rest/api/v1/shelf/posOff posId=库位;… posOff OK / posOff FAIL
GET /rest/api/v1/shelf/allPosOn allPosOn OK / FAIL
GET /rest/api/v1/shelf/allPosOff allPosOff OK / FAIL
GET /rest/api/v1/shelf/keepAlive OK
GET /rest/api/v1/shelf/lightHouse `op=灯码@ON\ OFF;…`

lightHouse 灯码与 GPIO 对应关系见下文 §3.3

2.7 设备与其它

方法 路径 说明
GET /getmac 优先 eth* 网卡 MAC,大写
GET /download/ 下载运行日志文件(部署路径因环境而异)

2.8 上游轮询与运维状态

方法 路径 说明
POST /startpost 在内存中运行状态非 on 且已配置服务器地址时:置运行状态为 on,启动定时 POST 上游闪烁调度不写磁盘
POST /stoppost 内存中置运行状态为 offis_start=false,复位灯与轮询相关内存态
POST /getstate 返回字段 state当前进程内存中的运行状态(on/off);另含与服务器通信摘要、本地版本号等
POST /taillog 日志尾部 + 最近 POST 成败摘要
POST /updateip JSON:ipcid → 写本地配置;原实现可顺带改 kiosk 自启动

2.9 网络(Linux)

方法 路径 说明
POST /ip_config form:DHCP 或静态 IP / 网关 / 掩码,写网络配置后延时重启
POST /get_networkinfo 返回 eth0 的 IP、掩码、网关 JSON

2.10 升级与其它

方法 路径 说明
POST /upgrade 比对远程 version.txt,下载 zip 覆盖应用目录后重启
POST /openteamview 启动 TeamViewer(运维)

2.11 二维码

方法 路径 说明
GET / POST /downqrcode/?code=<编码> 生成带文案的合成标贴图并下载

3. 核心功能说明

3.1 库位表文件与表头结构

  • 文件名linePositions.csv(通常位于应用下 static/uploads/)。
  • 编码:读取时做编码探测,失败时常用 GBK 回退。

仓库内示例表头(第 1 行为表头,加载时跳过:第一列值为 位置 的行不参与映射):

列索引 列名 加载时是否参与灯控映射
0 位置 — 作为库位主键(字符串,如 S11695-02-035
1 优先级 否(业务展示/排序,灯控未用)
2 高度
3 宽度
4 料仓ID
5 设备IP
6 区域ID — 在内部表示 灯条侧别/通道,字符串形式 '1''2''3'
7 灯索引 — 该库位在对应灯条上的 像素下标(整数,从 0 起)

内部映射构造规则(与现逻辑一致即可):

  • 对每一数据行:config[行[位置]] = 行[灯索引] + "@" + 行[区域ID]
    例:0@1 表示区域 1 那路灯条上像素 0
  • 区域ID 语义
    • '1':只驱动 GPIO 12 那一路 WS281x。
    • '2':只驱动 GPIO 21 那一路。
    • '3'双面 — 同一库位同时在两路上占用 相同像素下标(开灯/关灯/闪烁应对两路各写一次)。

/positionlist 返回的是 config 的 key 按 CSV 出现顺序 的列表;/api/open 用的数字索引是 该列表的 1-based 下标

3.2 WS281x 灯条(两路)

逻辑通道(区域ID) 数据线 BCM GPIO 说明
1 12 常称 channel1 / A 面条
2 21 常称 channel2 / B 面条

驱动库默认参数(与现有 Python 初始化一致便于对时序):

参数 典型值
信号频率 800 kHz
DMA 10
信号反相
亮度 0–255 内可配置,默认约 100
PWM 通道号 0

像素数量:运行时由货架配置 LED_COUNT 决定(默认量级可达千级,需与真实灯珠数一致)。

颜色顺序LED_ColorSort 为 RGB 时,Color(R,G,B) 按标准传参;为 GRB 时底层需交换 R/G 再送进控制器(与 WS2812 常见布线一致)。

命名颜色 → RGB(HTTP/API 颜色名不分大小写;#RRGGBB 十六进制也支持):

名称 R,G,B
red 255,0,0
green 0,255,0
blue 0,0,255
yellow 255,255,0
white 255,255,255
off / 灭灯 0,0,0
orange 255,97,0
cyan 0,255,255
magenta 255,0,255
purple 128,0,128
pink 255,192,203
lightblue 173,216,230
skyblue 135,206,235
forestgreen 34,139,34
darkgreen 0,100,0
firebrick 178,34,34
indianred 205,92,92
其它未识别名 白色 255,255,255

REST allPosOn所有像素 设为白色 (255,255,255)

3.3 GPIO 三色灯塔(数字输出,BCM 编号)

同一套引脚被 /workinglight/workingoff、聚合 API 的 statusa/statusb/rest/.../lightHouse、以及上游 lightHouseOpenOrClosetowerLightControl 共用。

默认映射表(A/B 为物理面;高电平一般为「灯亮」,依硬件接法确认):

逻辑键 含义 BCM GPIO
greenA A 面 绿 5
yellowA A 面 黄 6
redA A 面 红 16
greenB B 面 绿 17
yellowB B 面 黄 27
redB B 面 红 23

REST lightHouse 查询参数 op 中的灯码(不区分大小写)与上表对应:

灯码 逻辑键 GPIO
GA greenA 5
YA yellowA 6
RA redA 16
GB greenB 17
YB yellowB 27
RB redB 23

每段格式:灯码@ON灯码@OFF,多段用 ; 分隔。任一段格式错或灯码未知 → 整体可返回 FAIL

备注:代码里曾保留一套 A/B 面对掉 的注释映射(绿黄红引脚整组交换),若现场接线不同,.NET 版应做成可配置表,而非写死。

3.4 HTTP 业务与内部状态

  • /ledopen / /ledoff:除改像素外,还在内存字典里维护「当前点亮集合」,便于一条上多库位累加点亮后再 show()
  • /resetled:两路各清全带颜色为黑,并清空上述字典。
  • /api/open/api/close:直接操作 strip 缓冲区并 show();与上面的「测试用字典」非同一套状态,并行调用时以最后 show() 为准。
  • 并发:灯条与 GPIO 均被多线程(HTTP + 轮询 + 闪烁)触碰,迁移时应对 每条 strip 或全局灯控加互斥,避免交错 show()

3.5 上游轮询:/service/store/communication

  • 请求:POST JSON,Content-Type: application/json。Body 含 cid、递增 seqopstatus 等;业务方主要关心返回体中的 data 对象。
  • 典型 data 字段与语义
字段 格式 行为概要
open 多段用 `\ 分隔,每段库位=颜色库位=颜色=毫秒`
close `\ ` 分隔;每段至少含库位
closeAll 任意真值 两路清屏(逻辑等同全灭)
openAll 颜色名 两路所有像素设为该颜色
lightHouseOpenOrClose `\ 分隔多段;每段 **open=侧=颜色** 或 **close=侧=颜色**(恰好两个=` 拆成三段)

库位名字符串须在 库位表 中存在;否则记日志并跳过。

独占塔灯行为(当 开启 TOWER_CUSTCONTROL 时):

  • 轮询路径维护「每通道是否还有亮着的库位像素」。
  • A 面(通道 1 有任意库位亮 → yellowA (GPIO 6) 拉高;全灭 → 拉低。
  • B 面(通道 2 同理 → yellowB (GPIO 27)
  • 开启 TOWER_CUSTCONTROL 后,上述自动黄灯与 启动自检末尾拉 greenA/greenB 可被跳过,仅保留显式塔灯指令。

启动自检(轮询线程开始时,未开 TOWER_CUSTCONTROL):每条灯带依次整带红 → 绿 → 蓝 → 灭;然后 greenA(5)、greenB(17) 置高。

3.6 闪烁实现要点

  • (通道, led_index) 在闪烁结构体中保存:颜色名、剩余毫秒数、state 翻转。
  • 调度周期 约 333ms:每次递减剩余时间;若仍大于 0,则翻转亮/灭并在条上写当前色或 off;若 ≤0,写灭并从表删除。

3.7 持久化与路径(部署约定)

用途 常见相对/绝对路径
库位 CSV …/static/uploads/linePositions.csv
服务器 ip + cid …/state/ipconfig.csv(列:ip,cid
轮询运行状态 仅存进程内存on/offis_start 等);不使用 state.txt,进程退出即丢失,重启后需再次 startpost 或依赖 §3.9 自拉起逻辑
货架配置 shelfconfig.pkl,.NET 可改为 JSON
设备版本 version.txt
日志 logs/smart.log(轮转)

3.8 进程启动:加载顺序与一次性初始化

下列顺序描述 宿主进程已启动、即将/正在构建 WebHost 阶段应完成的工作(与现 Python 实现等价即可,不必逐文件同名)。

阶段 内容
1. 日志 配置日志级别(如 WARNING);挂 滚动文件 Handler(单文件大小上限、保留个数),路径如 logs/smart.log
2. 全局配置类 读入 SECRET_KEY、上传目录、state 目录、LOG_*DEFAULT_COLORTOWER_CUSTCONTROL 等(可来自环境变量 + 默认值)。
3. 货架硬件配置 从持久化文件加载 LED_ColorSortLED_COUNTTOWER_CUSTCONTROL;若无文件则用默认(如 LED_COUNT=1000,颜色顺序 RGB)。
4. 库位表 读取 linePositions.csv(chardet + 回退编码);跳过表头行(首列 位置);填充内存映射 config_dict / option_list,并置 config_state。失败则 config_state=false,接口仍应答但库位相关操作会失败或打日志。
5. 服务器连接参数 state/ipconfig.csv 首行两列 → 内存中的 ipcidpost 状态可先为 wait。文件缺失或读失败 → ip 为空并打日志(不得在未处理异常时崩溃整个进程)。
6. 注册路由 / Minimal APIs 挂载 §2 全部 HTTP 端点。
7. 惰性硬件 WS281x get_strip/初始化:首次需要点灯时再创建(按 PIN 12 / 21 与 LED_COUNT);避免在无任何 CSV 时强行占 DMA。
8. 轮询全局标志 内存中 is_start = false,以及对外 API 使用的 运行状态字符串on/off,与 startpost/stoppost/getstate 一致);均不持久化

version.txt

  • /getstate 等读取本地版本号一行文本;若不存在可返回空字符串或默认,避免抛错。
  • 与旧版 Python 的差异:旧实现另用 state.txt 持久化轮询开关;本迁移目标不再读写 state.txt,仅以内存为准。

与 Python 的差异说明:源码末尾注释块里「import 后直接起 serverpost未启用;真实「上电后自动连服务器」依赖下面 §3.9 的 HTTP 自调用线程。

3.9 启动后:监听就绪后的自动线程(原 __init__ 行为)

Web 服务 开始监听 之后,应启动(或调度)一条 后台线程,逻辑等价于:

  1. 等待约 2 秒(给 Kestrel/反向代理就绪时间)。
  2. POST http://127.0.0.1:<端口>/stoppost(body 可为空或非 JSON,现实现不校验)。
  3. 再等待约 2 秒
  4. POST http://127.0.0.1:<端口>/startpost
  5. 打日志「启动完成」类信息。

语义:进程每次拉起时 先停轮询再启轮询,等价于强制走一遍「关闭成功 / 启动成功」状态机,使内存中运行状态最终为 on 且(在 ip 非空时)开始 §3.10 的双线程。

迁移注意

  • 端口须与 Kestrel 实际监听一致(现网常 5000)。
  • 若 2 秒内服务尚未监听,stoppost 会失败;.NET 可改为 重试HostedService 在 IHostApplicationLifetime.ApplicationStarted 之后再执行,比固定 sleep(2) 稳妥。
  • stoppost 会执行 灯条黄闪复位、post_leds 清空、is_start=false、内存运行状态 off 等;startpost 在运行状态已为 on 时不会重复起线程,但自动线程先 stoppost 会把状态拉回 offstartpost,因此 每次冷启动仍会重新创建轮询与闪烁线程

3.10 运行期:startpost 成功后的双循环

POST /startpost 判定可启动时(内存中运行状态非 onip 非空):

  • 成功后:内存运行状态置 onis_start=true,启动下列两线程;不写任何「状态」文件
  • ip 为空或已在运行:不启线程,返回文案与现网 JSON 形状一致即可。
线程 / 任务 行为
serverpost ① 执行 启动自检 start_show(§3.5 灯条 RGB 扫描 + 未禁塔控时 greenA/greenB)。② 为通道 12get_strip 填入 strips。③ seq=1while is_start:调用 出站 POST communication(§3.4);若有返回且 data 非空则 resolve_datacheck_leds()(自动黄灯);seq++sleep(1)
blinkLedProcess threading.Timer(0.333, …) 链式重入 或等价周期任务:在 blink_lock 下遍历 blink_led,对每个条目 waittimecount -= 333,未到 0 则翻转亮灭并写像素、strip.show();到 0 则灭灯并删除。仅当 is_start 仍为 true 时注册下一次 333ms。

POST /stoppost:内存运行状态置 off不写 state.txt),is_start=falsereset_strip()(每路黄 wipe → 黑 wipe,清空 post_leds),post 状态置 wait,并依赖 check_leds() 关自动黄灯。

与 HTTP 灯控 API 的关系

  • /ledopen/ledoff/api/*/rest/* 等多数直接操作 strip,与 post_leds / blink_led 不一定同步(现实现存在多套内存状态)。.NET 可统一为单一「灯态」权威源;若需行为级兼容,需按端点分别对照 Python 侧字典与 strip 写入顺序。

全局变量 is_start:与 app 模块同级; startpost / stoppost / serverpost / blinkLedProcess 读写;HTTP 其它路径不直接改。

3.11 运行期:配置热更新

事件 行为
POST /upload/ 成功 重新执行与 §3.8 第 4 步相同的 CSV 解析,替换 config_dict / option_list / config_state。注意:现 Python 热加载可能把 CSV 表头行误写入映射,.NET 建议始终跳过表头。
POST /setconfig 合并货架配置并 Save已创建的 WS281x 条带LED_COUNT 变更,通常需 释放旧条并重建 才与配置一致。

4. .NET 8 迁移注意点

方面 建议
API 路径、方法、JSON 键、REST 纯文本返回值与现网一致,避免多套上位机改版。
启动与后台任务 §3.8–§3.11:加载顺序、监听后 stoppoststartpost 自调用、serverpost/blink 双循环及热更新;实现 IHostedService 时对齐该语义。
运行状态 仅内存:不创建、不读取 state.txt/getstatestate 与内部 is_start 一致维护;重启后默认视为 off,直至 startpost 或 §3.9 自拉起成功。
运行身份 必须 root(见 §1);启动时若检测到非 root 可直接拒绝启动或打日志告警(按产品约定)。
灯条 WS281x 与原生 rpi_ws281x 类库在 root 下使用;若拆成独立控灯子进程,该子进程同样 必须 root
GPIO 使用 System.Device.Gpio 等时,BCM 号与上表对齐;布线变种用配置覆盖。
配置 /prog/.../home/pi、重启脚本、日志路径全部外置配置。
线程安全 像素缓冲与 show()、GPIO 写建议单线程或锁串行化。
安全 内网 + 鉴权;当前接口可无认证改网络与 OTA。root 进程暴露 HTTP 时务必仅绑定内网或防火墙隔离。

5. 用本文档直接生成树莓派 .NET 8 程序:尚缺或可裁定的信息

下列项在正文中未写死或仅部分覆盖,实现前需要补齐约定或现场参数,否则无法「一键生成」可部署二进制。

5.1 运行与部署

缺口 说明
监听 URL 现 Flask 侧常见为 http://*:5000(仓库自测脚本指向 127.0.0.1:5000)。Kestrel 需约定:http://0.0.0.0:5000 是否与现网一致,或改用 reverse proxy + 其它端口。
HTTP 状态码 多数接口不论成败常返回 200,错误信息在 JSON/纯文本里。与 ASP.NET 默认 ProblemDetails 习惯不同,需刻意对齐。
HTTPS / 证书 未涉及;若内网 HTTP 即可需写明。

5.2 WS281x 在 .NET 上的实现方式(文档未指定技术路线)

缺口 说明
无官方等价 rpi_ws281x .NET 8 需自选其一:P/Invoke libws2811调用现有 Python/C 小服务单独进程 + IPC、或第三方绑定。文档只描述行为,不包含绑定的 C API/库版本/初始化顺序
权限 本文档约定:必须以 root 运行,不依赖 udev 细粒度授权作为正式方案。部署文档中写明 systemd User=root(或等价)即可。
LED_COUNT 不一致 物理灯珠少于 LED_COUNT 时行为依赖驱动;超标可能损坏显示逻辑,需现场确认或读配置。

5.3 上游 communication 协议(正文仅有字段语义)

缺口 说明
出站 POST 完整 JSON 现实现每次发送大致为:boxStatus(含占位 data)、alarmListcidseq(递增整数)、op(0)、data(空对象)、statusmsg 等。正文未逐字段列出,迁移时应从现网抓包或与后端确认是否可增加字段。
超时 客户端 2s 超时;是否沿用需在配置中体现。
响应解析 代码使用 response.json(),并判断 ele_json['data']。若服务端把业务包在字符串里或键名变化,需额外契约。datadata.open 等内层类型(对象 vs 嵌套 JSON 字符串)应向后端确认。
http 前缀的 ip 现逻辑进入本地演示分支(内置假 open 指令跑一次),.NET 需决定是否保留该兼容性。

5.4 文件与数据格式细节

缺口 说明
ipconfig.csv 无表头;第一行为两列:ipcid(与 csv.writer 写出字段名一致,但文件内不写表头行)。ip基址(例如 http://host:8080),代码再拼 /service/store/communication
state.txt 本迁移目标不采用;轮询开关仅存内存(见 §3.7、§4)。若与旧 Python 并存部署,可忽略其 state.txt 或删除以免混淆。
/setconfigLED_ColorSort 持久化里为 整数GRB = 1RGB = 2(默认 RGB)。若 API 用字符串 "RGB",需在服务内与整数互转。
/upload/ multipart/form-data 字段名 file;成功返回形如 {"filename":"..."} 的字符串。GET 行为异常(返回 True),.NET 可只实现 POST。
/reload 与表头 首次加载跳过 位置==位置热加载 reload_config 在 Python 里未再跳过表头,可能把表头行写进映射——.NET 建议始终跳过表头行,不必与 bug 对齐。
version.txt / OTA 本地版本为纯文本一行。但 GET {ip}/download/version.txt 的响应体被 response.json() 解析,即服务端应对该 URL 返回 JSON(数字或带引号的字符串等形式),不是任意纯文本。ZIP 名为 {version}.zip,解压后文件拷贝规则依赖现场目录(原 /prog/smartshelf/...)。

5.5 若干接口的精确契约(正文未展开)

接口 缺口
/getstate 响应为 JSON 数组,单元素对象,键含:statemsgipconfigversionipconfig 内含运行时 ipcid、最近 POST 结果 post(如 success/failed/wait)。
/startpost state 已为 on 时仍返回成功_msg「正在运行」;ip 为空时返回失败文案且 state 仍为 off
/api/close statusa/statusb = 时表示关该侧全部三色;有 = 时只关指定颜色。仅数字项灭灯时,通道为 3(双面) 的库位在现 Python 里只更新了其中一条 strip,属潜在缺陷;.NET 可_fix 或与现网行为对齐需业务裁定。
/download/ 固定读部署路径下 smart.log;无文件时无明确响应(.NET 应定义 404 或空)。
/ip_config application/x-www-form-urlencoded:字段含 dhcp(有则 DHCP)、new_iprouter_ipnew_mask(静态时);成功返回重启提示字符串,依赖 Linux 写 /etc/dhcpcd.conf 或 NetworkManager。
二维码 / TeamViewer / openteamview 依赖本机字体路径、TeamViewer 安装路径、root 密码交互(pexpect)等,不可仅凭本文档还原安全模型,需单独产品决策或标为「不移植」。

5.6 构建目标

缺口 说明
RID 树莓派 64 位 OS:linux-arm64;32 位则 linux-arm。是否 self-contained、是否 AOT,影响发布体积与依赖 glibc

5.7 建议的最小补齐物(生成代码前)

  1. 定稿 Kestrel 监听地址与端口、是否 systemd 托管(User=root 与 §1 一致)。
  2. 选定 WS281x 技术路线(见 §5.2);root 为硬性要求,无需再评估「仅 sudo」与普通用户方案。
  3. 向后端索取 /service/store/communication 请求/响应 JSON Schema 或真实样例(含 data 全类型)。
  4. 确认 OTAversion.txtJSON 形态 与 zip 内目录布局。
  5. 列表:不移植 接口(ip_configupgradeopenteamviewdownqrcode 等)由项目经理划掉,减少范围。

整理自现有实现与仓库内示例 CSV;现场若改表头或 GPIO 接线,以交付 configuration 为准。