日常生活的交流与学习

首页 新随笔 联系 管理

E:\song\agv_fastapi_socket_v4_mock\app.py

import time
from fastapi import FastAPI, WebSocket, Request, WebSocketDisconnect
from fastapi.responses import RedirectResponse, HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.openapi.docs import get_swagger_ui_html
from fastapi.templating import Jinja2Templates
from fastapi.middleware.cors import CORSMiddleware

# 启动数据库
# from config.config import initiate_database

# 引入路由


from socket_routes.status import add_status_schedulers

from websocket.manager import websocketmanager
# scheduler
from scheduler.scheduler import scheduler


# 启动定时器
scheduler.start()

# docs_url一定要设置成None,才会使用本地的 swagger-ui 的静态文件
app = FastAPI(docs_url=None)


app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

templates = Jinja2Templates(directory="templates")


app.mount('/static', StaticFiles(directory='static'), name='static')


@app.get('/docs', include_in_schema=False)
async def custom_swagger_ui_html():
    return get_swagger_ui_html(
        openapi_url=app.openapi_url,
        title=app.title + " - Swagger UI ",
        oauth2_redirect_url=app.swagger_ui_oauth2_redirect_url,
        swagger_js_url="/static/swagger-ui/swagger-ui-bundle.js",
        swagger_css_url="/static/swagger-ui/swagger-ui.css",
        swagger_favicon_url="/static/swagger-ui/favicon.png"
    )


# 初始化事件
@app.on_event("startup")
async def start_database():
    # await initiate_database()
    pass


# docs文档
@app.get("/", tags=["Docs"])
async def get_docs():
    return RedirectResponse("/docs")


# jinjia2的测试网页
@app.get("/jinjia2", response_class=HTMLResponse)
async def read_jinjia2(request: Request):
    return templates.TemplateResponse("index.html", {"request": request, "name": "Alice"})


# websocket的测试网页
@app.get("/websocket", response_class=HTMLResponse)
async def websocket_test(request: Request):
    return templates.TemplateResponse("websocket.html", {"request": request})


@app.websocket("/wstest")
async def websocket_endpoint(websocket: WebSocket):
    await websocket.accept()
    while True:
        data = await websocket.receive_text()
        await websocket.send_text(f"send from serve: {data}")


# client_id代表用户的唯一标识,用来区分不同的用户
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    # 将新的socket连接存储起来
    await websocketmanager.connect(websocket)
    try:
        while True:
            # data = await websocket.receive_json()
            data = await websocket.receive_text()
            data_dict = eval(data)
            if 'websocket' in data_dict:  # 处理心跳检测
                await websocket.send_text("pong")
            else:
                print('start')
                await add_status_schedulers(websocket)
    except WebSocketDisconnect:
        websocketmanager.disconnect(websocket)
        await websocketmanager.broadcast(f"Client left the chat")

E:\song\agv_fastapi_socket_v4_mock\main.py

import uvicorn

if __name__ == '__main__':
    uvicorn.run('app:app', host="127.0.0.1", port=9000, reload=True)

E:\song\agv_fastapi_socket_v4_mock\config\config.py

from typing import Optional

from beanie import init_beanie
from motor.motor_asyncio import AsyncIOMotorClient
from pydantic import BaseSettings

from models.student import Student


class Settings(BaseSettings):
    # database configurations
    DATABASE_URL: Optional[str] = None

    class Config:
        env_file = ".env"
        orm_mode = True


async def initiate_database():
    client = AsyncIOMotorClient(Settings().DATABASE_URL)
    await init_beanie(database=client['agv'],
                      document_models=[Student])

E:\song\agv_fastapi_socket_v4_mock\dao\student.py

from typing import List, Union

from beanie import PydanticObjectId

from models.student import Student

student_collection = Student


async def retrieve_students() -> List[Student]:
    students = await student_collection.all().to_list()
    return students


async def add_student(new_student: Student) -> Student:
    student = await new_student.create()
    return student


async def retrieve_student(id: PydanticObjectId) -> Student:
    student = await student_collection.get(id)
    if student:
        return student


async def delete_student(id: PydanticObjectId) -> bool:
    student = await student_collection.get(id)
    if student:
        await student.delete()
        return True


async def update_student_data(id: PydanticObjectId, data: dict) -> Union[bool, Student]:
    des_body = {k: v for k, v in data.items() if v is not None}
    update_query = {"$set": {
        field: value for field, value in des_body.items()
    }}
    student = await student_collection.get(id)
    if student:
        await student.update(update_query)
        return student
    return False

E:\song\agv_fastapi_socket_v4_mock\models\student.py

