一个通用的JSON数据源桩模块V1.0设计

0.背景

对于以嵌入式应用为代表的物联网项目,很多工作需要数据推送来调试后面的业务逻辑。如果完全依赖硬件环境调试,会非常麻烦,而且,对于大中专院校的学生,还有一些其他数据部署会推迟的项目,先行构建一个好用的测试桩是必要的。

1.需求限定

我们现在假定认为最终后续接口模块的输入是一组Json表示的结构化数据。比如这个:

sensor/monitor/realtime

{

"ver": "1.0",

"type": "monitor",

"time": "2024-01-09 13:51:03",

"sn": 22,

"speed1": 0, //电机1转速,单位:转/分

"speed2": 0, //电机2转速,单位:转/分

"speed3": 0, //电机转速,单位:转/分

}

类似这样的数据是一组。比如还有类似这样的:

sensor/monitor/state

{

"ver": "1.0",

"type": "state",

"time": "2023-08-31 18:25:34",

"net": 1 //采集设备网络状态,1-在线,0-离线

"dmiStat":1 //dmi工作状态,1-正常,0-异常。

}

我们现在的需求是,希望能够周期性模拟生成一些数据帧,然后把这些数据发送出去。

2.接口设计 - 配置文件

基本的设计理念是数据生成、表示、处理、分发,完全独立。进程独立,然后通过管道互相连接。让数据通过stdin->stdout的管道互相联通。

这些需要发送的数据帧,每一个帧,一个配置文件,放置在一个独立的cfg目录。上层应用遍历这个文件夹的json文件,就可以自行完成数据的填报,装配,发送。

fake_data.json

/*

* JSON 文件说明:

* topic: 表示传感器监测的数据

* data_template: 监测数据的字段名称和数据类型

* ver: 版本号

* type: 监测类型

* time: 数据生成时间

* sn: 系统序列号

* speed1: 电机1的转速,单位: 转/分

* speed2: 电机2的转速,单位: 转/分

* speed3: 电机3的转速,单位: 转/分

* monitor_data_engine.py <topic> <tag>

* postman.py <topic> <content>"

*/

{

"topic": "sensor/monitor/data",

"data_template": {

"ver": "1.0",

"type": "monitor",

"time": "2024-01-09 13:51:03",

"sn": 22,

"speed1": 0,

"speed2": 0,

"speed3": 0

},

"cmd_library": "python3 ./monitor_data_engine.py %s %s",

"cmd_postman": "python3 ./postman.py %s %s"

}

3.构建data_library程序 - V1.0

对于单独的数据帧可以考虑用这一版简化版的数据自动生成器,因为各帧数据之间需要加入起伏或者随机扰动。我们使用两个文件,来暂存sn和数据批次,来保证同一批次的几组数据是同步的。这个程序很像一个纯粹的查询器,这一版设计时,我打算直接把它做成能独立运行的进程。依据命令行参数,stdin = >stdout。完全使用文件,管道来处理一个流水线式的操作是可能的。

python 复制代码
import sys
from datetime import datetime
import random
import math
import os

SN_FILE = 'sn.cache'
TAG_FILE = 'tag.cache'

def RefreshCurrentSn(tag):
    if(os.path.exists(SN_FILE)):
        with open(SN_FILE, 'r') as f:
            sn = f.read()
    else:
        sn = 1
    
    # 从文件中读取数据
    if(os.path.exists(TAG_FILE)):
        with open(TAG_FILE, 'r') as f:
            data = f.read()
        if(data == tag): return sn;
    # 写入数据到文件中
    with open(TAG_FILE, 'w') as f:
        f.write(tag)
    with open(SN_FILE, 'w') as f:
        dd = int(sn)+1
        sn = str(dd)
        f.write(sn)
    return int(sn)

def GenDateTime():
    # 获取当前的日期和时间
    now = datetime.now()
    # 将日期和时间格式化成所需的字符串格式
    date_time_str = now.strftime("%Y-%m-%d %H:%M:%S")
    ret = date_time_str
    return ret;

def generate_next_point(amplitude, period, current_position):
    # 计算弧度
    angle = (2 * math.pi / period) * current_position
    # 计算下一个点的位置
    next_point = amplitude * math.sin(angle) + amplitude;
    return next_point

def LookupDataField(data_field, tag):
    sn = int(RefreshCurrentSn(tag))
    print(sn)
    if(data_field == "ver"): print("\"1.0\"");
    if(data_field == "type"): print("\"monitor\"");
    if(data_field == "time"): print("\"%s\"" %(GenDateTime()))
    if(data_field == "sn"): print("%d" %(sn))
    if(data_field == "speed1"): print("%d" %(int(generate_next_point(10,25,sn))))
    if(data_field == "speed2"): print("%d" %(int(generate_next_point(7,20,sn))))
    if(data_field == "speed3"): print("%d" %(int(generate_next_point(5,10,sn))))
    if(data_field == "hight"): print("%d" %(generate_next_point(0,20,sn)))
    if(data_field == "carPos"): print("%d" %(generate_next_point(0,20,sn)))
    if(data_field == "bigCarPos"): print("%d" %(generate_next_point(0,50,sn)))
    if(data_field == "pitch"): print("%d" %(generate_next_point(0,2,sn)))
    if(data_field == "weight"): print("%d" %(generate_next_point(0,20,sn)))
    if(data_field == "wind"): print("%d" %(random.randint(0,13)))
    return;


