一个通用的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 &

相关推荐
_.Switch4 分钟前
高级Python自动化运维:容器安全与网络策略的深度解析
运维·网络·python·安全·自动化·devops
2401_850410834 分钟前
文件系统和日志管理
linux·运维·服务器
JokerSZ.8 分钟前
【基于LSM的ELF文件安全模块设计】参考
运维·网络·安全
芯盾时代40 分钟前
数字身份发展趋势前瞻:身份韧性与安全
运维·安全·网络安全·密码学·信息与通信
测开小菜鸟1 小时前
使用python向钉钉群聊发送消息
java·python·钉钉
心灵彼岸-诗和远方2 小时前
DevOps业务价值流:架构设计最佳实践
运维·产品经理·devops
一只哒布刘2 小时前
NFS服务器
运维·服务器
萧鼎2 小时前
Python并发编程库:Asyncio的异步编程实战
开发语言·数据库·python·异步
学地理的小胖砸2 小时前
【一些关于Python的信息和帮助】
开发语言·python
疯一样的码农2 小时前
Python 继承、多态、封装、抽象
开发语言·python