from typing import Optional, Any

from beanie import Document
from pydantic import BaseModel, EmailStr


class Student(Document):
    fullname: str
    email: EmailStr
    course_of_study: str
    year: int
    gpa: float

    class Config:
        schema_extra = {
            "example": {
                "fullname": "Abdulazeez Abdulazeez Adeshina",
                "email": "abdul@school.com",
                "course_of_study": "Water resources engineering",
                "year": 4,
                "gpa": "3.76"
            }
        }


class UpdateStudentModel(BaseModel):
    fullname: Optional[str]
    email: Optional[EmailStr]
    course_of_study: Optional[str]
    year: Optional[int]
    gpa: Optional[float]

    class Collection:
        name = "student"

    class Config:
        schema_extra = {
            "example": {
                "fullname": "Abdulazeez Abdulazeez",
                "email": "abdul@school.com",
                "course_of_study": "Water resources and environmental engineering",
                "year": 4,
                "gpa": "5.0"
            }
        }


class Response(BaseModel):
    status_code: int
    response_type: str
    description: str
    data: Optional[Any]

    class Config:
        schema_extra = {
            "example": {
                "status_code": 200,
                "response_type": "success",
                "description": "Operation successful",
                "data": "Sample data"
            }
        }

E:\song\agv_fastapi_socket_v4_mock\routes\audio.py

import json
from fastapi import APIRouter, HTTPException
from typing import Optional, Any
from pydantic import BaseModel

# 创建audio_router路由
audio_router = APIRouter()


class StatusParams(BaseModel):
    play: int

    class Config:
        schema_extra = {
            "example": {
                "play": 1,
            }
        }


@audio_router.get("/")
async def control_audio(play: int):
    if play == 1:
        # 暂停机器人当前的导航运动
        # 播放agv里面的声音(只播放一次,所以不用恢复)
        # 通知前端网页显示相应结果(可选)
        return 'start'
    else:
        # 暂停机器人当前的导航运动
        # 通知前端网页显示相应结果(可选,前端网页如果自动消失,则可选)
        return 'cancel'

E:\song\agv_fastapi_socket_v4_mock\routes\config.py

from fastapi import APIRouter, HTTPException
from typing import Optional, Any
from pydantic import BaseModel
# agv socket 工厂方法
from tcpsocket.factory import agv_socket_factory
# status的端口号
from tcpsocket.params import Port

# 创建status router路由
config_router = APIRouter()

# 创建agv status的socket
agv_config_socket = agv_socket_factory.create(Port.CONFIG.value)


class StatusParams(BaseModel):
    msg_type: int
    msg: Optional[dict]

    class Config:
        schema_extra = {
            "example": {
                "msg_type": 1000,
                "msg": {"参数名称": "xx参数"}
            }
        }


@config_router.get("/", response_description="编号 查询结果")
async def get_config(msg_type: int):
    try:
        agv_config_socket.send(1, msg_type)
        res = agv_config_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )


@config_router.post("/", response_description="编号 查询结果")
async def post_config(params: StatusParams):
    try:
        agv_config_socket.send(1, params.msg_type, params.msg)
        res = agv_config_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )

E:\song\agv_fastapi_socket_v4_mock\routes\control.py


from fastapi import APIRouter
from typing import Optional
from pydantic import BaseModel
# agv socket 工厂方法
from tcpsocket.factory import agv_socket_factory
# status的端口号
from tcpsocket.params import Port

# 创建status router路由
control_router = APIRouter()

# 创建agv status的socket
agv_control_socket = agv_socket_factory.create(Port.CONTROL.value)


class StatusParams(BaseModel):
    msg_type: int
    msg: Optional[dict]

    class Config:
        schema_extra = {
            "example": {
                "msg_type": 1000,
                "msg": {"参数名称": "xx参数"}
            }
        }


@control_router.get("/", response_description="编号 查询结果")
async def get_control():
    return "地图"


@control_router.post("/", response_description="编号 查询结果")
async def post_control(params: StatusParams):
    print(params)
    return {
        "map": '地图'
    }
    # try:
    #     agv_socket.send(1,Status(params.msg_type).value,{})
    #     res = agv_socket.receive()
    #     return res
    # except Exception as e:
    #     print(e)
    #     raise HTTPException(
    #         status_code=409,
    #         detail="查询编号不正确"
    #     )

E:\song\agv_fastapi_socket_v4_mock\routes\navigate.py

from fastapi import APIRouter, HTTPException
from typing import Optional
from pydantic import BaseModel
# agv socket 工厂方法
from tcpsocket.factory import agv_socket_factory
# status的端口号
from tcpsocket.params import Port, Config

