從0開始疫情3D地球 - 3D疫情地球VDEarth - 6 - 數據推送

前面幾篇已經完成了前端3D疫情地球的實現和疫情數據的爬蟲,如何將這兩個連接起來,須要在這兩個程序之間搭建一個服務html

服務類型不少,這裏使用websocket實現前端

1 websocket服務端python

2 websocket客戶端mysql

websocket服務端

這裏使用python實現一個簡易的websocket服務端,藉助python組件websockets便可實現git

新建一個server.py,作一個簡單鏈接驗證,代碼最後設定服務地址爲 localhost:9998 github

import asyncio
import websockets
import json
from mysqlDataAccess import MysqlDataAccess
import logging
from datetime import datetime
import time

interval = 60

logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s',
                    level=logging.INFO)
logging.info('websocket服務啓動')

# 檢測客戶端權限,用戶名密碼經過才能退出循環
async def check_permit(websocket):
    while True:
        recv_str = await websocket.recv()
        cred_dict = recv_str.split(":")
        if cred_dict[0] == "admin" and cred_dict[1] == "123456":
            logging.info('認證經過')
            return True
        else:
            content = {"code": 0, "msg": "authentication failed"}
            logging.info('認證失敗')
            await websocket.send(json.dumps(content))

# 接收客戶端消息並處理,這裏只是簡單把客戶端發來的返回回去
async def send(websocket):
    await send_data(websocket)
    while True:
        await send_data(websocket)
        time.sleep(interval)

# 服務器端主邏輯
async def main_logic(websocket, path):
    await check_permit(websocket)
    # await recv_msg(websocket)
    await send(websocket)

async def send_data(websocket):
    access = MysqlDataAccess()
    data = access.getData()
    logging.info('數據獲取完成')
    await websocket.send(json.dumps(data))
    logging.info('信息發送中')


start_server = websockets.serve(main_logic, 'localhost', 9998)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

裏面調用了一個MysqlDataAccess類,這個用來定時的從mysql數據庫獲取數據,相關代碼以下,實現了MySQL的疫情數據的讀寫web

import pymysql 

MYSQL_HOST = 'localhost'
MYSQL_DB = 'covid19'
MYSQL_USER = 'root'
MYSQL_PASSWORD = '123456'
MYSQL_PORT = 3306


class MysqlDataAccess:
    def __init__(self,host = MYSQL_HOST,database=MYSQL_DB,user=MYSQL_USER,password=MYSQL_PASSWORD,port=MYSQL_PORT):
        try:
            self.db = pymysql.connect(host,user,password,database,charset='utf8',port=port)
            self.cursor = self.db.cursor(cursor=pymysql.cursors.DictCursor)
        except  pymysql.MySQLError as e :
            print(e.args)

    def updateCountryPosition(self,name,lng,lat,type):
        sql_update = 'insert into dic_lnglat (name,lng,lat,type) values(%s,%s,%s,%s) on duplicate key update'
        sql_update += ' name= %s,lng=%s,lat=%s,type=%s'
        try:
            self.cursor.execute(sql_update,(name,lng,lat,type) * 2)
            self.db.commit()
            print(name)
        except pymysql.MySQLError as e:
            print(e.args)
            self.db.rollback()

    def getData(self):
        sql_query = 'SELECT a.name,b.lng,b.lat,IFNULL(a.new,0) as new,IFNULL(a.now,0) as now,IFNULL(a.total,0) as total,IFNULL(a.cure,0) as cure,IFNULL(a.death,0) as death from covid19  a inner join dic_lnglat b on a.`name`=b.`name`'
        try:
            self.cursor.execute(sql_query)
            return self.sql_fetch_json()
        except pymysql.MySQLError as e:
            print(e.args)


    def sql_fetch_json(self):
        keys = []
        for column in self.cursor.description:
            keys.append(column[0])
        key_number = len(keys)

        json_data = []
        for row in self.cursor.fetchall():
            item = dict()
            for q in range(key_number):
                item[keys[q]] = row[keys[q]]
            json_data.append(item)

        return json_data

這樣一個簡單的websocket服務就完成了sql

爲了啓動簡單,在windows環境下,新建一個socketServer.bat用來啓動服務數據庫

@echo off


