ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

python 气象设备UDP传输数据解析——笔记

2019-07-31 11:35:55  阅读:277  来源: 互联网

标签:10.0 UDP socket 16 python 传输数据 int Data


文章目录

一、背景

最近在处理公司的一设备,内置的DTU通过UDP向服务器发送16进制的数据报文,由于第一次接触此类数据解析方式,在这里做总结与反省,避免大家走弯路

二、总结内容

2.1 UDP通信服务端创建方式

步骤

  1. 创建UDP的socket通信方式。
  2. 绑定具体的端口。
  3. 设置端口复用等待(这一步可以省略)
  4. 获取数据。
  5. 向客户端发送数据。
  6. 解析储存数据。
  7. 关闭UDP的socket链接。

下面分别讲解并总结,通过MVP的方式完成以上所有步骤,不做过多延展:

1. 创建UDP的socket通信方式。

import socket
Server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

在任何类型的通信开始之前,网络应用程序都必须创建套接字。
套接字最初是为同一主机上的应用程序所创建,使得主机上运行的一个程序(又名一个进程)与另一个运行的程序进行通信。这就是所谓的进程间通信(Inter Process Communication,IPC)
嵌套字有两种类型:

  • 基于文件的:AF_UNIX
  • 面向网络的:AF_INET(面向IPv4)、AF_INET6(面向IPv6)

DUP采用的是无连接的套接字

  • 特点:不可靠(局网内还是比较可靠的),开销小。为了创建UDP套接字,必须使用SOCK_DGRAM作为套接字类型。UDP套接字的SOCK_DGRAM名字来自于单词“datagram”(数据报)。

2. 绑定具体的端口

Server.bind("", 8600)

bind 表示将创建好的Server绑定到具体的端口,注意:
当作为UDP的服务端时,前面的IP地址是可以省略的,后面是端口号有效的端口号范围为0~65535(小于1024的端口号预留给了系统)

**3.设置端口复用等待(这一步可以省略) **

Server.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)

如果端口被使用过,并且利用Socket.close()关闭了端口链接,但是端口还没有释放,可以用上述函数来进行等待端口的重调用。

4. 获取数据

Msg, ClientAddr = Server.recvfrom(1024)

在UDP中使用recvform返回的是客户端发送过来的**字节流(10进制、ASCII码、16进制等)**与客户端的IP地址,1024表示缓存数据的大小。

5. 向客户端发送数据

Server.sendto(Data)

一般UDP服务器在接收到数据后会向客户端发送心跳包确认,可以通过此函数发送数据。

6. 解析数据

Data = binascii.b2a_hex(Msg)

此函数是将传送过来的字节流报文数据解析成16进制数据。相关用法见字节流转换成ASCII码

7. 关闭UDP的socket链接

Server.close()

没什么好讲的,一定要有这个就行,不然下一次没法继续用这个端口。

2.2 16进制数据解析

1. 报文时间解析

Time = datetime.datetime.strptime(Data[26:38], "%y%m%d%H%M%S")  # 解析时间

由于时间是十进制数据,这里不用做16进制转换,直接通过datetime的strptime方法进行数据转换。

2. 16进制转换成10进制,再转换成ASCII码

chr(int(Data[224 + (i - 2):224 + i],16))

int(num, 16) 将16进制转换成10进制
chr(十进制)解析出对应的ASCII码

2.3 文件创建与数据储存分析

不得不说在储存数据文件的时候遇到了很多的坑,现在总结:
坑1:如何自动创建 年->月->日的文件夹结构?

在自己创建文件夹的时候有想到过使用makedirs()函数链式创建文件夹,但是在判断文件夹是否存在的时候一直报错,一直以为是在makedirs()使用不当造成的,有想过联合使用chdir函数与mkdir()函数来进行逐级的文件夹创建,最后效果也不是特别好。后来仔细查了好久的资料发现:

if not os.path.exists(NewPath):
    os.makedirs(NewPath)

在默认的IDLE中判断文件夹是否存在时,一直是有问题会报错的,折腾了好久,后来改为:

if not os.path.isdir(FileDir):
     os.makedirs(FileDir)

才没有报错,这里做笔记好好提醒一下自己。

以上是我总结的所有错误,希望看到这篇帖子的你能够避开这些坑。下面附上我完整的UDP报文接收以及解析的代码:

#!/usr/local/bin/python3
# coding:utf-8

import socket
import binascii
import datetime
import os
import csv

'''
作者:Zflyee
Mailto: zflyee@126.com
'''

