当前位置: 首页 > article >正文

串口数据记录仪DIY,体积小,全开源

作用

产品到客户现场出现异常情况,这个时候就需要一个日志记录仪、黑匣子,可以记录产品的工作情况,当出现异常时,可以搜集到上下文的数据,从而判断问题原因。

之前从网上买过,但是出现过丢数据的情况耽误了问题分析,自此以后就一直心有怀疑不敢全信它了,后面都是挂两个来交叉对比,生怕又被坑。于是索性乘自己有空来做一个。
在这里插入图片描述

软硬件开源地址:https://gitee.com/qlexcel/serial-port-data-logger

硬件

在这里插入图片描述

在这里插入图片描述
尺寸长38mm,宽21mm
在这里插入图片描述

使用

找一张TF卡,格式化为FAT32格式。没有的话可以网上买,8G的8块钱包邮。比如: TF卡

记录仪开机的时候会读取SD中的配置文件config.txt,获取用户配置。如果没有读取成功或者参数异常,就会恢复默认配置。
恢复默认配置后,config.txt文件内容就会被覆盖为以下内容:

baud:115200,stopbit:0,parity:0,filemb:30,time:0,timeout:4,filename:log,

来解释下配置的作用:

baud:115200,  //波特率  9600  115200  921600
stopbit:0,    //停止位    =0,1停止位  =1,0.5停止位  =2,2停止位  =3,1.5停止位
parity:0,     //奇偶校验位  =0,无校验  =2,偶校验  =3,奇校验
filemb:30,    //日志文件最大大小,单位MB
time:0,       //是否添加时间戳  =1,添加  =0,不添加
timeout:4,    //超时时间  超过此时间没有收到新数据,就会关闭日志文件,此时可以安全拔出SD卡
filename:log, //日志文件名 

LED灯的作用:
绿灯闪烁表示接收到数据。
蓝灯亮起表示正在保存数据到SD卡。
蓝灯快闪表示出现故障。

软件

如下是main函数代码,功能实现都在这里