# 创建status router路由
navigate_router = APIRouter()

# 创建agv status的socket
agv_navigate_socket = agv_socket_factory.create(Port.NAVIGATE.value)


class StatusParams(BaseModel):
    msg_type: int
    msg: Optional[dict]

    class Config:
        schema_extra = {
            "example": {
                "msg_type": 1000,
                "msg": {"参数名称": "xx参数"}
            }
        }


@navigate_router.get("/", response_description="编号 查询结果")
async def get_navigate(msg_type: int):
    try:
        agv_navigate_socket.send(1, msg_type)
        res = agv_navigate_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )


@navigate_router.post("/", response_description="编号 查询结果")
async def post_navigate(params: StatusParams):
    try:
        agv_navigate_socket.send(1, params.msg_type, params.msg)
        res = agv_navigate_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )

E:\song\agv_fastapi_socket_v4_mock\routes\other.py

import json
from fastapi import APIRouter, HTTPException
from typing import Optional, Any
from pydantic import BaseModel
# agv socket 工厂方法
from tcpsocket.factory import agv_socket_factory
from tcpsocket.params import Status
# status的端口号
from tcpsocket.params import Port

# 创建status router路由
other_router = APIRouter()
# 创建agv status的socket
agv_other_socket = agv_socket_factory.create(Port.OTHER.value)


class StatusParams(BaseModel):
    msg_type: int
    msg: Optional[Any]

    class Config:
        schema_extra = {
            "example": {
                "msg_type": 1000,
                "msg": {"参数名称": "xx参数"}
            }
        }


@other_router.get("/")
async def get_other(msg_type: int):
    try:
        agv_other_socket.send(1, msg_type)
        res = agv_other_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )


@other_router.post("/", response_description="编号 查询结果")
async def post_other(params: StatusParams):
    try:
        agv_other_socket.send(1, params.msg_type, params.msg)
        res = agv_other_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )

E:\song\agv_fastapi_socket_v4_mock\routes\scheduler.py

from fastapi import APIRouter, Body
from scheduler.scheduler import scheduler

scheduler_router = APIRouter()


@scheduler_router.get("/", response_description="Toggle the scheduler")
async def toggle_scheduler(flag: str):
    if flag == 'start':
        pass
    elif flag == 'resume':
        scheduler.resume()
    elif flag == 'pause':
        scheduler.pause()
    return {
        "status_code": 200,
        "response_type": "success",
        "description": "Students data retrieved successfully",
        "data": "ok"
    }

E:\song\agv_fastapi_socket_v4_mock\routes\status.py

import json
from fastapi import APIRouter, HTTPException
from typing import Optional, Any
from pydantic import BaseModel
# agv socket 工厂方法
from tcpsocket.factory import agv_socket_factory
# status的端口号
from tcpsocket.params import Port

# 创建status router路由
status_router = APIRouter()
# 创建agv status的socket
agv_status_socket = agv_socket_factory.create(Port.STATUS.value)


class StatusParams(BaseModel):
    msg_type: int
    msg: Optional[Any]

    class Config:
        schema_extra = {
            "example": {
                "msg_type": 1000,
                "msg": {"参数名称": "xx参数"}
            }
        }


@status_router.get("/")
async def get_status(msg_type: int):
    try:
        agv_status_socket.send(1, msg_type)
        res = agv_status_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )


@status_router.post("/", response_description="编号 查询结果")
async def post_status(params: StatusParams):
    try:
        agv_status_socket.send(1, params.msg_type, params.msg)
        res = agv_status_socket.receive()
        return res
    except Exception as e:
        print(e)
        raise HTTPException(
            status_code=409,
            detail="查询编号不正确"
        )

E:\song\agv_fastapi_socket_v4_mock\routes\student.py

from fastapi import APIRouter, Body

from dao.student import *
from models.student import *

student_router = APIRouter()


@student_router.get("/", response_description="Students retrieved", response_model=Response)
async def get_students():
    return {
        "status_code": 200,
        "response_type": "success",
        "description": "Students data retrieved successfully",
        "data": "test"
    }

E:\song\agv_fastapi_socket_v4_mock\scheduler\scheduler.py

# 定时器任务计划
from apscheduler.schedulers.asyncio import AsyncIOScheduler
scheduler = AsyncIOScheduler()

E:\song\agv_fastapi_socket_v4_mock\socket_routes\status.py

import json
import time
import random
from websocket.manager import websocketmanager
from scheduler.scheduler import scheduler

time_start = time.time()


# 添加定时器任务

async def add_status_schedulers(websocket):
    scheduler.add_job(job_status_test, 'interval',
                      seconds=2, args=[websocket])


