当前位置: 首页 > news >正文

【设备状态与人员动态的监测和呈现-会议签到的补充】

基于flask-socketio的设备状态与人员动态的监测和呈现-会议签到的扩展

  • 系统结构
  • flask-socketio后端的设计
  • 前端布局
  • 数据获取的机制
    • 验证socket.io.js在网页的操作
    • 过程解释
  • 真实数据的处置
    • 源码主要使用jQuery和js
    • 解释:
  • 最终

系统结构

  1. 定义程序文件ckapp 定义 app
    socketio(app,allowcron=[*])
    2, 使用gunicorn 运行ckapp
gunicorn --worker-class gevent -w 1 ckapp:app

同目录下有个 gunicorn.conf.py

workers = 5                                                                                                         
worker_class = "gevent"                                                                                             
bind = "0.0.0.0:8080"                                                                                               

这里的参数也是有效的,但是只用-c, 这个文件,无法打开socketio服务.所以两项准备.文件和参数.
运行这个app 测试所开端口是否接受soketio测试html的访问.

  1. 使用caddy, 语法 path 指定socket.io路径的代理时复写此路径.,直接从根处获取操作,并且指定真实ip转发, caddy如果在容器要,network,要是host模式,不然真实ip无法得到.
    socket.io/*
    vision 2.6.2
:9000 {# Set this path to your site's directory.root * /caddy/webrootfile_server {browse}rewrite /socket.io /socket.io/handle_path /socket.io/* {rewrite * /socket.io{path}reverse_proxy 10.80.133.35:7055 {header_up Host {host}header_up X-Real-IP {remote}}

测试网页放入/caddy/webroot,接口改为9000
caddy的官方容器,alpine ,不带时区, 介绍是apk --no-cache add tzdata.需要在docker-compare中加入.并指定环境变量TZ
https://github.com/fjc0k/docker-caddy/blob/master/src/Dockerfile
离线下的安装, apk fetch tzdata , 然后cp tzdata-----.apk to 目标容器
apk --no-cache add tzdata----.apk.
完成后在容器变量加入 TZ= Asia/Shanghai

flask-socketio后端的设计

初始化app

app = Flask(__name__)
app.config['SECRET_KEY'] = 'top-secret!'
app.config['REDIS_URL']="redis://:6379/9"
cl=FlaskRedis()
cl.init_app(app)
message_queue= app.config['REDIS_URL']
socketio = SocketIO(app,cors_allowed_origins='*' ,message_queue= app.config['REDIS_URL']) 

主要path

online_users = {} 
last_status={}
def getdtime():return  datetime.datetime.now().strftime("%Y-%m-%d[%H:%M]")
@app.route('/ip')
def myip():if request.headers.getlist("X-Forwarded-For"):ip=request.headers.getlist("X-Forwarded-For")[0]elif  request.headers.getlist("X-Real-IP"):ip=request.headers.getlist("X-Real-IP")[0]else:ip =request.remote_addrreturn ip
def logstatus(log='<-->'):newvalue=f"{log}|{getdtime()}|{myip()}"tkey= (myip() in ipdic.keys()) and ipdic[myip()] or myip()last_status[tkey]=newvaluesocketio.emit('message', json.dumps({tkey:newvalue}),namespace='/chat')@socketio.on('connect',namespace='/chat')
def test_connect():global last_status,online_usersonline_users[request.sid]=myip()logstatus()print(request.sid,"connected from"+myip())@socketio.on('disconnect',namespace='/chat')
def test_disconnect():global online_users,last_statusdel online_users[request.sid]logstatus(log='><')print(request.sid,"disconnected from"+myip())
@app.route('/api/online/')
def getinline():     global online_users,last_statusreturn  str([online_users,last_status])
@app.route('/api/jonline/')
def getJsonOnline():global last_statusreturn json.dumps(last_status)

解释,数据两个,online_users,是由sid映射ip组成,last_status,是IP得到的站点名称来映射 状态|时间|IP,三个值组成的字符串.<–>代表在线><代表离线.一般情况下, 在线肯定是可以捕捉.但是离线有差别,部分客户的离线事件发生不到服务器端. 而判断逻辑也影响判断.只做一个参考.
last_status不做删除,只做更新,而online_users是会删除离线的sid条目.
其实关键的还是看前端如何表现这些数据,请看下面,虽然这些有待优化排错.

前端布局

主体是一个table

<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>当前状态列表</title><link href="/static/bootstrap.min1.css" rel="stylesheet"  ><script type="text/javascript" src="/js/socket.io.min.js"></script><script type="text/javascript" src="/js/jquery.min.js"></script><script type="text/javascript" charset="utf-8"><body><h1>当前状态</h1><a href='./api/online/'>原始数据</a>
<table  class="table"  >
<caption>各站状态监测</caption>
<thead><tr><th>机器地点</th><th>状态</th><th>更新时刻</th><th>签到</th><th>IP</th><th>行车电话</th>
<tbody id="mytable">
</tbody>
</table>
</body>
</html>    

数据获取的机制

验证socket.io.js在网页的操作

serve和client,都有,三个事件,connect ,disconnec,onmessage和一个发送方法emit.
需要试用socket.io.mini.js的代码,只有几行

 <script type="text/javascript" charset="utf-8">//虽然chat没有被reproxy 但是依然只是用/chat. 因为/socket.io将chat做参数告诉server,我等chat的消息.var socket = io.connect('http://yourip:9080/chat'  );socket.on('connect', function() {socket.emit('message', '用户已连接');});socket.on('message', function(msg) {console.log('收到消息:' + msg.data);});  </script>

serve和client,有,三个事件前面提到了,最后一个on message,也就是本次网页js唯一真正需要的

@socketio.on('message' ,namespace='/chat')
def handle_message(message):pass

从上代码经过验证,通过以后,

过程解释

server和client其实是对等的,在建立起链接后.只是双向通信的两端. .他们用message这个信封,交互.

  1. 发送: connect 时给mesage发送一个消息,
    服务器的connect事件和message事件会分两次收到网页消息,后期只用socketio机制自动派发的connect通知上线,.取消网页connect 事件处理.
  2. 接收:,网页的 on message是接受服务端的emit message内容.,构成一个回路. python中connec使用的logstatus函数 emit了一个消息,在网页这里被接收到.放在一起就是
  #python server send(on  coonect) :socketio.emit('message', json.dumps({tkey:newvalue}),namespace='/chat')//js  client  rech:
socket.on('message', function(msg) {
console.log('收到消息:' + msg.data);
})

真实数据的处置

源码主要使用jQuery和js

<script>
$().ready(()=>{function handleMsg(msg) {// alert(msg.data)console.log('收到消息:' + msg);jsonObj=JSON.parse(msg )Object.keys(jsonObj).forEach(function(key) {skey='#'+keyskey= skey.replaceAll('.','\\.')var stdt  = jsonObj[key].split('|')if ($(skey+'s').length>0){$(skey+'s').text(stdt[0])if (stdt[0].indexOf('-')>0){$(skey+'s').removeClass("label-default")$(skey+'s').addClass('label-success')}else {$(skey+'s').addClass('label-default')$(skey+'s').removeClass("label-success")}$(skey+'t').text(stdt[1])$(skey+'i').text(stdt[2])}else {if (stdt[0].indexOf('-')>0){   var cls="label-success"}else { var cls="label-default"}$('#mytable').append(`<tr><td>${key}</td><td><span class="label ${cls} " id='${key}s'> ${stdt[0]}</span></td><td><span id='${key}t'>  ${stdt[1]}</span></td><td> <span id='${key}p'></span> </td><td><span id='${key}i'>  ${stdt[2]}</span></td><td><span id='${key}c'></span> </td></tr>`)}});}var interID=null//有人点击, 5秒刷新一次,持续30秒. 等待再有人点.         
function handleMess(){if (!interID)    {interID= setInterval(refreshpersons,5000)setTimeout(function (){clearInterval(interID)interID=null},30000)}//if =0}  //end  Messvar socket = io.connect('http://10.80.133.35:9000/chat'  );socket.on('message',handleMsg);socket.on('mess',handleMess);var permisA= $.get("/api/gettp",function(data){stas=JSON.parse(data )Object.keys( stas).forEach(function(key) {$('#mytable').append(`<tr><td>${stas[key].sta}</td><td><span class="label" id='${stas[key].sta}s'></span></td><td><span id='${stas[key].sta}t'></span></td><td> <span id='${stas[key].sta}p'></span> </td>  <td> <span id='${stas[key].sta}i'></span> </td>  <td> <span id='${stas[key].sta}c'></span> </td></tr>`)})})function refreshpersons(){$.get("/api/getck", function (data){cks=JSON.parse(data )cks.forEach((item,index)=>{if (item.names.length>0){$(`#${item.sta}p`).text(item.names.join(","))}else$(`#${item.sta}p`).text("")})})} permisA.then( $.get("/api/jonline",handleMsg)) permisA.then(refreshpersons)permisA.then( $.get("/api/Jphone/", function (data){phs=JSON.parse(data )phs.forEach((item,index)=>{$(`#${item.sta}c`).text(item.iphone)})}))})</script>

解释:

  1. 初始化: 使用/api/gettp取得格式化顺序化的站点列表,有名称索引,继续补充站点电话信息,可有可无.这里用到了,$.get后的then,说明,只有在模板把信息补充完整后再指定电话信息和人员信息的填充.
  2. 取状态:分两种第一打开网页时的批量获取,和以后的message到达获取,都用到了handleMsg
    第一次批量
 permisA.then( $.get("/api/jonline",handleMsg)) 

结束模板罗列站点后,get jsonline,是一个字典(js不存在字典当object处理)后期切换成数组,固定属性名.{“店名”:“<–>|2024-1212[22:22]|IP”},此处看起来奇怪,实际上由IP也有汉字,而ip其中’."无法用在$()中进行定位,所以被’\.'替换.将按照key的名字找到行,用值的分解,赋值给状态列,时间列和ip列.其中状态根据判断<–>来设置label-success. 就是,

 $(skey+'s').addClass('label-default')
$(skey+'s').removeClass("label-success")

后续 剩下的即时更新,
流程是 server在connect和disconnect时候emit (‘message’,data),然后client, on(‘message,handelMsg’)来处理的消息,其中消息的内容是,python ckapp中的 logstatus中那一句.

 socketio.emit('message', json.dumps({tkey:newvalue}),namespace='/chat')

消息数据结构是有一个的{},在js中定义为object,.后期会模型化,用[]数组表示 [{sta:“站点”,status:“<–>|”,ip:“2.7.6.6.”}]这样处理方便一点,纯json的样子.也就是下面的人名签到数据getck所用到的样子

  1. 获取签到人员,并随时更新显示
    这次只有一个接口 /api/getck,
resutl=[{"sta": "\u664b\u5dde00", "names": ["\u738b\u4f7300"]}, {"sta": "\u8f9b\u96c6\u7ad923", "names": []},]
懂代码的人知道这是甚,,你们可以来人解密.

分不同的调用方式,初始化时调用一次,

   permisA.then(refreshpersons)

在接受到mess消息后30秒内调用6次. 之所以这样是,点名的频率是集中的.会不断有mess消息送进来.这是如果此次更新,会加重服务器负担.getck是一个综合处理后的结果,不适合,解析单个人名.平时没有mess的时候, 间隔执行有点浪费.所以采取一种复合的结果. mess在30秒内重来,将忽视, 因为此时interID不为空,表示正在处理. 30秒后 为空.可以开始下一个30秒.

var interID=null
function handleMess(){if (!interID)    {interID= setInterval(refreshpersons,5000)setTimeout(function (){clearInterval(interID)interID=null},30000)}//if =0}  //end  handleMesssocket.on('mess',handleMess);

最终

这样一篇杂乱的文章就结束了,验证了以前文章提到的事情,其一flask-socketio可以结合到flask一起用.而gunicorn可以让其一块运行在工作环境.其二caddy可以代理他们的结合体,
这次带来的新东西是连接状态的管理和监测,重点放在了 网页端的信息即使构建上.可以实时看到在线的设备.更改了标签的颜色.
好像有地方需要重新组织,有地方要补充,所有的不如直接把全部源代码提交一个地方,运行说明一切.
现在还在完善中,搞定了,有时间有心情会捣鼓的吧.
再见.
在这里插入图片描述
在这里插入图片描述


http://www.mrgr.cn/news/58361.html

相关文章:

  • 数据治理:确保数据资产健康的关键策略
  • Docker 部署 Jaeger
  • 晨辉考试抽签软件的两种注册方法之二:在线注册
  • Android 中的串口开发
  • 【C++智能指针深度解析】std::shared_ptr、std::unique_ptr与std::weak_ptr的构造、原理及应用实战
  • 深入解析Golang GMP
  • 智慧商城项目4-购物车功能
  • Django配置路由后,为什么输入http://127.0.0.1:8000/ 网址后报错了?
  • 【逆向基础】十七、PE文件格式(二)
  • 16 使用宏定义定义常量
  • OFFER攻略 08| 130+个offer背后:AIGC产品经理成长之路,零基础入门到精通,收藏这一篇就够了
  • 汇编教程 最终:文件管理与内存管理
  • Jvm中的堆和栈
  • Docker容器的基础镜像:构建现代应用程序的基石
  • 讲一讲AOP的原理,AOP在哪些场景下会失效?
  • openresty安装
  • Ubuntu 下安装 Nginx
  • NativeCrash 率从万分位降到十万分位,我做了这几件事...
  • 对比两个el-table,差异数据突显标记
  • springboot仓库管理系统-计算机毕业设计源码19585
  • 集群分发脚本
  • WUP-MY-POS-PRINTER 旻佑热敏打印机票据打印uniapp插件使用说明
  • 被面试官怼了,对nacos的原理都不理解,还多年的微服务工作经验?
  • CTF-RE 从0到N 1-1-1 开始之前-c函数手册
  • 一年四起供应链投毒事件的幕后黑手
  • 储能蓝海:技术革新与成本骤降引爆市场