#include "bsp.h"/*
最好每次抓数据时,把SD中的log文件清空
*//******************************************* 宏定义区域 ****************************************/
#define QL_SUCCESS          0       //函数执行成功
#define QL_FAIL             1       //函数执行失败
#define QL_ERROR_SD_CARD    2       //SD卡故障
#define QL_ERROR_FILE_SYS   3       //文件系统故障#define CFG_FILE_NAME "config.txt"  //配置文件名
#define FILE_NAME     "log"         //默认的日志文件名
#define DEBUG_STA 1                 //=1,打开调试模式   =0,关闭调试模式
#define WR_BUF_LEN 14000            //写数据buf大小/******************************************* 变量定义区域 ****************************************/
FATFS fs;
FIL fsrc; 
uint8_t wrbuffer[WR_BUF_LEN];        //写数据buf 
//                      1   2       4   5       7   8      10  11  
uint8_t  Date[14]={'[','0','0','_','0','1',':','0','1',':','0','1',']',0};
uint8_t  TimeStamp;                  //是否添加时间戳  =1,添加  =0,不添加
uint8_t  Day=0;                      //天数
uint8_t  PastSecond;                 //多长时间没有接收到数据,单位秒
uint8_t  TimeOut=4;                  //超时时间
uint8_t  FileOpenSta=0;              //日志文件打开状态
uint32_t BaudRate=115200;            //波特率
uint8_t  StopBits=0;                 //停止位    =0,1停止位  =1,0.5停止位  =2,2停止位  =3,1.5停止位
uint8_t  Parity=0;                   //奇偶校验位  =0,无校验  =2,偶校验  =3,奇校验
uint16_t Head,Tail,len;
uint32_t TotalLen=0,FileMaxLen;
uint32_t len_bk,DateWr,DateWrOld;
char  FileName[11];                  //日志文件名字
uint8_t FileNameIndex=0;             //日志文件名字编号
uint32_t LedG_Blink_Cnt;             //接收到数据绿灯闪烁计数
uint8_t  LedG_Blink_Flg;             //接收到数据绿灯闪烁标志/******************************************* 函数声明区域 ****************************************/
uint8_t Filesystem_Handle(void);
uint8_t GetstrstrValue(uint8_t* str, char* substr, uint8_t* out, uint16_t MaxLen);
uint32_t MyStr2Int(uint8_t* str, uint8_t dot);int main(void)   //监视下文件系统每次写扇区个数
{  	uint8_t res,tmp;mGPIO_Init();	RTC_Init();res=Filesystem_Handle();UART_Init();printf("/** Uart Data Saver V1.0**/\r\n");if(res==QL_ERROR_SD_CARD)printf("SD Card Error!\r\n");else if(res==QL_ERROR_FILE_SYS)printf("File System Error!\r\n");else if(res)printf("Other Error!\r\n");elseprintf("Init ok!\r\n");while(res)          //初始化不成功,就死循环闪灯提示{		gd_eval_led_toggle(LED_BLUE);delay_1ms(200);}		LED_G_OFF();LED_B_OFF();while(1){		Head=RX_BUF_LEN-DMA_CHCNT(X1_UART_DMA, X1_UART_DMA_CH_RX);  //DMA已经接收到的数据个数 		if(Head!=Tail)  //如果接收到数据{wrbuffer[len++]=rxbuffer[Tail++];if(Tail==RX_BUF_LEN) Tail=0;if(TimeStamp && DateWrOld!=DateWr)                      //时间戳打开 且 写入的时间发生了改变{if(wrbuffer[len-1]=='\n' || wrbuffer[len-1]=='\r')  //遇到换行,写入一次时间{tmp=0;while(tmp<13){wrbuffer[len++]=Date[tmp++];DateWrOld=DateWr;}
#if DEBUG_STA					printf("%s\r\n",Date);
#endif					}}PastSecond=0;LedG_Blink_Flg=1;   //接收到数据,LED闪烁提示}if(len>(WR_BUF_LEN-5))  //当接收数据比较多时,就写入一次SD卡。数据写完后,日志文件不关闭{LED_B_ON();if(FileOpenSta==0)  //如果日志文件没有打开{res = f_open(&fsrc,FileName,FA_WRITE);f_lseek(&fsrc, f_size(&fsrc));FileOpenSta=1;}res = f_write(&fsrc,wrbuffer,len,&len_bk);if(res) break;printf("wr%d\r\n",len);	TotalLen+=len;if(TotalLen>FileMaxLen)   //如果数据大小超过了文件最大{tmp=strlen(FileName);  //文件编号+1FileNameIndex++;FileName[tmp-7]=FileNameIndex/100+'0';FileName[tmp-6]=FileNameIndex%100/10+'0';FileName[tmp-5]=FileNameIndex%10+'0';f_close(&fsrc);res = f_open(&fsrc,FileName,FA_WRITE|FA_OPEN_ALWAYS);  //打开新文件f_lseek(&fsrc, f_size(&fsrc));TotalLen=0;}len=0;	LED_B_OFF();						}else if(PastSecond>TimeOut && (len>0 || FileOpenSta==1))  //没有新数据一段时间后,如果还有数据没写入,就立即写入{                                                          //或者日志文件处于打开状态,就关闭LED_B_ON();if(len>0)  //有数据没写入,就立即写入{if(FileOpenSta==0)  //没有打开文件{	res = f_open(&fsrc,FileName,FA_WRITE);f_lseek(&fsrc, f_size(&fsrc));FileOpenSta=1;				}				res = f_write(&fsrc,wrbuffer,len,&len_bk);if(res) break;}if(FileOpenSta==1){	f_close(&fsrc);FileOpenSta=0;}			printf("wr%d\r\n",len);	TotalLen+=len;len=0;	LED_B_OFF();			}if(LedG_Blink_Flg)  //接收到数据,LED闪烁提示{LedG_Blink_Cnt++;if(LedG_Blink_Cnt>30000){LedG_Blink_Cnt=0;if(gpio_input_bit_get(LED_G_PORT, LED_G_PIN))  //如果IO是高,LED灭的状态{LED_G_ON();}else  //如果IO是低,LED亮的状态{LED_G_OFF();LedG_Blink_Flg=0;}}}if(RTC_CTL & RTC_FLAG_SECOND)          //秒中断标志{RTC_CTL &= ~RTC_FLAG_SECOND;       //清除中断标志DateWr=(RTC_CNTH << 16)|RTC_CNTL;  //获取当前时间if (DateWr == 86399)               //当时间到达23:59:59时清零,天数加1{RTC_CTL |= RTC_CTL_CMF;RTC_CNTH=0;RTC_CNTL=0;RTC_CTL &= ~RTC_CTL_CMF;Day++;rtc_lwoff_wait();}	PastSecond++;Date[1]=Day%100/10+'0';Date[2]=Day%10+'0';			tmp=DateWr/3600;     //hoursDate[4]=tmp/10+'0';Date[5]=tmp%10+'0';tmp=DateWr%3600/60;  //minutesDate[7]=tmp/10+'0';Date[8]=tmp%10+'0';			tmp=DateWr%60;       //seconds	Date[10]=tmp/10+'0';Date[11]=tmp%10+'0';	
//			printf("%s\r\n",Date);}			}while(1)  //当文件系统有问题,就会跳出上面循环到达这里,闪灯提示{		gd_eval_led_toggle(LED_BLUE);delay_1ms(50);}	
}#define CONFIG_DEFAULT_LEN 71
static const char CONFIG_DEFAULT[CONFIG_DEFAULT_LEN]="baud:115200,stopbit:0,parity:0,filemb:30,time:0,timeout:4,filename:log,";
uint8_t Filesystem_Handle(void)
{uint8_t res,i;res=SD_Init();if(res) return QL_ERROR_SD_CARD;          //SD卡有问题res=f_mount(&fs,"0:",1);if(res!=FR_OK) return QL_ERROR_FILE_SYS;  //文件系统有问题res = f_open(&fsrc,CFG_FILE_NAME,FA_READ); //以只读方式打开配置文件if(res==FR_OK)                             //打开成功,说明文件存在{res = f_read(&fsrc,rxbuffer,200,&len_bk);f_close(&fsrc);if(res==FR_OK && len_bk>50)            //数据读取成功且大小正常{res |= GetstrstrValue(rxbuffer,"baud:",wrbuffer,10);      //波特率BaudRate=MyStr2Int(wrbuffer,0);res |= GetstrstrValue(rxbuffer,"stopbit:",wrbuffer,10);   //停止位StopBits=MyStr2Int(wrbuffer,0);res |= GetstrstrValue(rxbuffer,"parity:",wrbuffer,10);    //校验位Parity=MyStr2Int(wrbuffer,0);res |= GetstrstrValue(rxbuffer,"filemb:",wrbuffer,10);    //文件大小 FileMaxLen=MyStr2Int(wrbuffer,0);res |= GetstrstrValue(rxbuffer,"time:",wrbuffer,10);      //时间戳   TimeStamp=MyStr2Int(wrbuffer,0);res |= GetstrstrValue(rxbuffer,"timeout:",wrbuffer,10);   //超时事件TimeOut=MyStr2Int(wrbuffer,0);res |= GetstrstrValue(rxbuffer,"filename:",wrbuffer,10);  //日志文件名strcpy(FileName,(char*)wrbuffer);}elseres=1;}if(res!=FR_OK)  //如果配置文件读取失败,使用默认配置参数{BaudRate=115200;StopBits=0;Parity=0;FileMaxLen=30;TimeStamp=0;TimeOut=4;strcpy(FileName,FILE_NAME);res = f_open(&fsrc,CFG_FILE_NAME,FA_WRITE|FA_CREATE_ALWAYS);      //把默认参数写入SD卡res |= f_write(&fsrc,CONFIG_DEFAULT,CONFIG_DEFAULT_LEN,&len_bk);		f_close(&fsrc);if(res!=FR_OK) return QL_FAIL;	for(i=0;i<100;i++)  //配置参数被恢复默认了,要闪灯提示,避免用户不知道导致数据丢{		gd_eval_led_toggle(LED_BLUE);delay_1ms(50);gd_eval_led_toggle(LED_GREEN);}			}FileMaxLen<<=20;  //总大小单位由MB变成字节i=strlen(FileName);FileName[i]  =FileNameIndex/100+'0';     //日志文件名编号,第1个文件名是log000.txt,当超过文件大小,保存到第2个文件log001.txtFileName[i+1]=FileNameIndex%100/10+'0';FileName[i+2]=FileNameIndex%10+'0';FileName[i+3]='.';FileName[i+4]='t';FileName[i+5]='x';FileName[i+6]='t';FileName[i+7]=0;res = f_open(&fsrc,FileName,FA_OPEN_ALWAYS);  //如果日志文件存在,则打开该文件。 如果没有,将创建一个新文件。f_close(&fsrc); 	if(res!=FR_OK) return QL_FAIL;elsereturn QL_SUCCESS;
}/*********************************************************************************************************
* 功能说明: 在字符串str中匹配子字符串,并获取子字符串后面的字符串
*********************************************************************************************************/
uint8_t GetstrstrValue(uint8_t* str, char* substr, uint8_t* out, uint16_t MaxLen)
{uint16_t len = 0;char *p1,*p2;while(*str != 0){p1 = (char*)str;p2 = substr;while(*p1 && *p2 && *p1 == *p2){p1++;p2++;}if (*p2 == 0){p2 = (char*)out;while (*p1 != ','){*p2 = *p1;p1++;p2++;len++;if (len >= MaxLen)return QL_FAIL;}*p2 = 0;return QL_SUCCESS;}str++;}return QL_FAIL;
}/*********************************************************************************************************
* 功能说明: 字符串转整型数
* 形    参: str:字符串
*           dot:小数点  =0表示不放大  =2表示放大100倍
* 返 回 值: 整型数
*********************************************************************************************************/
uint32_t MyStr2Int(uint8_t* str, uint8_t dot)
{int out = 0;while (*str != 0){out *= 10;out += *str - '0';str++;if (*str == '.'){if (dot == 2){str++;out *= 10;if (*str >= '0' && *str <= '9')out += *str - '0';str++;out *= 10;if (*str >= '0' && *str <= '9')out += *str - '0';return out;}elsereturn out;}}if (dot == 2)out *= 100;return out;
}

