/* * 模块名称:UI Network Sink (RemoDispBus 服务端 — 模拟器 Winsock 版) * 模块功能:与 MCU 端相同协议,用 Windows Winsock 实现 TCP 监听 + RemoDispBus 帧收发 * 适用平台:Windows (Winsock2) */ #include "ui_net_sink.h" #include #include #include #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; } } }