async def job_status_test(websocket):
    # time
    time_end = time.time()
    # 确定随机小数点位数,比如.3f,表示小数点为3位小数
    bat_temp = format((random.uniform(18, 22)), '.2f')
    con_temp = format((random.uniform(38, 42)), '.2f')
    print(int(round((time_end-time_start) * 1000)))
    await websocketmanager.broadcast_json({
        "name": "test",
        "data": {
            "battery_temp": bat_temp,
            "controller_temp": con_temp,
            "time": int(round((time_end-time_start) * 1000))
        }
    }, websocket)

E:\song\agv_fastapi_socket_v4_mock\tcpsocket\agvsocket.py

import json
import socket
import struct


class AgvSocket:
    __PACK_FMT_STR = '!BBHLH6s'

    def __init__(self, ip, port):
        so = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        so.connect((ip, port))
        so.settimeout(5)
        self.so = so

    def send(self, req_id, msg_type, msg={}):
        raw_msg = self.__pack_msg(req_id, msg_type, msg)
        self.so.send(raw_msg)

    def receive(self):
        global data
        data_all = b''

        try:
            data = self.so.recv(16)
        except socket.timeout:
            print('timeout')
            self.so.close()

        json_data_len = 0
        back_req_num = 0

        if len(data) < 16:
            print('pack head error')
            print(data)
            self.so.close()
        else:
            header = struct.unpack(AgvSocket.__PACK_FMT_STR, data)
            json_data_len = header[3]
            back_req_num = header[4]
        data_all += data
        data = b''
        read_size = 1024
        try:
            while json_data_len > 0:
                recv = self.so.recv(read_size)
                data += recv
                json_data_len -= len(recv)
                if json_data_len < read_size:
                    read_size = json_data_len
            data_all += data
            return json.loads(data_all[16:].decode())

        except socket.timeout:
            print('timeout')

    def close(self):
        self.so.close()

    @staticmethod
    def __pack_msg(req_id, msg_type, msg={}):
        msg_len = 0
        json_str = json.dumps(msg)

        if msg != {}:
            msg_len = len(json_str)

        raw_msg = struct.pack(AgvSocket.__PACK_FMT_STR, 0x5A, 0x01, req_id,
                              msg_len, msg_type, b'\x00\x00\x00\x00\x00\x00')
        if msg != {}:
            raw_msg += bytearray(json_str, 'ascii')

        return raw_msg

E:\song\agv_fastapi_socket_v4_mock\tcpsocket\factory.py

from .params import IP, Port
from .agvsocket import AgvSocket


class FactoryTcpSocket:
    def __init__(self, ip: str):
        self.ip = ip
        self.socket_dic = {}

    def create(self, port):
        pass
        res = self.socket_dic.get(port)
        if res is None:
            agv_socket = AgvSocket(self.ip, port)
            self.socket_dic[port] = agv_socket
            return agv_socket
        else:
            return res


agv_socket_factory = FactoryTcpSocket(IP)

E:\song\agv_fastapi_socket_v4_mock\tcpsocket\params.py

from enum import Enum

IP = "10.13.32.11"
# IP = "192.168.1.184"


class Port(Enum):
    STATUS = 19204
    CONTROL = 19205
    NAVIGATE = 19206
    CONFIG = 19207
    OTHER = 19210
    PUSH = 192301


class Status(Enum):
    INFO = 1000  # 查询机器人信息
    RUN = 1002  # 机器人运行信息
    LOC = 1004  # 机器人位置
    SPEED = 1005  # 机器人速度
    BLOCK = 1006  # 机器人的被阻挡状态
    BATTERY = 1007  # 机器人电池状态
    MOTOR = 1040  # 电机状态信息
    LASER = 1009  # 激光点云数据
    AREA = 1011  # 机器人当前所在区域
    EMERGENCY = 1012  # 机器人急停状态
    IO = 1013  # 查询机器人I/O状态
    IMU = 1014  # 查询机器人IMU数据
    RFID = 1015  # RFID数据
    ULTRASONIC = 1016  # 超声传感器数据
    PGV = 1017  # 二维码数据
    ENCODER = 1018  # 编码器数据
    TASK = 1020  # 机器人导航状态
    TASK_STATUS_PACKAGE = 1110  # 机器人任务状态
    RELOC = 1021  # 机器人定位状态
    LOADMAP = 1022  # 地图载入状态
    SLAM = 1025  # 机器人扫图状态
    DISPATCH = 1030  # 机器人当前的调度状态
    ALARM = 1050  # 机器人报警状态
    CURRENT_LOCK = 1060  # 当前控制权所有者
    MAP = 1300  # 机器人载入的地图以及存储的地图
    STATION = 1301  # 查询机器人当前载入地图中的站点信息
    STARTDMXSCRIPT = 1903  # 运行氛围灯脚本
    STOPBATTERYSCRIPT = 1904  # 停止氛围灯脚本
    ALL1 = 1100  # 批量查询数据1
    ALL2 = 1101  # 批量查询数据2
    ALL3 = 1102  # 批量查询数据3