可以看到main函数中,先初始化IO、RTC后,就执行Filesystem_Handle函数。

	mGPIO_Init();	RTC_Init();res=Filesystem_Handle();UART_Init();printf("/** Uart Data Saver V1.0**/\r\n");if(res==QL_ERROR_SD_CARD)printf("SD Card Error!\r\n");else if(res==QL_ERROR_FILE_SYS)printf("File System Error!\r\n");else if(res)printf("Other Error!\r\n");elseprintf("Init ok!\r\n");while(res)          //初始化不成功,就死循环闪灯提示{		gd_eval_led_toggle(LED_BLUE);delay_1ms(200);}		

在Filesystem_Handle函数中初始化SD卡,然后读取配置文件config.txt中的内容来获取用户配置,如果没有读取成功就使用默认配置。
一切正常就进入while循环。在while循环中不停读取串口buf中的数据到wrbuffer中,当wrbuffer大小够大时就写入SD卡。
在这里插入图片描述

测试

方法1:不同波特率发送1M大小txt文件给串口数据记录仪,对比原始文件和日志文件内容。

方法2:单片机发送随机数据给两个串口数据记录仪,长时间测试后对比两个日志文件内容。

相关文章:

串口数据记录仪DIY,体积小,全开源

作用 产品到客户现场出现异常情况&#xff0c;这个时候就需要一个日志记录仪、黑匣子&#xff0c;可以记录产品的工作情况&#xff0c;当出现异常时&#xff0c;可以搜集到上下文的数据&#xff0c;从而判断问题原因。 之前从网上买过&#xff0c;但是出现过丢数据的情况耽误…...

