275 lines
7.4 KiB
C
275 lines
7.4 KiB
C
/*
|
|
* 模块名称:UI Network Sink (RemoDispBus 服务端 — 模拟器 Winsock 版)
|
|
* 模块功能:与 MCU 端相同协议,用 Windows Winsock 实现 TCP 监听 + RemoDispBus 帧收发
|
|
* 适用平台:Windows (Winsock2)
|
|
*/
|
|
#include "ui_net_sink.h"
|
|
|
|
#include <string.h>
|
|
#include <winsock2.h>
|
|
#include <ws2tcpip.h>
|
|
#include "lvgl.h"
|
|
#include "sim_port.h"
|
|
|
|
/* RemoDispBus 标签与命令(与 ui_net_sink.h / MCU 端一致) */
|
|
#define TAG_CLIENT 0xAA
|
|
#define TAG_DEVICE 0xBB
|
|
#define CMD_KEEPLIVE 0
|
|
#define CMD_INIT 1
|
|
#define CMD_KEY 2
|
|
#define CMD_LCDMEM 3
|
|
|
|
#define UI_W 160
|
|
#define UI_H 160
|
|
#define UI_MEMSIZE (UI_W * UI_H / 8) /* 3200 字节 */
|
|
|
|
/* 远端按键码(与 ui_port.h 一致) */
|
|
#define UI_KEY_UP 0x02
|
|
#define UI_KEY_DOWN 0x40
|
|
#define UI_KEY_LEFT 0x10
|
|
#define UI_KEY_RIGHT 0x08
|
|
#define UI_KEY_ENTER 0x20
|
|
#define UI_KEY_ESC 0x01
|
|
|
|
static uint8_t g_tx[4 + 4 + UI_MEMSIZE + 1];
|
|
static uint8_t g_lcdmem_data[4 + UI_MEMSIZE];
|
|
static uint8_t g_rx[4096];
|
|
|
|
static SOCKET g_listen = INVALID_SOCKET;
|
|
static SOCKET g_client = INVALID_SOCKET;
|
|
static int g_recv_off = 0; /* g_rx 已累积字节数 */
|
|
|
|
static uint8_t remo_crc(const uint8_t *d, int len)
|
|
{
|
|
uint8_t c = 0;
|
|
for (int i = 0; i < len; i++) {
|
|
c ^= d[i];
|
|
}
|
|
return c;
|
|
}
|
|
|
|
static void put_u16(uint8_t *p, uint16_t v)
|
|
{
|
|
p[0] = (uint8_t)(v >> 8);
|
|
p[1] = (uint8_t)(v & 0xFF);
|
|
}
|
|
|
|
static void put_u32(uint8_t *p, uint32_t v)
|
|
{
|
|
p[0] = (uint8_t)(v >> 24);
|
|
p[1] = (uint8_t)(v >> 16);
|
|
p[2] = (uint8_t)(v >> 8);
|
|
p[3] = (uint8_t)(v & 0xFF);
|
|
}
|
|
|
|
static void remo_send(SOCKET s, uint8_t cmd, const uint8_t *data, uint16_t len)
|
|
{
|
|
if (s == INVALID_SOCKET) {
|
|
return;
|
|
}
|
|
uint8_t *p = g_tx;
|
|
p[0] = TAG_DEVICE;
|
|
p[1] = cmd;
|
|
p[2] = (uint8_t)(len >> 8);
|
|
p[3] = (uint8_t)(len & 0xFF);
|
|
if (len && data) {
|
|
memcpy(p + 4, data, len);
|
|
}
|
|
p[4 + len] = remo_crc(data, len);
|
|
send(s, (const char *)p, 4 + len + 1, 0);
|
|
}
|
|
|
|
static void remo_send_init(SOCKET s)
|
|
{
|
|
uint8_t init[8];
|
|
put_u16(init, UI_W);
|
|
put_u16(init + 2, UI_H);
|
|
put_u32(init + 4, UI_MEMSIZE);
|
|
remo_send(s, CMD_INIT, init, 6);
|
|
}
|
|
|
|
static uint32_t sim_map_key(uint8_t remo_key)
|
|
{
|
|
switch (remo_key) {
|
|
case UI_KEY_UP: return LV_KEY_UP;
|
|
case UI_KEY_DOWN: return LV_KEY_DOWN;
|
|
case UI_KEY_LEFT: return LV_KEY_LEFT;
|
|
case UI_KEY_RIGHT: return LV_KEY_RIGHT;
|
|
case UI_KEY_ENTER: return LV_KEY_ENTER;
|
|
case UI_KEY_ESC: return LV_KEY_ESC;
|
|
default: return 0;
|
|
}
|
|
}
|
|
|
|
static void remo_handle(SOCKET s, const uint8_t *f, int n)
|
|
{
|
|
if (n < 5) {
|
|
return;
|
|
}
|
|
uint8_t tag = f[0];
|
|
uint8_t cmd = f[1];
|
|
uint16_t len = ((uint16_t)f[2] << 8) | f[3];
|
|
const uint8_t *data = f + 4;
|
|
if ((int)(4 + len + 1) > n) {
|
|
return;
|
|
}
|
|
uint8_t crc = f[4 + len];
|
|
if (crc != remo_crc(data, len)) {
|
|
return;
|
|
}
|
|
if (tag != TAG_CLIENT) {
|
|
return;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case CMD_KEEPLIVE:
|
|
remo_send(s, CMD_KEEPLIVE, NULL, 0);
|
|
break;
|
|
case CMD_INIT:
|
|
remo_send_init(s);
|
|
break;
|
|
case CMD_KEY:
|
|
if (len >= 1) {
|
|
uint32_t key = sim_map_key(data[0]);
|
|
if (key != 0) {
|
|
lv_group_send_data(lv_group_get_default(), key);
|
|
}
|
|
}
|
|
remo_send(s, CMD_KEY, NULL, 0);
|
|
break;
|
|
case CMD_LCDMEM: {
|
|
if (len < 4) {
|
|
break;
|
|
}
|
|
uint32_t off = ((uint32_t)data[0] << 24) |
|
|
((uint32_t)data[1] << 16) |
|
|
((uint32_t)data[2] << 8) |
|
|
(uint32_t)data[3];
|
|
if (off == 0) {
|
|
/* 窗口模式 I1+PARTIAL 显示缓冲系分块且经 I1→ARGB8888 转换,
|
|
* 不能直接读;读回屏幕实际呈现像素再转 1bpp(布局与 MCU 端一致) */
|
|
if (sim_port_get_screen_mono(g_lcdmem_data + 4, UI_MEMSIZE) == 0) {
|
|
memcpy(g_lcdmem_data, data, 4);
|
|
remo_send(s, CMD_LCDMEM, g_lcdmem_data, 4 + UI_MEMSIZE);
|
|
}
|
|
/* 读回失败则静默丢弃(与 MCU 端一致) */
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* 尝试从已接收数据中解析一帧并处理 */
|
|
static void try_parse_frames(SOCKET s)
|
|
{
|
|
for (;;) {
|
|
if (g_recv_off < 5) {
|
|
break; /* 不够最小帧长 */
|
|
}
|
|
uint16_t payload_len = ((uint16_t)g_rx[2] << 8) | g_rx[3];
|
|
int frame_total = 4 + payload_len + 1;
|
|
if (g_recv_off < frame_total) {
|
|
break; /* 帧不完整,等更多数据 */
|
|
}
|
|
remo_handle(s, g_rx, frame_total);
|
|
/* 移除已处理帧(RemoDispBus 是请求-响应模式,不会有粘包;但以防万一保留移动逻辑) */
|
|
int remain = g_recv_off - frame_total;
|
|
if (remain > 0) {
|
|
memmove(g_rx, g_rx + frame_total, remain);
|
|
}
|
|
g_recv_off = remain;
|
|
}
|
|
}
|
|
|
|
int ui_net_sink_start(uint16_t port)
|
|
{
|
|
g_listen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (g_listen == INVALID_SOCKET) {
|
|
return -1;
|
|
}
|
|
|
|
int yes = 1;
|
|
setsockopt(g_listen, SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));
|
|
|
|
struct sockaddr_in addr;
|
|
memset(&addr, 0, sizeof(addr));
|
|
addr.sin_family = AF_INET;
|
|
addr.sin_port = htons(port);
|
|
addr.sin_addr.s_addr = INADDR_ANY;
|
|
|
|
if (bind(g_listen, (struct sockaddr *)&addr, sizeof(addr)) == SOCKET_ERROR) {
|
|
closesocket(g_listen);
|
|
g_listen = INVALID_SOCKET;
|
|
return -1;
|
|
}
|
|
if (listen(g_listen, 1) == SOCKET_ERROR) {
|
|
closesocket(g_listen);
|
|
g_listen = INVALID_SOCKET;
|
|
return -1;
|
|
}
|
|
|
|
/* 设为非阻塞 */
|
|
u_long mode = 1;
|
|
ioctlsocket(g_listen, FIONBIO, &mode);
|
|
|
|
g_client = INVALID_SOCKET;
|
|
g_recv_off = 0;
|
|
return 0;
|
|
}
|
|
|
|
void ui_net_sink_stop(void)
|
|
{
|
|
if (g_client != INVALID_SOCKET) {
|
|
closesocket(g_client);
|
|
g_client = INVALID_SOCKET;
|
|
}
|
|
if (g_listen != INVALID_SOCKET) {
|
|
closesocket(g_listen);
|
|
g_listen = INVALID_SOCKET;
|
|
}
|
|
g_recv_off = 0;
|
|
}
|
|
|
|
void ui_net_sink_poll(void)
|
|
{
|
|
if (g_listen == INVALID_SOCKET) {
|
|
return;
|
|
}
|
|
|
|
/* 无客户端时尝试接受连接 */
|
|
if (g_client == INVALID_SOCKET) {
|
|
struct sockaddr_in cli;
|
|
int cli_len = sizeof(cli);
|
|
SOCKET c = accept(g_listen, (struct sockaddr *)&cli, &cli_len);
|
|
if (c != INVALID_SOCKET) {
|
|
u_long mode = 1;
|
|
ioctlsocket(c, FIONBIO, &mode);
|
|
g_client = c;
|
|
g_recv_off = 0;
|
|
/* 不主动上报 INIT(与 MCU 端一致:避免污染客户端首帧读取,由客户端按需请求) */
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* 有客户端时非阻塞收帧 */
|
|
int n = recv(g_client, (char *)(g_rx + g_recv_off),
|
|
(int)(sizeof(g_rx) - g_recv_off), 0);
|
|
if (n > 0) {
|
|
g_recv_off += n;
|
|
try_parse_frames(g_client);
|
|
} else if (n == 0) {
|
|
closesocket(g_client);
|
|
g_client = INVALID_SOCKET;
|
|
g_recv_off = 0;
|
|
} else {
|
|
int err = WSAGetLastError();
|
|
if (err != WSAEWOULDBLOCK) {
|
|
closesocket(g_client);
|
|
g_client = INVALID_SOCKET;
|
|
g_recv_off = 0;
|
|
}
|
|
}
|
|
}
|