# 打印脚本名称
#print('脚本名称:', sys.argv[0])

# 打印传递给脚本的参数
if len(sys.argv) == 3:
    data_field = sys.argv[1]
    tag = sys.argv[2]
    LookupDataField(data_field, tag)
else:
    print('sys.argv[0] <data_field> <tag>')

4.构建顶层接口程序fake_data_engine

昨晚因为其他事务牵绊,写到这里时已经到22:30左右,最终没有实现管道型的工作模式,仍然是走了直接硬逻辑连接的老路,回头我把它再整理一下。整理完,连同git日志,发在这里:

python 复制代码
import sys
from datetime import datetime
import random
import math
import os
import json
import data_library
import uuid
import postman

def getfilecountByExt(dirOfFile, extWithDot):
    # 指定目录路径
    directory = dirOfFile

    # 初始化计数器
    csv_count = 0

    # 遍历目录下所有文件
    for filename in os.listdir(directory):
        if filename.endswith(extWithDot):  # 判断文件是否以.csv结尾
            csv_count += 1
    return csv_count;

def ReadAllCfgOfJsonPayloadIn(dirOfCfgFile):
    # 定义要遍历的路径
    path_to_json_files = dirOfCfgFile

    rawJsonCfg=[]
    # 遍历路径下的所有文件
    for root, dirs, files in os.walk(path_to_json_files):
        for file in files:
            print(file)
            if file.endswith('.json'):
                # 读取JSON文件
                with open(os.path.join(root, file)) as json_file:
                    data = json.load(json_file)
                    rawJsonCfg.append(data)
    return rawJsonCfg;
    

def ReadCfgOfJsonPayload(jsonRawObj):
    topic = jsonRawObj["topic"]
    payload = jsonRawObj["payload"]
    dataEngineCmd = jsonRawObj["data_engine_cmd"]
    postmanCmd = jsonRawObj["postman_cmd"]
    return (topic, payload, dataEngineCmd, postmanCmd);

  
def GenCompleteFakePayloadFromDirOf(dirOfCfg):
    arrayOfJsonPayloadToSent = ReadAllCfgOfJsonPayloadIn(dirOfCfg);

    tagOfThisTurn = str(uuid.uuid4())

    payloads=[]
    for payloadCfgItem in arrayOfJsonPayloadToSent:
        (topic, payload, dataEngineCmd, postmanCmd) = ReadCfgOfJsonPayload(payloadCfgItem)
        new_payload =json.loads('{}')
        for payloadField in payload:
            new_value = data_library.LookupDataField(topic, payloadField, tagOfThisTurn)
            print(new_value)
            new_payload[payloadField] = new_value;
        payload = new_payload
        payloads.append((topic, payload))
        postman.sendMqttMsg(topic, json.dumps(payload), "240318999")
       
        
        
GenCompleteFakePayloadFromDirOf("cfg")

注意倒数第二行那个postman,它是负责把组装好的消息发出去的。很清晰,对吧?

5.最终的调用

@echo off

:loop

REM 在这里输入您要执行的命令行命令

REM 例如,执行一个示例命令: echo Hello, World!

python fake_data_engine.py

REM 等待15秒

timeout /t 15 /nobreak >nul

REM 继续循环

goto loop

你可能不会相信,我用.bat完成了 这个定时循环调用。这个dos窗口必须保持打开,如果想要变换一下,需要加入服务,但是还有可能的权限问题,我在这里曾经讨论过它。linux下面的脱离终端串口的执行命令非常清晰,对吧:

nohup ./app_daemon_240302999 ./shake_240302999 > /dev/null 2>&1 &

相关推荐
葬爱家族小阿杰12 分钟前
python执行测试用例,allure报乱码且未成功生成报告
开发语言·python·测试用例
xx155802862xx14 分钟前
Python如何给视频添加音频和字幕
java·python·音视频
酷爱码15 分钟前
Python实现简单音频数据压缩与解压算法
开发语言·python
langmeng11026 分钟前
使用docker在3台服务器上搭建基于版本redis 6.x的一主两从模式
运维·redis·docker·容器·集群
XxxxHe31 分钟前
博客系统测试报告
功能测试·测试工具
jllllyuz36 分钟前
如何为服务器生成TLS证书
运维·服务器·数据库
花果山总钻风42 分钟前
SQLAlchemy 中的 func 函数使用指南
python
简朴-ocean1 小时前
如何删除linux空的文件夹
linux·运维·服务器
知识中的海王1 小时前
Python html 库用法详解
开发语言·python
面朝大海,春不暖,花不开1 小时前
使用 Python 正则表达式实现文本替换与电话号码规范化
python·mysql·正则表达式