Enjoy Eating & Creating
post @ 2017-11-26

Context

好像很久很久没有更新博客了,最近事情确实太多了。新产品的app,教研室新项目,两个大头。还有毕业设计的开题,虽然我想好了题目,但是,磨人的开题报告,也是个很头疼的东西。

这次教研室的项目是电力线通信,使用G3-PLC协议,这也是第一次做一个偏硬件的项目了,一开始老师在问大家有没有兴趣的时候,还是蛮犹豫的,但是还是因为兴趣,决定搞一下试试。

花了几周的时间好好的熟悉了一下这个协议。在电力线通信的场景中,会有很多意想不到的情况。比如A->B是直连的,B->A就不能直连,需要绕一条路走。而且电力线网络的拓扑结构是会随时随刻在改变的,这一时刻你和他直连,下一时刻他就不见了。

对于这种物理层这么不可靠的连接,需要在它的基础上构建一个相对可靠的网络,还是比较麻烦的。G3-PLC借鉴了ZigBee的技术,并且引入了6LowPan技术,使得这个网络的可靠通信成为可能。

ZigBee?

ZigBee不是无线的嘛?

这电力线是有线连接的,竟然还需要用无线的协议。其实他们还是挺像的,ZigBee每个节点的发射功率有限,为了与不在射程范围内的节点进行通信,就只能通过其他节点来帮助它传递消息。电力线虽然说大家都是有线连接在一根主线(总线)(插线板)上,但是他的衰减比较严重,那么中间节点就需要充当一下”中继”,将收到的消息进行转发,以实现全网的通信。

6LowPan?

这个概念倒是很有意思,大家都知道ipv4的地址就要分配完了,所以赶紧推广ipv6是非常重要的,而物联网时代,网络节点的数量会比互联网时代大的更多,毕竟每个物联网外设都很便宜,他们要入网也需要一个地址来标识。大概是出于这一点的考虑,G3-PLC在设计的时候就引入了6LowPan,是一种有超大地址空间的一个协议。这个名字很奇特6LowPan,他的意思就是低功耗的,ipv6的,个域网。

为了低功耗,它甚至省去了很多数据,通过减少数据量来降低功耗,真是很拼。

Router

网络通信,最重要的一个部分,莫过于路由了。

G3-PLC的适配层用了LAODng协议,这个东西的资料比较的少。不过另一个东西的资料我在图书馆里找到了很多——AODV,是Ad Hoc网络中的按需询问的路由协议。LAODng是,新一代轻量级按需路由协议。

怎么个轻量级,我现在还真是说不准,按照我自己的猜想,这个轻量级只是减少了一些操作,少维护一些内容,数据发送频率低一些。

比如说,在刚加入网络的时候,AODV协议里需要节点对整个网络进行一下”摸索”,比如发送RREQ询问一下距离本节点x跳的各位节点,维护一张路由表。中间节点也会根据这个RREQ请求,自己建立一张表,在未来出现链路中断现象的时候可以发送RERR信息告诉上游节点,路线断了。

不过,按照我的理解,以及我对官方给的非常简略的协议的理解,以及我对借的各种书的理解,我觉得这个低功耗大概是第一次获取路由的时候只获取一跳的节点、出现断路不通知、之类的操作。通过这一系列的优化来达到轻量级低功耗的目的。

Oops…

其实在理解这个协议的时候还是走了不少弯路的,比如我一直无法理解自己的地址是怎么来的,直到我明白了这个系统有coordinator和device两种设备。我一直不知道我怎么获取别人的地址,知道我想明白,这个地址不用我们获取,而是上层指使就好了。

这个项目从最早的无从下手,到现在感觉有把握了。我觉得自己还是学到了不少新东西,说明自己还没有老。作为一个学生还是要有这种学习能力的嘛~

下面大概要好好研究一下Keil和嵌入式开发了!

阅读此文
post @ 2017-10-26

最近真的是很忙呢。

