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

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

基于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/webroot
	file_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_addr
    return ip
def logstatus(log='<-->'):
    newvalue=f"{log}|{getdtime()}|{myip()}"
    tkey= (myip() in ipdic.keys()) and ipdic[myip()] or myip()
    last_status[tkey]=newvalue
    socketio.emit('message', json.dumps({tkey:newvalue}),namespace='/chat')

@socketio.on('connect',namespace='/chat')
def test_connect():
    global last_status,online_users
    online_users[request.sid]=myip()
    logstatus()
    print(request.sid,"connected from"+myip())
    
  
@socketio.on('disconnect',namespace='/chat')
def test_disconnect():
    global online_users,last_status
    del online_users[request.sid]
    logstatus(log='><')
    print(request.sid,"disconnected from"+myip())
@app.route('/api/online/')
def getinline():     
    global online_users,last_status
    return  str([online_users,last_status])
@app.route('/api/jonline/')
def getJsonOnline():
    global last_status
    return 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='#'+key
            skey= 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  Mess
  
         var 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  handleMess
     socket.on('mess',handleMess);

最终

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


http://www.kler.cn/news/366496.html

相关文章:

  • ubuntu22.04安装Python的uwsgi
  • 青训营 X 豆包MarsCode 技术训练营--小M的比赛胜场计算
  • 路由器 相关知识
  • 银行客户贷款行为数据挖掘与分析
  • OceanBase 首席科学家阳振坤:大模型时代的数据库思考
  • Python基础知识-异常处理篇
  • Android 开发 调节声音 SeekBar自定义样式
  • 【入门篇】2.9 系统滴答定时器 SysTick
  • 论文笔记:通用世界模型WorldDreamer
  • 标准版关于申请火山翻译的流程
  • Oracle锁表问题处理
  • python读取学术论文PDF文件内容
  • GCN+BiLSTM多特征输入时间序列预测(Pytorch)
  • Java基础第四天(实训学习整理资料(四)Java中的方法(函数))
  • How to install Node.js and NPM on CentOS
  • 运输层知识点汇总3
  • 【ArcGIS Pro实操第5期】全局及局部空间插值:GPI、LPI、IDW等
  • win7现在还能用吗_哪些配置的电脑还可以安装win7系统
  • 基于JSP实习管理系统【附源码】
  • 五,Linux基础环境搭建(CentOS7)- 安装Kafka
  • 【排序】4.插入排序(含优化)
  • TPLCM柔性屏自动化贴合应用
  • 算法打卡 Day43(动态规划)-背包问题 + 分割等和子集
  • 查看Chrome安装路
  • IDEA项目代码报红,但可以正常编译运行
  • #HarmonyOS:页面和自定义组件生命周期