springboot 使用websocket来记录移动人物坐标

依赖

xml 复制代码
        <!-- WebSocket 依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

配置文件

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

代码

java 复制代码
import com.alibaba.fastjson.JSONObject;
import com.shengun.dao.entity.DevicePosition;
import com.shengun.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
@ServerEndpoint("/twin/ws")
public class DigitalTwinWebSocket {
    @Autowired
    private RedisUtil redisUtil;

    // 在线会话
    private static final Map<String, Session> SESSION_MAP = new ConcurrentHashMap<>();

    // 最新位置缓存(实时展示用)
    public static final Map<String, DevicePosition> POSITION_CACHE = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session) {
        SESSION_MAP.put(session.getId(), session);
        System.out.println("孪生前端连接成功:" + session.getId());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            // 前端传过来的位置JSON
            DevicePosition position = JSONObject.parseObject(message, DevicePosition.class);
            position.setUpdateTime(LocalDateTime.now());

            // 1. 缓存最新位置(实时展示)
            POSITION_CACHE.put(position.getObjId(), position);

            // 2. 异步存库(不阻塞实时推送)
            saveToDB(position);

            // 3. 广播给所有孪生大屏(多端同步)
            broadcast(message);

        } catch (Exception e) {
            System.err.println("位置解析失败");
        }
    }

    @OnClose
    public void onClose(Session session) {
        SESSION_MAP.remove(session.getId());
    }

    @OnError
    public void onError(Session session, Throwable error) {
        SESSION_MAP.remove(session.getId());
    }

    // 广播消息
    private void broadcast(String message) {
        for (Session session : SESSION_MAP.values()) {
            try {
                session.getBasicRemote().sendText(message);
            } catch (Exception ignored) {}
        }
    }

    // 存库(可替换 MySQL/Redis/时序库)
    private void saveToDB(DevicePosition position) {
        System.out.println("物体:" + position.getObjId()
                + " X:" + position.getX()
                + " Y:" + position.getY());
        // 你自己写 insert 逻辑
        redisUtil.set("position", JSONObject.toJSONString(position));
    }
}

实体类

java 复制代码
import lombok.Data;
import java.time.LocalDateTime;

@Data
public class DevicePosition {
    // 物体ID(车辆/设备/人物)
    private String objId;
    // 孪生场景坐标
    private double x;
    private double y;
    private double z;
    // 旋转角度(可选)
    private double rotateY;
    // 更新时间
    private LocalDateTime updateTime;
}

前端调用,循环调用

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
<body>
</body>
<script>
    let ws;
    const objId = "car_001"; // 你的物体ID

    // 连接 WebSocket
    function connect() {
        ws = new WebSocket("ws://localhost:8080/twin/ws");

        ws.onopen = function () {
            console.log("孪生连接成功");
        };

        // 接收后端广播的位置(多端实时同步)
        ws.onmessage = function (evt) {
            const pos = JSON.parse(evt.data);
            console.log("实时位置:", pos);
            // ========== 数字孪生引擎更新物体位置 ==========
            // threejs / 虚幻 / 大屏组件 在这里更新坐标
            // model.position.set(pos.x, pos.y, pos.z);
            // model.rotation.y = pos.rotateY;
        };
    }

    // 发送位置给后端(物体移动时调用)
    function sendPosition(x, y, z, rotateY) {
        const data = {
            objId: objId,
            x: x,
            y: y,
            z: z,
            rotateY: rotateY
        };
        ws.send(JSON.stringify(data));
    }

    // 模拟物体移动:每100ms推送一次(丝滑流畅)
    let x = 0;
    setInterval(() => {
        x += 0.1;
        sendPosition(x, 10, 0, 0);
    }, 100);

    connect();
</script>
</html>
相关推荐
一只幸运猫.2 小时前
Rust实用工具特型-Clone
开发语言·后端·rust
0xDevNull2 小时前
Java BigDecimal 完全指南:从入门到精通
java·开发语言·后端
无心水2 小时前
17、Java内存溢出(OOM)避坑指南:三个典型案例深度解析
java·开发语言·后端·python·架构·java.time·java时间处理
小江的记录本2 小时前
【Docker】Docker系统性知识体系与命令大全(镜像、容器、数据卷、网络、仓库)
java·网络·spring boot·spring·docker·容器·eureka
花千树-0102 小时前
JMeter 入门与进阶指南:从零开始构建你的压测环境
java·spring boot·jmeter·性能优化·压力测试·可用性测试
Arthas2172 小时前
互联网大厂Java面试实战:从基础到架构的渐进式考察
java·spring boot·微服务·面试·技术栈
咚为2 小时前
深入浅出 Rust 内存顺序:从 CPU 重排到 Atomic Ordering
开发语言·后端·rust
xxjj998a3 小时前
spring security 超详细使用教程(接入springboot、前后端分离)
java·spring boot·spring
weixin_408099673 小时前
OCR 在线识别 + API 接口实战:从网页验证到系统集成
图像处理·人工智能·后端·ocr·api·图片文字识别·文字识别