F:
rem ִ開啓socker服務 
cd F:\mygithub\VDDataServer\SocketServer
python3 server.py

TIMEOUT /T 10
exit

執行bat就能夠打開服務json

websocket客戶端

websocket的客戶端,VDEarth是基於js的前端可視化應用,這裏須要實現一個前端的websocket客戶端,這裏直接對以前的VDEarth前端組件進行改造

在VDEarth項目文件夾src文件夾下新增一個socket.js, 新增socket訪問類

import _ from 'lodash';

function reconnect() {
  console.log('從新鏈接');
}
function open() {
  // Web Socket 已鏈接上,使用 send() 方法發送數據
  this.ws.send(this.account + ':' + this.password);
}
// 接受數據執行回調方法
function receive(evt, callback) {
  var data = evt.data;
  if (_.isFunction(callback)) {
    callback(JSON.parse(data));
  }
}
function close() {
  // 關閉 websocket
  console.log('鏈接已關閉...');
  reconnect();
}
function reload() {
  this.ws.send('reload');
}
class socket {
  constructor(server, port, account, password) {
    this.account = account;
    this.password = password;
    this.server = server;
    this.port = port;
  }

  init(onmessage) {
    if ('WebSocket' in window) {
      try {
        this.uri = 'ws://' + this.server + ':' + this.port;
        this.ws = new WebSocket(this.uri);
        this.ws.onopen = () => {
          open.call(this);
        };
        this.ws.onmessage = (evt) => {
          receive.call(this, evt, onmessage);
        };
        this.ws.onclose = () => {
          close.call(this);
        };
        window.onbeforeunload = () => {
          reload();
        };
      } catch (error) {
        console.log(error);
      }
    } else {
      // 瀏覽器不支持 WebSocket
      console.log('您的瀏覽器不支持 WebSocket!');
    }
  }
}

export default socket;

修改VDEarth.js

數據的加載主要是用在了柱形圖和名稱標記那裏,因此修改那裏的代碼,即initObj方法,VDEarth類的options方法配置默認的socket連接,server  = localhost,port = 9998,account = admin,password = 123456

// 建立模型
function initObj() {
  let self = this;
  let fontloader = new FontLoader();
  // 建立地球模型組
  this.baseGroup = new Group();
  // 建立地球
  this.radius = minSize(this.contentWidth, this.contentHeight) * 0.2;
  let globalMesh = new model().createGlobe(this.radius, this.textrue);
  this.baseGroup.add(globalMesh);
  // 建立星點
  let stars = new model().createStars();
  this.baseGroup.add(stars);

  // 建立鏈接
  var mySocket = new socket(
    this.options.server,
    this.options.port,
    this.options.account,
    this.options.password
  );
  mySocket.init(function (data) {
    // 建立一個標記組
    self.markerGroup = new Group();
    // 建立標記
    var myMarkers = new marker();
    // 柱形
    myMarkers.addBoxMarkers(self.markerGroup, self.radius, data);
    // 加載字體
    if (self.font) {
      myMarkers.addNameMarkers(self.markerGroup, self.radius, data, self.font);
    } else {
      fontloader.load('./fonts/SimHei_Regular.json', function (font) {
        self.font = font;
        myMarkers.addNameMarkers(
          self.markerGroup,
          self.radius,
          data,
          self.font
        );
      });
    }
    self.baseGroup.add(self.markerGroup);
  });
  this.scene.add(this.baseGroup);
}

至此定時數據推送和VDEarth的展現更新就完成了

打開爬蟲服務,打開websokcet服務,打開瀏覽器就輸入VDEarth 的web容器的地址就能夠了

 

相關連接

從0開始疫情3D地球 - 3D疫情地球VDEarth - 1- 引言 

從0開始疫情3D地球 - 3D疫情地球VDEarth - 2 - 前端代碼構建 

從0開始疫情3D地球 - 3D疫情地球VDEarth - 3 - 3D地球組件實現(1) 

從0開始疫情3D地球 - 3D疫情地球VDEarth - 4 - 3D地球組件實現(2) 

從0開始疫情3D地球 - 3D疫情地球VDEarth - 5 - 疫情數據爬蟲 

從0開始疫情3D地球 - 3D疫情地球VDEarth - 6 - 數據推送  

相關文章
相關標籤/搜索