Server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建UDP传输数据
# Server.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)    # 快速实现端口复用

Server.bind(("", 8600))
print(u"已绑定本机端口:8600,正在监听数据...")


def DataParsing(Data):
    Time = datetime.datetime.strptime(Data[26:38], "%y%m%d%H%M%S")  # 解析时间

    Voltage = int(Data[44:46], 16) / 10.0  # 电压数据

    Temp = int(Data[60:64], 16) / 10.0  # 实时温度数据

    Temp_1h_max = int(Data[70:74], 16) / 10.0  # 温度小时最大值

    Temp_1h_min = int(Data[80:84], 16) / 10.0  # 温度小时最小值

    Hum = int(Data[90:94], 16) / 10.0  # 湿度数据

    Hum_1h_max = int(Data[100:104], 16) / 10.0  # 湿度小时最大值

    Hum_1h_min = int(Data[110:114], 16) / 10.0  # 湿度小时最小值

    Pa = int(Data[120:124], 16) / 10.0  # 气压数据

    Pa_1h_max = int(Data[130:134], 16) / 10.0  # 气压小时最大值

    Pa_1h_min = int(Data[150:154], 16) / 10.0  # 气压小时最小值

    WindSpd = int(Data[140:144], 16) / 10.0  # 瞬时风速值

    WindSpd_10min_avg = int(Data[160:164], 16) / 10.0  # 10分钟风速平均值

    WindSpd_max = int(Data[170:174], 16) / 10.0  # 当前风速最大值

    WindSpd_min = int(Data[180:184], 16) / 10.0  # 当前风速最小值

    WindDir = int(Data[190:194], 16) / 10.0  # 当前风向瞬时值

    WindDir_10min_avg = int(Data[200:204], 16) / 10.0  # 10分钟风向平均值

    Rain_1day = int(Data[210:214], 16) / 10.0  # 当日降雨量

    # 推算纬度数据长度
    Lati_len = int(Data[220:224], 16)  # 转换为10进制
    Lati = ""
    for i in range(2, Lati_len * 2, 2):
        Lati += (chr(int(Data[224 + (i - 2):224 + i], 16)))

    # 推算维度长度与数据
    Long_len = int(Data[250:252], 16)
    Long = ""
    for i in range(2, Long_len * 2, 2):
        Long += (chr(int(Data[254 + (i - 2):254 + i], 16)))

    # 组装数据
    DataList = [Time, WindSpd, WindSpd_10min_avg, WindSpd_max, WindSpd_min, WindDir, WindDir_10min_avg,
                Temp, Temp_1h_max, Temp_1h_min, Hum, Hum_1h_max, Hum_1h_min, Pa, Pa_1h_max, Pa_1h_min, Rain_1day,
                Voltage]

    # 创建文件路径
    Path = "E:\\富奥通\\data\\SY_FWS600\\"
    Text = os.path.join(Path, Time.strftime("%Y"), Time.strftime("%m")) + "\\" + Time.strftime("%d") + ".csv"
    FileDir = os.path.split(Text)[0]
    if not os.path.isdir(FileDir):
        os.makedirs(FileDir)
    # 写入数据
    with open(Text, "a+", newline="") as f:
        Writer = csv.writer(f)
        Writer.writerow(DataList)
    # 储存提示
    print(u"{}时刻数据已成功储存。".format(Time))

while True:
    try:
        Msg, ClientAddr = Server.recvfrom(1024)
        Data = binascii.b2a_hex(Msg)  # 将数据转换成16进制数据
        Data = str(Data, encoding="utf-8")
        print(u"从{0}的{1}端口得到如下数据:\n{2}".format(ClientAddr[0], ClientAddr[1], Data))
        if Data[0:2] == "7b":
            Content = "7B810010" + Data[8:30] + "7B"
            Response = binascii.a2b_hex(Content)
            Server.sendto(Response, ClientAddr)  # 回复客户端数据
            print(u"已回复客户端")
        elif Data[0:2] == '01':
            DataParsing(Data)
        else:
            print(u"%%%%%%%%%%%%%%%数据报错,重新获取%%%%%%%%%%%%%%%")
            continue
    except:
        print(u"%%%%%%%%%%%%%%%数据报错,重新获取%%%%%%%%%%%%%%%")
        continue


Server.close()

标签:10.0,UDP,socket,16,python,传输数据,int,Data
来源: https://blog.csdn.net/Geoffrey_Zflyee/article/details/97821899

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有