[Python + UDP通讯]检测指定文件最新结果并发送给feiq

一、写在前面

       本文所用例子为个人学习的小结,如有不足之处请各位多多海涵,欢迎小伙伴一起学习进步,如果想法可在评论区指出,我会尽快回复您,不胜感激!

        所公布代码或截图均为运行成功后展示。

二、本文内容

       产线新增了一台设备,测试完成后会上传数据到服务器,但由于设备自动测试时无人值守,导致测试失败时无人知晓,故利用python写个脚本监测服务器数据。

        服务器每天会产生新日志,并且日志中只关注新增的一行的测试结果,当结果为FAIL时,才发送警报给指定飞秋。为了测试路径和指定飞秋IP可配置,将值写在配置文件中。

        所以,整理需求后即得:

检查指定目录下,最新文件.DB1后缀文件的最新一行是否新增并且存在Fail,如果存在Fail则发送报警信息。

         测试完成后,打包成exe使用。

三、开发环境

1.Python 3.9

IDE:

1.Pycharm

四、代码实现

4.1 引入所需包

        引入后报红,则说明缺少对应module,可以通过pip install xx解决,如果pip install失败,可以尝试更换镜像源

  #更换为豆瓣的镜像源

 pip config set global.index-url https://pypi.douban.com/simple

import socket
import os
import glob
import sys
import time
import json

4.2 定义发送给feiq的方法

       定义发送feiqiu消息的方法,设定传入参数为ip / port / message。

       套接字的两个参数是“socket.AF_INET”表示我们使用的协议是IPV4,还有一个是“socket.SOCK_DGRAM”表示我们使用的是UDP类型的通讯技术。

       创建UDP套接字后定义基本测试参数,信息的格式,发送的格式。

# 通过feiq的socket发送到指定ip
def send_feiqiu_message(ip, port, message):
    # 创建UDP套接字
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 飞秋的消息格式通常为:版本号:包编号:发送者用户名:发送者主机名:命令字:附加信息
    # 命令字32表示发送消息
    version = 1
    packet_no = 12345
    sender = 'XXX'
    hostname = 'Hardware_server'
    command = 32
    additional_info = message

    # 组装消息内容
    feiqiu_message = f"{version}:{packet_no}:{sender}:{hostname}:{command}:{additional_info}"

    # 发送消息
    sock.sendto(feiqiu_message.encode('gbk'), (ip, port))

    # 关闭套接字
    sock.close()

4.3定义获取指定路径下最新DB1后缀的文件名的方法

        根据传入路径,获取路径下所有的DB1文件,并根据创建的时间倒排,获取最新的文件名。

        os.path.getctime为创建时间,getmtime为修改时间,根据自己需求定义

# 获取指定目录下最新创建的DB1后缀的文件
def find_latest_db1_file(directory):
    # 获取指定目录下所有.DB1文件
    files = glob.glob(os.path.join(directory, '*.DB1'))
    # 按创建时间排序
    files.sort(key=os.path.getctime, reverse=True)
    # 返回最新的文件
    if files:
        return files[0]
    else:
        return None

4.4定义读取文件最新一行是否为FAIL的方法

        根据传入文件的路径读取文件最后一行,并检查是否存在FAIL字符串,返回检查结果

# 读取文件中最新一行是否包含FAIL
def check_last_line_for_fail(filename):
    try:
        with open(filename, 'r') as file:
            # 读取文件的最后一行
            last_line = file.readlines()[-1]
            # 检查是否包含'FAIL'
            return 'FAIL' in last_line
    except Exception as e:
        print(f"Error reading file {filename}: {e}")
        return False

4.5定义统计文件行数的方法

        根据传入文件的路径,统计该文件的行数。

# 统计当前文件中有多少行
def count_lines(filename):
    """统计文件的行数"""
    try:
        with open(filename, 'r') as file:
            return sum(1 for line in file)
    except FileNotFoundError:
        print(f"文件未找到: {filename}")
        return None
    except Exception as e:
        print(f"读取文件时发生错误: {e}")
        return None

4.6定义以JSON格式从配置文件中读取字典值的方法

        从指定文件中以JSON格式读取各字段,并匹配key和value,成功则返回值。

