• 家庭燃气表微信抄表识别系统


    1.设计目标

    • 需求:家里燃气度数的读数上报,每个月需在社区微信群里将手机拍摄的燃气表读数截图(加住址信息水印),发到群里给抄表员
    • 目标:手机上随时可以远程采集读数图片(自动加住址信息水印),在微信上发送给社区抄表员。

    2.总体设计

    在这里插入图片描述

    在这里插入图片描述

    3.燃气抄表采集端

    使用树莓派(两点理由,第一支持Type-C供电且低功耗,可以加移动电源长期运行,第二支持GPIO,以后加传感器有扩展空间),部署Docker环境。
    https://www.raspberrypi.com/software/
    在这里插入图片描述
    使用树莓派官网的系统安装工具完成系统安装,安装完成之后部属宝塔BT系统进行系统管理。
    https://www.bt.cn/new/download.html
    在这里插入图片描述

    3.1 功能描述

    接收管理服务器实时采集请求,实现燃气表拍照的采集上报,摄像头采集之前要自动打开小夜灯照明,厨柜内较黑拍不清楚。

    3.2 容器配置

    3.2.1 Dockerfile-GAS-ARM文件

    FROM ubuntu:latest
    ENV LANG C.UTF-8
    ENV DEBIAN_FRONTEND noninteractive
    
    RUN apt-get update
    RUN apt install -y openssh-client openssh-server
    RUN apt-get install -y wget curl git telnet vim make gcc 
    RUN apt-get install -y python3 python3-pip libpcre3 libpcre3-dev
    RUN apt-get install -y python3-opencv
    
    RUN apt clean \
        && rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp* \
    RUN apt autoremove
    
    WORKDIR /tmp
    RUN apt-get update
    RUN apt-get upgrade -y
    RUN apt install -y libusb-1.0-0-dev
    RUN git clone https://github.com/mvp/uhubctl
    WORKDIR /tmp/uhubctl
    RUN make && make install
    
    # Install opencv-python
    RUN pip3 install --upgrade pip -i https://pypi.mirrors.ustc.edu.cn/simple/
    RUN pip3 install paho-mqtt pytest-shutil requests -i https://pypi.mirrors.ustc.edu.cn/simple/ 
    
    WORKDIR /home
    CMD ["/bin/bash"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    3.2.2 docker-compose.yaml文件

    version: "3.3"
    services:
      #燃气采集设备端
      #docker exec -it gas_meter_reading_t /bin/bash
      gas_meter_reading_t:
        build:
          context: ./
          dockerfile: Dockerfile-GAS-ARM
        image: gas_meter_reading:v1
        container_name: gas_meter_reading_t
        working_dir: /workspace
        devices:
          - /dev/video0:/dev/video0
        volumes:
          - ./workspace:/workspace
        restart: always
        tty: true
        privileged: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3.3 采集代码

    在这里插入图片描述

    #!/usr/bin/python3
    import base64
    import asyncio
    import paho.mqtt.client as mqtt
    import cv2
    import subprocess
    import datetime
    import threading
    import time
    import json
    import shutil
    
    class GMRDevice:
        def __init__(self, cam_simulate=False):
            self.mqtt_server_ip = '58.212.21.66'
            self.usbhub  = '1'
            self.hubport = '1'
            self.client = mqtt.Client()
            self.cam_simulate = cam_simulate
    
        def get_img_path(self):
            now = datetime.datetime.now()
            img_name = "{:04d}{:02d}{:02d}{:02d}{:02d}{:02d}".format(
                now.year, 
                now.month, 
                now.day, 
                now.hour, 
                now.minute, 
                now.second
                )
            print("当前时间:{}".format(img_name))
            return './images/' + img_name + '.jpg'
    
        #夜灯开关
        def set_led(self, cmd):
            try:
                output = subprocess.check_output(
                ["uhubctl", "-l", self.usbhub, "-p", self.hubport, "-a", cmd], 
                stderr=subprocess.STDOUT)
                print(output.decode("utf-8"))
            except subprocess.CalledProcessError as e:
                print("命令执行失败:", e.output.decode("utf-8"))
    
        #摄像头拍照
        def gas_meter_photography(self, imgPath):
            if self.cam_simulate == True:
                print('gas_meter_photography simulate(true)')
                shutil.copyfile('./''simulate''.jpg', imgPath)
                return True
    
            print('gas_meter_photography simulate(false)')
            self.set_led('on')
            time.sleep(1)
            isOK = False
            cap = cv2.VideoCapture(0)
            if cap.isOpened():
                print('宽:', cap.get(cv2.CAP_PROP_FRAME_WIDTH) )
                print('高:', cap.get(cv2.CAP_PROP_FRAME_HEIGHT) )
                print('帧率:', cap.get(cv2.CAP_PROP_FPS) )
                print('亮度:', cap.get(cv2.CAP_PROP_BRIGHTNESS) )
                print('对比度:', cap.get(cv2.CAP_PROP_CONTRAST) )
                print('饱和度:', cap.get(cv2.CAP_PROP_SATURATION) )
                print('色调:', cap.get(cv2.CAP_PROP_HUE) )
                print('曝光度:', cap.get(cv2.CAP_PROP_EXPOSURE) )
                #cap.set(cv2.CAP_PROP_CONTRAST, 100)
                #cap.set(cv2.CAP_PROP_EXPOSURE, 200)
                #cap.set(cv2.CAP_PROP_BRIGHTNESS, 100)
                
                __, frame = cap.read()
                farm = cv2.resize(frame, dsize = (1600, 1200) )
                cv2.imwrite(imgPath,farm)
                isOK = True
            cap.release()
            time.sleep(1)
            self.set_led('off')
            return isOK
    
        #上报抄表照片
        def send_photo(self, imgPath):
            if not self.client.is_connected(): 
                return
    
            with open(imgPath, 'rb') as f:
                image_data = f.read()
                image_base64 = base64.b64encode(image_data)
                self.client.publish('gmrdev/fixedtime', image_base64)
    
        def on_connect(self, client, userdata, flags, rc):
            print("Connection returned " + str(rc))
        
        #实时上报
        def on_message(self, client, userdata, message):
            print(message.topic)
            print("Message Recieved: " + message.payload.decode())
            
            if message.topic == 'gmrplat/nowtime':
                imgPath = self.get_img_path()
                self.gas_meter_photography(imgPath)
                self.send_photo(imgPath)
    
        def on_publish(self, mqttc, obj, mid):
            print("mid: " + str(mid))
        
        def on_subscribe(self, mqttc, obj, mid, granted_qos):
            print("Subscribed: " + str(mid) + " " + str(granted_qos))
        
        def on_log(self, mqttc, obj, level, string):
            print(string)
        
        def on_disconnect(self, mqttc, obj, rc):
            print("unsuccess connect %s" % rc)
        
        def loop_forever(self):
            self.client.connect(self.mqtt_server_ip, 1883)
            self.client.on_message    = self.on_message
            self.client.on_connect    = self.on_connect
            self.client.on_publish    = self.on_publish
            self.client.on_subscribe  = self.on_subscribe
            self.client.on_log        = self.on_log
            self.client.on_disconnect = self.on_disconnect
            self.client.subscribe('gmrplat/nowtime')
            self.client.loop_forever()
        
    def mqtt_task(grmdev):
        print("子线程%s开始..."%(threading.current_thread().name))
        grmdev.loop_forever()
        print("子线程%s结束..."%(threading.current_thread().name))
    
    #每天自动采集一次,设计采集1年的量    
    def gmr_task(grmdev):
        time.sleep(5)
        for x in range(1, 365):
            imgPath = grmdev.get_img_path()
            grmdev.gas_meter_photography(imgPath)
            grmdev.send_photo(imgPath)
            time.sleep(60 * 60 * 24)
        print('');
    
    if __name__ == '__main__':
        gmrdev = GMRDevice()
        #gmrdev = GMRDevice(cam_simulate=True)
    
        print("主线程%s启动..."%(threading.current_thread().name))
        t1 = threading.Thread(target = mqtt_task, args=(gmrdev,))
        t1.start()
        
        gmr_task(gmrdev)
        t1.join()
        print("主线程%s结束..."%(threading.current_thread().name))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149

    4.燃气表抄表平台

    系统环境要求:LINUX系统,支持Docker环境。

    4.1 容器配置

    4.1.1 Dockerfile-GMR文件

    FROM sapk/cloud9
    
    COPY sources.list /etc/apt/sources.list
    
    RUN apt update
    RUN apt upgrade -y
    RUN apt install -y wget python3 curl inetutils-ping graphicsmagick vim
    
    WORKDIR /tmp
    #download font(AlibabaPuHuiTi-3-65-Medium.ttf)
    RUN apt install -y unzip
    RUN wget https://puhuiti.oss-cn-hangzhou.aliyuncs.com/AlibabaPuHuiTi-3/AlibabaPuHuiTi-3-65-Medium.zip
    RUN unzip AlibabaPuHuiTi-3-65-Medium.zip && rm AlibabaPuHuiTi-3-65-Medium.zip && rm -r __MACOSX
    RUN cp /tmp/AlibabaPuHuiTi-3-65-Medium/AlibabaPuHuiTi-3-65-Medium.ttf /tmp && rm -r /tmp/AlibabaPuHuiTi-3-65-Medium
    
    # Install nvm node 16
    RUN apt install -y git
    RUN git clone http://github.com/creationix/nvm.git /root/.nvm;
    RUN chmod -R 777 /root/.nvm/;
    RUN bash /root/.nvm/install.sh;
    RUN export NVM_DIR="$HOME/.nvm";
    RUN echo "[[ -s $HOME/.nvm/nvm.sh ]] && . $HOME/.nvm/nvm.sh" >> $HOME/.bashrc;
    
    RUN bash -i -c 'nvm install 16'
    RUN bash -i -c 'npm install -g pnpm pm2'
    
    WORKDIR /workspace
    
    CMD ["/bin/bash"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    4.1.2 Dockerfile_OCR文件

    FROM ubuntu
    RUN apt update
    RUN apt upgrade -y
    RUN apt install -y wget libgl1 libglib2.0-dev python3 pip
    RUN pip3 install --upgrade pip -i https://mirror.baidu.com/pypi/simple
    
    #安装百度PaddleOCR
    RUN pip3 install requests paddlepaddle paddleocr -i https://mirror.baidu.com/pypi/simple
    RUN wget http://nz2.archive.ubuntu.com/ubuntu/pool/main/o/openssl/libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb
    RUN dpkg -i libssl1.1_1.1.1f-1ubuntu2.19_amd64.deb
    
    CMD ["/bin/bash"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.1.3 docker-compose.yaml文件

    version: '3.3'
    services:
      #Gas meter reading platform:燃气采集平台
      #docker exec -it gmrplat_t /bin/bash
      gmrplat_t:
        hostname: gmrplat
        build:
          context: ./
          dockerfile: Dockerfile-GMR
        working_dir: /workspace
        image: gmrplat:v1
        volumes:
          - ./workspace:/workspace
        container_name: gmrplat_t
        ports:
          - 8484:8181
          - 8477:8477
          - 8488:8488
        restart: always
        command: --port 8181 --auth admin:cw    
        tty: true
        networks:
          - gmrnet
    
      #百度AI/图像识别
      #docker exec -it paddle_ocr_t /bin/bash  
      paddle_ocr_t:
        hostname: ocr
        container_name: paddle_ocr_t
        build:
          context: ./
          dockerfile: Dockerfile-OCR
        image: gmr_paddle_ocr
        volumes:
          - ./PaddleOCR:/PaddleOCR
        tty: true
        privileged: true
        restart: always
        command: /bin/bash
        networks:
          - gmrnet
        
      #开源物联网MQTT服务器(https://www.emqx.com/)
      #初始用户名密码:admin/public
      #docker exec -it emq_t /bin/bash
      emq_t:
        hostname: emq
        container_name: emq_t
        image: emqx/emqx:5.3.0
        ports:
          - 1883:1883
          - 8083:8083
          - 8084:8084
          - 8883:8883
          - 18083:18083
        restart: always
        tty: true
        networks:
          - gmrnet
      
      #开源数据库,兼容MYSQL
      mariadb_t:
        hostname: mariadb
        container_name: mariadb_t
        image: mariadb
        environment:
         - MYSQL_ROOT_PASSWORD=cjy
         - MYSQL_DATABASE=gmrplat
         - MYSQL_USER=cjy
         - MYSQL_PASSWORD=cjy
        restart: always
        networks: 
         - gmrnet
         
      #数据库WEB后台管理
      phpmyadmin_t:
        hostname: phpmyadmin
        container_name: phpmyadmin_t
        image: phpmyadmin:5.2-apache
        ports:
         - 4090:80
        environment:
         - PMA_ARBITRARY=1
         - PMA_HOST=mariadb
         - PMA_PORT=3306
         - PMA_USER=root
         - PMA_PASSWORD=cjy
        restart: always
        networks: 
         - gmrnet
    
    #自定义容器网络
    networks:
      gmrnet:
        ipam:
          driver: default
          config:
            - subnet: 172.77.1.0/24
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98

    4.2 图像识别

    百度开源AI:https://github.com/PaddlePaddle/PaddleOCR
    在这里插入图片描述照片OCR识别样例代码

    from paddleocr import PaddleOCR, draw_ocr
    ocr = PaddleOCR(use_angle_cls=True, lang="en")
    img_path = 'xx.jpg'
    result = ocr.ocr(img_path, cls=True)
    for idx in range(len(result)):
        res = result[idx]
        for line in res:
            print(line)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.3 微信前台

    主要代码。

    <script setup>
    import { ref } from 'vue'
    import 'weui';
    import axios from 'axios';
    
    async function get_gmr_img() {
        console.log('get_gmr_img')
        try{
            let res = await axios.get('/api/gmr_img_now');
            console.log(res);
            let result = res.data;
            console.log(result);
    
            res = await axios.get('/api/gmr_get_last_img');
            console.log(res);
            result = res.data[0];
            console.log(result.img_name);
            const imgUrl = '/images/mask_' + result.img_name;
            document.getElementById('gmr_img').src = imgUrl
        }catch{
            console.log('请求失败');
        }
    }
    </script>
    
    <template>
        <img id="gmr_img" src="/pi.jpg" class=gmr_img>
        <button class="weui-btn weui-btn_primary" @click="get_gmr_img">立即更新</button>
        <div style="text-align:center;" id="actionsheet1" class="weui_actionsheet_cell">长按燃气表图片2秒转发给抄表员</div>
    </template>
    
    <style scoped>
    .gmr_img {
        width:  100vw;
        height: 80vh;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    4.4 采集后台

    CREATE TABLE `gmrinfo` (
      `id` bigint(12) NOT NULL,
      `gather_time` datetime NOT NULL DEFAULT current_timestamp(),
      `img_name` varchar(30) NOT NULL,
      `img_ocr` varchar(255) DEFAULT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
    
    ALTER TABLE `gmrinfo`
      ADD PRIMARY KEY (`id`);
    
    ALTER TABLE `gmrinfo`
      MODIFY `id` bigint(12) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=12;
    COMMIT;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    const {promises:fs} = require("fs");
    const moment = require('moment');
    const Koa    = require('koa');
    const Router = require('koa-router');
    const koa_static  = require('koa-static');
    const mqtt   = require('mqtt');
    const axios  = require('axios');
    const knex   = require('knex'); //https://knexjs.org/
    const gm     = require('gm');
    
    const OCR_SERVER_IP    = 'ocr'
    const OCR_SERVER_PORT  = 8466
    
    const MQTT_SERVER_IP   = 'emq'
    const MQTT_SERVER_PORT = 1883
    
    const DB_SERVER_IP     = 'mariadb'
    const DB_USER          = 'cjy'
    const DB_PWD           = 'cjy'
    const DB_NAME          = 'gmrplat'
    
    const GMRPLAT_SERVER_PORT = 8488
    
    const db = knex({
      client: "mysql2",
      connection: {
        host: DB_SERVER_IP,
        port: 3306,
        user: DB_USER,
        password: DB_PWD,
        database: DB_NAME,
      },
    });
    
    const WWWROOT = __dirname + '/public'
    const IMGDIR  = WWWROOT + '/images/'
    
    //图像识别
    const GMRToOCR = async (imgfile) => {
        const url = "http://" + OCR_SERVER_IP + ":" + OCR_SERVER_PORT + "/GMROCR"
        const bitmap = await fs.readFile(imgfile);
        const adata = {
            request_id: '001',
            img_base64: ''
        }
        adata['img_base64'] = Buffer.from(bitmap).toString('base64');
        const res = await axios.post(url, adata);
        console.log(res.data)
        console.log(res.data.ocr_res)
        return res.data
    }
    
    const client = mqtt.connect("mqtt://" + MQTT_SERVER_IP + ":" + MQTT_SERVER_PORT);
    client.subscribe("gmrdev/fixedtime")
    
    client.on('message', async (topic, payload) => {
        console.log(`接收MQTT订阅消息:${topic}`);
    
        const base64EncodedfileData = payload.toString();
        const fileDataDecoded = Buffer.from(base64EncodedfileData,'base64');
        const img_name = moment(Date.now()).format('YYYYMMDDHHmmss');
        fs.writeFile(IMGDIR + img_name + ".jpg", fileDataDecoded, err => {})
    
        gm(IMGDIR + img_name + ".jpg")
          .stroke("blue") //字体外围颜色
          .fill("blue")   //字体内围颜色(不设置默认为黑色)
          .font("/tmp/AlibabaPuHuiTi-3-65-Medium.ttf", 60) //字库所在文件夹和字体大小
          .drawText(50,50, img_name + "\nXX街道X小区X幢X室")
          .write(IMGDIR + "mask_" + img_name + ".jpg", async (err) => {
             if (!err){
                console.log(`${img_name}:加水印成功`);
                await db('gmrinfo').insert({img_name: `${img_name}.jpg`})
                console.log(`${img_name}:写入数据库`);
                //const ocr_res = await GMRToOCR(IMGDIR + img_name + ".jpg");
             }
             else {
                console.log(err);
             }
        });
    });
    
    const app_http  = new Koa()
    //允许跨域
    app_http.use(async (ctx, next) =>{
        await next();
        ctx.set("Access-Control-Allow-Origin", "*");
        ctx.set("Access-Control-Allow-Headers", "Content-Type");
        ctx.set("Access-Control-Allow-Methods", "*");
    });
    
    //静态文件
    app_http.use(koa_static(WWWROOT, {index: 'index.html', hidden: false, defer: true}))
    
    let lastupdate = true
    const sleep = async (ms) => {
        return new Promise(resolve=>setTimeout(resolve, ms))
    }
    
    //收到微信上H5的请求后,通知采集设备拍照燃气表上传
    const gmr_img_now = async (ctx) => {
        console.log('微信H5请求:通知采集设备拍照燃气表上传')
        client.publish('gmrplat/nowtime', 'now')
        ctx.body = '{errorcode:0}'
        lastupdate = false
    }
    
    //收到微信上H5的请求后,返回最新的燃气表拍照
    const gmr_get_last_img = async (ctx) => {
        console.log('微信H5请求:获取最新燃气表拍照')
        for(x = 0; x < 10; x++){
            if(lastupdate == false){
                lastupdate = true
                break
            }
            await sleep(1)
        }
        const result = await db("gmrinfo").select(["id", "gather_time","img_name","img_ocr"]).orderBy('id', 'desc').limit(1)
        console.log(result)
        ctx.body = result
    }
    
    const router  = new Router();
    router.get('/api/gmr_img_now', gmr_img_now);
    router.get('/api/gmr_get_last_img', gmr_get_last_img);
    app_http.use(router.routes());
    
    app_http.listen(GMRPLAT_SERVER_PORT, '0.0.0.0', async () => {
        //获取公网IP地址
        let publicip = 'x.x.x.x'
        const res = await axios('http://ifconfig.co/json')
        publicip = res.data.ip.trim()
        console.log(`服务启动:http://${publicip}:${GMRPLAT_SERVER_PORT}`);
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133

    5.系统演示

    配套代码
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    技术干货| 如何运用 MongoDB 部分索引优化性能问题?
    2024广东省职业技能大赛云计算赛项实战——构建CICD
    问题记录:../../lib/libopencv_core.so: undefined reference to `dlclose‘
    C/C++语言100题练习计划 93——矩阵乘法(线性代数)
    API网关之微服务网关Spring Cloud Gateway与Netflix Zuul
    股票买卖Ⅴ
    阿里新一代微服务,内部资深架构师手抄的笔记+脑图不容错过,全是精华
    在计算机领域如神一般存在的人都有哪些?
    VNC图形化远程连接Ubuntu服务器
    JMeter 如何实现 Elasticsearch 8.X 性能测试?
  • 原文地址:https://blog.csdn.net/cwdelphi/article/details/133943897