class Control(Enum):
    pass


class Navigate(Enum):
    pass


class Config(Enum):
    # 从机器人上下载地图
    DOWN_LOAD_MAP = 4011


class Other(Enum):
    pass


class Push(Enum):
    pass

E:\song\agv_fastapi_socket_v4_mock\templates\index.html

<!DOCTYPE html>
<html lang="zh">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>jinjia2</title>
    <link href="{{ url_for('static', path='/index/style.css') }}" rel="stylesheet">
</head>

<body>
    <h1>this is jinja2 template</h1>
    <h2>{{name}}</h2>

</body>

</html>```
# `E:\song\agv_fastapi_socket_v4_mock\templates\websocket.html`

```html
<!DOCTYPE html>
<html>

<head>
    <title>Chat</title>
</head>

<body>
    <h1>WebSocket Chat</h1>
    <form action="" onsubmit="sendMessage(event)">
        <input type="text" id="messageText" autocomplete="off" />
        <button>Send</button>
    </form>
    <ul id='messages'>
    </ul>
    <script>
        // socket的连接地址
        const ws = new WebSocket("ws://localhost:9000/ws");

        // socket 连接成功的回调
        ws.addEventListener('open', (e) => {
            console.log(`socket 连接成功`)
        })
        // socket 连接失败的回调
        ws.addEventListener('error', (e) => {
            console.log(`socket 连接失败`)
        })
        // socket 连接断开时候的回调
        ws.addEventListener('close', (e) => {
            console.log(`socket 连接断开`)
        })

        // 采用这中属性赋值的方法,只能有一个回调函数,如果想要有多个回调函数,可以采用上面的addEventListener的方法,来添加回调函数
        ws.onmessage = function (event) {
            receMessage(event.data)
        };

        // 将接受到内容渲染到界面上
        function receMessage(msg) {
            var messages = document.getElementById('messages')
            var message = document.createElement('li')
            var content = document.createTextNode(msg)
            message.appendChild(content)
            messages.appendChild(message)
        }

        // 发送内容
        function sendMessage(event) {
            var input = document.getElementById("messageText")
            ws.send(input.value)
            input.value = ''
            event.preventDefault()
        }
    </script>
</body>

</html>```
# `E:\song\agv_fastapi_socket_v4_mock\websocket\dispatch.py`

```py
from socket_routes.status import dispatch_status


# 分发任务
async def dispatch_socket(msg_type, msg, websocket):
    # 查询机器人状态
    if msg_type >= 1000 and msg_type <= 1999:
        print("状态api")
        await dispatch_status(msg_type, msg, websocket)
    elif msg_type >= 2000 and msg_type <= 2999:
        print("控制api")
    elif msg_type >= 3000 and msg_type <= 3999:
        print("导航api")
    elif msg_type >= 4000 and msg_type <= 4999:
        print("配置api")
    elif msg_type >= 6000 and msg_type <= 6999:
        print("其他api")
    elif msg_type == 9300 or msg_type == 19301:
        print("推送api")
    else:
        print("暂无此api")

E:\song\agv_fastapi_socket_v4_mock\websocket\manager.py

from typing import List
from fastapi.websockets import WebSocket


class ConnectionManager:
    def __init__(self):
        self.active_connections: List[WebSocket] = []

    async def connect(self, websocket: WebSocket):
        await websocket.accept()
        self.active_connections.append(websocket)

    def disconnect(self, websocket: WebSocket):
        self.active_connections.remove(websocket)

    # 直接发送,就是单个发送
    async def send_personal_message(self, message: str, websocket: WebSocket):
        await websocket.send_text(message)

    async def send_personal_json(self, message: str, websocket: WebSocket):
        await websocket.send_json(message)

    async def broadcast_json(self, message: str, websocket: WebSocket):
        for connection in self.active_connections:
            await connection.send_json(message)

    # 广播,遍历所有的connection连接,然后发送消息

    async def broadcast(self, message: str):
        for connection in self.active_connections:
            await connection.send_text(message)


websocketmanager = ConnectionManager()
posted on 2022-12-21 14:56  lazycookie  阅读(72)  评论(0编辑  收藏  举报