# 从配置文件中以JSON格式读取字典值
def read_config_value(config_file, *keys):
    """从JSON配置文件中读取嵌套的键值"""
    try:
        with open(config_file, 'r') as file:
            config_data = json.load(file)
            value = config_data
            for key in keys:
                value = value[key]  # 深入字典获取值
            return value
    except FileNotFoundError:
        print(f"配置文件未找到: {config_file}")
        return None
    except KeyError:
        print(f"键 {' -> '.join(keys)} 在配置文件中未找到")
        return None
    except json.JSONDecodeError:
        print("配置文件格式错误(不是有效的JSON)")
        return None
    except Exception as e:
        print(f"读取配置文件时发生错误: {e}")
        return None

4.7执行方法

        获取和运行路径同级目录下的配置文件config.ini的文件路径。

        获取配置文件中ip字符串,当ip字符串不为空则以 ","分割,给ipaddress_list赋值。

# 获取当前项目路径同目录下的配置文件config.ini
config_file_path = os.path.dirname(os.path.realpath(sys.argv[0])) + "\config.ini"
# 读取配置文件中指定ip值
host = read_config_value(config_file_path, "ipaddress", "ip")
# 给ipaddress_list一个默认值
ipaddress_list = {"xx.x.x.xxx"}
if host is not None:
    print(f"待接收警报主机地址: {host}")
    ipaddress_list = host.split(",")

4.8 执行main方法

        注释写的比较清楚,feiq的端口默认是2425,循环60s一次。

if __name__ == '__main__':
    # 记录上一次行数统计
    last_lineCount = 0
    # 记录上一份文件名
    last_filename = ''
    # 从配置文件中读取DB1文件所在目录
    directory = read_config_value(config_file_path, "ipaddress", "DB1directory")
    while True:
        # 找到最新的.DB1文件
        latest_file = find_latest_db1_file(directory)
        # 统计当前文件的行数
        current_lineCount = count_lines(latest_file)
        # 获取当前文件的文件名
        current_filename = os.path.basename(latest_file)
        # 如果文件存在
        if latest_file:
            # 如果当前文件和上一份文件名不相同,计数归0
            if current_filename != last_filename:
                last_filename = current_filename
                print("new DB1 File created!")
                last_lineCount = 0
            # 检查是否有新增行数
            if current_lineCount > last_lineCount:
                last_lineCount = current_lineCount
                print("new line created!")
                # 检查最后一行是否包含'Fail'
                if check_last_line_for_fail(latest_file):
                    print("XX Failed!!!!")
                    # 发送警报信息给指定飞秋
                    for ip in ipaddress_list:
                        send_feiqiu_message(ip, 2425, 'XX Failed!!!!')
                else:
                    print("XX passed")
            else:
                print("No new line created!")

        else:
            # 如果文件不存在,统计行数清空
            last_lineCount = 0
            print("No .DB1 files found in the directory.")
        # 休眠指定秒数开启新循环
        time.sleep(60)

4.9配置文件格式

{
	"ipaddress": {
		"ip": "xx.xx.x.xxx,xx.xx.x.xxx",
		"DB1directory": "C:\\Users\\Administrator\\Desktop"
	}
}

4.10打包成exe

1.安装pyinstaller,清华源。

 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple  pyinstaller

2.终端切换到项目目录下

 cd F:\pythonProject1\pythonProject  

3.执行以下命令,就打包好啦

 pyinstaller --onefile .\checkFail.py

4.同级目录下会出现dist文件,将config.ini拷贝进去就可以运行了

        

五、看一看实际效果吧

打包后的脚本运行图

        

 给Feiq发送信息的图

六、完整代码

"""
脚本目标:
检查指定目录下,最新文件.DB1后缀文件的最新一行是否新增并且存在Fail,如果存在Fail则发送报警信息
"""

import socket
import os
import glob
import sys
import time
import json


# 通过feiq的socket发送到指定ip
def send_feiqiu_message(ip, port, message):
    # 创建UDP套接字
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 飞秋的消息格式通常为:版本号:包编号:发送者用户名:发送者主机名:命令字:附加信息
    # 命令字32表示发送消息
    version = 1
    packet_no = 12345
    sender = 'XX'
    hostname = 'Hardware_server'
    command = 32
    additional_info = message

    # 组装消息内容
    feiqiu_message = f"{version}:{packet_no}:{sender}:{hostname}:{command}:{additional_info}"

    # 发送消息
    sock.sendto(feiqiu_message.encode('gbk'), (ip, port))

    # 关闭套接字
    sock.close()


