# CAN-panel **Repository Path**: NjinN/CAN-panel ## Basic Information - **Project Name**: CAN-panel - **Description**: No description available - **Primary Language**: C# - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-09-14 - **Last Updated**: 2025-09-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # CAN信号测试台架 ## 简介 有很多设备可以读取CAN总线上的数据,或者模拟发生数据,而可编程的平台并不多,一般是借助厂商提供的API调用驱动,达成和设备进行数据交换的目的。 本软件的目的,是构建一个通用的CAN通信规范,可以兼容多种设备,同时有着统一的编程接口,可以对CAN总线进行各种数据操作,完成模拟或者测试的工作。我决定将我这部分工作开源出来,是为了所有饱受手工操作之苦的测试人员做一点事情。 成本起见,请尽可能使用便宜的设备,如果总线负载重,超过500帧每秒,请选择贵一点的设备。 ## 使用文档 ### 软件概览 ![overview](img/overview.PNG) ### 信号量编辑 一个CAN ID发出的报文(Message)可能承载多个信号(Signal),这个里面,有些是连续量,有些是离散量,还有一些有着特殊的解析(例如DM1故障码)。 同时,开关量也有着和正常逻辑不同的定义,除了开,关以外,还有错误和无效两种状态。 为了方便测试人员进行硬件测试,这里预设了大部分可能用到的类型,你也可以通过JavaScrip函数(点击“编辑用户函数”)来完成一些比较复杂的逻辑(CAN ID上点击右键-增加信号-用户自定义即可)。 ### 设备连接 点击设置菜单,即可选择你需要的设备去连接。 ### TODO 这个软件本身比较简单,熟悉1939协议的很快应该就能上手,重点介绍可编程部分而不是GUI。 ## JS API 使用文档 本节面对的是专业的开发人员,需要**熟练掌握JavaScript的基础语法**和少量C#的知识。 本节的目的是帮助专业的开发人员完成个性化的开发,开发的内容包括但不局限于信号测试,功能测试,UDS和网络管理测试。 请在窗口菜单中打开IDE。 本模块的功能是极其强大的,举个例子,我们如何通过变成完成一个简单的UDS模式跳转测试: ```javascript /* 测试用例: 默认会话模式跳转到扩展会话模式 */ function str2arr(str){ var arr = str.split(" "); var arr_new = []; for (var i = 0; i10; } $.waitMessageUDF("callbackFcn","cdataBuffer","isExit()",10000); ``` ##### `$.newTransmitThread()` **函数原型** ```csharp public static TransmitThread newTransmitThread(CANData cd, int cycleTime); ``` **说明** `$.newTransmitThread()`是函数,返回一个TransmitThread对象,该对象作用是后台发送数据。 `$.newTransmitThread(CANData,TransmitCycle)`,其中CANData是CAN数据,请使用`$.getNewCANData()`函数生成。TransmitCycle是发送周期,单位是ms。 **附TransmitThread操作** 开始发送信号:TransmitThread.start() 停止发送信号:TransmitThread.stop() **练一练** 下面这段代码以100ms的周期发送指定CAN数据,持续5秒。 ```javascript var transmitThread = $.newTransmitThread($.getNewCANData(123,[1,2,3,4,5,6,7,8]),100); transmitThread.start(); $.sleep(5000); transmitThread.stop(); ``` ##### `$.newSendThread()` **注意,该名称已经废弃,不推荐使用(但是仍然保留)** **函数原型** ```csharp public static SendThread newSendThread(CANData cd, int cycleTime); ``` **说明** `$.newSendThread()`是函数,返回一个SendThread对象,该对象作用是后台发送数据。 `$.newSendThread(CANData,SendCycle)`,其中CANData是CAN数据,请使用`$.getNewCANData()`函数生成。SendCycle是发送周期,单位是ms。 **附SendThread操作** 开始发送信号:SendThread.start() 停止发送信号:SendThread.stop() **练一练** 下面这段代码以100ms的周期发送指定CAN数据,持续5秒。 ```javascript var sendThread = $.newSendThread($.getNewCANData(123,[1,2,3,4,5,6,7,8]),100); sendThread.start(); $.sleep(5000); sendThread.stop(); ``` #### File I/O Functions 这一组是文件读写操作函数。 ##### `$.appendFile()` **函数原型** ```csharp public static void appendFile(string filename, string text); ``` **说明** `$.appendFile()`是函数,作用是向指定文件追加内容。 `$.appendFile(filename,text)`:其中filename是文件名,text是写入内容。 ##### `$.writeFile()` **函数原型** ```csharp public static void writeFile(string filename, string text); ``` **说明** `$.writeFile()`是函数,作用是向指定文件写入内容。 `$.writeFile(filename,text)`:其中filename是文件名,text是写入内容。 ##### `$.readFile()` **函数原型** ```csharp public static string readFile(string filename); ``` **说明** `$.readFile()`是函数,作用是读取指定文件。 `$.readFile(filename)`:其中filename是文件名,返回文件内容。 **练一练** ```javascript //默认的文件生成路径是程序所在路径 var fn = "TestFile.txt"; $.writeFile(fn,"Hello"); $.appendFile(fn," World!"); $.print($.readFile(fn)); ``` #### UI Control Functions 这一组函数用来控制用户界面。 ##### `$.clear()` **函数原型** ```csharp public static void clear(); ``` **说明** `$.clear()`是函数,没有输入,作用是清空IDE的输出界面。 **练一练** ```javascript $.print("Don't clear me, please T_T ...") $.sleep(1000) $.clear() ``` ##### `$.exit()` **函数原型** ```csharp public static void exit(); ``` **说明** `$.exit()`是函数,没有输入,作用是强制退出程序,慎用。 **练一练** ```javascript $.exit() //然后程序就没了…… //想恶作剧的话在UserLib.js里面加入$.exit() //谁要是给我加这么一句我保证不打死他 ``` ##### `$.openLog()` **函数原型** ```csharp public static void openLog(); ``` **说明** 用来打开日志系统窗口 **练一练** ```javascript $.openLog(); ``` ##### `$.closeLog()` **函数原型** ```csharp public static void closeLog(); ``` **说明** 用来隐藏日志系统窗口 **练一练** ```javascript $.closeLog(); ``` ##### `$.openIDE()` **函数原型** ```csharp public static void openIDE(); ``` **说明** 用来打开IDE窗口 **练一练** ```javascript $.openIDE(); ``` ##### `$.closeIDE()` **函数原型** ```csharp public static void closeIDE(); ``` **说明** 用来隐藏IDE窗口 **练一练** ```javascript $.closeLog(); ``` #### 练一练升级版 ```javascript //感受一下这个神经病的过程 for (var i = 0;i<10;i++){ $.openIDE(); $.sleep(300); $.closeIDE(); $.sleep(300); $.openLog(); $.sleep(300); $.closeLog(); $.sleep(300); } ``` #### Others 这是一些乱七八糟的函数 ##### `$.currentTicks` **说明** `currentTicks`是属性,不能以函数的形式调用,作用是获取一个整形的数据,该数据用以描述时间,单位是1/10000毫秒。 **用例** ```javascript $.print($.currentTicks)//打印出当前的Tick ``` ##### `$.currentmsTicks` **说明** `currentmsTicks`是属性,不能以函数的形式调用,作用是获取一个浮点型的数据,该数据用以描述时间,单位是毫秒。 **用例** ```javascript $.print($.currentmsTicks)//打印出当前的毫秒Tick ``` ##### `$.gbk2utf8()` **函数原型** ```csharp public static string gbk2utf8(string strIn); ``` **说明** `$.gbk2utf8()`是函数,用来对编码进行转换。 `$.gbk2utf8(strIN)`:输入以GBK编码的文本,返回以UTF-8编码的文本。 **用例** ```javascript //我也不知道举什么例子,我就先不举了吧…… ``` ##### `$.loadJSFile()` **函数原型** ```csharp public static void loadJSFile(string filename); ``` **说明** `$.loadJSFile()`是函数,用来执行一些用户定义的脚本**文件**。 `$.loadJSFile(filename)`:执行指定文件 **用例** 比如你新建了一个文件,叫`Damn.js` ```javascript var x = "Damn it"; function alert(data){ $.alert(data); } alert(data); ``` 随后你加载它: ```javascript $.loadJSFile('Damn.js'); ``` ##### `$.sleep()` **函数原型** ```csharp public static void sleep(int mills); ``` **说明** `$.sleep()`是函数,用来做延时。 `$.sleep(delayms)`:其中delayms是延迟的毫秒数。 **用例** ```javascript for(var i = 0;i<10;i++){ $.print(i); $.sleep(300); } ``` ##### `$.printParameter()` **函数原型** ```csharp public static void printParameter(string param); ``` **说明** `$.printParameter()`是函数,用来获取数据在C#中的类型和值。 `$.printParameter(parameterName)`:parameterName,是变量的名字,是字符串。 **用例** ```javascript var cdata = $.getNewCANData(123,[1,2,3,4,5,6,7,8]); $.printParameter("cdata"); ``` ### 其它细节问题 #### Q:如果多次点击Run会发生什么? A:多次点击Run会将当前编辑器中的代码加入队列中,而且因为JS引擎的缘故不可撤销,所以请谨慎使用你麒麟臂的连击功能。简而言之,点几次就执行几次。 #### Q:CANData类型的数据如何操作? A:CANData操作汇总: ##### 新建一个CANData类型的数据 参看`$.getNewCANData()`函数,下面我们假设cd是我们新增的CANData类型的变量 ##### CANData数据属性的读取 ```javascript var cdata = $.getNewCANData(123,[1,2,3,4,5,6,7,8]); $.print(cdata); cdata.ID = 12345;//修改ID cdata.setDataJS([3,4,8,9])//修改所有CAN数据 cdata.setDataJS(1,100)//修改CAN数据的某一位 $.print(cdata); ``` ## 二次开发文档 对于新的CAN收发设备,本软件既支持直接导入兼容CAN-PRO的驱动程序,如果本程序不支持,你也愿意修改源代码,你可以尝试编写自己的驱动层。 编写驱动的时候,如果您的设备在初始化的时候需要选择设备或者端口(比如双路CAN的设备),需要自行编写配置窗体,在调用初始化函数的时候以阻塞的形式弹出。这里可以参考其它驱动的做法。 由于不同的设备配置不一样,在此仅仅说明如何编写标准的驱动并且集成到App中去。 ### 编写驱动类 首先找到CANSignalControlPanel.Driver.CANDriver这个接口,您的驱动需要编写一个类继承这个接口,实现其三个抽象方法。 ### 集成到界面 随后在主界面菜单的设置->CAN设备选项中添加您的菜单项,随后在Click事件中添加如下初始化代码(仅供参考): ```csharp private void menuSomebusCAN_Click(object sender, EventArgs e) { if (DriverManager.initDevice(ExistedDeviceType.Somebus)) { lbDeviceStatus.Text = "设备状态-Somebus已连接"; logMessage("成功初始化Somebus设备!"); frmRX.Show(); } else { lbDeviceStatus.Text = "设备状态-无设备连接"; errorReport("初始化Somebus设备失败!"); } } ``` ### 修改驱动管理器 在DriverManager文件中增加初始化设备的相关代码。 首先修改枚举:`ExistedDeviceType`。 其次在工厂方法`CANDriverFactory`中增加你自己的设备。 ## 本项目的不足之处 以下的不足之处,希望有志之士能帮我改进以下。 我最早写这些代码的时候经验不足,很多东西如果让我重新来过我一定会重新设计,比如 - CANSignal的结构设计的比较“硬” - 对于驱动的支持和测试任务的支持,都应该做成插件式,这样的话比较利于扩展。 - 关于选择脚本语言,JavaScript的资源会多一点,但是如果重新来过我可能会选择C#。 - 关于脚本语言的IDE,也不是很友善,如果重新来过,我可能做成插件式开发,打包之后可以加载到程序里。 - 再来一遍我可能全部使用WPF开发,主要是我之前开发VB6的经验让我更加熟悉WinForm的开发,所以仅仅是编辑器组件使用了WPF 最后关于源代码中的变量和函数的命名问题,我需要解释一下: 你可能会看见两种命名风格,一种是`getDeviceId`这样的Java式(lowerCamelCase),一种是`GetDeviceId`这种C#推荐的方式(UpperCamelCase/PascalCase)。 这个可能会让你维护的时候觉得有点肮脏。 但是由于我在开发的时候还需要写Java,Python,Matlab以及其它很多语言。在你同时写多种语言的时候切换风格真的是一件非常折磨人的事情,所以最早期的代码全部是Java式的命名(包括我早期的Python代码),最近我已经竭尽全力重构为C#式,但是工作量太大,一时难以全部完成。 还有一部分UNIX C式的命名往往是第三方的驱动引入的,这一部分我不准备修改,原因也很简单,开发驱动的人大多是写C或者C++的,他们写的C# API自然也是C的风格。