无障碍阅读(Web Accessibility)NVDA打开朗读查看器后,enter键不生效的原因

用NVDA测试Web Accessibility时&#xff0c;打开朗读查看器&#xff0c;enter键会无效&#xff0c;而不打开测试器&#xff0c;就没有问题&#xff0c;很大原因是被应用的元素不是可聚焦的&#xff0c;解决方法尝试&#xff1a; 将标签改为可聚焦的语义化标签&#xff0c;如 b…...

基于docker+python+paddleocr构建自己本地化ocr服务

1、使用FastAPI创建服务实例 1.1、正常程序 from fastapi import FastAPI, UploadFile, File, HTTPException from typing import List from paddleocr import PaddleOCR import numpy as np from PIL import Image import io import loggingapp FastAPI(title"游戏截图…...

【视频】V4L2、ffmpeg、OpenCV中对YUV的定义

1、常见的YUV格式 1.1 YUV420 每像素16位 IMC1:YYYYYYYY VV-- UU– IMC3:YYYYYYYY UU-- VV– 每像素12位 I420: YYYYYYYY UU VV =>YUV420P YV12: YYYYYYYY VV UU =>YUV420P NV12: YYYYYYYY UV UV =>YUV420SP(最受欢迎格式) NV21: YYYYYYYY VU VU =>YUV420SP…...

歌词相关实现

歌词相关 歌词数据模型&#xff1a; // Lyric.swift class Lyric: BaseModel {/// 是否是精确到字的歌词var isAccurate:Bool false/// 所有的歌词var datum:Array<LyricLine>! }// LyricLine.swift class LyricLine: BaseModel {/// 整行歌词var data:String!/// 开始…...

51单片机Proteus仿真速成教程——P1-软件与配置+Proteus绘制51单片机最小系统+新建程序模版

前言&#xff1a;本文主要围绕 51 单片机最小系统的绘制及程序模板创建展开。首先介绍了使用 Proteus 绘制 51 单片机最小系统的详细步骤&#xff0c;包括软件安装获取途径、工程创建、器件添加&#xff08;如单片机 AT89C51、晶振、电容、电阻、按键等&#xff09;、外围电路&…...

使用 pytesseract 进行 OCR 识别:以固定区域经纬度提取为例

引言 在智能交通、地图定位等应用场景中&#xff0c;经常会遇到需要从图像中提取经纬度信息的需求。本篇文章将介绍如何利用 Python 的 pytesseract 库结合 PIL 对图像进行预处理&#xff0c;通过固定区域裁剪&#xff0c;来有效地识别出图像上显示的经纬度信息。 1. OCR 与 …...

【18】单片机编程核心技巧:变量赋值与高位填充机制

【18】单片机编程核心技巧&#xff1a;变量赋值与高位填充机制 七律 变量赋值探秘 单字赋多字疑云开&#xff0c;高位零填自天来。 清零保守虽稳妥&#xff0c;强制转换更悠哉。 实验验证真章显&#xff0c;编译器间无异态。 嵌入式海行舟稳&#xff0c;类型分明避坑台。 注释…...

网络安全系统集成

随着信息技术的迅猛发展&#xff0c;网络安全问题变得越来越突出。为了应对这一挑战&#xff0c;软考网络安全系统集成应运而生&#xff0c;成为众多企业和机构的重要需求。软考网络安全系统集成旨在培养具备网络安全系统设计、实施和维护能力的专业人才&#xff0c;以满足国家…...

【51单片机】程序实验15.DS18B20温度传感器

主要参考学习资料&#xff1a;B站【普中官方】51单片机手把手教学视频 开发资料下载链接&#xff1a;http://www.prechin.cn/gongsixinwen/208.html 单片机套装&#xff1a;普中STC51单片机开发板A4标准版套餐7 目录 DS18B20介绍主要特性内部结构控制时序初始化时序写时序读时序…...

Vue项目上传到GitHub,vscode拉取vue项目更新后推送到GitHub上

1、新建Vue项目 2、在GitHub新建仓库 3、留意建立好仓库后提示的命令 4、进入vue项目目录&#xff0c;在空白处点击鼠标右键选择git bash here 5、输入命令 git init git add . git commit -m "注释内容" 输入之前创建GitHub仓库后记下的代码的第一句 git remote…...

数字孪生技术在工业制造中的应用探索

一、数字孪生&#xff1a;工业4.0的虚实纽带 1.1 技术定义与发展脉络 数字孪生&#xff08;Digital Twin&#xff09;通过实时数据映射&#xff0c;在虚拟空间构建物理实体的动态镜像。其演进历程&#xff1a; 概念萌芽&#xff08;2002年&#xff09;&#xff1a;NASA首次提…...

# linux有哪些桌面环境?有哪些显示服务器协议及显示服务器?有哪些用于开发图形用户界面的工具包?

linux有哪些桌面环境&#xff1f;有哪些显示服务器协议及显示服务器&#xff1f;有哪些用于开发图形用户界面的工具包&#xff1f; 文章目录 linux有哪些桌面环境&#xff1f;有哪些显示服务器协议及显示服务器&#xff1f;有哪些用于开发图形用户界面的工具包&#xff1f;1 显…...

【心理课堂】学习软件的道路上若感到了困难和迷茫怎么办

在科技飞速发展的今天&#xff0c;软件领域以其广阔的发展前景和丰厚的薪资待遇吸引着众多人投身其中。然而&#xff0c;学习软件并非一帆风顺&#xff0c;在这个过程中&#xff0c;我们难免会遇到困难和迷茫。那么&#xff0c;当我们在学习软件的道路上感到力不从心时&#xf…...

【Docker项目实战】使用Docker与Caddy部署BanBan任务管理工具

【Docker项目实战】使用Docker部署BanBan任务管理工具 一、BanBan介绍1.1 BanBan简介1.2 主要特点1.3 使用场景二、本次实践规划2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3 检查docker compose 版本四、下载BanBan镜像五、…...

InternVL:论文阅读 -- 多模态大模型(视觉语言模型)

更多内容&#xff1a;XiaoJ的知识星球 文章目录 InternVL: 扩展视觉基础模型与通用视觉语言任务对齐1.概述2.InternVL整体架构1&#xff09;大型视觉编码器&#xff1a;InternViT-6B2&#xff09;语言中间件&#xff1a;QLLaMA。3&#xff09;训练策略&#xff08;1&#xff09…...

【BUG】类文件具有错误的版本 61.0, 应为 52.0,请删除该文件或确保该文件位于正确的类路径子目录中。

报错&#xff1a; [ERROR] 类文件具有错误的版本 61.0, 应为 52.0 [ERROR] 请删除该文件或确保该文件位于正确的类路径子目录中。 报错截图&#xff1a; 原因&#xff1a;Java 版本和 Spring 不兼容&#xff0c;显示 Spring 版本过高 解决方法 1. 使用更高版本的 J…...

康谋应用 | 基于多传感器融合的海洋数据采集系统

在海洋监测领域&#xff0c;基于无人艇能够实现高效、实时、自动化的海洋数据采集&#xff0c;从而为海洋环境保护、资源开发等提供有力支持。其中&#xff0c;无人艇的控制算法训练往往需要大量高质量的数据支持。然而&#xff0c;海洋数据采集也面临数据噪声和误差、数据融合…...

双周报Vol.67: 模式匹配支持守卫、LLVM 后端发布、支持 Attribute 语法...多项核心技术更新!

2025-03-10 语言更新 模式匹配支持守卫&#xff08;Pattern Guard&#xff09; 模式守卫可以通过在模式后追加 if ... 的语法结构来指定。有模式守卫的分支只有在被模式匹配的值满足对应模式&#xff0c;并且模式守卫为真的情况下才会执行。如果模式守卫为假&#xff0c;则会…...

深入探索 Java Stream

目录 引言一、Java Stream 基础二、Java Stream 常用操作的语法结构及示例三、Java Stream 的应用场景四、总结 引言 在 Java 编程领域&#xff0c;随着数据量的不断增长以及对高效数据处理需求的日益迫切&#xff0c;Java 8 引入的 Stream API 成为了开发者们的得力助手。Str…...

搜广推校招面经四十六

Minimax llm&广告推荐算法 一、反向梯度下降的数学推导&#xff08;以逻辑回归为例&#xff09; 1.1. 模型定义 假设模型为逻辑回归&#xff0c;输入特征为 x ∈ R d \mathbf{x} \in \mathbb{R}^d x∈Rd&#xff0c;权重参数为 w ∈ R d \mathbf{w} \in \mathbb{R}^d …...

【Java 和 Scala】-- Java 与 Scala 的 Assert 断言对比

目录 Java 与 Scala 的 Assert 断言对比 1. 什么是 Assert&#xff08;断言&#xff09;&#xff1f; 2. 断言的使用场景 3. Java 断言示例 3.1 Java 断言的基本用法 3.2 Java 启用断言 4. Scala 断言示例 4.1 Scala 断言的基本用法 4.2 Scala 断言默认行为 5. Java 与…...

嵌入式软件测试的东方智慧:WinAMS工具的技术哲学与实践启示——一名汽车电子工程师的七年工具演进观察

引言&#xff1a;在丰田精益生产线上诞生的测试哲学 2017年参与某日系车企的ECU&#xff08;电子控制单元&#xff09;联合开发时&#xff0c;我第一次在名古屋工厂见到产线旁部署的WinAMS测试站。不同于欧美工具强调的“全流程覆盖”&#xff0c;这个诞生于日本制造业精益文化…...

MCP-代码解读TypeScript版本

MCP-代码解读TypeScript版本 文章目录 MCP-代码解读TypeScript版本1-参考网址2-TypeScript代码3-代码解读1-[非重点]定义函数2-[非重点]定义工具说明3-[重点]运行MCP服务 1-参考网址 B站视频参考 2-TypeScript代码 import { McpServer } from "modelcontextprotocol/sd…...

写了一个二叉树构造函数和画图函数,方便debug

代码 class TreeNode(object):def __init__(self, val, leftNone, rightNone):self.val valself.left leftself.right rightdef construct_tree(nodes):if not nodes:return Noneroot TreeNode(nodes[0])queue [root]index 1while index < len(nodes):node queue.p…...

docker 小记

一、卸载 查看当前版本 docker -v2. 如果有&#xff0c;先停止docker systemctl stop docker如果是yum安装&#xff0c;卸载方式为 #已防版本冲突&#xff0c;直接卸载 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-lat…...

G-Star 公益行起航,挥动开源技术点亮公益!

公益组织&#xff0c;一直是社会温暖的传递者&#xff0c;但在数字化浪潮中&#xff0c;也面临着诸多比大众想象中复杂的挑战&#xff1a;项目管理如何更高效&#xff1f;志愿者管理又该如何创新&#xff1f;宣传推广怎么才能更有影响力&#xff1f;内部管理和技术支持又该如何…...

CMD批处理一些冷门命令,编写windows脚本常用?

场景1&#xff1a; 考虑一种情况&#xff0c;需要使用变量对变量循环替换这个时候&#xff0c;如果不加以一些特殊的设置&#xff0c;很有可能出现与预设的结果不相符的情况&#xff0c;这个时候可以通过设置这样一个命令来避免这个问题。 解决方式&#xff1a; setlocal ena…...

医疗AI测试实战:如何确保人工智能安全赋能医疗行业?

一、医疗AI测试的重要性 人工智能&#xff08;AI&#xff09;正广泛应用于医疗行业&#xff0c;如疾病诊断、医学影像分析、药物研发、手术机器人和智能健康管理等领域。医疗AI技术的应用不仅提高了诊断效率&#xff0c;还能降低误诊率&#xff0c;改善患者治疗效果。然而&…...

k9s入门及实战

概述 k9s&#xff0c;GitHub&#xff0c;是用于管理k8s集群的CLI&#xff0c;提供一个终端UI来与k8s集群进行交互。通过封装kubectl功能&#xff0c;k9s会以特定时间间隔监控k8s的变化&#xff0c;默认为2秒&#xff0c;并提供后续命令来与k8s资源进行交互&#xff0c;k9s可让…...