# 获取指定目录下最新创建的DB1后缀的文件
def find_latest_db1_file(directory):
    # 获取指定目录下所有.DB1文件
    files = glob.glob(os.path.join(directory, '*.DB1'))
    # 按创建时间排序
    files.sort(key=os.path.getctime, reverse=True)
    # 返回最新的文件
    if files:
        return files[0]
    else:
        return None


# 读取文件中最新一行是否包含FAIL
def check_last_line_for_fail(filename):
    try:
        with open(filename, 'r') as file:
            # 读取文件的最后一行
            last_line = file.readlines()[-1]
            # 检查是否包含'FAIL'
            return 'FAIL' in last_line
    except Exception as e:
        print(f"Error reading file {filename}: {e}")
        return False


# 统计当前文件中有多少行
def count_lines(filename):
    """统计文件的行数"""
    try:
        with open(filename, 'r') as file:
            return sum(1 for line in file)
    except FileNotFoundError:
        print(f"文件未找到: {filename}")
        return None
    except Exception as e:
        print(f"读取文件时发生错误: {e}")
        return None


# 从配置文件中以JSON格式读取字典值
def read_config_value(config_file, *keys):
    """从JSON配置文件中读取嵌套的键值"""
    try:
        with open(config_file, 'r') as file:
            config_data = json.load(file)
            value = config_data
            for key in keys:
                value = value[key]  # 深入字典获取值
            return value
    except FileNotFoundError:
        print(f"配置文件未找到: {config_file}")
        return None
    except KeyError:
        print(f"键 {' -> '.join(keys)} 在配置文件中未找到")
        return None
    except json.JSONDecodeError:
        print("配置文件格式错误(不是有效的JSON)")
        return None
    except Exception as e:
        print(f"读取配置文件时发生错误: {e}")
        return None


# 获取当前项目路径同目录下的配置文件config.ini
config_file_path = os.path.dirname(os.path.realpath(sys.argv[0])) + "\config.ini"
# 读取配置文件中指定ip值
host = read_config_value(config_file_path, "ipaddress", "ip")
# 给ipaddress_list一个默认值
ipaddress_list = {"XX.X.X.XXX"}
if host is not None:
    print(f"待接收警报主机地址: {host}")
    ipaddress_list = host.split(",")

if __name__ == '__main__':
    # 记录上一次行数统计
    last_lineCount = 0
    # 记录上一份文件名
    last_filename = ''
    # 从配置文件中读取DB1文件所在目录
    directory = read_config_value(config_file_path, "ipaddress", "DB1directory")
    while True:
        # 找到最新的.DB1文件
        latest_file = find_latest_db1_file(directory)
        # 统计当前文件的行数
        current_lineCount = count_lines(latest_file)
        # 获取当前文件的文件名
        current_filename = os.path.basename(latest_file)
        # 如果文件存在
        if latest_file:
            # 如果当前文件和上一份文件名不相同,计数归0
            if current_filename != last_filename:
                last_filename = current_filename
                print("new DB1 File created!")
                last_lineCount = 0
            # 检查是否有新增行数
            if current_lineCount > last_lineCount:
                last_lineCount = current_lineCount
                print("new line created!")
                # 检查最后一行是否包含'Fail'
                if check_last_line_for_fail(latest_file):
                    print("XX Failed!!!!")
                    # 发送警报信息给指定飞秋
                    for ip in ipaddress_list:
                        send_feiqiu_message(ip, 2425, 'XX Failed!!!!')
                else:
                    print("XX passed")
            else:
                print("No new line created!")

        else:
            # 如果文件不存在,统计行数清空
            last_lineCount = 0
            print("No .DB1 files found in the directory.")
        # 休眠指定秒数开启新循环
        time.sleep(60)

七、小结

       这是一个小功能,不需要人一直跑就可以被动收到提示,收到后再去处理问题,避免设备一直处于测试失败状态

        

八、感谢

        感谢各位大佬的莅临,学习之路漫漫,吾将上下而求索。有任何想法请在评论区留言哦!

        再次感谢!

        

  • 9
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值