上周刚刚把公司官网勉强整好,写前段并不是主要的目的。做官网主要的目的是学习一些先进的前端技能。虽然光靠html,css,js就确实可以完成所有的网站需求了,但是,要提高效率,与时俱进,这些必然是不够的。所以通过做官网,我顺便补充了一些技能,什么node.js,gulp,vue.js,angular之类的高端东西。不过实在是太忙了,所以就只能空了再整理到博客里面去。

教研室项目也是很多……包括做不出来的,以及不可能做出来的东西…

比如二维码防伪,你别以为是简单的用二维码防伪…而是验证一个二维码是不是真的,仔细想想,是不是没可能…?

G3PLC电力线通信协议??好难啊!如果我能把这个做出来,估计可以在嵌入式领域装个逼了~哈哈

还剩一个比较轻松的,就不用说了。

12月要出一个新产品,感觉时间也是非常的紧。虽然前面花了一些时间,整了一下socket的知识,但是感觉这个项目还是蛮大的(而且有iOS和Android两个版本呢)…希望可以按期完成吧。

生活很艰辛,抽空还是要吃几盘鸡的,对吧!

阅读此文
post @ 2017-10-10

Context

Http是在tcp的基础上做了高级封装,变成了一个被动的,短链接请求.请求完了就把连接断开了,下一次要请求,就需要再一次进行tcp握手,然后通信……在前面第一篇中,我用了定时轮训的方式问服务器,当前这个二维码是否登录,从功能的角度来说,应该是没有问题的,而且写起来也是特别的方便.

不过仔细的想一想,其实还是比较浪费资源的.从信息论的角度来说,中间的N次质询,相当于没有任何的信息量(请不要深究这一句),所以,我们不用http来更新扫码状态!

我们用

Web Socket!

Web Socket

关于websocket的定义,解释什么的,百度一下,你全都知道了,所以我就不在这个地方多说.

只说明一点,websocket屌的飞起,和socket的操作一毛一样,关键是服务器终于可以主动的发消息给客户端啦!

他算是一个长连接,握手之后,就开始了肮脏的通信,通信完了,一方可以发起关闭操作,然后……,最后连接就拆掉了.

Django

websocket怎么说也就只能算是对web服务器的一个扩充,如何在已有的web服务器上快速集成一个websocket的服务,才是比较重要的~

由于之前用的是django做服务器,所以我们就找找给django加上websocket的方法~

在他的官方文档中提到了很多可用的开源app,其中有一个叫做channels的库,他用了asgi,直接可以快速集成在django中,牛的一批.

Channels

这个库的文档,真的是无力吐槽,我真是集成的很辛苦

我们来简化一下他的文档吧!

Router

和django一样,websocket的连接也要有个路由的,我们先建立一个py文件

from channels.routing import route
# 这个文件等会儿解释
from users.webClient import connection_handler, disconnection_handler,subscribe_token

channel_routing = [
    route("websocket.connect", connection_handler, path=r"/ws/$"),
    route("websocket.disconnect", disconnection_handler, path=r"/ws/$"),
    route("websocket.receive", subscribe_token, path=r"/ws/$"),
]

这里的websocket.connect,websocket.disconnect,websocket.receive相当于是websocket的三种事件,已连接、已断开、收到消息

中间那个参数和django里面的那个描述一样,就是该路由对应的处理方法.

Handler

现在来解释一下上一节说的 users.webClient这个文件

# -*- coding: utf-8 -*-

import json
from channels.sessions import channel_session
from users.qrcode_helper import *


@channel_session
def connection_handler(message):
    message.reply_channel.send({"text": json.dumps({"status":False,"msg":"connected"})})


@channel_session
def subscribe_token(message):
    try:
        obj = json.loads(message.content["text"])
        tokens = obj["token"]
        qrcode_record = fetch_qrcode_record_with_token(token=tokens)
        if qrcode_record is not None:
            if is_token_pass_the_authentic(tokens):
                message.reply_channel.send(
                    {"text": json.dumps({"status": True, "msg": "succeed"})})
            else:
                message.reply_channel.send(
                    {"text": json.dumps({"status": False, "msg": "succeed"})})
                qrcode_record.webSocket_session = str(message.content["reply_channel"])
                qrcode_record.save()
                # link token to this channel
        else:
            message.reply_channel.send({"text": json.dumps({"status": "-1", "msg": "token invalid"})})
            message.reply_channel.send({"close": True})
    except Exception as e:
        message.reply_channel.send({"text": e.message})
        message.reply_channel.send({"close": True})


