Files
STM32F4-Base/simulator/ui_net_sink.c
T

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;
}
}
}