├── .gitignore ├── LICENSE ├── README.md ├── dbc4cpp ├── builder.cpp ├── builder.h ├── context.cpp ├── context.h ├── dbc4cpp.cpp ├── dbc4cpp.h ├── dbc4cpp.pro ├── dbc4cpp.pro.user ├── dbc4cpp_config.h ├── dbc4cpp_global.h ├── entities.cpp ├── entities.h ├── parser.cpp └── parser.h ├── dbc4cpp_test ├── dbc4cpp_test.pro ├── dbc4cpp_test.pro.user └── main.cpp ├── dist └── v1.0.zip ├── docs ├── TEST.dbc ├── TEST.ini ├── black-theme-scroll.png ├── black-theme-update.png ├── usage-step1.png ├── usage-step21.png ├── usage-step22.png ├── usage-step31.png ├── usage-step32.png ├── usage-step4.png ├── usage-step5.png ├── usage-step61.png ├── usage-step62.png ├── usage-step63.png ├── white-theme-scroll.png └── white-theme-update.png └── ecanspy3 ├── aboutecanspy3.cpp ├── aboutecanspy3.h ├── aboutecanspy3.ui ├── base ├── mainwindowbase.cpp ├── mainwindowbase.h ├── titletableviewwidget.cpp ├── titletableviewwidget.h ├── titletreeviewwidget.cpp └── titletreeviewwidget.h ├── can ├── Core - 副本.cpp ├── Core - 副本.h ├── Core.cpp ├── Core.h ├── ECanVci.H ├── ECanVci.dll ├── ECanVci.lib ├── UsbCanUtil.cpp └── UsbCanUtil.h ├── database ├── databasewidget.cpp ├── databasewidget.h ├── dbctablemodel.cpp └── dbctablemodel.h ├── diag ├── diagwidget.cpp └── diagwidget.h ├── ecanspy3.ico ├── ecanspy3.pro ├── ecanspy3.pro.user ├── ecanspy3.qrc ├── ecanspy3.rc ├── images ├── ECanSpy3.png ├── about.png ├── copy.png ├── cut.png ├── exit.png ├── new.png ├── next.png ├── open.png ├── paste.png ├── previous.png ├── redo.png ├── reset.png ├── save.png ├── saveAs.png ├── settings.png ├── start.png ├── stop.png ├── theme.png └── undo.png ├── log ├── logwidget.cpp └── logwidget.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── receive ├── filtertablemodel.cpp ├── filtertablemodel.h ├── filterwidget.cpp ├── filterwidget.h ├── receivemessagefilterproxymodel.cpp ├── receivemessagefilterproxymodel.h ├── receivemessagetreemodel.cpp ├── receivemessagetreemodel.h ├── receivemessagewidget.cpp ├── receivemessagewidget.h ├── receivewidget.cpp └── receivewidget.h ├── send ├── checkedbuttondelegate.cpp ├── checkedbuttondelegate.h ├── sendmessagewidget.cpp ├── sendmessagewidget.h ├── sendwidget.cpp ├── sendwidget.h ├── signaltablemodel.cpp ├── signaltablemodel.h ├── signaltableviewphysicalvaluedelegate.cpp ├── signaltableviewphysicalvaluedelegate.h ├── signalwidget.cpp ├── signalwidget.h ├── txmessagetablemodel.cpp └── txmessagetablemodel.h ├── settings ├── canconfigdialog.cpp ├── canconfigdialog.h ├── canconfigdialog.ui ├── projectfile.cpp ├── projectfile.h ├── themesettingdialog.cpp ├── themesettingdialog.h └── themesettingdialog.ui ├── statistic ├── statisticswidget.cpp └── statisticswidget.h ├── status ├── statuswidget.cpp └── statuswidget.h ├── themes ├── black_snapshot.png ├── dracula.qss └── white_snapshot.PNG └── utils ├── mmtimer.cpp ├── mmtimer.h ├── scrollingfile.cpp ├── scrollingfile.h ├── signalaccessor.cpp ├── signalaccessor.h ├── signalaccesstest.cpp └── signalaccesstest.h /.gitignore: -------------------------------------------------------------------------------- 1 | build-* 2 | ecanspy3_2019-10-9 3 | qss-dracula -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ecanspy3 2 | 3 | ## 基本功能 4 | 5 | ecanspy3是一个类似于VehicleSpy3的一款应用软件,主要功能是进行CAN总线测试。 6 | 7 | ## 功能列表 8 | 9 | 当前版本的ecanspy3支持以下功能: 10 | 11 | + 支持广成科技的USB CAN盒作为CAN输入输出设备 12 | + 支持加载dbc,并且自动解码CAN总线上的信息 13 | + 支持发送多个报文,并且提供比较友好的界面来改变要发送报文中的信号值 14 | + 支持周期性发送和触发性发送 15 | + 支持报文接收报文的软件滤波 16 | + 支持接收报文有滚动和更新模式,在滚动模式下,可以看多所有的数据,在更新模式下可以看到最新数据 17 | + 支持接收报文的树状显示,顶层级别显示报文信息,叶子级别显示解码后的信号信息 18 | + 支持数据保存功能,注意数据保存最多只能保存最新的30万条信息 19 | + 支持实时保存功能 20 | + 支持主题切换,当前支持两套主题,一套是默认主题,一套是黑色炫酷主题 21 | + 支持项目相关操作(新建项目,保存项目,另存为项目) 22 | 23 | ## 性能优势 24 | 25 | + 报文发送周期误差可以控制在10ms之内 26 | + 总线上即使很高,CPU使用率也不会很高(更新模式:实测在4%左右, 滚动模式下:实测不超过10%),内存占用也是确定的, 27 | + 可以显示的最新报文数目为35万条 28 | + 接收显示的数据量即使非常大(35万条),可以做到滑动自如,不卡屏 29 | 30 | ## 软件快照 31 | 32 | 1. 白色主题 33 | 34 | 滚动模式 35 | 36 | ![white-theme-scrolll](./docs/white-theme-scroll.png "白色主题滚动模式运行效果") 37 | 38 | 更新模式 39 | 40 | ![white-theme-update](./docs/white-theme-update.png "白色主题更新模式运行效果") 41 | 42 | 2. 黑色主题 43 | 44 | 滚动模式 45 | 46 | ![white-theme-scrolll](./docs/black-theme-scroll.png "黑色主题滚动模式运行效果") 47 | 48 | 更新模式 49 | 50 | ![white-theme-update](./docs/black-theme-update.png "黑色主题更新模式运行效果") 51 | 52 | ## 下载 53 | 54 | [download](./dist/v1.0.zip "V1.0版本下载链接") 55 | 56 | 57 | ## 使用指导 58 | 59 | ### 前置步骤 60 | 61 | 1. 因为本程序支持的CAN设备是广成科技的CAN设备,所以你需要有个广诚科技的USB CAN盒 62 | 63 | 2. 安装号USB CAN盒驱动,并且和电脑连接起来 64 | 65 | ### 软件使用步骤 66 | 67 | 1. 点击工具按钮关闭CAN设备,只有在CAN设备关闭的情况下,才可以进行后续操作(解释下为什么使CAN设备默认开启,因为此程序带有项目保存功能,当下次启动的适合 68 | ,会自动加载上次的项目, 所以就不需要再次进行配置了,直接使用就行了),如下图所示: 69 | 70 | ![usage-step1](./docs/usage-step1.png "关闭CAN设备") 71 | 72 | 2. 加载DBC文件(这个是信号编码和解码的前提,并且发送操作也是依赖这一步骤), 入下图所示: 73 | 74 | 首先,点击加载DBC按钮 75 | 76 | ![usage-step21](./docs/usage-step21.png "点击加载dbc按钮") 77 | 78 | 然后,选择要加载的DBC文件, 点击打开 79 | 80 | ![usage-step22](./docs/usage-step22.png "选择DBC文件,点击打开") 81 | 82 | 3. 选中要发送的报文,然后右键弹出菜单,选择copy to transmit,入下图所示: 83 | 84 | ![usage-step31](./docs/usage-step31.png "添加发送报文") 85 | 86 | 当执行第三步后,会在整个窗口的发送面板中加载所选择的发送报文信息,并且发送面板中的信号窗体中会自动显示当前报文的所有信号 87 | 88 | ![usage-step32](./docs/usage-step32.png "显示所有要发送的报文") 89 | 90 | 4. 启动CAN设备, 如下图所示: 91 | 92 | ![usage-step4](./docs/usage-step4.png "启动CAN设备") 93 | 94 | 5. 如果想发送哪个报文,值需要将对应报文的CheckBox选中,即可 95 | ![usage-step5](./docs/usage-step5.png "发送报文") 96 | 97 | 6. 如果想改变哪个信号的值,只需要点击发送面板中,信号窗口中的对应信号的相关单元格(附加说明:+,-按钮,物理值单元个和原始值单元值,都可以改变信号的值,并且对于有物理含义 98 | 的信号,可以直接在物理信号单元格中输入你想输入的物理值,内部有算法找到一个最接近的值) 99 | 100 | 点击+/-按钮修改信号值 101 | 102 | ![usage-step61](./docs/usage-step61.png "点击+/-按钮修改信号值") 103 | 104 | 点击物理值单元格来信号值 105 | 106 | ![usage-step62](./docs/usage-step62.png "直接输入物理值来改变信号值") 107 | 108 | ![usage-step63](./docs/usage-step63.png "直接选中物理值状态来改变信号值") 109 | 110 | ## 项目结构介绍 111 | 112 | ### 1. dbc4cpp文件夹 113 | 114 | 此文件夹是解析dbc库的项目文件夹,此项目最新产生dbc4cpp.a dbc4cpp.dll文件 115 | 116 | 此库内部因为实现考虑使用者就是ecanspy3 QT程序,所以内部容器就是使用QT库内的容器库,所以此库目前仅支持QT程序,不能用于普通的c++程序中。 117 | 118 | ### 2. dbc4cpp_test文件夹 119 | 120 | 此文件夹使dbc4cpp库的一个使用demo项目,所以如何使用dbc4cpp.dll可以参考此文件夹 121 | 122 | 123 | ### 3. ecanspy3文件夹 124 | 125 | 此文件夹是主项目QT工厂所在的目录,此项目最新产生可执行的ecanspy3.exe文件 126 | 127 | 本应用软件使用QT进行界面开发,内部主要使用view + model, 保证在总线负载很大的情况下,也不会占用太多的资源。 128 | 129 | 感兴趣的同学,可以先熟悉下QT库,然后在参与项目的开发 130 | 131 | ### 4. dist文件夹 132 | 133 | 此文件夹包含每个版本的最终释放的程序,其中每个版本文件夹下都有两个文件夹,一个叫packaged, 一个叫unpackaged, 现在说下区别: 134 | 135 | + packaged 这个文件夹下只有一个文件ecanspy3.exe程序,这个程序使打包后的程序。使用这个程序后,你不需要考虑有没有缺XXX.dll文件了, 但是它有个劣势,其占用的内存会偏多 136 | + unpackaged 这个文件夹下只会一个ecanspy3.exe文件,还有好多其他的dll文件,你如果使用此文件夹下的ecanspy3.exe的话,那么需要就此文件夹下对应的dll文件也拷贝过去,这样才 137 | 不会运行出错,这儿虽然拷贝不太方便,但是它相对于前者的优势是内存占用正常,不会偏多 138 | 139 | 总结 packaged文件夹下的程序拷贝(安装)方便,运行时内存占用偏高;unpackaged文件夹下的程序拷贝(安装)不方便,很容易出现缺少XXX.dll文件的问题,但是运行时内存相比较于前者 140 | 会少很多。推荐packaged下的程序 141 | 142 | ## 致谢 143 | + 感谢QT库 144 | + 感谢Vector公司的发明DBC文件格式 145 | + 感谢dracula项目 146 | -------------------------------------------------------------------------------- /dbc4cpp/context.cpp: -------------------------------------------------------------------------------- 1 | #include "context.h" 2 | 3 | BEGIN_DBC4CPP_NAMESPACE 4 | 5 | Context::Context() : state(STATE_OK) 6 | { 7 | 8 | } 9 | 10 | const QString &Context::getDBCFileName() 11 | { 12 | this->state = STATE_OK; 13 | return this->dbcFileName; 14 | } 15 | 16 | void Context::setDBCFileName(const QString &dbcFileName) 17 | { 18 | this->dbcFileName = dbcFileName; 19 | } 20 | 21 | int Context::getCurLineNo() const 22 | { 23 | return this->curLineNo; 24 | } 25 | 26 | void Context::setCurLineNo(int curLineNo) 27 | { 28 | this->curLineNo = curLineNo+1; 29 | } 30 | 31 | int Context::getCurColNo() const 32 | { 33 | return this->curColNo; 34 | } 35 | 36 | void Context::setCurColNo(int curColNo) 37 | { 38 | this->curColNo = curColNo; 39 | this->preColNo = curColNo; 40 | } 41 | 42 | const QString &Context::getErrMsg() const 43 | { 44 | return this->errMsg; 45 | } 46 | 47 | void Context::setErrMsg(const QString &errMsg) 48 | { 49 | this->state = STATE_ERR; 50 | this->errMsg = errMsg; 51 | } 52 | 53 | void Context::clrErrMsg() 54 | { 55 | this->state = STATE_OK; 56 | this->errMsg.clear(); 57 | } 58 | 59 | const QString &Context::getCurLine() const 60 | { 61 | return this->curLine; 62 | } 63 | 64 | void Context::setCurLine(const QString newCurLine) 65 | { 66 | this->curLine = newCurLine; 67 | } 68 | 69 | void Context::moveNextLine() 70 | { 71 | this->curLineNo++; 72 | this->curColNo = 0; 73 | } 74 | 75 | void Context::movePrevLineLastPos() 76 | { 77 | this->curLineNo--; 78 | this->curColNo = this->preColNo; 79 | } 80 | 81 | void Context::movePrevCol() 82 | { 83 | this->curColNo--; 84 | } 85 | 86 | void Context::moveNextCol() 87 | { 88 | this->preColNo = this->curColNo; 89 | this->curColNo++; 90 | } 91 | 92 | Context::operator bool() const 93 | { 94 | return STATE_OK == this->state; 95 | } 96 | 97 | END_DBC4CPP_NAMESPACE 98 | -------------------------------------------------------------------------------- /dbc4cpp/context.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTEXT_H 2 | #define CONTEXT_H 3 | 4 | #include "dbc4cpp_global.h" 5 | 6 | #include 7 | 8 | BEGIN_DBC4CPP_NAMESPACE 9 | 10 | /*! 11 | * \brief Context类保存解析过程中的上下文 12 | */ 13 | class Context 14 | { 15 | public: 16 | enum State{ 17 | STATE_OK = 0, //!< 当内部解析都正确的时候,为此状态 18 | STATE_ERR //!< 当内部解析出错的时候,为此状态 19 | }; 20 | 21 | /*! 22 | * \brief Context 默认构造器 23 | */ 24 | Context(); 25 | 26 | /*! 27 | * \brief getDBCFileName 获取当前解析的DBC文件名称 28 | * 29 | * 当解析出错的时候,可以调用此方法来获取温习出错的DBC文件名称 30 | * 31 | * \retval 当前解析的DBC文件名称 32 | */ 33 | const QString& getDBCFileName(); 34 | 35 | /*! 36 | * \brief setDBCFileName 设置当前解析的DBC文件名称 37 | * \param dbcFileName 要设置的DBC文件名称 38 | */ 39 | void setDBCFileName(const QString &dbcFileName); 40 | 41 | /*! 42 | * \brief getCurLineNo 获取当前正在解析的行号 43 | * 44 | * 当解析出错的时候,可以调用此方法来获取解析出错的行号 45 | * 46 | * \retval 当前正在解析的行号 47 | */ 48 | int getCurLineNo() const; 49 | 50 | /*! 51 | * \brief setCurLineNo 设置当前正在解析的行号 52 | * \param curLineNo 要设置的当前正在解析的行号 53 | */ 54 | void setCurLineNo(int curLineNo); 55 | 56 | /*! 57 | * \brief getCurColNo 获取当前列号 58 | * \retval 当前列号 59 | */ 60 | int getCurColNo() const; 61 | 62 | /*! 63 | * \brief setCurColNo 设置当前列号 64 | * \param curColNo 当前列号 65 | */ 66 | void setCurColNo(int curColNo); 67 | 68 | /*! 69 | * \brief operator bool 检测当前是否解析出错 70 | * \retval 如果没有出错,则返回true, 否则返回false 71 | */ 72 | operator bool() const; 73 | 74 | /*! 75 | * \brief getErrMsg 获取解析出错的消息 76 | * \retval 解析出错的消息 77 | */ 78 | const QString &getErrMsg() const; 79 | 80 | /*! 81 | * \brief setErrMsg 设置错误消息 82 | * \param errMsg 要设置的错误消息 83 | */ 84 | void setErrMsg(const QString &errMsg); 85 | 86 | /*! 87 | * \brief clrErrMsg 清除消息 88 | */ 89 | void clrErrMsg(); 90 | 91 | /*! 92 | * \brief getCurLine 获取当前行 93 | * \retval 当前行 94 | */ 95 | const QString &getCurLine() const; 96 | 97 | /*! 98 | * \brief setCurLine 设置当前行 99 | * \param newCurLine 新的当前行 100 | */ 101 | void setCurLine(const QString newCurLine); 102 | 103 | /*! 104 | * \brief moveNextLine 移到下一行 105 | */ 106 | void moveNextLine(); 107 | 108 | /*! 109 | * \brief movePrevLineLastPos 移动前一行的最后一个位置 110 | */ 111 | void movePrevLineLastPos(); 112 | 113 | /*! 114 | * \brief movePrevCol 移动到前一列 115 | */ 116 | void movePrevCol(); 117 | 118 | /*! 119 | * \brief moveNextCol 移动到下一列 120 | */ 121 | void moveNextCol(); 122 | 123 | private: 124 | QString dbcFileName; //!< 解析DBC文件名称 125 | 126 | int curLineNo; //!< 当前报文行号 127 | 128 | int curColNo; //!< 当前列号 129 | int preColNo; //!< 之前的列好 130 | 131 | QString curLine; //!< 当前行内容 132 | 133 | State state; //!< 当前状态 134 | QString errMsg; //!< 当包状态为出错的时候,保存出错消息 135 | }; 136 | 137 | END_DBC4CPP_NAMESPACE 138 | 139 | #endif // CONTEXT_H 140 | -------------------------------------------------------------------------------- /dbc4cpp/dbc4cpp.cpp: -------------------------------------------------------------------------------- 1 | #include "dbc4cpp.h" 2 | 3 | -------------------------------------------------------------------------------- /dbc4cpp/dbc4cpp.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC4CPP_H 2 | #define DBC4CPP_H 3 | 4 | #include "dbc4cpp_global.h" 5 | 6 | #include "parser.h" 7 | 8 | #endif // DBC4CPP_H 9 | -------------------------------------------------------------------------------- /dbc4cpp/dbc4cpp.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2019-10-04T16:34:07 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core 8 | 9 | TARGET = dbc4cpp 10 | TEMPLATE = lib 11 | 12 | DEFINES += DBC4CPP_LIBRARY 13 | 14 | QMAKE_CXXFLAGS += -std=c++11 15 | 16 | # The following define makes your compiler emit warnings if you use 17 | # any feature of Qt which has been marked as deprecated (the exact warnings 18 | # depend on your compiler). Please consult the documentation of the 19 | # deprecated API in order to know how to port your code away from it. 20 | DEFINES += QT_DEPRECATED_WARNINGS 21 | 22 | # You can also make your code fail to compile if you use deprecated APIs. 23 | # In order to do so, uncomment the following line. 24 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 25 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 26 | 27 | SOURCES += \ 28 | dbc4cpp.cpp \ 29 | context.cpp \ 30 | entities.cpp \ 31 | parser.cpp \ 32 | builder.cpp 33 | 34 | HEADERS += \ 35 | dbc4cpp.h \ 36 | dbc4cpp_global.h \ 37 | context.h \ 38 | dbc4cpp_config.h \ 39 | entities.h \ 40 | parser.h \ 41 | builder.h 42 | 43 | unix { 44 | target.path = /usr/lib 45 | INSTALLS += target 46 | } 47 | -------------------------------------------------------------------------------- /dbc4cpp/dbc4cpp_config.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC4CPP_CONFIG_H 2 | #define DBC4CPP_CONFIG_H 3 | 4 | #ifdef USE_DBC4CPP_NAMESPACE 5 | #define BEGIN_DBC4CPP_NAMESPACE namespace { 6 | #define END_DBC4CPP_NAMESPACE } 7 | #else 8 | #define BEGIN_DBC4CPP_NAMESPACE 9 | #define END_DBC4CPP_NAMESPACE 10 | #endif 11 | 12 | #endif // DBC4CPP_CONFIG_H 13 | -------------------------------------------------------------------------------- /dbc4cpp/dbc4cpp_global.h: -------------------------------------------------------------------------------- 1 | #ifndef DBC4CPP_GLOBAL_H 2 | #define DBC4CPP_GLOBAL_H 3 | 4 | #include 5 | 6 | #if defined(DBC4CPP_LIBRARY) 7 | # define DBC4CPPSHARED_EXPORT Q_DECL_EXPORT 8 | #else 9 | # define DBC4CPPSHARED_EXPORT Q_DECL_IMPORT 10 | #endif 11 | 12 | #include "dbc4cpp_config.h" 13 | 14 | #endif // DBC4CPP_GLOBAL_H 15 | -------------------------------------------------------------------------------- /dbc4cpp/entities.cpp: -------------------------------------------------------------------------------- 1 | #include "entities.h" 2 | 3 | Version::Version(const QString &_version) 4 | : version(_version) 5 | { 6 | 7 | } 8 | 9 | const QString &Version::get() const 10 | { 11 | return version; 12 | } 13 | 14 | void Version::set(const QString &version) 15 | { 16 | this->version = version; 17 | } 18 | 19 | NS::NS(const QSet &set) 20 | : newSymboSet(set) 21 | { 22 | 23 | } 24 | 25 | void NS::addNewSymbol(const QString &newSymbol) 26 | { 27 | this->newSymboSet.insert(newSymbol); 28 | } 29 | 30 | void NS::reset() 31 | { 32 | this->newSymboSet.clear(); 33 | } 34 | 35 | const QSet &NS::getNewSymbolSet() const 36 | { 37 | return this->newSymboSet; 38 | } 39 | 40 | 41 | BS::BS(quint32 _baurate, quint32 _btr1, quint32 _btr2) 42 | : baurate(_baurate), btr1(_btr1), btr2(_btr2) 43 | { 44 | 45 | } 46 | 47 | quint32 BS::getBaurate() const 48 | { 49 | return this->baurate; 50 | } 51 | 52 | void BS::setBaurate(quint32 baurate) 53 | { 54 | this->baurate = baurate; 55 | } 56 | 57 | quint32 BS::getBTR1() const 58 | { 59 | return this->btr1; 60 | } 61 | 62 | void BS::setBTR1(quint32 btr1) 63 | { 64 | this->btr1 = btr1; 65 | } 66 | 67 | quint32 BS::getBTR2() const 68 | { 69 | return this->btr2; 70 | } 71 | 72 | void BS::setBTR2(quint32 btr2) 73 | { 74 | this->btr2 = btr2; 75 | } 76 | 77 | Document::Document() 78 | { 79 | // 新建默认的节点 80 | const QString defaultNodeName("Vector__XXX"); 81 | Node *node = new Node; 82 | if (node) { 83 | node->setName(defaultNodeName); 84 | addNode(node); 85 | } 86 | } 87 | 88 | Document::~Document() 89 | { 90 | removeAllNode(); 91 | removeAllValueTable(); 92 | removeAllEV(); 93 | removeAllEVData(); 94 | } 95 | 96 | void Document::foreachAllNewSymbols(Document::ForEachNewSymbolFn fn, void *arg) 97 | { 98 | if (fn != NULL) { 99 | const QSet &newSymbolSet = ns.getNewSymbolSet(); 100 | for (auto iter = newSymbolSet.begin(); iter != newSymbolSet.end(); ++iter) { 101 | if (!fn(arg, *iter)) { 102 | break; 103 | } 104 | } 105 | } 106 | } 107 | 108 | const QSet &Document::getNS() const 109 | { 110 | return ns.getNewSymbolSet(); 111 | } 112 | 113 | void Document::setBS(quint32 baurate, quint32 btr1, quint32 btr2) 114 | { 115 | bs.setBaurate(baurate); 116 | bs.setBTR1(btr1); 117 | bs.setBTR2(btr2); 118 | } 119 | 120 | void Document::getBS(quint32 &baurate, quint32 &btr1, quint32 &btr2) 121 | { 122 | baurate = bs.getBaurate(); 123 | btr1 = bs.getBTR1(); 124 | btr2 = bs.getBTR2(); 125 | } 126 | 127 | void Document::addNode(Node *node) 128 | { 129 | if (node != NULL) { 130 | nodeMap.insert(node->getName(), node); 131 | } 132 | } 133 | 134 | void Document::removeNode(const QString &nodeName) 135 | { 136 | auto iter = nodeMap.find(nodeName); 137 | if (iter != nodeMap.end()) { 138 | delete iter.value(); 139 | nodeMap.erase(iter); 140 | } 141 | } 142 | 143 | void Document::removeNode(Node *node) 144 | { 145 | if (node != NULL) { 146 | removeNode(node->getName()); 147 | } 148 | } 149 | 150 | void Document::removeAllNode() 151 | { 152 | // 释放掉nodeList 153 | for (auto iter = nodeMap.begin(); iter != nodeMap.end(); ++iter) { 154 | delete iter.value(); 155 | iter.value() = NULL; 156 | } 157 | nodeMap.clear(); 158 | } 159 | 160 | Node *Document::getNode(const QString &nodeName) 161 | { 162 | auto iter = nodeMap.find(nodeName); 163 | if (iter != nodeMap.end()) { 164 | return iter.value(); 165 | } 166 | 167 | return NULL; 168 | } 169 | 170 | QList Document::getBU() 171 | { 172 | return nodeMap.keys(); 173 | } 174 | 175 | void Document::addValueTable(ValTable *valTable) 176 | { 177 | if (valTable != NULL) { 178 | valueTableMap.insert(valTable->getName(), valTable); 179 | } 180 | } 181 | 182 | void Document::removeValueTable(const QString &name) 183 | { 184 | auto iter = valueTableMap.find(name); 185 | if (iter != valueTableMap.end()) { 186 | delete iter.value(); 187 | iter.value() = NULL; 188 | valueTableMap.erase(iter); 189 | } 190 | } 191 | 192 | void Document::removeAllValueTable() 193 | { 194 | for (auto iter = valueTableMap.begin(); iter != valueTableMap.end(); ++iter) { 195 | delete iter.value(); 196 | iter.value() = NULL; 197 | } 198 | valueTableMap.clear(); 199 | } 200 | 201 | ValTable *Document::getValueTable(const QString &name) 202 | { 203 | auto iter = valueTableMap.find(name); 204 | if (iter != valueTableMap.end()) { 205 | return iter.value(); 206 | } 207 | return NULL; 208 | } 209 | 210 | Message *Document::getMessage(qint32 msgId) 211 | { 212 | Message *msg = NULL; 213 | for (auto iter = nodeMap.begin(); iter != nodeMap.end(); ++iter) { 214 | Node *node = iter.value(); 215 | msg = node->getTxMsg(msgId); 216 | if (msg != NULL) { 217 | break; 218 | } 219 | } 220 | return msg; 221 | } 222 | 223 | Signal *Document::getSignal(qint32 msgId, const QString &sigName) 224 | { 225 | Message *msg = getMessage(msgId); 226 | if (msg != NULL) { 227 | return msg->getSignal(sigName); 228 | } 229 | return NULL; 230 | } 231 | 232 | const AdditionalAttribute* Document::getMsgAttribute(quint32 msgId, const QString &attrName) const 233 | { 234 | // 先从报文表中获取属性 235 | auto iter = msgAttrTblMap.find(msgId); 236 | if (iter != msgAttrTblMap.end()) { 237 | auto &tbl = iter.value(); 238 | return tbl.getAdditionalAttribute(attrName); 239 | } else { //找不到从默认属性中找 240 | auto msgIter = defaultAttrTblMap.find(OBJECT_TYPE_BO); 241 | if (msgIter != defaultAttrTblMap.end()) { 242 | return msgIter.value().getAdditionalAttribute(attrName); 243 | } 244 | } 245 | return NULL; 246 | } 247 | 248 | void Document::setDefaultAttributeIntValue(const QString &attrName, qint32 value) 249 | { 250 | for (auto iter = defaultAttrTblMap.begin(); iter != defaultAttrTblMap.end(); ++iter) { 251 | AdditionalAttribute *attr = iter.value().getAdditionalAttribute(attrName); 252 | if (attr != NULL) { 253 | attr->setIntValue(value); 254 | break; 255 | } 256 | } 257 | } 258 | 259 | void Document::setDefaultAttributeHexValue(const QString &attrName, qint32 value) 260 | { 261 | for (auto iter = defaultAttrTblMap.begin(); iter != defaultAttrTblMap.end(); ++iter) { 262 | AdditionalAttribute *attr = iter.value().getAdditionalAttribute(attrName); 263 | if (attr != NULL) { 264 | attr->setHexValue(value); 265 | break; 266 | } 267 | } 268 | } 269 | 270 | void Document::setDefaultAttributeFloatValue(const QString &attrName, double value) 271 | { 272 | for (auto iter = defaultAttrTblMap.begin(); iter != defaultAttrTblMap.end(); ++iter) { 273 | AdditionalAttribute *attr = iter.value().getAdditionalAttribute(attrName); 274 | if (attr != NULL) { 275 | attr->setFloatValue(value); 276 | break; 277 | } 278 | } 279 | } 280 | 281 | void Document::setDefaultAttributeStringValue(const QString &attrName, const QString &value) 282 | { 283 | for (auto iter = defaultAttrTblMap.begin(); iter != defaultAttrTblMap.end(); ++iter) { 284 | AdditionalAttribute *attr = iter.value().getAdditionalAttribute(attrName); 285 | if (attr != NULL) { 286 | attr->setStringValue(value); 287 | break; 288 | } 289 | } 290 | } 291 | 292 | void Document::setDefaultAttributeEnumValue(const QString &attrName, const QString &value) 293 | { 294 | for (auto iter = defaultAttrTblMap.begin(); iter != defaultAttrTblMap.end(); ++iter) { 295 | AdditionalAttribute *attr = iter.value().getAdditionalAttribute(attrName); 296 | if (attr != NULL) { 297 | attr->setEnumValue(value); 298 | break; 299 | } 300 | } 301 | } 302 | 303 | void Document::onParseDone() 304 | { 305 | msgList.clear(); 306 | for (auto iter = nodeMap.begin(); iter != nodeMap.end(); ++iter) { 307 | Node *node = iter.value(); 308 | auto &txMsgMap = node->getTxMsgMap(); 309 | for (auto iter = txMsgMap.begin(); iter != txMsgMap.end(); ++iter) { 310 | msgList.push_back(iter.value()); 311 | } 312 | } 313 | } 314 | -------------------------------------------------------------------------------- /dbc4cpp/parser.h: -------------------------------------------------------------------------------- 1 | #ifndef PARSER_H 2 | #define PARSER_H 3 | 4 | #include "dbc4cpp_global.h" 5 | #include "builder.h" 6 | 7 | #include 8 | #include 9 | 10 | BEGIN_DBC4CPP_NAMESPACE 11 | 12 | /*! 13 | * \brief Parser定义了解析接口 14 | */ 15 | struct Parser 16 | { 17 | virtual ~Parser() {} 18 | 19 | /*! 20 | * \brief parse 解析指定文件 21 | * \param file 要解析的文件名称 22 | * \param builder 用于构建最终的结果 23 | * \retval 返回成功与否信息 24 | */ 25 | virtual bool parse(const QString &file, Builder &builder) = 0; 26 | 27 | /*! 28 | * \brief getErrMsg 获取错误消息 29 | * \retval 返回错误消息 30 | */ 31 | virtual const QString &getErrMsg() const = 0; 32 | }; 33 | 34 | class ParserBase : public Parser 35 | { 36 | public: 37 | ParserBase(); 38 | 39 | const QString &getErrMsg() const; 40 | bool parse(const QString &file, Builder &builder); 41 | 42 | protected: 43 | virtual bool onParse(Builder &Builder) = 0; 44 | virtual QString getCodec() const = 0; 45 | 46 | QChar getChar(); 47 | void putChar(); 48 | bool isEnd(); 49 | 50 | protected: 51 | Context context; 52 | 53 | QString content; 54 | qint32 cursor; 55 | }; 56 | 57 | class DBCParser : public ParserBase 58 | { 59 | public: 60 | typedef bool (DBCParser::*ParseMethod)(Builder &Builder); 61 | typedef QMap ParseMethodMap; 62 | 63 | bool onParse(Builder &builder); 64 | DBCParser(); 65 | protected: 66 | 67 | QString getCodec() const 68 | { 69 | return "gbk"; 70 | } 71 | 72 | /*! 73 | * \brief parseVersion 解析版本号 74 | * \param builder 构建器 75 | * \retval 解析成功与否 76 | */ 77 | bool parseVersion(Builder &builder); 78 | 79 | /*! 80 | * \brief parseNewSymbol 解析新标签 81 | * \param builder 构建器 82 | * \retval 解析成功与否 83 | */ 84 | bool parseNewSymbol(Builder &builder); 85 | 86 | /*! 87 | * \brief parseBitSetting 解析位设置 88 | * \param builder 构建器 89 | * \retval 解析成功与否 90 | */ 91 | bool parseBitSetting(Builder &builder); 92 | 93 | /*! 94 | * \brief parseNode 解析节点 95 | * \param builder 构建器 96 | * \retval 解析成功与否 97 | */ 98 | bool parseNode(Builder &builder); 99 | 100 | /*! 101 | * \brief parseMessage 解析报文 102 | * \param builder 构建器 103 | * \retval 解析成功与否 104 | */ 105 | bool parseMessage(Builder &builder); 106 | 107 | /*! 108 | * \brief parseSignal 解析信号 109 | * \param builder 构建器 110 | * \retval 解析成功与否 111 | */ 112 | bool parseSignal(Builder &builder); 113 | 114 | /*! 115 | * \brief parseValueTable 解析值表 116 | * \param builder 构建器 117 | * \retval 解析成功与否 118 | */ 119 | bool parseValueTable(Builder &builder); 120 | 121 | /*! 122 | * \brief parseAttrDefine 解析属性定义 123 | * \param builder 构建器 124 | * \retval 解析成功与否 125 | */ 126 | bool parseAttrDefine(Builder &builder); 127 | 128 | /*! 129 | * \brief parseAttrDefaultDefine 解析属性默认值定义 130 | * \param builder 构建器 131 | * \retval 解析成功与否 132 | */ 133 | bool parseAttrDefaultDefine(Builder &builder); 134 | 135 | /*! 136 | * \brief parseAttrValue 解析属性值 137 | * \param builder 构建器 138 | * \retval 解析成功与否 139 | */ 140 | bool parseAttrValue(Builder &builder); 141 | 142 | /*! 143 | * \brief parseValue 解析值描述 144 | * \param builder 构建器 145 | * \retval 解析成功与否 146 | */ 147 | bool parseValue(Builder &builder); 148 | 149 | /*! 150 | * \brief parseBoTxBu 解析单报文多发送节点 151 | * \param builder 构建器 152 | * \return 解析成功与否 153 | */ 154 | bool parseBoTxBu(Builder &builder); 155 | 156 | /*! 157 | * \brief parseComment 解析注释 158 | * \param builder 构建器 159 | * \return 构建成功与否 160 | */ 161 | bool parseComment(Builder &builder); 162 | 163 | /*! 164 | * \brief doParseValueDesc 进行解析值描述 165 | * \param [out] valueDesc 接收解析后的结果 166 | * \retval 解析成功与否 167 | */ 168 | bool doParseValueDesc(QList &valueDesc); 169 | 170 | private: 171 | 172 | /*! 173 | * \brief getNextIdentifier 获取下一个标识符 174 | * \param [out] nextIdentifier 接收下一个标识符 175 | * \param [in] eol true - 检测到行尾则终止, false - 只有检测到eof才终止 176 | * \retval 是否接收到 177 | */ 178 | bool getNextIdentifier(QString &nextIdentifier, bool eol=true); 179 | 180 | /*! 181 | * \brief getNextString 获取下一个字符串 182 | * \param [out] nextString 接收下一个字符串 183 | * \retval 是否接收到 184 | */ 185 | bool getNextString(QString &nextString); 186 | 187 | /*! 188 | * \brief getNextSemicolon 获取一个封号';' 189 | * \retval 是否接收到 190 | */ 191 | bool getNextSemicolon(); 192 | 193 | /*! 194 | * \brief getNextColon 获取下一个冒号':' 195 | * \retval 是否接收到 196 | */ 197 | bool getNextColon(); 198 | 199 | /*! 200 | * \brief getNextComma 获取下一个逗号',' 201 | * \retval 是否接收到 202 | */ 203 | bool getNextComma(); 204 | 205 | /*! 206 | * \brief getNextInt 获取下一个整数 207 | * \param [out] intVal 接收下一个整数 208 | * \param [out] eol true - 检测到行尾自动终止,false - 扫描到行尾没有扫描到,则换行扫描 209 | * \retval 是否接收到 210 | */ 211 | bool getNextInt(qint32 &intVal, bool eol=true); 212 | 213 | /*! 214 | * \brief getNextUnsignedInt 获取下一个无符号整数 215 | * \param [out] uintVal 接收下一个无符号整数 216 | * \param [out] eol true - 检测到行尾自动终止,false - 扫描到行尾没有扫描到,则换行扫描 217 | * \retval 是否接收到 218 | */ 219 | bool getNextUnsignedInt(quint32 &uintVal, bool eol=true); 220 | 221 | /*! 222 | * \brief getNextDouble 获取下一个小数值 223 | * \param [out] doubleVal 接收下一个小数值 224 | * \param [out] eol true - 检测到行尾自动终止,false - 扫描到行尾没有扫描到,则换行扫描 225 | * \return 是否接收到 226 | */ 227 | bool getNextDouble(double &doubleVal, bool eol=true); 228 | 229 | /*! 230 | * \brief getNextNewSymbol 获取下一个New Symbol 231 | * \param [out] newSymbol 接收下一个New Symbol 232 | * \retval 是否接收到 233 | */ 234 | bool getNextNewSymbol(QString &newSymbol); 235 | 236 | /*! 237 | * \brief getNextChar 获取下一个字符 238 | * \param ch 要获取的字符 239 | * \param desc 描述信息 240 | * \param eol 以换行符作为终止字符 241 | * \retval 是否接收到 242 | */ 243 | bool getNextChar(QChar ch, QString desc, bool eol=true); 244 | 245 | /*! 246 | * \brief getNextChar 获取下一个在集合中中的字符 247 | * \param set 集合 248 | * \param desc 描述 249 | * \param [out] out 接收收到的字符 250 | * \param eol 以换行符作为终止字符 251 | * \retval 是否接收到 252 | */ 253 | bool getNextChar(const QString set, const QString &desc, QChar &out, bool eol=true); 254 | 255 | /*! 256 | * \brief getNextValue 获取下一个值 257 | * \param [out] iret true - 则说明返回值是ival 258 | * \param [out] ival 接收读到的整数值 259 | * \param [out] dret true - 则说明返回值是dval 260 | * \param [out] dval 接收读到的小数值 261 | * \param [out] sret true - 则说明返回值是sval 262 | * \param [out] sval 接收读到的字符串值 263 | * \return 是否接收到 264 | */ 265 | bool getNextValue(bool &iret, qint32 &ival, bool &dret, double &dval, 266 | bool &sret, QString &sval); 267 | 268 | private: 269 | ParseMethodMap parseMethodMap; 270 | }; 271 | 272 | END_DBC4CPP_NAMESPACE 273 | 274 | #endif // PARSER_H 275 | -------------------------------------------------------------------------------- /dbc4cpp_test/dbc4cpp_test.pro: -------------------------------------------------------------------------------- 1 | QT -= gui 2 | 3 | CONFIG += console 4 | CONFIG -= app_bundle 5 | 6 | QMAKE_CXXFLAGS += -std=c++11 7 | 8 | INCLUDEPATH += F:\\projects\\cpp_projects\\HostComputerProjects\\CommonTool\\ECanSpy3\\Projects 9 | LIBS += F:\\projects\\cpp_projects\\HostComputerProjects\\CommonTool\\ECanSpy3\\Projects\\build-dbc4cpp-Qt4_8_7_Win32-Debug\\debug\\libdbc4cpp.a 10 | 11 | # The following define makes your compiler emit warnings if you use 12 | # any feature of Qt which as been marked deprecated (the exact warnings 13 | # depend on your compiler). Please consult the documentation of the 14 | # deprecated API in order to know how to port your code away from it. 15 | DEFINES += QT_DEPRECATED_WARNINGS 16 | 17 | # You can also make your code fail to compile if you use deprecated APIs. 18 | # In order to do so, uncomment the following line. 19 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 20 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 21 | 22 | SOURCES += main.cpp 23 | -------------------------------------------------------------------------------- /dist/v1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/dist/v1.0.zip -------------------------------------------------------------------------------- /docs/TEST.ini: -------------------------------------------------------------------------------- 1 | [View_Vehicles] 2 | HIDDEN= 3 | ORDER=0,1, 4 | DEFINITIONS= 5 | COLUMNWIDTHS=125,150, 6 | [View_Vehicle] 7 | HIDDEN= 8 | ORDER=0,1, 9 | DEFINITIONS= 10 | COLUMNWIDTHS=125,125, 11 | [View_VehicleNetworks] 12 | HIDDEN=3, 13 | ORDER=0,1,2,3,4,5, 14 | DEFINITIONS=21,22, 15 | COLUMNWIDTHS=125,125,100,100,150,100,50, 16 | [View_VehicleNetwork] 17 | HIDDEN= 18 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12, 19 | DEFINITIONS=13,14,15,16,17,18,19,20, 20 | COLUMNWIDTHS=125,125,100,125,150,50,50,50,50,100,100,100,50, 21 | [View_VehicleNetworkTxMessages] 22 | HIDDEN= 23 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 24 | DEFINITIONS=5,6,7,8,9,10,11,12, 25 | COLUMNWIDTHS=125,125,55,100,50,100,100,100,150,50,100,100,50,50,50,50,100, 26 | [View_VehicleNetworkSignals] 27 | HIDDEN= 28 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, 29 | DEFINITIONS=1,2,3,4, 30 | COLUMNWIDTHS=125,125,100,50,100,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 31 | [View_VehicleControlUnit] 32 | HIDDEN= 33 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12, 34 | DEFINITIONS=13,14,15,16,17,18,19,20, 35 | COLUMNWIDTHS=125,125,100,125,150,50,50,50,50,100,100,100,50, 36 | [View_VehicleGateways] 37 | HIDDEN= 38 | ORDER=0,1,2, 39 | DEFINITIONS= 40 | COLUMNWIDTHS=125,125,100, 41 | [View_VehicleGatewaySignals] 42 | HIDDEN= 43 | ORDER=0,1,2,3,4,5,6, 44 | DEFINITIONS= 45 | COLUMNWIDTHS=125,125,125,125,125,125,125, 46 | [View_Networks] 47 | HIDDEN=2, 48 | ORDER=0,1,2,3,4, 49 | DEFINITIONS=21,22, 50 | COLUMNWIDTHS=125,100,100,150,100,50, 51 | [View_Network] 52 | HIDDEN= 53 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11, 54 | DEFINITIONS=13,14,15,16,17,18,19,20, 55 | COLUMNWIDTHS=125,125,100,150,50,50,50,50,100,100,100,50, 56 | [View_NetworkTxMessages] 57 | HIDDEN= 58 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 59 | DEFINITIONS=5,6,7,8,9,10,11,12, 60 | COLUMNWIDTHS=125,125,55,100,50,100,100,100,150,50,100,100,50,50,50,50,100, 61 | [View_NetworkTxSignals] 62 | HIDDEN= 63 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, 64 | DEFINITIONS=1,2,3,4, 65 | COLUMNWIDTHS=125,125,100,50,100,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 66 | [View_NetworkNodeGroup] 67 | HIDDEN= 68 | ORDER=0,1,2,3,4,5,6,7,8,9,10, 69 | DEFINITIONS=13,14,15,16,17,18,19,20, 70 | COLUMNWIDTHS=125,100,150,50,50,50,50,100,100,100,50, 71 | [View_Ecus] 72 | HIDDEN= 73 | ORDER=0,1, 74 | DEFINITIONS= 75 | COLUMNWIDTHS=125,150, 76 | [View_Ecu] 77 | HIDDEN= 78 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11, 79 | DEFINITIONS=13,14,15,16,17,18,19,20, 80 | COLUMNWIDTHS=125,125,100,150,50,50,50,50,100,100,100,50, 81 | [View_EnvVars] 82 | HIDDEN= 83 | ORDER=0,1,2,3,4,5,6,7,8,9, 84 | DEFINITIONS= 85 | COLUMNWIDTHS=125,100,100,50,50,50,50,100,100,150, 86 | [View_EnvVar] 87 | HIDDEN= 88 | ORDER=0,1,2,3,4,5,6,7,8,9,10, 89 | DEFINITIONS= 90 | COLUMNWIDTHS=125,125,100,100,50,50,50,50,100,100,150, 91 | [View_NodeGroups] 92 | HIDDEN= 93 | ORDER=0,1, 94 | DEFINITIONS= 95 | COLUMNWIDTHS=125,150, 96 | [View_NodeGroup] 97 | HIDDEN= 98 | ORDER=0,1,2,3, 99 | DEFINITIONS= 100 | COLUMNWIDTHS=125,125,55,150, 101 | [View_Nodes] 102 | HIDDEN= 103 | ORDER=0,1,2,3,4,5,6,7,8,9,10, 104 | DEFINITIONS=13,14,15,16,17,18,19,20, 105 | COLUMNWIDTHS=125,100,150,50,50,50,50,100,100,100,50, 106 | [View_Node] 107 | HIDDEN= 108 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, 109 | DEFINITIONS=1,2,3,4, 110 | COLUMNWIDTHS=125,125,100,50,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 111 | [View_NodeTxMessages] 112 | HIDDEN= 113 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 114 | DEFINITIONS=5,6,7,8,9,10,11,12, 115 | COLUMNWIDTHS=125,125,55,100,50,100,100,150,50,100,100,50,50,50,50,100, 116 | [View_NodeTxMsg] 117 | HIDDEN= 118 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, 119 | DEFINITIONS=1,2,3,4, 120 | COLUMNWIDTHS=125,125,100,50,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 121 | [View_NodeTxSignals] 122 | HIDDEN= 123 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, 124 | DEFINITIONS=1,2,3,4, 125 | COLUMNWIDTHS=125,125,100,50,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 126 | [View_NodeRxSignals] 127 | HIDDEN= 128 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, 129 | DEFINITIONS=1,2,3,4, 130 | COLUMNWIDTHS=125,125,100,50,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 131 | [View_NodeTxSigs] 132 | HIDDEN= 133 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 134 | DEFINITIONS=1,2,3,4, 135 | COLUMNWIDTHS=125,125,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 136 | [View_NodeRxSigs] 137 | HIDDEN= 138 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 139 | DEFINITIONS=1,2,3,4, 140 | COLUMNWIDTHS=125,125,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 141 | [View_Messages] 142 | HIDDEN= 143 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 144 | DEFINITIONS=5,6,7,8,9,10,11,12, 145 | COLUMNWIDTHS=125,55,100,50,100,100,100,150,50,100,100,50,50,50,50,100, 146 | [View_Message] 147 | HIDDEN= 148 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18, 149 | DEFINITIONS=1,2,3,4, 150 | COLUMNWIDTHS=125,125,100,50,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 151 | [View_Signals] 152 | HIDDEN= 153 | ORDER=0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, 154 | DEFINITIONS=1,2,3,4, 155 | COLUMNWIDTHS=125,50,100,100,100,50,50,50,50,100,100,150,50,100,50,100, 156 | [View_ValueTables] 157 | HIDDEN= 158 | ORDER=0,1, 159 | DEFINITIONS= 160 | COLUMNWIDTHS=125,150, 161 | [View_AttrDefs] 162 | HIDDEN=6, 163 | ORDER=0,1,2,3,4,5, 164 | DEFINITIONS= 165 | COLUMNWIDTHS=125,100,100,50,50,100,150, 166 | -------------------------------------------------------------------------------- /docs/black-theme-scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/black-theme-scroll.png -------------------------------------------------------------------------------- /docs/black-theme-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/black-theme-update.png -------------------------------------------------------------------------------- /docs/usage-step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step1.png -------------------------------------------------------------------------------- /docs/usage-step21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step21.png -------------------------------------------------------------------------------- /docs/usage-step22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step22.png -------------------------------------------------------------------------------- /docs/usage-step31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step31.png -------------------------------------------------------------------------------- /docs/usage-step32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step32.png -------------------------------------------------------------------------------- /docs/usage-step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step4.png -------------------------------------------------------------------------------- /docs/usage-step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step5.png -------------------------------------------------------------------------------- /docs/usage-step61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step61.png -------------------------------------------------------------------------------- /docs/usage-step62.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step62.png -------------------------------------------------------------------------------- /docs/usage-step63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/usage-step63.png -------------------------------------------------------------------------------- /docs/white-theme-scroll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/white-theme-scroll.png -------------------------------------------------------------------------------- /docs/white-theme-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/docs/white-theme-update.png -------------------------------------------------------------------------------- /ecanspy3/aboutecanspy3.cpp: -------------------------------------------------------------------------------- 1 | #include "aboutecanspy3.h" 2 | #include "ui_aboutecanspy3.h" 3 | 4 | AboutEcanSpy3Diaglog::AboutEcanSpy3Diaglog(QWidget *parent) : 5 | QDialog(parent), 6 | ui(new Ui::AboutEcanSpy3) 7 | { 8 | ui->setupUi(this); 9 | } 10 | 11 | AboutEcanSpy3Diaglog::~AboutEcanSpy3Diaglog() 12 | { 13 | delete ui; 14 | } 15 | 16 | void AboutEcanSpy3Diaglog::on_okPushButton_clicked() 17 | { 18 | close(); 19 | } 20 | -------------------------------------------------------------------------------- /ecanspy3/aboutecanspy3.h: -------------------------------------------------------------------------------- 1 | #ifndef ABOUTECANSPY3_H 2 | #define ABOUTECANSPY3_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class AboutEcanSpy3; 8 | } 9 | 10 | class AboutEcanSpy3Diaglog : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit AboutEcanSpy3Diaglog(QWidget *parent = 0); 16 | ~AboutEcanSpy3Diaglog(); 17 | 18 | private slots: 19 | void on_okPushButton_clicked(); 20 | 21 | private: 22 | Ui::AboutEcanSpy3 *ui; 23 | }; 24 | 25 | #endif // ABOUTECANSPY3_H 26 | -------------------------------------------------------------------------------- /ecanspy3/aboutecanspy3.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | AboutEcanSpy3 4 | 5 | 6 | 7 | 0 8 | 0 9 | 407 10 | 239 11 | 12 | 13 | 14 | 关于EcanSpy3 15 | 16 | 17 | 18 | 19 | 180 20 | 60 21 | 241 22 | 61 23 | 24 | 25 | 26 | 版本所有归ECanSpy3所有。 27 | 28 | 版本:V1.0 29 | 30 | 31 | 32 | 33 | 34 | 180 35 | 150 36 | 93 37 | 28 38 | 39 | 40 | 41 | 确定 42 | 43 | 44 | 45 | 46 | 47 | 30 48 | 60 49 | 91 50 | 91 51 | 52 | 53 | 54 | border-image: url(:/images/ECanSpy3.png); 55 | 56 | 57 | QFrame::NoFrame 58 | 59 | 60 | QFrame::Raised 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /ecanspy3/base/mainwindowbase.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindowbase.h" 2 | 3 | MainWindowBase::MainWindowBase(QWidget *parent) : QMainWindow(parent) 4 | { 5 | 6 | } 7 | 8 | QDockWidget *MainWindowBase::createDockWidget(const QString &title, QWidget *widget) 9 | { 10 | QDockWidget *dockWidget = new QDockWidget(title); 11 | dockWidget->setObjectName(title); 12 | dockWidget->setFeatures(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable); 13 | dockWidget->setWidget(widget); 14 | dockWidget->setAllowedAreas(Qt::LeftDockWidgetArea|Qt::RightDockWidgetArea 15 | |Qt::TopDockWidgetArea|Qt::BottomDockWidgetArea); 16 | dockWidget->hide(); 17 | dockWidgets.push_back(dockWidget); 18 | return dockWidget; 19 | } 20 | 21 | void MainWindowBase::showDockWidgets() 22 | { 23 | foreach (QDockWidget *dockWidget, dockWidgets) { 24 | if (dockWidget) { 25 | dockWidget->show(); 26 | } 27 | } 28 | } 29 | 30 | void MainWindowBase::hideDockWidgets() 31 | { 32 | foreach (QDockWidget *dockWidget, dockWidgets) { 33 | if (dockWidget) { 34 | dockWidget->hide(); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /ecanspy3/base/mainwindowbase.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOWBASE_H 2 | #define MAINWINDOWBASE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class MainWindowBase : public QMainWindow 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit MainWindowBase(QWidget *parent = nullptr); 13 | 14 | protected: 15 | 16 | QDockWidget *createDockWidget(const QString &title, QWidget *widget); 17 | void showDockWidgets(); 18 | void hideDockWidgets(); 19 | 20 | signals: 21 | 22 | public slots: 23 | 24 | private: 25 | QList dockWidgets; 26 | }; 27 | 28 | #endif // MAINWINDOWBASE_H 29 | -------------------------------------------------------------------------------- /ecanspy3/base/titletableviewwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "titletableviewwidget.h" 2 | 3 | TitleTableViewWidget::TitleTableViewWidget(QWidget *parent) : QWidget(parent) 4 | { 5 | QVBoxLayout *layout = new QVBoxLayout; 6 | layout->setContentsMargins(2, 0, 0, 0); 7 | initTitle(layout); 8 | initTableView(layout); 9 | setLayout(layout); 10 | } 11 | 12 | void TitleTableViewWidget::initTitle(QVBoxLayout *layout) 13 | { 14 | title = new QLabel(this); 15 | //title->setStyleSheet(QString("background-color:rgb(200,200,200);padding:5;color:black;")); 16 | layout->addWidget(title); 17 | } 18 | 19 | void TitleTableViewWidget::initTableView(QVBoxLayout *layout) 20 | { 21 | tableView = new QTableView(this); 22 | layout->addWidget(tableView); 23 | } 24 | -------------------------------------------------------------------------------- /ecanspy3/base/titletableviewwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef TITLETABLEVIEWWIDGET_H 2 | #define TITLETABLEVIEWWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class TitleTableViewWidget : public QWidget 10 | { 11 | Q_OBJECT 12 | public: 13 | TitleTableViewWidget(QWidget *parent = nullptr); 14 | 15 | void initTitle(QVBoxLayout *); 16 | void initTableView(QVBoxLayout *); 17 | 18 | signals: 19 | 20 | public slots: 21 | 22 | protected: 23 | QLabel *title; 24 | QTableView *tableView; 25 | }; 26 | 27 | #endif // TITLETABLEVIEWWIDGET_H 28 | -------------------------------------------------------------------------------- /ecanspy3/base/titletreeviewwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "titletreeviewwidget.h" 2 | 3 | TitleTreeViewWidget::TitleTreeViewWidget(QWidget *parent) : QWidget(parent) 4 | { 5 | QVBoxLayout *layout = new QVBoxLayout; 6 | layout->setContentsMargins(0, 0, 0, 0); 7 | initTitle(layout); 8 | initTreeView(layout); 9 | setLayout(layout); 10 | } 11 | 12 | void TitleTreeViewWidget::initTitle(QVBoxLayout *layout) 13 | { 14 | title = new QLabel(this); 15 | //title->setStyleSheet(QString("background-color:rgb(200,200,200);padding:5;color:black;")); 16 | layout->addWidget(title); 17 | } 18 | 19 | void TitleTreeViewWidget::initTreeView(QVBoxLayout *layout) 20 | { 21 | treeView = new QTreeView(this); 22 | layout->addWidget(treeView); 23 | } 24 | -------------------------------------------------------------------------------- /ecanspy3/base/titletreeviewwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef TITLETREEVIEWWIDGET_H 2 | #define TITLETREEVIEWWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | class TitleTreeViewWidget : public QWidget 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit TitleTreeViewWidget(QWidget *parent = nullptr); 14 | 15 | void initTitle(QVBoxLayout *); 16 | void initTreeView(QVBoxLayout *); 17 | 18 | signals: 19 | 20 | public slots: 21 | 22 | protected: 23 | QLabel *title; 24 | QTreeView *treeView; 25 | }; 26 | 27 | #endif // TITLETREEVIEWWIDGET_H 28 | -------------------------------------------------------------------------------- /ecanspy3/can/Core - 副本.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "ECanVci.H" 6 | 7 | #include 8 | #include 9 | 10 | ///////////////////////////////////////////////////////////////////////////////////////// 11 | // 周期性发送报文 12 | //////////////////////////////////////////////////////////////////////////////////////// 13 | 14 | // 这个类当需要调用的时候调用, 15 | // 当前用于,当报文发送完毕的时候,调用 16 | class MessageTxCallback 17 | { 18 | public: 19 | MessageTxCallback(BOOL need_called = FALSE) : need_called_(need_called) {} 20 | 21 | void NeedCalled() 22 | { 23 | need_called_ = TRUE; 24 | } 25 | 26 | void Call() 27 | { 28 | if (need_called_) { 29 | need_called_ = FALSE; 30 | OnCall(); 31 | } 32 | } 33 | virtual void OnCall() = 0; 34 | protected: 35 | BOOL need_called_; 36 | }; 37 | 38 | //MessageTxCallback管理类 39 | class MessageTxCallbackManager 40 | { 41 | public: 42 | void AddMessageTxCallback(MessageTxCallback *callback) 43 | { 44 | callbacks_.push_back(callback); 45 | } 46 | 47 | void Call() 48 | { 49 | for (auto iter = callbacks_.begin(); iter != callbacks_.end(); ++iter) { 50 | if ((*iter) != NULL) { 51 | (*iter)->Call(); 52 | } 53 | } 54 | } 55 | 56 | static MessageTxCallbackManager& GetInstance() 57 | { 58 | static MessageTxCallbackManager inst; 59 | return inst; 60 | } 61 | 62 | protected: 63 | std::vector callbacks_; 64 | }; 65 | 66 | enum TestMode 67 | { 68 | TEST_MODE_NORMAL = 0, /*普通测试模式*/ 69 | TEST_MODE_PRESS /*压力测试模式*/ 70 | }; 71 | 72 | // 发送报文基类 73 | class MessageTx 74 | { 75 | public: 76 | MessageTx(UINT id, UINT len); 77 | 78 | void SetCallback(MessageTxCallback *callback) 79 | { 80 | callbacks_.push_back(callback); 81 | MessageTxCallbackManager::GetInstance().AddMessageTxCallback(callback); 82 | } 83 | 84 | void SetNormalTestMode(); 85 | void SetPressTestMode(); 86 | TestMode GetTestMode() const; 87 | 88 | void PreTransmit(); 89 | 90 | protected: 91 | void Transmit(); 92 | 93 | public: 94 | CAN_OBJ can_obj_; 95 | CAN_OBJ press_can_obj_; 96 | CAN_OBJ *cur_can_obj_; 97 | 98 | std::vectorcallbacks_; 99 | TestMode mode_; 100 | }; 101 | 102 | class MessagePeriodTx : public MessageTx 103 | { 104 | public: 105 | MessagePeriodTx(UINT id, UINT len, UINT period, UINT offset); 106 | 107 | void OnElapsed(UINT elapsed); 108 | void Start(); 109 | void Stop(); 110 | 111 | UINT GetPeriod() const 112 | { 113 | return period_; 114 | } 115 | 116 | void SetPeriod(UINT period) 117 | { 118 | period_ = period; 119 | } 120 | 121 | protected: 122 | UINT period_; 123 | UINT tick_count_; 124 | int offset_; 125 | BOOL started_; 126 | }; 127 | 128 | class MessagePeriodTxMgr 129 | { 130 | public: 131 | static MessagePeriodTxMgr& GetInstance(); 132 | void AddMessagePeriodTx(UINT id, UINT len, UINT period, UINT offset); 133 | void OnElapsed(UINT elapsed); 134 | 135 | void Start(); 136 | void Start(UINT can_id); 137 | void Stop(); 138 | void Stop(UINT can_id); 139 | 140 | void AddCallback(UINT can_id, MessageTxCallback *callback); 141 | 142 | BYTE* GetCanData(UINT can_id) 143 | { 144 | auto iter = message_period_txs_.find(can_id); 145 | if (iter != message_period_txs_.end()) { 146 | return iter->second.can_obj_.Data; 147 | } 148 | return NULL; 149 | } 150 | 151 | CAN_OBJ* GetCanObj(UINT can_id) 152 | { 153 | auto iter = message_period_txs_.find(can_id); 154 | if (iter != message_period_txs_.end()) { 155 | return &iter->second.can_obj_; 156 | } 157 | return NULL; 158 | } 159 | 160 | BOOL IsStarted() const 161 | { 162 | return started_; 163 | } 164 | 165 | void clear() 166 | { 167 | message_period_txs_.clear(); 168 | } 169 | 170 | void SetNormalTestMode(); 171 | void SetPressTestMode(); 172 | 173 | TestMode GetTestMode() const 174 | { 175 | return mode_; 176 | } 177 | 178 | std::vector GetAllCanIds() const 179 | { 180 | std::vector ids; 181 | for (auto iter = message_period_txs_.begin(); iter != message_period_txs_.end(); ++iter) { 182 | ids.push_back(iter->first); 183 | } 184 | return ids; 185 | } 186 | 187 | protected: 188 | std::map message_period_txs_; 189 | 190 | BOOL started_; 191 | private: 192 | MessagePeriodTxMgr() : started_(FALSE), mode_(TEST_MODE_NORMAL) {} 193 | TestMode mode_; 194 | public: 195 | BOOL SetCanPeriod(UINT id, UINT period); 196 | BOOL GetCanPeriod(UINT id, UINT & period); 197 | }; 198 | 199 | class QTimerMessagePeriodTxMgr : public QObject 200 | { 201 | Q_OBJECT 202 | public: 203 | enum { 204 | MULTI_MEDIA_MESSAGE_PERIOD = 10, 205 | }; 206 | 207 | static QTimerMessagePeriodTxMgr &GetInstance() 208 | { 209 | static QTimerMessagePeriodTxMgr instance(MULTI_MEDIA_MESSAGE_PERIOD); 210 | return instance; 211 | } 212 | 213 | ~QTimerMessagePeriodTxMgr() 214 | { 215 | //Q_ASSERT(already_destroyed_ && "MultiMedia Must Destroyed"); 216 | if (timer_) { 217 | delete timer_; 218 | timer_ = NULL; 219 | } 220 | } 221 | 222 | operator bool() const 223 | { 224 | return (TRUE != already_destroyed_); 225 | } 226 | 227 | void Start() 228 | { 229 | message_period_tx_mgr_.Start(); 230 | } 231 | 232 | void Start(UINT can_id) 233 | { 234 | message_period_tx_mgr_.Start(can_id); 235 | } 236 | 237 | void Stop() 238 | { 239 | message_period_tx_mgr_.Stop(); 240 | } 241 | 242 | void Stop(UINT can_id) 243 | { 244 | message_period_tx_mgr_.Stop(can_id); 245 | } 246 | 247 | void Close() 248 | { 249 | Stop(); 250 | destroyed_ = TRUE; 251 | } 252 | 253 | protected: 254 | void Init(UINT period, UINT res); 255 | 256 | protected slots: 257 | void OnTimer(); 258 | 259 | private: 260 | UINT timer_res_; 261 | UINT timer_id_; 262 | BOOL destroyed_; 263 | BOOL already_destroyed_; 264 | UINT period_; 265 | QTimer *timer_; 266 | 267 | MessagePeriodTxMgr &message_period_tx_mgr_; 268 | 269 | private: 270 | QTimerMessagePeriodTxMgr(UINT period, UINT timer_res=1) : message_period_tx_mgr_(MessagePeriodTxMgr::GetInstance()) 271 | { 272 | Init(period, timer_res); 273 | } 274 | }; 275 | 276 | struct RxMessageListener 277 | { 278 | virtual void OnMessageChanged(const CAN_OBJ &message) = 0; 279 | virtual ~RxMessageListener() {} 280 | }; 281 | 282 | class RxMessageDispatcher 283 | { 284 | public: 285 | ~RxMessageDispatcher(); 286 | 287 | void AddRxMessageListener(RxMessageListener *listener, BOOL auto_delete); 288 | void RemoveRxMessageListener(RxMessageListener *listener); 289 | void Dispatch(const CAN_OBJ &message); 290 | 291 | static RxMessageDispatcher& GetInstance() 292 | { 293 | static RxMessageDispatcher instance; 294 | return instance; 295 | } 296 | 297 | protected: 298 | std::map items_; 299 | }; 300 | 301 | class RxMessageFilter 302 | { 303 | public: 304 | enum { 305 | FILTER_BIT_VALUE = 0, 306 | }; 307 | 308 | enum EndianKind{ 309 | INTEL_LSB = 0, 310 | INTEL_MSB, 311 | MOTOROLA_LSB, 312 | MOTOROLA_MSB 313 | }; 314 | 315 | struct FilterRange { 316 | UINT start_pos; 317 | UINT len; 318 | EndianKind kind; 319 | }; 320 | 321 | void AddFilterRange(UINT start_pos, UINT len, EndianKind kind); 322 | void Filter(CAN_OBJ &obj); 323 | 324 | protected: 325 | void doFilter(CAN_OBJ &obj, UINT start_pos, UINT len, EndianKind kind); 326 | void doIntelLsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 327 | void doIntelMsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 328 | void doMotorolaLsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 329 | void doMotorolaMsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 330 | 331 | protected: 332 | std::vector filter_ranges_; 333 | }; 334 | 335 | class RxMessageFilterManager 336 | { 337 | public: 338 | ~RxMessageFilterManager(); 339 | void AddRxMessageFilter(UINT can_id, RxMessageFilter *filter); 340 | void RemoveRxMessageFilter(UINT can_id); 341 | void Filter(CAN_OBJ &obj); 342 | 343 | static RxMessageFilterManager& GetInstance() 344 | { 345 | static RxMessageFilterManager instance; 346 | return instance; 347 | } 348 | 349 | protected: 350 | std::map filters_; 351 | }; 352 | 353 | #define BEGIN_RX_MESSAGE_FILTER(__can_id) {\ 354 | auto can_id = __can_id;\ 355 | auto *filter = new RxMessageFilter;\ 356 | RxMessageFilterManager::GetInstance().AddRxMessageFilter(can_id, filter); 357 | 358 | #define RX_MESSAGE_FILTER_ITEM(start_pos, len, kind)\ 359 | filter->AddFilterRange(start_pos, len, RxMessageFilter::kind); 360 | 361 | #define END_RX_MESSAGE_FILTER() } 362 | 363 | class RxMessageMonitor 364 | { 365 | public: 366 | RxMessageMonitor(); 367 | 368 | static RxMessageMonitor& GetInstance() 369 | { 370 | static RxMessageMonitor instance; 371 | return instance; 372 | } 373 | 374 | void Receive(CAN_OBJ &obj); 375 | void DispatchObj(const CAN_OBJ &obj); 376 | void Reset(); 377 | 378 | protected: 379 | RxMessageDispatcher &dispatcher_; 380 | RxMessageFilterManager &filter_; 381 | std::map objs_; 382 | }; 383 | -------------------------------------------------------------------------------- /ecanspy3/can/Core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "ECanVci.H" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | ///////////////////////////////////////////////////////////////////////////////////////// 12 | // 周期性发送报文 13 | //////////////////////////////////////////////////////////////////////////////////////// 14 | 15 | // 这个类当需要调用的时候调用, 16 | // 当前用于,当报文发送完毕的时候,调用 17 | class MessageTxCallback 18 | { 19 | public: 20 | MessageTxCallback(BOOL need_called = FALSE) : need_called_(need_called) {} 21 | 22 | void NeedCalled() 23 | { 24 | need_called_ = TRUE; 25 | } 26 | 27 | void Call() 28 | { 29 | if (need_called_) { 30 | need_called_ = FALSE; 31 | OnCall(); 32 | } 33 | } 34 | virtual void OnCall() = 0; 35 | protected: 36 | BOOL need_called_; 37 | }; 38 | 39 | //MessageTxCallback管理类 40 | class MessageTxCallbackManager 41 | { 42 | public: 43 | void AddMessageTxCallback(MessageTxCallback *callback) 44 | { 45 | callbacks_.push_back(callback); 46 | } 47 | 48 | void Call() 49 | { 50 | for (auto iter = callbacks_.begin(); iter != callbacks_.end(); ++iter) { 51 | if ((*iter) != NULL) { 52 | (*iter)->Call(); 53 | } 54 | } 55 | } 56 | 57 | static MessageTxCallbackManager& GetInstance() 58 | { 59 | static MessageTxCallbackManager inst; 60 | return inst; 61 | } 62 | 63 | protected: 64 | std::vector callbacks_; 65 | }; 66 | 67 | enum TestMode 68 | { 69 | TEST_MODE_NORMAL = 0, /*普通测试模式*/ 70 | TEST_MODE_PRESS /*压力测试模式*/ 71 | }; 72 | 73 | // 发送报文基类 74 | class MessageTx 75 | { 76 | public: 77 | MessageTx(UINT id, UINT len); 78 | virtual ~MessageTx() {} 79 | 80 | void SetCallback(MessageTxCallback *callback) 81 | { 82 | callbacks_.push_back(callback); 83 | MessageTxCallbackManager::GetInstance().AddMessageTxCallback(callback); 84 | } 85 | 86 | void SetNormalTestMode(); 87 | void SetPressTestMode(); 88 | TestMode GetTestMode() const; 89 | 90 | void PreTransmit(); 91 | 92 | virtual void SetContTxCount(UINT cont_tx_cnt) 93 | { 94 | (void)cont_tx_cnt; 95 | } 96 | 97 | protected: 98 | void Transmit(); 99 | 100 | public: 101 | CAN_OBJ can_obj_; 102 | CAN_OBJ press_can_obj_; 103 | CAN_OBJ *cur_can_obj_; 104 | 105 | std::vectorcallbacks_; 106 | TestMode mode_; 107 | }; 108 | 109 | 110 | class MessagePeriodTx : public MessageTx 111 | { 112 | public: 113 | MessagePeriodTx(UINT id, UINT len, UINT period, UINT offset); 114 | virtual ~MessagePeriodTx() {} 115 | 116 | void OnElapsed(UINT elapsed); 117 | void Start(); 118 | void Stop(); 119 | 120 | UINT GetPeriod() const 121 | { 122 | return period_; 123 | } 124 | 125 | void SetPeriod(UINT period) 126 | { 127 | period_ = period; 128 | } 129 | 130 | protected: 131 | virtual void OnPostTransmit() {} 132 | 133 | protected: 134 | UINT period_; 135 | UINT tick_count_; 136 | int offset_; 137 | BOOL started_; 138 | }; 139 | 140 | class MessageTriggerTx: public MessagePeriodTx 141 | { 142 | public: 143 | enum { 144 | DEFAULT_CONT_TX_CNT = 3, 145 | }; 146 | 147 | MessageTriggerTx(UINT id, UINT len, UINT period, UINT offset, 148 | UINT cont_tx_cnt = DEFAULT_CONT_TX_CNT); 149 | void SetContTxCount(UINT cont_tx_cnt); 150 | 151 | protected: 152 | void OnPostTransmit() override; 153 | 154 | private: 155 | UINT cont_tx_cnt_; //!< 连续发送次数 156 | UINT curr_tx_cnt_; 157 | }; 158 | 159 | class MessagePeriodTxMgr 160 | { 161 | public: 162 | ~MessagePeriodTxMgr(); 163 | static MessagePeriodTxMgr& GetInstance(); 164 | 165 | void AddMessagePeriodTx(UINT id, UINT len, UINT period, UINT offset); 166 | void AddMessageTriggerTx(UINT id, UINT len, UINT period, UINT offset, 167 | UINT cont_tx_cnt = MessageTriggerTx::DEFAULT_CONT_TX_CNT); 168 | 169 | void OnElapsed(UINT elapsed); 170 | 171 | void Start(); 172 | void Start(UINT can_id); 173 | void Stop(); 174 | void Stop(UINT can_id); 175 | 176 | void SetContTxCount(UINT cont_tx_cnt); 177 | void SetContTxCount(UINT can_id, UINT cont_tx_cnt); 178 | 179 | void AddCallback(UINT can_id, MessageTxCallback *callback); 180 | 181 | BYTE* GetCanData(UINT can_id) 182 | { 183 | auto iter = message_period_txs_.find(can_id); 184 | if (iter != message_period_txs_.end()) { 185 | return iter->second->can_obj_.Data; 186 | } 187 | return NULL; 188 | } 189 | 190 | CAN_OBJ* GetCanObj(UINT can_id) 191 | { 192 | auto iter = message_period_txs_.find(can_id); 193 | if (iter != message_period_txs_.end()) { 194 | return &iter->second->can_obj_; 195 | } 196 | return NULL; 197 | } 198 | 199 | BOOL IsStarted() const 200 | { 201 | return started_; 202 | } 203 | 204 | void clear(); 205 | 206 | std::size_t size() const; 207 | 208 | void SetNormalTestMode(); 209 | void SetPressTestMode(); 210 | 211 | TestMode GetTestMode() const 212 | { 213 | return mode_; 214 | } 215 | 216 | std::vector GetAllCanIds() const 217 | { 218 | std::vector ids; 219 | for (auto iter = message_period_txs_.begin(); iter != message_period_txs_.end(); ++iter) { 220 | ids.push_back(iter->first); 221 | } 222 | return ids; 223 | } 224 | 225 | protected: 226 | void SafeRemove(UINT can_id); 227 | 228 | protected: 229 | std::map message_period_txs_; 230 | 231 | BOOL started_; 232 | private: 233 | MessagePeriodTxMgr() : started_(FALSE), mode_(TEST_MODE_NORMAL) {} 234 | TestMode mode_; 235 | public: 236 | BOOL SetCanPeriod(UINT id, UINT period); 237 | BOOL GetCanPeriod(UINT id, UINT & period); 238 | }; 239 | 240 | class MultiMediaMessagePeriodTxMgr 241 | { 242 | public: 243 | enum { 244 | MULTI_MEDIA_MESSAGE_PERIOD = 10, 245 | }; 246 | 247 | static MultiMediaMessagePeriodTxMgr &GetInstance() 248 | { 249 | static MultiMediaMessagePeriodTxMgr instance(MULTI_MEDIA_MESSAGE_PERIOD); 250 | return instance; 251 | } 252 | 253 | ~MultiMediaMessagePeriodTxMgr() 254 | { 255 | //Q_ASSERT(already_destroyed_ && "MultiMedia Must Destroyed"); 256 | } 257 | 258 | operator bool() const 259 | { 260 | return (TRUE != already_destroyed_); 261 | } 262 | 263 | void Start() 264 | { 265 | message_period_tx_mgr_.Start(); 266 | } 267 | 268 | void Start(UINT can_id) 269 | { 270 | message_period_tx_mgr_.Start(can_id); 271 | } 272 | 273 | void Stop() 274 | { 275 | message_period_tx_mgr_.Stop(); 276 | } 277 | 278 | void Stop(UINT can_id) 279 | { 280 | message_period_tx_mgr_.Stop(can_id); 281 | } 282 | 283 | void Close() 284 | { 285 | Stop(); 286 | destroyed_ = TRUE; 287 | } 288 | 289 | static void CALLBACK TimeCallback(UINT uTimerID, UINT uMsg, DWORD_PTR dwUser, DWORD_PTR dw1, DWORD_PTR dw2); 290 | 291 | protected: 292 | void Init(UINT period, UINT res); 293 | void OnTimer(); 294 | 295 | private: 296 | UINT timer_res_; 297 | UINT timer_id_; 298 | BOOL destroyed_; 299 | BOOL already_destroyed_; 300 | UINT period_; 301 | 302 | MessagePeriodTxMgr &message_period_tx_mgr_; 303 | 304 | private: 305 | MultiMediaMessagePeriodTxMgr(UINT period, UINT timer_res=1) : message_period_tx_mgr_(MessagePeriodTxMgr::GetInstance()) 306 | { 307 | Init(period, timer_res); 308 | } 309 | }; 310 | 311 | struct RxMessageListener 312 | { 313 | virtual void OnMessageChanged(const CAN_OBJ &message) = 0; 314 | virtual ~RxMessageListener() {} 315 | }; 316 | 317 | class RxMessageDispatcher 318 | { 319 | public: 320 | ~RxMessageDispatcher(); 321 | 322 | void AddRxMessageListener(RxMessageListener *listener, BOOL auto_delete); 323 | void RemoveRxMessageListener(RxMessageListener *listener); 324 | void Dispatch(const CAN_OBJ &message); 325 | 326 | static RxMessageDispatcher& GetInstance() 327 | { 328 | static RxMessageDispatcher instance; 329 | return instance; 330 | } 331 | 332 | protected: 333 | std::map items_; 334 | }; 335 | 336 | class RxMessageFilter 337 | { 338 | public: 339 | enum { 340 | FILTER_BIT_VALUE = 0, 341 | }; 342 | 343 | enum EndianKind{ 344 | INTEL_LSB = 0, 345 | INTEL_MSB, 346 | MOTOROLA_LSB, 347 | MOTOROLA_MSB 348 | }; 349 | 350 | struct FilterRange { 351 | UINT start_pos; 352 | UINT len; 353 | EndianKind kind; 354 | }; 355 | 356 | void AddFilterRange(UINT start_pos, UINT len, EndianKind kind); 357 | void Filter(CAN_OBJ &obj); 358 | 359 | protected: 360 | void doFilter(CAN_OBJ &obj, UINT start_pos, UINT len, EndianKind kind); 361 | void doIntelLsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 362 | void doIntelMsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 363 | void doMotorolaLsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 364 | void doMotorolaMsbFilter(CAN_OBJ &obj, UINT start_pos, UINT len); 365 | 366 | protected: 367 | std::vector filter_ranges_; 368 | }; 369 | 370 | class RxMessageFilterManager 371 | { 372 | public: 373 | ~RxMessageFilterManager(); 374 | void AddRxMessageFilter(UINT can_id, RxMessageFilter *filter); 375 | void RemoveRxMessageFilter(UINT can_id); 376 | void Filter(CAN_OBJ &obj); 377 | 378 | static RxMessageFilterManager& GetInstance() 379 | { 380 | static RxMessageFilterManager instance; 381 | return instance; 382 | } 383 | 384 | protected: 385 | std::map filters_; 386 | }; 387 | 388 | #define BEGIN_RX_MESSAGE_FILTER(__can_id) {\ 389 | auto can_id = __can_id;\ 390 | auto *filter = new RxMessageFilter;\ 391 | RxMessageFilterManager::GetInstance().AddRxMessageFilter(can_id, filter); 392 | 393 | #define RX_MESSAGE_FILTER_ITEM(start_pos, len, kind)\ 394 | filter->AddFilterRange(start_pos, len, RxMessageFilter::kind); 395 | 396 | #define END_RX_MESSAGE_FILTER() } 397 | 398 | class RxMessageMonitor 399 | { 400 | public: 401 | RxMessageMonitor(); 402 | 403 | static RxMessageMonitor& GetInstance() 404 | { 405 | static RxMessageMonitor instance; 406 | return instance; 407 | } 408 | 409 | void Receive(CAN_OBJ &obj); 410 | void DispatchObj(const CAN_OBJ &obj); 411 | void Reset(); 412 | 413 | protected: 414 | RxMessageDispatcher &dispatcher_; 415 | RxMessageFilterManager &filter_; 416 | std::map objs_; 417 | }; 418 | -------------------------------------------------------------------------------- /ecanspy3/can/ECanVci.H: -------------------------------------------------------------------------------- 1 | #ifndef _V_ECANVCI_H_ 2 | #define _V_ECANVCI_H_ 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | #define Dll_EXPORTS 11 | 12 | //接口卡类型定义 13 | 14 | #define USBCAN1 3 15 | #define USBCAN2 4 16 | 17 | //CAN错误码 18 | #define ERR_CAN_OVERFLOW 0x0001 //CAN控制器内部FIFO溢出 19 | #define ERR_CAN_ERRALARM 0x0002 //CAN控制器错误报警 20 | #define ERR_CAN_PASSIVE 0x0004 //CAN控制器消极错误 21 | #define ERR_CAN_LOSE 0x0008 //CAN控制器仲裁丢失 22 | #define ERR_CAN_BUSERR 0x0010 //CAN控制器总线错误 23 | #define ERR_CAN_REG_FULL 0x0020 //CAN接收寄存器满 24 | #define ERR_CAN_REG_OVER 0x0040 //CAN接收寄存器溢出 25 | #define ERR_CAN_ZHUDONG 0x0080 //CAN控制器主动错误 26 | 27 | //通用错误码 28 | #define ERR_DEVICEOPENED 0x0100 //设备已经打开 29 | #define ERR_DEVICEOPEN 0x0200 //打开设备错误 30 | #define ERR_DEVICENOTOPEN 0x0400 //设备没有打开 31 | #define ERR_BUFFEROVERFLOW 0x0800 //缓冲区溢出 32 | #define ERR_DEVICENOTEXIST 0x1000 //此设备不存在 33 | #define ERR_LOADKERNELDLL 0x2000 //装载动态库失败 34 | #define ERR_CMDFAILED 0x4000 //执行命令失败错误码 35 | #define ERR_BUFFERCREATE 0x8000 //内存不足 36 | 37 | 38 | //函数调用返回状态值 39 | #define STATUS_OK 1 40 | #define STATUS_ERR 0 41 | 42 | #define CMD_DESIP 0 43 | #define CMD_DESPORT 1 44 | #define CMD_CHGDESIPANDPORT 2 45 | 46 | //#define NON_WIN32 47 | 48 | #ifdef NON_WIN32 49 | #include 50 | 51 | typedef uint16_t USHORT; 52 | typedef uint8_t BYTE; 53 | typedef char CHAR; 54 | typedef uint32_t UINT; 55 | typedef uint8_t UCHAR; 56 | typedef uint32_t DWORD; 57 | typedef void* PVOID; 58 | typedef uint32_t ULONG; 59 | typedef uint8_t BOOL; 60 | typedef int32_t INT; 61 | 62 | #ifndef TRUE 63 | #define TRUE (1) 64 | #endif 65 | 66 | #ifndef FALSE 67 | #define FALSE (0) 68 | #endif 69 | 70 | #ifndef _T 71 | #define _T(m) QObject::tr(m) 72 | #endif 73 | #endif //NON_WIN32 74 | 75 | //1.ZLGCAN系列接口卡信息的数据类型。 76 | typedef struct _BOARD_INFO{ 77 | USHORT hw_Version; 78 | USHORT fw_Version; 79 | USHORT dr_Version; 80 | USHORT in_Version; 81 | USHORT irq_Num; 82 | BYTE can_Num; 83 | CHAR str_Serial_Num[20]; 84 | CHAR str_hw_Type[40]; 85 | USHORT Reserved[4]; 86 | } BOARD_INFO,*P_BOARD_INFO; 87 | 88 | //2.定义CAN信息帧的数据类型。 89 | typedef struct _CAN_OBJ{ 90 | UINT ID; 91 | UINT TimeStamp; 92 | BYTE TimeFlag; 93 | BYTE SendType; 94 | BYTE RemoteFlag;//是否是远程帧 95 | BYTE ExternFlag;//是否是扩展帧 96 | BYTE DataLen; 97 | BYTE Data[8]; 98 | BYTE Reserved[3]; 99 | }CAN_OBJ,*P_CAN_OBJ; 100 | 101 | //3.定义CAN控制器状态的数据类型。 102 | typedef struct _CAN_STATUS{ 103 | UCHAR ErrInterrupt; 104 | UCHAR regMode; 105 | UCHAR regStatus; 106 | UCHAR regALCapture; 107 | UCHAR regECCapture; 108 | UCHAR regEWLimit; 109 | UCHAR regRECounter; 110 | UCHAR regTECounter; 111 | DWORD Reserved; 112 | }CAN_STATUS,*P_CAN_STATUS; 113 | 114 | //4.定义错误信息的数据类型。 115 | typedef struct _ERR_INFO{ 116 | UINT ErrCode; 117 | BYTE Passive_ErrData[3]; 118 | BYTE ArLost_ErrData; 119 | } ERR_INFO,*P_ERR_INFO; 120 | 121 | //5.定义初始化CAN的数据类型 122 | typedef struct _INIT_CONFIG{ 123 | DWORD AccCode; 124 | DWORD AccMask; 125 | DWORD Reserved; 126 | UCHAR Filter; 127 | UCHAR Timing0; 128 | UCHAR Timing1; 129 | UCHAR Mode; 130 | }INIT_CONFIG,*P_INIT_CONFIG; 131 | 132 | typedef struct _tagChgDesIPAndPort 133 | { 134 | char szpwd[10]; 135 | char szdesip[20]; 136 | int desport; 137 | }CHGDESIPANDPORT; 138 | 139 | ///////// new add struct for filter ///////// 140 | typedef struct _FILTER_RECORD{ 141 | DWORD ExtFrame; //是否为扩展帧 142 | DWORD Start; 143 | DWORD End; 144 | }FILTER_RECORD,*P_FILTER_RECORD; 145 | 146 | 147 | #ifdef Dll_EXPORTS 148 | #define DllAPI __declspec(dllexport) 149 | #else 150 | #define DllAPI __declspec(dllimport) 151 | 152 | #endif 153 | 154 | #define EXTERNC extern "C" 155 | #define CALL __stdcall//__cdecl 156 | 157 | //extern "C" 158 | //{ 159 | 160 | EXTERNC DllAPI DWORD CALL OpenDevice(DWORD DeviceType,DWORD DeviceInd,DWORD Reserved); 161 | EXTERNC DllAPI DWORD CALL CloseDevice(DWORD DeviceType,DWORD DeviceInd); 162 | 163 | EXTERNC DllAPI DWORD CALL InitCAN(DWORD DeviceType, DWORD DeviceInd, DWORD CANInd, P_INIT_CONFIG pInitConfig); 164 | 165 | EXTERNC DllAPI DWORD CALL ReadBoardInfo(DWORD DeviceType,DWORD DeviceInd,P_BOARD_INFO pInfo); 166 | EXTERNC DllAPI DWORD CALL ReadErrInfo(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,P_ERR_INFO pErrInfo); 167 | EXTERNC DllAPI DWORD CALL ReadCANStatus(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,P_CAN_STATUS pCANStatus); 168 | 169 | EXTERNC DllAPI DWORD CALL GetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData); 170 | EXTERNC DllAPI DWORD CALL SetReference(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,DWORD RefType,PVOID pData); 171 | 172 | EXTERNC DllAPI ULONG CALL GetReceiveNum(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd); 173 | EXTERNC DllAPI DWORD CALL ClearBuffer(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd); 174 | 175 | EXTERNC DllAPI DWORD CALL StartCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd); 176 | EXTERNC DllAPI DWORD CALL ResetCAN(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd); 177 | 178 | EXTERNC DllAPI ULONG CALL Transmit(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,P_CAN_OBJ pSend,ULONG Len); 179 | EXTERNC DllAPI ULONG CALL Receive(DWORD DeviceType,DWORD DeviceInd,DWORD CANInd,P_CAN_OBJ pReceive,ULONG Len,INT WaitTime); 180 | 181 | #ifdef __cplusplus 182 | } 183 | #endif 184 | 185 | 186 | #endif 187 | -------------------------------------------------------------------------------- /ecanspy3/can/ECanVci.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/can/ECanVci.dll -------------------------------------------------------------------------------- /ecanspy3/can/ECanVci.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/can/ECanVci.lib -------------------------------------------------------------------------------- /ecanspy3/can/UsbCanUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "UsbCanUtil.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | UsbCanUtil::UsbCanUtil() : QObject(NULL), opened_(FALSE), dev_ind_(0), dev_type_(USBCAN1) 9 | { 10 | INIT_CONFIG init_config = DEFAULT_VCI_INIT_CONFIG; 11 | init_config_ = init_config; 12 | 13 | baurate = USB_CAN_BAURATE_500KBPS; 14 | initBaurateMap(); 15 | } 16 | 17 | void UsbCanUtil::initBaurateMap() 18 | { 19 | AddBaurate(USB_CAN_BAURATE_5KBPS, BAURATE_5KBPS); 20 | AddBaurate(USB_CAN_BAURATE_10KBPS, BAURATE_10KBPS); 21 | AddBaurate(USB_CAN_BAURATE_20KBPS, BAURATE_20KBPS); 22 | AddBaurate(USB_CAN_BAURATE_40KBPS, BAURATE_40KBPS); 23 | AddBaurate(USB_CAN_BAURATE_80KBPS, BAURATE_80KBPS); 24 | AddBaurate(USB_CAN_BAURATE_100KBPS, BAURATE_100KBSP); 25 | AddBaurate(USB_CAN_BAURATE_125KBPS, BAURATE_125KBPS); 26 | AddBaurate(USB_CAN_BAURATE_200KBPS, BAURATE_200KBPS); 27 | AddBaurate(USB_CAN_BAURATE_250KBPS, BAURATE_250KBPS); 28 | AddBaurate(USB_CAN_BAURATE_400KBPS, BAURATE_400KBPS); 29 | AddBaurate(USB_CAN_BAURATE_500KBPS, BAURATE_500KBPS); 30 | AddBaurate(USB_CAN_BAURATE_666KBPS, BAURATE_666KBPS); 31 | AddBaurate(USB_CAN_BAURATE_800KBPS, BAURATE_800KBPS); 32 | AddBaurate(USB_CAN_BAURATE_1000KBPS, BAURATE_1000KBPS); 33 | } 34 | 35 | UsbCanUtil::~UsbCanUtil( void ) 36 | { 37 | Close(); 38 | } 39 | 40 | UsbCanUtil& UsbCanUtil::GetInstance() 41 | { 42 | static UsbCanUtil instance; 43 | return instance; 44 | } 45 | 46 | QString UsbCanUtil::GetBaurate() const 47 | { 48 | return baurate; 49 | } 50 | 51 | void UsbCanUtil::AddBaurate(QString name, UsbCanUtil::Baurate baurate) 52 | { 53 | baurateMap.insert(name, baurate); 54 | supportedBaurateList.append(name); 55 | } 56 | 57 | QList UsbCanUtil::GetSupportedBaurateList() 58 | { 59 | return this->supportedBaurateList; 60 | } 61 | 62 | void UsbCanUtil::SetBaurate(QString baurate) 63 | { 64 | if (this->baurate == baurate) return; 65 | 66 | auto iter = this->baurateMap.find(baurate); 67 | if (iter != baurateMap.end()) { 68 | this->baurate = baurate; 69 | } else { 70 | this->baurate = USB_CAN_BAURATE_DEFAULT; 71 | } 72 | 73 | USHORT uBaurate = this->baurateMap.value(this->baurate); 74 | init_config_.Timing0 = (uBaurate>>8); 75 | init_config_.Timing1 = (uBaurate&0xff); 76 | 77 | if (opened_) { 78 | Open(dev_type_); 79 | } 80 | } 81 | 82 | bool UsbCanUtil::ReadCANStatus(P_CAN_STATUS can_status) 83 | { 84 | if (opened_ && can_status) { 85 | return (1 == ::ReadCANStatus(dev_type_, dev_ind_, 0, can_status)); 86 | } else { 87 | return false; 88 | } 89 | } 90 | 91 | BOOL UsbCanUtil::Open( DWORD device_type, INIT_CONFIG *init_config ) 92 | { 93 | if (opened_) { 94 | Close(); 95 | } 96 | 97 | dev_type_ = device_type; 98 | 99 | if (init_config) { 100 | init_config_ = *init_config; 101 | } 102 | 103 | if (FALSE == OpenDevice(MAX_DEV_IND)) { 104 | qDebug() << QObject::tr("Can设备打开失败"); 105 | QMessageBox::critical(NULL, QObject::tr("CAN错误"), tr("CAN设备打开失败")); 106 | return FALSE; 107 | } 108 | 109 | if (::InitCAN(dev_type_, dev_ind_, 0, &init_config_) != STATUS_OK) { 110 | qDebug() << QObject::tr("CAN初始化失败"); 111 | QMessageBox::critical(NULL, QObject::tr("CAN错误"), tr("CAN设备初始化失败")); 112 | return FALSE; 113 | } 114 | 115 | if (::ClearBuffer(dev_type_, dev_ind_, 0) != STATUS_OK) { 116 | qDebug() << QObject::tr("清除CAN缓冲区失败"); 117 | QMessageBox::critical(NULL, QObject::tr("CAN错误"), tr("CAN设备清除缓冲区失败")); 118 | return FALSE; 119 | } 120 | 121 | if (::StartCAN(dev_type_, dev_ind_, 0)) { 122 | opened_ = TRUE; 123 | qDebug() << QObject::tr("USB CAN open successfully!\n"); 124 | } else { 125 | qDebug() << QObject::tr("CAN启动失败"); 126 | QMessageBox::critical(NULL, QObject::tr("CAN错误"), tr("CAN设备启动失败")); 127 | return FALSE; 128 | } 129 | 130 | if (opened_) { 131 | emit openCan(); 132 | } 133 | 134 | return TRUE; 135 | } 136 | 137 | ULONG UsbCanUtil::Close() 138 | { 139 | if (opened_) { 140 | opened_ = FALSE; 141 | ULONG ret = CloseDevice(dev_ind_, dev_ind_); 142 | emit closeCan(); 143 | return ret; 144 | } 145 | return 0; 146 | } 147 | 148 | BOOL UsbCanUtil::IsOpened() const 149 | { 150 | return opened_; 151 | } 152 | 153 | #if 0 154 | static void logErr(const ERR_INFO err) 155 | { 156 | QString errStr(_T("[")); 157 | static USHORT errCodeTable[] = {0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080}; 158 | static QString errDescTable[] = {_T("设备已经打开"), _T("设备打开错误"), _T("设备没有打开"), _T("缓冲区溢出"), _T("此设备不存在"), 159 | _T("装载动态库失败"), _T("表示为执行命令失败错误"), _T("内存不足"), _T("CAN控制器内部FIFO溢出"), 160 | _T("CAN控制器错误报警"), _T("CAN控制器消极错误"), _T("CAN控制器仲裁丢失"), _T("CAN控制器总线错误"), _T("CAN接收寄存器满"), 161 | _T("CAN接收寄存器溢出"), _T("CAN控制器主动错误")}; 162 | for (int i = 0; i < (int)(sizeof(errCodeTable) / sizeof(USHORT)); i++) { 163 | if (errCodeTable[i] & err.ErrCode) { 164 | errStr += errDescTable[i]; 165 | errStr += _T(","); 166 | } 167 | } 168 | errStr += _T("\n"); 169 | qDebug() << errStr; 170 | } 171 | #endif 172 | 173 | ULONG UsbCanUtil::Send(CAN_OBJ & can_obj ) 174 | { 175 | USB_CAN_NOT_OPENED_RETURN_VALUE(ERR_CAN_NOT_OPENED); 176 | ULONG ret = ::Transmit(dev_ind_, dev_ind_, 0, &can_obj, 1); 177 | 178 | if (ret != 1) { 179 | ERR_INFO err; 180 | if (ReadErrInfo(dev_ind_, dev_ind_, 0, &err)) { 181 | //logErr(err); 182 | } 183 | } 184 | 185 | return ret; 186 | } 187 | 188 | ULONG UsbCanUtil::Send( CAN_OBJ *can_objs, UINT obj_len ) 189 | { 190 | USB_CAN_NOT_OPENED_RETURN_VALUE(ERR_CAN_NOT_OPENED); 191 | return ::Transmit(dev_type_, dev_ind_, 0, can_objs, obj_len); 192 | } 193 | 194 | DWORD UsbCanUtil::Receive( CAN_OBJ *can_obj, UINT obj_len /*= 1*/, INT wait_time) 195 | { 196 | USB_CAN_NOT_OPENED_RETURN_VALUE(ERR_CAN_NOT_OPENED); 197 | return ::Receive(dev_type_, dev_ind_, 0, can_obj, obj_len, wait_time); 198 | } 199 | 200 | DWORD UsbCanUtil::ReadErrorInfo( ERR_INFO &error_info ) 201 | { 202 | //USB_CAN_NOT_OPENED_RETURN_VALUE(ERR_CAN_NOT_OPENED); 203 | if (!opened_) return 0; 204 | return ReadErrInfo(dev_type_, dev_ind_, 0, &error_info); 205 | } 206 | 207 | BOOL UsbCanUtil::OpenDevice( UINT max_dev_ind ) 208 | { 209 | for (UINT i = 0; i < max_dev_ind; i++) { 210 | if (STATUS_OK == ::OpenDevice(dev_type_, i, 0)) { 211 | dev_ind_ = i; 212 | return TRUE; 213 | } 214 | } 215 | 216 | return FALSE; 217 | } 218 | -------------------------------------------------------------------------------- /ecanspy3/can/UsbCanUtil.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ECanVci.H" 4 | #include 5 | 6 | /*! 7 | * \brief 设备类型,对于测试软件就是使用USBCAN了 8 | */ 9 | #define USB_CAN_DEVICE_TYPE (USBCAN1) 10 | #define USB_CAN_DEVICE_INDEX (0) 11 | 12 | /*! 13 | * \brief 默认的VCI_CAN初始化设置 14 | */ 15 | #define DEFAULT_VCI_INIT_CONFIG {0x00000000, 0xffffffff, 0, 0x00, 0, 0x1c, 0x00} 16 | 17 | /*! 18 | * \brief 辅助宏,当USB CAN没有被打开的时候,直接返回 19 | */ 20 | #define USB_CAN_NOT_OPENED_RETURN() \ 21 | if(!UsbCanUtil::GetInstance().IsOpened()) { \ 22 | return; \ 23 | } 24 | 25 | /*! 26 | * \brief 辅助宏,当USB CAN没有被打开的时候,直接返回某个值 27 | */ 28 | #define USB_CAN_NOT_OPENED_RETURN_VALUE(val) \ 29 | if(!UsbCanUtil::GetInstance().IsOpened()) { \ 30 | return val; \ 31 | } 32 | 33 | /*! 34 | * \brief 常量,当返回ERR_CAN_NOT_OPENED,就表示USB CAN没有被打开 35 | */ 36 | #define ERR_CAN_NOT_OPENED (-1) 37 | 38 | #include 39 | #include 40 | 41 | #define USB_CAN_BAURATE_5KBPS QObject::tr("5KBPS") 42 | #define USB_CAN_BAURATE_10KBPS QObject::tr("10KBPS") 43 | #define USB_CAN_BAURATE_20KBPS QObject::tr("20KBPS") 44 | #define USB_CAN_BAURATE_40KBPS QObject::tr("40KBPS") 45 | #define USB_CAN_BAURATE_80KBPS QObject::tr("80KBPS") 46 | #define USB_CAN_BAURATE_100KBPS QObject::tr("100KBPS") 47 | #define USB_CAN_BAURATE_125KBPS QObject::tr("125KBPS") 48 | #define USB_CAN_BAURATE_200KBPS QObject::tr("200KBPS") 49 | #define USB_CAN_BAURATE_250KBPS QObject::tr("250KBPS") 50 | #define USB_CAN_BAURATE_400KBPS QObject::tr("400KBPS") 51 | #define USB_CAN_BAURATE_500KBPS QObject::tr("500KBPS") 52 | #define USB_CAN_BAURATE_666KBPS QObject::tr("666KBPS") 53 | #define USB_CAN_BAURATE_800KBPS QObject::tr("800KBPS") 54 | #define USB_CAN_BAURATE_1000KBPS QObject::tr("1000KBPS") 55 | #define USB_CAN_BAURATE_DEFAULT USB_CAN_BAURATE_500KBPS 56 | 57 | 58 | /*! 59 | * \brief VCI_CAN的一个辅助封装类 60 | */ 61 | class UsbCanUtil : public QObject 62 | { 63 | Q_OBJECT 64 | 65 | public: 66 | enum { 67 | MAX_DEV_IND = 10, 68 | }; 69 | 70 | /*! 71 | * \brief The Baurate enum 波特率 72 | */ 73 | enum Baurate{ 74 | BAURATE_5KBPS = 0xBFFF, 75 | BAURATE_10KBPS = 0x311C, 76 | BAURATE_20KBPS = 0x181C, 77 | BAURATE_40KBPS = 0x87FF, 78 | BAURATE_80KBPS = 0x83FF, 79 | BAURATE_100KBSP = 0x041C, 80 | BAURATE_125KBPS = 0x031C, 81 | BAURATE_200KBPS = 0x81FA, 82 | BAURATE_250KBPS = 0x011C, 83 | BAURATE_400KBPS = 0x80FA, 84 | BAURATE_500KBPS = 0x001C, 85 | BAURATE_666KBPS = 0x80B6, 86 | BAURATE_800KBPS = 0x0016, 87 | BAURATE_1000KBPS = 0x0014, 88 | BAURATE_DEFAULT = BAURATE_500KBPS, 89 | }; 90 | 91 | /*! 92 | * \brief 析构器 93 | */ 94 | ~UsbCanUtil(void); 95 | 96 | /*! 97 | * \brief 单利模式在c/c++中就相当于使用全局变量 98 | * 99 | * 总之要想获取UsbCanUtil就直接使用UsbCanUtil::GetInstance()就行 100 | */ 101 | static UsbCanUtil& GetInstance(); 102 | 103 | /*! 104 | * \brief getBaurate 获取波特率 105 | * \retval 波特率 106 | */ 107 | QString GetBaurate() const; 108 | 109 | /*! 110 | * \brief addBaurate 添加波特率 111 | * \param name 波特率名称 112 | * \param baurate 波特率值 113 | */ 114 | void AddBaurate(QString name, Baurate baurate); 115 | 116 | /*! 117 | * \brief getSupportedBaurate 获取支持的波特率 118 | * \retval 支持的波特率列表 119 | */ 120 | QList GetSupportedBaurateList(); 121 | 122 | /*! 123 | * \brief setBaurate 设置波特率 124 | * \param baurate 波特率,可以是: 125 | * + USB_CAN_BAURATE_5KBPS 126 | * + USB_CAN_BAURATE_10KBPS 127 | * + USB_CAN_BAURATE_20KBPS 128 | * + USB_CAN_BAURATE_40KBPS 129 | * + USB_CAN_BAURATE_80KBPS 130 | * + USB_CAN_BAURATE_100KBPS 131 | * + USB_CAN_BAURATE_125KBPS 132 | * + USB_CAN_BAURATE_200KBPS 133 | * + USB_CAN_BAURATE_250KBPS 134 | * + USB_CAN_BAURATE_400KBPS 135 | * + USB_CAN_BAURATE_500KBPS 136 | * + USB_CAN_BAURATE_666KBPS 137 | * + USB_CAN_BAURATE_800KBPS 138 | * + USB_CAN_BAURATE_1000KBPS 139 | */ 140 | void SetBaurate(QString baurate); 141 | 142 | /*! 143 | * \brief ReadCANStatus 读取CAN状态 144 | * \param can_status 接收CAN状态 145 | * \retval 成功与否 146 | */ 147 | bool ReadCANStatus(P_CAN_STATUS can_status); 148 | 149 | 150 | /*! 151 | * \brief 打开指定的VCI_CAN设备 152 | * 153 | * \param [in] device_type 设备类型,对于我们测试项目直接使用DEVICE_TYPE宏就可以,使用的是USB CAN 154 | * \param [in] init 打开VCI_CAN后要进行的配置,如果为NULL,那么默认的值就是使用DEFAULT_VCI_INIT_CONFIG 155 | * \retval 返回成功与否 156 | */ 157 | BOOL Open(DWORD device_type, INIT_CONFIG *init = NULL); 158 | 159 | /*! 160 | * \brief 关闭已经打开的CAN设备 161 | */ 162 | ULONG Close(); 163 | 164 | /*! 165 | * \brief 获取CAN设备是否打开 166 | */ 167 | BOOL IsOpened() const; 168 | 169 | /*! 170 | * \brief 使用CAN设备来发送数据 171 | * \param [in] can_obj 表示要发送的对象 172 | * \retval 返回STATUS_OK表示发送成功,否则表示发送失败 173 | */ 174 | ULONG Send(CAN_OBJ & can_obj); 175 | 176 | /*! 177 | * \brief 使用CAN设备来发送多组报文 178 | * \param [in] can_objs 要发送can报文的首地址 179 | * \param [in] obj_len CAN报文的数目 180 | */ 181 | ULONG Send(CAN_OBJ *can_objs, UINT obj_len); 182 | 183 | /*! 184 | * \brief 接收数据 185 | * \param [in] can_obj 表示要发送的对象指针 186 | * \param [in] obj_len 表示can_obj指针具有包含几个VCI_CAN_OBJ 187 | * \param [in] wait_time 188 | * \retval 返回值<=0表示发送失败,否则发送成功 189 | */ 190 | DWORD Receive(CAN_OBJ *can_obj, UINT obj_len = 1, INT wait_time = INT(0)); 191 | 192 | // 档接收失败的时候,使用此函数可以获取错误消息 193 | 194 | /*! 195 | * \brief 读取错误信息 196 | * \param [out] err_info 用来读取具体的错误信息 197 | * \retval 198 | */ 199 | DWORD ReadErrorInfo(ERR_INFO &error_info); 200 | 201 | signals: 202 | void openCan(); 203 | void closeCan(); 204 | 205 | private: 206 | UsbCanUtil(); //!< 单利模式的固定模式,禁止其他类直接构造这个对象 207 | 208 | void initBaurateMap(); 209 | 210 | BOOL opened_; //!< 保存CAN设备是否被打开的状态 211 | 212 | DWORD dev_ind_; //!< CAN设备索引,对于新版ECAN DLL非常重要 213 | DWORD dev_type_; //!< 设备类型 214 | 215 | INIT_CONFIG init_config_; //!< 初始化配置 216 | 217 | QMap baurateMap; //!< 管理波特率 218 | QList supportedBaurateList; //!< 支持的波特率列表 219 | 220 | QString baurate; 221 | 222 | BOOL OpenDevice(UINT max_dev_ind); //!< 用来打开可以可选的CAN设备,最多尝试max_dev_ind此 223 | }; 224 | 225 | -------------------------------------------------------------------------------- /ecanspy3/database/databasewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef DATABASEWIDGET_H 2 | #define DATABASEWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "dbctablemodel.h" 15 | 16 | class DatabaseWidget : public QWidget 17 | { 18 | Q_OBJECT 19 | public: 20 | enum { 21 | PAGE_INDEX_TRANSMIT = 0, 22 | PAGE_INDEX_RECEIVE, 23 | PAGE_INDEX_DATABASE 24 | }; 25 | 26 | explicit DatabaseWidget(QWidget *parent = nullptr); 27 | ~DatabaseWidget(); 28 | 29 | void initTableView(QVBoxLayout *layout); 30 | void intiContextMenu(); 31 | void initAllButtons(QVBoxLayout *layout); 32 | 33 | void resetSizePolicy(); 34 | 35 | DBCTableModel *getTransmitTableModel() 36 | { 37 | return this->transmitTableModel; 38 | } 39 | 40 | DBCTableModel *getReceiveTableModel() 41 | { 42 | return this->receiveTableModel; 43 | } 44 | 45 | DBCTableModel *getDatabaseTableModel() 46 | { 47 | return this->databaseTableModel; 48 | } 49 | 50 | int getCurrPageIndex() 51 | { 52 | return this->currPageIndex; 53 | } 54 | 55 | void setCurrPageIndex(int currPageIndex); 56 | 57 | QString getCurrDBCFileName() 58 | { 59 | return currDBCFileName; 60 | } 61 | 62 | void clear(); 63 | 64 | signals: 65 | 66 | // 刷新发送报文 67 | void refreshSendMessage(Document *doc, const QList &); 68 | void refreshDocument(Document *doc); 69 | 70 | public slots: 71 | void copyToTransmit(); 72 | void copyToReceive(); 73 | void copyTo(DBCTableModel *model); 74 | 75 | void deleteCurr(); 76 | 77 | void switchToTransmit(); 78 | void switchToReceive(); 79 | void switchToDatabase(); 80 | void buttonGroupClicked(QAbstractButton *button); 81 | 82 | void loadDBC(); 83 | void loadDBC(const QString &dbcFile); 84 | void loadDocument(Document *doc); 85 | 86 | 87 | void loadTransmitMsgIdList(QList transmitMsgIdList); 88 | void loadReceiveMsgIdList(QList receiveMsgIdList); 89 | void loadDatabaseMsgIdList(QList databaseMsgIdList); 90 | 91 | void loadMsgIdList(QList msgIdList, DBCTableModel &model); 92 | void loadCurrentPageIndex(int currPageIndex); 93 | 94 | void onContextMenu(const QPoint &pos); 95 | 96 | void onOpenCan(); 97 | void onCloseCan(); 98 | 99 | void onContextMenuAboutToShow(); 100 | 101 | private: 102 | QTableView *tableView; 103 | 104 | Document *doc; 105 | 106 | DBCTableModel *transmitTableModel; 107 | DBCTableModel *receiveTableModel; 108 | DBCTableModel *databaseTableModel; 109 | 110 | QMenu *contextMenu; 111 | QAction *copyToTransmitAction; 112 | QAction *copyToReceiveAction; 113 | QAction *deleteCurrAction; 114 | 115 | QPushButton *transmitButton; 116 | QPushButton *receiveButton; 117 | QPushButton *databaseButton; 118 | QButtonGroup *buttonGroup; 119 | 120 | QPushButton *loadDBCButton; 121 | 122 | QSizePolicy oldSizePolicy; 123 | 124 | int currPageIndex = -1; 125 | QString currDBCFileName; 126 | }; 127 | 128 | #endif // DATABASEWIDGET_H 129 | -------------------------------------------------------------------------------- /ecanspy3/database/dbctablemodel.cpp: -------------------------------------------------------------------------------- 1 | #include "dbctablemodel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | QString DBCTableModel::columnTitle[DBCTableModel::COLUMN_COUNT] = {"Name", "Type", "ID", "Node", "Period", "Len"}; 8 | 9 | DBCTableModel::DBCTableModel(QObject *parent) 10 | : QAbstractTableModel(parent), doc(NULL) 11 | { 12 | 13 | } 14 | 15 | DBCTableModel::~DBCTableModel() 16 | { 17 | } 18 | 19 | 20 | int DBCTableModel::rowCount(const QModelIndex &parent) const 21 | { 22 | (void)parent; 23 | return msgList.size(); 24 | } 25 | 26 | int DBCTableModel::columnCount(const QModelIndex &parent) const 27 | { 28 | (void)parent; 29 | return COLUMN_COUNT; 30 | } 31 | 32 | QVariant DBCTableModel::data(const QModelIndex &index, int role) const 33 | { 34 | if (!index.isValid()) { 35 | return QVariant(); 36 | } 37 | if (NULL == doc) { 38 | return QVariant(); 39 | } 40 | if (index.row() >= msgList.size()) { 41 | return QVariant(); 42 | } 43 | if (index.column() >= COLUMN_COUNT) { 44 | return QVariant(); 45 | } 46 | if (Qt::DisplayRole != role) { 47 | return QVariant(); 48 | } 49 | 50 | auto col = index.column(); 51 | auto msg = msgList.at(index.row()); 52 | if (0 == col) { //报文名称 53 | return msg->getName(); 54 | } else if (1 == col) { //帧类型 55 | return (msg->getId() <= 0x7ff) ? "Std" : "Extend"; 56 | } else if (2 == col) { //报文ID 57 | return QString("%1").arg(msg->getId(), 0, 16).toUpper(); 58 | } else if (3 == col) { //发送节点 59 | return msg->getTransmitter(); 60 | } else if (4 == col) { //报文周期 61 | const AdditionalAttribute* attr = doc->getMsgAttribute(msg->getId(), "GenMsgCycleTime"); 62 | if (attr) { 63 | return QString("%1").arg(attr->getIntValue()); 64 | } else { 65 | return QString("0"); 66 | } 67 | } else if (5 == col) { //报文长度 68 | return QString("%1").arg(msg->getByteLen()); 69 | } 70 | return QVariant(); 71 | } 72 | 73 | QVariant DBCTableModel::headerData(int section, Qt::Orientation orientation, int role) const 74 | { 75 | if (role != Qt::DisplayRole) { 76 | return QVariant(); 77 | } 78 | if (Qt::Horizontal == orientation) { 79 | return columnTitle[section]; 80 | } else { 81 | return QString("%1 ").arg(section+1); 82 | } 83 | } 84 | 85 | Qt::ItemFlags DBCTableModel::flags(const QModelIndex &index) const 86 | { 87 | return QAbstractTableModel::flags(index); 88 | } 89 | 90 | const QList &DBCTableModel::getMsgList() const 91 | { 92 | return this->msgList; 93 | } 94 | 95 | void DBCTableModel::setMsgList(const QList &lst, Document *doc) 96 | { 97 | beginResetModel(); 98 | this->msgList = lst; 99 | this->doc = doc; 100 | endResetModel(); 101 | } 102 | 103 | void DBCTableModel::setMsgIdList(QList msgIdList, Document *doc) 104 | { 105 | Q_ASSERT(doc); 106 | 107 | beginResetModel(); 108 | 109 | this->msgList.clear(); 110 | foreach (int msgId, msgIdList) { 111 | Message *msg = doc->getMessage(msgId); 112 | if (msg) { 113 | this->msgList.append(msg); 114 | } 115 | } 116 | 117 | this->doc = doc; 118 | endResetModel(); 119 | } 120 | 121 | void DBCTableModel::clear() 122 | { 123 | setMsgList(QList(), NULL); 124 | } 125 | 126 | void DBCTableModel::setDocument(Document *doc) 127 | { 128 | this->doc = doc; 129 | } 130 | 131 | void DBCTableModel::addMsg(Message *msg) 132 | { 133 | #if SEND_MESSAGE_UNIQUE_ENABLED 134 | if (msgList.indexOf(msg) == -1) { 135 | beginInsertRows(QModelIndex(), msgList.size(), msgList.size()); 136 | this->msgList.push_back(msg); 137 | endInsertRows(); 138 | } 139 | #else 140 | beginInsertRows(QModelIndex(), msgList.size(), msgList.size()); 141 | this->msgList.push_back(msg); 142 | endInsertRows(); 143 | #endif //SEND_MESSAGE_UNIQUE_ENABLED 144 | } 145 | 146 | Message *DBCTableModel::getMsg(qint32 index) const 147 | { 148 | if (index < this->msgList.size()) { 149 | return this->msgList.at(index); 150 | } 151 | return NULL; 152 | } 153 | 154 | bool DBCTableModel::removeRows(int row, int count, const QModelIndex &parent) 155 | { 156 | beginRemoveRows(parent, row, row + count - 1); 157 | for (int i = 0; i < count; i++) { 158 | this->msgList.removeAt(row); 159 | } 160 | endRemoveRows(); 161 | return true; 162 | } 163 | 164 | bool DBCTableModel::removeRows(const QModelIndexList &list) 165 | { 166 | std::vector rowCntList; 167 | for (auto iter = list.begin(); iter != list.end(); ++iter) { 168 | rowCntList.push_back(iter->row()); 169 | } 170 | 171 | std::sort(rowCntList.begin(), rowCntList.end(), std::greater()); 172 | 173 | for (auto iter = rowCntList.begin(); iter != rowCntList.end(); ++iter) { 174 | removeRows(*iter, 1, QModelIndex()); 175 | } 176 | return true; 177 | } 178 | -------------------------------------------------------------------------------- /ecanspy3/database/dbctablemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef DBCMESSAGETABLEMODEL_H 2 | #define DBCMESSAGETABLEMODEL_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #define SEND_MESSAGE_UNIQUE_ENABLED (1) 10 | 11 | class DBCTableModel : public QAbstractTableModel 12 | { 13 | Q_OBJECT 14 | public: 15 | 16 | enum { 17 | COLUMN_COUNT = 6 18 | }; 19 | DBCTableModel(QObject *parent = 0); 20 | ~DBCTableModel(); 21 | 22 | // QAbstractItemModel interface 23 | public: 24 | int rowCount(const QModelIndex &parent) const; 25 | int columnCount(const QModelIndex &parent) const; 26 | QVariant data(const QModelIndex &index, int role) const; 27 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 28 | Qt::ItemFlags flags(const QModelIndex &index) const; 29 | bool removeRows(int row, int count, const QModelIndex &parent); 30 | bool removeRows(const QModelIndexList &list); 31 | 32 | const QList &getMsgList() const; 33 | 34 | void setMsgList(const QList &lst, Document *doc); 35 | void setMsgIdList(QList msgIdList, Document *doc); 36 | 37 | void clear(); 38 | void setDocument(Document *doc); 39 | 40 | 41 | void addMsg(Message *msg); 42 | Message *getMsg(qint32 index) const; 43 | 44 | protected: 45 | Document *doc; 46 | QList msgList; 47 | static QString columnTitle[COLUMN_COUNT]; 48 | }; 49 | 50 | #endif // DBCMESSAGETABLEMODEL_H 51 | -------------------------------------------------------------------------------- /ecanspy3/diag/diagwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "diagwidget.h" 2 | 3 | DiagWidget::DiagWidget(QWidget *parent) 4 | : QWidget(parent) 5 | { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /ecanspy3/diag/diagwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef DIAGWIDGET_H 2 | #define DIAGWIDGET_H 3 | 4 | #include 5 | 6 | class DiagWidget : public QWidget 7 | { 8 | public: 9 | DiagWidget(QWidget *parent); 10 | }; 11 | 12 | #endif // DIAGWIDGET_H 13 | -------------------------------------------------------------------------------- /ecanspy3/ecanspy3.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/ecanspy3.ico -------------------------------------------------------------------------------- /ecanspy3/ecanspy3.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2019-10-09T11:33:28 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui xml 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = ecanspy3 12 | TEMPLATE = app 13 | 14 | INCLUDEPATH += F:\\projects\\cpp_projects\\HostComputerProjects\\CommonTool\\ECanSpy3\\Projects 15 | INCLUDEPATH += F:\\projects\\cpp_projects\\HostComputerProjects\\CommonTool\\ECanSpy3\\Projects\\ecanspy3\\can 16 | 17 | CONFIG(debug,debug|release) { 18 | LIBS += F:\\projects\\cpp_projects\\HostComputerProjects\\CommonTool\\ECanSpy3\\Projects\\build-dbc4cpp-Qt4_8_7_Win32-Debug\\debug\\libdbc4cpp.a 19 | } else { 20 | LIBS += F:\\projects\\cpp_projects\\HostComputerProjects\\CommonTool\\ECanSpy3\\Projects\\build-dbc4cpp-Qt4_8_7_Win32-Release\\release\\libdbc4cpp.a 21 | } 22 | 23 | 24 | LIBS += F:\\projects\\cpp_projects\\HostComputerProjects\\CommonTool\\ECanSpy3\\Projects\\ecanspy3\\can\\ECanVci.lib 25 | LIBS += -lwinmm 26 | 27 | QMAKE_CXXFLAGS += -std=c++11 28 | 29 | # The following define makes your compiler emit warnings if you use 30 | # any feature of Qt which has been marked as deprecated (the exact warnings 31 | # depend on your compiler). Please consult the documentation of the 32 | # deprecated API in order to know how to port your code away from it. 33 | DEFINES += QT_DEPRECATED_WARNINGS 34 | 35 | # You can also make your code fail to compile if you use deprecated APIs. 36 | # In order to do so, uncomment the following line. 37 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 38 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 39 | 40 | 41 | RESOURCES += \ 42 | ecanspy3.qrc 43 | 44 | RC_FILE += ecanspy3.rc 45 | 46 | FORMS += \ 47 | aboutecanspy3.ui \ 48 | mainwindow.ui \ 49 | settings/canconfigdialog.ui \ 50 | settings/themesettingdialog.ui 51 | 52 | HEADERS += \ 53 | aboutecanspy3.h \ 54 | diag/diagwidget.h \ 55 | mainwindow.h \ 56 | base/mainwindowbase.h \ 57 | base/titletableviewwidget.h \ 58 | base/titletreeviewwidget.h \ 59 | can/Core.h \ 60 | can/ECanVci.H \ 61 | can/UsbCanUtil.h \ 62 | database/databasewidget.h \ 63 | database/dbctablemodel.h \ 64 | log/logwidget.h \ 65 | receive/filtertablemodel.h \ 66 | receive/filterwidget.h \ 67 | receive/receivemessagefilterproxymodel.h \ 68 | receive/receivemessagetreemodel.h \ 69 | receive/receivemessagewidget.h \ 70 | receive/receivewidget.h \ 71 | send/checkedbuttondelegate.h \ 72 | send/sendmessagewidget.h \ 73 | send/sendwidget.h \ 74 | send/signaltablemodel.h \ 75 | send/signaltableviewphysicalvaluedelegate.h \ 76 | send/signalwidget.h \ 77 | send/txmessagetablemodel.h \ 78 | settings/projectfile.h \ 79 | statistic/statisticswidget.h \ 80 | status/statuswidget.h \ 81 | utils/mmtimer.h \ 82 | utils/scrollingfile.h \ 83 | utils/signalaccessor.h \ 84 | utils/signalaccesstest.h \ 85 | settings/canconfigdialog.h \ 86 | settings/themesettingdialog.h 87 | 88 | SOURCES += \ 89 | aboutecanspy3.cpp \ 90 | diag/diagwidget.cpp \ 91 | main.cpp \ 92 | mainwindow.cpp \ 93 | base/mainwindowbase.cpp \ 94 | base/titletableviewwidget.cpp \ 95 | base/titletreeviewwidget.cpp \ 96 | can/Core.cpp \ 97 | can/UsbCanUtil.cpp \ 98 | database/databasewidget.cpp \ 99 | database/dbctablemodel.cpp \ 100 | log/logwidget.cpp \ 101 | receive/filtertablemodel.cpp \ 102 | receive/filterwidget.cpp \ 103 | receive/receivemessagefilterproxymodel.cpp \ 104 | receive/receivemessagetreemodel.cpp \ 105 | receive/receivemessagewidget.cpp \ 106 | receive/receivewidget.cpp \ 107 | send/checkedbuttondelegate.cpp \ 108 | send/sendmessagewidget.cpp \ 109 | send/sendwidget.cpp \ 110 | send/signaltablemodel.cpp \ 111 | send/signaltableviewphysicalvaluedelegate.cpp \ 112 | send/signalwidget.cpp \ 113 | send/txmessagetablemodel.cpp \ 114 | settings/projectfile.cpp \ 115 | statistic/statisticswidget.cpp \ 116 | status/statuswidget.cpp \ 117 | utils/mmtimer.cpp \ 118 | utils/scrollingfile.cpp \ 119 | utils/signalaccessor.cpp \ 120 | utils/signalaccesstest.cpp \ 121 | settings/canconfigdialog.cpp \ 122 | settings/themesettingdialog.cpp 123 | -------------------------------------------------------------------------------- /ecanspy3/ecanspy3.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | images/about.png 4 | images/copy.png 5 | images/cut.png 6 | images/exit.png 7 | images/new.png 8 | images/next.png 9 | images/open.png 10 | images/paste.png 11 | images/previous.png 12 | images/redo.png 13 | images/save.png 14 | images/saveAs.png 15 | images/undo.png 16 | images/settings.png 17 | images/start.png 18 | images/stop.png 19 | images/theme.png 20 | themes/dracula.qss 21 | themes/black_snapshot.png 22 | themes/white_snapshot.PNG 23 | images/ECanSpy3.png 24 | images/reset.png 25 | 26 | 27 | -------------------------------------------------------------------------------- /ecanspy3/ecanspy3.rc: -------------------------------------------------------------------------------- 1 | IDI_ICON1 ICON DISCARDABLE "ecanspy3.ico" 2 | -------------------------------------------------------------------------------- /ecanspy3/images/ECanSpy3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/ECanSpy3.png -------------------------------------------------------------------------------- /ecanspy3/images/about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/about.png -------------------------------------------------------------------------------- /ecanspy3/images/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/copy.png -------------------------------------------------------------------------------- /ecanspy3/images/cut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/cut.png -------------------------------------------------------------------------------- /ecanspy3/images/exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/exit.png -------------------------------------------------------------------------------- /ecanspy3/images/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/new.png -------------------------------------------------------------------------------- /ecanspy3/images/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/next.png -------------------------------------------------------------------------------- /ecanspy3/images/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/open.png -------------------------------------------------------------------------------- /ecanspy3/images/paste.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/paste.png -------------------------------------------------------------------------------- /ecanspy3/images/previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/previous.png -------------------------------------------------------------------------------- /ecanspy3/images/redo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/redo.png -------------------------------------------------------------------------------- /ecanspy3/images/reset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/reset.png -------------------------------------------------------------------------------- /ecanspy3/images/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/save.png -------------------------------------------------------------------------------- /ecanspy3/images/saveAs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/saveAs.png -------------------------------------------------------------------------------- /ecanspy3/images/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/settings.png -------------------------------------------------------------------------------- /ecanspy3/images/start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/start.png -------------------------------------------------------------------------------- /ecanspy3/images/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/stop.png -------------------------------------------------------------------------------- /ecanspy3/images/theme.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/theme.png -------------------------------------------------------------------------------- /ecanspy3/images/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/images/undo.png -------------------------------------------------------------------------------- /ecanspy3/log/logwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "logwidget.h" 2 | 3 | LogWidget::LogWidget(QWidget *parent) : QWidget(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /ecanspy3/log/logwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef LOGWIDGET_H 2 | #define LOGWIDGET_H 3 | 4 | #include 5 | 6 | class LogWidget : public QWidget 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit LogWidget(QWidget *parent = nullptr); 11 | 12 | signals: 13 | 14 | public slots: 15 | }; 16 | 17 | #endif // LOGWIDGET_H -------------------------------------------------------------------------------- /ecanspy3/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils/signalaccesstest.h" 8 | 9 | #define LOG_TO_LOCAL_ENABLED (0) 10 | 11 | #if LOG_TO_LOCAL_ENABLED 12 | void customMessageHandler( QtMsgType type, const char *msg) 13 | { 14 | QString txt; 15 | switch (type) 16 | { 17 | case QtDebugMsg: 18 | txt = QString("Debug: %1").arg(msg); 19 | break; 20 | case QtWarningMsg: 21 | txt = QString("Warning: %1").arg(msg); 22 | break; 23 | case QtCriticalMsg: 24 | txt = QString("Critical: %1").arg(msg); 25 | break; 26 | case QtFatalMsg: 27 | txt = QString("Fatal: %1").arg(msg); 28 | abort(); 29 | } 30 | QFile outFile("debuglog.txt"); 31 | outFile.open(QIODevice::WriteOnly | QIODevice::Append|QIODevice::Text); 32 | QTextStream ts(&outFile); 33 | ts << txt << endl; 34 | 35 | //输出到控制台 36 | //QDateTime now = QDateTime::currentDateTime(); 37 | //cout << now.toString("hh:mm:ss.zzz").toStdString() << " "<< txt.toStdString() < 9 | 10 | #include "settings/themesettingdialog.h" 11 | 12 | namespace Ui { 13 | class MainWindow; 14 | } 15 | 16 | QT_BEGIN_NAMESPACE 17 | class QTimer; 18 | QT_END_NAMESPACE 19 | 20 | #define UPDATE_DATAChANGED_FLAG() MainWindow::dataHasChageFlg = true 21 | 22 | class MainWindow : public MainWindowBase 23 | { 24 | Q_OBJECT 25 | 26 | public: 27 | explicit MainWindow(QWidget *parent = 0); 28 | ~MainWindow(); 29 | 30 | void initDockWidgets(); 31 | void initToolBar(); 32 | 33 | void initCanMonitor(); 34 | 35 | void initDispatchSignal(); 36 | void initDataChanged(); 37 | 38 | void removeCentralWindow(); 39 | 40 | void readSettings(); 41 | void writeSettings(); 42 | 43 | void setTheme(QString themeStyleSheet); 44 | 45 | DatabaseWidget *getDatabaseWidget(); 46 | SendWidget *getSendWidget(); 47 | 48 | protected slots: 49 | void resetDefaultDockWidgetsLayout(); 50 | void openCan(); 51 | void closeCan(); 52 | void about(); 53 | void configCan(); 54 | void configTheme(); 55 | 56 | void triggerMsgOption(); 57 | 58 | void save(); 59 | void saveAs(); 60 | void open(); 61 | void create(); 62 | 63 | void onOpenCan(); 64 | void onCloseCan(); 65 | 66 | protected: 67 | void resetSizePolicy(); 68 | void saveProjectFile(const QString &projectFileName); 69 | void openProjectFile(const QString &projectFileName); 70 | void loadLastTheme(); 71 | 72 | protected: 73 | QDockWidget *receiveDockWidget; 74 | QDockWidget *diagDockWidget; 75 | QDockWidget *statusDockWidget; 76 | QDockWidget *statisticsDockWidget; 77 | QDockWidget *logDockWidget; 78 | QDockWidget *sendDockWidget; 79 | QDockWidget *databaseDockWidget; 80 | 81 | QString currTheme; //当前主题 82 | QString currProjectFileName; //当前项目 83 | QTimer *uiUpdateTimer = nullptr; 84 | 85 | private: 86 | Ui::MainWindow *ui; 87 | 88 | // QWidget interface 89 | protected: 90 | void closeEvent(QCloseEvent *); 91 | 92 | public: 93 | static bool dataHasChageFlg; //表示用户有过操作 94 | }; 95 | 96 | 97 | #endif // MAINWINDOW_H 98 | -------------------------------------------------------------------------------- /ecanspy3/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 300 11 | 12 | 13 | 14 | ECanSpy3 15 | 16 | 17 | 18 | 19 | 20 | 0 21 | 0 22 | 400 23 | 26 24 | 25 | 26 | 27 | 28 | 文件(&F) 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 设备(D) 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 帮助(H) 49 | 50 | 51 | 52 | 53 | 54 | 选项(O) 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | TopToolBarArea 70 | 71 | 72 | false 73 | 74 | 75 | 76 | 77 | 78 | 79 | :/images/open.png:/images/open.png 80 | 81 | 82 | 打开(O) 83 | 84 | 85 | 86 | 87 | 88 | :/images/new.png:/images/new.png 89 | 90 | 91 | 新建(N) 92 | 93 | 94 | 95 | 96 | 97 | :/images/save.png:/images/save.png 98 | 99 | 100 | 保存(S) 101 | 102 | 103 | 104 | 105 | 106 | :/images/saveAs.png:/images/saveAs.png 107 | 108 | 109 | 另存为(A) 110 | 111 | 112 | 113 | 114 | 115 | :/images/exit.png:/images/exit.png 116 | 117 | 118 | 退出(X) 119 | 120 | 121 | 122 | 123 | 124 | :/images/start.png:/images/start.png 125 | 126 | 127 | 打开CAN 128 | 129 | 130 | 131 | 132 | 133 | :/images/stop.png:/images/stop.png 134 | 135 | 136 | 关闭CAN 137 | 138 | 139 | 140 | 141 | 142 | :/images/settings.png:/images/settings.png 143 | 144 | 145 | 配置CAN... 146 | 147 | 148 | 149 | 150 | 151 | :/images/about.png:/images/about.png 152 | 153 | 154 | 关于ECanSpy3... 155 | 156 | 157 | 158 | 159 | 160 | :/images/theme.png:/images/theme.png 161 | 162 | 163 | 主题设置 164 | 165 | 166 | 167 | 168 | 169 | :/images/reset.png:/images/reset.png 170 | 171 | 172 | 重置窗口布局 173 | 174 | 175 | 176 | 177 | 触发报文选项 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /ecanspy3/receive/filtertablemodel.cpp: -------------------------------------------------------------------------------- 1 | #include "filtertablemodel.h" 2 | #include "mainwindow.h" 3 | 4 | #include 5 | 6 | QString FilterTableModel::columnTitles[FilterTableModel::COLUMN_COUNT] = { 7 | QObject::tr("BegID(Hex)"), QObject::tr("EndID(Hex)"), QObject::tr("En") 8 | }; 9 | 10 | FilterTableModel::FilterTableModel(QObject *parent) : QAbstractTableModel(parent) 11 | { 12 | 13 | } 14 | 15 | void FilterTableModel::setChecked(int row, bool enabled) 16 | { 17 | if (row < filterList.size()) { 18 | if (filterList[row].enabled != enabled) { 19 | filterList[row].enabled = enabled; 20 | emit updateFilter(); 21 | } 22 | } 23 | } 24 | 25 | QList *FilterTableModel::getFilterList() 26 | { 27 | return &filterList; 28 | } 29 | 30 | 31 | int FilterTableModel::rowCount(const QModelIndex &parent) const 32 | { 33 | Q_UNUSED(parent); 34 | return filterList.size(); 35 | } 36 | 37 | int FilterTableModel::columnCount(const QModelIndex &parent) const 38 | { 39 | Q_UNUSED(parent); 40 | return COLUMN_COUNT; 41 | } 42 | 43 | QVariant FilterTableModel::data(const QModelIndex &index, int role) const 44 | { 45 | if (!index.isValid()) return QVariant(); 46 | 47 | if (Qt::TextAlignmentRole == role) { 48 | return int(Qt::AlignRight|Qt::AlignVCenter); 49 | } 50 | 51 | if (role != Qt::DisplayRole && role != Qt::EditRole) return QVariant(); 52 | 53 | int row = index.row(); 54 | int column = index.column(); 55 | 56 | if (0 == column) { 57 | return QString("%1").arg(filterList[row].idStart, 3, 16, QChar('0')).toUpper(); 58 | } else if (1 == column) { 59 | return QString("%1").arg(filterList[row].idEnd, 3, 16, QChar('0')).toUpper(); 60 | } 61 | 62 | return QVariant(); 63 | } 64 | 65 | QVariant FilterTableModel::headerData(int section, Qt::Orientation orientation, int role) const 66 | { 67 | if (role != Qt::DisplayRole) return QVariant(); 68 | if (Qt::Horizontal == orientation) { 69 | return columnTitles[section]; 70 | } else { 71 | return QString("%1 ").arg(section+1); 72 | } 73 | } 74 | 75 | bool FilterTableModel::insertRows(int position, int count, const QModelIndex &parent) 76 | { 77 | Q_UNUSED(parent); 78 | beginInsertRows(QModelIndex(), position, position + count - 1); 79 | for (int row = 0; row < count; ++row) { 80 | filterList.insert(position, Entity()); 81 | } 82 | endInsertRows(); 83 | return true; 84 | } 85 | 86 | bool FilterTableModel::removeRows(int position, int count, const QModelIndex &parent) 87 | { 88 | Q_UNUSED(parent); 89 | beginRemoveRows(QModelIndex(), position, position + count - 1); 90 | for (int row = 0; row < count; ++row) { 91 | filterList.removeAt(position); 92 | } 93 | endRemoveRows(); 94 | return true; 95 | } 96 | 97 | static inline quint32 hexToUINT(const QString str, bool *ok) 98 | { 99 | QByteArray stream(str.toAscii()); 100 | return stream.toUInt(ok, 16); 101 | } 102 | 103 | bool FilterTableModel::setData(const QModelIndex &index, const QVariant &value, int role) 104 | { 105 | if (role != Qt::EditRole) return false; 106 | int column = index.column(); 107 | bool ok = false; 108 | 109 | UPDATE_DATAChANGED_FLAG(); 110 | 111 | if (0 == column) { 112 | auto &entity = filterList[index.row()]; 113 | auto id = hexToUINT(value.toString(), &ok); 114 | if (ok) { 115 | if (entity.idStart != id) { 116 | entity.idStart = id; 117 | emit updateFilter(); 118 | } 119 | } 120 | return true; 121 | } else if (1 == column) { 122 | auto &entity = filterList[index.row()]; 123 | auto id = hexToUINT(value.toString(), &ok); 124 | if (ok) { 125 | if (entity.idEnd != id) { 126 | entity.idEnd = id; 127 | emit updateFilter(); 128 | } 129 | } 130 | return true; 131 | } 132 | return false; 133 | } 134 | 135 | Qt::ItemFlags FilterTableModel::flags(const QModelIndex &index) const 136 | { 137 | int column = index.column(); 138 | if (0 == column || 1 == column) { 139 | return QAbstractTableModel::flags(index) | Qt::ItemIsEditable; 140 | } 141 | return QAbstractTableModel::flags(index); 142 | } 143 | 144 | bool FilterTableModel::filterCanObj(const CAN_OBJ &canObj) 145 | { 146 | bool allUnchecked = false; 147 | UINT id = canObj.ID; 148 | 149 | for (int i = 0; i < filterList.size(); i++) { 150 | auto &entity = filterList.at(i); 151 | if (entity.enabled) { 152 | allUnchecked = true; 153 | if (id >= entity.idStart && id <= entity.idEnd) { 154 | return true; 155 | } 156 | } 157 | } 158 | 159 | return !allUnchecked; 160 | } 161 | -------------------------------------------------------------------------------- /ecanspy3/receive/filtertablemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERTABLEMODEL_H 2 | #define FILTERTABLEMODEL_H 3 | 4 | #include 5 | #include 6 | #include "UsbCanUtil.h" 7 | 8 | struct CanObjFilter 9 | { 10 | virtual ~CanObjFilter() {} 11 | virtual bool filterCanObj(const CAN_OBJ &canObj) = 0; 12 | }; 13 | 14 | class FilterTableModel : public QAbstractTableModel, public CanObjFilter 15 | { 16 | Q_OBJECT 17 | public: 18 | struct Entity { 19 | UINT idStart; 20 | UINT idEnd; 21 | bool enabled; 22 | Entity() : idStart(0), idEnd(0), enabled(false) {} 23 | }; 24 | 25 | enum {COLUMN_COUNT=3}; 26 | 27 | FilterTableModel(QObject *parent=nullptr); 28 | 29 | void setChecked(int row, bool enabled); 30 | QList *getFilterList(); 31 | 32 | signals: 33 | void updateFilter(); 34 | 35 | // QAbstractItemModel interface 36 | public: 37 | int rowCount(const QModelIndex &parent=QModelIndex()) const; 38 | int columnCount(const QModelIndex &parent) const; 39 | QVariant data(const QModelIndex &index, int role) const; 40 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 41 | bool insertRows(int row, int count, const QModelIndex &parent); 42 | bool removeRows(int row, int count, const QModelIndex &parent); 43 | bool setData(const QModelIndex &index, const QVariant &value, int role); 44 | Qt::ItemFlags flags(const QModelIndex &index) const; 45 | 46 | CanObjFilter* getCanObjFilter() 47 | { 48 | return this; 49 | } 50 | 51 | bool filterCanObj(const CAN_OBJ &canObj); 52 | 53 | void clear() 54 | { 55 | beginResetModel(); 56 | filterList.clear(); 57 | endResetModel(); 58 | } 59 | 60 | void setFilterEntList(QList filterList) 61 | { 62 | beginResetModel(); 63 | this->filterList = filterList; 64 | endResetModel(); 65 | } 66 | 67 | protected: 68 | QList filterList; 69 | static QString columnTitles[COLUMN_COUNT]; 70 | }; 71 | 72 | #endif // FILTERTABLEMODEL_H 73 | -------------------------------------------------------------------------------- /ecanspy3/receive/filterwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "filterwidget.h" 2 | #include "mainwindow.h" 3 | #include 4 | 5 | FilterWidget::FilterWidget(QWidget *parent) 6 | : TitleTableViewWidget(parent) 7 | { 8 | initTitle(); 9 | setMinimumWidth(300); 10 | 11 | initButtons(static_cast(this->layout())); 12 | initModel(); 13 | 14 | initFilters(); 15 | } 16 | 17 | void FilterWidget::initButtons(QVBoxLayout *layout) 18 | { 19 | QHBoxLayout *hboxLayout = new QHBoxLayout; 20 | 21 | hboxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed)); 22 | 23 | // 添加按钮 24 | addButton = new QPushButton(tr("Add"), this); 25 | addButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 26 | connect(addButton, SIGNAL(clicked(bool)), this, SLOT(addFilter())); 27 | hboxLayout->addWidget(addButton); 28 | 29 | // 删除按钮 30 | removeButton = new QPushButton(tr("Remove"), this); 31 | removeButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 32 | connect(removeButton, SIGNAL(clicked(bool)), this, SLOT(removeFilter())); 33 | hboxLayout->addWidget(removeButton); 34 | 35 | // 选中所有选项按钮 36 | toggleAllCheckButton = new QPushButton(tr("Toggle"), this); 37 | toggleAllCheckButton->setCheckable(true); 38 | toggleAllCheckButton->setChecked(false); 39 | connect(toggleAllCheckButton, SIGNAL(clicked(bool)), this, SLOT(onToggleAllCheckbox(bool))); 40 | hboxLayout->addWidget(toggleAllCheckButton); 41 | 42 | QSpacerItem *item = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed); 43 | hboxLayout->addSpacerItem(item); 44 | 45 | layout->insertLayout(1, hboxLayout); 46 | } 47 | 48 | void FilterWidget::initModel() 49 | { 50 | tableView->setSelectionBehavior(QAbstractItemView::SelectRows); 51 | tableView->setSelectionMode(QAbstractItemView::SingleSelection); 52 | 53 | model = new FilterTableModel(this); 54 | tableView->setModel(model); 55 | tableView->resizeColumnsToContents(); 56 | } 57 | 58 | void FilterWidget::initFilters() 59 | { 60 | for (int i = 0; i < DEFUALT_FILTER_COUNT; ++i) { 61 | addFilter(); 62 | } 63 | } 64 | 65 | void FilterWidget::clear() 66 | { 67 | model->clear(); 68 | checkBoxList.clear(); 69 | } 70 | 71 | QList *FilterWidget::getFilterList() 72 | { 73 | return model->getFilterList(); 74 | } 75 | 76 | void FilterWidget::addFilter(UINT idStart, UINT idEnd, bool checked) 77 | { 78 | addFilter(); 79 | int rowIndex = model->rowCount() - 1; 80 | model->setData(model->index(rowIndex, 0), QString("%1").arg(idStart, 0, 16), Qt::EditRole); 81 | model->setData(model->index(rowIndex, 1), QString("%1").arg(idEnd, 0, 16), Qt::EditRole); 82 | model->setChecked(rowIndex, checked); 83 | checkBoxList[rowIndex]->setChecked(checked); 84 | } 85 | 86 | void FilterWidget::addFilter() 87 | { 88 | if (model->insertRow(model->rowCount())) { 89 | QCheckBox *checkbox = new QCheckBox(this); 90 | checkBoxList.append(checkbox); 91 | tableView->setIndexWidget(model->index(model->rowCount()-1, 2), checkbox); 92 | checkbox->setProperty("row", model->rowCount()-1); 93 | connect(checkbox, SIGNAL(clicked(bool)), this, SLOT(onCheckboxClicked(bool))); 94 | UPDATE_DATAChANGED_FLAG(); 95 | } 96 | } 97 | 98 | void FilterWidget::removeFilter() 99 | { 100 | if (tableView->currentIndex().isValid()) { 101 | checkBoxList.removeAt(tableView->currentIndex().row()); 102 | model->removeRow(tableView->currentIndex().row()); 103 | UPDATE_DATAChANGED_FLAG(); 104 | } 105 | } 106 | 107 | void FilterWidget::onCheckboxClicked(bool enabled) 108 | { 109 | bool ok = false; 110 | int row = sender()->property("row").toInt(&ok); 111 | if (ok) { 112 | model->setChecked(row, enabled); 113 | UPDATE_DATAChANGED_FLAG(); 114 | } 115 | } 116 | 117 | void FilterWidget::onToggleAllCheckbox(bool enabled) 118 | { 119 | for (int row = 0; row < checkBoxList.size(); row++) { 120 | model->setChecked(row, enabled); 121 | checkBoxList[row]->setChecked(enabled); 122 | } 123 | UPDATE_DATAChANGED_FLAG(); 124 | } 125 | 126 | void FilterWidget::setFilterList(Document *doc) 127 | { 128 | #if SYNC_DOCUMENT_MESSAGE_ID_FILTER_ENABLED 129 | if (oldDoc == doc) return; 130 | 131 | clear(); 132 | oldDoc = doc; 133 | 134 | if (doc) { 135 | foreach (const Message *msg, doc->GetMsgList()) { 136 | if (msg) { 137 | addFilter(msg->getId(), msg->getId(), false); 138 | } 139 | } 140 | } 141 | #else 142 | Q_UNUSED(doc); 143 | #endif 144 | } 145 | 146 | void FilterWidget::loadFilterEntList(QList entList) 147 | { 148 | clear(); 149 | foreach (const FilterTableModel::Entity &entity, entList) { 150 | addFilter(entity.idStart, entity.idEnd, entity.enabled); 151 | } 152 | } 153 | 154 | void FilterWidget::initTitle() 155 | { 156 | title->setText(tr("过滤器")); 157 | } 158 | -------------------------------------------------------------------------------- /ecanspy3/receive/filterwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef FILTERWIDGET_H 2 | #define FILTERWIDGET_H 3 | 4 | #include "base/titletableviewwidget.h" 5 | #include "receive/filtertablemodel.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define SYNC_DOCUMENT_MESSAGE_ID_FILTER_ENABLED (0) 13 | 14 | class FilterWidget : public TitleTableViewWidget 15 | { 16 | Q_OBJECT 17 | public: 18 | enum { 19 | DEFUALT_FILTER_COUNT = 18 20 | }; 21 | 22 | FilterWidget(QWidget *parent); 23 | 24 | void initButtons(QVBoxLayout *layout); 25 | void initModel(); 26 | void initFilters(); 27 | 28 | void clear(); 29 | 30 | CanObjFilter* getCanObjFilter() 31 | { 32 | return model->getCanObjFilter(); 33 | } 34 | 35 | QList* getFilterList(); 36 | 37 | FilterTableModel *getFilterTableModel() 38 | { 39 | return this->model; 40 | } 41 | 42 | void addFilter(UINT idStart, UINT idEnd, bool checked); 43 | 44 | public slots: 45 | void addFilter(); 46 | void removeFilter(); 47 | void onCheckboxClicked(bool enabled); 48 | void onToggleAllCheckbox(bool enabled); 49 | 50 | void setFilterList(Document *doc); 51 | 52 | void loadFilterEntList(QList entList); 53 | 54 | protected: 55 | void initTitle(); 56 | 57 | protected: 58 | QPushButton *addButton; 59 | QPushButton *removeButton; 60 | QPushButton *toggleAllCheckButton; 61 | 62 | FilterTableModel *model; 63 | QList checkBoxList; 64 | Document *oldDoc = nullptr; 65 | }; 66 | 67 | #endif // FILTERWIDGET_H 68 | -------------------------------------------------------------------------------- /ecanspy3/receive/receivemessagefilterproxymodel.cpp: -------------------------------------------------------------------------------- 1 | #include "receivemessagefilterproxymodel.h" 2 | 3 | ReceiveMessageFilterProxyModel::ReceiveMessageFilterProxyModel(QObject *parent) 4 | : QSortFilterProxyModel(parent) 5 | { 6 | 7 | } 8 | 9 | void ReceiveMessageFilterProxyModel::setFilterList(QList *filterList) 10 | { 11 | this->filterList = filterList; 12 | invalidateFilter(); 13 | } 14 | 15 | void ReceiveMessageFilterProxyModel::refilter() 16 | { 17 | invalidateFilter(); 18 | } 19 | 20 | 21 | bool ReceiveMessageFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const 22 | { 23 | if (!filterList) return true; 24 | 25 | QModelIndex idIndex = sourceModel()->index(source_row, 1, source_parent); 26 | 27 | bool allUnchecked = false; //如果所有行都没有选中,则说明不需要过滤,如果有一行都选中,则说明需要过滤 28 | QString idStr = sourceModel()->data(idIndex).toString(); 29 | QByteArray byteArray(idStr.toAscii()); 30 | quint32 id = byteArray.toUInt(NULL, 16); 31 | 32 | for (int i = 0; i < this->filterList->size(); i++) { 33 | auto &entity = this->filterList->at(i); 34 | if (entity.enabled) { 35 | allUnchecked = true; 36 | if (id >= entity.idStart && id <= entity.idEnd) { 37 | return true; 38 | } 39 | } 40 | } 41 | 42 | return !allUnchecked; 43 | } 44 | -------------------------------------------------------------------------------- /ecanspy3/receive/receivemessagefilterproxymodel.h: -------------------------------------------------------------------------------- 1 | #ifndef RECEIVEMESSAGEFILTERPROXYMODEL_H 2 | #define RECEIVEMESSAGEFILTERPROXYMODEL_H 3 | 4 | #include 5 | #include 6 | #include "filtertablemodel.h" 7 | 8 | class ReceiveMessageFilterProxyModel : public QSortFilterProxyModel 9 | { 10 | Q_OBJECT 11 | public: 12 | ReceiveMessageFilterProxyModel(QObject *parent=nullptr); 13 | 14 | void setFilterList(QList * filterList); 15 | void refilter(); 16 | 17 | // QSortFilterProxyModel interface 18 | protected: 19 | bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; 20 | 21 | private: 22 | QList *filterList; 23 | }; 24 | 25 | #endif // RECEIVEMESSAGEFILTERPROXYMODEL_H 26 | -------------------------------------------------------------------------------- /ecanspy3/receive/receivemessagetreemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef RECEIVEMESSAGETABLEMODEL_H 2 | #define RECEIVEMESSAGETABLEMODEL_H 3 | 4 | #include 5 | #include 6 | 7 | #include "UsbCanUtil.h" 8 | #include "filtertablemodel.h" 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | 19 | 20 | class ReceiveMessageTreeModel : public QAbstractItemModel 21 | { 22 | Q_OBJECT 23 | 24 | public: 25 | enum { 26 | COLUMN_COUNT = 9, 27 | 28 | MAX_MESSAGE_COUNT_UNRESTRICTED = -1, //!< 内存没有限制 29 | MAX_MESSAGE_COUNT = 350000, //!< 内存中最多只存5000行 30 | 31 | DEFAULT_REFRESH_PREIOD_MS = 250, //!< 默认刷新周期,单位:毫秒 32 | SIGNAL_LEVEL = -1, 33 | 34 | DEFAULT_SCROLL_UPDATE = 0, //!< 默认的滚动更新,1表示滚动更新,0表示不进行滚动更新 35 | }; 36 | 37 | enum UpdateMode { 38 | UPDATE_MODE_NONE = 0, 39 | UPDATE_MODE_INSERT, //插入若干数据 40 | UPDATE_MODE_UPDATE //数据更新,长度未变 41 | }; 42 | 43 | struct Entity { 44 | CAN_OBJ obj; 45 | DWORD periodUs; 46 | DWORD serialCount; 47 | }; 48 | 49 | ReceiveMessageTreeModel(QObject *parent=NULL); 50 | 51 | // QAbstractItemModel interface 52 | public: 53 | QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const; 54 | QModelIndex parent(const QModelIndex &child) const; 55 | int rowCount(const QModelIndex &parent) const; 56 | int columnCount(const QModelIndex &parent) const; 57 | QVariant data(const QModelIndex &index, int role) const; 58 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 59 | 60 | QVariant messageColumnData(const Entity &entity, int column, int role) const; 61 | 62 | Signal *getSignal(int msgId, int index) const; 63 | 64 | void setDocument(Document *doc); 65 | Entity* addCanObj(CAN_OBJ &obj); 66 | 67 | void stopReceive(); 68 | void startReceive(); 69 | 70 | int signalCount(int msgRow) const; 71 | QString msgName(int msgID) const; 72 | 73 | void setTreeView(QTreeView *treeView) 74 | { 75 | this->treeView = treeView; 76 | } 77 | 78 | void setCanObjFilter(CanObjFilter *filter) 79 | { 80 | this->filter = filter; 81 | } 82 | 83 | const std::deque& getObjList() const 84 | { 85 | return objList; 86 | } 87 | 88 | bool isScrollUpdate() const 89 | { 90 | return this->scrollUpdateFlag; 91 | } 92 | 93 | private: 94 | int mostTopRowCount() const; 95 | const Entity &entity(int row) const; 96 | 97 | public slots: 98 | void refresh(); 99 | void forceUpdate(); 100 | void clear(); 101 | void setScroll(bool scroll); 102 | 103 | protected: 104 | QVariant messageData(const QModelIndex &index, int role) const; 105 | 106 | QVariant signalData(const QModelIndex &index, int role) const; 107 | 108 | void initRefreshTimer(); 109 | 110 | protected: 111 | Document *doc = nullptr; 112 | static QString columnTitles[COLUMN_COUNT]; 113 | 114 | std::deque objList; 115 | QMap newestObjMap; 116 | 117 | QMap > msgTimeStampMap; 118 | QMap msgNameMap; //内部缓存,报文id和报文名称关系 119 | 120 | QTimer *timer; //刷新数据定时器 121 | UpdateMode updateMode = UPDATE_MODE_NONE; //数据变化标识 122 | bool stopReceiveFlag = false; 123 | bool scrollUpdateFlag = DEFAULT_SCROLL_UPDATE; 124 | 125 | std::size_t oldSize = 0; 126 | 127 | QTreeView *treeView = nullptr; 128 | UINT serialCount = 0; 129 | 130 | CanObjFilter* filter = nullptr; 131 | 132 | inline int encodeSignal(int rowParent) const 133 | { 134 | return SIGNAL_LEVEL - rowParent; 135 | } 136 | 137 | inline int decodeSignal(int internData) const 138 | { 139 | return SIGNAL_LEVEL - internData; 140 | } 141 | 142 | }; 143 | 144 | #endif // RECEIVEMESSAGETABLEMODEL_H 145 | -------------------------------------------------------------------------------- /ecanspy3/receive/receivemessagewidget.cpp: -------------------------------------------------------------------------------- 1 | #include "receivemessagewidget.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | template 8 | static inline void safeDelete(T *&ptr) 9 | { 10 | delete ptr; 11 | ptr = nullptr; 12 | } 13 | 14 | ReceiveMessageWidget::ReceiveMessageWidget(QWidget *parent) 15 | : TitleTreeViewWidget(parent) 16 | { 17 | initTitle(); 18 | initModel(); 19 | initTimer(); 20 | initToolButtons(); 21 | initColumnsWidth(); 22 | } 23 | 24 | ReceiveMessageWidget::~ReceiveMessageWidget() 25 | { 26 | safeDelete(scrollingFile); 27 | } 28 | 29 | void ReceiveMessageWidget::initTitle() 30 | { 31 | title->setText(tr("报文")); 32 | } 33 | 34 | void ReceiveMessageWidget::initModel() 35 | { 36 | model = new ReceiveMessageTreeModel(this); 37 | model->setTreeView(treeView); 38 | 39 | #if RECEIVE_FILTER_PROXY_ENABLED 40 | proxyModel = new ReceiveMessageFilterProxyModel(this); 41 | proxyModel->setSourceModel(model); 42 | proxyModel->setDynamicSortFilter(true); 43 | 44 | treeView->setModel(proxyModel); 45 | #else 46 | treeView->setModel(model); 47 | #endif 48 | treeView->setUniformRowHeights(true); 49 | 50 | treeView->setColumnWidth(0, 125); 51 | treeView->setColumnWidth(5, 200); 52 | } 53 | 54 | void ReceiveMessageWidget::initTimer() 55 | { 56 | timer = new QTimer(this); 57 | connect(timer, SIGNAL(timeout()), this, SLOT(onTimer())); 58 | timer->start(RECEIVE_INTERVAL_MS); 59 | } 60 | 61 | void ReceiveMessageWidget::initToolButtons() 62 | { 63 | QHBoxLayout *hBoxLayout = new QHBoxLayout; 64 | 65 | // 显示/停止显示按钮 66 | stopShowButton = new QPushButton(tr("Show/Stop"), this); 67 | stopShowButton->setCheckable(true); 68 | stopShowButton->setChecked(true); 69 | connect(stopShowButton, SIGNAL(clicked(bool)), this, SLOT(onStartReceive(bool))); 70 | 71 | // 初始化清空按钮 72 | clearButton = new QPushButton(tr("Clear"), this); 73 | connect(clearButton, SIGNAL(clicked(bool)), model, SLOT(clear())); 74 | 75 | // 重新调整所有栏目 76 | resizeColumns = new QPushButton(tr("Resize Columns"), this); 77 | connect(resizeColumns, SIGNAL(clicked(bool)), this, SLOT(onResizeColumns())); 78 | 79 | // 滚动模式/统计模式 80 | scrollButton = new QPushButton(tr("Scroll"), this); 81 | scrollButton->setCheckable(true); 82 | scrollButton->setChecked(model->isScrollUpdate()); 83 | connect(scrollButton, SIGNAL(clicked(bool)), model, SLOT(setScroll(bool))); 84 | 85 | // 保存按钮 86 | saveButton = new QPushButton(tr("Save"), this); 87 | connect(saveButton, SIGNAL(clicked(bool)), this, SLOT(onSave())); 88 | 89 | // 实时保存按钮 90 | realTimeSaveButton = new QPushButton(tr("RealTime Save"), this); 91 | realTimeSaveButton->setCheckable(true); 92 | connect(realTimeSaveButton, SIGNAL(clicked(bool)), this, SLOT(onRealTimeSave(bool))); 93 | 94 | 95 | hBoxLayout->addWidget(stopShowButton); 96 | hBoxLayout->addSpacing(5); 97 | hBoxLayout->addWidget(clearButton); 98 | hBoxLayout->addSpacing(5); 99 | hBoxLayout->addWidget(resizeColumns); 100 | hBoxLayout->addSpacing(5); 101 | hBoxLayout->addWidget(scrollButton); 102 | hBoxLayout->addSpacing(5); 103 | hBoxLayout->addWidget(saveButton); 104 | hBoxLayout->addSpacing(5); 105 | hBoxLayout->addWidget(realTimeSaveButton); 106 | 107 | QSpacerItem *spacerItem = new QSpacerItem(0, 0, QSizePolicy::Expanding); 108 | hBoxLayout->addSpacerItem(spacerItem); 109 | 110 | QVBoxLayout *mainLayout = static_cast(layout()); 111 | if (mainLayout) { 112 | mainLayout->insertLayout(1, hBoxLayout); 113 | } 114 | } 115 | 116 | void ReceiveMessageWidget::initColumnsWidth() 117 | { 118 | //enum { COLUMN_SERIAL_NUM=0, COLUMN_PERIOD_MS, COLUMN_INDEX_ID, COLUMN_INDEX_NAME, COLUMN_INDEX_LEN, 119 | // COLUMN_INDEX_DATA, COLUMN_FRAME_TYPE, COLUMN_FRAME_FMT, COLUMN_INDEX_ABS_TIME}; 120 | int maxColumnsWidth[ReceiveMessageTreeModel::COLUMN_COUNT] = {16, 12, 5, 30, 5, 25, 13, 12, 10}; 121 | int charWidth = getTextWidth(QChar('X')); 122 | for (int i = 0; i < ReceiveMessageTreeModel::COLUMN_COUNT; i++) { 123 | treeView->setColumnWidth(i, charWidth * maxColumnsWidth[i]); 124 | } 125 | } 126 | 127 | int ReceiveMessageWidget::getTextWidth(QChar ch) 128 | { 129 | QPainter p(this); 130 | QFontMetrics fm = p.fontMetrics(); 131 | return fm.width(ch); 132 | } 133 | 134 | void ReceiveMessageWidget::doRealTimeSave(const ReceiveMessageTreeModel::Entity &ent) 135 | { 136 | if (!scrollingFile) return; 137 | QByteArray buf; 138 | QTextStream stream(&buf); 139 | doSave(stream, ent); 140 | scrollingFile->write(buf); 141 | } 142 | 143 | void ReceiveMessageWidget::doSave(QTextStream &stream, const ReceiveMessageTreeModel::Entity &ent) 144 | { 145 | int columnCount = model->columnCount(QModelIndex()); 146 | for (int i = 0; i < columnCount; i++) { 147 | stream << model->messageColumnData(ent, i, Qt::DisplayRole).toString(); 148 | if (i != columnCount-1) { 149 | stream << ","; 150 | } else { 151 | stream << "\r\n"; 152 | } 153 | } 154 | stream.flush(); 155 | } 156 | 157 | void ReceiveMessageWidget::setFilterList(QList *filterList) 158 | { 159 | #if RECEIVE_FILTER_PROXY_ENABLED 160 | if (this->proxyModel) { 161 | proxyModel->setFilterList(filterList); 162 | } 163 | #else 164 | Q_UNUSED(filterList); 165 | #endif 166 | } 167 | 168 | void ReceiveMessageWidget::setCanObjFilter(CanObjFilter *filter) 169 | { 170 | if (model) { 171 | model->setCanObjFilter(filter); 172 | } 173 | } 174 | 175 | QList ReceiveMessageWidget::getColumnWidthList() const 176 | { 177 | QList columnWidthList; 178 | for (int i = 0; i < ReceiveMessageTreeModel::COLUMN_COUNT; ++i) { 179 | columnWidthList.append(treeView->columnWidth(i)); 180 | } 181 | return columnWidthList; 182 | } 183 | 184 | void ReceiveMessageWidget::setColumnWidthList(QList widthList) 185 | { 186 | Q_ASSERT(treeView); 187 | int columnCount = qMin(widthList.size(), (int)ReceiveMessageTreeModel::COLUMN_COUNT-1); 188 | for (int i = 0; i < columnCount; i++) { 189 | treeView->setColumnWidth(i, widthList.at(i)); 190 | } 191 | } 192 | 193 | void ReceiveMessageWidget::refreshDocument(Document *doc) 194 | { 195 | model->setDocument(doc); 196 | qDebug() << "ReceiveMessageWidget::refreshDocument"; 197 | } 198 | 199 | void ReceiveMessageWidget::updateFilter() 200 | { 201 | #if RECEIVE_FILTER_PROXY_ENABLED 202 | if (proxyModel) { 203 | proxyModel->refilter(); 204 | } 205 | #endif 206 | } 207 | 208 | void ReceiveMessageWidget::onTimer() 209 | { 210 | auto &usbCan = UsbCanUtil::GetInstance(); 211 | CAN_OBJ objBuffer[RECEIVE_BUFFER_SIZE]; 212 | 213 | DWORD len = usbCan.Receive(objBuffer, RECEIVE_BUFFER_SIZE); 214 | for (int i = 0; i < (int)len; i++) { 215 | auto ent = model->addCanObj(objBuffer[i]); 216 | if (ent) { 217 | doRealTimeSave(*ent); 218 | } 219 | } 220 | } 221 | 222 | void ReceiveMessageWidget::onStartReceive(bool checked) 223 | { 224 | if (checked) { 225 | model->startReceive(); 226 | } else { 227 | model->stopReceive(); 228 | } 229 | } 230 | 231 | void ReceiveMessageWidget::onSave() 232 | { 233 | QString fileName = QFileDialog::getSaveFileName(this, 234 | tr("保存日志"), tr(""), tr("日志文件(*.csv)")); 235 | if (!fileName.isEmpty()) { 236 | QFile file(fileName); 237 | if (!file.open(QFile::WriteOnly | QFile::Truncate)) { 238 | QMessageBox::critical(this, tr("错误"), tr("打开%1失败").arg(fileName)); 239 | return; 240 | } 241 | 242 | // 保存头 243 | QTextStream stream(&file); 244 | int columnCnt = model->columnCount(QModelIndex()); 245 | for (int i = 0; i < columnCnt; ++i) { 246 | stream << model->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); 247 | if (i != columnCnt -1) { 248 | stream << ","; 249 | } else { 250 | stream << "\r\n"; 251 | } 252 | } 253 | 254 | // 保存内容 255 | auto &objList = model->getObjList(); 256 | for (auto iter = objList.begin(); iter != objList.end(); ++iter) { 257 | doSave(stream, *iter); 258 | } 259 | } 260 | } 261 | 262 | void ReceiveMessageWidget::onRealTimeSave(bool checked) 263 | { 264 | if (checked) { 265 | QString fileName = QFileDialog::getSaveFileName(this, 266 | tr("保存日志"), tr(""), tr("日志文件(*.csv)")); 267 | if (!fileName.isEmpty()) { 268 | scrollingFile = new ScrollingFile(fileName); 269 | } 270 | } else { 271 | scrollingFile->flush(); 272 | safeDelete(scrollingFile); 273 | } 274 | } 275 | 276 | void ReceiveMessageWidget::onResizeColumns() 277 | { 278 | int columns = model->columnCount(QModelIndex()); 279 | for (int i = 0; i < columns; i++) { 280 | treeView->resizeColumnToContents(i); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /ecanspy3/receive/receivemessagewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef RECEIVEMESSAGEWIDGET_H 2 | #define RECEIVEMESSAGEWIDGET_H 3 | 4 | #include "base/titletreeviewwidget.h" 5 | #include "receive/receivemessagetreemodel.h" 6 | #include "receive/receivemessagefilterproxymodel.h" 7 | #include "utils/scrollingfile.h" 8 | 9 | #include 10 | #include 11 | 12 | #define RECEIVE_FILTER_PROXY_ENABLED (0) 13 | 14 | QT_BEGIN_NAMESPACE 15 | class QTextStream; 16 | QT_END_NAMESPACE 17 | 18 | class ReceiveMessageWidget : public TitleTreeViewWidget 19 | { 20 | Q_OBJECT 21 | public: 22 | enum { 23 | RECEIVE_INTERVAL_MS = 10, 24 | RECEIVE_BUFFER_SIZE = 100, 25 | }; 26 | 27 | ReceiveMessageWidget(QWidget *parent = nullptr); 28 | ~ReceiveMessageWidget(); 29 | 30 | void setFilterList(QList *filterList); 31 | void setCanObjFilter(CanObjFilter *filter); 32 | 33 | QList getColumnWidthList() const; 34 | void setColumnWidthList(QList widthList); 35 | 36 | protected: 37 | void initTitle(); 38 | void initModel(); 39 | void initTimer(); 40 | void initToolButtons(); 41 | 42 | void initColumnsWidth(); 43 | int getTextWidth(QChar ch); 44 | 45 | void doRealTimeSave(const ReceiveMessageTreeModel::Entity &ent); 46 | void doSave(QTextStream &stream, const ReceiveMessageTreeModel::Entity &ent); 47 | 48 | public slots: 49 | void refreshDocument(Document *doc); 50 | void updateFilter(); 51 | void onTimer(); 52 | 53 | void onStartReceive(bool checked); 54 | void onSave(); 55 | void onRealTimeSave(bool checked); 56 | void onResizeColumns(); 57 | 58 | private: 59 | ReceiveMessageTreeModel *model = nullptr; 60 | 61 | #if RECEIVE_FILTER_PROXY_ENABLED 62 | ReceiveMessageFilterProxyModel *proxyModel = nullptr; 63 | #endif 64 | 65 | QTimer *timer = nullptr; 66 | 67 | QPushButton *stopShowButton = nullptr; 68 | QPushButton *clearButton = nullptr; 69 | QPushButton *scrollButton = nullptr; 70 | QPushButton *saveButton = nullptr; 71 | QPushButton *realTimeSaveButton = nullptr; 72 | QPushButton *resizeColumns = nullptr; 73 | 74 | ScrollingFile *scrollingFile = nullptr; 75 | }; 76 | 77 | #endif // RECEIVEMESSAGEWIDGET_H 78 | -------------------------------------------------------------------------------- /ecanspy3/receive/receivewidget.cpp: -------------------------------------------------------------------------------- 1 | #include "receivewidget.h" 2 | 3 | #include 4 | #include 5 | 6 | ReceiveWidget::ReceiveWidget(QWidget *parent) 7 | : QWidget(parent), filterWidget(NULL), receiveMessageWidget(NULL) 8 | { 9 | QSplitter *splitter = new QSplitter(this); 10 | splitter->setOrientation(Qt::Horizontal); 11 | 12 | initFilterWidget(*splitter); 13 | initReceiveMessageWidget(*splitter); 14 | initDispatchSignal(); 15 | 16 | splitter->setStretchFactor(0, 1); 17 | splitter->setStretchFactor(1, 4); 18 | 19 | QVBoxLayout *layout = new QVBoxLayout; 20 | layout->addWidget(splitter); 21 | layout->setMargin(0); 22 | setLayout(layout); 23 | } 24 | 25 | void ReceiveWidget::initFilterWidget(QSplitter &splitter) 26 | { 27 | filterWidget = new FilterWidget(this); 28 | splitter.addWidget(filterWidget); 29 | } 30 | 31 | void ReceiveWidget::initReceiveMessageWidget(QSplitter &splitter) 32 | { 33 | receiveMessageWidget = new ReceiveMessageWidget(this); 34 | splitter.addWidget(receiveMessageWidget); 35 | 36 | receiveMessageWidget->setFilterList(filterWidget->getFilterList()); 37 | receiveMessageWidget->setCanObjFilter(filterWidget->getCanObjFilter()); 38 | } 39 | 40 | void ReceiveWidget::initDispatchSignal() 41 | { 42 | connect(filterWidget->getFilterTableModel(), SIGNAL(updateFilter()), receiveMessageWidget, 43 | SLOT(updateFilter())); 44 | } 45 | 46 | ReceiveMessageWidget *ReceiveWidget::getReceiveMessageWidget() 47 | { 48 | return receiveMessageWidget; 49 | } 50 | 51 | FilterWidget *ReceiveWidget::getFilterWidget() 52 | { 53 | return filterWidget; 54 | } 55 | -------------------------------------------------------------------------------- /ecanspy3/receive/receivewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef RECEIVEWIDGET_H 2 | #define RECEIVEWIDGET_H 3 | 4 | #include 5 | #include 6 | 7 | #include "filterwidget.h" 8 | #include "receivemessagewidget.h" 9 | 10 | class ReceiveWidget : public QWidget 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit ReceiveWidget(QWidget *parent = nullptr); 15 | 16 | void initFilterWidget(QSplitter &splitter); 17 | void initReceiveMessageWidget(QSplitter &splitter); 18 | void initDispatchSignal(); 19 | 20 | ReceiveMessageWidget *getReceiveMessageWidget(); 21 | FilterWidget *getFilterWidget(); 22 | 23 | signals: 24 | 25 | public slots: 26 | 27 | protected: 28 | FilterWidget *filterWidget; 29 | ReceiveMessageWidget *receiveMessageWidget; 30 | }; 31 | 32 | #endif // RECEIVEWIDGET_H 33 | -------------------------------------------------------------------------------- /ecanspy3/send/checkedbuttondelegate.cpp: -------------------------------------------------------------------------------- 1 | #include "checkedbuttondelegate.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | CheckedButtonDelegate::CheckedButtonDelegate(QObject *parent) : QStyledItemDelegate(parent) 9 | { 10 | 11 | } 12 | 13 | 14 | void CheckedButtonDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 15 | { 16 | QStyleOptionViewItem viewOption(option); 17 | initStyleOption(&viewOption, index); 18 | if (option.state.testFlag(QStyle::State_HasFocus)) { 19 | viewOption.state = viewOption.state ^ QStyle::State_HasFocus; 20 | } 21 | 22 | if (index.column() == checkedButtonColumn) { 23 | bool data = index.model()->data(index, Qt::UserRole).toBool(); 24 | 25 | QStyleOptionButton checkedButtonStyle; 26 | checkedButtonStyle.state = data ? QStyle::State_On : QStyle::State_Off; 27 | checkedButtonStyle.iconSize = QSize(20, 20); 28 | checkedButtonStyle.rect = option.rect; 29 | 30 | QPushButton pushButton; 31 | pushButton.setCheckable(true); 32 | pushButton.setChecked(data); 33 | 34 | QApplication::style()->drawPrimitive(QStyle::PE_IndicatorCheckBox, &checkedButtonStyle, painter, &pushButton); 35 | return; 36 | } 37 | 38 | QStyledItemDelegate::paint(painter, option, index); 39 | } 40 | 41 | bool CheckedButtonDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) 42 | { 43 | QRect decorationRect = option.rect; 44 | QMouseEvent *mouseEvent = static_cast(event); 45 | if (event->type() == QEvent::MouseButtonPress && decorationRect.contains(mouseEvent->pos())) { 46 | if (index.column() == checkedButtonColumn) { 47 | bool data = model->data(index, Qt::UserRole).toBool(); 48 | model->setData(index, !data, Qt::UserRole); 49 | emit checkedButtonClicked(index.row(), checkedButtonColumn); 50 | } 51 | } 52 | return QStyledItemDelegate::editorEvent(event, model, option, index); 53 | } 54 | -------------------------------------------------------------------------------- /ecanspy3/send/checkedbuttondelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef CHECKEDBUTTONDELEGATE_H 2 | #define CHECKEDBUTTONDELEGATE_H 3 | 4 | #include 5 | 6 | class CheckedButtonDelegate : public QStyledItemDelegate 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit CheckedButtonDelegate(QObject *parent = nullptr); 11 | 12 | void setCheckedButotnColumn(int column) 13 | { 14 | checkedButtonColumn = column; 15 | } 16 | 17 | signals: 18 | void checkedButtonClicked(int row, int col); 19 | 20 | public slots: 21 | 22 | // QAbstractItemDelegate interface 23 | public: 24 | void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; 25 | bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index); 26 | 27 | private: 28 | int checkedButtonColumn = -1; 29 | }; 30 | 31 | #endif // CHECKEDBUTTONDELEGATE_H 32 | -------------------------------------------------------------------------------- /ecanspy3/send/sendmessagewidget.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDMESSAGEWIDGET_H 2 | #define SENDMESSAGEWIDGET_H 3 | 4 | #include "base/titletableviewwidget.h" 5 | #include "send/txmessagetablemodel.h" 6 | #include "send/checkedbuttondelegate.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define SEND_MESSAGE_TABLE_VIEW_FILTER_ENABLED 1 16 | 17 | class SendMessageWidget : public TitleTableViewWidget 18 | { 19 | Q_OBJECT 20 | public: 21 | explicit SendMessageWidget(QWidget *parent = nullptr); 22 | ~SendMessageWidget(); 23 | 24 | void resetSizePolicy(); 25 | 26 | public slots: 27 | void resetContent(Document *doc, QList msgList); 28 | void sendButtonClicked(int row, int col); 29 | void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); 30 | void updateMessageData(Message *msg); 31 | 32 | void onToggleAllCyclicButton(bool checked); 33 | 34 | #if SEND_MESSAGE_TABLE_VIEW_FILTER_ENABLED 35 | void filterTextChanged(QString text); 36 | void filterColumnChanged(int index); 37 | #endif 38 | 39 | signals: 40 | void refreshCurrMessage(Message *msg, CAN_OBJ *obj); 41 | 42 | protected: 43 | void initTitle(); 44 | void initModel(); 45 | void initTableView(); 46 | void initCan(); 47 | 48 | void initToolButtons(); 49 | 50 | #if SEND_MESSAGE_TABLE_VIEW_FILTER_ENABLED 51 | void initFilter(QHBoxLayout *toolButtonsLayout); 52 | #endif 53 | 54 | private: 55 | void enableSendMessage(int msgId, bool enabled); 56 | int maxWidth = -1; 57 | 58 | protected: 59 | TxMessageTableModel *model = nullptr; 60 | 61 | QPushButton *enableAllCyclicButton; 62 | 63 | #if SEND_MESSAGE_TABLE_VIEW_FILTER_ENABLED 64 | QComboBox *filterColumnCombo = nullptr; 65 | QLineEdit *filterTextEdit = nullptr; 66 | QSortFilterProxyModel *proxyModel = nullptr; 67 | #endif 68 | CheckedButtonDelegate *checkedItemDelegate; 69 | 70 | QPushButton *resizeColumnsButton; 71 | }; 72 | 73 | #endif // SENDMESSAGEWIDGET_H 74 | -------------------------------------------------------------------------------- /ecanspy3/send/sendwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "sendwidget.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | SendWidget::SendWidget(QWidget *parent) : QWidget(parent) 8 | { 9 | QVBoxLayout *layout = new QVBoxLayout; 10 | 11 | QSplitter *splitter = new QSplitter(this); 12 | splitter->setOrientation(Qt::Horizontal); 13 | 14 | initMessageTableView(splitter); 15 | initSignalTableView(splitter); 16 | 17 | layout->addWidget(splitter); 18 | setLayout(layout); 19 | 20 | defaultMaxHeight = maximumHeight(); 21 | setMaximumHeight(250); //设置它,主要为了形成默认的布局 22 | setMinimumHeight(200); 23 | } 24 | 25 | void SendWidget::initMessageTableView(QSplitter *splitter) 26 | { 27 | sendMessageWidget = new SendMessageWidget(this); 28 | splitter->addWidget(sendMessageWidget); 29 | } 30 | 31 | void SendWidget::initSignalTableView(QSplitter *splitter) 32 | { 33 | signalWidget = new SignalWidget(this); 34 | splitter->addWidget(signalWidget); 35 | } 36 | 37 | SendMessageWidget *SendWidget::getSendMessageWidget() 38 | { 39 | return sendMessageWidget; 40 | } 41 | 42 | SignalWidget *SendWidget::getSignalWidget() 43 | { 44 | return signalWidget; 45 | } 46 | 47 | void SendWidget::resetSizePolicy() 48 | { 49 | setMaximumHeight(defaultMaxHeight); 50 | sendMessageWidget->resetSizePolicy(); 51 | } 52 | -------------------------------------------------------------------------------- /ecanspy3/send/sendwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef SENDWIDGET_H 2 | #define SENDWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "sendmessagewidget.h" 9 | #include "signalwidget.h" 10 | 11 | class SendWidget : public QWidget 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit SendWidget(QWidget *parent = nullptr); 16 | 17 | void initMessageTableView(QSplitter *splitter); 18 | void initSignalTableView(QSplitter *splitter); 19 | 20 | SendMessageWidget *getSendMessageWidget(); 21 | SignalWidget *getSignalWidget(); 22 | 23 | void resetSizePolicy(); 24 | 25 | signals: 26 | 27 | public slots: 28 | 29 | private: 30 | SendMessageWidget *sendMessageWidget; 31 | SignalWidget *signalWidget; 32 | 33 | int defaultMaxHeight; 34 | }; 35 | 36 | #endif // SENDWIDGET_H 37 | -------------------------------------------------------------------------------- /ecanspy3/send/signaltablemodel.cpp: -------------------------------------------------------------------------------- 1 | #include "send/signaltablemodel.h" 2 | #include "utils/signalaccessor.h" 3 | 4 | const QString SignalTableModel::columnTitles[SignalTableModel::COLUMN_COUNT] = { 5 | "Description", "In", "Dc", "Step", "Physical Value", "Physical Unit", "Raw Value(Hex)" 6 | }; 7 | 8 | SignalTableModel::SignalTableModel(QObject *parent) 9 | : QAbstractTableModel(parent), msg(NULL), obj(NULL) 10 | { 11 | 12 | } 13 | 14 | 15 | int SignalTableModel::rowCount(const QModelIndex &parent) const 16 | { 17 | (void)parent; 18 | if (!msg) return 0; 19 | int size = msg->getSignalList().size(); 20 | return size; 21 | } 22 | 23 | int SignalTableModel::columnCount(const QModelIndex &parent) const 24 | { 25 | (void)parent; 26 | return COLUMN_COUNT; 27 | } 28 | 29 | QVariant SignalTableModel::data(const QModelIndex &index, int role) const 30 | { 31 | if (!msg || !index.isValid() || (Qt::DisplayRole != role && Qt::EditRole != role)) return QVariant(); 32 | const Signal *signal = signalAt(index.row()); 33 | int column = index.column(); 34 | if (!signal) return QVariant(); 35 | 36 | if (0 == column) { //信号名称 37 | return signal->getName(); 38 | } else if (1 == column || 2 == column) { //+,-按钮 39 | return QVariant(); 40 | } else if (3 == column) { //步长 41 | return QString("%1").arg(mores.at(index.row()).step); 42 | } else if (4 == column) { //物理值 43 | return getPhysicalColumnValue(index.row()); 44 | } else if (5 == column) { 45 | return signal->getUnit(); 46 | } else if (6 == column) { //原始值 47 | return QString("%1").arg(getRawValue(index.row()), 0, 16).toUpper(); 48 | } else { 49 | return QVariant(); 50 | } 51 | } 52 | 53 | QVariant SignalTableModel::headerData(int section, Qt::Orientation orientation, int role) const 54 | { 55 | if (Qt::DisplayRole == role) { 56 | if (Qt::Horizontal == orientation) { 57 | return columnTitles[section]; 58 | } else { 59 | return QString("%1 ").arg(section+1); 60 | } 61 | } 62 | return QVariant(); 63 | } 64 | 65 | void SignalTableModel::setMessage(Message *msg, CAN_OBJ *obj) 66 | { 67 | beginResetModel(); 68 | this->msg = msg; 69 | this->obj = obj; 70 | setMores(); 71 | endResetModel(); 72 | } 73 | 74 | Signal *SignalTableModel::signalAt(int index) const 75 | { 76 | if (!msg || index >= msg->getSignalList().size()) return NULL; 77 | return (msg->getSignalList().begin() + index).value(); 78 | } 79 | 80 | void SignalTableModel::setMores() 81 | { 82 | int rowCnt = rowCount(); 83 | for (int i = 0; i < rowCnt; ++i) { 84 | mores.append(MoreInfo()); 85 | } 86 | } 87 | 88 | 89 | bool SignalTableModel::setData(const QModelIndex &index, const QVariant &value, int role) 90 | { 91 | if (!index.isValid() && role != Qt::EditRole) { 92 | return false; 93 | } 94 | 95 | bool flag = false; 96 | bool ok = false; 97 | int column = index.column(); 98 | 99 | if (3 == column) { //设置步长 100 | auto step = value.toDouble(&ok); 101 | if (ok) { 102 | mores[index.row()].step = step; 103 | flag = true; 104 | } 105 | } else if (6 == column) { //物理值 106 | QByteArray array; 107 | array.append(value.toString()); 108 | auto rawValue = array.toUInt(&ok, 16); 109 | if (ok) { 110 | setRawValue(index.row(), rawValue); 111 | flag = true; 112 | } 113 | } else if (4 == column) { 114 | switch (value.type()) { 115 | case QVariant::Int: //设置的是状态 116 | { 117 | auto idx = value.toInt(&ok); 118 | auto &tbl = signalAt(index.row())->getValueDescTable(); 119 | if (ok) { 120 | setRawValue(index.row(), (tbl.begin()+idx).key()); 121 | } 122 | break; 123 | } 124 | case QVariant::Double: //设置的是物理值 125 | { 126 | auto phyVal = value.toDouble(&ok); 127 | if (ok) { 128 | setPhysicalValue(index.row(), phyVal); 129 | } 130 | break; 131 | } 132 | default: 133 | break; 134 | } 135 | } 136 | 137 | if (flag) { 138 | emit dataChanged(index, index); 139 | } 140 | 141 | return flag; 142 | } 143 | 144 | Qt::ItemFlags SignalTableModel::flags(const QModelIndex &index) const 145 | { 146 | Qt::ItemFlags oldFlags = QAbstractTableModel::flags(index); 147 | if (!index.isValid()) return oldFlags; 148 | 149 | int column = index.column(); 150 | if (3 == column || 6 == column || 4 == column) { 151 | return oldFlags | Qt::ItemIsEditable; 152 | } 153 | 154 | return oldFlags; 155 | } 156 | 157 | void SignalTableModel::incStep(int row) 158 | { 159 | if (row >= rowCount()) return; 160 | auto rawVal = getRawValue(row); 161 | rawVal += mores.at(row).step; 162 | setRawValue(row, rawVal); 163 | } 164 | 165 | void SignalTableModel::decStep(int row) 166 | { 167 | if (row >= rowCount()) return; 168 | auto rawVal = getRawValue(row); 169 | rawVal -= mores.at(row).step; 170 | setRawValue(row, rawVal); 171 | } 172 | 173 | double SignalTableModel::getPhysicalValue(int row) const 174 | { 175 | auto signal = signalAt(row); 176 | auto rawValue = SignalAccessor::getSignalValue(this->obj, signal); 177 | return rawValue * signal->getFactor() + signal->getOffset(); 178 | } 179 | 180 | void SignalTableModel::setPhysicalValue(int row, double phyVal) 181 | { 182 | auto signal = signalAt(row); 183 | double rawValue = (phyVal - signal->getOffset()) / signal->getFactor(); 184 | if (fabs(rawValue - (int)rawValue) < 0.000001) { 185 | setRawValue(row, rawValue); 186 | } else { 187 | double phyValL = ((int)rawValue) * signal->getFactor() + signal->getOffset(); 188 | double phyValR = ((int)rawValue+1) * signal->getFactor() + signal->getOffset(); 189 | 190 | if (fabs(phyValL - phyVal) < fabs(phyValR - phyVal)) { 191 | setRawValue(row, (int)rawValue); 192 | } else { 193 | setRawValue(row, (int)rawValue+1); 194 | } 195 | } 196 | } 197 | 198 | QString SignalTableModel::getPhysicalColumnValue(int row) const 199 | { 200 | auto signal = signalAt(row); 201 | auto rawValue = getRawValue(row); 202 | 203 | QString ret = signal->getValueDescTable().value(rawValue, ""); 204 | if (ret.isEmpty()) { 205 | return QString("%1").arg(getPhysicalValue(row)); 206 | } else { 207 | return ret.trimmed(); 208 | } 209 | } 210 | 211 | quint64 SignalTableModel::getRawValue(int row) const 212 | { 213 | auto signal = signalAt(row); 214 | return SignalAccessor::getSignalValue(this->obj, signal); 215 | } 216 | 217 | void SignalTableModel::setRawValue(int row, quint64 value) 218 | { 219 | auto signal = signalAt(row); 220 | SignalAccessor::setSignalValue(this->obj, signal, value); 221 | emit dataChanged(index(row, 4), index(row, 6)); 222 | } 223 | -------------------------------------------------------------------------------- /ecanspy3/send/signaltablemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNALTABLEMODEL_H 2 | #define SIGNALTABLEMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include "UsbCanUtil.h" 11 | 12 | 13 | class SignalTableModel : public QAbstractTableModel 14 | { 15 | Q_OBJECT 16 | public: 17 | enum {COLUMN_COUNT=7}; 18 | 19 | SignalTableModel(QObject *parent = nullptr); 20 | 21 | // QAbstractItemModel interface 22 | public: 23 | int rowCount(const QModelIndex &parent=QModelIndex()) const; 24 | int columnCount(const QModelIndex &parent) const; 25 | QVariant data(const QModelIndex &index, int role) const; 26 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 27 | bool setData(const QModelIndex &index, const QVariant &value, int role); 28 | Qt::ItemFlags flags(const QModelIndex &index) const; 29 | 30 | void incStep(int row); 31 | void decStep(int row); 32 | 33 | double getPhysicalValue(int row) const; 34 | void setPhysicalValue(int row, double value); 35 | QString getPhysicalColumnValue(int row) const; 36 | 37 | quint64 getRawValue(int row) const; 38 | void setRawValue(int row, quint64 value); 39 | 40 | Signal* getSignal(int row) const 41 | { 42 | return signalAt(row); 43 | } 44 | 45 | void setMessage(Message *msg, CAN_OBJ *obj); 46 | Message *getMessage() 47 | { 48 | return this->msg; 49 | } 50 | 51 | protected: 52 | Signal *signalAt(int index) const; 53 | void setMores(); 54 | 55 | protected: 56 | Message *msg; 57 | CAN_OBJ *obj; 58 | static const QString columnTitles[COLUMN_COUNT]; 59 | 60 | struct MoreInfo { 61 | double step = 1.0; 62 | }; 63 | 64 | QList mores; 65 | }; 66 | 67 | #endif // SIGNALTABLEMODEL_H 68 | -------------------------------------------------------------------------------- /ecanspy3/send/signaltableviewphysicalvaluedelegate.cpp: -------------------------------------------------------------------------------- 1 | #include "signaltableviewphysicalvaluedelegate.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "signaltablemodel.h" 7 | 8 | SignalTableViewPhysicalValueDelegate::SignalTableViewPhysicalValueDelegate(QObject *parent) : QItemDelegate(parent) 9 | { 10 | 11 | } 12 | 13 | 14 | QWidget *SignalTableViewPhysicalValueDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, 15 | const QModelIndex &index) const 16 | { 17 | Q_UNUSED(option); 18 | Q_UNUSED(index); 19 | 20 | const SignalTableModel *model = static_cast(index.model()); 21 | auto signal = model->getSignal(index.row()); 22 | if (NULL == signal) return NULL; 23 | 24 | auto &tbl = signal->getValueDescTable(); 25 | if (tbl.size() != 0 && signal->getUnit().isEmpty()) { 26 | return createComboBoxEditor(parent, signal); 27 | } else { 28 | return createDoubleSpinBoxEditor(parent, signal); 29 | } 30 | } 31 | 32 | //Model -> Editor 33 | void SignalTableViewPhysicalValueDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const 34 | { 35 | QString value = index.model()->data(index, Qt::EditRole).toString(); 36 | QComboBox *comboBox = qobject_cast(editor); 37 | if (comboBox) { 38 | for (int i = 0; i < comboBox->count(); i++) { 39 | if (value == comboBox->itemText(i)) { 40 | comboBox->setCurrentIndex(i); 41 | break; 42 | } 43 | } 44 | } else { 45 | QDoubleSpinBox *spinBox = qobject_cast(editor); 46 | if (spinBox) { 47 | spinBox->setValue(value.toDouble()); 48 | } 49 | } 50 | } 51 | 52 | // Editor -> Model 53 | void SignalTableViewPhysicalValueDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, 54 | const QModelIndex &index) const 55 | { 56 | QComboBox *comboBox = qobject_cast(editor); 57 | if (comboBox) { 58 | auto curIndex = comboBox->currentIndex(); 59 | if (curIndex != -1) { 60 | model->setData(index, curIndex, Qt::EditRole); 61 | } 62 | } else { 63 | QDoubleSpinBox *spinBox = qobject_cast(editor); 64 | spinBox->interpretText(); 65 | model->setData(index, spinBox->value(), Qt::EditRole); 66 | } 67 | } 68 | 69 | void SignalTableViewPhysicalValueDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const 70 | { 71 | Q_UNUSED(editor); 72 | Q_UNUSED(index); 73 | editor->setGeometry(option.rect); 74 | } 75 | 76 | QWidget *SignalTableViewPhysicalValueDelegate::createComboBoxEditor(QWidget *parent, Signal *signal) const 77 | { 78 | auto &tbl = signal->getValueDescTable(); 79 | QComboBox *editor = new QComboBox(parent); 80 | for (auto iter = tbl.begin(); iter != tbl.end(); ++iter) { 81 | editor->insertItem(editor->count(), iter.value()); 82 | } 83 | return editor; 84 | } 85 | 86 | QWidget *SignalTableViewPhysicalValueDelegate::createDoubleSpinBoxEditor(QWidget *parent, Signal *signal) const 87 | { 88 | QDoubleSpinBox *editor = new QDoubleSpinBox(parent); 89 | editor->setMinimum(signal->getMinimum()); 90 | editor->setMaximum(signal->getMaximum()); 91 | editor->setDecimals(calucSignalFactor(signal)); 92 | return editor; 93 | } 94 | 95 | int SignalTableViewPhysicalValueDelegate::calucSignalFactor(Signal *signal) const 96 | { 97 | QString str = QString("%1").arg(signal->getFactor()); 98 | auto dotPos = str.indexOf("."); 99 | if (-1 == dotPos) { 100 | return 0; 101 | } else { 102 | return str.size() - dotPos - 1; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /ecanspy3/send/signaltableviewphysicalvaluedelegate.h: -------------------------------------------------------------------------------- 1 | #ifndef COMBOBOXDELEGATE_H 2 | #define COMBOBOXDELEGATE_H 3 | 4 | #include 5 | #include 6 | 7 | class SignalTableViewPhysicalValueDelegate : public QItemDelegate 8 | { 9 | public: 10 | SignalTableViewPhysicalValueDelegate(QObject *parent=nullptr); 11 | 12 | // QAbstractItemDelegate interface 13 | public: 14 | QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; 15 | void setEditorData(QWidget *editor, const QModelIndex &index) const; 16 | void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; 17 | void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; 18 | 19 | protected: 20 | QWidget *createComboBoxEditor(QWidget *parent, Signal *signal) const; 21 | QWidget *createDoubleSpinBoxEditor(QWidget *parent, Signal *signal) const; 22 | int calucSignalFactor(Signal *signal) const; 23 | }; 24 | 25 | #endif // COMBOBOXDELEGATE_H 26 | -------------------------------------------------------------------------------- /ecanspy3/send/signalwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "signalwidget.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "signaltableviewphysicalvaluedelegate.h" 7 | 8 | SignalWidget::SignalWidget(QWidget *parent) 9 | : TitleTableViewWidget(parent) 10 | { 11 | title->setText(tr("信号")); 12 | 13 | initModel(); 14 | } 15 | 16 | void SignalWidget::initModel() 17 | { 18 | model = new SignalTableModel(this); 19 | tableView->setModel(model); 20 | tableView->resizeColumnsToContents(); 21 | connect(model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), 22 | this, SLOT(modelDataChanged(QModelIndex,QModelIndex))); 23 | } 24 | 25 | void SignalWidget::setMessage(Message *msg, CAN_OBJ *obj) 26 | { 27 | model->setMessage(msg, obj); 28 | setAllButtons(); 29 | setAllPhyDelegate(); 30 | tableView->scrollToTop(); 31 | tableView->resizeColumnsToContents(); 32 | } 33 | 34 | void SignalWidget::onIncButton() 35 | { 36 | model->incStep(sender()->property("row").toInt()); 37 | emit updateMessageData(model->getMessage()); 38 | } 39 | 40 | void SignalWidget::onDecButton() 41 | { 42 | model->decStep(sender()->property("row").toInt()); 43 | emit updateMessageData(model->getMessage()); 44 | } 45 | 46 | void SignalWidget::modelDataChanged(QModelIndex leftTopIndex, QModelIndex rightBottomIndex) 47 | { 48 | Q_UNUSED(leftTopIndex); 49 | Q_UNUSED(rightBottomIndex); 50 | emit updateMessageData(model->getMessage()); 51 | } 52 | 53 | void SignalWidget::setRowButtons(int row) 54 | { 55 | QPushButton *incButton = new QPushButton(tr("+"), this); 56 | incButton->setProperty("row", row); 57 | connect(incButton, SIGNAL(clicked(bool)), this, SLOT(onIncButton())); 58 | 59 | QPushButton *decButton = new QPushButton(tr("-"), this); 60 | decButton->setProperty("row", row); 61 | connect(decButton, SIGNAL(clicked(bool)), this, SLOT(onDecButton())); 62 | 63 | tableView->setIndexWidget(model->index(row, 1), incButton); 64 | tableView->setIndexWidget(model->index(row, 2), decButton); 65 | } 66 | 67 | void SignalWidget::setAllPhyDelegate() 68 | { 69 | int rows = model->rowCount(); 70 | for (int i = 0; i < rows; i++) { 71 | tableView->setItemDelegateForColumn(4, new SignalTableViewPhysicalValueDelegate(this)); 72 | } 73 | } 74 | 75 | void SignalWidget::setAllButtons() 76 | { 77 | int rows = model->rowCount(); 78 | for (int i = 0; i < rows; i++) { 79 | setRowButtons(i); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ecanspy3/send/signalwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNALWIDGET_H 2 | #define SIGNALWIDGET_H 3 | 4 | #include "base/titletableviewwidget.h" 5 | #include "send/signaltablemodel.h" 6 | #include "can/UsbCanUtil.h" 7 | 8 | class SignalWidget : public TitleTableViewWidget 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit SignalWidget(QWidget *parent = nullptr); 13 | 14 | void initModel(); 15 | void setAllButtons(); 16 | void setRowButtons(int row); 17 | void setAllPhyDelegate(); 18 | 19 | signals: 20 | void updateMessageData(Message *msg); 21 | 22 | public slots: 23 | void setMessage(Message *msg, CAN_OBJ *obj); 24 | void onIncButton(); 25 | void onDecButton(); 26 | void modelDataChanged(QModelIndex, QModelIndex); 27 | 28 | protected: 29 | SignalTableModel *model; 30 | }; 31 | 32 | #endif // SIGNALWIDGET_H 33 | -------------------------------------------------------------------------------- /ecanspy3/send/txmessagetablemodel.cpp: -------------------------------------------------------------------------------- 1 | #include "txmessagetablemodel.h" 2 | 3 | QString TxMessageTableModel::columnTitles[TxMessageTableModel::COLUMN_COUNT] = { 4 | "Description", "Tx", "AutoTx", "Rates(ms)", "ID ", "Len", "B1 ", "B2 ", "B3 ", "B4 ", "B5 ", "B6 ", "B7 ", "B8 ", "Type" 5 | }; 6 | 7 | TxMessageTableModel::TxMessageTableModel(Document *_doc, QObject *parent) 8 | : QAbstractTableModel(parent), doc(_doc) 9 | { 10 | 11 | } 12 | 13 | int TxMessageTableModel::rowCount(const QModelIndex &parent) const 14 | { 15 | (void)parent; 16 | return objs.size(); 17 | } 18 | 19 | int TxMessageTableModel::columnCount(const QModelIndex &parent) const 20 | { 21 | (void)parent; 22 | return COLUMN_COUNT; 23 | } 24 | 25 | QVariant TxMessageTableModel::data(const QModelIndex &index, int role) const 26 | { 27 | if (!index.isValid()) { 28 | return QVariant(); 29 | } 30 | if (NULL == doc) { 31 | return QVariant(); 32 | } 33 | if (index.row() >= objs.size()) { 34 | return QVariant(); 35 | } 36 | if (index.column() >= COLUMN_COUNT) { 37 | return QVariant(); 38 | } 39 | if (!(Qt::DisplayRole == role || Qt::UserRole == role)) { 40 | return QVariant(); 41 | } 42 | 43 | auto row = index.row(); 44 | auto col = index.column(); 45 | auto &obj = *objs.at(row); 46 | auto &more = mores.at(row); 47 | 48 | if (0 == col) { //描述 49 | return more.name; 50 | } else if (1 == col) { //发送按钮使能 51 | return more.tx; 52 | } else if (2 == col) { //周期类型 53 | return more.type; 54 | } else if (3 == col) { //周期 55 | return more.period; 56 | } else if (4 == col) { //ID 57 | return QString("%1").arg(obj.ID, 0, 16).toUpper(); 58 | } else if (5 == col) { //报文长度 59 | return obj.DataLen; 60 | } else if (col >= 6 && col <= 13) { //数据 61 | return QString("%1").arg(obj.Data[col-6], 2, 16, QChar('0')).toUpper(); 62 | } else { //帧类型 63 | return obj.ExternFlag ? "Ext Frame" : "Std Frame"; 64 | } 65 | } 66 | 67 | QVariant TxMessageTableModel::headerData(int section, Qt::Orientation orientation, int role) const 68 | { 69 | if (role != Qt::DisplayRole) { 70 | return QVariant(); 71 | } 72 | if (Qt::Horizontal == orientation) { 73 | return columnTitles[section]; 74 | } else { 75 | return QString("%1 ").arg(section+1); 76 | } 77 | } 78 | 79 | Qt::ItemFlags TxMessageTableModel::flags(const QModelIndex &index) const 80 | { 81 | return QAbstractTableModel::flags(index); 82 | } 83 | 84 | 85 | bool TxMessageTableModel::setData(const QModelIndex &index, const QVariant &value, int role) 86 | { 87 | if (!index.isValid() || index.row() >= mores.size()) return false; 88 | if (role == Qt::UserRole) { 89 | if (index.column() == TX_MESSAGE_TABLE_VIEW_TX_EN) { 90 | bool val = value.toBool(); 91 | if (val != mores[index.row()].tx) { 92 | mores[index.row()].tx = val; 93 | emit dataChanged(index, index); 94 | } 95 | return true; 96 | } 97 | } 98 | return false; 99 | } 100 | 101 | void TxMessageTableModel::reset() 102 | { 103 | beginResetModel(); 104 | objs.clear(); 105 | mores.clear(); 106 | endResetModel(); 107 | } 108 | 109 | void TxMessageTableModel::appendRow(CAN_OBJ &obj, const TxMessageTableModel::MoreInfo &more) 110 | { 111 | objs.append(&obj); 112 | mores.append(more); 113 | } 114 | 115 | void TxMessageTableModel::setDocument(Document *doc) 116 | { 117 | this->doc = doc; 118 | } 119 | 120 | Message *TxMessageTableModel::getMessage(QModelIndex index) 121 | { 122 | if (!index.isValid()||!doc) return NULL; 123 | CAN_OBJ *obj = objs.at(index.row()); 124 | return doc->getMessage(obj->ID); 125 | } 126 | 127 | CAN_OBJ *TxMessageTableModel::getCanObj(QModelIndex index) 128 | { 129 | if (!index.isValid()||!doc) return NULL; 130 | CAN_OBJ *obj = const_cast(objs.at(index.row())); 131 | return obj; 132 | } 133 | 134 | void TxMessageTableModel::refreshMessageData(Message *msg) 135 | { 136 | for (int i = 0; i < objs.size(); i++) { 137 | if (objs[i]->ID == msg->getId()) { 138 | emit dataChanged(index(i,6), index(i,13)); 139 | } 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /ecanspy3/send/txmessagetablemodel.h: -------------------------------------------------------------------------------- 1 | #ifndef TXMESSAGETABLEMODEL_H 2 | #define TXMESSAGETABLEMODEL_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #define TX_MESSAGE_TABLE_VIEW_COLUMN_INDEX_NAME (0) 11 | #define TX_MESSAGE_TABLE_VIEW_TX_EN (1) 12 | #define TX_MESSAGE_TABLE_VIEW_MESSAGE_TYPE (2) 13 | #define TX_MESSAGE_TABLE_VIEW_COLUMN_INDEX_ID (4) 14 | 15 | class TxMessageTableModel : public QAbstractTableModel 16 | { 17 | Q_OBJECT 18 | public: 19 | enum { 20 | COLUMN_COUNT = 15 21 | }; 22 | 23 | /*! 24 | * \brief MoreInfo 存放报文附加信息 25 | */ 26 | struct MoreInfo{ 27 | QString name; 28 | bool tx; 29 | QString type; 30 | qint32 period; 31 | }; 32 | 33 | TxMessageTableModel(Document *doc = NULL, QObject *parent=NULL); 34 | 35 | // QAbstractItemModel interface 36 | public: 37 | int rowCount(const QModelIndex &parent) const; 38 | int columnCount(const QModelIndex &parent) const; 39 | QVariant data(const QModelIndex &index, int role) const; 40 | QVariant headerData(int section, Qt::Orientation orientation, int role) const; 41 | Qt::ItemFlags flags(const QModelIndex &index) const; 42 | bool setData(const QModelIndex &index, const QVariant &value, int role); 43 | 44 | void reset(); 45 | void appendRow(CAN_OBJ &obj, const MoreInfo &more); 46 | void setDocument(Document *doc); 47 | 48 | Message *getMessage(QModelIndex index); 49 | CAN_OBJ *getCanObj(QModelIndex index); 50 | 51 | void refreshMessageData(Message *msg); 52 | 53 | void beginResetModel() 54 | { 55 | QAbstractTableModel::beginResetModel(); 56 | } 57 | 58 | void endResetModel() 59 | { 60 | QAbstractTableModel::endResetModel(); 61 | } 62 | 63 | QList objs; //!< 存放要发送的数据信号 64 | QList mores; //!< 存放附加信息 65 | 66 | Document *doc; 67 | private: 68 | static QString columnTitles[COLUMN_COUNT]; 69 | 70 | // QAbstractItemModel interface 71 | public: 72 | 73 | 74 | }; 75 | 76 | #endif // TXMESSAGETABLEMODEL_H 77 | -------------------------------------------------------------------------------- /ecanspy3/settings/canconfigdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "canconfigdialog.h" 2 | #include "ui_canconfigdialog.h" 3 | 4 | #include "UsbCanUtil.h" 5 | 6 | CanConfigDialog::CanConfigDialog(QWidget *parent) : 7 | QDialog(parent), 8 | ui(new Ui::CanConfigDialog) 9 | { 10 | ui->setupUi(this); 11 | initBaurateCombo(); 12 | } 13 | 14 | CanConfigDialog::~CanConfigDialog() 15 | { 16 | delete ui; 17 | } 18 | 19 | QString CanConfigDialog::getBaurate() 20 | { 21 | return ui->baurateComboBox->currentText(); 22 | } 23 | 24 | void CanConfigDialog::on_okPushButton_clicked() 25 | { 26 | accept(); 27 | } 28 | 29 | void CanConfigDialog::initBaurateCombo() 30 | { 31 | QList supportedBaurateList = UsbCanUtil::GetInstance().GetSupportedBaurateList(); 32 | foreach (QString baurate, supportedBaurateList) { 33 | ui->baurateComboBox->addItem(baurate); 34 | } 35 | 36 | int index = supportedBaurateList.indexOf(UsbCanUtil::GetInstance().GetBaurate()); 37 | ui->baurateComboBox->setCurrentIndex(index); 38 | } 39 | -------------------------------------------------------------------------------- /ecanspy3/settings/canconfigdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef CANCONFIGDIALOG_H 2 | #define CANCONFIGDIALOG_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class CanConfigDialog; 8 | } 9 | 10 | class CanConfigDialog : public QDialog 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit CanConfigDialog(QWidget *parent = 0); 16 | ~CanConfigDialog(); 17 | 18 | QString getBaurate(); 19 | 20 | private slots: 21 | void on_okPushButton_clicked(); 22 | 23 | private: 24 | void initBaurateCombo(); 25 | 26 | private: 27 | Ui::CanConfigDialog *ui; 28 | }; 29 | 30 | #endif // CANCONFIGDIALOG_H 31 | -------------------------------------------------------------------------------- /ecanspy3/settings/canconfigdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | CanConfigDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 278 10 | 71 11 | 12 | 13 | 14 | 配置CAN 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 0 24 | 0 25 | 26 | 27 | 28 | 波特率: 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 0 37 | 0 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 确定 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /ecanspy3/settings/projectfile.h: -------------------------------------------------------------------------------- 1 | #ifndef PROJECTFILE_H 2 | #define PROJECTFILE_H 3 | 4 | #include "database/dbctablemodel.h" 5 | #include "receive/filtertablemodel.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #define DBC_MODEL_NAME_TRANSMIT "transmit" 13 | #define DBC_MODEL_NAME_RECEIVE "receive" 14 | #define DBC_MODEL_NAME_DATABASE "database" 15 | 16 | class ProjectFile : public QObject 17 | { 18 | Q_OBJECT 19 | public: 20 | 21 | enum ErrorCode { 22 | ERROR_CODE_SUCCESS = 0, 23 | ERROR_CODE_FILE_NOT_EXIST, 24 | ERROR_CODE_FILE_CREATE_FAILED, 25 | ERROR_CODE_PARSE_ERROR 26 | }; 27 | 28 | explicit ProjectFile(QObject *parent = nullptr); 29 | 30 | /*! 31 | * \brief save 保存当前项目数据 32 | * 33 | * 保存数据时候,需要设置号相应的Model 34 | * 35 | * \param projectFile 要加的文件文件所在路径 36 | * \retval 返回错误码, 如果返回ERROR_CODE_SUCCESS,则说明保存成功 37 | */ 38 | ErrorCode save(const QString &projectFile); 39 | 40 | /*! 41 | * \brief load 加载当前项目数据 42 | * \param projectFile 要加的文件文件所在路径 43 | * \retval 返回错误码, 如果返回ERROR_CODE_SUCCESS,则说明保存成功 44 | */ 45 | ErrorCode load(const QString &projectFile); 46 | 47 | void setDBCFile(QString dbcFile) 48 | { 49 | this->dbcFile = dbcFile; 50 | } 51 | 52 | QString getDBCFile() const 53 | { 54 | return this->dbcFile; 55 | } 56 | 57 | void setTransmitTableModel(DBCTableModel *transmitTableModel) 58 | { 59 | this->transmitTableModel = transmitTableModel; 60 | } 61 | 62 | void setReceiveTableModel(DBCTableModel *receiveTableModel) 63 | { 64 | this->receiveTableModel = receiveTableModel; 65 | } 66 | 67 | void setDatabaseTableModel(DBCTableModel *databaseTableModel) 68 | { 69 | this->databaseTableModel = databaseTableModel; 70 | } 71 | 72 | void setFilterTableModel(FilterTableModel *filterTableModel) 73 | { 74 | this->filterTableModel = filterTableModel; 75 | } 76 | 77 | void setCurrDatabasePageIndex(int currDatabasePageIndex) 78 | { 79 | this->currDatabasePageIndex = currDatabasePageIndex; 80 | } 81 | 82 | int getCurrDatabasePageIndex() 83 | { 84 | return this->currDatabasePageIndex; 85 | } 86 | 87 | QList getTransmitMsgIdList() 88 | { 89 | return transmitMsgIdList; 90 | } 91 | 92 | QList getReceiveMsgIdList() 93 | { 94 | return receiveMsgIdList; 95 | } 96 | 97 | QList getDatabaseMsgIdList() 98 | { 99 | return databaseMsgIdList; 100 | } 101 | 102 | QList getFilterEntList() 103 | { 104 | return this->filterEntList; 105 | } 106 | 107 | QString getBaurate() const 108 | { 109 | return this->baurate; 110 | } 111 | 112 | void setBaurate(QString baurate) 113 | { 114 | this->baurate = baurate; 115 | } 116 | 117 | signals: 118 | 119 | public slots: 120 | 121 | private: 122 | QDomElement createDatabaseElement(QDomDocument &doc); 123 | QDomElement createTextElement(QDomDocument &doc, const QString &tagName, const QString &innerText); 124 | QDomElement createDBCTableModel(QDomDocument &doc, const QString &name, DBCTableModel *model); 125 | 126 | QDomElement createFilterElement(QDomDocument &doc); 127 | QDomElement createFilterTableModelElement(QDomDocument &doc, FilterTableModel *model); 128 | 129 | QDomElement createCanElement(QDomDocument &doc); 130 | QDomElement createCanBaurateElement(QDomDocument &doc); 131 | 132 | ErrorCode doParseDatabase(QDomElement databaseElem); 133 | ErrorCode doParseFilter(QDomElement filterElem); 134 | ErrorCode doParseCan(QDomElement canElem); 135 | 136 | private: 137 | // Database相关配置 138 | QString dbcFile; //!< 当前DBC文件名称 139 | 140 | DBCTableModel *transmitTableModel = nullptr; //!< 发送Database模型 141 | DBCTableModel *receiveTableModel = nullptr; //!< 接收Database模型 142 | DBCTableModel *databaseTableModel = nullptr; //!< Database模型 143 | FilterTableModel *filterTableModel = nullptr; //!< 过滤TableModel 144 | 145 | QList transmitMsgIdList; 146 | QList receiveMsgIdList; 147 | QList databaseMsgIdList; 148 | QList filterEntList; 149 | 150 | int currDatabasePageIndex = 2; //!< Database DockWidget当前页面索引 151 | QString baurate; 152 | }; 153 | 154 | #endif // PROJECTFILE_H 155 | -------------------------------------------------------------------------------- /ecanspy3/settings/themesettingdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "themesettingdialog.h" 2 | #include "ui_themesettingdialog.h" 3 | 4 | #include 5 | 6 | #define THEME_FILE_DEFAULT tr("") 7 | #define THEME_NAME_DEFAULT tr("default") 8 | #define THEME_SNAPSHOT_DEFAULT tr(":/themes/white_snapshot.PNG") 9 | 10 | #define THEME_FILE_BLACK tr(":/themes/dracula.qss") 11 | #define THEME_NAME_BLACK tr("black") 12 | #define THEME_SNAPSHOT_BLACK tr(":/themes/black_snapshot.png") 13 | 14 | ThemeSettingDialog::ThemeSettingDialog(QWidget *parent) : 15 | QDialog(parent), 16 | ui(new Ui::ThemeSettingDialog) 17 | { 18 | ui->setupUi(this); 19 | 20 | initTheme(ui->defaultThemeLabel, ui->defaultRadioButton, 21 | THEME_NAME_DEFAULT, THEME_FILE_DEFAULT, THEME_SNAPSHOT_DEFAULT); 22 | initTheme(ui->blackThemeLabel, ui->blackRadioButton, 23 | THEME_NAME_BLACK, THEME_FILE_BLACK, THEME_SNAPSHOT_BLACK); 24 | } 25 | 26 | ThemeSettingDialog::~ThemeSettingDialog() 27 | { 28 | delete ui; 29 | } 30 | 31 | void ThemeSettingDialog::initTheme(QLabel *label, QRadioButton *radio, 32 | QString themeName, QString themeStylesheet, 33 | QString themeSnapshot) 34 | { 35 | Entity entity; 36 | entity.themeName = themeName; 37 | entity.themeStyleSheet = themeStylesheet; 38 | entity.showLabel = label; 39 | entity.radioButton = radio; 40 | entity.themeSnapshot = themeSnapshot; 41 | themeMap.insert(themeName, entity); 42 | 43 | updateSnapshot(label, themeSnapshot); 44 | } 45 | 46 | void ThemeSettingDialog::updateSnapshot(QLabel *showLabel, QString themeSnapshot) 47 | { 48 | if (!showLabel) return; 49 | 50 | int width = showLabel->width(); 51 | int height = showLabel->height(); 52 | 53 | QPixmap px; 54 | px.load(themeSnapshot); 55 | 56 | showLabel->setPixmap(px.scaled(width, height, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); 57 | } 58 | 59 | QString ThemeSettingDialog::getTheme() const 60 | { 61 | return this->currTheme; 62 | } 63 | 64 | void ThemeSettingDialog::setTheme(QString themeName) 65 | { 66 | auto iter = themeMap.find(themeName); 67 | if (iter != themeMap.end()) { 68 | this->currTheme = themeName; 69 | auto &entity = iter.value(); 70 | if (entity.radioButton) { 71 | entity.radioButton->setChecked(true); 72 | } 73 | } 74 | } 75 | 76 | QString ThemeSettingDialog::getThemeStyleSheet(QString themeName) 77 | { 78 | auto iter = themeMap.find(themeName); 79 | if (iter != themeMap.end()) { 80 | qDebug() << tr("ThemeSettingDialog::getThemeStyleSheet -> [%1]").arg(iter.value().themeStyleSheet); 81 | return iter.value().themeStyleSheet; 82 | } 83 | 84 | qDebug() << tr("ThemeSettingDialog::getThemeStyleSheet -> [%1]").arg(THEME_FILE_DEFAULT); 85 | return THEME_FILE_DEFAULT; 86 | } 87 | 88 | QString ThemeSettingDialog::getCurrThemeStyleSheet() 89 | { 90 | return getThemeStyleSheet(this->currTheme); 91 | } 92 | 93 | void ThemeSettingDialog::on_okPushButton_clicked() 94 | { 95 | accept(); 96 | } 97 | 98 | void ThemeSettingDialog::on_cancelPushButton_clicked() 99 | { 100 | reject(); 101 | } 102 | 103 | void ThemeSettingDialog::on_defaultRadioButton_clicked(bool checked) 104 | { 105 | if (checked) { 106 | this->currTheme = THEME_NAME_DEFAULT; 107 | } 108 | } 109 | 110 | void ThemeSettingDialog::on_blackRadioButton_clicked(bool checked) 111 | { 112 | if (checked) { 113 | this->currTheme = THEME_NAME_BLACK; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /ecanspy3/settings/themesettingdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef THEMESETTINGDIALOG_H 2 | #define THEMESETTINGDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace Ui { 10 | class ThemeSettingDialog; 11 | } 12 | 13 | QT_BEGIN_NAMESPACE 14 | class QLabel; 15 | QT_END_NAMESPACE 16 | 17 | class ThemeSettingDialog : public QDialog 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | struct Entity 23 | { 24 | QString themeName; 25 | QString themeStyleSheet; 26 | QString themeSnapshot; 27 | QLabel *showLabel; 28 | QRadioButton *radioButton; 29 | }; 30 | 31 | explicit ThemeSettingDialog(QWidget *parent = 0); 32 | ~ThemeSettingDialog(); 33 | 34 | QString getTheme() const; 35 | void setTheme(QString themeName); 36 | QString getThemeStyleSheet(QString themeName); 37 | QString getCurrThemeStyleSheet(); 38 | 39 | private slots: 40 | void on_okPushButton_clicked(); 41 | void on_cancelPushButton_clicked(); 42 | void on_defaultRadioButton_clicked(bool checked); 43 | void on_blackRadioButton_clicked(bool checked); 44 | 45 | private: 46 | void initTheme(QLabel *label, QRadioButton *radio, 47 | QString themeName, QString themeStylesheet, 48 | QString themeSnapshot); 49 | void updateSnapshot(QLabel *showLabel, QString themeSnapshot); 50 | 51 | private: 52 | Ui::ThemeSettingDialog *ui; 53 | 54 | QMap themeMap; 55 | QString currTheme; 56 | }; 57 | 58 | #endif // THEMESETTINGDIALOG_H 59 | -------------------------------------------------------------------------------- /ecanspy3/settings/themesettingdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | ThemeSettingDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 937 10 | 518 11 | 12 | 13 | 14 | 主题设置 15 | 16 | 17 | 18 | 19 | 20 20 | 430 21 | 115 22 | 19 23 | 24 | 25 | 26 | 默认 27 | 28 | 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 460 36 | 430 37 | 115 38 | 19 39 | 40 | 41 | 42 | 炫酷黑色 43 | 44 | 45 | 46 | 47 | 48 | 670 49 | 470 50 | 93 51 | 28 52 | 53 | 54 | 55 | 确定 56 | 57 | 58 | 59 | 60 | 61 | 810 62 | 470 63 | 93 64 | 28 65 | 66 | 67 | 68 | 取消 69 | 70 | 71 | 72 | 73 | 74 | 20 75 | 20 76 | 421 77 | 391 78 | 79 | 80 | 81 | QFrame::Box 82 | 83 | 84 | QFrame::Raised 85 | 86 | 87 | TextLabel 88 | 89 | 90 | 91 | 92 | 93 | 460 94 | 20 95 | 461 96 | 391 97 | 98 | 99 | 100 | QFrame::Box 101 | 102 | 103 | QFrame::Raised 104 | 105 | 106 | TextLabel 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /ecanspy3/statistic/statisticswidget.cpp: -------------------------------------------------------------------------------- 1 | #include "statisticswidget.h" 2 | 3 | StatisticsWidget::StatisticsWidget(QWidget *parent) : QWidget(parent) 4 | { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /ecanspy3/statistic/statisticswidget.h: -------------------------------------------------------------------------------- 1 | #ifndef STATISTICSWIDGET_H 2 | #define STATISTICSWIDGET_H 3 | 4 | #include 5 | 6 | class StatisticsWidget : public QWidget 7 | { 8 | Q_OBJECT 9 | public: 10 | explicit StatisticsWidget(QWidget *parent = nullptr); 11 | 12 | signals: 13 | 14 | public slots: 15 | }; 16 | 17 | #endif // STATISTICSWIDGET_H -------------------------------------------------------------------------------- /ecanspy3/status/statuswidget.cpp: -------------------------------------------------------------------------------- 1 | #include "statuswidget.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | StatusWidget::StatusWidget(QWidget *parent) : QWidget(parent) 10 | { 11 | QHBoxLayout *layout = new QHBoxLayout; 12 | 13 | QVBoxLayout *vboxLayout = new QVBoxLayout; 14 | initCanBusStatus(vboxLayout); 15 | initErrorCounter(vboxLayout); 16 | initControlStatus(vboxLayout); 17 | vboxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); 18 | 19 | layout->addLayout(vboxLayout); 20 | setLayout(layout); 21 | 22 | initUpdateTimer(); 23 | } 24 | 25 | void StatusWidget::initErrorCounter(QLayout *layout) 26 | { 27 | QVBoxLayout *vboxLayout = new QVBoxLayout; 28 | QGroupBox *counterGroupBox = new QGroupBox(tr("bus error count"), this); 29 | counterGroupBox->setLayout(vboxLayout); 30 | 31 | sendErrCnt = new QLabel(tr("receive: 0"), this); 32 | sendErrCnt->setEnabled(false); 33 | vboxLayout->addWidget(sendErrCnt); 34 | 35 | receiveErrCnt = new QLabel(tr("send: 0"), this); 36 | receiveErrCnt->setEnabled(false); 37 | vboxLayout->addWidget(receiveErrCnt); 38 | 39 | //vboxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); 40 | 41 | layout->addWidget(counterGroupBox); 42 | } 43 | 44 | void StatusWidget::initCanBusStatus(QLayout *layout) 45 | { 46 | QVBoxLayout *vboxLayout = new QVBoxLayout; 47 | QGroupBox *counterGroupBox = new QGroupBox(tr("bus status"), this); 48 | counterGroupBox->setLayout(vboxLayout); 49 | 50 | busNormal = new QCheckBox(tr("bus normal"), this); 51 | busNormal->setEnabled(false); 52 | vboxLayout->addWidget(busNormal); 53 | 54 | passiveErr = new QCheckBox(tr("passive error"), this); 55 | passiveErr->setEnabled(false); 56 | vboxLayout->addWidget(passiveErr); 57 | 58 | activeErr = new QCheckBox(tr("active error"), this); 59 | activeErr->setEnabled(false); 60 | vboxLayout->addWidget(activeErr); 61 | 62 | busOff = new QCheckBox(tr("bus off"), this); 63 | busOff->setEnabled(false); 64 | vboxLayout->addWidget(busOff); 65 | 66 | //vboxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); 67 | 68 | layout->addWidget(counterGroupBox); 69 | } 70 | 71 | void StatusWidget::initControlStatus(QLayout *layout) 72 | { 73 | QVBoxLayout *vboxLayout = new QVBoxLayout; 74 | QGroupBox *counterGroupBox = new QGroupBox(tr("control status"), this); 75 | counterGroupBox->setLayout(vboxLayout); 76 | 77 | receiveRegFull = new QCheckBox(tr("receive reg full"), this); 78 | receiveRegFull->setEnabled(false); 79 | vboxLayout->addWidget(receiveRegFull); 80 | 81 | receiveRegOv = new QCheckBox(tr("receive reg overflow"), this); 82 | receiveRegOv->setEnabled(false); 83 | vboxLayout->addWidget(receiveRegOv); 84 | 85 | controllerErrWarn = new QCheckBox(tr("controller error warning"), this); 86 | controllerErrWarn->setEnabled(false); 87 | vboxLayout->addWidget(controllerErrWarn); 88 | 89 | controllerFIFOOv = new QCheckBox(tr("controller fifo overflow"), this); 90 | controllerFIFOOv->setEnabled(false); 91 | vboxLayout->addWidget(controllerFIFOOv); 92 | 93 | //vboxLayout->addSpacerItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding)); 94 | 95 | layout->addWidget(counterGroupBox); 96 | } 97 | 98 | void StatusWidget::initUpdateTimer() 99 | { 100 | updateTimer = new QTimer(this); 101 | connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateStatus())); 102 | updateTimer->start(UPDATE_PERIOD_SECOND*1000); 103 | } 104 | 105 | void StatusWidget::updateStatus() 106 | { 107 | updateErrCnt(); 108 | updateBusStatus(); 109 | } 110 | 111 | void StatusWidget::updateErrCnt() 112 | { 113 | CAN_STATUS status = {0, 0, 0, 0, 0, 0, 0, 0, 0}; 114 | if (!UsbCanUtil::GetInstance().ReadCANStatus(&status)) return; 115 | 116 | sendErrCnt->setText(QString("send: %1").arg(status.regTECounter)); 117 | receiveErrCnt->setText(QString("receive: %1").arg(status.regRECounter)); 118 | } 119 | 120 | void StatusWidget::updateBusStatus() 121 | { 122 | ERR_INFO errInfo; 123 | if (!UsbCanUtil::GetInstance().ReadErrorInfo(errInfo)) return; 124 | activeErr->setChecked(ERR_CAN_ZHUDONG & errInfo.ErrCode); 125 | passiveErr->setChecked(ERR_CAN_PASSIVE & errInfo.ErrCode); 126 | busOff->setChecked(ERR_CAN_BUSERR & errInfo.ErrCode); 127 | busNormal->setChecked(!(activeErr->isChecked() && passiveErr->isChecked() && busOff->isChecked())); 128 | 129 | updateControllerStatus(&errInfo); 130 | } 131 | 132 | void StatusWidget::updateControllerStatus(ERR_INFO *err) 133 | { 134 | receiveRegFull->setChecked(err->ErrCode&ERR_CAN_REG_FULL); 135 | receiveRegOv->setChecked(err->ErrCode&ERR_CAN_REG_OVER); 136 | controllerErrWarn->setChecked(err->ErrCode&ERR_CAN_ERRALARM); 137 | controllerFIFOOv->setChecked(err->ErrCode&ERR_CAN_OVERFLOW); 138 | } 139 | -------------------------------------------------------------------------------- /ecanspy3/status/statuswidget.h: -------------------------------------------------------------------------------- 1 | #ifndef STATUSWIDGET_H 2 | #define STATUSWIDGET_H 3 | 4 | #include 5 | #include "UsbCanUtil.h" 6 | 7 | QT_BEGIN_NAMESPACE 8 | class QLabel; 9 | class QCheckBox; 10 | class QHBoxLayout; 11 | class QVBoxLayout; 12 | class QTimer; 13 | QT_END_NAMESPACE 14 | 15 | class StatusWidget : public QWidget 16 | { 17 | Q_OBJECT 18 | public: 19 | enum { 20 | UPDATE_PERIOD_SECOND = 1, 21 | }; 22 | 23 | explicit StatusWidget(QWidget *parent = nullptr); 24 | 25 | void initErrorCounter(QLayout *layout); 26 | void initCanBusStatus(QLayout *layout); 27 | void initControlStatus(QLayout *layout); 28 | void initUpdateTimer(); 29 | 30 | signals: 31 | 32 | public slots: 33 | void updateStatus(); 34 | void updateErrCnt(); 35 | void updateBusStatus(); 36 | void updateControllerStatus(ERR_INFO *err); 37 | 38 | private: 39 | QTimer *updateTimer; 40 | 41 | QLabel *sendErrCnt; 42 | QLabel *receiveErrCnt; 43 | 44 | QCheckBox *busNormal; 45 | QCheckBox *passiveErr; 46 | QCheckBox *activeErr; 47 | QCheckBox *busOff; 48 | 49 | QCheckBox *receiveRegFull; 50 | QCheckBox *receiveRegOv; 51 | QCheckBox *controllerErrWarn; 52 | QCheckBox *controllerFIFOOv; 53 | }; 54 | 55 | #endif // STATUSWIDGET_H 56 | -------------------------------------------------------------------------------- /ecanspy3/themes/black_snapshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/themes/black_snapshot.png -------------------------------------------------------------------------------- /ecanspy3/themes/white_snapshot.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/twoflyliu/ecanspy3/3fadffed6c15981e054ecac7c57a27a953b9f2d8/ecanspy3/themes/white_snapshot.PNG -------------------------------------------------------------------------------- /ecanspy3/utils/mmtimer.cpp: -------------------------------------------------------------------------------- 1 | #include "mmtimer.h" 2 | 3 | #ifdef __MINGW32__ //w32api bug 4 | #define TIME_KILL_SYNCHRONOUS 0x0100 5 | #endif 6 | 7 | void WINAPI CALLBACK mmtimer_proc(uint, uint, DWORD_PTR user, DWORD_PTR, DWORD_PTR) 8 | { 9 | MMTimer *t = reinterpret_cast(user); 10 | emit t->timeout(); 11 | } 12 | 13 | MMTimer::MMTimer(int interval, QObject *parent) 14 | : QObject(parent) 15 | { 16 | this->interval = interval; 17 | this->id = 0; 18 | } 19 | 20 | void MMTimer::start() 21 | { 22 | this->id = timeSetEvent(this->interval, 1, mmtimer_proc, (DWORD_PTR)this, 23 | TIME_CALLBACK_FUNCTION | TIME_PERIODIC | TIME_KILL_SYNCHRONOUS); 24 | } 25 | 26 | void MMTimer::stop() 27 | { 28 | if (this->id) { 29 | timeKillEvent(this->id); 30 | this->id = 0; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ecanspy3/utils/mmtimer.h: -------------------------------------------------------------------------------- 1 | #ifndef MMTIMER_H 2 | #define MMTIMER_H 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | class MMTimer : public QObject 10 | { 11 | Q_OBJECT 12 | public: 13 | friend void WINAPI CALLBACK mmtimer_proc(uint, uint, DWORD_PTR user, DWORD_PTR, DWORD_PTR); 14 | 15 | explicit MMTimer(int interval, QObject *parent=nullptr); 16 | 17 | signals: 18 | void timeout(); 19 | 20 | public slots: 21 | void start(); 22 | void stop(); 23 | 24 | private: 25 | int interval; 26 | int id; 27 | }; 28 | 29 | #endif // MMTIMER_H 30 | -------------------------------------------------------------------------------- /ecanspy3/utils/scrollingfile.cpp: -------------------------------------------------------------------------------- 1 | #include "scrollingfile.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | ScrollingFile::ScrollingFile(QString filePath, int maxFileSize, ScrollingFile::FileNameRule rule) 8 | { 9 | QFileInfo info(filePath); 10 | extName = info.suffix(); 11 | fileName = info.fileName(); 12 | dirName = info.absolutePath(); 13 | 14 | if (!extName.isEmpty()) { 15 | fileName = fileName.left(fileName.size() - extName.size() - 1); 16 | } 17 | 18 | this->maxFileSize = maxFileSize; 19 | this->rule = rule; 20 | 21 | generateNextFile(); 22 | } 23 | 24 | ScrollingFile::~ScrollingFile() 25 | { 26 | 27 | } 28 | 29 | qint64 ScrollingFile::write(const char *data, qint64 maxSize) 30 | { 31 | if (file) { 32 | qint64 ret = file->write(data, maxSize); 33 | if (file->size() >= maxFileSize) { 34 | generateNextFile(); 35 | } 36 | return ret; 37 | } 38 | return 0; 39 | } 40 | 41 | qint64 ScrollingFile::write(const char *data) 42 | { 43 | if (file) { 44 | qint64 ret = file->write(data); 45 | if (file->size() >= maxFileSize) { 46 | generateNextFile(); 47 | } 48 | return ret; 49 | } 50 | return 0; 51 | } 52 | 53 | qint64 ScrollingFile::write(const QByteArray &byteArray) 54 | { 55 | if (file) { 56 | qint64 ret = file->write(byteArray); 57 | if (file->size() >= maxFileSize) { 58 | generateNextFile(); 59 | } 60 | return ret; 61 | } 62 | return 0; 63 | } 64 | 65 | void ScrollingFile::close() 66 | { 67 | if (file) { 68 | file->close(); 69 | } 70 | } 71 | 72 | void ScrollingFile::flush() 73 | { 74 | if (file) { 75 | file->flush(); 76 | } 77 | } 78 | 79 | QString ScrollingFile::generateNextFileName() 80 | { 81 | switch (rule) { 82 | case FILE_NAME_RULE_ID_INC: 83 | return QString("%1/%2_%3.%4").arg(dirName).arg(fileName).arg(id++).arg(extName); 84 | default: 85 | Q_ASSERT(false && "invalid file name"); 86 | } 87 | return QString(); 88 | } 89 | 90 | void ScrollingFile::generateNextFile() 91 | { 92 | if (file) { 93 | file->flush(); 94 | delete file; 95 | } 96 | 97 | file = new QFile(generateNextFileName()); 98 | if (!file->open(QFile::WriteOnly|QFile::Truncate)) { 99 | qDebug() << QString("ScrollingFile::generateNextFile()-> Create File [%1] Failed: %2") 100 | .arg(file->fileName()).arg(file->errorString()); 101 | Q_ASSERT(false && "Create Scrolling File Failed!"); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /ecanspy3/utils/scrollingfile.h: -------------------------------------------------------------------------------- 1 | #ifndef SCROLLINGFILE_H 2 | #define SCROLLINGFILE_H 3 | 4 | #include 5 | 6 | class ScrollingFile 7 | { 8 | public: 9 | enum { 10 | DEFAULT_SINGILE_FILE_COUNT = 40 * 1024 * 1024 //!< 单位字节 11 | }; 12 | 13 | enum FileNameRule { 14 | FILE_NAME_RULE_ID_INC = 0, //!< 以ID递增方式 15 | }; 16 | 17 | ScrollingFile(QString filePath, int maxFileSize = DEFAULT_SINGILE_FILE_COUNT, 18 | FileNameRule rule = FILE_NAME_RULE_ID_INC); 19 | ~ScrollingFile(); 20 | 21 | qint64 write(const char *data, qint64 maxSize); 22 | qint64 write(const char *data); 23 | qint64 write(const QByteArray &byteArray); 24 | 25 | void close(); 26 | void flush(); 27 | 28 | void setMaxFileSize(int maxFileSize) 29 | { 30 | this->maxFileSize = maxFileSize; 31 | } 32 | 33 | int getMaxFileSize() const 34 | { 35 | return this->maxFileSize; 36 | } 37 | 38 | void setFileNameRule(FileNameRule rule) 39 | { 40 | this->rule = rule; 41 | } 42 | 43 | FileNameRule getFileNameRule() const 44 | { 45 | return this->rule; 46 | } 47 | 48 | protected: 49 | void generateNextFile(); 50 | QString generateNextFileName(); 51 | 52 | private: 53 | QFile *file = nullptr; 54 | 55 | QString fileName; 56 | QString extName; 57 | QString dirName; 58 | FileNameRule rule; 59 | 60 | int maxFileSize; 61 | 62 | 63 | int id = 0; 64 | }; 65 | 66 | #endif // SCROLLINGFILE_H 67 | -------------------------------------------------------------------------------- /ecanspy3/utils/signalaccessor.cpp: -------------------------------------------------------------------------------- 1 | #include "signalaccessor.h" 2 | 3 | namespace { 4 | 5 | //Intel Signal内的起始位是最低位 6 | //Intel Signal内的起始位是最高位 7 | 8 | quint64 getSignalValueIntel(const CAN_OBJ *obj, const Signal *signal) 9 | { 10 | quint64 value = 0; 11 | 12 | // 起始长度(MSB行) 13 | int startRowIndex = signal->getStartBit() / 8; 14 | int startBegColIndex = signal->getStartBit() % 8; 15 | int startRowBitLen = (8 - startBegColIndex); 16 | int startRowOffsetBit = startBegColIndex; 17 | 18 | if (startRowBitLen > signal->getBitLen()) { 19 | startRowBitLen = signal->getBitLen(); 20 | } 21 | 22 | //最低行(LSB) 23 | value = (obj->Data[startRowIndex]>>startRowOffsetBit) & ((1<getBitLen() - startRowBitLen) / 8; 29 | for (int i = 0; i < midRowCnt; i++) { 30 | value |= (obj->Data[curRowIndex++] << offsetBit); 31 | offsetBit += 8; 32 | } 33 | 34 | // 最后一行 35 | int lastRowBitLen = signal->getBitLen() - (8*midRowCnt) -startRowBitLen; 36 | if (lastRowBitLen > 0) { 37 | value |= (obj->Data[curRowIndex] & ((1 << lastRowBitLen)-1)) << offsetBit; 38 | } 39 | 40 | return value; 41 | } 42 | 43 | quint64 getSignalValueMotorola(const CAN_OBJ *obj, const Signal *signal) 44 | { 45 | quint64 value = 0; 46 | 47 | // 起始长度(MSB行) 48 | int startRowIndex = signal->getStartBit() / 8; 49 | int startEndColIndex = signal->getStartBit() % 8; 50 | int startRowBitLen = startEndColIndex + 1; 51 | int startRowMaxBitLen = startRowBitLen; 52 | int startRowOffsetBit = 0; 53 | 54 | if (signal->getBitLen() < startRowBitLen) { 55 | startRowOffsetBit = startRowBitLen - signal->getBitLen(); 56 | startRowBitLen = signal->getBitLen(); 57 | } 58 | 59 | //最低行(MSB) 60 | value = (obj->Data[startRowIndex] & ((1<> startRowOffsetBit; 61 | 62 | // 中间行 63 | int curRowIndex = startRowIndex + 1; 64 | int midRowCnt = (signal->getBitLen() - startRowBitLen) / 8; 65 | int offsetBit = startRowBitLen; 66 | for (int i = 0; i < midRowCnt; i++) { 67 | value = (value << offsetBit) | obj->Data[curRowIndex++]; 68 | offsetBit = 8; 69 | } 70 | 71 | // 最后一行 72 | int lastRowBitLen = signal->getBitLen() - (8*midRowCnt) - startRowBitLen; 73 | if (lastRowBitLen > 0) { 74 | value = (value << lastRowBitLen) | (obj->Data[curRowIndex]>>(8-lastRowBitLen)); 75 | } 76 | 77 | return value; 78 | } 79 | 80 | void setSignalValueIntel(CAN_OBJ *obj, Signal *signal, quint64 value) 81 | { 82 | // 起始长度(MSB行)信息 83 | int startRowIndex = signal->getStartBit() / 8; 84 | int startBegColIndex = signal->getStartBit() % 8; 85 | int startRowBitLen = (8 - startBegColIndex); 86 | int startRowOffsetBit = startBegColIndex; 87 | 88 | if (startRowBitLen > signal->getBitLen()) { 89 | startRowBitLen = signal->getBitLen(); 90 | } 91 | 92 | // 中间行信息 93 | int midRowCnt = (signal->getBitLen() - startRowBitLen) / 8; 94 | 95 | // 最后行信息 96 | int lastRowBitLen = signal->getBitLen() - (8*midRowCnt) - startRowBitLen; 97 | int curRowIndex = startRowIndex; 98 | 99 | // 设置最低行(LSB) 100 | int mask = ((1<Data[curRowIndex] &= ~mask; 102 | obj->Data[curRowIndex++] |= (value & ((1<>= startRowBitLen; 104 | 105 | // 设置中间行 106 | for (int i = 0; i < midRowCnt; i++) { 107 | obj->Data[curRowIndex++] = value & 0xff; 108 | value >>= 8; 109 | } 110 | 111 | // 设置最后行 112 | if (lastRowBitLen > 0) { 113 | mask = ((1<Data[curRowIndex] &= ~mask; 115 | obj->Data[curRowIndex] |= value & ((1<getStartBit() / 8; 123 | int startEndColIndex = signal->getStartBit() % 8; 124 | int startRowBitLen = startEndColIndex + 1; 125 | int startRowMaxBitLen = startRowBitLen; 126 | int startRowOffsetBit = 0; 127 | int mask = 0; 128 | 129 | if (startRowBitLen > signal->getBitLen()) { 130 | startRowOffsetBit = startRowBitLen - signal->getBitLen(); 131 | startRowBitLen = signal->getBitLen(); 132 | } 133 | 134 | // 中间行信息 135 | int midRowCnt = (signal->getBitLen() - startRowBitLen) / 8; 136 | 137 | // 最后行信息 138 | int lastRowBitLen = signal->getBitLen() - (8*midRowCnt) - startRowBitLen; 139 | 140 | int curRowIndex = startRowIndex; 141 | 142 | // 设置起始行(MSB) 143 | mask = (((1<> startRowOffsetBit) << startRowOffsetBit; 144 | obj->Data[curRowIndex] &= ~mask; 145 | obj->Data[curRowIndex++] |= ((value >> (signal->getBitLen()-startRowBitLen))&((1<Data[curRowIndex++] = (value >> (lastRowBitLen+(midRowCnt-1-i)*8)) & 0xff; 151 | } 152 | 153 | // 设置最后行 154 | if (lastRowBitLen > 0) { 155 | mask = ((1<Data[curRowIndex] &= ~mask; 157 | obj->Data[curRowIndex] |= (value & ((1<getByteOrder()) { 169 | case Signal::BYTE_ORDER_TYPE_MOTOROLA: 170 | return getSignalValueMotorola(obj, signal); 171 | case Signal::BYTE_ORDER_TYPE_INTEL: 172 | return getSignalValueIntel(obj, signal); 173 | } 174 | return 0; 175 | } 176 | 177 | void SignalAccessor::setSignalValue(CAN_OBJ *obj, Signal *signal, quint64 value) 178 | { 179 | if (NULL == obj || NULL == signal) return; 180 | switch (signal->getByteOrder()) { 181 | case Signal::BYTE_ORDER_TYPE_MOTOROLA: 182 | return setSignalValueMotorola(obj, signal, value); 183 | case Signal::BYTE_ORDER_TYPE_INTEL: 184 | return setSignalValueIntel(obj, signal, value); 185 | } 186 | } 187 | 188 | QString SignalAccessor::getSignalPhysicalValue(const CAN_OBJ *obj, Signal *signal) 189 | { 190 | if (NULL == obj || NULL == signal) return QString(""); 191 | quint64 rawValue = getSignalValue(obj, signal); 192 | 193 | auto &tbl = signal->getValueDescTable(); 194 | auto iter = tbl.find(rawValue); 195 | if (iter != tbl.end()) { 196 | return iter.value().trimmed(); 197 | } 198 | return QString("%1 %2").arg(signal->getFactor()*rawValue + signal->getOffset()).arg(signal->getUnit()); 199 | } 200 | -------------------------------------------------------------------------------- /ecanspy3/utils/signalaccessor.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNALACCESSOR_H 2 | #define SIGNALACCESSOR_H 3 | 4 | #include "dbc4cpp/parser.h" 5 | #include "UsbCanUtil.h" 6 | #include 7 | 8 | namespace SignalAccessor 9 | { 10 | quint64 getSignalValue(const CAN_OBJ *obj, const Signal *signal); 11 | void setSignalValue(CAN_OBJ *obj, Signal *signal, quint64 value); 12 | 13 | QString getSignalPhysicalValue(const CAN_OBJ *obj, Signal *signal); 14 | } 15 | 16 | #endif // SIGNALACCESSOR_H 17 | -------------------------------------------------------------------------------- /ecanspy3/utils/signalaccesstest.h: -------------------------------------------------------------------------------- 1 | #ifndef SIGNALACCESSTEST_H 2 | #define SIGNALACCESSTEST_H 3 | 4 | #define SIGNAL_ACCESS_TEST_ENABLED (0) 5 | 6 | #if SIGNAL_ACCESS_TEST_ENABLED 7 | 8 | int signalAccessorTest(void); 9 | 10 | #endif //SIGNAL_ACCESS_ENABLED 11 | 12 | #endif // SIGNALACCESSTEST_H 13 | --------------------------------------------------------------------------------