@channel_session
def disconnection_handler(message):
    channel = str(message.content["reply_channel"])
    qrcode_records = fetch_qrcode_record_with_web_socket_channel(channel)
    if qrcode_records is not None:
        for qrcode_record in qrcode_records:
            qrcode_record.webSocket_session = ""
            qrcode_record.save()

很长,也不用仔细看,因为里面主要做了扫码登录里面的事情,如果只是要集成一下websocket的话,不用关心这些方法里面的操作,只要注意这里每个方法的传入参数 message

Message有那些用处呢?

1.通过message.channel或者message.reply_channel可以获取到连接的通道(请暂时不要使用高级操作)
2.message.content其实是一个字典,这个字典遵循了ASGI的消息模型,里面有text,reply_channel等等字段

在我的处理代码里面,我从message.content里面取出了reply_channel,他的value是一个长得很奇怪的字符串(我把它类比成文件描述符把),毕竟要有很多很多很多连接的话,它们的文件描述符不能重复对吧,所以就又长又难看,比如像daphne.response.FSITXWDDzG!IOtDNILqzh

有了这个reply_channel,在后面主动发送消息下去的时候,就可以指定连接发送了~

Attention

注意,这里的websocket.connect的handler里面一定一定,千万千万要给反馈消息回去,不然浏览器(只测试了Chrome的)会以为连接超时的!然后给你一个503 bad gateway,之后就主动断开连接了,留你一脸蒙逼的在那儿傻看着.

Send

现在我们可以看看之前第一章中的app调用的扫码接口了

@csrf_exempt
@require_POST
@pass_auth
@require_parameter(["code"])
def allow_the_qrcode_login(request):
    code = request.POST["code"]
    user = get_user_from_response_session(request)
    qr_record = fetch_qrcode_record_with_code(code)
    if user is not None and qr_record is not None:
        if qr_record.user_id is not None:
            return JsonResponse({"msg": "expired", "status": -1}, status=400)
        qr_record.user_id = user.user_uuid
        qr_record.status = True
        qr_record.save()
        if qr_record.webSocket_session is not None:
            # send new status to the web socket
            channel = Channel(name=qr_record.webSocket_session)
            content = {"text": json.dumps({"status": True, "msg": "succeed"})}
            channel.send(content)
            pass
        return JsonResponse({"msg": "succeed", "status": 0}, status=200)
    return JsonResponse({"msg": "code not existed", "status": -400}, status=400)

这里加了一些些小的改动,我再把他取出来

if qr_record.webSocket_session is not None:
    channel = Channel(name=qr_record.webSocket_session)
    content = {"text": json.dumps({"status": True, "msg": "succeed"})}
    channel.send(content)

websocket.receive的时候,我把websocket的文件描述符记录在了qrcode的记录中,这样扫描某一个qrcode的时候,就可以知道这个二维码是否有websocket正在订阅他,有订阅的话,就可以拿来对指定的通道进行发送了

End

网页端真的没什么好说的,直接贴(里面没有很严密的控制权限,稍微改一改就行,但是我懒)

function listen_for_status() {
    socket = new WebSocket("ws://" + window.location.host + '/ws/');
    socket.onmessage = function (e) {
        var obj = JSON.parse(e.data);
        if (obj.status == true) {
            socket.close();
            window.location="dashboard.html";
        }
    };
    socket.onopen = function () {
        var obj = {};
        obj.token = token;
        socket.send(JSON.stringify(obj));
    };
}

还有不要忘记更新nginx的配置

扫码登录的这个小玩意儿,虽然看起来很是简单,但是也耗了我很多的时间,这个过程中,也是学到了很多新的东西呢!

这次也是达成了一个新的成就:WebSocket(Server&Client)

阅读此文
⬆︎TOP