Repository: famez/J1939-Framework Branch: master Commit: 5153e5005995 Files: 187 Total size: 624.3 KB Directory structure: gitextract_rarda5g4/ ├── .travis.yml ├── BinTest/ │ ├── AccelCCVS/ │ │ ├── CMakeLists.txt │ │ └── src/ │ │ └── accelCCVS.cpp │ └── FrameInject/ │ ├── CMakeLists.txt │ └── src/ │ └── frame_inject.cpp ├── BinUtils/ │ ├── CMakeLists.txt │ ├── TRCDumper/ │ │ ├── .cproject │ │ ├── CMakeLists.txt │ │ └── src/ │ │ └── TRCDumper.cpp │ ├── TRCPlayer/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── src/ │ │ └── TRCPlayer.cpp │ ├── TRCToCap/ │ │ ├── .cproject │ │ ├── CMakeLists.txt │ │ ├── include/ │ │ │ ├── config.h │ │ │ └── pcapio.h │ │ └── src/ │ │ ├── TRCToCap.cpp │ │ └── pcapio.c │ ├── j1939AddrClaim/ │ │ ├── CMakeLists.txt │ │ ├── Readme.md │ │ └── j1939AddrClaim.cpp │ ├── j1939AddressMapper/ │ │ ├── CMakeLists.txt │ │ ├── Readme.md │ │ └── j1939AddressMapper.cpp │ ├── j1939Decoder/ │ │ ├── .cproject │ │ ├── .settings/ │ │ │ ├── org.eclipse.cdt.codan.core.prefs │ │ │ └── org.eclipse.cdt.core.prefs │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── src/ │ │ └── j1939Decoder.cpp │ ├── j1939Sender/ │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ └── src/ │ │ └── j1939Sender.cpp │ └── j1939Sniffer/ │ ├── .cproject │ ├── .gitignore │ ├── .settings/ │ │ └── org.eclipse.cdt.codan.core.prefs │ ├── CMakeLists.txt │ └── src/ │ └── j1939Sniffer.cpp ├── CAN/ │ ├── .gitignore │ ├── .settings/ │ │ └── org.eclipse.cdt.codan.core.prefs │ ├── Backends/ │ │ ├── PeakCan/ │ │ │ ├── PeakCanChannels.cpp │ │ │ ├── PeakCanHelper.cpp │ │ │ ├── PeakCanReceiver.cpp │ │ │ ├── PeakCanSender.cpp │ │ │ └── PeakCanSymbols.cpp │ │ └── Sockets/ │ │ ├── SocketCanHelper.cpp │ │ ├── SocketCanReceiver.cpp │ │ └── SocketCanSender.cpp │ ├── CMakeLists.txt │ ├── CanEasy.cpp │ ├── CanFrame.cpp │ ├── CanSniffer.cpp │ ├── CommonCanReceiver.cpp │ ├── CommonCanSender.cpp │ ├── ICanHelper.cpp │ ├── README.md │ ├── TRCReader.cpp │ ├── TRCWriter.cpp │ └── include/ │ ├── Backends/ │ │ ├── PeakCan/ │ │ │ ├── PeakCanChannels.h │ │ │ ├── PeakCanHelper.h │ │ │ ├── PeakCanReceiver.h │ │ │ ├── PeakCanSender.h │ │ │ └── PeakCanSymbols.h │ │ └── Sockets/ │ │ ├── SocketCanHelper.h │ │ ├── SocketCanReceiver.h │ │ └── SocketCanSender.h │ ├── CanEasy.h │ ├── CanFilter.h │ ├── CanFrame.h │ ├── CanSniffer.h │ ├── CommonCanReceiver.h │ ├── CommonCanSender.h │ ├── ICanHelper.h │ ├── ICanSender.h │ ├── TRCReader.h │ └── TRCWriter.h ├── CMakeLists.txt ├── Common/ │ ├── .cproject │ ├── .gitignore │ ├── CMakeLists.txt │ ├── Utils.cpp │ └── include/ │ ├── Assert.h │ ├── ICloneable.h │ ├── Singleton.h │ ├── Types.h │ └── Utils.h ├── Database/ │ ├── CMakeLists.txt │ ├── README.md │ └── frames.json ├── GUI_WEB/ │ ├── README.md │ ├── backend/ │ │ ├── CMakeLists.txt │ │ ├── graph.proto │ │ └── src/ │ │ ├── J1939GUI.cpp │ │ ├── graph.cpp │ │ └── graph.h │ └── frontend/ │ ├── graph.html │ ├── index.html │ └── js/ │ └── graph.js ├── J1939/ │ ├── .cproject │ ├── .gitignore │ ├── .settings/ │ │ └── org.eclipse.cdt.codan.core.prefs │ ├── Addressing/ │ │ └── AddressClaimFrame.cpp │ ├── CMakeLists.txt │ ├── Diagnosis/ │ │ ├── DTC.cpp │ │ └── Frames/ │ │ └── DM1.cpp │ ├── FMS/ │ │ └── TellTale/ │ │ ├── FMS1Frame.cpp │ │ └── TellTale.cpp │ ├── Frames/ │ │ └── RequestFrame.cpp │ ├── GenericFrame.cpp │ ├── J1939Common.cpp │ ├── J1939DataBase.cpp │ ├── J1939Factory.cpp │ ├── J1939Frame.cpp │ ├── README.md │ ├── SPN/ │ │ ├── SPN.cpp │ │ ├── SPNHistory.cpp │ │ ├── SPNNumeric.cpp │ │ ├── SPNSpec/ │ │ │ ├── SPNNumericSpec.cpp │ │ │ ├── SPNSpec.cpp │ │ │ └── SPNStatusSpec.cpp │ │ ├── SPNStatus.cpp │ │ └── SPNString.cpp │ ├── Transport/ │ │ ├── BAM/ │ │ │ ├── BamFragmenter.cpp │ │ │ └── BamReassembler.cpp │ │ ├── RTSCTS/ │ │ │ └── RTSCTSConnectionManager.cpp │ │ ├── TPCMFrame.cpp │ │ └── TPDTFrame.cpp │ └── include/ │ ├── Addressing/ │ │ └── AddressClaimFrame.h │ ├── Diagnosis/ │ │ ├── DTC.h │ │ └── Frames/ │ │ └── DM1.h │ ├── FMS/ │ │ └── TellTale/ │ │ ├── FMS1Frame.h │ │ └── TellTale.h │ ├── Frames/ │ │ └── RequestFrame.h │ ├── GenericFrame.h │ ├── J1939Common.h │ ├── J1939DataBase.h │ ├── J1939Factory.h │ ├── J1939Frame.h │ ├── SPN/ │ │ ├── SPN.h │ │ ├── SPNHistory.h │ │ ├── SPNNumeric.h │ │ ├── SPNSpec/ │ │ │ ├── SPNNumericSpec.h │ │ │ ├── SPNSpec.h │ │ │ └── SPNStatusSpec.h │ │ ├── SPNStatus.h │ │ └── SPNString.h │ └── Transport/ │ ├── BAM/ │ │ ├── BamFragmenter.h │ │ └── BamReassembler.h │ ├── RTSCTS/ │ │ └── RTSCTSConnectionManager.h │ ├── TPCMFrame.h │ └── TPDTFrame.h ├── LICENSE ├── README.md ├── Scripts/ │ ├── Readme.md │ ├── define_frames.j1939 │ ├── gear_level.sh │ ├── simulate_dtc.sh │ ├── simulate_fuel_cons.sh │ └── test_tco.sh ├── Tests/ │ ├── BAM_test.cpp │ ├── CMakeLists.txt │ ├── SPNNumeric_test.cpp │ ├── SPNStatus_test.cpp │ ├── SPNString_test.cpp │ ├── TestFrame.cpp │ ├── address_claim_frame_test.cpp │ ├── database/ │ │ ├── test1.json │ │ ├── test2.json │ │ ├── test3.json │ │ ├── test4.json │ │ └── test5.json │ ├── database_test.cpp │ ├── fms1_frame_test.cpp │ ├── genericframe_test.cpp │ ├── include/ │ │ └── TestFrame.h │ ├── j1939Factory_test.cpp │ ├── main.cpp │ └── request_frame.cpp ├── cmake/ │ ├── FindLibWebSockets.cmake │ └── J1939FrameworkConfig.cmake ├── j1939AddressClaimer/ │ ├── AddressClaimer.cpp │ ├── CMakeLists.txt │ └── include/ │ └── AddressClaimer.h └── wireshark/ └── dissector/ ├── CMakeLists.txt ├── Readme.md ├── packet-j1939.cpp └── plugin.c ================================================ FILE CONTENTS ================================================ ================================================ FILE: .travis.yml ================================================ dist: trusty sudo: required language: cpp addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-6 - libgtest-dev - protobuf-compiler - libprotobuf-dev install: - pushd ~ - git clone https://github.com/open-source-parsers/jsoncpp.git - pushd jsoncpp - cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON - sudo make install - popd - popd - pushd ~ - git clone https://github.com/warmcat/libwebsockets.git - pushd libwebsockets - cmake . - sudo make install - popd - popd - pushd /usr/src/gtest - sudo env "PATH=$PATH" cmake CMakeLists.txt - sudo make - sudo cp *.a /usr/lib - popd script: - CXX=/usr/bin/g++-6 CC=/usr/bin/gcc-6 cmake . - cmake --build . - ./Tests/execTests ================================================ FILE: BinTest/AccelCCVS/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(accelCCVS) set(CMAKE_BUILD_TYPE Debug) #For debug purposes find_package(J1939Framework REQUIRED) add_definitions(-DDATABASE_PATH="${J1939_Database}") set(CMAKE_CXX_STANDARD 11) add_executable(accelCCVS src/accelCCVS.cpp ) target_link_libraries(accelCCVS PUBLIC J1939 Can rt -rdynamic ) install (TARGETS accelCCVS DESTINATION bin) ================================================ FILE: BinTest/AccelCCVS/src/accelCCVS.cpp ================================================ #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif #include #include #include #include #include #include #include #include #include #define BAUD_250K 250000 #define MAX_ACCEL_VALUE 2 /*m/s²*/ #define DIFF_ACCEL 0.5 using namespace J1939; using namespace Can; using namespace Utils; int main (int argc, char **argv) { CanEasy::initialize(BAUD_250K); std::set interfaces = CanEasy::getCanIfaces(); std::shared_ptr sender = CanEasy::getSender("vcan0"); if(!sender) { std::cerr << "Cannot send through the interface" << std::endl; return -1; } //Read database if available if(!J1939Factory::getInstance().registerDatabaseFrames(DATABASE_PATH)) { std::cerr << "Database not found in " << DATABASE_PATH << std::endl; exit(-2); } std::unique_ptr myFrame = J1939Factory::getInstance().getJ1939Frame("CCVS"); if(!myFrame) return -3; GenericFrame *ccvsFrame = static_cast(myFrame.get()); SPNNumeric *spnSpeed = static_cast(ccvsFrame->getSPN(84)); ccvsFrame->setPriority(3); ccvsFrame->setSrcAddr(0x20); CanFrame frame(true, ccvsFrame->getIdentifier()); sender->sendFrame(frame, 100, [ccvsFrame](u32 id, std::string& data) { //We need to modify speed dynamically size_t length = ccvsFrame->getDataLength(); u8* buff = new u8[length]; ccvsFrame->encode(id, buff, length); //Set data data.append((char*)buff, length); delete[] buff; }); double diffAccel = DIFF_ACCEL; bool inc = true; double accel = /*0*/MAX_ACCEL_VALUE; double speed = 2; double coef = 1; while(true) { //Simulate acceleration speed += accel*36/100; std::cout << "Accel: " << accel << std::endl; std::cout << "Speed: " << speed << std::endl; spnSpeed->setFormattedValue(speed); //modify accel if(inc) { accel += diffAccel * coef; } else { accel -= diffAccel * coef; } coef = MAX_ACCEL_VALUE - abs(accel); if(coef < 0.1) coef = 0.1; coef /= MAX_ACCEL_VALUE; if(accel >= MAX_ACCEL_VALUE) inc = false; if(accel <= -MAX_ACCEL_VALUE) inc = true; std::this_thread::sleep_for(std::chrono::milliseconds(100)); } return 0; } ================================================ FILE: BinTest/FrameInject/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(frameInject) set(CMAKE_BUILD_TYPE Debug) #For debug purposes find_package(J1939Framework REQUIRED) add_definitions(-DDATABASE_PATH="${J1939_Database}") set(CMAKE_CXX_STANDARD 11) add_executable(frameInject src/frame_inject.cpp ) target_link_libraries(frameInject PUBLIC J1939 Can rt -rdynamic ) install (TARGETS frameInject DESTINATION bin) ================================================ FILE: BinTest/FrameInject/src/frame_inject.cpp ================================================ #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif #include #include #include #include #include #include #include #include #include #include #include using namespace J1939; using namespace Can; using namespace Utils; int main (int argc, char **argv) { TRCReader trcReader; TRCWriter writer; //Read database if available if(!J1939Factory::getInstance().registerDatabaseFrames(DATABASE_PATH)) { std::cerr << "Database not found in " << DATABASE_PATH << std::endl; exit(-2); } if(!trcReader.loadFile("input.trc")) { std::cerr << "TRC file is corrupted or not readable by " << argv[0] << std::endl; return -2; } if(trcReader.getNumberOfFrames() == 0) { std::cerr << "TRC file is empty" << std::endl; return -3; } int progress = 0, oldProgress = 0; if(!writer.open("output.trc")) { std::cerr << "File could not be opened for writing..." << std::endl; return 2; } std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame("EEC1"); frame->setSrcAddr(0x15); frame->setPriority(4); GenericFrame *eec1Frame = static_cast(frame.get()); SPNNumeric *rpm = static_cast(eec1Frame->getSPN(190)); rpm->setFormattedValue(310); //310 rpm std::pair pairTStampFrame; u64 eec1TimeStamp = 0; do { trcReader.readNextCanFrame(); pairTStampFrame = trcReader.getLastCanFrame(); u64 timeStamp = pairTStampFrame.first; const CanFrame& frame = pairTStampFrame.second; if(eec1TimeStamp < timeStamp) { u32 id; size_t length = eec1Frame->getDataLength(); u8* buff = new u8[length]; eec1Frame->encode(id, buff, length); //Set data std::string data; data.append((char*)buff, length); delete[] buff; CanFrame frame(true, id, data); writer.write(frame, TimeStamp(eec1TimeStamp/1000000, eec1TimeStamp % 1000000)); eec1TimeStamp += 100000; //100ms } writer.write(frame, TimeStamp(timeStamp/1000000, timeStamp % 1000000)); progress = 100 * trcReader.getCurrentPos() / trcReader.getNumberOfFrames(); if(progress != oldProgress) { std::cout << "Progress: " << progress << " %" << std::endl; } } while(trcReader.getCurrentPos() < trcReader.getNumberOfFrames() - 1); writer.close(); return 0; } ================================================ FILE: BinUtils/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(BinUtils) #set(PROPERTIES EXCLUDE_FROM_ALL TRUE) add_subdirectory(j1939Decoder) add_subdirectory(j1939Sender) add_subdirectory(j1939Sniffer) add_subdirectory(TRCDumper) add_subdirectory(TRCPlayer) add_subdirectory(TRCToCap) add_subdirectory(j1939AddrClaim) add_subdirectory(j1939AddressMapper) ================================================ FILE: BinUtils/TRCDumper/.cproject ================================================ ================================================ FILE: BinUtils/TRCDumper/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(TRCDumper) add_executable(TRCDumper src/TRCDumper.cpp ) target_include_directories(TRCDumper PUBLIC include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) target_link_libraries(TRCDumper PUBLIC Can dl rt ) install (TARGETS TRCDumper DESTINATION bin) ================================================ FILE: BinUtils/TRCDumper/src/TRCDumper.cpp ================================================ //============================================================================ // Name : TRCDumper.cpp // Author : // Version : // Copyright : MIT License // Description : Application that reads frames from the can interface and writes them to a file in TRC format. //============================================================================ #include #include #include //Can includes #include #include //Bitrate for J1939 protocol #define BAUD_250K 250000 using namespace Can; using namespace Utils; TRCWriter writer; bool firstFrame; TimeStamp initialTimeStamp; void onRcv(const Can::CanFrame& frame, const TimeStamp&, const std::string& interface, void*); bool onTimeout(); void onSignal(int); std::string interface, file; int main(int argc, char **argv) { firstFrame = true; static struct option long_options[] = { {"interface", required_argument, NULL, 'i'}, {"file", required_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; while (1) { int c = getopt_long (argc, argv, "s:i:", long_options, NULL); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'f': file = optarg; break; case 'i': interface = optarg; break; default: break; } } CanEasy::initialize(BAUD_250K, onRcv, onTimeout); CanSniffer& sniffer = CanEasy::getSniffer(); if(sniffer.getNumberOfReceivers() == 0) { std::cerr << "No interface available from to sniffer" << std::endl; return 2; } if(!writer.open(file)) { std::cerr << "File could not be opened for writing..." << std::endl; return 2; } signal(SIGINT, onSignal); sniffer.sniff(1000); } void onRcv(const Can::CanFrame& frame, const TimeStamp& timeStamp, const std::string& , void*) { if(firstFrame) { initialTimeStamp = timeStamp; firstFrame = false; } writer.write(frame, timeStamp - initialTimeStamp); } bool onTimeout() { return true; } void onSignal(int) { std::cout << "Closing file..." << std::endl; writer.close(); std::cout << "Done" << std::endl; exit(0); } ================================================ FILE: BinUtils/TRCPlayer/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(TRCPlayer) find_package (Curses) if(CURSES_FOUND) message("-- NCURSES is available") message("-- Project " ${PROJECT_NAME} " will be built") add_executable(TRCPlayer src/TRCPlayer.cpp ) target_include_directories(TRCPlayer PUBLIC include ${J1939_SOURCE_DIR}/include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) target_link_libraries(TRCPlayer PUBLIC J1939 Can ncurses dl rt ) install (TARGETS TRCPlayer DESTINATION bin) endif(CURSES_FOUND) ================================================ FILE: BinUtils/TRCPlayer/README.md ================================================ ## TRCPlayer Let's play trc files. ```bash TRCPlayer -i vcan0 -f file.trc ``` ![alt text](https://github.com/famez/J1939-Framework/blob/master/BinUtils/TRCPlayer/TRCPlayer.png) ================================================ FILE: BinUtils/TRCPlayer/src/TRCPlayer.cpp ================================================ //============================================================================ // Name : TRCDumper.cpp // Author : // Version : // Copyright : MIT License // Description : Application that reads frames from the can interface and writes them to a file in TRC format. //============================================================================ #include #include #include #include #include #include #include #include //Can includes #include #include //J1939 includes #include #include #include //Bitrate for J1939 protocol #define BAUD_250K 250000 #define SELECT_COLOR 1 using namespace Can; using namespace Utils; using namespace J1939; TRCReader reader; TimeStamp start; std::string interface, file; //Vector to show the parsed frames from trc file std::vector< std::pair > vectorFrames; //Same as the vector but useful for fast access by id (To check if frame exeist or copy frame in case that it exists) std::unordered_map mapFrames; //To reassemble frames fragmented by means of Broadcast Announce Message protocol BamReassembler reassembler; //Selected frame size_t currentSel = 0; //KEY int key; u32 progress = 0; void printFrames(); int main(int argc, char **argv) { static struct option long_options[] = { {"interface", required_argument, NULL, 'i'}, {"file", required_argument, NULL, 'f'}, {NULL, 0, NULL, 0} }; while (1) { int c = getopt_long (argc, argv, "f:i:", long_options, NULL); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'f': file = optarg; break; case 'i': interface = optarg; break; default: break; } } CanEasy::initialize(BAUD_250K); std::shared_ptr sender = CanEasy::getSender(interface); if(!sender) { std::cerr << "Cannot send through the interface" << std::endl; return 1; } std::cout << "Loading file..." << std::endl; if(!reader.loadFile(file)) { std::cerr << "File could not be opened for reading..." << std::endl; return 2; } if(reader.getNumberOfFrames() == 0) { std::cerr << "TRC file is empty" << std::endl; return 3; } std::pair pairTStampFrame; if(!J1939Factory::getInstance().registerDatabaseFrames(DATABASE_PATH)) { std::cerr << "Database not found in " << DATABASE_PATH << std::endl; return 4; } //Initialize ncurses initscr(); cbreak(); timeout(1); keypad(stdscr, true); start_color(); init_pair(SELECT_COLOR, COLOR_BLACK, COLOR_WHITE); int width = getmaxx(stdscr); //End of initialization start = TimeStamp::now(); TimeStamp lastPrintTime = TimeStamp::now(); do { reader.readNextCanFrame(); pairTStampFrame = reader.getLastCanFrame(); const CanFrame& frame = pairTStampFrame.second; TimeStamp aux(pairTStampFrame.first / 1000000, pairTStampFrame.first % 1000000); TimeStamp lastTs(start + aux); //While waiting, we try to read arrow keys while(TimeStamp::now() < lastTs) { //Blocks during one millisecond key = getch(); switch(key) { case KEY_UP: if(currentSel > 0) --currentSel; break; case KEY_DOWN: if(currentSel + 1 < vectorFrames.size()) ++currentSel; break; case '\n': //Key Enter. Show details of frame when it is selected (or hide them) if(!vectorFrames.empty()) { vectorFrames[currentSel].first = !vectorFrames[currentSel].first; } break; default: //To skip printFrames if key not detected continue; } //If one of the valid keys was pressed, repaint printFrames(); } sender->sendFrameOnce(frame); progress = width * reader.getCurrentPos() / reader.getNumberOfFrames(); try { //Try to print frames std::unique_ptr j1939Frame = J1939Factory::getInstance(). getJ1939Frame(frame.getId(), (const u8*)(frame.getData().c_str()), frame.getData().size()); if(j1939Frame) { //Frame registered in the factory? if(reassembler.toBeHandled(*j1939Frame)) { //Check if the frame is part of a fragmented frame (BAM protocol) //Actually it is, reassembler will handle it. reassembler.handleFrame(*j1939Frame); if(reassembler.reassembledFramesPending()) { j1939Frame = reassembler.dequeueReassembledFrame(); } else { continue; //Frame handled by reassembler but the original frame to be reassembled is not complete. } } //Check if it is already added. auto iter = mapFrames.find(j1939Frame->getIdentifier()); if(iter != mapFrames.end()) { iter->second->copy(*j1939Frame); //Update frame } else { //Add frame to the list mapFrames[j1939Frame->getIdentifier()] = j1939Frame.get(); vectorFrames.push_back(std::make_pair(false, j1939Frame.release())); } } } catch (J1939DecodeException &) { //Decode exception, skip frame. Add handler so that the program keeps running. } TimeStamp elapsed = TimeStamp::now() - lastPrintTime; //Every 100 ms print frames if(elapsed.getMicroSec() > 100000 || elapsed.getSeconds() > 0) { printFrames(); lastPrintTime = TimeStamp::now(); } } while(reader.getCurrentPos() < reader.getNumberOfFrames() - 1); //Finalize ncurses endwin(); //Free frames for(auto iter = vectorFrames.begin(); iter != vectorFrames.end(); ++iter) { delete iter->second; } //Finalize CanEasy CanEasy::finalize(); } void printFrames() { clear(); //Print progress std::string progressBar = std::string(progress, '#') + "\n"; printw(progressBar.c_str()); for(size_t i = 0; i < vectorFrames.size(); ++i) { //If currentSel then print black over white if(currentSel == i) { attron(COLOR_PAIR(SELECT_COLOR)); } if(vectorFrames[i].first) { //Show details of frame when it is selected printw(vectorFrames[i].second->toString().c_str()); } else { printw(vectorFrames[i].second->getHeader().c_str()); } if(currentSel == i) { attroff(COLOR_PAIR(SELECT_COLOR)); } } refresh(); } ================================================ FILE: BinUtils/TRCToCap/.cproject ================================================ ================================================ FILE: BinUtils/TRCToCap/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(TRCToCap) find_package (PkgConfig) if(PKGCONFIG_FOUND) pkg_check_modules (GLIB2 glib-2.0) if(GLIB2_FOUND) message("-- GLIB2 is available") message("-- Project " ${PROJECT_NAME} " will be built") add_executable(TRCToCap src/TRCToCap.cpp src/pcapio.c ) target_include_directories(TRCToCap PUBLIC include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ${GLIB2_INCLUDE_DIRS} ) target_link_libraries(TRCToCap PUBLIC Can ) install (TARGETS TRCToCap DESTINATION bin) else(GLIB2_FOUND) message(WARNING "-- GLIB2 is not available") message(WARNING "-- Project " ${PROJECT_NAME} " is skipped...") message(WARNING "-- Try to install GLIB2 and run cmake again...") endif(GLIB2_FOUND) endif(PKGCONFIG_FOUND) ================================================ FILE: BinUtils/TRCToCap/include/config.h ================================================ /* cmakeconfig.h.in */ #ifndef __CONFIG_H__ #define __CONFIG_H__ /* Note: You cannot use earlier #defines in later #cmakedefines (cmake 2.6.2). */ /* Name of package */ #define PACKAGE "wireshark" #define VERSION_EXTRA "" /* Version number of package */ #define VERSION "2.6.1" #define VERSION_MAJOR 2 #define VERSION_MINOR 6 #define VERSION_MICRO 1 #define VERSION_RELEASE "2.6" #define VERSION_FLAVOR "" /* FIXME: Move the path stuff to the CMakeInstallDirs.cmake file */ /* Directory for data */ #define DATAFILE_DIR "/usr/local/share/wireshark" /* Build wsutil with SIMD optimization */ #define HAVE_SSE4_2 1 /* Directory where extcap hooks reside */ #define EXTCAP_DIR "/usr/local/lib/wireshark/extcap" /* Define to 1 if we want to enable plugins */ #define HAVE_PLUGINS 1 /* Define to 1 if we check hf conflict */ /* #undef ENABLE_CHECK_FILTER */ /* Link Wireshark libraries statically */ /* #undef ENABLE_STATIC */ /* Enable AirPcap */ /* #undef HAVE_AIRPCAP */ /* Define to 1 if you have the header file. */ /* #undef HAVE_ALLOCA_H */ /* Define to 1 if you have the header file. */ #define HAVE_ARPA_INET_H 1 /* Define to 1 if you have the `bpf_image' function. */ #define HAVE_BPF_IMAGE 1 /* Define to use c-ares library */ /* #undef HAVE_C_ARES */ /* Define to 1 if you have the `dladdr' function. */ /* #undef HAVE_DLADDR */ /* Define to 1 if you have the header file. */ #define HAVE_FCNTL_H 1 /* Define to use the MaxMind DB library */ /* #undef HAVE_MAXMINDDB */ /* Define to 1 if you have the header file. */ #define HAVE_IFADDRS_H 1 /* Define to 1 if you have the `getexecname' function. */ /* #undef HAVE_GETEXECNAME */ /* Define to 1 if you have the `getifaddrs' function. */ #define HAVE_GETIFADDRS 1 /* Define if LIBSSH support is enabled */ /* #undef HAVE_LIBSSH */ /* Define if LIBSSH has ssh_userauth_agent() function */ /* #undef HAVE_SSH_USERAUTH_AGENT */ /* Define if you have the 'floorl' function. */ #define HAVE_FLOORL 1 /* Define if you have the 'lrint' function. */ #define HAVE_LRINT 1 /* Define to 1 if you have the getopt_long function. */ #define HAVE_GETOPT_LONG 1 /* Define to 1 if you have the header file. */ #define HAVE_GETOPT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_GRP_H 1 /* Define to use heimdal kerberos */ /* #undef HAVE_HEIMDAL_KERBEROS */ /* Define to 1 if you have the `inflatePrime' function. */ #define HAVE_INFLATEPRIME 1 /* Define to 1 if you have the `issetugid' function. */ /* #undef HAVE_ISSETUGID */ /* Define to use kerberos */ /* #undef HAVE_KERBEROS */ /* Define to use nghttp2 */ /* #undef HAVE_NGHTTP2 */ /* Define to use the libcap library */ /* #undef HAVE_LIBCAP */ /* Define to use GnuTLS library */ /* #undef HAVE_LIBGNUTLS */ /* Enable libnl support */ /* #undef HAVE_LIBNL */ /* libnl version 1 */ /* #undef HAVE_LIBNL1 */ /* libnl version 2 */ /* #undef HAVE_LIBNL2 */ /* libnl version 3 */ /* #undef HAVE_LIBNL3 */ /* Define to use libpcap library */ #define HAVE_LIBPCAP 1 /* Define to 1 if you have the `smi' library (-lsmi). */ /* #undef HAVE_LIBSMI */ /* Define to use zlib library */ #define HAVE_ZLIB 1 /* Define to use lz4 library */ /* #undef HAVE_LZ4 */ /* Define to use snappy library */ /* #undef HAVE_SNAPPY */ /* Define to 1 if you have the header file. */ #define HAVE_LINUX_SOCKIOS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_LINUX_IF_BONDING_H 1 /* Define to use Lua */ /* #undef HAVE_LUA */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LUA_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_MEMORY_H */ /* Define to use MIT kerberos */ /* #undef HAVE_MIT_KERBEROS */ /* Define to 1 if you have the `mkstemps' function. */ #define HAVE_MKSTEMPS 1 /* Define to 1 if you have the `mmap' function. */ /* #undef HAVE_MMAP */ /* Define to 1 if you have the `mprotect' function. */ /* #undef HAVE_MPROTECT */ /* Define to 1 if you have the header file. */ #define HAVE_NETDB_H 1 /* Define to 1 if you have the header file. */ #define HAVE_NETINET_IN_H 1 /* nl80211.h is new enough */ /* #undef HAVE_NL80211 */ /* SET_CHANNEL is supported */ /* #undef HAVE_NL80211_CMD_SET_CHANNEL */ /* SPLIT_WIPHY_DUMP is supported */ /* #undef HAVE_NL80211_SPLIT_WIPHY_DUMP */ /* VHT_CAPABILITY is supported */ /* #undef HAVE_NL80211_VHT_CAPABILITY */ /* Define to 1 if you have macOS frameworks */ /* #undef HAVE_MACOS_FRAMEWORKS */ /* Define to 1 if you have the macOS CFPropertyListCreateWithStream function */ /* #undef HAVE_CFPROPERTYLISTCREATEWITHSTREAM */ /* Define if pcap_breakloop is known */ #define HAVE_PCAP_BREAKLOOP 1 /* Define to 1 if you have the `pcap_create' function. */ #define HAVE_PCAP_CREATE 1 /* Define to 1 if the capture buffer size can be set. */ #define CAN_SET_CAPTURE_BUFFER_SIZE 1 /* Define to 1 if you have the `pcap_datalink_name_to_val' function. */ #define HAVE_PCAP_DATALINK_NAME_TO_VAL 1 /* Define to 1 if you have the `pcap_datalink_val_to_description' function. */ #define HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION 1 /* Define to 1 if you have the `pcap_datalink_val_to_name' function. */ #define HAVE_PCAP_DATALINK_VAL_TO_NAME 1 /* Define to 1 if you have the `pcap_findalldevs' function and a pcap.h that declares pcap_if_t. */ #define HAVE_PCAP_FINDALLDEVS 1 /* Define to 1 if you have the `pcap_freecode' function. */ #define HAVE_PCAP_FREECODE 1 /* Define to 1 if you have the `pcap_free_datalinks' function. */ #define HAVE_PCAP_FREE_DATALINKS 1 /* Define to 1 if you have the `pcap_get_selectable_fd' function. */ #define HAVE_PCAP_GET_SELECTABLE_FD 1 /* Define to 1 if you have the `pcap_lib_version' function. */ #define HAVE_PCAP_LIB_VERSION 1 /* Define to 1 if you have the `pcap_list_datalinks' function. */ #define HAVE_PCAP_LIST_DATALINKS 1 /* Define to 1 if you have the `pcap_open' function. */ /* #undef HAVE_PCAP_OPEN */ /* Define to 1 if you have the `pcap_open_dead' function. */ #define HAVE_PCAP_OPEN_DEAD 1 /* Define to 1 if you have libpcap/WinPcap remote capturing support. */ /* #undef HAVE_PCAP_REMOTE */ /* Define to 1 if you have the `pcap_set_datalink' function. */ #define HAVE_PCAP_SET_DATALINK 1 /* Define to 1 if you have the `pcap_setsampling' function. */ /* #undef HAVE_PCAP_SETSAMPLING */ /* Define to 1 if you have the `pcap_set_tstamp_precision' function. */ #define HAVE_PCAP_SET_TSTAMP_PRECISION 1 /* Define to 1 if you have the `pcap_set_tstamp_type' function. */ #define HAVE_PCAP_SET_TSTAMP_TYPE 1 /* Define to 1 if you have the header file. */ #define HAVE_PWD_H 1 /* Define to 1 if you have the optreset variable */ /* #undef HAVE_OPTRESET */ /* Define if sa_len field exists in struct sockaddr */ /* #undef HAVE_STRUCT_SOCKADDR_SA_LEN */ /* Define to 1 if you want to playing SBC by standalone BlueZ SBC library */ /* #undef HAVE_SBC */ /* Define to 1 if you have the SpanDSP library. */ /* #undef HAVE_SPANDSP */ /* Define to 1 if you have the bcg729 library. */ /* #undef HAVE_BCG729 */ /* Define to 1 if you have the lixbml2 library. */ /* #undef HAVE_LIBXML2 */ /* Define to 1 if you have the `setresgid' function. */ #define HAVE_SETRESGID 1 /* Define to 1 if you have the `setresuid' function. */ #define HAVE_SETRESUID 1 /* Define to 1 if you have the WinSparkle library */ /* #undef HAVE_SOFTWARE_UPDATE */ /* Define if you have the 'strptime' function. */ #define HAVE_STRPTIME 1 /* Define to 1 if `st_birthtime' is a member of `struct stat'. */ /* #undef HAVE_STRUCT_STAT_ST_BIRTHTIME */ /* Define if st_flags field exists in struct stat */ /* #undef HAVE_STRUCT_STAT_ST_FLAGS */ /* Define to 1 if `__st_birthtime' is a member of `struct stat'. */ /* #undef HAVE_STRUCT_STAT___ST_BIRTHTIME */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_IOCTL_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_SOCKET_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_SOCKIO_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TIME_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_UTSNAME_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_WAIT_H 1 /* Define if tm_zone field exists in struct tm */ #define HAVE_STRUCT_TM_TM_ZONE 1 /* Define if tzname array exists */ #define HAVE_TZNAME 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Name of package */ /* #undef PACKAGE */ /* Define to the address where bug reports for this package should be sent. */ /* #undef PACKAGE_BUGREPORT */ /* Define to the full name of this package. */ /* #undef PACKAGE_NAME */ /* Define to the full name and version of this package. */ /* #undef PACKAGE_STRING */ /* Define to the one symbol short name of this package. */ /* #undef PACKAGE_TARNAME */ /* Define to the version of this package. */ /* #undef PACKAGE_VERSION */ /* Support for pcapng */ #define PCAP_NG_DEFAULT 1 /* Define if we are using version of of the Portaudio library API */ /* #undef PORTAUDIO_API_1 */ /* Define if we have QtMultimedia */ #define QT_MULTIMEDIA_LIB 1 /* Define if we have QtMacExtras */ /* #undef QT_MACEXTRAS_LIB */ /* Define if we have QtWinExtras */ /* #undef QT_WINEXTRAS_LIB */ /* Build androiddump with libpcap instead of wireshark stuff */ /* #undef ANDROIDDUMP_USE_LIBPCAP */ /* Large file support */ /* #undef _LARGEFILE_SOURCE */ /* #undef _LARGEFILE64_SOURCE */ /* #undef _LARGE_FILES */ /* #undef _FILE_OFFSET_BITS */ /* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a `char[]'. */ /* Note: not use in the code */ /* #undef YYTEXT_POINTER */ #if defined(_WIN32) /* * Make sure everyone is using the same API and that it's sufficient * for our needs. * This should match the following: * - The section in image\wireshark.exe.manifest.in * - The GetWindowsVersion parts of packaging\nsis\wireshark.nsi * - The VersionNT parts of packaging\wix\Prerequisites.wxi */ # if defined(NTDDI_VERSION) # error NTDDI_VERSION already defined. # endif # define NTDDI_VERSION NTDDI_WIN7 # if defined(_WIN32_WINNT) # error _WIN32_WINNT already defined. # endif # define _WIN32_WINNT _WIN32_WINNT_WIN7 /* WpdPack/INclude/pcap/pcap.h checks for "#if defined(WIN32)" */ # ifndef WIN32 # define WIN32 1 # endif # if !defined(QT_VERSION) || !defined(_SSIZE_T_DEFINED) typedef int ssize_t; # endif /* * Flex (v 2.5.35) uses this symbol to "exclude" unistd.h */ # define YY_NO_UNISTD_H # define strncasecmp strnicmp # define popen _popen # define pclose _pclose # ifndef __STDC__ # define __STDC__ 0 # endif /* Use Unicode in Windows runtime functions. */ # define UNICODE 1 # define _UNICODE 1 # define NEED_STRPTIME_H 1 #endif #if defined(__APPLE__) /* This is to trigger the integration of objective-c * code builds for removing unnecessary menu entries * in 10.12 > and Qt 5.3 > */ # define CMAKE_BUILD 1 #endif #endif /* __CONFIG_H__ */ ================================================ FILE: BinUtils/TRCToCap/include/pcapio.h ================================================ /* pcapio.h * Declarations of our own routines for writing libpcap files. * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * Derived from code in the Wiretap Library * Copyright (c) 1998 by Gilbert Ramirez * * SPDX-License-Identifier: GPL-2.0-or-later */ /* Writing pcap files */ /** Write the file header to a dump file. Returns TRUE on success, FALSE on failure. Sets "*err" to an error code, or 0 for a short write, on failure*/ extern gboolean libpcap_write_file_header(FILE* pfile, int linktype, int snaplen, gboolean ts_nsecs, guint64 *bytes_written, int *err); /** Write a record for a packet to a dump file. Returns TRUE on success, FALSE on failure. */ extern gboolean libpcap_write_packet(FILE* pfile, time_t sec, guint32 usec, guint32 caplen, guint32 len, const guint8 *pd, guint64 *bytes_written, int *err); /* Writing pcapng files */ /* Write a pre-formatted pcapng block */ extern gboolean pcapng_write_block(FILE* pfile, const guint8 *data, guint32 block_total_length, guint64 *bytes_written, int *err); /** Write a section header block (SHB) * */ extern gboolean pcapng_write_session_header_block(FILE* pfile, /**< Write information */ const char *comment, /**< Comment on the section, Optinon 1 opt_comment * A UTF-8 string containing a comment that is associated to the current block. */ const char *hw, /**< HW, Optinon 2 shb_hardware * An UTF-8 string containing the description of the hardware used to create this section. */ const char *os, /**< Operating system name, Optinon 3 shb_os * An UTF-8 string containing the name of the operating system used to create this section. */ const char *appname, /**< Application name, Optinon 4 shb_userappl * An UTF-8 string containing the name of the application used to create this section. */ guint64 section_length, /**< Length of section */ guint64 *bytes_written, /**< Number of written bytes */ int *err /**< Error type */ ); extern gboolean pcapng_write_interface_description_block(FILE* pfile, const char *comment, /* OPT_COMMENT 1 */ const char *name, /* IDB_NAME 2 */ const char *descr, /* IDB_DESCRIPTION 3 */ const char *filter, /* IDB_FILTER 11 */ const char *os, /* IDB_OS 12 */ int link_type, int snap_len, guint64 *bytes_written, guint64 if_speed, /* IDB_IF_SPEED 8 */ guint8 tsresol, /* IDB_TSRESOL 9 */ int *err); extern gboolean pcapng_write_interface_statistics_block(FILE* pfile, guint32 interface_id, guint64 *bytes_written, const char *comment, /* OPT_COMMENT 1 */ guint64 isb_starttime, /* ISB_STARTTIME 2 */ guint64 isb_endtime, /* ISB_ENDTIME 3 */ guint64 isb_ifrecv, /* ISB_IFRECV 4 */ guint64 isb_ifdrop, /* ISB_IFDROP 5 */ int *err); extern gboolean pcapng_write_enhanced_packet_block(FILE* pfile, const char *comment, time_t sec, guint32 usec, guint32 caplen, guint32 len, guint32 interface_id, guint ts_mul, const guint8 *pd, guint32 flags, guint64 *bytes_written, int *err); /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */ ================================================ FILE: BinUtils/TRCToCap/src/TRCToCap.cpp ================================================ //============================================================================ // Name : TRCToCap.cpp // Author : Fernando Ámez García // Version : // Copyright : // Description : A tool to convert TRC files to CAP files so that it can be analyzed by wireshark //============================================================================ extern "C" { #include #include #include #include //To write cap files } #include #include //To read TRC files #define CAN_LINKTYPE 0xE3 #define CAN_ID_LENGTH 4 #define LENGTH_LENGTH 1 #define RESERVED_LENGTH 3 using namespace Can; int main(int argc, char **argv) { //Get options int c; std::string input, output; static struct option long_options[] = { {"input", required_argument, NULL, 'i'}, {"output", required_argument, NULL, 'o'}, {NULL, 0, NULL, 0} }; while (1) { c = getopt_long (argc, argv, "i:o:", long_options, NULL); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'i': input = optarg; break; case 'o': output = optarg; break; default: break; } } if(input.empty()) { std::cerr << "No input specified" << std::endl; return -1; } if(output.empty()) { std::cerr << "No output specified" << std::endl; return -1; } TRCReader trcReader; if(!trcReader.loadFile(input)) { std::cerr << "TRC file is corrupted or not readable by " << argv[0] << std::endl; return -2; } if(trcReader.getNumberOfFrames() == 0) { std::cerr << "TRC file is empty" << std::endl; return -3; } std::cout << "TRC file loaded" << std::endl; FILE *fd = fopen(output.c_str(), "wb"); if(!fd) { std::cerr << "Output file could not be open" << std::endl; } std::cout << "Cap file open" << std::endl; guint64 bytes_written; int err; std::pair pairTStampFrame; libpcap_write_file_header(fd, CAN_LINKTYPE, 0xFFFF, TRUE, &bytes_written, &err); std::cout << "Header written" << std::endl; int progress = 0, oldProgress = 0; //Iterate over the frames do { trcReader.readNextCanFrame(); pairTStampFrame = trcReader.getLastCanFrame(); u64 timeStamp = pairTStampFrame.first; const CanFrame& frame = pairTStampFrame.second; size_t length = CAN_ID_LENGTH + LENGTH_LENGTH + RESERVED_LENGTH + frame.getData().size(); std::string data; //Add the ID data += (((frame.getId() >> 24) & 0xFF) | (frame.isExtendedFormat() ? 0x80 : 0x00)); //Extended flag present? data += ((frame.getId() >> 16) & 0xFF); data += ((frame.getId() >> 8) & 0xFF); data += (frame.getId() & 0xFF); //Add the length data += frame.getData().size(); //Add the reserved characters data += (char)0; data += (char)0; data += (char)0; //Append the DLC of the frame data += frame.getData(); libpcap_write_packet(fd, timeStamp / 1000000, timeStamp % 1000000, length, length, (const guint8 *)(data.c_str()), &bytes_written, &err); progress = 100 * trcReader.getCurrentPos() / trcReader.getNumberOfFrames(); if(progress != oldProgress) { std::cout << "Progress: " << progress << " %" << std::endl; } oldProgress = progress; } while(trcReader.getCurrentPos() < trcReader.getNumberOfFrames() - 1); fclose(fd); std::cout << "Cap file correctly generated" << std::endl; } ================================================ FILE: BinUtils/TRCToCap/src/pcapio.c ================================================ /* pcapio.c * Our own private code for writing libpcap files when capturing. * * We have these because we want a way to open a stream for output given * only a file descriptor. libpcap 0.9[.x] has "pcap_dump_fopen()", which * provides that, but * * 1) earlier versions of libpcap doesn't have it * * and * * 2) WinPcap doesn't have it, because a file descriptor opened * by code built for one version of the MSVC++ C library * can't be used by library routines built for another version * (e.g., threaded vs. unthreaded). * * Libpcap's pcap_dump() also doesn't return any error indications. * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * Derived from code in the Wiretap Library * Copyright (c) 1998 by Gilbert Ramirez * * SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #include #include #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef _WIN32 #include #endif #include #include "pcapio.h" /* Magic numbers in "libpcap" files. "libpcap" file records are written in the byte order of the host that writes them, and the reader is expected to fix this up. PCAP_MAGIC is the magic number, in host byte order; PCAP_SWAPPED_MAGIC is a byte-swapped version of that. PCAP_NSEC_MAGIC is for Ulf Lamping's modified "libpcap" format, which uses the same common file format as PCAP_MAGIC, but the timestamps are saved in nanosecond resolution instead of microseconds. PCAP_SWAPPED_NSEC_MAGIC is a byte-swapped version of that. */ #define PCAP_MAGIC 0xa1b2c3d4 #define PCAP_SWAPPED_MAGIC 0xd4c3b2a1 #define PCAP_NSEC_MAGIC 0xa1b23c4d #define PCAP_SWAPPED_NSEC_MAGIC 0x4d3cb2a1 /* "libpcap" file header. */ struct pcap_hdr { guint32 magic; /* magic number */ guint16 version_major; /* major version number */ guint16 version_minor; /* minor version number */ gint32 thiszone; /* GMT to local correction */ guint32 sigfigs; /* accuracy of timestamps */ guint32 snaplen; /* max length of captured packets, in octets */ guint32 network; /* data link type */ }; /* "libpcap" record header. */ struct pcaprec_hdr { guint32 ts_sec; /* timestamp seconds */ guint32 ts_usec; /* timestamp microseconds (nsecs for PCAP_NSEC_MAGIC) */ guint32 incl_len; /* number of octets of packet saved in file */ guint32 orig_len; /* actual length of packet */ }; /* Magic numbers in ".pcapng" files. * * .pcapng file records are written in the byte order of the host that * writes them, and the reader is expected to fix this up. * PCAPNG_MAGIC is the magic number, in host byte order; * PCAPNG_SWAPPED_MAGIC is a byte-swapped version of that. */ #define PCAPNG_MAGIC 0x1A2B3C4D #define PCAPNG_SWAPPED_MAGIC 0xD4C3B2A1 /* Currently we are only supporting the initial version of the file format. */ #define PCAPNG_MAJOR_VERSION 1 #define PCAPNG_MINOR_VERSION 0 /* Section Header Block without options and trailing Block Total Length */ struct shb { guint32 block_type; guint32 block_total_length; guint32 byte_order_magic; guint16 major_version; guint16 minor_version; guint64 section_length; }; #define SECTION_HEADER_BLOCK_TYPE 0x0A0D0D0A /* Interface Description Block without options and trailing Block Total Length */ struct idb { guint32 block_type; guint32 block_total_length; guint16 link_type; guint16 reserved; guint32 snap_len; }; #define INTERFACE_DESCRIPTION_BLOCK_TYPE 0x00000001 /* Interface Statistics Block without actual packet, options, and trailing Block Total Length */ struct isb { guint32 block_type; guint32 block_total_length; guint32 interface_id; guint32 timestamp_high; guint32 timestamp_low; }; #define INTERFACE_STATISTICS_BLOCK_TYPE 0x00000005 /* Enhanced Packet Block without actual packet, options, and trailing Block Total Length */ struct epb { guint32 block_type; guint32 block_total_length; guint32 interface_id; guint32 timestamp_high; guint32 timestamp_low; guint32 captured_len; guint32 packet_len; }; #define ENHANCED_PACKET_BLOCK_TYPE 0x00000006 struct option { guint16 type; guint16 value_length; }; #define OPT_ENDOFOPT 0 #define OPT_COMMENT 1 #define EPB_FLAGS 2 #define SHB_HARDWARE 2 /* currently not used */ #define SHB_OS 3 #define SHB_USERAPPL 4 #define IDB_NAME 2 #define IDB_DESCRIPTION 3 #define IDB_IF_SPEED 8 #define IDB_TSRESOL 9 #define IDB_FILTER 11 #define IDB_OS 12 #define ISB_STARTTIME 2 #define ISB_ENDTIME 3 #define ISB_IFRECV 4 #define ISB_IFDROP 5 #define ISB_FILTERACCEPT 6 #define ISB_OSDROP 7 #define ISB_USRDELIV 8 #define ADD_PADDING(x) ((((x) + 3) >> 2) << 2) /* Write to capture file */ static gboolean write_to_file(FILE* pfile, const guint8* data, size_t data_length, guint64 *bytes_written, int *err) { size_t nwritten; nwritten = fwrite(data, data_length, 1, pfile); if (nwritten != 1) { if (ferror(pfile)) { *err = errno; } else { *err = 0; } return FALSE; } (*bytes_written) += data_length; return TRUE; } /* Writing pcap files */ /* Write the file header to a dump file. Returns TRUE on success, FALSE on failure. Sets "*err" to an error code, or 0 for a short write, on failure*/ gboolean libpcap_write_file_header(FILE* pfile, int linktype, int snaplen, gboolean ts_nsecs, guint64 *bytes_written, int *err) { struct pcap_hdr file_hdr; file_hdr.magic = ts_nsecs ? PCAP_NSEC_MAGIC : PCAP_MAGIC; /* current "libpcap" format is 2.4 */ file_hdr.version_major = 2; file_hdr.version_minor = 4; file_hdr.thiszone = 0; /* XXX - current offset? */ file_hdr.sigfigs = 0; /* unknown, but also apparently unused */ file_hdr.snaplen = snaplen; file_hdr.network = linktype; return write_to_file(pfile, (const guint8*)&file_hdr, sizeof(file_hdr), bytes_written, err); } /* Write a record for a packet to a dump file. Returns TRUE on success, FALSE on failure. */ gboolean libpcap_write_packet(FILE* pfile, time_t sec, guint32 usec, guint32 caplen, guint32 len, const guint8 *pd, guint64 *bytes_written, int *err) { struct pcaprec_hdr rec_hdr; rec_hdr.ts_sec = (guint32)sec; /* Y2.038K issue in pcap format.... */ rec_hdr.ts_usec = usec; rec_hdr.incl_len = caplen; rec_hdr.orig_len = len; if (!write_to_file(pfile, (const guint8*)&rec_hdr, sizeof(rec_hdr), bytes_written, err)) return FALSE; return write_to_file(pfile, pd, caplen, bytes_written, err); } /* Writing pcapng files */ static guint32 pcapng_count_string_option(const char *option_value) { if ((option_value != NULL) && (strlen(option_value) > 0) && (strlen(option_value) < G_MAXUINT16)) { /* There's a value to write; get its length */ return (guint32)(sizeof(struct option) + (guint16)ADD_PADDING(strlen(option_value))); } return 0; /* nothing to write */ } static gboolean pcapng_write_string_option(FILE* pfile, guint16 option_type, const char *option_value, guint64 *bytes_written, int *err) { size_t option_value_length; struct option option; const guint32 padding = 0; if (option_value == NULL) return TRUE; /* nothing to write */ option_value_length = strlen(option_value); if ((option_value_length > 0) && (option_value_length < G_MAXUINT16)) { /* something to write */ option.type = option_type; option.value_length = (guint16)option_value_length; if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)option_value, (int) option_value_length, bytes_written, err)) return FALSE; if (option_value_length % 4) { if (!write_to_file(pfile, (const guint8*)&padding, 4 - option_value_length % 4, bytes_written, err)) return FALSE; } } return TRUE; } /* Write a pre-formatted pcapng block directly to the output file */ gboolean pcapng_write_block(FILE* pfile, const guint8 *data, guint32 length, guint64 *bytes_written, int *err) { guint32 block_length, end_lenth; /* Check * - length and data are aligned to 4 bytes * - block_total_length field is the same at the start and end of the block * * The block_total_length is not checked against the provided length but * getting the trailing block_total_length from the length argument gives * us an implicit check of correctness without needing to do an endian swap */ if (((length & 3) != 0) || (((gintptr)data & 3) != 0)) { return FALSE; } memcpy(&block_length, data+sizeof(guint32), sizeof(guint32)); memcpy(&end_lenth, data+length-sizeof(guint32), sizeof(guint32)); if (block_length != end_lenth) { return FALSE; } return write_to_file(pfile, data, length, bytes_written, err); } gboolean pcapng_write_session_header_block(FILE* pfile, const char *comment, const char *hw, const char *os, const char *appname, guint64 section_length, guint64 *bytes_written, int *err) { struct shb shb; struct option option; guint32 block_total_length; guint32 options_length; /* Size of base header */ block_total_length = sizeof(struct shb) + sizeof(guint32); options_length = 0; options_length += pcapng_count_string_option(comment); options_length += pcapng_count_string_option(hw); options_length += pcapng_count_string_option(os); options_length += pcapng_count_string_option(appname); /* If we have options add size of end-of-options */ if (options_length != 0) { options_length += (guint32)sizeof(struct option); } block_total_length += options_length; /* write shb header */ shb.block_type = SECTION_HEADER_BLOCK_TYPE; shb.block_total_length = block_total_length; shb.byte_order_magic = PCAPNG_MAGIC; shb.major_version = PCAPNG_MAJOR_VERSION; shb.minor_version = PCAPNG_MINOR_VERSION; shb.section_length = section_length; if (!write_to_file(pfile, (const guint8*)&shb, sizeof(struct shb), bytes_written, err)) return FALSE; if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment, bytes_written, err)) return FALSE; if (!pcapng_write_string_option(pfile, SHB_HARDWARE, hw, bytes_written, err)) return FALSE; if (!pcapng_write_string_option(pfile, SHB_OS, os, bytes_written, err)) return FALSE; if (!pcapng_write_string_option(pfile, SHB_USERAPPL, appname, bytes_written, err)) return FALSE; if (options_length != 0) { /* write end of options */ option.type = OPT_ENDOFOPT; option.value_length = 0; if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; } /* write the trailing block total length */ return write_to_file(pfile, (const guint8*)&block_total_length, sizeof(guint32), bytes_written, err); } gboolean pcapng_write_interface_description_block(FILE* pfile, const char *comment, /* OPT_COMMENT 1 */ const char *name, /* IDB_NAME 2 */ const char *descr, /* IDB_DESCRIPTION 3 */ const char *filter, /* IDB_FILTER 11 */ const char *os, /* IDB_OS 12 */ int link_type, int snap_len, guint64 *bytes_written, guint64 if_speed, /* IDB_IF_SPEED 8 */ guint8 tsresol, /* IDB_TSRESOL 9 */ int *err) { struct idb idb; struct option option; guint32 block_total_length; guint32 options_length; const guint32 padding = 0; block_total_length = (guint32)(sizeof(struct idb) + sizeof(guint32)); options_length = 0; /* 01 - OPT_COMMENT */ options_length += pcapng_count_string_option(comment); /* 02 - IDB_NAME */ options_length += pcapng_count_string_option(name); /* 03 - IDB_DESCRIPTION */ options_length += pcapng_count_string_option(descr); /* 08 - IDB_IF_SPEED */ if (if_speed != 0) { options_length += (guint32)(sizeof(struct option) + sizeof(guint64)); } /* 09 - IDB_TSRESOL */ if (tsresol != 0) { options_length += (guint32)(sizeof(struct option) + sizeof(struct option)); } /* 11 - IDB_FILTER */ if ((filter != NULL) && (strlen(filter) > 0) && (strlen(filter) < G_MAXUINT16)) { /* No, this isn't a string, it has an extra type byte */ options_length += (guint32)(sizeof(struct option) + (guint16)(ADD_PADDING(strlen(filter)+ 1))); } /* 12 - IDB_OS */ options_length += pcapng_count_string_option(os); /* If we have options add size of end-of-options */ if (options_length != 0) { options_length += (guint32)sizeof(struct option); } block_total_length += options_length; /* write block header */ idb.block_type = INTERFACE_DESCRIPTION_BLOCK_TYPE; idb.block_total_length = block_total_length; idb.link_type = link_type; idb.reserved = 0; idb.snap_len = snap_len; if (!write_to_file(pfile, (const guint8*)&idb, sizeof(struct idb), bytes_written, err)) return FALSE; /* 01 - OPT_COMMENT - write comment string if applicable */ if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment, bytes_written, err)) return FALSE; /* 02 - IDB_NAME - write interface name string if applicable */ if (!pcapng_write_string_option(pfile, IDB_NAME, name, bytes_written, err)) return FALSE; /* 03 - IDB_DESCRIPTION */ /* write interface description string if applicable */ if (!pcapng_write_string_option(pfile, IDB_DESCRIPTION, descr, bytes_written, err)) return FALSE; /* 08 - IDB_IF_SPEED */ if (if_speed != 0) { option.type = IDB_IF_SPEED; option.value_length = sizeof(guint64); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&if_speed, sizeof(guint64), bytes_written, err)) return FALSE; } /* 09 - IDB_TSRESOL */ if (tsresol != 0) { option.type = IDB_TSRESOL; option.value_length = sizeof(guint8); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&tsresol, sizeof(guint8), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&padding, 3, bytes_written, err)) return FALSE; } /* 11 - IDB_FILTER - write filter string if applicable * We only write version 1 of the filter, pcapng string */ if ((filter != NULL) && (strlen(filter) > 0) && (strlen(filter) < G_MAXUINT16 - 1)) { option.type = IDB_FILTER; option.value_length = (guint16)(strlen(filter) + 1 ); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; /* The first byte of the Option Data keeps a code of the filter used, 0 = lipbpcap filter string */ if (!write_to_file(pfile, (const guint8*)&padding, 1, bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)filter, (int) strlen(filter), bytes_written, err)) return FALSE; if ((strlen(filter) + 1) % 4) { if (!write_to_file(pfile, (const guint8*)&padding, 4 - (strlen(filter) + 1) % 4, bytes_written, err)) return FALSE; } } /* 12 - IDB_OS - write os string if applicable */ if (!pcapng_write_string_option(pfile, IDB_OS, os, bytes_written, err)) return FALSE; if (options_length != 0) { /* write end of options */ option.type = OPT_ENDOFOPT; option.value_length = 0; if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; } /* write the trailing Block Total Length */ return write_to_file(pfile, (const guint8*)&block_total_length, sizeof(guint32), bytes_written, err); } /* Write a record for a packet to a dump file. Returns TRUE on success, FALSE on failure. */ gboolean pcapng_write_enhanced_packet_block(FILE* pfile, const char *comment, time_t sec, guint32 usec, guint32 caplen, guint32 len, guint32 interface_id, guint ts_mul, const guint8 *pd, guint32 flags, guint64 *bytes_written, int *err) { struct epb epb; struct option option; guint32 block_total_length; guint64 timestamp; guint32 options_length; const guint32 padding = 0; guint8 buff[8]; guint8 i; guint8 pad_len = 0; block_total_length = (guint32)(sizeof(struct epb) + ADD_PADDING(caplen) + sizeof(guint32)); options_length = 0; options_length += pcapng_count_string_option(comment); if (flags != 0) { options_length += (guint32)(sizeof(struct option) + sizeof(guint32)); } /* If we have options add size of end-of-options */ if (options_length != 0) { options_length += (guint32)sizeof(struct option); } block_total_length += options_length; timestamp = (guint64)sec * ts_mul + (guint64)usec; epb.block_type = ENHANCED_PACKET_BLOCK_TYPE; epb.block_total_length = block_total_length; epb.interface_id = interface_id; epb.timestamp_high = (guint32)((timestamp>>32) & 0xffffffff); epb.timestamp_low = (guint32)(timestamp & 0xffffffff); epb.captured_len = caplen; epb.packet_len = len; if (!write_to_file(pfile, (const guint8*)&epb, sizeof(struct epb), bytes_written, err)) return FALSE; if (!write_to_file(pfile, pd, caplen, bytes_written, err)) return FALSE; /* Use more efficient write in case of no "extras" */ if(caplen % 4) { pad_len = 4 - (caplen % 4); } /* * If we have no options to write, just write out the padding and * the block total length with one fwrite() call. */ if(!comment && flags == 0 && options_length==0){ /* Put padding in the buffer */ for (i = 0; i < pad_len; i++) { buff[i] = 0; } /* Write the total length */ memcpy(&buff[i], &block_total_length, sizeof(guint32)); i += sizeof(guint32); return write_to_file(pfile, (const guint8*)&buff, i, bytes_written, err); } if (pad_len) { if (!write_to_file(pfile, (const guint8*)&padding, pad_len, bytes_written, err)) return FALSE; } if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment, bytes_written, err)) return FALSE; if (flags != 0) { option.type = EPB_FLAGS; option.value_length = sizeof(guint32); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&flags, sizeof(guint32), bytes_written, err)) return FALSE; } if (options_length != 0) { /* write end of options */ option.type = OPT_ENDOFOPT; option.value_length = 0; if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; } return write_to_file(pfile, (const guint8*)&block_total_length, sizeof(guint32), bytes_written, err); } gboolean pcapng_write_interface_statistics_block(FILE* pfile, guint32 interface_id, guint64 *bytes_written, const char *comment, /* OPT_COMMENT 1 */ guint64 isb_starttime, /* ISB_STARTTIME 2 */ guint64 isb_endtime, /* ISB_ENDTIME 3 */ guint64 isb_ifrecv, /* ISB_IFRECV 4 */ guint64 isb_ifdrop, /* ISB_IFDROP 5 */ int *err) { struct isb isb; #ifdef _WIN32 FILETIME now; #else struct timeval now; #endif struct option option; guint32 block_total_length; guint32 options_length; guint64 timestamp; #ifdef _WIN32 /* * Current time, represented as 100-nanosecond intervals since * January 1, 1601, 00:00:00 UTC. * * I think DWORD might be signed, so cast both parts of "now" * to guint32 so that the sign bit doesn't get treated specially. * * Windows 8 provides GetSystemTimePreciseAsFileTime which we * might want to use instead. */ GetSystemTimeAsFileTime(&now); timestamp = (((guint64)(guint32)now.dwHighDateTime) << 32) + (guint32)now.dwLowDateTime; /* * Convert to same thing but as 1-microsecond, i.e. 1000-nanosecond, * intervals. */ timestamp /= 10; /* * Subtract difference, in microseconds, between January 1, 1601 * 00:00:00 UTC and January 1, 1970, 00:00:00 UTC. */ timestamp -= G_GUINT64_CONSTANT(11644473600000000); #else /* * Current time, represented as seconds and microseconds since * January 1, 1970, 00:00:00 UTC. */ gettimeofday(&now, NULL); /* * Convert to delta in microseconds. */ timestamp = (guint64)(now.tv_sec) * 1000000 + (guint64)(now.tv_usec); #endif block_total_length = (guint32)(sizeof(struct isb) + sizeof(guint32)); options_length = 0; if (isb_ifrecv != G_MAXUINT64) { options_length += (guint32)(sizeof(struct option) + sizeof(guint64)); } if (isb_ifdrop != G_MAXUINT64) { options_length += (guint32)(sizeof(struct option) + sizeof(guint64)); } /* OPT_COMMENT */ options_length += pcapng_count_string_option(comment); if (isb_starttime !=0) { options_length += (guint32)(sizeof(struct option) + sizeof(guint64)); /* ISB_STARTTIME */ } if (isb_endtime !=0) { options_length += (guint32)(sizeof(struct option) + sizeof(guint64)); /* ISB_ENDTIME */ } /* If we have options add size of end-of-options */ if (options_length != 0) { options_length += (guint32)sizeof(struct option); } block_total_length += options_length; isb.block_type = INTERFACE_STATISTICS_BLOCK_TYPE; isb.block_total_length = block_total_length; isb.interface_id = interface_id; isb.timestamp_high = (guint32)((timestamp>>32) & 0xffffffff); isb.timestamp_low = (guint32)(timestamp & 0xffffffff); if (!write_to_file(pfile, (const guint8*)&isb, sizeof(struct isb), bytes_written, err)) return FALSE; /* write comment string if applicable */ if (!pcapng_write_string_option(pfile, OPT_COMMENT, comment, bytes_written, err)) return FALSE; if (isb_starttime !=0) { guint32 high, low; option.type = ISB_STARTTIME; option.value_length = sizeof(guint64); high = (guint32)((isb_starttime>>32) & 0xffffffff); low = (guint32)(isb_starttime & 0xffffffff); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&high, sizeof(guint32), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&low, sizeof(guint32), bytes_written, err)) return FALSE; } if (isb_endtime !=0) { guint32 high, low; option.type = ISB_ENDTIME; option.value_length = sizeof(guint64); high = (guint32)((isb_endtime>>32) & 0xffffffff); low = (guint32)(isb_endtime & 0xffffffff); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&high, sizeof(guint32), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&low, sizeof(guint32), bytes_written, err)) return FALSE; } if (isb_ifrecv != G_MAXUINT64) { option.type = ISB_IFRECV; option.value_length = sizeof(guint64); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&isb_ifrecv, sizeof(guint64), bytes_written, err)) return FALSE; } if (isb_ifdrop != G_MAXUINT64) { option.type = ISB_IFDROP; option.value_length = sizeof(guint64); if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; if (!write_to_file(pfile, (const guint8*)&isb_ifdrop, sizeof(guint64), bytes_written, err)) return FALSE; } if (options_length != 0) { /* write end of options */ option.type = OPT_ENDOFOPT; option.value_length = 0; if (!write_to_file(pfile, (const guint8*)&option, sizeof(struct option), bytes_written, err)) return FALSE; } return write_to_file(pfile, (const guint8*)&block_total_length, sizeof(guint32), bytes_written, err); } /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=8 tabstop=8 expandtab: * :indentSize=8:tabSize=8:noTabs=true: */ ================================================ FILE: BinUtils/j1939AddrClaim/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(j1939AddrClaim) add_executable(j1939AddrClaim j1939AddrClaim.cpp ) target_include_directories(j1939AddrClaim PUBLIC include ${J1939_SOURCE_DIR}/include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ${J1939AddressClaimer_SOURCE_DIR}/include ) target_link_libraries(j1939AddrClaim PUBLIC J1939 Can J1939AddressClaimer rt jsoncpp -rdynamic ) install (TARGETS j1939AddrClaim DESTINATION bin) ================================================ FILE: BinUtils/j1939AddrClaim/Readme.md ================================================ Used to simulate the Address Claiming Process Usage: ```bash ./j1939AddrClaim -n -i -p "[,,,...]" ``` ================================================ FILE: BinUtils/j1939AddrClaim/j1939AddrClaim.cpp ================================================ /* * AddressClaim.cpp * * Created on: Jan 13, 2019 * Author: famez */ #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif #include #include #include #include #include #include #include #include #define BAUD_250K 250000 namespace J1939 { class MyAddressClaimer : public AddressClaimer { private: std::shared_ptr mSender; protected: void onSrcAddrChanged(u8 newAddr) { if(newAddr == J1939_INVALID_ADDRESS) { std::cout << "No address!!!" << std::endl; } else { std::cout << "New address obtained " << static_cast(newAddr) << std::endl; } } void sendFrame(const J1939Frame& frame) { size_t length = frame.getDataLength(); u32 id; u8 *buff = new u8[length]; frame.encode(id, buff, length); Can::CanFrame canFrame; //J1939 data is always transmitted in extended format canFrame.setExtendedFormat(true); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); mSender->sendFrameOnce(canFrame); delete[] buff; } public: MyAddressClaimer(const EcuName& name, const std::queue& preferred, std::shared_ptr sender) : AddressClaimer(name, preferred), mSender(sender) {} ~MyAddressClaimer() {} }; } using namespace J1939; using namespace Can; using namespace Utils; std::unique_ptr addresClaimer; void onRcv(const CanFrame& frame, const TimeStamp&, const std::string& interface, void*); bool onTimeout(); int main (int argc, char **argv) { //Get options int c; std::string interface; std::string preferredAddr; std::string idNumberStr; static struct option long_options[] = { {"interface", required_argument, NULL, 'i'}, {"preferred", required_argument, NULL, 'p'}, {"identity_number", required_argument, NULL, 'n'}, {NULL, 0, NULL, 0} }; while (1) { c = getopt_long (argc, argv, "i:p:n:", long_options, NULL); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'i': interface = optarg; break; case 'p': preferredAddr = optarg; break; case 'n': idNumberStr = optarg; break; default: break; } } u32 idNumber; try { idNumber = std::stoul(idNumberStr); } catch (std::invalid_argument& ) { std::cerr << "identity_number is not a number..." << std::endl; return -1; } CanEasy::initialize(BAUD_250K, onRcv, onTimeout); std::set interfaces = CanEasy::getCanIfaces(); if(interfaces.find(interface) == interfaces.end()) { std::cerr << "Interface not available" << std::endl; std::cerr << "Interfaces: " << std::endl; for(auto iter = interfaces.begin(); iter != interfaces.end(); ++iter) { std::cerr << *iter << std::endl; } return -2; } std::shared_ptr sender = CanEasy::getSender(interface); if(!sender) { std::cerr << "Cannot send through the interface" << std::endl; return -3; } EcuName name; name.setIdNumber(idNumber); Json::Value addresses; Json::CharReaderBuilder builder; Json::CharReader *jSonReader = builder.newCharReader(); std::string errs; if(!jSonReader->parse(preferredAddr.c_str(), preferredAddr.c_str() + preferredAddr.size(), &addresses, &errs)) { std::cerr << "No json format for preferred" << std::endl; return -4; } std::queue preferred; if(!addresses.isArray()) { std::cerr << "preferred is not an array" << std::endl; return -5; } for(unsigned int i = 0; i < addresses.size(); ++i) { if(addresses[i].isUInt() && (addresses[i].asUInt() == addresses[i].asUInt() & J1939_SRC_ADDR_MASK)) { preferred.push(addresses[i].asUInt()); } } if(addresses.empty()) { std::cerr << "No addresses passed to argument preferred" << std::endl; return -6; } addresClaimer = std::unique_ptr(new MyAddressClaimer(name, preferred, sender)); const CanSniffer& canSniffer = CanEasy::getSniffer(); canSniffer.sniff(1000); return 0; } void onRcv(const CanFrame& frame, const TimeStamp&, const std::string& interface, void*) { std::unique_ptr j1939Frame = J1939Factory::getInstance(). getJ1939Frame(frame.getId(), (const u8*)(frame.getData().c_str()), frame.getData().size()); if(j1939Frame) { if(addresClaimer->toBeHandled(*j1939Frame)) { addresClaimer->receive(*j1939Frame); } } } bool onTimeout() { return true; } ================================================ FILE: BinUtils/j1939AddressMapper/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(j1939AddressMapper) add_executable(j1939AddressMapper j1939AddressMapper.cpp ) target_include_directories(j1939AddressMapper PUBLIC include ${J1939_SOURCE_DIR}/include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) target_link_libraries(j1939AddressMapper PUBLIC J1939 Can rt jsoncpp -rdynamic ) install (TARGETS j1939AddressMapper DESTINATION bin) ================================================ FILE: BinUtils/j1939AddressMapper/Readme.md ================================================ To detect the ECUs in the Can Network, their Sources Address and Ecu Names Usage: ```bash j1939AddressMapper ``` ================================================ FILE: BinUtils/j1939AddressMapper/j1939AddressMapper.cpp ================================================ /* * j1939AddressMapper.cpp * * Created on: Jan 25, 2019 * Author: famez */ #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif #include #include #include #include #include #include #include #include #define BAUD_250K 250000 using namespace J1939; using namespace Can; using namespace Utils; std::set names; void onRcv(const CanFrame& frame, const TimeStamp&, const std::string& interface, void*); bool onTimeout(); int main (int argc, char **argv) { //Initialize can CanEasy::initialize(BAUD_250K, onRcv, onTimeout); const std::set& ifaces = CanEasy::getInitializedCanIfaces(); if(ifaces.empty()) { std::cerr << "No interfaces initialized" << std::endl; return 1; } /* * Create request frame to request the PGN corresponding to the Address Claim */ RequestFrame reqFrame(ADDRESS_CLAIM_PGN); reqFrame.setSrcAddr(J1939_INVALID_ADDRESS); reqFrame.setDstAddr(J1939_BROADCAST_ADDRESS); CanFrame canFrame; size_t length = reqFrame.getDataLength(); u32 id; u8 *buff = new u8[length]; reqFrame.encode(id, buff, length); //J1939 data is always transmitted in extended format canFrame.setExtendedFormat(true); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; for(auto iface = ifaces.begin(); iface != ifaces.end(); ++iface) { std::shared_ptr sender = CanEasy::getSender(*iface); sender->sendFrame(canFrame, 1000); //Send the frame every second } CanSniffer& canSniffer = CanEasy::getSniffer(); std::set filters; //Receive only frames with the ADDRESS_CLAIM_PGN CanFilter filter(ADDRESS_CLAIM_PGN << J1939_PGN_OFFSET , ((J1939_PDU_FMT_MASK << J1939_PDU_FMT_OFFSET) << J1939_PGN_OFFSET), true, false); filters.insert(filter); canSniffer.setFilters(filters); std::cout << "Interface\tSource Address\tIN\tMC\tEI\tFI" "\tF\tVS\tVSI\tIG\tAAC" << std::endl << std::endl; canSniffer.sniff(1000); return 0; } void onRcv(const CanFrame& frame, const TimeStamp&, const std::string& interface, void*) { std::unique_ptr j1939Frame = J1939Factory::getInstance(). getJ1939Frame(frame.getId(), (const u8*)(frame.getData().c_str()), frame.getData().size()); if(j1939Frame && j1939Frame->getPGN() == ADDRESS_CLAIM_PGN) { AddressClaimFrame *addrClaimFrame = static_cast(j1939Frame.get()); const EcuName &name = addrClaimFrame->getEcuName(); if(names.find(name) == names.end()) { //Check if we have already shown the frame std::cout << interface << '\t' << '\t' << static_cast(j1939Frame->getSrcAddr()) << '\t' << '\t' << name.getIdNumber() << '\t' << static_cast(name.getManufacturerCode()) << '\t' << static_cast(name.getEcuInstance()) << '\t' << static_cast(name.getFunctionInstance()) << '\t' << static_cast(name.getFunction()) << '\t' << static_cast(name.getVehicleSystem()) << '\t' << static_cast(name.getVehicleSystemInstance()) << '\t' << static_cast(name.getIndustryGroup()) << '\t' << (name.isArbitraryAddressCapable() ? "Address Capable" : "No address capable") << std::endl; names.insert(name); } } } bool onTimeout() { return true; } ================================================ FILE: BinUtils/j1939Decoder/.cproject ================================================ ================================================ FILE: BinUtils/j1939Decoder/.settings/org.eclipse.cdt.codan.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.cdt.codan.checkers.errnoreturn=Warning org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.checkers.errreturnvalue=Error org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.checkers.noreturn=Error org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=-Error org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=-Error org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=-Error org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidArguments=-Error org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=-Error org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.OverloadProblem=-Error org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} useParentScope=false ================================================ FILE: BinUtils/j1939Decoder/.settings/org.eclipse.cdt.core.prefs ================================================ eclipse.preferences.version=1 environment/project/cdt.managedbuild.config.gnu.exe.debug.1861074874/append=true environment/project/cdt.managedbuild.config.gnu.exe.debug.1861074874/appendContributed=true ================================================ FILE: BinUtils/j1939Decoder/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(j1939Decoder) add_executable(j1939Decoder src/j1939Decoder.cpp ) target_include_directories(j1939Decoder PUBLIC include ${J1939_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) target_link_libraries(j1939Decoder PUBLIC J1939 ) install (TARGETS j1939Decoder DESTINATION bin) ================================================ FILE: BinUtils/j1939Decoder/README.md ================================================ To decode frames. For example: ```bash ./j1939Decoder --id 00febffe --data "00 c4 00 00 00 00 00 00" Name PGN Source Address PDU format Priority EBC2 FEBF FE 2 0 SPN 904: Front Axe Speed -> Value: 196 kph SPN 905: Relative Speed; Front Axle, Left Wheel -> Value: -7 kph SPN 906: Relative Speed; Front Axle, Right Wheel -> Value: -7 kph SPN 907: Relative Speed; Rear Axle 1, Left Wheel -> Value: -7 kph SPN 908: Relative Speed; Rear Axle 1, Right Wheel -> Value: -7 kph SPN 909: Relative Speed; Rear Axle 2, Left Wheel -> Value: -7 kph SPN 910: Relative Speed; Rear Axle 2, Right Wheel -> Value: -7 kph ``` ================================================ FILE: BinUtils/j1939Decoder/src/j1939Decoder.cpp ================================================ //============================================================================ // Name : j1939Encoder.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include #include #include #include #include #include #include #include #include #include #include //J1939 libraries #include #include #include #include #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif using namespace J1939; std::basic_string decodeData(const std::string &data) { //At this point we have the options, we build regular expressions to validate them //Data regex will match the format XXXXXXXXXXXXXXXX where X is an hexadecimal digit. std::string dataRegex("^(\\s{0,1}([0-9a-fA-F][0-9a-fA-F]))+$"); regex_t regex; int retVal; retVal = regcomp(®ex, dataRegex.c_str(), REG_EXTENDED); if (retVal) { std::cerr << "Problem compiling reg expression for data" << std::endl; exit(2); } retVal = regexec(®ex, data.c_str(), 0, NULL, 0); if (retVal == REG_NOMATCH) { std::cerr << "The introduced data has wrong format..." << std::endl; exit(3); } else if(retVal){ std::cerr << "Problem executing reg expression for ID"<< std::endl; exit(2); } regfree(®ex); std::stringstream dataStream; dataStream << std::hex << data; //String where to store the data of the frame ready to be passed to the frame factory with the correct format. std::basic_string formattedData; std::string token; do { dataStream >> std::ws >> std::setw(2) >> token; formattedData.push_back(static_cast(std::stoul(token, nullptr, 16))); } while(!(dataStream.rdstate() & std::ios_base::eofbit)); //End of file return formattedData; } u32 decodeID(const std::string& id) { //ID regex will match the format XXXXXXXX where X is an hexadecimal digit. The id in extended format in CAN has a length of 29 bits. It must be at least 8 digits. std::string idRegex("^[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]" "[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]$"); regex_t regex; int retVal; retVal = regcomp(®ex, idRegex.c_str(), 0); if (retVal) { std::cerr << "Problem compiling reg expression for ID"<< std::endl; exit(2); } retVal = regexec(®ex, id.c_str(), 0, NULL, 0); if (retVal == REG_NOMATCH) { std::cerr << "The introduced ID has wrong format..." << std::endl; exit(3); } else if(retVal){ std::cerr << "Problem executing reg expression for ID" << std::endl; exit(2); } regfree(®ex); //Convert the introduced string to a number to be interpreted by the frame factory when using it. u32 formattedId = std::stoul(id, nullptr, 16); return formattedId; } int main (int argc, char **argv) { int c; //In case of BAM decoding BamReassembler reassembler; std::string id, data; static struct option long_options[] = { {"id", required_argument, NULL, 'i'}, {"data", required_argument, NULL, 'd'}, {NULL, 0, NULL, 0} }; while (1) { c = getopt_long (argc, argv, "i:d:", long_options, NULL); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'i': id = optarg; break; case 'd': data = optarg; break; } } if(id.empty() || data.empty()) { std::cerr << "Id or data of J1939 frame not specified"; exit(1); } //At this point we have the options, we build regular expressions to validate them u32 formattedId = decodeID(id); std::basic_string formattedData = decodeData(data); if(!J1939Factory::getInstance().registerDatabaseFrames(DATABASE_PATH)) { std::cerr << "Database not found in " << DATABASE_PATH << std::endl; exit(4); } //The rest is easy std::unique_ptr frame; try { frame = J1939Factory::getInstance().getJ1939Frame(formattedId, formattedData.c_str(), formattedData.size()); } catch(J1939DecodeException& e) { std::cerr << "Error decoding frame: " << e.getMessage() << std::endl; exit(6); } if(!frame) { std::cerr << "Frame not identified" << std::endl; exit(5); } //Check if it is part of the BAM protocol if(reassembler.toBeHandled(*frame)) { std::cout << "Bam frame detected... Waiting for the rest of frames to be received..." << std::endl; //First frame should be CM frame, otherwise, it is likely that we remain in the loop forever reassembler.handleFrame(*frame); while(!reassembler.reassembledFramesPending()) { std::cout << "Id: " << std::endl; std::getline(std::cin, id); formattedId = decodeID(id); std::cout << "Data: " << std::endl; std::getline(std::cin, data); formattedData = decodeData(data); try { frame = J1939Factory::getInstance().getJ1939Frame(formattedId, formattedData.c_str(), formattedData.size()); if(frame) { reassembler.handleFrame(*frame); } else { std::cerr << "Frame not identified" << std::endl; exit(5); } } catch(J1939DecodeException& e) { std::cerr << "Error decoding frame: " << e.getMessage() << std::endl; exit(6); } } frame = reassembler.dequeueReassembledFrame(); //Check again if the frame is well decoded if(!frame) { std::cerr << "Frame not identified" << std::endl; exit(5); } } std::cout << frame->toString(); exit (0); } ================================================ FILE: BinUtils/j1939Sender/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(j1939Sender) add_executable(j1939Sender src/j1939Sender.cpp ) target_include_directories(j1939Sender PUBLIC include ${J1939_SOURCE_DIR}/include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) target_link_libraries(j1939Sender PUBLIC J1939 Can rt -rdynamic ) install (TARGETS j1939Sender DESTINATION bin) ================================================ FILE: BinUtils/j1939Sender/README.md ================================================ A CLI utility to create frames, configure them and send them through the CAN bus. ```bash j1939Sender Version: 1.0 Interfaces: can0 Initialized: can0 j1939Sender> j1939Sender> create frame name: my_frame title: CCVS Frame correctly created j1939Sender> create frame name: my_frame1 pgn: FEF1 Frame correctly created j1939Sender> list frames my_frame: 00fef1fe ff ff ff ff ff ff ff ff my_frame1: 00fef1fe ff ff ff ff ff ff ff ff set frame my_frame period: 100 set frame my_frame source: 45 set frame my_frame spn: 84 value: 15 print frame my_frame send frame my_frame interface: can0 ``` # Commands ## Create frame ```bash create frame name: title: create frame name: pgn: ``` Example: ```bash create frame name: my_frame title: CCVS create frame name: my_frame1 pgn: FEF1 ``` ## List frames ```bash list frames ``` ## Set frame values ```bash set frame [period: ] [source: ] [source: ] [priority: ] [spn: value: ] [spn: value: ]... ``` Example: ```bash set frame my_frame period: 100 source: 2C set frame my_frame spn: 84 value: 15 priority: 4 ``` ## Print frame ```bash print frame ``` Example: ```bash print frame my_frame ``` ## List interfaces ```bash list interfaces ``` ## Send frame ```bash send frame interface: ``` Example: ```bash send frame my_frame interface: can0 ``` ## Unsend frame ```bash unsend frame [interface: ] ``` Example: ```bash unsend frame my_frame unsend frame my_frame interface: can0 ``` ## List TTS ```bash list tts all list tts number: ``` Example: ```bash list tts number: 18 ``` ## Set TTS ```bash set tts number: status: ``` Example: ```bash set tts number: 3 status: 2 ``` ## Send TTS ```bash send tts interface: period: ``` Example: ```bash send tts interface: can0 period: 4000 ``` ## Add dtc ```bash add dtc oc: fmi: spn: ``` Example: ```bash create frame name: dm1 title: DM1 add dtc dm1 oc: 2 fmi: 6 spn: 84 ``` ## Set dtc ```bash set dtc oc: fmi: spn: ``` Example: ```bash set dtc dm1 0 oc: 3 fmi: 5 spn: 84 ``` ## Delete dtc ```bash delete dtc ``` Example: ```bash delete dtc dm1 0 ``` ## Quit ```bash quit ``` ================================================ FILE: BinUtils/j1939Sender/src/j1939Sender.cpp ================================================ //============================================================================ // Name : j1939Sender.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include #include #include #include #include #include #include #include #include #include #include //J1939 includes #include #include #include #include #include #include #include #include #include //CAN includes #include #define VERSION_STR "1.0" //Bitrate for J1939 protocol #define BAUD_250K 250000 #define SENDER_PROMPT "j1939Sender> " #define CREATE_TOKEN "create" #define QUIT_TOKEN "quit" #define SEND_TOKEN "send" #define UNSEND_TOKEN "unsend" #define EXEC_TOKEN "exec" #define LIST_TOKEN "list" #define PRINT_TOKEN "print" #define SET_TOKEN "set" #define FRAME_TOKEN "frame" #define FRAMES_TOKEN "frames" #define COMMANDS_TOKEN "commands" #define INTERFACE_TOKEN "interface" #define INTERFACES_TOKEN "interfaces" #define ADD_TOKEN "add" #define CHANGE_TOKEN "change" #define DELETE_TOKEN "delete" #define DTC_TOKEN "dtc" #define OC_TOKEN "oc" #define FMI_TOKEN "fmi" #define NAME_TOKEN "name" #define PGN_TOKEN "pgn" #define TITLE_TOKEN "title" #define PRIORITY_TOKEN "priority" #define SOURCE_TOKEN "source" #define PERIOD_TOKEN "period" #define SPN_TOKEN "spn" #define VALUE_TOKEN "value" #define VIN_TOKEN "vin" #define TTS_TOKEN "tts" #define TTS_NUMBER "number" #define TTS_STATUS "status" #define TTS_ALL "all" #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif typedef std::function ParamParserFunc; using namespace Can; using namespace J1939; class CommandHelper { public: typedef void (*CmdFunc)(void); typedef void (*CmdFuncWithArgs)(std::list); private: std::string mCommand; CmdFunc mCmdFunc; CmdFuncWithArgs mCmdFuncWithArgs; std::vector mSubCommands; public: CommandHelper(const std::string& command = "") : mCommand(command), mCmdFunc(nullptr), mCmdFuncWithArgs(nullptr) {} CommandHelper(const std::string& command, CmdFunc func) : mCommand(command), mCmdFunc(func), mCmdFuncWithArgs(nullptr) {} CommandHelper(const std::string& command, CmdFuncWithArgs func) : mCommand(command), mCmdFunc(nullptr), mCmdFuncWithArgs(func) {} CmdFunc getCmdFunc() const { return mCmdFunc; } CmdFuncWithArgs getCmdFuncWithArgs() const { return mCmdFuncWithArgs; } const std::string& getCommand() const { return mCommand; } CommandHelper& addSubCommand(const CommandHelper& command) { mSubCommands.push_back(command); return *this; } const std::vector& getSubCommands() const { return mSubCommands; } }; //Command that will hold the other commands CommandHelper baseCommand; //Map of the created frames to be sent to the CAN interface std::map framesToSend; //Map to specify the period for the different frames (in millis) std::map framePeriods; //Take all the tokens from a line (separated by spaces) and introduces them in the list std::list splitTokens(std::string); //TTS std::vector fms1Frames; u32 ttsPeriod; bool silent; void registerCommands(); const CommandHelper& findSubCommand(const CommandHelper&, std::list&); std::list getSubCommandNames(const CommandHelper&); void parseLine(const std::string& line); //Functions to interpret the different commands void parseSetFrameCommand(std::list arguments); void parseSetTTSCommand(std::list arguments); void parseListCommandsCommand(); void parseListTTSCommand(std::list arguments); void processCommandParameters(std::list arguments, ParamParserFunc func); void parsePrintFrameCommand(std::list arguments); void parseQuitCommand(); void parseCreateFrameCommand(std::list arguments); void parseListFramesCommand(); void parseListInterfacesCommand(); void parseSendFrameCommand(std::list arguments); void parseSendTTSCommand(std::list arguments); void parseUnsendTTSCommand(); void parseExecCommand(std::list arguments); void parseUnsendFrameCommand(std::list arguments); void parseAddDtcCommand(std::list arguments); void parseSetDtcCommand(std::list arguments); void parseDeleteDtcCommand(std::list arguments); bool parseDtcCommand(std::list arguments, DTC& dtc); std::vector ttsFramesToCanFrames(const std::vector& ttsFrames); void execScript(const std::string& file); void uninitializeVariables(); bool parseSetGenericParams(const std::string& name, J1939Frame* frame, const std::string& key, const std::string& value); void sendFrameThroughInterface(const J1939Frame* frame, u32 period, const std::string& interface); void unsendFrameThroughInterface(const J1939Frame* frame, const std::string& interface); bool isFrameSent(const J1939Frame* frame, const std::string& interface); int main(int argc, char **argv) { //Get options int c; std::string file; silent = false; static struct option long_options[] = { {"file", required_argument, NULL, 'f'}, {"silent", no_argument, NULL, 's'}, {NULL, 0, NULL, 0} }; while (1) { c = getopt_long (argc, argv, "f:s:", long_options, NULL); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'f': file = optarg; break; case 's': silent = true; break; default: break; } } std::string line; //Print the version if(!silent) { std::cout << "Version: " << VERSION_STR << std::endl; } //Register possible commands to execute by the user registerCommands(); //Load database J1939DataBase ddbb; if(!ddbb.parseJsonFile(DATABASE_PATH)) { switch (ddbb.getLastError()) { case J1939DataBase::ERROR_FILE_NOT_FOUND: std::cerr << "Json database not found in " DATABASE_PATH << std::endl; break; case J1939DataBase::ERROR_JSON_SYNTAX: std::cerr << "Json file has syntax errors" << std::endl; break; case J1939DataBase::ERROR_UNEXPECTED_TOKENS: std::cerr << "Json file has tokens not identified by the application" << std::endl; break; case J1939DataBase::ERROR_OUT_OF_RANGE: std::cerr << "Json file has some values that exceed the permitted ranges" << std::endl; break; case J1939DataBase::ERROR_UNKNOWN_SPN_TYPE: std::cerr << "Json file has undefined type for SPN" << std::endl; break; default: std::cerr << "Something in the database is not working" << std::endl; break; } return -1; } //Register frames in the factory const std::vector& frames = ddbb.getParsedFrames(); for(auto iter = frames.begin(); iter != frames.end(); ++iter) { J1939Factory::getInstance().registerFrame(*iter); } //Generate frames for the TTSs fms1Frames.push_back(FMS1Frame(0)); fms1Frames.push_back(FMS1Frame(1)); fms1Frames.push_back(FMS1Frame(2)); fms1Frames.push_back(FMS1Frame(3)); //Determine the possible backends std::set ifaces = CanEasy::getCanIfaces(); if(!silent) std::cout << "Interfaces: " << std::endl; for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { if(!silent) std::cout << *iter << " "; } if(!silent) std::cout << std::endl; CanEasy::initialize(BAUD_250K); ifaces = CanEasy::getInitializedCanIfaces(); if(!silent) std::cout << "Initialized: "; for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { if(!silent) std::cout << *iter << " "; } if(!silent) std::cout << std::endl; //If any file is defined, first execute commands from it if(!file.empty()) { execScript(file); } //Read from standard input if(!silent) std::cout << SENDER_PROMPT; while (std::getline(std::cin, line)) { parseLine(line); if(!silent) std::cout << SENDER_PROMPT; } return 0; } void registerCommands() { baseCommand.addSubCommand( CommandHelper(CREATE_TOKEN).addSubCommand(CommandHelper(FRAME_TOKEN, parseCreateFrameCommand)) ).addSubCommand( CommandHelper(QUIT_TOKEN, parseQuitCommand) ).addSubCommand( CommandHelper(LIST_TOKEN).addSubCommand(CommandHelper(FRAMES_TOKEN, parseListFramesCommand)). addSubCommand(CommandHelper(COMMANDS_TOKEN, parseListCommandsCommand)). addSubCommand(CommandHelper(INTERFACES_TOKEN, parseListInterfacesCommand)). addSubCommand(CommandHelper(TTS_TOKEN, parseListTTSCommand)) ).addSubCommand( CommandHelper(PRINT_TOKEN).addSubCommand(CommandHelper(FRAME_TOKEN, parsePrintFrameCommand)) ).addSubCommand( CommandHelper(SET_TOKEN).addSubCommand(CommandHelper(FRAME_TOKEN, parseSetFrameCommand)). addSubCommand(CommandHelper(TTS_TOKEN, parseSetTTSCommand)). addSubCommand(CommandHelper(DTC_TOKEN, parseSetDtcCommand)) ).addSubCommand( CommandHelper(SEND_TOKEN).addSubCommand(CommandHelper(FRAME_TOKEN, parseSendFrameCommand)). addSubCommand(CommandHelper(TTS_TOKEN, parseSendTTSCommand)) ).addSubCommand( CommandHelper(EXEC_TOKEN, parseExecCommand) ).addSubCommand( CommandHelper(UNSEND_TOKEN).addSubCommand(CommandHelper(FRAME_TOKEN, parseUnsendFrameCommand)). addSubCommand(CommandHelper(TTS_TOKEN, parseUnsendTTSCommand)) ).addSubCommand( CommandHelper(ADD_TOKEN).addSubCommand(CommandHelper(DTC_TOKEN, parseAddDtcCommand)) ).addSubCommand( CommandHelper(DELETE_TOKEN).addSubCommand(CommandHelper(DTC_TOKEN, parseDeleteDtcCommand)) ); } void parseLine(const std::string& line) { std::string command; std::list arguments; if(line.empty()) { return; //Nothing to do } //If there is the character #, omit every character until the end of line (including the # character) size_t found = line.find_first_of('#'); if(found == 0) { return; } std::list tokens = splitTokens(line.substr(0, found)); const CommandHelper& cmd = findSubCommand(baseCommand, tokens); if(cmd.getCmdFuncWithArgs() == nullptr && cmd.getCmdFunc() == nullptr) { std::cerr << "This command does not exist" << std::endl; return; } if(!tokens.empty() && cmd.getCmdFuncWithArgs()) { (cmd.getCmdFuncWithArgs())(tokens); } else if(tokens.empty() && cmd.getCmdFunc()) { (cmd.getCmdFunc())(); } else { std::cerr << "This command does " << (arguments.empty() ? "" : "not ") << "need arguments" << std::endl; } } std::list splitTokens(std::string arguments) { std::list retVal; size_t startArgPos = 0, endArgPos = 0; while(startArgPos != std::string::npos) { endArgPos = arguments.find_first_of(' ', startArgPos); if(endArgPos == std::string::npos) { endArgPos = arguments.size(); } retVal.push_back(arguments.substr(startArgPos, endArgPos - startArgPos)); startArgPos = arguments.find_first_not_of(' ', endArgPos); } return retVal; } //To have some introspection... void parseListCommandsCommand() { std::list commands = getSubCommandNames(baseCommand); for(auto iter = commands.begin(); iter != commands.end(); ++iter) { std::cout << *iter << std::endl; } } void parseCreateFrameCommand(std::list arguments) { std::string name; std::string pgn; std::string title; auto func = [&name, &pgn, &title](const std::string& key, const std::string& value) { if(key == NAME_TOKEN) { name = value; } else if(key == PGN_TOKEN) { pgn = value; } else if(key == TITLE_TOKEN) { title = value; } }; processCommandParameters(arguments, func); if(name.empty()) { std::cerr << "No name defined for this frame" << std::endl; return; } if(pgn.empty() == title.empty()) { std::cerr << "Define either pgn or title of frame" << std::endl; return; } if(framesToSend.find(name) != framesToSend.end()) { std::cerr << "Name already in use..." << std::endl; return; } std::unique_ptr frameToAdd(nullptr); if(!title.empty()) { //Title was specified frameToAdd = J1939Factory::getInstance().getJ1939Frame(title); } if(!pgn.empty()) { //PGN was defined try { u32 pgnNumber = std::stoul(pgn, nullptr, 16); frameToAdd = J1939Factory::getInstance().getJ1939Frame(pgnNumber); } catch (std::invalid_argument& e) { std::cerr << "PGN is not a number..." << std::endl; } } if(frameToAdd.get()) { framesToSend[name] = frameToAdd.release(); std::cout << "Frame correctly created" << std::endl; } else { std::cerr << "Frame not recognized..." << std::endl; } } void parseListFramesCommand() { std::stringstream str; for(auto iter = framesToSend.begin(); iter != framesToSend.end(); ++iter) { J1939Frame* frame = iter->second; size_t size = frame->getDataLength(); std::vector txInterfaces; //Add the given name when the frame was created str << iter->first << ": "; //Encode the frame to raw data u32 id; u8* buff = new u8[size]; try { frame->encode(id, buff, size); //Add identifier to the stream str << std::setfill('0') << std::setw(8) << std::hex << static_cast(id) << " "; //Add the raw data, but before, we format it for(unsigned int i = 0; i < size; ++i) { str << std::setfill('0') << std::setw(2) << std::hex << static_cast(buff[i]) << " "; } //Check if the frame is being sent through an interface const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { if(isFrameSent(frame, *iter)) { txInterfaces.push_back(*iter); } } } catch (J1939EncodeException& e) { std::cerr << e.getMessage() << std::endl; } //Print period if defined auto periodIter = framePeriods.find(iter->first); if(periodIter != framePeriods.end()) { str << " Period: " << std::dec << periodIter->second << " ms"; } //Print if the frame is being sent if(!txInterfaces.empty()) { str << " Sent from: "; for(auto iter = txInterfaces.begin(); iter != txInterfaces.end(); ++iter) { str << *iter << " "; } } str << std::endl; delete[] buff; } std::cout << str.str(); } void parseListTTSCommand(std::list arguments) { std::vector ids; u8 number = 0; auto paramParser = [&number](const std::string& key, const std::string& value) { if(key == TTS_NUMBER) { try { u32 ttsNumber = std::stoul(value); if(ttsNumber == (ttsNumber & 0xFF)) { number = static_cast(ttsNumber); } else { std::cerr << "number out of range..." << std::endl; } } catch (std::invalid_argument&) { std::cerr << "number is not a number..." << std::endl; } } }; if(arguments.front() != TTS_ALL) { processCommandParameters(arguments, paramParser); } bool somePrinted = false; for(auto iter = fms1Frames.begin(); iter != fms1Frames.end(); ++iter) { if(number == 0) { somePrinted = true; std::cout << iter->toString(); } else { if(iter->hasTTS(number)) { std::cout << iter->getTTS(number).toString(); somePrinted = true; } } ids.push_back(iter->getIdentifier()); } if(somePrinted) { const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { //Print the interface from which tts are sent std::shared_ptr sender = CanEasy::getSender(*iter); if(sender->isSent(ids)) { std::cout << "Sent from interface " << *iter << std::endl; } } } } void parseUnsendTTSCommand() { std::vector ids; for(auto iter = fms1Frames.begin(); iter != fms1Frames.end(); ++iter) { ids.push_back(iter->getIdentifier()); } const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::shared_ptr sender = CanEasy::getSender(*iter); sender->unSendFrames(ids); } } void parseSendTTSCommand(std::list arguments) { std::string interface; bool periodValid = false; auto paramParser = [&interface, &periodValid](const std::string& key, const std::string& value) { if(key == INTERFACE_TOKEN) { const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { if(*iter == value) { interface = value; return; } } } else if(key == PERIOD_TOKEN) { try { ttsPeriod = std::stoul(value); periodValid = true; } catch (std::invalid_argument& e) { std::cerr << "Period is not a number..." << std::endl; } } }; processCommandParameters(arguments, paramParser); if(!periodValid) { std::cerr << "Period not defined..." << std::endl; return; } if(interface.empty()) { std::cerr << "Interface not defined or not initialized..." << std::endl; return; } std::vector frames = ttsFramesToCanFrames(fms1Frames); std::shared_ptr sender = CanEasy::getSender(interface); sender->sendFrames(frames, ttsPeriod); } void parseSetTTSCommand(std::list arguments) { u8 number = 0xFF, status = 0xFF; auto paramParser = [&number, &status](const std::string& key, const std::string& value) { if(key == TTS_NUMBER) { try { u32 ttsNumber = std::stoul(value); if(ttsNumber == (ttsNumber & 0xFF)) { number = static_cast(ttsNumber); } else { std::cerr << "Number out of range..." << std::endl; number = 0xFF; } } catch (std::invalid_argument&) { std::cerr << "Number is not a number..." << std::endl; number = 0xFF; } } else if(key == TTS_STATUS) { try { u32 ttsStatus = std::stoul(value); if(ttsStatus == (ttsStatus & 0xFF)) { status = static_cast(ttsStatus); } else { std::cerr << "Status out of range..." << std::endl; status = 0xFF; } } catch (std::invalid_argument&) { std::cerr << "Status is not a number..." << std::endl; status = 0xFF; } } }; processCommandParameters(arguments, paramParser); if(number != 0xFF && status != 0xFF) { bool ttsSet = false; for(auto iter = fms1Frames.begin(); iter != fms1Frames.end(); ++iter) { if(iter->hasTTS(number)) { iter->setTTS(number, status); if(!silent) std::cout << "TTS " << static_cast(number) << " set to " << TellTale::getSatusname(status) << std::endl; ttsSet = true; break; } } if(!ttsSet) { std::cerr << "TTS not found"; } else { std::vector ids; for(auto iter = fms1Frames.begin(); iter != fms1Frames.end(); ++iter) { ids.push_back(iter->getIdentifier()); } const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::shared_ptr sender = CanEasy::getSender(*iter); if(sender->isSent(ids)) { std::vector frames = ttsFramesToCanFrames(fms1Frames); sender->sendFrames(frames, ttsPeriod); } } } } } void parsePrintFrameCommand(std::list arguments) { std::string name = arguments.back(); auto frameIter = framesToSend.find(name); if(frameIter != framesToSend.end()) { std::cout << frameIter->second->toString() << std::endl; } else { std::cerr << "Frame not defined..." << std::endl; } } void parseSetFrameCommand(std::list arguments) { std::string name = arguments.front(); arguments.pop_front(); auto frameIter = framesToSend.find(name); if(frameIter == framesToSend.end()) { std::cerr << "Frame not defined..." << std::endl; return; } J1939Frame* frame = frameIter->second; SPN* spn = nullptr; auto paramParser = [name, &frame, &spn](const std::string& key, const std::string& value) { if(key == SPN_TOKEN) { //Setting a SPN if(!frame->isGenericFrame()) { std::cerr << "This frame does not have standard SPNs..." << std::endl; } GenericFrame* genFrame = static_cast(frame); try { u32 spnNumber = std::stoul(value); if(!genFrame->hasSPN(spnNumber)) { std::cerr << "This spn does not belong to the given frame..." << std::endl; return; } spn = genFrame->getSPN(spnNumber); } catch (std::invalid_argument& e) { std::cerr << "spn is not a number..." << std::endl; } } else if(key == VALUE_TOKEN) { //The value used to set the SPN if(!spn) { std::cerr << "Not spn to which assign this value..." << std::endl; return; } try { switch(spn->getType()) { case SPN::SPN_NUMERIC: { double valueNumber = std::stod(value); SPNNumeric* spnNum = static_cast(spn); if(spnNum->setFormattedValue(valueNumber)) { std::cout << "Spn " << spn->getSpnNumber() << " from frame " << frame->getName() << " set to value " << spnNum->getFormattedValue() << std::endl; } } break; case SPN::SPN_STATUS: { u32 valueNumber = std::stoul(value); u8 status = static_cast(valueNumber); if((status & 0xFF) == valueNumber) { SPNStatus* spnStat = static_cast(spn); if(!spnStat->setValue(status)) { std::cerr << "Value out of range" << std::endl; } else { std::cout << "SPN " << spn->getSpnNumber() << " set to (" << valueNumber << ") " << spnStat->getValueDescription(status) << std::endl; } } else { std::cerr << "Value out of range" << std::endl; } } break; case SPN::SPN_STRING: { SPNString* spnStr = static_cast(spn); spnStr->setValue(value); } break; default: break; } } catch (std::invalid_argument& e) { std::cerr << "value is not a number..." << std::endl; } spn = nullptr; } else { //If we are not setting a SPN, maybe we are setting generic parameters of a frame... //Parse generic parameters as the period to send the frames, the priority, the source address, dst address and so on if(!parseSetGenericParams(name, frame, key, value)) { //If this function returns false, it means that the given key is not a generic parameter, but it is particular of certain frame std::cerr << "Unknown parameter..." << std::endl; } } }; processCommandParameters(arguments, paramParser); auto period = framePeriods.find(name); if(period == framePeriods.end()) { return; } u32 id; size_t length = frame->getDataLength(); u8* buff = new u8[length]; frame->encode(id, buff, length); CanFrame canFrame; //J1939 data is always transmitted in extended format canFrame.setExtendedFormat(true); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; //If the frame is being sent, refresh the information to the sender const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::shared_ptr sender = CanEasy::getSender(*iter); if(isFrameSent(frame, *iter)) { sendFrameThroughInterface(frame, period->second, *iter); } } } void processCommandParameters(std::list arguments, ParamParserFunc parserFunc) { while(!arguments.empty()) { std::string key = arguments.front(); arguments.pop_front(); if(key.find(':') == key.size() - 1) { key = key.substr(0, key.size() - 1); } else { std::cerr << "Necessary to add a colon at the end" << std::endl; return; } if(arguments.empty()) { std::cerr << "Incomplete arguments for this command" << std::endl; return; } std::string value = arguments.front(); arguments.pop_front(); parserFunc(key, value); } } void parseQuitCommand() { uninitializeVariables(); std::cout << "Exiting..." << std::endl; exit(0); } const CommandHelper& findSubCommand(const CommandHelper& cmd, std::list& args) { if(args.empty()) { //No more tokens, return the cmd itself return cmd; } std::string arg = args.front(); for(auto iter = cmd.getSubCommands().begin(); iter != cmd.getSubCommands().end(); ++iter) { if(iter->getCommand() == arg) { args.pop_front(); return findSubCommand(*iter, args); } } return cmd; } std::list getSubCommandNames(const CommandHelper& command) { std::list retVal; for(auto iter = command.getSubCommands().begin(); iter != command.getSubCommands().end(); ++iter) { std::list aux = getSubCommandNames(*iter); for(auto name = aux.begin(); name != aux.end(); ++name) { retVal.push_back(command.getCommand() + (command.getCommand().empty() ? "" : " ") + *name); } } if(retVal.empty()) { retVal.push_back(command.getCommand()); } return retVal; } void parseListInterfacesCommand() { const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::cout << *iter << std::endl; } } void parseSendFrameCommand(std::list arguments) { std::string interface; std::string name = arguments.front(); arguments.pop_front(); auto frameIter = framesToSend.find(name); if(frameIter == framesToSend.end()) { std::cerr << "Frame not defined..." << std::endl; return; } const J1939Frame* j1939Frame = frameIter->second; auto func = [&interface](const std::string& key, const std::string& value) { if(key == INTERFACE_TOKEN) { const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iface = ifaces.begin(); iface != ifaces.end(); ++iface) { //Check that the corresponding interface really exists if(*iface == value) { interface = value; return; } } } }; processCommandParameters(arguments, func); if(interface.empty()) { std::cerr << "Interface not defined or not initialized..." << std::endl; return; } //The frame has a periodicity associated? auto period = framePeriods.find(name); if(period == framePeriods.end()) { std::cerr << "Period not defined..." << std::endl; return; } sendFrameThroughInterface(j1939Frame, period->second, interface); } void sendFrameThroughInterface(const J1939Frame* j1939Frame, u32 period, const std::string& interface) { //Send the frame with the configured period std::shared_ptr sender = CanEasy::getSender(interface); size_t length = j1939Frame->getDataLength(); CanFrame canFrame; u32 id; u8* buff; std::string data; //J1939 data is always transmitted in extended format canFrame.setExtendedFormat(true); //If the frame is bigger than 8 bytes, we use BAM if(length > MAX_CAN_DATA_SIZE) { std::vector canFrames; BamFragmenter fragmenter; fragmenter.fragment(*j1939Frame); const TPCMFrame& connFrame = fragmenter.getConnFrame(); length = connFrame.getDataLength(); buff = new u8[length]; connFrame.encode(id, buff, length); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; canFrames.push_back(canFrame); std::vector dataFrames = fragmenter.getDataFrames(); for(auto iter = dataFrames.begin(); iter != dataFrames.end(); ++iter) { length = iter->getDataLength(); buff = new u8[length]; iter->encode(id, buff, length); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; canFrames.push_back(canFrame); } sender->sendFrames(canFrames, period); } else { //Can be sent in one frame buff = new u8[length]; j1939Frame->encode(id, buff, length); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; sender->sendFrame(canFrame, period); } } void unsendFrameThroughInterface(const J1939Frame* j1939Frame, const std::string& interface) { std::vector ids; bool found = false; //If the frame is bigger than 8 bytes, we use BAM if(j1939Frame->getDataLength() > MAX_CAN_DATA_SIZE) { BamFragmenter fragmenter; fragmenter.fragment(*j1939Frame); const TPCMFrame& connFrame = fragmenter.getConnFrame(); ids.push_back(connFrame.getIdentifier()); std::vector dataFrames = fragmenter.getDataFrames(); for(auto iter = dataFrames.begin(); iter != dataFrames.end(); ++iter) { ids.push_back(iter->getIdentifier()); } } else { //Can be sent in one frame ids.push_back(j1939Frame->getIdentifier()); } const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::shared_ptr sender = CanEasy::getSender(*iter); if(interface.empty() || interface == *iter) { sender->unSendFrames(ids); found = true; } } if(!found) { std::cerr << "Frame not sent through the given interface..." << std::endl; } } bool isFrameSent(const J1939Frame* frame, const std::string& interface) { std::vector ids; std::shared_ptr sender = CanEasy::getSender(interface); //If the frame is bigger than 8 bytes, we use BAM if(frame->getDataLength() > MAX_CAN_DATA_SIZE) { BamFragmenter fragmenter; fragmenter.fragment(*frame); const TPCMFrame& connFrame = fragmenter.getConnFrame(); ids.push_back(connFrame.getIdentifier()); std::vector dataFrames = fragmenter.getDataFrames(); for(auto iter = dataFrames.begin(); iter != dataFrames.end(); ++iter) { ids.push_back(iter->getIdentifier()); } } else { //Can be sent in one frame ids.push_back(frame->getIdentifier()); } return sender->isSent(ids); } void execScript(const std::string& file) { std::string line; std::ifstream fileScript; fileScript.open(file); if(fileScript.is_open()) { if(!silent) std::cout << "Executing commands..." << std::endl; while (std::getline(fileScript, line)) { std::cout << (!silent ? SENDER_PROMPT : "") << line << std::endl; //Feedback of read command in the file parseLine(line); } } else { std::cerr << "Could not open the script file..." << std::endl; } } void parseUnsendFrameCommand(std::list arguments) { std::string name = arguments.front(); arguments.pop_front(); auto frameIter = framesToSend.find(name); if(frameIter == framesToSend.end()) { std::cerr << "Frame not defined..." << std::endl; return; } J1939Frame* frame = frameIter->second; std::string interface; auto func = [&interface](const std::string& key, const std::string& value) { if(key == INTERFACE_TOKEN) { interface = value; } }; processCommandParameters(arguments, func); unsendFrameThroughInterface(frame, interface); } void parseExecCommand(std::list arguments) { std::string file = arguments.front(); arguments.pop_front(); if(arguments.empty()) { execScript(file); } else { std::cerr << "Too many arguments..." << std::endl; } } void uninitializeVariables() { //Dealloc allocated frames for(auto iter = framesToSend.begin(); iter != framesToSend.end(); ++iter) { delete iter->second; } //Finalize Can Backends... CanEasy::finalize(); //Deallocate frames J1939Factory::getInstance().releaseInstance(); } bool parseSetGenericParams(const std::string& name, J1939Frame* frame, const std::string& key, const std::string& value) { bool retVal = true; if(key == PRIORITY_TOKEN) { try { u32 priority = std::stoul(value); if(priority == (priority & J1939_PRIORITY_MASK)) { frame->setPriority(static_cast(priority)); } else { std::cerr << "Priority out of range" << std::endl; } } catch (std::invalid_argument& e) { std::cerr << "Priority is not a number..." << std::endl; } } else if(key == PERIOD_TOKEN) { try { u32 period = std::stoul(value); framePeriods[name] = period; } catch (std::invalid_argument& e) { std::cerr << "Period is not a number..." << std::endl; } } else if(key == SOURCE_TOKEN) { try { u32 src = std::stoul(value, nullptr, 16); if(src == (src & J1939_SRC_ADDR_MASK)) { frame->setSrcAddr(static_cast(src)); } else { std::cerr << "Source address out of range" << std::endl; } } catch (std::invalid_argument& e) { std::cerr << "Source address is not a number..." << std::endl; } } else { retVal = false; } return retVal; } std::vector ttsFramesToCanFrames(const std::vector& ttsFrames) { std::vector frames; size_t length; CanFrame canFrame; u32 id; u8* buff; std::string data; for(auto frame = fms1Frames.begin(); frame != fms1Frames.end(); ++frame) { length = frame->getDataLength(); buff = new u8[length]; frame->encode(id, buff, length); canFrame.setId(id); canFrame.setExtendedFormat(true); std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; frames.push_back(canFrame); } return frames; } bool parseDtcCommand(std::list arguments, DTC& dtc) { u32 spn = 0; u8 oc = 0xFF; u8 fmi = 0xFF; auto func = [&spn, &oc, &fmi](const std::string& key, const std::string& value) { if(key == SPN_TOKEN) { try { spn = std::stoul(value); } catch (std::invalid_argument& e) { std::cerr << "Spn is not a number..." << std::endl; } } else if(key == OC_TOKEN) { try { u32 ocNumber = std::stoul(value); if(ocNumber == (ocNumber & DTC_OC_MASK)) { oc = static_cast(ocNumber); } else { std::cerr << "Occurrence Count is out of range..." << std::endl; } } catch (std::invalid_argument& e) { std::cerr << "Occurrence Count is not a number..." << std::endl; } } else if(key == FMI_TOKEN) { try { u32 fmiNumber = std::stoul(value); if(fmiNumber == (fmiNumber & DTC_FMI_MASK)) { fmi = static_cast(fmiNumber); } else { std::cerr << "Failure Mode Identifier is out of range..." << std::endl; } } catch (std::invalid_argument& e) { std::cerr << "Failure Mode Identifier is not a number..." << std::endl; } } }; processCommandParameters(arguments, func); if(spn == 0) { std::cerr << "SPN not set..." << std::endl; return false; } if(oc == 0xFF) { std::cerr << "Occurrence Count not set..." << std::endl; return false; } if(fmi == 0xFF) { std::cerr << "Failure Mode Identifier not set..." << std::endl; return false; } dtc = DTC(spn, fmi, oc); return true; } //Example: add dtc abb spn: 87 oc: 3 fmi: 8 (To add a dtc to frame aaa to the tail). void parseAddDtcCommand(std::list arguments) { std::string name = arguments.front(); arguments.pop_front(); auto frameIter = framesToSend.find(name); if(frameIter == framesToSend.end()) { std::cerr << "Frame not defined..." << std::endl; return; } J1939Frame* frame = frameIter->second; if(frame->getPGN() != DM1_PGN) { std::cerr << "Not a DM1 frame..." << std::endl; return; } DTC dtc; if(parseDtcCommand(arguments, dtc)) { DM1* dm1Frame = static_cast(frame); dm1Frame->addDTC(std::move(dtc)); } } //Example: set dtc aaa 1 spn: 14 oc: 2 fmi: 5 (To set second dtc for frame aaa). void parseSetDtcCommand(std::list arguments) { //Take frame name from arguments std::string name = arguments.front(); arguments.pop_front(); auto frameIter = framesToSend.find(name); if(frameIter == framesToSend.end()) { std::cerr << "Frame not defined..." << std::endl; return; } J1939Frame* frame = frameIter->second; if(frame->getPGN() != DM1_PGN) { std::cerr << "Not a DM1 frame..." << std::endl; return; } //Take dtc postion from arguments std::string posStr = arguments.front(); arguments.pop_front(); size_t pos; try { pos = std::stoul(posStr); } catch (std::invalid_argument& e) { std::cerr << "Position is not a number..." << std::endl; } DTC dtc; if(parseDtcCommand(arguments, dtc)) { DM1* dm1Frame = static_cast(frame); dm1Frame->setDTC(pos, std::move(dtc)); } } //Example: delete dtc abb 0 (To delete first dtc from frame abb). void parseDeleteDtcCommand(std::list arguments) { //Take frame name from arguments std::string name = arguments.front(); arguments.pop_front(); auto frameIter = framesToSend.find(name); if(frameIter == framesToSend.end()) { std::cerr << "Frame not defined..." << std::endl; return; } J1939Frame* frame = frameIter->second; if(frame->getPGN() != DM1_PGN) { std::cerr << "Not a DM1 frame..." << std::endl; return; } //Take dtc postion from arguments std::string posStr = arguments.front(); arguments.pop_front(); size_t pos; try { pos = std::stoul(posStr); } catch (std::invalid_argument& e) { std::cerr << "Position is not a number..." << std::endl; } DM1* dm1Frame = static_cast(frame); dm1Frame->deleteDTC(pos); } ================================================ FILE: BinUtils/j1939Sniffer/.cproject ================================================ ================================================ FILE: BinUtils/j1939Sniffer/.gitignore ================================================ /Debug/ ================================================ FILE: BinUtils/j1939Sniffer/.settings/org.eclipse.cdt.codan.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.cdt.codan.checkers.errnoreturn=Warning org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return\\")",implicit\=>false} org.eclipse.cdt.codan.checkers.errreturnvalue=Error org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused return value\\")"} org.eclipse.cdt.codan.checkers.nocommentinside=-Error org.eclipse.cdt.codan.checkers.nocommentinside.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Nesting comments\\")"} org.eclipse.cdt.codan.checkers.nolinecomment=-Error org.eclipse.cdt.codan.checkers.nolinecomment.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Line comments\\")"} org.eclipse.cdt.codan.checkers.noreturn=Error org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return value\\")",implicit\=>false} org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=-Error org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Abstract class cannot be instantiated\\")"} org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=-Error org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Ambiguous problem\\")"} org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment in condition\\")"} org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment to itself\\")"} org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No break at end of case\\")",no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Catching by reference is recommended\\")",unknown\=>false,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=-Error org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Circular inheritance\\")"} org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class members should be properly initialized\\")",skip\=>true} org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Field cannot be resolved\\")"} org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function cannot be resolved\\")"} org.eclipse.cdt.codan.internal.checkers.InvalidArguments=-Error org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid arguments\\")"} org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=-Error org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid template argument\\")"} org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Label statement not found\\")"} org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Member declaration not found\\")"} org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Method cannot be resolved\\")"} org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Name convention for function\\")",pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class has a virtual method and non-virtual destructor\\")"} org.eclipse.cdt.codan.internal.checkers.OverloadProblem=-Error org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid overload\\")"} org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redeclaration\\")"} org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redefinition\\")"} org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return with parenthesis\\")"} org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Format String Vulnerability\\")"} org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Statement has no effect\\")",macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suggested parenthesis around expression\\")",paramNot\=>false} org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suspicious semicolon\\")",else\=>false,afterelse\=>false} org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Type cannot be resolved\\")"} org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused function declaration\\")",macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused static function\\")",macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused variable declaration in file scope\\")",macro\=>true,exceptions\=>("@(\#)","$Id")} org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol is not resolved\\")"} useParentScope=false ================================================ FILE: BinUtils/j1939Sniffer/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(j1939Sniffer) find_package (Curses) if(CURSES_FOUND) message("-- NCURSES is available") message("-- Project " ${PROJECT_NAME} " will be built") add_executable(j1939Sniffer src/j1939Sniffer.cpp ) target_include_directories(j1939Sniffer PUBLIC include ${J1939_SOURCE_DIR}/include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) target_link_libraries(j1939Sniffer PUBLIC J1939 Can ncurses -rdynamic ) install (TARGETS j1939Sniffer DESTINATION bin) else(CURSES_FOUND) message(WARNING "-- NCURSES is not available") message(WARNING "-- Project " ${PROJECT_NAME} " is skipped...") message(WARNING "-- Try to install ncurses and run cmake again...") endif(CURSES_FOUND) ================================================ FILE: BinUtils/j1939Sniffer/src/j1939Sniffer.cpp ================================================ /* * j1939Sniffer.cpp * * Created on: May 7, 2018 * Author: fernado */ #include #include #include #include #include #include #include #include #include #include #include //Bitrate for J1939 protocol #define BAUD_250K 250000 #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif using namespace Can; using namespace Utils; using namespace J1939; //To reassemble frames fragmented by means of Broadcast Announce Message protocol BamReassembler reassembler; void onRcv(const Can::CanFrame& frame, const TimeStamp&, const std::string& interface, void*); bool onTimeout(); u32 pgn; u32 spn; std::string interface, title; u8 source; int main (int argc, char **argv) { int c; pgn = 0; spn = 0; source = J1939_INVALID_ADDRESS; std::string pgnStr, spnStr, sourceStr; static struct option long_options[] = { {"pgn", required_argument, NULL, 'p'}, {"spn", required_argument, NULL, 's'}, {"interface", required_argument, NULL, 'i'}, {"title", required_argument, NULL, 't'}, {"source", required_argument, NULL, 'o'}, {NULL, 0, NULL, 0} }; while (1) { c = getopt_long (argc, argv, "p:s:i:t:", long_options, NULL); /* Detect the end of the options. */ if (c == -1) break; switch (c) { case 'p': pgnStr = optarg; break; case 's': spnStr = optarg; break; case 'i': interface = optarg; break; case 't': title = optarg; break; case 'o': sourceStr = optarg; break; default: break; } } if(!sourceStr.empty()) { try { u32 src = std::stoul(sourceStr, nullptr, 16); if((src & J1939_SRC_ADDR_MASK) != src) { std::cerr << "The parameter source is too big..." << std::endl; return 2; } source = static_cast(src); } catch (std::invalid_argument& ) { std::cerr << "The parameter source is not a number..." << std::endl; return 2; } } if(pgnStr.empty() && title.empty()) { std::cerr << "The parameter pgn or title are not specified..." << std::endl; return 1; } if(!pgnStr.empty() && !title.empty()) { std::cerr << "Only pgn or title can be specified..." << std::endl; return 1; } if(!pgnStr.empty()) { try { pgn = std::stoul(pgnStr, nullptr, 16); if((pgn & J1939_PGN_MASK) != pgn) { std::cerr << "The parameter pgn is too big..." << std::endl; return 2; } } catch (std::invalid_argument& ) { std::cerr << "The parameter pgn is not a number..." << std::endl; return 2; } } if(!spnStr.empty()) { try { spn = std::stoul(spnStr); } catch (std::invalid_argument& ) { std::cerr << "The parameter spn is not a number..." << std::endl; return 3; } } //Load database J1939DataBase ddbb; if(!ddbb.parseJsonFile(DATABASE_PATH)) { switch (ddbb.getLastError()) { case J1939DataBase::ERROR_FILE_NOT_FOUND: std::cerr << "Json database not found in" DATABASE_PATH << std::endl; break; case J1939DataBase::ERROR_JSON_SYNTAX: std::cerr << "Json file has syntax errors" << std::endl; break; case J1939DataBase::ERROR_UNEXPECTED_TOKENS: std::cerr << "Json file has tokens not identified by the application" << std::endl; break; case J1939DataBase::ERROR_OUT_OF_RANGE: std::cerr << "Json file has some values that exceed the permitted ranges" << std::endl; break; case J1939DataBase::ERROR_UNKNOWN_SPN_TYPE: std::cerr << "Json file has undefined type for SPN" << std::endl; break; default: std::cerr << "Something in the database is not working" << std::endl; break; } return 4; } //Register frames in the factory const std::vector& frames = ddbb.getParsedFrames(); for(auto iter = frames.begin(); iter != frames.end(); ++iter) { J1939Factory::getInstance().registerFrame(*iter); } //Search the frame in the database by the given pgn std::unique_ptr frame; if(pgn != 0) { frame = J1939Factory::getInstance().getJ1939Frame(pgn); } else if(!title.empty()) { frame = J1939Factory::getInstance().getJ1939Frame(title); } if(!frame) { std::cerr << "The frame given by the pgn or title is not defined..." << std::endl; return 5; } if(spn != 0) { //Spn has been defined if(!frame->isGenericFrame()) { std::cerr << "The frame given by the pgn does not have SPNs associated..." << std::endl; return 6; } GenericFrame* genFrame = static_cast(frame.get()); if(!genFrame->hasSPN(spn)) { std::cerr << "The frame given by the pgn does not have the given SPN..." << std::endl; return 7; } } CanEasy::initialize(BAUD_250K, onRcv, onTimeout); CanSniffer& sniffer = CanEasy::getSniffer(); if(sniffer.getNumberOfReceivers() == 0) { std::cerr << "No interface available from to sniffer" << std::endl; return 8; } std::set filters; //Install filter to get only the frame whose pgn is equals to the specified as argument. CanFilter dataFilter((frame->getPGN() << J1939_PGN_OFFSET) | (source != J1939_INVALID_ADDRESS ? (source << J1939_SRC_ADDR_OFFSET) : 0) , (J1939_PGN_MASK << J1939_PGN_OFFSET) | (source != J1939_INVALID_ADDRESS ? (J1939_SRC_ADDR_MASK << J1939_SRC_ADDR_OFFSET) : 0), true, false); //Also install filters to receive frames which are part of BAM transport protocol CanFilter tpcmFilter((TP_CM_PGN << J1939_PGN_OFFSET) | (source != J1939_INVALID_ADDRESS ? (source << J1939_SRC_ADDR_OFFSET) : 0), ((J1939_PDU_FMT_MASK << J1939_PDU_FMT_OFFSET) << J1939_PGN_OFFSET) | (source != J1939_INVALID_ADDRESS ? (J1939_SRC_ADDR_MASK << J1939_SRC_ADDR_OFFSET) : 0), true, false); CanFilter tpdtFilter((TP_DT_PGN << J1939_PGN_OFFSET) | (source != J1939_INVALID_ADDRESS ? (source << J1939_SRC_ADDR_OFFSET) : 0), ((J1939_PDU_FMT_MASK << J1939_PDU_FMT_OFFSET) << J1939_PGN_OFFSET) | (source != J1939_INVALID_ADDRESS ? (J1939_SRC_ADDR_MASK << J1939_SRC_ADDR_OFFSET) : 0), true, false); filters.insert(dataFilter); filters.insert(tpcmFilter); filters.insert(tpdtFilter); sniffer.setFilters(filters); //Initialize ncurses initscr(); sniffer.sniff(1000); endwin(); return 0; } void onRcv(const Can::CanFrame& frame, const TimeStamp&, const std::string& interface, void*) { std::unique_ptr j1939Frame = J1939Factory::getInstance(). getJ1939Frame(frame.getId(), (const u8*)(frame.getData().c_str()), frame.getData().size()); if(!j1939Frame) return; //Frame not registered in the factory. Should never happen if(reassembler.toBeHandled(*j1939Frame)) { //Check if the frame is part of a fragmented frame (BAM protocol) //Actually it is, reassembler will handle it. reassembler.handleFrame(*j1939Frame); if(reassembler.reassembledFramesPending()) { j1939Frame = reassembler.dequeueReassembledFrame(); } else { return; //Frame handled by reassembler but the original frame to be reassembled is not complete. } } //At this point we have either a simple frame or a reassembled frame. //Necesary to check again if pgn is the same even if the filters are supposed to do the work, because maybe, //we have reassembled another frame than the expected one. Check the title also. if(pgn == j1939Frame->getPGN() || title == j1939Frame->getName()) { std::string toPrint; if(spn != 0) { //Defined if(j1939Frame->isGenericFrame()) { GenericFrame* genFrame = static_cast(j1939Frame.get()); if(genFrame->hasSPN(spn)) { SPN* spnToPrint = genFrame->getSPN(spn); toPrint = spnToPrint->toString(); } } } else { //Spn not defined toPrint = j1939Frame->toString(); } clear(); printw(toPrint.c_str()); refresh(); } } bool onTimeout() { return true; } ================================================ FILE: CAN/.gitignore ================================================ /Debug/ ================================================ FILE: CAN/.settings/org.eclipse.cdt.codan.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.cdt.codan.checkers.errnoreturn=Warning org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.checkers.errreturnvalue=Error org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.checkers.noreturn=Error org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=-Error org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=-Error org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=-Error org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidArguments=-Error org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=-Error org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.OverloadProblem=-Error org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} useParentScope=false ================================================ FILE: CAN/Backends/PeakCan/PeakCanChannels.cpp ================================================ /* * PeakCanChannels.cpp * * Created on: Apr 4, 2018 * Author: famez */ #include namespace Can { namespace PeakCan { PeakCanChannels::PeakCanChannels() { //USB channels REGISTER_CHANNEL("usb0", PCAN_USBBUS1); REGISTER_CHANNEL("usb1", PCAN_USBBUS2); REGISTER_CHANNEL("usb2", PCAN_USBBUS3); REGISTER_CHANNEL("usb3", PCAN_USBBUS4); REGISTER_CHANNEL("usb4", PCAN_USBBUS5); REGISTER_CHANNEL("usb5", PCAN_USBBUS6); REGISTER_CHANNEL("usb6", PCAN_USBBUS7); REGISTER_CHANNEL("usb7", PCAN_USBBUS8); REGISTER_CHANNEL("usb8", PCAN_USBBUS9); REGISTER_CHANNEL("usb9", PCAN_USBBUS10); REGISTER_CHANNEL("usb10", PCAN_USBBUS11); REGISTER_CHANNEL("usb11", PCAN_USBBUS12); REGISTER_CHANNEL("usb12", PCAN_USBBUS13); REGISTER_CHANNEL("usb13", PCAN_USBBUS14); REGISTER_CHANNEL("usb14", PCAN_USBBUS15); REGISTER_CHANNEL("usb15", PCAN_USBBUS16); } PeakCanChannels::~PeakCanChannels() { } Channel PeakCanChannels::getChannel(const std::string& name) const { auto iter = mChannels.find(name); if(iter != mChannels.end()) { return iter->second; } return Channel(); } } /* namespace PeakCan */ } /* namespace Can */ ================================================ FILE: CAN/Backends/PeakCan/PeakCanHelper.cpp ================================================ /* * CanHelper.cpp * * Created on: Sep 28, 2017 * Author: famez */ #include #include #include #include #include struct BitrateItem { u32 bitrate; TPCANBaudrate code; }; static const BitrateItem bitratetable[] = { { 5000, PCAN_BAUD_5K }, { 10000, PCAN_BAUD_10K }, { 20000, PCAN_BAUD_20K }, { 33000, PCAN_BAUD_33K }, { 47000, PCAN_BAUD_47K }, { 50000, PCAN_BAUD_50K }, { 83000, PCAN_BAUD_83K }, { 95000, PCAN_BAUD_95K }, { 100000, PCAN_BAUD_100K }, { 125000, PCAN_BAUD_125K }, { 250000, PCAN_BAUD_250K }, { 500000, PCAN_BAUD_500K }, { 800000, PCAN_BAUD_800K }, { 1000000, PCAN_BAUD_1M } }; namespace Can { namespace PeakCan { PeakCanHelper::PeakCanHelper() { if(!PeakCanSymbols::getInstance().areSymbolsLoaded()) { PeakCanSymbols::getInstance().tryLoadSymbols(); } } PeakCanHelper::~PeakCanHelper() { } std::set PeakCanHelper::getCanIfaces() { std::set retVal; if(PeakCanSymbols::getInstance().getLoadingError() || (!PeakCanSymbols::getInstance().areSymbolsLoaded() && !PeakCanSymbols::getInstance().tryLoadSymbols())) { return retVal; //Symbols not available. Cannot keep going... } //Get registered channels std::map channels = PeakCanChannels::getInstance().getChannels(); for(auto channel = channels.begin(); channel != channels.end(); ++channel) { int value = 0; //Callback to PeakCan library to get the condition of channel TPCANStatus status = PeakCanSymbols::getInstance().CAN_GetValue(channel->second.getIndex(), PCAN_CHANNEL_CONDITION, &value, sizeof(value)); if((status == PCAN_ERROR_OK) && (value & PCAN_CHANNEL_PCANVIEW)) { //The channel is available or occupied, we add to the set retVal.insert(channel->second.getName()); } } return retVal; } bool PeakCanHelper::initialize(std::string interface, u32 bitrate) { TPCANBaudrate baudrate = 0; TPCANStatus status; //Are symbols already loaded? if(!PeakCanSymbols::getInstance().areSymbolsLoaded()) { return false; } //Have there been any errors? if(PeakCanSymbols::getInstance().getLoadingError()) { return false; } for(unsigned int i = 0; i < (sizeof(bitratetable) / sizeof(BitrateItem)); ++i) { if(bitratetable[i].bitrate == bitrate) { baudrate = bitratetable[i].code; break; } } if(!baudrate) { //No available baudrate for this number return false; } //Get the interface Channel channel = PeakCanChannels::getInstance().getChannel(interface); if(channel.getName() != interface) { //The interface does not exist return false; } //Try to initialize the channel status = PeakCanSymbols::getInstance().CAN_Initialize(channel.getIndex(), baudrate, 0, 0, 0); if(status != PCAN_ERROR_OK) { return false; } mCurrentHandle = channel.getIndex(); return true; } void PeakCanHelper::finalize() { //uninitialize PCAN device... PeakCanSymbols::getInstance().CAN_Uninitialize(mCurrentHandle); } bool PeakCanHelper::initialized() { int value = 0; //Callback to PeakCan library to get the condition of channel TPCANStatus status = PeakCanSymbols::getInstance().CAN_GetValue(mCurrentHandle, PCAN_CHANNEL_CONDITION, &value, sizeof(value)); return ((status == PCAN_ERROR_OK) && (value & PCAN_CHANNEL_OCCUPIED)); //Channel already initialized? } ICanSender* PeakCanHelper::allocateCanSender() { return new PeakCanSender(mCurrentHandle); } CommonCanReceiver* PeakCanHelper::allocateCanReceiver() { return new PeakCanReceiver(mCurrentHandle); } } /* namespace Can */ } /* namespace Sockets */ ================================================ FILE: CAN/Backends/PeakCan/PeakCanReceiver.cpp ================================================ /* * PeakCanReceiver.cpp * * Created on: May 10, 2018 * Author: fernado */ #include #include using namespace Utils; namespace Can { namespace PeakCan { PeakCanReceiver::PeakCanReceiver(TPCANHandle handle) : mCurrentHandle(handle), mReadFd(-1) { PeakCanSymbols::getInstance().CAN_GetValue(handle, PCAN_RECEIVE_EVENT, //To obtain the file descriptor to watch for events &mReadFd, sizeof(mReadFd)); } PeakCanReceiver::~PeakCanReceiver() { } bool PeakCanReceiver::receive(CanFrame& frame, TimeStamp& timestamp) { TPCANMsg message; TPCANTimestamp tmStamp; TPCANStatus status = PeakCanSymbols::getInstance().CAN_Read(mCurrentHandle, &message, &tmStamp); if (status != PCAN_ERROR_OK || message.LEN > MAX_CAN_DATA_SIZE || (message.MSGTYPE != PCAN_MESSAGE_STANDARD && message.MSGTYPE != PCAN_MESSAGE_EXTENDED)) { return false; } //Set data std::string data; data.append((char*)message.DATA, message.LEN); frame = CanFrame(message.MSGTYPE == PCAN_MESSAGE_EXTENDED, message.ID, data); timestamp = TimeStamp(tmStamp.millis / 1000, (tmStamp.millis % 1000) * 1000 + tmStamp.micros); return true; } int PeakCanReceiver::getFD() { return mReadFd; } } /* namespace PeakCan */ } /* namespace Can */ ================================================ FILE: CAN/Backends/PeakCan/PeakCanSender.cpp ================================================ /* * CanSender.cpp * * Created on: Apr 1, 2018 * Author: famez */ #include #include #include #include #include #include namespace Can { namespace PeakCan { PeakCanSender::PeakCanSender(TPCANHandle handle) : mCurrentHandle(handle) { } PeakCanSender::~PeakCanSender() { } void PeakCanSender::_sendFrame(const CanFrame& frame) const { TPCANStatus status; TPCANMsg frameToSend; //Copy the frame frameToSend.MSGTYPE = (frame.isExtendedFormat() ? PCAN_MESSAGE_EXTENDED : PCAN_MESSAGE_STANDARD); frameToSend.ID = frame.getId(); frameToSend.LEN = frame.getData().size(); memcpy(frameToSend.DATA, frame.getData().c_str(), frameToSend.LEN); status = PeakCanSymbols::getInstance().CAN_Write(mCurrentHandle, &frameToSend); if(status == PCAN_ERROR_OK) { //OK } else { //ERROR } } } /* namespace Sockets */ } /* namespace Can */ ================================================ FILE: CAN/Backends/PeakCan/PeakCanSymbols.cpp ================================================ /* * PeakCanSymbols.cpp * * Created on: Apr 3, 2018 * Author: famez */ #include #include namespace Can { namespace PeakCan { bool PeakCanSymbols::tryLoadSymbols() { void* handle = dlopen(PEAKCAN_LIB, RTLD_LAZY); if (!handle) { mLoadingError = true; mSymbolsLoaded = false; return false; } //Reset errors dlerror(); CAN_Initialize = (CAN_InitializePtr)(dlsym(handle, "CAN_Initialize")); if (dlerror()) goto dll_error; CAN_Uninitialize = (CAN_UninitializePtr)(dlsym(handle, "CAN_Uninitialize")); if (dlerror()) goto dll_error; CAN_Reset = (CAN_ResetPtr)(dlsym(handle, "CAN_Reset")); if (dlerror()) goto dll_error; CAN_Read = (CAN_ReadPtr)(dlsym(handle, "CAN_Read")); if (dlerror()) goto dll_error; CAN_Write = (CAN_WritePtr)(dlsym(handle, "CAN_Write")); if (dlerror()) goto dll_error; CAN_FilterMessages = (CAN_FilterMessagesPtr)(dlsym(handle, "CAN_FilterMessages")); if (dlerror()) goto dll_error; CAN_GetValue = (CAN_GetValuePtr)(dlsym(handle, "CAN_GetValue")); if (dlerror()) goto dll_error; CAN_SetValue = (CAN_SetValuePtr)(dlsym(handle, "CAN_SetValue")); if (dlerror()) goto dll_error; CAN_GetErrorText = (CAN_GetErrorTextPtr)(dlsym(handle, "CAN_GetErrorText")); if (dlerror()) goto dll_error; //Success reading all the symbols mSymbolsLoaded = true; mLoadingError = false; return true; dll_error: dlclose(handle); mSymbolsLoaded = false; mLoadingError = true; return false; } } /* namespace PeakCan */ } /* namespace Can */ ================================================ FILE: CAN/Backends/Sockets/SocketCanHelper.cpp ================================================ /* * CanHelper.cpp * * Created on: Sep 28, 2017 * Author: famez */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SYS_CLASS_NET_PATH "/sys/class/net/" /** * System commands to get/configure the interfaces. Much better than handling netlink sockets... */ #define GET_IFACE_STAT_CMD "ip link show %s up" #define SET_IFACE_UP_CMD "ip link set %s up" #define SET_IFACE_DOWN_CMD "ip link set %s down" #define SET_IFACE_BITRATE_CMD "ip link set %s type can bitrate %d" namespace Can { namespace Sockets { SocketCanHelper::SocketCanHelper() { } SocketCanHelper::~SocketCanHelper() { } bool SocketCanHelper::isVirtual() const { char resolvedPath[PATH_MAX]; std::string path = SYS_CLASS_NET_PATH + mInterface; realpath(path.c_str(), resolvedPath); std::string realPath = resolvedPath; return realPath.find("virtual") != std::string::npos; } std::set SocketCanHelper::getCanIfaces() { struct ifaddrs *addrs,*tmp; int sock; struct sockaddr_can addr; memset(&addr, 0, sizeof(sockaddr_can)); std::set retVal; if((sock = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0) { return retVal; } getifaddrs(&addrs); tmp = addrs; //Iterate over all network interfaces while (tmp) { struct ifreq ifr; strcpy(ifr.ifr_name, tmp->ifa_name); ioctl(sock, SIOCGIFINDEX, &ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; //Only Can Interfaces will succeed to do a proper bind of an address family of type CAN if(bind(sock, (sockaddr *)&addr, sizeof(addr)) >= 0) { retVal.insert(tmp->ifa_name); } tmp = tmp->ifa_next; } freeifaddrs(addrs); close(sock); return retVal; } bool SocketCanHelper::isUp() const { char aux[1024]; snprintf(aux, 1024, GET_IFACE_STAT_CMD, mInterface.c_str()); FILE *fp = popen(aux, "r"); if(fp == NULL) { return false; } if(fgets(aux, sizeof(aux), fp) != NULL) { //if there is some output, interface up, otherwise, interface down. return true; } return false; } bool SocketCanHelper::bringUp() const { char aux[1024]; snprintf(aux, 1024, SET_IFACE_UP_CMD, mInterface.c_str()); int ret = system(aux); return ret == 0; } bool SocketCanHelper::bringDown() const { char aux[1024]; snprintf(aux, 1024, SET_IFACE_DOWN_CMD, mInterface.c_str()); int ret = system(aux); return ret == 0; } bool SocketCanHelper::setBitrate(u32 bitrate) const { char aux[1024]; snprintf(aux, 1024, SET_IFACE_BITRATE_CMD, mInterface.c_str(), bitrate); int ret = system(aux); return ret == 0; } bool SocketCanHelper::initialize(std::string interface, u32 bitrate) { mInterface = interface; if(!isUp()) { //Interface is down? if(!isVirtual()) { //Avoid setting bitrate for virtual interfaces... if(!setBitrate(bitrate)) { return false; //Something went wrong } } //Bring the interface up if(!bringUp()) { return false; //Something went wrong } } //Initialize socket ifreq ifr; sockaddr_can addr; memset(&ifr, 0, sizeof(ifreq)); memset(&addr, 0, sizeof(sockaddr_can)); /* open socket */ mSock = socket(PF_CAN, SOCK_RAW, CAN_RAW); if(mSock < 0) { return false; } const int timestamp_flags = (SOF_TIMESTAMPING_SOFTWARE | \ SOF_TIMESTAMPING_RX_SOFTWARE | \ SOF_TIMESTAMPING_RAW_HARDWARE); //Activate timestamp if (setsockopt(mSock, SOL_SOCKET, SO_TIMESTAMPING, ×tamp_flags, sizeof(timestamp_flags)) < 0) { mTimeStamp = false; //Option not supported by kernel. Timestamp cannot be obtained. } //Bind to socket to start receiving frames from the specified interface addr.can_family = AF_CAN; strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ - 1); if (ioctl(mSock, SIOCGIFINDEX, &ifr) < 0) { close(mSock); mSock = -1; return false; } addr.can_ifindex = ifr.ifr_ifindex; if (bind(mSock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { close(mSock); mSock = -1; return false; } //Avoid receiving the same frame which is sent in the reception buffer int recvOwnMsg = 0; if(setsockopt(mSock, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &recvOwnMsg, sizeof(recvOwnMsg)) < 0) { close(mSock); mSock = -1; return false; } mInterface = interface; return true; } void SocketCanHelper::finalize() { if(mSock != -1) { close(mSock); mSock = -1; } //Down interface bringDown(); } bool SocketCanHelper::initialized() { return isUp(); //If the interface is already up, that means that it has been already initialized by //another application or by ourselves } ICanSender* SocketCanHelper::allocateCanSender() { return new SocketCanSender(mSock); } CommonCanReceiver* SocketCanHelper::allocateCanReceiver() { return new SocketCanReceiver(mSock, mTimeStamp); } } /* namespace Can */ } /* namespace Sockets */ ================================================ FILE: CAN/Backends/Sockets/SocketCanReceiver.cpp ================================================ /* * SocketCanReceiver.cpp * * Created on: May 10, 2018 * Author: fernado */ #include #include #include #include #include #include #include #include #include #include using namespace Utils; namespace Can { namespace Sockets { SocketCanReceiver::SocketCanReceiver(int sock, bool timeStamp) : mSock(sock), mTimeStamp(timeStamp) { iov.iov_base = &frame; msg.msg_name = &addr; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &ctrlmsg; } SocketCanReceiver::~SocketCanReceiver() { } bool SocketCanReceiver::setFilters(std::set filters) { bool retVal; can_filter *rfilters, *filterPtr; if(mSock == -1) return false; //Socket is not open... nothing todo if(filters.empty()) return false; //No filters specified filterPtr = rfilters = new can_filter[filters.size()]; //Allocate filters according to the number of them for(auto iter = filters.begin(); iter != filters.end(); ++iter) { filterPtr->can_id = iter->getId() & CAN_EFF_MASK; filterPtr->can_mask = iter->getMask() & CAN_EFF_MASK; if(iter->filterStdFrame() == iter->filterExtFrame()) { //If none of them are filtered or both are filtered, remove the extended frame flag from the mask filterPtr->can_mask &= ~CAN_EFF_FLAG; } else { filterPtr->can_mask |= CAN_EFF_FLAG; //Set the flag in the mask to check if it is a standard frame or extended frame if(iter->filterExtFrame()) { filterPtr->can_id |= CAN_EFF_FLAG; //If it is extended, we set the EFF flag } else { filterPtr->can_mask &= CAN_SFF_MASK; //If it is standard, we set to 0 the unnecessary bits from the id (only 11 bits) } } ++filterPtr; } retVal = (setsockopt(mSock, SOL_CAN_RAW, CAN_RAW_FILTER, rfilters, filters.size() * sizeof(can_filter)) == 0); delete[] rfilters; //Deallocate filters return retVal; } bool SocketCanReceiver::receive(CanFrame& canFrame, TimeStamp& timestamp) { cmsghdr *cmsg; int nbytes; iov.iov_len = sizeof(frame); msg.msg_namelen = sizeof(addr); msg.msg_controllen = sizeof(ctrlmsg); msg.msg_flags = 0; nbytes = recvmsg(mSock, &msg, 0); if(nbytes >= 0) { if(mTimeStamp) { //Timestamp option is enabled //Extract timestamp for (cmsg = CMSG_FIRSTHDR(&msg); cmsg && (cmsg->cmsg_level == SOL_SOCKET); cmsg = CMSG_NXTHDR(&msg,cmsg)) { if (cmsg->cmsg_type == SO_TIMESTAMP) { timeval *stamp = (timeval*)(CMSG_DATA(cmsg)); timestamp.setMicroSec(stamp->tv_usec); timestamp.setSeconds(stamp->tv_sec); } else if (cmsg->cmsg_type == SO_TIMESTAMPING) { timespec *stamp = (struct timespec *)CMSG_DATA(cmsg); //Take timestamp from software timestamp.setSeconds(stamp[0].tv_sec); timestamp.setMicroSec(stamp[0].tv_nsec/1000); } } } //Copy Frame canFrame.setExtendedFormat(frame.can_id & CAN_EFF_FLAG); canFrame.setId(frame.can_id & ~CAN_EFF_FLAG); std::string data; data.append((char*)(frame.data), frame.len); canFrame.setData(data); } return true; } int SocketCanReceiver::getFD() { return mSock; } } /* namespace Sockets */ } /* namespace Can */ ================================================ FILE: CAN/Backends/Sockets/SocketCanSender.cpp ================================================ /* * CanSender.cpp * * Created on: Apr 1, 2018 * Author: famez */ #include #include #include #include #include #include #include #include #include #include namespace Can { namespace Sockets { SocketCanSender::SocketCanSender(int sock) : mSock(sock) { } SocketCanSender::~SocketCanSender() { finalize(); } void SocketCanSender::_sendFrame(const CanFrame& frame) const { int retval; can_frame frameToSend; memset(&frameToSend, 0, sizeof(can_frame)); frameToSend.can_id = frame.getId(); frameToSend.can_id |= (frame.isExtendedFormat() ? CAN_EFF_FLAG : 0); frameToSend.can_dlc = frame.getData().size(); memcpy(frameToSend.data, frame.getData().c_str(), frameToSend.can_dlc); retval = write(mSock, &frameToSend, sizeof(struct can_frame)); if (retval != sizeof(struct can_frame)) { printf("[SocketCanSender::_sendFrame] retval: %d, error: %s\n", retval, strerror(errno)); } } } /* namespace Sockets */ } /* namespace Can */ ================================================ FILE: CAN/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(Can) add_library(Can SHARED ./CanFrame.cpp ./TRCWriter.cpp ./CanSniffer.cpp ./Backends/Sockets/SocketCanReceiver.cpp ./Backends/Sockets/SocketCanHelper.cpp ./Backends/Sockets/SocketCanSender.cpp ./Backends/PeakCan/PeakCanChannels.cpp ./Backends/PeakCan/PeakCanReceiver.cpp ./Backends/PeakCan/PeakCanSender.cpp ./Backends/PeakCan/PeakCanHelper.cpp ./Backends/PeakCan/PeakCanSymbols.cpp ./TRCReader.cpp ./CommonCanSender.cpp ./ICanHelper.cpp ./CommonCanReceiver.cpp ./CanEasy.cpp ) target_include_directories(Can PUBLIC include ${Common_SOURCE_DIR}/include ) target_link_libraries(Can PUBLIC Common pthread dl ) install (TARGETS Can LIBRARY DESTINATION lib) install(DIRECTORY include/ DESTINATION include) ================================================ FILE: CAN/CanEasy.cpp ================================================ /* * CanEasy.cpp * * Created on: Jan 13, 2019 * Author: famez */ #include namespace Can { std::map > CanEasy::mSenders; CanSniffer CanEasy::mSniffer; std::set CanEasy::mInitializedIfaces; void CanEasy::initialize(u32 bitrate, OnReceiveFramePtr recvCB, OnTimeoutPtr timeoutCB) { //Initialize can const std::map& canHelpers = ICanHelper::createCanHelpers(bitrate); mSniffer.setOnRecv(recvCB); mSniffer.setOnTimeout(timeoutCB); for(auto iter = canHelpers.begin(); iter != canHelpers.end(); ++iter) { ICanSender* sender = iter->second->allocateCanSender(); mSenders[iter->first] = std::shared_ptr(sender); CommonCanReceiver* receiver = iter->second->allocateCanReceiver(); receiver->setInterface(iter->first); mSniffer.addReceiver(receiver); mInitializedIfaces.insert(iter->first); } } void CanEasy::initialize(u32 bitrate) { std::set initializedIfaces; //Initialize can const std::map& canHelpers = ICanHelper::createCanHelpers(bitrate); for(auto iter = canHelpers.begin(); iter != canHelpers.end(); ++iter) { ICanSender* sender = iter->second->allocateCanSender(); mSenders[iter->first] = std::shared_ptr(sender); mInitializedIfaces.insert(iter->first); } } std::set CanEasy::getCanIfaces() { return ICanHelper::getInterfaces(); } std::shared_ptr CanEasy::getSender(const std::string& interface) { auto iter = mSenders.find(interface); if(iter != mSenders.end()) { return iter->second; } return nullptr; } void CanEasy::finalize() { //Auto pointers will free the senders mSenders.clear(); //Free the helpers ICanHelper::deallocateCanHelpers(); } } /* namespace Can */ ================================================ FILE: CAN/CanFrame.cpp ================================================ /* * CanFrame.cpp * * Created on: Oct 15, 2017 * Author: famez */ #include #include #include "CanFrame.h" namespace Can { CanFrame::CanFrame() : mId (0) { } CanFrame::~CanFrame() { } std::string CanFrame::hexDump() const { std::stringstream sstr; for(auto c = mData.begin(); c != mData.end(); ++c) { sstr << std::setfill('0') << std::setw(2) << std::hex << (static_cast(*c) & 0xFF) << " "; } return sstr.str(); } } /* namespace Can */ ================================================ FILE: CAN/CanSniffer.cpp ================================================ /* * CanSniffer.cpp * * Created on: Jun 5, 2018 * Author: fernado */ #include #include using namespace Utils; namespace Can { CanSniffer::CanSniffer(OnReceiveFramePtr recvCB, OnTimeoutPtr timeoutCB, void* data) : mRcvCB(recvCB), mTimeoutCB(timeoutCB), mData(data) { } CanSniffer::~CanSniffer() { for(auto receiver = mReceivers.begin(); receiver != mReceivers.end(); ++receiver) { delete *receiver; } } void CanSniffer::setFilters(std::set filters) { for(auto receiver = mReceivers.begin(); receiver != mReceivers.end(); ++receiver) { (*receiver)->setFilters(filters); } } void CanSniffer::sniff(u32 timeout) const { int result; fd_set rdfs; timeval tv; CanFrame canFrame; TimeStamp timestamp; ASSERT(!mReceivers.empty()); ASSERT(mRcvCB != nullptr); ASSERT(mTimeoutCB != nullptr); do { tv.tv_sec = timeout / 1000; tv.tv_usec = (timeout % 1000) / 1000000; do { int maxFd = -1; FD_ZERO(&rdfs); for(auto receiver = mReceivers.begin(); receiver != mReceivers.end(); ++receiver) { if((*receiver)->getFD() > maxFd){ maxFd = (*receiver)->getFD(); } FD_SET((*receiver)->getFD(), &rdfs); } if(maxFd == -1) return; result = select(maxFd + 1, &rdfs, NULL, NULL, &tv); } while (result == -1 && errno == EINTR); if (result > 0) { for(auto receiver = mReceivers.begin(); receiver != mReceivers.end(); ++receiver) { if (FD_ISSET((*receiver)->getFD(), &rdfs)) { //Frame available from interface if((*receiver)->receive(canFrame, timestamp) && (*receiver)->filter(canFrame.getId())) { (mRcvCB)(canFrame, timestamp, (*receiver)->getInterface(), mData); } } } } else if (result == 0) { //Timeout expired (mTimeoutCB)(); } } while (mRunning); } } /* namespace Can */ ================================================ FILE: CAN/CommonCanReceiver.cpp ================================================ #include namespace Can { bool CommonCanReceiver::setInterface(const std::string& interface) { mInterface = interface; return true; } bool CommonCanReceiver::setFilters(std::set filters) { mFilters = filters; return true; } bool CommonCanReceiver::filter(u32 id) { bool filtered = false; if(mFilters.empty()) return true; //If no filters set, send everything for(auto filter = mFilters.begin(); filter != mFilters.end(); ++filter) { if((filter->getId() & filter->getMask()) == (id & filter->getMask())) { filtered = true; break; } } return filtered; } } ================================================ FILE: CAN/CommonCanSender.cpp ================================================ /* * CommonCanSender.cpp * * Created on: Apr 1, 2018 * Author: famez */ #include #include #include "CommonCanSender.h" namespace Can { void CommonCanSender::CanFrameRing::setFrames(const std::vector& frames) { mFrames = frames; mCurrentpos = 0; } void CommonCanSender::CanFrameRing::pushFrame(const CanFrame& frame) { mFrames.push_back(frame); mCurrentpos = 0; } void CommonCanSender::CanFrameRing::shift() { if(mCurrentpos == mFrames.size()) return; if(++mCurrentpos == mFrames.size()) mCurrentpos = 0; } u32 CommonCanSender::CanFrameRing::getCurrentPeriod() const { if(mFrames.empty()) return 0; u32 period; //Little algorithm to compensate the loss of accuracy when dividing the period by the number of frames if(mCurrentpos + 1 == mFrames.size()) { //Last frame. The period is slightly different to compensate the loss of the decimal part for the other frames which is accumulated period = mPeriod - (mPeriod / mFrames.size()) * (mFrames.size() - 1); } else { //Any other frame. period = mPeriod / mFrames.size(); } return period; } CommonCanSender::CommonCanSender() : mFinished(false) { initialize(); } CommonCanSender::~CommonCanSender() { finalize(); } bool CommonCanSender::initialize() { mThread = std::unique_ptr(new std::thread(&CommonCanSender::run, this)); //Initialize the thread in charge of sending the frames return true; } bool CommonCanSender::finalize() { if(mFinished) return false; //Already finalized mFinished = true; //This makes the thread finish mThread->join(); //Wait for thread to finish doing proper cleaning and claim resources return true; } bool CommonCanSender::sendFrame(CanFrame frame, u32 period, OnSendCallback callback) { std::vector frames; frames.push_back(frame); sendFrames(frames, period, callback); return true; } bool CommonCanSender::sendFrames(std::vector frames, u32 period, OnSendCallback callback) { if(frames.empty()) return false; CanFrameRing ring(period, callback); ring.setFrames(frames); //Check if a frame with the same id is being sent auto found = mFrameRings.end(); //The access is concurrent but only for reading, not necessary to lock the mutex. for(auto iter = mFrameRings.begin(); iter != mFrameRings.end(); ++iter) { if(iter->getFrames().size() != ring.getFrames().size()) continue; found = iter; for(auto iter2 = iter->getFrames().begin(); iter2 != iter->getFrames().end(); ++iter2) { if(iter2->getId() != (ring.getFrames())[iter2 - iter->getFrames().begin()].getId()) { found = mFrameRings.end(); break; } } if(found != mFrameRings.end()) break; } //To modify, lock the mutex mFramesLock.lock(); if(found != mFrameRings.end()) { mFrameRings.erase(found); } mFrameRings.push_back(ring); mFramesLock.unlock(); return true; } void CommonCanSender::unSendFrame(u32 id) { std::vector ids; ids.push_back(id); unSendFrames(ids); } void CommonCanSender::run() { timespec now, current; u32 elapsed; while(!mFinished) { clock_gettime(CLOCK_MONOTONIC, &now); mFramesLock.lock(); for(auto ring = mFrameRings.begin(); ring != mFrameRings.end(); ++ring) { timespec start = ring->getTxTimestamp(); elapsed = abs(Utils::getElapsedMillis(&start, &now)); if((start.tv_nsec == 0 && start.tv_sec == 0) || (elapsed >= ring->getCurrentPeriod())) { CanFrame& toSend = ring->getCurrentFrame(); if(ring->getCallback()) { std::string data; ring->getCallback()(toSend.getId(), data); toSend.setData(data); } _sendFrame(toSend); //Backend in charge of sending the frame ring->shift(); //Move to the next frame current = Utils::addMillis(&start, ring->getCurrentPeriod()); //Calculate the time in which the frame should have been sent elapsed = abs(Utils::getElapsedMillis(¤t, &now)); //Does the calculated time (current) differ from the current one (now) more than the period? if(elapsed < ring->getCurrentPeriod()) { ring->setTxTimestamp(current); //If not, the calculated time will be the timestamp } else { ring->setTxTimestamp(now); //If so, the current time will be the timestamp } } } mFramesLock.unlock(); usleep(1000); } } void CommonCanSender::unSendFrames(const std::vector& ids) { //Check if a frame with the same id is being sent auto found = mFrameRings.end(); //The access is concurrent but only for reading, not necessary to lock the mutex. for(auto iter = mFrameRings.begin(); iter != mFrameRings.end(); ++iter) { if(iter->getFrames().size() != ids.size()) continue; found = iter; for(auto iter2 = iter->getFrames().begin(); iter2 != iter->getFrames().end(); ++iter2) { if(iter2->getId() != ids[iter2 - iter->getFrames().begin()]) { found = mFrameRings.end(); break; } } if(found != mFrameRings.end()) break; } //To modify, lock the mutex mFramesLock.lock(); if(found != mFrameRings.end()) { mFrameRings.erase(found); } mFramesLock.unlock(); } bool CommonCanSender::isSent(const std::vector& ids) { bool found = false; for(auto iter = mFrameRings.begin(); iter != mFrameRings.end(); ++iter) { if(iter->getFrames().size() != ids.size()) continue; found = true; for(auto iter2 = iter->getFrames().begin(); iter2 != iter->getFrames().end(); ++iter2) { if(iter2->getId() != ids[iter2 - iter->getFrames().begin()]) { found = false; break; } } if(found) return true; } return false; } bool CommonCanSender::isSent(u32 id) { std::vector ids; ids.push_back(id); return isSent(ids); } } /* namespace Can */ ================================================ FILE: CAN/ICanHelper.cpp ================================================ /* * ICanHelper.cpp * * Created on: Apr 19, 2018 * Author: famez */ #include #include #include namespace Can { std::map ICanHelper::mHelpers; const std::map& ICanHelper::createCanHelpers(u32 bitrate) { if(mHelpers.empty()) { std::set socketCanIfaces = Sockets::SocketCanHelper::getCanIfaces(); for(auto iter = socketCanIfaces.begin(); iter != socketCanIfaces.end(); ++iter) { ICanHelper* canHelper = new Sockets::SocketCanHelper; if(canHelper->initialize(*iter, bitrate)) { mHelpers[*iter] = canHelper; } else { delete canHelper; } } std::set peakCanIfaces = PeakCan::PeakCanHelper::getCanIfaces(); for(auto iter = peakCanIfaces.begin(); iter != peakCanIfaces.end(); ++iter) { ICanHelper* canHelper = new PeakCan::PeakCanHelper; if(canHelper->initialize(*iter, bitrate)) { mHelpers[*iter] = canHelper; } else { delete canHelper; } } } return mHelpers; } void ICanHelper::deallocateCanHelpers() { for(auto iter = mHelpers.begin(); iter != mHelpers.end(); ++iter) { iter->second->finalize(); delete iter->second; } mHelpers.clear(); } std::set ICanHelper::getInterfaces() { std::set retVal; std::set socketCanIfaces = Sockets::SocketCanHelper::getCanIfaces(); retVal.insert(socketCanIfaces.begin(), socketCanIfaces.end()); std::set peakCanIfaces = PeakCan::PeakCanHelper::getCanIfaces(); retVal.insert(peakCanIfaces.begin(), peakCanIfaces.end()); return retVal; } } ================================================ FILE: CAN/README.md ================================================ # Developing with libCAN ## Sending frames ```c++ #include using namespace Can; void main() { //Initialize Can CanEasy::initialize(250000/*J1939 Baudrate*/); //Get available interfaces std::set interfaces = CanEasy::getCanIfaces(); //Check whether the interface "can0" exists if(interfaces.find("can0") == interfaces.end()) exit(1); //No can0 interface //Get sender from the interface we are insterested in. std::shared_ptr sender = CanEasy::getSender("can0"); if(!sender) exit(2); //If can0 not available or it does not exist, sender is equals to nullPtr. //Create frame. char raw[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF}; std::string data; data.append(raw, sizeof(raw)); u32 id = 0xAD45FD2A; CanFrame frame(true/*Extended format*/, id, data); ////Send frames //First method. Send frame once. sender->sendFrameOnce(canFrame); //Second method. Send frame periodically (period: 500 ms) sender->sendFrame(canFrame, 500/*Period*/); //Stop sending frames sender->unSendFrame(id); //Third method. Send frame periodically (period: 200 ms), with dynamic modifications (callback called every time the frame is about to be sent). sender->sendFrame(canFrame, 200/*Period*/, [](u32 id, std::string& data) { char raw[] = {0x01, 0x23, 0x45}; //Set data data.append(raw, sizeof(raw)); }); } ``` ## Sniffing frames ```c++ #include using namespace Can; //Callbacks. //Called when frames are received void onRcv(const Can::CanFrame& frame, const TimeStamp&, const std::string& interface, void*); //Called when nothing is received within a given timeout bool onTimeout(); void main() { //Initialize Can. Pass the callbacks as arguments. CanEasy::initialize(250000/*J1939 Baudrate*/, onRcv/*Called when frame received*/, onTimeout); //Sniffer class CanSniffer& sniffer = CanEasy::getSniffer(); if(sniffer.getNumberOfReceivers() == 0) { //No interfaces from where to sniff. std::cerr << "No interface available from to sniffer" << std::endl; return 8; } //Blocking call. Loop. Waiting for frames to be received. sniffer.sniff(1000 /*Timeout in millis*/); } //Implement our callback to handle the received frames. void onRcv(const Can::CanFrame& frame, const TimeStamp&, const std::string& interface, void*) { //Get id u32 id = frame.getId(); //Get data std::string data = frame.getData(); //Get format bool ext = frame.isExtendedFormat(); ... } ``` ## Adding filters Filters can be added to out Sniffer object to receive only the frames we are interested in. ```c++ #include using namespace Can; void main() { .... //Sniffer class CanSniffer& sniffer = CanEasy::getSniffer(); std::set filters; //Install filters. CanFilter filter1(0x12ABCD34, 0x00FFFF00/*Only filter 2nd and 3rd byte from the ID*/, true/*Extended format frames are received*/, false/*Base format frames are filtered*/); CanFilter filter2(0x148, 0x7FF/*Only filter 1st 11 bits from the ID*/, false/*Extended format frames are filtered*/, true/*Base format frames are received*/); filters.insert(filter1); filters.insert(filter2); sniffer.setFilters(filters); sniffer.sniff(1000 /*Timeout in millis*/); .... } ``` ### CAN/ This static library is in charge of the transmission and reception of CAN frames and provides an abstraction layer to manage the communication through the CAN bus. It provides: - #### CanFrame Class that represents a frame to be sent / received through the CAN interface. -Two interfaces ICanHelper and ICanSender to be implemented by a lower level layer and provide access to the CAN interfaces and the capability of transmitting frames through a CAN interface. - #### CAN/Backends/ Provides the low level implementation of the modules in charge of communicating with the bus CAN. This modules implement ICanHelper and ICanSender. - ##### CAN/Backends/SocketCan/ Is the low level implementation of [SocketCan](https://www.kernel.org/doc/Documentation/networking/can.txt) Stack. It is necessary that linux kernel is compiled with SocketCan networking stack. - ##### CAN/Backends/PeakCan/ Is the low level implementation of Peak Can propietary stack. It is necessary to install the [Peak Can Linux driver](https://www.peak-system.com/fileadmin/media/linux/files/peak-linux-driver-8.5.1.tar.gz) and the [PCAN basic api](http://www.peak-system.com/produktcd/Develop/PC%20interfaces/Linux/PCAN-Basic_API_for_Linux/PCAN_Basic_Linux-4.2.0.tar.gz). - #### TRCReader Class to read TRC files (only version 1.1). This format is used by the Peak Can programs. See [PEAK CAN TRC File Format ](https://www.peak-system.com/produktcd/Pdf/English/PEAK_CAN_TRC_File_Format.pdf) for detailed infomarion. ================================================ FILE: CAN/TRCReader.cpp ================================================ /* * TRCReader.cpp * * Created on: Oct 24, 2017 * Author: root */ #include #include #include "TRCReader.h" #define TABULATION_CHAR '\t' #define WHITE_SPACE_CHAR ' ' #define END_OF_LINE_CHAR '\n' #define SEMI_COLON_CHAR ';' #define PARENTHESIS_CHAR ')' #define RETURN_CHAR '\r' #define PARSE_STATE_GET_POSITION 0 #define PARSE_STATE_GET_TIME 1 #define PARSE_STATE_GET_TYPE 2 #define PARSE_STATE_GET_ID 3 #define PARSE_STATE_GET_LENGTH 4 #define PARSE_STATE_GET_DATA 5 #define PARSE_STATE_GET_END 6 #define PARSE_STATE_GET_ERROR 7 namespace Can { TRCReader::TRCReader() : mCurrentPos (0), mTotalFrames(0) { } TRCReader::TRCReader(const std::string& path) : mCurrentPos (0), mTotalFrames(0) { loadFile(path); } TRCReader::~TRCReader() { } bool TRCReader::loadFile(const std::string& path) { unloadFile(); mFileName = path; //It is important to open the file in binary mode, as in text mode, //for gcc compiler in windows, the method tellg() returns incorrect size mFileStream.open(path.c_str(), std::ifstream::in | std::ifstream::binary); if(mFileStream.rdstate() & std::ios_base::failbit) { unloadFile(); return false; } if(!checkIntegrity()) { unloadFile(); return false; } reset(); return true; } void TRCReader::unloadFile() { if(mFileStream.is_open()) { mFileStream.close(); } mFileName.clear(); mCurrentPos = 0; mTotalFrames = 0; } void TRCReader::reset() { mFileStream.clear(); mFileStream.seekg(0, mFileStream.beg); mCurrentPos = 0; } bool TRCReader::seekPosition(size_t pos) { if(!isFileLoaded() || pos >= mTotalFrames ) { return false; } if(mCurrentPos == pos) { return true; } mFileStream.clear(); mFileStream.seekg(0, mFileStream.beg); mCurrentPos = 0; bool error, empty; while(!mFileStream.eof()) { readNextLine(error, empty); if(error) { return false; } if(mCurrentPos == pos) { return true; } } return false; } std::pair TRCReader::getLastCanFrame() { return mLastReadFrameTimePair; } void TRCReader::readNextLine(bool& error, bool& empty) { char c; double timeAux; u32 position, id, length; u64 time; std::string type; u32 dataAux; std::string data; error = false; empty = false; u8 state = PARSE_STATE_GET_POSITION; mLastReadFrameTimePair.first = 0; mLastReadFrameTimePair.second.clear(); while (mFileStream.get(c)) { if(c == WHITE_SPACE_CHAR || c == TABULATION_CHAR) continue; if(c == SEMI_COLON_CHAR) { //Skip until end of line mFileStream.ignore(std::numeric_limits::max(), END_OF_LINE_CHAR); continue; } if(c == END_OF_LINE_CHAR) break; if(c == RETURN_CHAR) { mFileStream.get(c); if(c == END_OF_LINE_CHAR) { break; } else { state = PARSE_STATE_GET_ERROR; } } int pos = mFileStream.tellg(); pos -= 1; mFileStream.seekg(pos); switch(state) { case PARSE_STATE_GET_POSITION: mFileStream >> std::dec >> position; mFileStream.get(c); if(c != PARENTHESIS_CHAR) state = PARSE_STATE_GET_ERROR; break; case PARSE_STATE_GET_TIME: mFileStream >> std::dec >> timeAux; timeAux *= 1000; time = (u64)timeAux; break; case PARSE_STATE_GET_TYPE: mFileStream.get(c); type.append(1, c); mFileStream.get(c); type.append(1, c); break; case PARSE_STATE_GET_ID: mFileStream >> std::hex >> id; break; case PARSE_STATE_GET_LENGTH: mFileStream >> length; break; case PARSE_STATE_GET_DATA: mFileStream >> std::hex >> dataAux; if(dataAux <= 0xFF) { data.push_back((char)dataAux); } else { state = PARSE_STATE_GET_ERROR; break; } if(data.size() < length) { --state; } break; case PARSE_STATE_GET_END: default: break; } if(mFileStream.rdstate() == std::ios_base::failbit || mFileStream.rdstate() == std::ios_base::badbit) { //If error parsing stream... mFileStream.clear(); //Clear bit state mFileStream.ignore(std::numeric_limits::max(), END_OF_LINE_CHAR); //Just go until end of line mFileStream.get(c); //Get end of line state = PARSE_STATE_GET_ERROR; } if(state == PARSE_STATE_GET_ERROR) { break; } ++state; } if(state != PARSE_STATE_GET_END && state != PARSE_STATE_GET_POSITION) { error = true; return; } if(state == PARSE_STATE_GET_POSITION) { empty = true; return; } if(data.size() != length) { error = true; return; } mCurrentPos = position - 1; CanFrame frame(true, id, data); mLastReadFrameTimePair.first = time; mLastReadFrameTimePair.second = frame; } bool TRCReader::checkIntegrity() { bool error, empty; while(!mFileStream.eof()) { readNextLine(error, empty); if(error) { return false; } } mTotalFrames = mCurrentPos + 1; return true; } void TRCReader::readNextCanFrame() { bool error, empty; if(!mFileStream.eof()) { readNextLine(error, empty); } } } /* namespace Can */ ================================================ FILE: CAN/TRCWriter.cpp ================================================ /* * TRCWriter.cpp * * Created on: Jun 8, 2018 * Author: fernado */ #include #include #include "TRCWriter.h" #define TRC_FILE_HEADER ";$FILEVERSION=1.1\n;\n" namespace Can { TRCWriter::TRCWriter() : mCounter(0) { } TRCWriter::TRCWriter(const std::string& file) : mCounter(0) { open(file); } TRCWriter::~TRCWriter() { } void TRCWriter::write(const CanFrame& frame, const Utils::TimeStamp& timeStamp) { if(!mFileStream.is_open()) { //File not open throw TRCWriteException(); } std::stringstream sstr; double ts = timeStamp.getSeconds() * 1000 + (double)(timeStamp.getMicroSec()) /1000; size_t size = frame.getData().length(); sstr << ++mCounter << ")"; mFileStream << std::right << std::setw(8) << sstr.str(); sstr.str(""); sstr.clear(); // Clear state flags. sstr << std::fixed << std::setprecision(1) << ts; mFileStream << std::right << std::setw(12) << sstr.str(); sstr.str(""); sstr.clear(); // Clear state flags. mFileStream << std::right << std::setw(4) << "Rx"; sstr << std::setfill('0') << std::setw(8) << std::hex << std::uppercase << frame.getId(); mFileStream << std::right << std::setw(13) << sstr.str(); sstr.str(""); sstr.clear(); // Clear state flags. mFileStream << std::right << std::setw(3) << size; sstr << " "; for(unsigned int i = 0; i < size; ++i) { u8 octet = frame.getData()[i]; sstr << std::setfill('0') << std::setw(2) << std::hex << static_cast(octet) << " "; } mFileStream << sstr.str() << std::endl; } bool TRCWriter::open(const std::string& file) { close(); mFileStream.open(file.c_str(), std::ifstream::out | std::ifstream::trunc); if(mFileStream.is_open()) { mFileStream << TRC_FILE_HEADER; } return mFileStream.is_open(); } void TRCWriter::close() { mCounter = 0; if(mFileStream.is_open()) mFileStream.close(); } } /* namespace Can */ ================================================ FILE: CAN/include/Backends/PeakCan/PeakCanChannels.h ================================================ /* * PeakCanChannels.h * * Created on: Apr 4, 2018 * Author: famez */ #ifndef BACKENDS_PEAKCAN_PEAKCANCHANNELS_H_ #define BACKENDS_PEAKCAN_PEAKCANCHANNELS_H_ #include #include #include #include "PeakCanSymbols.h" #define REGISTER_CHANNEL(x, y) mChannels[x] = Channel(x, y) namespace Can { namespace PeakCan { //Class to represent available channels in PeakCan class Channel{ private: std::string mName; TPCANHandle mIndex; public: Channel() : mIndex(0) {} Channel(const std::string name, TPCANHandle index) : mName(name), mIndex(index) {} ~Channel() {} const std::string& getName() const { return mName; } TPCANHandle getIndex() const { return mIndex; } }; class PeakCanChannels : public ISingleton{ SINGLETON_ACCESS; private: PeakCanChannels(); std::map mChannels; public: virtual ~PeakCanChannels(); const std::map& getChannels() const { return mChannels; } Channel getChannel(const std::string& name) const; }; } /* namespace PeakCan */ } /* namespace Can */ #endif /* BACKENDS_PEAKCAN_PEAKCANCHANNELS_H_ */ ================================================ FILE: CAN/include/Backends/PeakCan/PeakCanHelper.h ================================================ /* * CanHelper.h * * Created on: Sep 28, 2017 * Author: famez */ #ifndef PEAKCANHELPER_H_ #define PEAKCANHELPER_H_ #include #include #include #include namespace Can { namespace PeakCan { class PeakCanHelper : public Can::ICanHelper { private: TPCANHandle mCurrentHandle; public: PeakCanHelper(); virtual ~PeakCanHelper(); static std::set getCanIfaces(); std::string getBackend() override { return "PeakCan"; } ICanSender* allocateCanSender() override; CommonCanReceiver* allocateCanReceiver() override; bool initialize(std::string interface, u32 bitrate) override; void finalize() override; bool initialized(); }; } } /* namespace Can */ #endif /* CANHELPER_H_ */ ================================================ FILE: CAN/include/Backends/PeakCan/PeakCanReceiver.h ================================================ /* * PeakCanReceiver.h * * Created on: May 10, 2018 * Author: fernado */ #ifndef BACKENDS_PEAKCAN_PEAKCANRECEIVER_H_ #define BACKENDS_PEAKCAN_PEAKCANRECEIVER_H_ #include #include namespace Can { namespace PeakCan { class PeakCanReceiver : public CommonCanReceiver { private: TPCANHandle mCurrentHandle; int mReadFd; public: PeakCanReceiver(TPCANHandle handle); virtual ~PeakCanReceiver(); int getFD() override; bool receive(CanFrame&, Utils::TimeStamp&) override; }; } /* namespace PeakCan */ } /* namespace Can */ #endif /* BACKENDS_PEAKCAN_PEAKCANRECEIVER_H_ */ ================================================ FILE: CAN/include/Backends/PeakCan/PeakCanSender.h ================================================ /* * CanSender.h * * Created on: Apr 1, 2018 * Author: famez * Implementation of can sender for the linux sockets layer */ #ifndef BACKENDS_SOCKETS_PEAKCANSENDER_H_ #define BACKENDS_SOCKETS_PEAKCANSENDER_H_ #include #include namespace Can { namespace PeakCan { class PeakCanSender : public CommonCanSender { private: TPCANHandle mCurrentHandle; protected: void _sendFrame(const CanFrame& frame) const override; public: PeakCanSender(TPCANHandle handle); virtual ~PeakCanSender(); }; } /* namespace Sockets */ } /* namespace Can */ #endif /* BACKENDS_SOCKETS_CANSENDER_H_ */ ================================================ FILE: CAN/include/Backends/PeakCan/PeakCanSymbols.h ================================================ /* * PeakCanSymbols.h * * Created on: Apr 3, 2018 * Author: famez */ #ifndef BACKENDS_PEAKCAN_PEAKCANSYMBOLS_H_ #define BACKENDS_PEAKCAN_PEAKCANSYMBOLS_H_ #include #include // Currently defined and supported PCAN channels // #define PCAN_NONEBUS 0x00U // Undefined/default value for a PCAN bus #define PCAN_ISABUS1 0x21U // PCAN-ISA interface, channel 1 #define PCAN_ISABUS2 0x22U // PCAN-ISA interface, channel 2 #define PCAN_ISABUS3 0x23U // PCAN-ISA interface, channel 3 #define PCAN_ISABUS4 0x24U // PCAN-ISA interface, channel 4 #define PCAN_ISABUS5 0x25U // PCAN-ISA interface, channel 5 #define PCAN_ISABUS6 0x26U // PCAN-ISA interface, channel 6 #define PCAN_ISABUS7 0x27U // PCAN-ISA interface, channel 7 #define PCAN_ISABUS8 0x28U // PCAN-ISA interface, channel 8 #define PCAN_DNGBUS1 0x31U // PCAN-Dongle/LPT interface, channel 1 #define PCAN_PCIBUS1 0x41U // PCAN-PCI interface, channel 1 #define PCAN_PCIBUS2 0x42U // PCAN-PCI interface, channel 2 #define PCAN_PCIBUS3 0x43U // PCAN-PCI interface, channel 3 #define PCAN_PCIBUS4 0x44U // PCAN-PCI interface, channel 4 #define PCAN_PCIBUS5 0x45U // PCAN-PCI interface, channel 5 #define PCAN_PCIBUS6 0x46U // PCAN-PCI interface, channel 6 #define PCAN_PCIBUS7 0x47U // PCAN-PCI interface, channel 7 #define PCAN_PCIBUS8 0x48U // PCAN-PCI interface, channel 8 #define PCAN_PCIBUS9 0x409U // PCAN-PCI interface, channel 9 #define PCAN_PCIBUS10 0x40AU // PCAN-PCI interface, channel 10 #define PCAN_PCIBUS11 0x40BU // PCAN-PCI interface, channel 11 #define PCAN_PCIBUS12 0x40CU // PCAN-PCI interface, channel 12 #define PCAN_PCIBUS13 0x40DU // PCAN-PCI interface, channel 13 #define PCAN_PCIBUS14 0x40EU // PCAN-PCI interface, channel 14 #define PCAN_PCIBUS15 0x40FU // PCAN-PCI interface, channel 15 #define PCAN_PCIBUS16 0x410U // PCAN-PCI interface, channel 16 #define PCAN_USBBUS1 0x51U // PCAN-USB interface, channel 1 #define PCAN_USBBUS2 0x52U // PCAN-USB interface, channel 2 #define PCAN_USBBUS3 0x53U // PCAN-USB interface, channel 3 #define PCAN_USBBUS4 0x54U // PCAN-USB interface, channel 4 #define PCAN_USBBUS5 0x55U // PCAN-USB interface, channel 5 #define PCAN_USBBUS6 0x56U // PCAN-USB interface, channel 6 #define PCAN_USBBUS7 0x57U // PCAN-USB interface, channel 7 #define PCAN_USBBUS8 0x58U // PCAN-USB interface, channel 8 #define PCAN_USBBUS9 0x509U // PCAN-USB interface, channel 9 #define PCAN_USBBUS10 0x50AU // PCAN-USB interface, channel 10 #define PCAN_USBBUS11 0x50BU // PCAN-USB interface, channel 11 #define PCAN_USBBUS12 0x50CU // PCAN-USB interface, channel 12 #define PCAN_USBBUS13 0x50DU // PCAN-USB interface, channel 13 #define PCAN_USBBUS14 0x50EU // PCAN-USB interface, channel 14 #define PCAN_USBBUS15 0x50FU // PCAN-USB interface, channel 15 #define PCAN_USBBUS16 0x510U // PCAN-USB interface, channel 16 #define PCAN_PCCBUS1 0x61U // PCAN-PC Card interface, channel 1 #define PCAN_PCCBUS2 0x62U // PCAN-PC Card interface, channel 2 #define PCAN_LANBUS1 0x801U // PCAN-LAN interface, channel 1 #define PCAN_LANBUS2 0x802U // PCAN-LAN interface, channel 2 #define PCAN_LANBUS3 0x803U // PCAN-LAN interface, channel 3 #define PCAN_LANBUS4 0x804U // PCAN-LAN interface, channel 4 #define PCAN_LANBUS5 0x805U // PCAN-LAN interface, channel 5 #define PCAN_LANBUS6 0x806U // PCAN-LAN interface, channel 6 #define PCAN_LANBUS7 0x807U // PCAN-LAN interface, channel 7 #define PCAN_LANBUS8 0x808U // PCAN-LAN interface, channel 8 #define PCAN_LANBUS9 0x809U // PCAN-LAN interface, channel 9 #define PCAN_LANBUS10 0x80AU // PCAN-LAN interface, channel 10 #define PCAN_LANBUS11 0x80BU // PCAN-LAN interface, channel 11 #define PCAN_LANBUS12 0x80CU // PCAN-LAN interface, channel 12 #define PCAN_LANBUS13 0x80DU // PCAN-LAN interface, channel 13 #define PCAN_LANBUS14 0x80EU // PCAN-LAN interface, channel 14 #define PCAN_LANBUS15 0x80FU // PCAN-LAN interface, channel 15 #define PCAN_LANBUS16 0x810U // PCAN-LAN interface, channel 16 // Represent the PCAN error and status codes // #define PCAN_ERROR_OK 0x00000U // No error #define PCAN_ERROR_XMTFULL 0x00001U // Transmit buffer in CAN controller is full #define PCAN_ERROR_OVERRUN 0x00002U // CAN controller was read too late #define PCAN_ERROR_BUSLIGHT 0x00004U // Bus error: an error counter reached the 'light' limit #define PCAN_ERROR_BUSHEAVY 0x00008U // Bus error: an error counter reached the 'heavy' limit #define PCAN_ERROR_BUSWARNING PCAN_ERROR_BUSHEAVY // Bus error: an error counter reached the 'warning' limit #define PCAN_ERROR_BUSPASSIVE 0x40000U // Bus error: the CAN controller is error passive #define PCAN_ERROR_BUSOFF 0x00010U // Bus error: the CAN controller is in bus-off state #define PCAN_ERROR_ANYBUSERR (PCAN_ERROR_BUSWARNING | PCAN_ERROR_BUSLIGHT | PCAN_ERROR_BUSHEAVY | PCAN_ERROR_BUSOFF | PCAN_ERROR_BUSPASSIVE) // Mask for all bus errors #define PCAN_ERROR_QRCVEMPTY 0x00020U // Receive queue is empty #define PCAN_ERROR_QOVERRUN 0x00040U // Receive queue was read too late #define PCAN_ERROR_QXMTFULL 0x00080U // Transmit queue is full #define PCAN_ERROR_REGTEST 0x00100U // Test of the CAN controller hardware registers failed (no hardware found) #define PCAN_ERROR_NODRIVER 0x00200U // Driver not loaded #define PCAN_ERROR_HWINUSE 0x00400U // Hardware already in use by a Net #define PCAN_ERROR_NETINUSE 0x00800U // A Client is already connected to the Net #define PCAN_ERROR_ILLHW 0x01400U // Hardware handle is invalid #define PCAN_ERROR_ILLNET 0x01800U // Net handle is invalid #define PCAN_ERROR_ILLCLIENT 0x01C00U // Client handle is invalid #define PCAN_ERROR_ILLHANDLE (PCAN_ERROR_ILLHW | PCAN_ERROR_ILLNET | PCAN_ERROR_ILLCLIENT) // Mask for all handle errors #define PCAN_ERROR_RESOURCE 0x02000U // Resource (FIFO, Client, timeout) cannot be created #define PCAN_ERROR_ILLPARAMTYPE 0x04000U // Invalid parameter #define PCAN_ERROR_ILLPARAMVAL 0x08000U // Invalid parameter value #define PCAN_ERROR_UNKNOWN 0x10000U // Unknown error #define PCAN_ERROR_ILLDATA 0x20000U // Invalid data, function, or action #define PCAN_ERROR_CAUTION 0x2000000U // An operation was successfully carried out, however, irregularities were registered #define PCAN_ERROR_INITIALIZE 0x4000000U // Channel is not initialized [Value was changed from 0x40000 to 0x4000000] #define PCAN_ERROR_ILLOPERATION 0x8000000U // Invalid operation [Value was changed from 0x80000 to 0x8000000] // PCAN devices // #define PCAN_NONE 0x00U // Undefined, unknown or not selected PCAN device value #define PCAN_PEAKCAN 0x01U // PCAN Non-Plug&Play devices. NOT USED WITHIN PCAN-Basic API #define PCAN_ISA 0x02U // PCAN-ISA, PCAN-PC/104, and PCAN-PC/104-Plus #define PCAN_DNG 0x03U // PCAN-Dongle #define PCAN_PCI 0x04U // PCAN-PCI, PCAN-cPCI, PCAN-miniPCI, and PCAN-PCI Express #define PCAN_USB 0x05U // PCAN-USB and PCAN-USB Pro #define PCAN_PCC 0x06U // PCAN-PC Card #define PCAN_VIRTUAL 0x07U // PCAN Virtual hardware. NOT USED WITHIN PCAN-Basic API #define PCAN_LAN 0x08U // PCAN Gateway devices // PCAN parameters // #define PCAN_DEVICE_NUMBER 0x01U // PCAN-USB device number parameter #define PCAN_5VOLTS_POWER 0x02U // PCAN-PC Card 5-Volt power parameter #define PCAN_RECEIVE_EVENT 0x03U // PCAN receive event handler parameter #define PCAN_MESSAGE_FILTER 0x04U // PCAN message filter parameter #define PCAN_API_VERSION 0x05U // PCAN-Basic API version parameter #define PCAN_CHANNEL_VERSION 0x06U // PCAN device channel version parameter #define PCAN_BUSOFF_AUTORESET 0x07U // PCAN Reset-On-Busoff parameter #define PCAN_LISTEN_ONLY 0x08U // PCAN Listen-Only parameter #define PCAN_LOG_LOCATION 0x09U // Directory path for log files #define PCAN_LOG_STATUS 0x0AU // Debug-Log activation status #define PCAN_LOG_CONFIGURE 0x0BU // Configuration of the debugged information (LOG_FUNCTION_***) #define PCAN_LOG_TEXT 0x0CU // Custom insertion of text into the log file #define PCAN_CHANNEL_CONDITION 0x0DU // Availability status of a PCAN-Channel #define PCAN_HARDWARE_NAME 0x0EU // PCAN hardware name parameter #define PCAN_RECEIVE_STATUS 0x0FU // Message reception status of a PCAN-Channel #define PCAN_CONTROLLER_NUMBER 0x10U // CAN-Controller number of a PCAN-Channel #define PCAN_TRACE_LOCATION 0x11U // Directory path for PCAN trace files #define PCAN_TRACE_STATUS 0x12U // CAN tracing activation status #define PCAN_TRACE_SIZE 0x13U // Configuration of the maximum file size of a CAN trace #define PCAN_TRACE_CONFIGURE 0x14U // Configuration of the trace file storing mode (TRACE_FILE_***) #define PCAN_CHANNEL_IDENTIFYING 0x15U // Physical identification of a USB based PCAN-Channel by blinking its associated LED #define PCAN_CHANNEL_FEATURES 0x16U // Capabilities of a PCAN device (FEATURE_***) #define PCAN_BITRATE_ADAPTING 0x17U // Using of an existing bit rate (PCAN-View connected to a channel) #define PCAN_BITRATE_INFO 0x18U // Configured bit rate as Btr0Btr1 value #define PCAN_BITRATE_INFO_FD 0x19U // Configured bit rate as TPCANBitrateFD string #define PCAN_BUSSPEED_NOMINAL 0x1AU // Configured nominal CAN Bus speed as Bits per seconds #define PCAN_BUSSPEED_DATA 0x1BU // Configured CAN data speed as Bits per seconds #define PCAN_IP_ADDRESS 0x1CU // Remote address of a LAN channel as string in IPv4 format #define PCAN_LAN_SERVICE_STATUS 0x1DU // Status of the Virtual PCAN-Gateway Service #define PCAN_ALLOW_STATUS_FRAMES 0x1EU // Status messages reception status within a PCAN-Channel #define PCAN_ALLOW_RTR_FRAMES 0x1FU // RTR messages reception status within a PCAN-Channel #define PCAN_ALLOW_ERROR_FRAMES 0x20U // Error messages reception status within a PCAN-Channel #define PCAN_INTERFRAME_DELAY 0x21U // Delay, in microseconds, between sending frames #define PCAN_ACCEPTANCE_FILTER_11BIT 0x22U // Filter over code and mask patterns for 11-Bit messages #define PCAN_ACCEPTANCE_FILTER_29BIT 0x23U // Filter over code and mask patterns for 29-Bit messages // PCAN parameter values // #define PCAN_PARAMETER_OFF 0x00U // The PCAN parameter is not set (inactive) #define PCAN_PARAMETER_ON 0x01U // The PCAN parameter is set (active) #define PCAN_FILTER_CLOSE 0x00U // The PCAN filter is closed. No messages will be received #define PCAN_FILTER_OPEN 0x01U // The PCAN filter is fully opened. All messages will be received #define PCAN_FILTER_CUSTOM 0x02U // The PCAN filter is custom configured. Only registered messages will be received #define PCAN_CHANNEL_UNAVAILABLE 0x00U // The PCAN-Channel handle is illegal, or its associated hardware is not available #define PCAN_CHANNEL_AVAILABLE 0x01U // The PCAN-Channel handle is available to be connected (Plug&Play Hardware: it means furthermore that the hardware is plugged-in) #define PCAN_CHANNEL_OCCUPIED 0x02U // The PCAN-Channel handle is valid, and is already being used #define PCAN_CHANNEL_PCANVIEW (PCAN_CHANNEL_AVAILABLE | PCAN_CHANNEL_OCCUPIED) // The PCAN-Channel handle is already being used by a PCAN-View application, but is available to connect #define LOG_FUNCTION_DEFAULT 0x00U // Logs system exceptions / errors #define LOG_FUNCTION_ENTRY 0x01U // Logs the entries to the PCAN-Basic API functions #define LOG_FUNCTION_PARAMETERS 0x02U // Logs the parameters passed to the PCAN-Basic API functions #define LOG_FUNCTION_LEAVE 0x04U // Logs the exits from the PCAN-Basic API functions #define LOG_FUNCTION_WRITE 0x08U // Logs the CAN messages passed to the CAN_Write function #define LOG_FUNCTION_READ 0x10U // Logs the CAN messages received within the CAN_Read function #define LOG_FUNCTION_ALL 0xFFFFU // Logs all possible information within the PCAN-Basic API functions #define TRACE_FILE_SINGLE 0x00U // A single file is written until it size reaches PAN_TRACE_SIZE #define TRACE_FILE_SEGMENTED 0x01U // Traced data is distributed in several files with size PAN_TRACE_SIZE #define TRACE_FILE_DATE 0x02U // Includes the date into the name of the trace file #define TRACE_FILE_TIME 0x04U // Includes the start time into the name of the trace file #define TRACE_FILE_OVERWRITE 0x80U // Causes the overwriting of available traces (same name) #define FEATURE_FD_CAPABLE 0x01U // Device supports flexible data-rate (CAN-FD) #define FEATURE_DELAY_CAPABLE 0x02U // Device supports a delay between sending frames (FPGA based USB devices) #define SERVICE_STATUS_STOPPED 0x01U // The service is not running #define SERVICE_STATUS_RUNNING 0x04U // The service is running // PCAN message types // #define PCAN_MESSAGE_STANDARD 0x00U // The PCAN message is a CAN Standard Frame (11-bit identifier) #define PCAN_MESSAGE_RTR 0x01U // The PCAN message is a CAN Remote-Transfer-Request Frame #define PCAN_MESSAGE_EXTENDED 0x02U // The PCAN message is a CAN Extended Frame (29-bit identifier) #define PCAN_MESSAGE_FD 0x04U // The PCAN message represents a FD frame in terms of CiA Specs #define PCAN_MESSAGE_BRS 0x08U // The PCAN message represents a FD bit rate switch (CAN data at a higher bit rate) #define PCAN_MESSAGE_ESI 0x10U // The PCAN message represents a FD error state indicator(CAN FD transmitter was error active) #define PCAN_MESSAGE_ERRFRAME 0x40U // The PCAN message represents an error frame #define PCAN_MESSAGE_STATUS 0x80U // The PCAN message represents a PCAN status message // Frame Type / Initialization Mode // #define PCAN_MODE_STANDARD PCAN_MESSAGE_STANDARD #define PCAN_MODE_EXTENDED PCAN_MESSAGE_EXTENDED // Baud rate codes = BTR0/BTR1 register values for the CAN controller. // You can define your own Baud rate with the BTROBTR1 register. // Take a look at www.peak-system.com for our free software "BAUDTOOL" // to calculate the BTROBTR1 register for every bit rate and sample point. // #define PCAN_BAUD_1M 0x0014U // 1 MBit/s #define PCAN_BAUD_800K 0x0016U // 800 kBit/s #define PCAN_BAUD_500K 0x001CU // 500 kBit/s #define PCAN_BAUD_250K 0x011CU // 250 kBit/s #define PCAN_BAUD_125K 0x031CU // 125 kBit/s #define PCAN_BAUD_100K 0x432FU // 100 kBit/s #define PCAN_BAUD_95K 0xC34EU // 95,238 kBit/s #define PCAN_BAUD_83K 0x852BU // 83,333 kBit/s #define PCAN_BAUD_50K 0x472FU // 50 kBit/s #define PCAN_BAUD_47K 0x1414U // 47,619 kBit/s #define PCAN_BAUD_33K 0x8B2FU // 33,333 kBit/s #define PCAN_BAUD_20K 0x532FU // 20 kBit/s #define PCAN_BAUD_10K 0x672FU // 10 kBit/s #define PCAN_BAUD_5K 0x7F7FU // 5 kBit/s // Represents the configuration for a CAN bit rate // Note: // * Each parameter and its value must be separated with a '='. // * Each pair of parameter/value must be separated using ','. // // Example: // f_clock=80000000,nom_brp=10,nom_tseg1=5,nom_tseg2=2,nom_sjw=1,data_brp=4,data_tseg1=7,data_tseg2=2,data_sjw=1 // #define PCAN_BR_CLOCK __T("f_clock") #define PCAN_BR_CLOCK_MHZ __T("f_clock_mhz") #define PCAN_BR_NOM_BRP __T("nom_brp") #define PCAN_BR_NOM_TSEG1 __T("nom_tseg1") #define PCAN_BR_NOM_TSEG2 __T("nom_tseg2") #define PCAN_BR_NOM_SJW __T("nom_sjw") #define PCAN_BR_NOM_SAMPLE __T("nom_sam") #define PCAN_BR_DATA_BRP __T("data_brp") #define PCAN_BR_DATA_TSEG1 __T("data_tseg1") #define PCAN_BR_DATA_TSEG2 __T("data_tseg2") #define PCAN_BR_DATA_SJW __T("data_sjw") #define PCAN_BR_DATA_SAMPLE __T("data_ssp_offset") // Type of PCAN (non plug&play) hardware // #define PCAN_TYPE_ISA 0x01U // PCAN-ISA 82C200 #define PCAN_TYPE_ISA_SJA 0x09U // PCAN-ISA SJA1000 #define PCAN_TYPE_ISA_PHYTEC 0x04U // PHYTEC ISA #define PCAN_TYPE_DNG 0x02U // PCAN-Dongle 82C200 #define PCAN_TYPE_DNG_EPP 0x03U // PCAN-Dongle EPP 82C200 #define PCAN_TYPE_DNG_SJA 0x05U // PCAN-Dongle SJA1000 #define PCAN_TYPE_DNG_SJA_EPP 0x06U // PCAN-Dongle EPP SJA1000 #define PEAKCAN_LIB "libpcanbasic.so" //////////////////////////////////////////////////////////// // Type definitions //////////////////////////////////////////////////////////// typedef u16 TPCANHandle; typedef u32 TPCANStatus; typedef u8 TPCANParameter; typedef u8 TPCANDevice; typedef u8 TPCANMessageType; typedef u8 TPCANType; typedef u8 TPCANMode; typedef u16 TPCANBaudrate; // Represents a PCAN message typedef struct tagTPCANMsg { u32 ID; // 11/29-bit message identifier TPCANMessageType MSGTYPE; // Type of the message u8 LEN; // Data Length Code of the message (0..8) u8 DATA[8]; // Data of the message (DATA[0]..DATA[7]) } TPCANMsg; // Represents a timestamp of a received PCAN message // Total Microseconds = micros + 1000 * millis + 0xFFFFFFFF * 1000 * millis_overflow typedef struct tagTPCANTimestamp { u32 millis; // Base-value: milliseconds: 0.. 2^32-1 u16 millis_overflow; // Roll-arounds of millis u16 micros; // Microseconds: 0..999 } TPCANTimestamp; typedef TPCANStatus (*CAN_InitializePtr)(TPCANHandle, TPCANBaudrate, TPCANType, u32, u16); typedef TPCANStatus (*CAN_UninitializePtr) (TPCANHandle); typedef TPCANStatus (*CAN_ResetPtr) (TPCANHandle); typedef TPCANStatus (*CAN_ReadPtr) (TPCANHandle, TPCANMsg *, TPCANTimestamp *); typedef TPCANStatus (*CAN_WritePtr) (TPCANHandle, TPCANMsg *); typedef TPCANStatus (*CAN_FilterMessagesPtr) (TPCANHandle, u32, u32, TPCANMode); typedef TPCANStatus (*CAN_GetValuePtr) (TPCANHandle, TPCANParameter, void *, u32); typedef TPCANStatus (*CAN_SetValuePtr) (TPCANHandle, TPCANParameter, void *, u32); typedef TPCANStatus (*CAN_GetErrorTextPtr) (TPCANStatus, u16, char *); namespace Can { namespace PeakCan { class PeakCanSymbols : public ISingleton { SINGLETON_ACCESS; friend class PeakCanHelper; friend class PeakCanSender; friend class PeakCanReceiver; private: PeakCanSymbols() {} ~PeakCanSymbols() {} //All functions to load as interface of PeakCan library CAN_InitializePtr CAN_Initialize = nullptr; CAN_UninitializePtr CAN_Uninitialize = nullptr; CAN_ResetPtr CAN_Reset = nullptr; CAN_ReadPtr CAN_Read = nullptr; CAN_WritePtr CAN_Write = nullptr; CAN_FilterMessagesPtr CAN_FilterMessages = nullptr; CAN_GetValuePtr CAN_GetValue = nullptr; CAN_SetValuePtr CAN_SetValue = nullptr; CAN_GetErrorTextPtr CAN_GetErrorText = nullptr; //Keep track of the status of the symbols bool mSymbolsLoaded = false; bool areSymbolsLoaded() const { return mSymbolsLoaded; } bool mLoadingError = false; bool getLoadingError() const { return mLoadingError; } //Method to load the symbols bool tryLoadSymbols(); }; } /* namespace PeakCan */ } /* namespace Can */ #endif /* BACKENDS_PEAKCAN_PEAKCANSYMBOLS_H_ */ ================================================ FILE: CAN/include/Backends/Sockets/SocketCanHelper.h ================================================ /* * CanHelper.h * * Created on: Sep 28, 2017 * Author: famez */ #ifndef SOCKETCANHELPER_H_ #define SOCKETCANHELPER_H_ #include #include #include namespace Can { namespace Sockets { class SocketCanHelper : public Can::ICanHelper { private: int mSock = -1; bool mTimeStamp = true; std::string mInterface; bool isUp() const; bool bringUp() const; bool bringDown() const; bool setBitrate(u32 bitrate) const; bool isVirtual() const; public: SocketCanHelper(); virtual ~SocketCanHelper(); static std::set getCanIfaces(); std::string getBackend() override { return "SocketCan"; } ICanSender* allocateCanSender() override; CommonCanReceiver* allocateCanReceiver() override; bool initialize(std::string interface, u32 bitrate) override; void finalize() override; bool initialized() override; }; } /* namespace Can */ } #endif /* CANHELPER_H_ */ ================================================ FILE: CAN/include/Backends/Sockets/SocketCanReceiver.h ================================================ /* * SocketCanReceiver.h * * Created on: May 10, 2018 * Author: fernado */ #ifndef BACKENDS_SOCKETS_SOCKETCANRECEIVER_H_ #define BACKENDS_SOCKETS_SOCKETCANRECEIVER_H_ #include namespace Can { namespace Sockets { class SocketCanReceiver : public CommonCanReceiver { private: /* * An already initialized socket where to send the frames */ int mSock; bool mTimeStamp; iovec iov; msghdr msg; canfd_frame frame; sockaddr_can addr; char ctrlmsg[CMSG_SPACE(sizeof(timeval) + 3*sizeof(timespec) + sizeof(u32))]; public: SocketCanReceiver(int sock, bool timeStamp); virtual ~SocketCanReceiver(); /*ICanReceiver implementation*/ bool setFilters(std::set filters) override; bool filter(u32 id) override { return true; } //Filtering is already done in kernel space bool receive(CanFrame&, Utils::TimeStamp&) override; int getFD() override; }; } /* namespace Sockets */ } /* namespace Can */ #endif /* BACKENDS_SOCKETS_SOCKETCANRECEIVER_H_ */ ================================================ FILE: CAN/include/Backends/Sockets/SocketCanSender.h ================================================ /* * CanSender.h * * Created on: Apr 1, 2018 * Author: famez * Implementation of can sender for the linux sockets layer */ #ifndef BACKENDS_SOCKETS_SOCKETCANSENDER_H_ #define BACKENDS_SOCKETS_SOCKETCANSENDER_H_ #include "../../CommonCanSender.h" namespace Can { namespace Sockets { class SocketCanSender : public CommonCanSender { private: /* * An already initialized socket where to send the frames */ int mSock; protected: void _sendFrame(const CanFrame& frame) const override; public: SocketCanSender(int sock); virtual ~SocketCanSender(); }; } /* namespace Sockets */ } /* namespace Can */ #endif /* BACKENDS_SOCKETS_SOCKETCANSENDER_H_ */ ================================================ FILE: CAN/include/CanEasy.h ================================================ /* * CanEasy.h * * Created on: Jan 13, 2019 * Author: famez */ #ifndef CANEASY_H_ #define CANEASY_H_ #include #include #include #include #include namespace Can { class CanEasy { private: //Backends in charge of sending the corresponding frames static std::map > mSenders; static CanSniffer mSniffer; static std::set mInitializedIfaces; public: /* * To initialize for sending and receiving */ static void initialize(u32 bitrate, OnReceiveFramePtr recvCB, OnTimeoutPtr timeoutCB); /* * To initialize only for sending frames */ static void initialize(u32 bitrate); static std::set getCanIfaces(); static const std::set& getInitializedCanIfaces() { return mInitializedIfaces; } static std::shared_ptr getSender(const std::string& interface); static CanSniffer& getSniffer() { return mSniffer; } static void finalize(); }; } /* namespace Can */ #endif /* CANEASY_H_ */ ================================================ FILE: CAN/include/CanFilter.h ================================================ /* * CanFilter.h * * Created on: Oct 15, 2017 * Author: famez */ #ifndef CANFILTER_H_ #define CANFILTER_H_ #include namespace Can { class CanFilter { private: u32 mId; u32 mMask; bool mStdFrame; bool mExtFrame; public: CanFilter() : mId(0), mMask(0), mStdFrame(false), mExtFrame(false) {} CanFilter(u32 id, u32 mask, bool filterExt, bool filterStd) : mId(id), mMask(mask), mStdFrame(filterStd), mExtFrame(filterExt) {} virtual ~CanFilter() {} u32 getId() const { return mId; } void setId(u32 id) { mId = id; } u32 getMask() const { return mMask; } void setMask(u32 mask) { mMask = mask; } bool filterExtFrame() const { return mExtFrame; } void setExtFrame(bool extFrame) { mExtFrame = extFrame; } bool filterStdFrame() const { return mStdFrame; } void setStdFrame(bool stdFrame) { mStdFrame = stdFrame; } bool operator<(const CanFilter& other) const { if(mId < other.mId) return true; if(mId > other.mId) return false; if(mMask < other.mMask) return true; if(mMask > other.mMask) return false; if(!mStdFrame && other.mStdFrame) return true; if(mStdFrame && !other.mStdFrame) return false; if(!mExtFrame && other.mExtFrame) return true; return false; } }; } /* namespace Can */ #endif /* CANFILTER_H_ */ ================================================ FILE: CAN/include/CanFrame.h ================================================ /* * CanFrame.h * * Created on: Oct 15, 2017 * Author: famez */ #ifndef CANFRAME_H_ #define CANFRAME_H_ #include #include #define MAX_CAN_DATA_SIZE 8 namespace Can { class CanFrame { private: bool mExtendedFormat; u32 mId; std::string mData; public: CanFrame(); CanFrame(bool extFormat, u32 id) : mExtendedFormat(extFormat), mId(id) {} CanFrame(bool extFormat, u32 id, const std::string& data) : mExtendedFormat(extFormat), mId(id) { setData(data); } virtual ~CanFrame(); const std::string& getData() const { return mData; } bool setData(const std::string& data) { if(data.size() > MAX_CAN_DATA_SIZE) return false; mData = data; return true; } u32 getId() const { return mId; } void setId(u32 id) { mId = id; } void clear() { mId = 0; mData.clear(); } bool isExtendedFormat() const { return mExtendedFormat; } void setExtendedFormat(bool extendedFormat) { mExtendedFormat = extendedFormat; } //To show human readable data std::string hexDump() const; }; } /* namespace Can */ #endif /* CANFRAME_H_ */ ================================================ FILE: CAN/include/CanSniffer.h ================================================ /* * CanSniffer.h * * Created on: Jun 5, 2018 * Author: fernado */ #ifndef CANSNIFFER_H_ #define CANSNIFFER_H_ #include #include "CommonCanReceiver.h" typedef void (*OnReceiveFramePtr)(const Can::CanFrame& frame, const Utils::TimeStamp& tStamp, const std::string& interface, void* data); typedef bool (*OnTimeoutPtr)(); namespace Can { class CanSniffer { private: OnReceiveFramePtr mRcvCB = nullptr; OnTimeoutPtr mTimeoutCB = nullptr; void* mData = nullptr; //Data to be passed to the OnReceiveFramePtr callback std::vector mReceivers; bool mRunning = true; public: CanSniffer() {} CanSniffer(OnReceiveFramePtr recvCB, OnTimeoutPtr timeoutCB, void* data = nullptr); CanSniffer(const CanFilter &other) = delete; CanSniffer(CanFilter &&other) = delete; virtual ~CanSniffer(); CanSniffer& operator=(const CanFilter &other) = delete; CanSniffer& operator=(CanFilter &&other) = delete; /* * Add a receiver from where to receive the frames. CanSniffer becomes the owner and will deallocate the receiver. */ void addReceiver(CommonCanReceiver *receiver) { mReceivers.push_back(receiver); } void sniff(u32 timeout) const; void setFilters(std::set filters); int getNumberOfReceivers() const { return mReceivers.size(); } void reset() { mRunning = true; } void finish() { mRunning = false; } void setOnRecv(OnReceiveFramePtr recvCB) { mRcvCB = recvCB; } void setOnTimeout(OnTimeoutPtr timeoutCB) { mTimeoutCB = timeoutCB; } void setData(void* data) { mData = data; } }; } /* namespace Can */ #endif /* CANSNIFFER_H_ */ ================================================ FILE: CAN/include/CommonCanReceiver.h ================================================ /* * ICanReceiver.h * * Created on: May 7, 2018 * Author: fernado */ #ifndef COMMONCANRECEIVER_H_ #define COMMONCANRECEIVER_H_ #include #include #include #include namespace Can { class CommonCanReceiver { private: std::set mFilters; std::string mInterface; public: CommonCanReceiver() {} virtual ~CommonCanReceiver() {} /* * Initializes the receiver to be used with the specified interface */ bool setInterface(const std::string& interface); /* * There is the default implementation which is based in a check in user space, but there are specific implementations that let delegate the work to kernel space */ virtual bool setFilters(std::set filters); virtual int getFD() = 0; virtual bool receive(CanFrame&, Utils::TimeStamp&) = 0; virtual bool filter(u32 id); const std::string& getInterface() const { return mInterface; } }; } /* namespace Can */ #endif /* COMMONCANRECEIVER_H_ */ ================================================ FILE: CAN/include/CommonCanSender.h ================================================ /* * CommonCanSender.h * * Created on: Apr 1, 2018 * Author: famez * Implementation of can sender for the linux sockets layer */ #ifndef BACKENDS_SOCKETS_COMMONCANSENDER_H_ #define BACKENDS_SOCKETS_COMMONCANSENDER_H_ #include #include #include #include #include #include namespace Can { class CommonCanSender : public ICanSender { private: class CanFrameRing { private: std::vector mFrames; timespec mTxTimestamp; u32 mPeriod; size_t mCurrentpos; OnSendCallback mCallback; public: CanFrameRing(u32 period, OnSendCallback callback = OnSendCallback()) : mPeriod(period), mCurrentpos(0), mCallback(callback) { mTxTimestamp = {0, 0}; } ~CanFrameRing() {} CanFrameRing(const CanFrameRing& other) = default; CanFrameRing& operator=(const CanFrameRing& other) = delete; CanFrameRing(CanFrameRing&& other) = default; CanFrameRing& operator=(CanFrameRing&& other) = default; void setTxTimestamp(timespec timeStamp) { mTxTimestamp = timeStamp; } timespec getTxTimestamp() const { return mTxTimestamp; } void pushFrame(const CanFrame& frame); void setFrames(const std::vector&); void shift(); CanFrame& getCurrentFrame() { return mFrames[mCurrentpos]; } u32 getCurrentPeriod() const; const std::vector& getFrames() const { return mFrames; } const OnSendCallback& getCallback() { return mCallback; } }; mutable std::mutex mFramesLock; std::vector mFrameRings; bool mFinished; std::unique_ptr mThread = nullptr; protected: virtual void _sendFrame(const CanFrame& frame) const = 0; public: CommonCanSender(); virtual ~CommonCanSender(); //ICanSender implementation bool initialize(); bool finalize(); bool sendFrame(CanFrame frame, u32 period, OnSendCallback callback = OnSendCallback()); bool sendFrames(std::vector frames, u32 period, OnSendCallback callback = OnSendCallback()); /* * Sends the frame given as argument to the CAN network only once. */ void sendFrameOnce(const CanFrame& frame) override { _sendFrame(frame); } void unSendFrame(u32 id); void unSendFrames(const std::vector& ids); bool isSent(const std::vector& ids); bool isSent(u32 id); void run(); }; } /* namespace Can */ #endif /* BACKENDS_SOCKETS_CANSENDER_H_ */ ================================================ FILE: CAN/include/ICanHelper.h ================================================ /* * CanHelper.h * * Created on: Sep 28, 2017 * Author: famez */ #ifndef ICANHELPER_H_ #define ICANHELPER_H_ #include #include #include #include namespace Can { class ICanHelper { private: static std::map mHelpers; public: ICanHelper() {} virtual ~ICanHelper() {} static std::set getInterfaces(); virtual std::string getBackend() = 0; virtual bool initialize(std::string interface, u32 bitrate) = 0; virtual void finalize() = 0; /* * Determines if the corresponding interface is correctly initialized */ virtual bool initialized() = 0; /* * Allocates a CanSender, the caller is in charge of the deallocation */ virtual ICanSender* allocateCanSender() = 0; /* * Allocates a CanReceiver, the caller is in charge of the deallocation */ virtual CommonCanReceiver* allocateCanReceiver() = 0; /* * Returns a set with all the available helpers. To free the helpers, call deallocateCanHelpers(). */ static const std::map& createCanHelpers(u32 bitrate); static void deallocateCanHelpers(); }; } /* namespace Can */ #endif /* ICANHELPER_H_ */ ================================================ FILE: CAN/include/ICanSender.h ================================================ /* * ICanSender.h * * Created on: Apr 1, 2018 * Author: famez */ #ifndef ICANSENDER_H_ #define ICANSENDER_H_ #include #include #include #include #include namespace Can { typedef std::function OnSendCallback; class ICanSender { public: ICanSender() {} virtual ~ICanSender() {} /** * Sends a frame through the can interface with the specified period */ virtual bool sendFrame(CanFrame frame, u32 period, OnSendCallback callback = OnSendCallback()) = 0; /** * Stops sending the frame whose id matches the given argument */ virtual void unSendFrame(u32 id) = 0; /* * Sends periodically a set of frames within the given period in the order defined in the vector. */ virtual bool sendFrames(std::vector frames, u32 period, OnSendCallback callback = OnSendCallback()) = 0; virtual void sendFrameOnce(const CanFrame& frame) = 0; /* * Stop sending the set of frames whose ids match the ones given as arguments */ virtual void unSendFrames(const std::vector& ids) = 0; /* * Returns true if the set of frames given by their corresponding ids are sent * (All the ids must match in the same order as specified in the method (sendFrames)) */ virtual bool isSent(const std::vector& ids) = 0; /** * To check if a frame with the current id is being sent through the interface */ virtual bool isSent(u32 id) = 0; }; } /* namespace Can */ #endif /* ICANSENDER_H_ */ ================================================ FILE: CAN/include/TRCReader.h ================================================ /* * TRCReader.h * * Created on: Oct 24, 2017 * Author: root */ #ifndef TRCREADER_H_ #define TRCREADER_H_ #include #include #include #include #include #include #include #include "CanFrame.h" #define MAX_LOADED_FRAMES 1000000 namespace Can { class TRCReadException : public std::exception { }; class TRCReader { private: std::string mFileName; size_t mCurrentPos; size_t mTotalFrames; std::ifstream mFileStream; std::pair mLastReadFrameTimePair; void readNextLine(bool& error, bool& empty); bool checkIntegrity(); public: TRCReader(); TRCReader(const std::string& path); virtual ~TRCReader(); bool loadFile(const std::string& path); void unloadFile(); bool isFileLoaded() const { return !mFileName.empty(); } size_t getNumberOfFrames() const { return mTotalFrames; } size_t getCurrentPos() const { return mCurrentPos; } bool seekPosition(size_t pos); void seekTime(u32 millis); std::pair getLastCanFrame(); void readNextCanFrame(); /* * Resets the reader to the beginning */ void reset(); }; } /* namespace Can */ #endif /* TRCREADER_H_ */ ================================================ FILE: CAN/include/TRCWriter.h ================================================ /* * TRCWriter.h * * Created on: Jun 8, 2018 * Author: fernado */ #ifndef TRCWRITER_H_ #define TRCWRITER_H_ #include #include #include "CanFrame.h" #include "Utils.h" namespace Can { class TRCWriter { private: std::ofstream mFileStream; unsigned int mCounter; public: TRCWriter(); TRCWriter(const std::string& file); virtual ~TRCWriter(); void write(const CanFrame& frame, const Utils::TimeStamp& timeStamp); bool open(const std::string& file); void close(); class TRCWriteException : public std::exception { }; }; } /* namespace Can */ #endif /* TRCWRITER_H_ */ ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(J1939Framework) set(CMAKE_BUILD_TYPE Debug) #For debug purposes set(J1939Framework_VERSION 1.0.0) set(CMAKE_MODULE_PATH "./cmake/;${CMAKE_MODULE_PATH}") find_package (Threads REQUIRED) find_package (jsoncpp REQUIRED) set (CMAKE_CXX_STANDARD 11) set(CMAKE_POSITION_INDEPENDENT_CODE ON) add_definitions(-DDATABASE_PATH="${CMAKE_INSTALL_PREFIX}/etc/j1939/frames.json") add_subdirectory(Common) add_subdirectory(CAN) add_subdirectory(J1939) add_subdirectory(Database) add_subdirectory(BinUtils) add_subdirectory(j1939AddressClaimer) add_subdirectory(Tests) find_package (LibWebSockets) find_package(Protobuf) if(LIBWEBSOCKETS_FOUND AND PROTOBUF_FOUND) message("-- LibWebSockets and protobuf are available") message("-- Protobuf version ${Protobuf_VERSION}") if("${Protobuf_VERSION}" VERSION_GREATER_EQUAL "3.0.0") message("-- Project j1939GUI will be built") add_subdirectory(GUI_WEB/backend) else() message("-- Protobuf version must be equals to or greater than 3.0.0") endif() else(LIBWEBSOCKETS_FOUND AND PROTOBUF_FOUND) message("-- Project " ${PROJECT_NAME} " will not be built") endif(LIBWEBSOCKETS_FOUND AND PROTOBUF_FOUND) include(GenerateExportHeader) generate_export_header(J1939) set_property(TARGET J1939 PROPERTY VERSION ${J1939Framework_VERSION}) set_property(TARGET J1939 PROPERTY SOVERSION 1) set_property(TARGET J1939 PROPERTY INTERFACE_J1939_MAJOR_VERSION 1) set_property(TARGET J1939 APPEND PROPERTY COMPATIBLE_INTERFACE_STRING J1939MAJOR_VERSION ) include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/J1939FrameworkConfigVersion.cmake" VERSION ${J1939Framework_VERSION} COMPATIBILITY AnyNewerVersion ) export(EXPORT J1939FrameworkTargets FILE "${CMAKE_CURRENT_BINARY_DIR}/J1939FrameworkTargets.cmake" NAMESPACE J1939:: ) configure_file(cmake/J1939FrameworkConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/J1939FrameworkConfig.cmake" COPYONLY ) set(ConfigPackageLocation lib/cmake/J1939Framework) install(EXPORT J1939FrameworkTargets FILE J1939FrameworkTargets.cmake NAMESPACE J1939:: DESTINATION ${ConfigPackageLocation} ) install( FILES cmake/J1939FrameworkConfig.cmake "${CMAKE_CURRENT_BINARY_DIR}/J1939FrameworkConfigVersion.cmake" DESTINATION ${ConfigPackageLocation} COMPONENT Devel ) ================================================ FILE: Common/.cproject ================================================ ================================================ FILE: Common/.gitignore ================================================ /Debug/ ================================================ FILE: Common/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(Common) add_library(Common STATIC Utils.cpp ) target_include_directories(Common PRIVATE include ) install (TARGETS Common EXPORT J1939FrameworkTargets DESTINATION lib) install(DIRECTORY include/ DESTINATION include) ================================================ FILE: Common/Utils.cpp ================================================ /* * Utils.cpp * * Created on: Apr 5, 2018 * Author: famez */ #include #include "Utils.h" namespace Utils { u32 getElapsedMillis(timespec* start, timespec* end) { return (end->tv_sec - start->tv_sec) * 1000 + (end->tv_nsec - start->tv_nsec) / 1000000; } timespec addMillis(timespec* time, u32 millis) { timespec retVal; retVal.tv_nsec = time->tv_nsec + (millis % 1000) * 1000000; retVal.tv_sec = time->tv_sec + millis / 1000; return retVal; } TimeStamp TimeStamp::operator-(const TimeStamp& other) const { TimeStamp retVal(*this); if(other.mMicroSec > mMicroSec) { if(other.mSeconds >= mSeconds) { return TimeStamp(); } else { retVal.mMicroSec = mMicroSec + 1000000; retVal.mSeconds = mSeconds - 1; } } if(other.mSeconds > retVal.mSeconds) { return TimeStamp(); } retVal.mSeconds = retVal.mSeconds - other.mSeconds; retVal.mMicroSec = retVal.mMicroSec - other.mMicroSec; return retVal; } TimeStamp TimeStamp::operator+(const TimeStamp& other) const { TimeStamp retVal(*this); retVal.mSeconds = retVal.mSeconds + other.mSeconds; retVal.mMicroSec = retVal.mMicroSec + other.mMicroSec; if(retVal.mMicroSec > 1000000) { retVal.mMicroSec -= 1000000; ++retVal.mSeconds; } return retVal; } bool TimeStamp::operator>(const TimeStamp& other) const { if(mSeconds > other.mSeconds) return true; if(mSeconds == other.mSeconds && mMicroSec > other.mMicroSec) return true; return false; } bool TimeStamp::operator<(const TimeStamp& other) const { if(mSeconds < other.mSeconds) return true; if(mSeconds == other.mSeconds && mMicroSec < other.mMicroSec) return true; return false; } bool TimeStamp::operator<=(const TimeStamp& other) const { return !(*this > other); } bool TimeStamp::operator>=(const TimeStamp& other) const { return !(*this < other); } TimeStamp TimeStamp::operator-(u32 millis) const { TimeStamp aux(millis/1000, (millis%1000)*1000); return (*this - aux); } TimeStamp TimeStamp::operator+(u32 millis) const { TimeStamp aux(millis/1000, (millis%1000)*1000); return (*this + aux); } TimeStamp TimeStamp::now() { auto now = std::chrono::steady_clock::now(); auto duration = now.time_since_epoch(); auto micro = std::chrono::duration_cast(duration); return TimeStamp(micro.count() / 1000000, micro.count() % 1000000); } } ================================================ FILE: Common/include/Assert.h ================================================ /* * Assert.h * * Created on: Apr 14, 2018 * Author: famez */ #ifndef ASSERT_H_ #define ASSERT_H_ #define ASSERT(condition) if(!(condition)) { fprintf(stderr, "Assertion failed in condition " #condition ", file %s, line %d\n", __FILE__, __LINE__); exit(-1); } #endif /* ASSERT_H_ */ ================================================ FILE: Common/include/ICloneable.h ================================================ /* * ICloneable.h * * Created on: Oct 10, 2017 * Author: famez */ #ifndef ICLONEABLE_H_ #define ICLONEABLE_H_ #define IMPLEMENT_CLONEABLE(CLASS, SUBCLASS) \ virtual CLASS* clone() const { \ return new SUBCLASS(*this); \ } template class ICloneable { public: ICloneable() {} virtual ~ICloneable() {} virtual T* clone() const = 0; }; #endif /* ICLONEABLE_H_ */ ================================================ FILE: Common/include/Singleton.h ================================================ /* * Singleton.h * * Created on: Mar 15, 2016 * Author: famez * Interfaz para crear una clase singleton. * Incluir siempre al inicio de la definición de la clase la macro SINGLETON_ACCESS */ #ifndef SINGLETON_H_ #define SINGLETON_H_ #include #include #include //#define SINGLETON_DEBUG #define SINGLETON_ACCESS friend class ISingleton; template class ISingleton { protected: ISingleton() {} static T* mInstance; public: virtual ~ISingleton(){} inline static T& getInstance() { if(!mInstance) { try{ mInstance = new T(); }catch (std::exception& e) { #ifdef SINGLETON_DEBUG printf("[ISingleton] Could not allocate instance for class %s. Constructor not defined\n", typeid(T).name()); #endif } #ifdef SINGLETON_DEBUG if(mInstance) printf("[ISingleton] Instance created for class %s\n", typeid(T).name()); #endif } return *mInstance; } inline static T* getInstancePtr() { if(!mInstance) { try{ mInstance = new T(); }catch (std::exception& e) { #ifdef SINGLETON_DEBUG printf("[ISingleton] Could not allocate instance for class %s. Constructor not defined\n", typeid(T).name()); #endif } #ifdef SINGLETON_DEBUG if(mInstance) printf("[ISingleton] Instance created for class %s\n", typeid(T).name()); #endif } return mInstance; } inline static void releaseInstance() { if(mInstance) { delete mInstance; mInstance = NULL; #ifdef SINGLETON_DEBUG printf("[ISingleton] Instance released for class %s\n", typeid(T).name()); #endif } } }; template T* ISingleton::mInstance = NULL; #endif /* SINGLETON_H_ */ ================================================ FILE: Common/include/Types.h ================================================ /* * Types.h * * Created on: Sep 23, 2017 * Author: famez */ #ifndef TYPES_H_ #define TYPES_H_ #include #include typedef unsigned char u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef int32_t s32; #endif /* TYPES_H_ */ ================================================ FILE: Common/include/Utils.h ================================================ /* * Utils.h * * Created on: Mar 11, 2018 * Author: famez */ #ifndef UTILS_H_ #define UTILS_H_ #include #include #include "Types.h" #define J1939_MIN(a,b) ((a)<(b)?(a):(b)) #define J1939_MAX(a,b) ((a)>(b)?(a):(b)) namespace Utils { u32 getElapsedMillis(timespec* start, timespec* end); timespec addMillis(timespec* time, u32 millis); class TimeStamp { private: u32 mSeconds; u32 mMicroSec; public: TimeStamp(): mSeconds(0), mMicroSec(0) {} TimeStamp(u32 seconds, u32 microSec) : mSeconds(seconds), mMicroSec(microSec) {} ~TimeStamp() {} u32 getMicroSec() const { return mMicroSec; } void setMicroSec(u32 microSec) { mMicroSec = microSec; } u32 getSeconds() const { return mSeconds; } void setSeconds(u32 seconds) { mSeconds = seconds; } TimeStamp operator-(const TimeStamp& other) const; TimeStamp operator+(const TimeStamp& other) const; TimeStamp operator-(u32 millis) const; TimeStamp operator+(u32 millis) const; bool operator==(const TimeStamp& other) const { return (mSeconds == other.mSeconds && mMicroSec == other.mMicroSec); }; bool operator>(const TimeStamp& other) const; bool operator<(const TimeStamp& other) const; bool operator<=(const TimeStamp& other) const; bool operator>=(const TimeStamp& other) const; static TimeStamp now(); }; } #endif /* UTILS_H_ */ ================================================ FILE: Database/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(Database) install (FILES frames.json DESTINATION etc/j1939/) ================================================ FILE: Database/README.md ================================================ Contains a database with some frames defined in FMS standard. This database is used by the utilities. ================================================ FILE: Database/frames.json ================================================ [ { "name" : "VI", "pgn" : 65260, "length" : 0, "spns" : [ { "name" : "Vehicle Number Identifier", "number" : 237, "type" : 2 } ] }, { "name" : "DI", "pgn" : 65131, "length" : 0, "spns" : [ { "name" : "Driver 1 Identification", "number" : 1625, "type" : 2 }, { "name" : "Driver 2 Identification", "number" : 1626, "type" : 2 } ] }, { "name" : "DD", "pgn" : 65276, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 0.40000000000000002, "formatOffset" : 0, "name" : "Fuel Level", "number" : 96, "offset" : 1, "type" : 0, "units" : "%" } ] }, { "name" : "ETC1", "pgn" : 61442, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 0.4, "formatOffset" : 0, "name" : "Percent Clutch Slip", "number" : 522, "offset" : 3, "type" : 0, "units" : "%" } ] }, { "name" : "ETC2", "pgn" : 61445, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 1, "formatOffset" : -125, "name" : "Transmission Selected Gear", "number" : 524, "offset" : 0, "type" : 0, "units" : "" }, { "byteSize" : 1, "formatGain" : 1, "formatOffset" : -125, "name" : "Transmission Current Gear", "number" : 523, "offset" : 3, "type" : 0, "units" : "" } ] }, { "name" : "HOURS", "pgn" : 65253, "length" : 8, "spns" : [ { "byteSize" : 4, "formatGain" : 0.05, "formatOffset" : 0, "name" : "Engine Total Hours of Operation", "number" : 247, "offset" : 0, "type" : 0, "units" : "hours" } ] }, { "name" : "TRF1", "pgn" : 65272, "length" : 8, "spns" : [ { "byteSize" : 2, "formatGain" : 0.03125, "formatOffset" : -273, "name" : "Transmission Oil Temperature 1", "number" : 177, "offset" : 4, "type" : 0, "units" : "°C" } ] }, { "name" : "ERC1", "pgn" : 61440, "length" : 8, "spns" : [ { "bitOffset" : 4, "bitSize" : 2, "descriptions" : [ "Retarder - brake assist disabled", "Retarder - brake assist enabled", "Error", "Not available" ], "name" : "Retarder Enable - Brake Assist Switch", "number" : 571, "offset" : 0, "type" : 1 } ] }, { "name" : "EBC1", "pgn" : 61441, "length" : 8, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "Not Fully Operational", "Fully Operational", "Error", "Not available" ], "name" : "ABS Fully Operational", "number" : 1243, "offset" : 5, "type" : 1 } ] }, { "name" : "IC1", "pgn" : 65270, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 2, "formatOffset" : 0, "name" : "Engine Intake Manifold #1 Pressure", "number" : 102, "offset" : 1, "type" : 0, "units" : "kPa" } ] }, { "name" : "RF", "pgn" : 65275, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 16, "formatOffset" : 0, "name" : "Hydraulic Retarder Pressure", "number" : 119, "offset" : 0, "type" : 0, "units" : "kPa" }, { "byteSize" : 1, "formatGain" : 1, "formatOffset" : -40, "name" : "Hydraulic Retarder Oil Temperature", "number" : 120, "offset" : 1, "type" : 0, "units" : "°C" } ] }, { "name" : "VDHR", "pgn" : 65217, "length" : 8, "spns" : [ { "byteSize" : 4, "formatGain" : 5, "formatOffset" : 0, "name" : "High resolution total vehicle distance", "number" : 917, "offset" : 0, "type" : 0, "units" : "m" } ] }, { "name" : "AT1T1I", "pgn" : 65110, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 0.4, "formatOffset" : 0, "name" : "Aftertreatment 1 Diesel Exhaust Fluid Tank 1 Level", "number" : 1761, "offset" : 0, "type" : 0, "units" : "%" } ] }, { "name" : "EFL", "pgn" : 65263, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 4, "formatOffset" : 0, "name" : "Engine Fuel Delivery Pressure", "number" : 94, "offset" : 0, "type" : 0, "units" : "KPa" }, { "byteSize" : 1, "formatGain" : 0.05, "formatOffset" : 0, "name" : "Engine Extended Crankcase Blow-by Pressure", "number" : 22, "offset" : 1, "type" : 0, "units" : "KPa" }, { "byteSize" : 1, "formatGain" : 0.4, "formatOffset" : 0, "name" : "Engine Oil Level", "number" : 98, "offset" : 2, "type" : 0, "units" : "%" }, { "byteSize" : 1, "formatGain" : 4, "formatOffset" : 0, "name" : "Engine Oil Pressure", "number" : 100, "offset" : 3, "type" : 0, "units" : "KPa" }, { "byteSize" : 2, "formatGain" : 0.0078125, "formatOffset" : -250, "name" : "Engine Crankcase Pressure", "number" : 101, "offset" : 4, "type" : 0, "units" : "KPa" }, { "byteSize" : 1, "formatGain" : 2, "formatOffset" : 0, "name" : "Engine Coolant Pressure", "number" : 109, "offset" : 6, "type" : 0, "units" : "KPa" }, { "byteSize" : 1, "formatGain" : 0.4, "formatOffset" : 0, "name" : "Engine Coolant Level", "number" : 111, "offset" : 7, "type" : 0, "units" : "%" } ] }, { "name" : "LFC", "pgn" : 65257, "length" : 8, "spns" : [ { "byteSize" : 4, "formatGain" : 0.5, "formatOffset" : 0, "name" : "Engine total fuel used", "number" : 250, "offset" : 4, "type" : 0, "units" : "L" } ] }, { "name" : "ET1", "pgn" : 65262, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 1, "formatOffset" : -40, "name" : "Engine Coolant Temperature", "number" : 110, "offset" : 0, "type" : 0, "units" : "°C" }, { "byteSize" : 2, "formatGain" : 0.03125, "formatOffset" : -273, "name" : "Engine Oil Temperature 1", "number" : 175, "offset" : 2, "type" : 0, "units" : "°C" } ] }, { "name" : "TRF1", "pgn" : 65272, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 16, "formatOffset" : 0, "name" : "Clutch Pressure", "number" : 123, "offset" : 0, "type" : 0, "units" : "kPa" }, { "byteSize" : 1, "formatGain" : 0.4, "formatOffset" : 0, "name" : "Transmission Oil Level", "number" : 124, "offset" : 1, "type" : 0, "units" : "%" }, { "byteSize" : 1, "formatGain" : 2, "formatOffset" : 0, "name" : "Transmission Filter Differential Pressure", "number" : 126, "offset" : 2, "type" : 0, "units" : "kPa" }, { "byteSize" : 1, "formatGain" : 16, "formatOffset" : 0, "name" : "Transmission Oil Pressure", "number" : 127, "offset" : 3, "type" : 0, "units" : "kPa" }, { "byteSize" : 2, "formatGain" : 0.03125, "formatOffset" : -273, "name" : "Transmission Oil Temperature", "number" : 177, "offset" : 4, "type" : 0, "units" : "kPa" }, { "byteSize" : 1, "formatGain" : 0.5, "formatOffset" : -62.5, "name" : "Transmission Oil Level High / Low", "number" : 3027, "offset" : 6, "type" : 0, "units" : "L" }, { "byteSize" : 1, "formatGain" : 0.5, "formatOffset" : -62.5, "name" : "Transmission Oil Level Countdown Timer", "number" : 3027, "offset" : 6, "type" : 0, "units" : "L" }, { "bitOffset" : 0, "bitSize" : 4, "name" : "Transmission Oil Level Countdown Timer", "number" : 3028, "offset" : 7, "type" : 1 }, { "bitOffset" : 4, "bitSize" : 4, "name" : "Transmission Oil Level Measurement Status", "number" : 3026, "offset" : 7, "type" : 1 } ] }, { "name" : "LFE", "pgn" : 65266, "length" : 8, "spns" : [ { "byteSize" : 2, "formatGain" : 0.05, "formatOffset" : 0, "name" : "Fuel Rate", "number" : 183, "offset" : 0, "type" : 0, "units" : "L/h" } ] }, { "name" : "HRLFC", "pgn" : 64777, "length" : 8, "spns" : [ { "byteSize" : 4, "formatGain" : 0.001, "formatOffset" : 0, "name" : "High resolution engine total fuel used", "number" : 5054, "offset" : 4, "type" : 0, "units" : "L" } ] }, { "name" : "EEC2", "pgn" : 61443, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 0.4, "formatOffset" : 0, "name" : "Accelerator pedal position 1", "number" : 91, "offset" : 1, "type" : 0, "units" : "%" }, { "byteSize" : 1, "formatGain" : 1, "formatOffset" : 0, "name" : "Engine Percent Load At Current Speed", "number" : 92, "offset" : 2, "type" : 0, "units" : "%" }, { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "Accelerator pedal 1 not in low idle condition", "Accelerator pedal 1 in low idle condition", "Error", "Not available" ], "name" : "Accelerator Pedal 1 Low Idle Switch", "number" : 558, "offset" : 0, "type" : 1 }, { "bitOffset" : 6, "bitSize" : 2, "descriptions" : [ "Accelerator pedal 2 not in low idle condition", "Accelerator pedal 2 in low idle condition", "Error", "Not available" ], "name" : "Accelerator Pedal 2 Low Idle Switch", "number" : 2970, "offset" : 0, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "Kickdown passive", "Kickdown active", "Error", "Not available" ], "name" : "Accelerator Pedal Kickdown Switch", "number" : 559, "offset" : 0, "type" : 1 } ] }, { "name" : "TCO1", "pgn" : 65132, "length" : 8, "spns" : [ { "bitOffset" : 0, "bitSize" : 3, "descriptions" : [ "Rest", "Driver Available", "Work", "Drive" ], "name" : "Driver 1 Working State", "number" : 1612, "offset" : 0, "type" : 1 }, { "bitOffset" : 3, "bitSize" : 3, "descriptions" : [ "Rest", "Driver Available", "Work", "Drive" ], "name" : "Driver 2 Working State", "number" : 1613, "offset" : 0, "type" : 1 }, { "bitOffset" : 4, "bitSize" : 2, "descriptions" : [ "Card not present", "Card present", "Error", "Not available" ], "name" : "Driver 1 Card", "number" : 1615, "offset" : 1, "type" : 1 }, { "bitOffset" : 4, "bitSize" : 2, "descriptions" : [ "Card not present", "Card present", "Error", "Not available" ], "name" : "Driver 2 Card", "number" : 1616, "offset" : 2, "type" : 1 } ] }, { "name" : "DC1", "pgn" : 65102, "length" : 8, "spns" : [ { "bitOffset" : 6, "bitSize" : 2, "descriptions" : [ "all bus doors disabled", "at least 1 bus door enabled", "Error", "Not available" ], "name" : "Status 2 of doors", "number" : 3411, "offset" : 0, "type" : 1 }, { "bitOffset" : 4, "bitSize" : 2, "descriptions" : [ "inside bus", "outside bus", "Error", "Not available" ], "name" : "Ramp/Wheel chairlift", "number" : 1820, "offset" : 0, "type" : 1 }, { "bitOffset" : 0, "bitSize" : 4, "descriptions" : [ "at least 1 door is open", "closing last door", "all doors closed" ], "name" : "Position of doors", "number" : 1821, "offset" : 0, "type" : 1 } ] }, { "name" : "AS", "pgn" : 65237, "length" : 8, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "Alternator 1 not charging", "Alternator 1 charging", "Error", "Not available" ], "name" : "Alternator 1 Status", "number" : 3353, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "Alternator 2 not charging", "Alternator 2 charging", "Error", "Not available" ], "name" : "Alternator 2 Status", "number" : 3354, "offset" : 2, "type" : 1 }, { "bitOffset" : 4, "bitSize" : 2, "descriptions" : [ "Alternator 3 not charging", "Alternator 3 charging", "Error", "Not available" ], "name" : "Alternator 3 Status", "number" : 3355, "offset" : 2, "type" : 1 }, { "bitOffset" : 6, "bitSize" : 2, "descriptions" : [ "Alternator 4 not charging", "Alternator 4 charging", "Error", "Not available" ], "name" : "Alternator 4 Status", "number" : 3356, "offset" : 2, "type" : 1 } ] }, { "name" : "CCVS", "pgn" : 65265, "length" : 8, "spns" : [ { "bitOffset" : 0, "bitSize" : 5, "descriptions" : [ "Off", "", "", "", "", "Set" ], "name" : "PTO State", "number" : 976, "offset" : 6, "type" : 1 }, { "byteSize" : 2, "formatGain" : 0.00390625, "formatOffset" : 0, "name" : "Wheel Speed", "number" : 84, "offset" : 1, "type" : 0, "units" : "km/h" }, { "bitOffset" : 4, "bitSize" : 2, "descriptions" : [ "Pedal released", "Pedal depressed", "Error", "Not available" ], "name" : "Brake Switch", "number" : 597, "offset" : 3, "type" : 1 }, { "bitOffset" : 6, "bitSize" : 2, "descriptions" : [ "Pedal released", "Pedal depressed", "Error", "Not available" ], "name" : "Clutch Switch", "number" : 598, "offset" : 3, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "Parking brake not set", "Parking brake set", "Error", "Not available" ], "name" : "Parking Brake Switch", "number" : 70, "offset" : 0, "type" : 1 } ] }, { "name" : "PTODE", "pgn" : 64932, "length" : 8, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "No PTO drive is engaged", "At least one PTO drive is engaged", "Error", "Not available" ], "name" : "At least one PTO engaged", "number" : 3948, "offset" : 6, "type" : 1 } ] }, { "name" : "EEC1", "pgn" : 61444, "length" : 8, "spns" : [ { "byteSize" : 2, "formatGain" : 0.125, "formatOffset" : 0, "name" : "Engine Speed", "number" : 190, "offset" : 3, "type" : 0, "units" : "rpm" }, { "byteSize" : 1, "formatGain" : 1.0, "formatOffset" : -125, "name" : "Percent Torque", "number" : 513, "offset" : 2, "type" : 0, "units" : "%" } ] }, { "name" : "AMB", "pgn" : 65269, "length" : 8, "spns" : [ { "byteSize" : 1, "formatGain" : 0.5, "formatOffset" : 0, "name" : "Barometric Pressure", "number" : 108, "offset" : 0, "type" : 0, "units" : "kPa" } ] }, { "name" : "SERV", "pgn" : 65216, "length" : 8, "spns" : [ { "byteSize" : 2, "formatGain" : 5, "formatOffset" : -160635, "name" : "Service distance", "number" : 914, "offset" : 1, "type" : 0, "units" : "km" } ] }, { "name" : "DC2", "pgn" : 64933, "length" : 8, "spns" : [ { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "Closed", "Open", "Error", "Not available" ], "name" : "Open Status Door 1", "number" : 3413, "offset" : 0, "type" : 1 }, { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "Closed", "Open", "Error", "Not available" ], "name" : "Open Status Door 2", "number" : 3416, "offset" : 1, "type" : 1 }, { "bitOffset" : 6, "bitSize" : 2, "descriptions" : [ "Closed", "Open", "Error", "Not available" ], "name" : "Open Status Door 3", "number" : 3419, "offset" : 1, "type" : 1 }, { "bitOffset" : 4, "bitSize" : 2, "descriptions" : [ "Closed", "Open", "Error", "Not available" ], "name" : "Open Status Door 3", "number" : 3422, "offset" : 2, "type" : 1 } ] }, { "name" : "VDC2", "pgn" : 61449, "length" : 8, "spns" : [ { "byteSize" : 2, "formatGain" : 0.00048828125, "formatOffset" : -15.687, "name" : "Lateral Acceleration", "number" : 1809, "offset" : 5, "type" : 0, "units" : "m/s2" } ] }, { "name" : "EBC2", "pgn" : 65215, "length" : 8, "spns" : [ { "byteSize" : 2, "formatGain" : 0.00390625, "formatOffset" : 0, "name" : "Front Axe Speed", "number" : 904, "offset" : 0, "type" : 0, "units" : "kph" },{ "byteSize" : 1, "formatGain" : 0.0625, "formatOffset" : -7.18125, "name" : "Relative Speed; Front Axle, Left Wheel", "number" : 905, "offset" : 2, "type" : 0, "units" : "kph" },{ "byteSize" : 1, "formatGain" : 0.0625, "formatOffset" : -7.18125, "name" : "Relative Speed; Front Axle, Right Wheel", "number" : 906, "offset" : 3, "type" : 0, "units" : "kph" },{ "byteSize" : 1, "formatGain" : 0.0625, "formatOffset" : -7.18125, "name" : "Relative Speed; Rear Axle 1, Left Wheel", "number" : 907, "offset" : 4, "type" : 0, "units" : "kph" },{ "byteSize" : 1, "formatGain" : 0.0625, "formatOffset" : -7.18125, "name" : "Relative Speed; Rear Axle 1, Right Wheel", "number" : 908, "offset" : 5, "type" : 0, "units" : "kph" },{ "byteSize" : 1, "formatGain" : 0.0625, "formatOffset" : -7.18125, "name" : "Relative Speed; Rear Axle 2, Left Wheel", "number" : 909, "offset" : 6, "type" : 0, "units" : "kph" },{ "byteSize" : 1, "formatGain" : 0.0625, "formatOffset" : -7.18125, "name" : "Relative Speed; Rear Axle 2, Right Wheel", "number" : 910, "offset" : 7, "type" : 0, "units" : "kph" } ] } ] ================================================ FILE: GUI_WEB/README.md ================================================ ![alt text](https://github.com/famez/J1939-Framework/blob/master/GUI_WEB/J1939GUI.png) ================================================ FILE: GUI_WEB/backend/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(j1939GUI) set (GUI_SERVER_DIR "/var/www/j1939gui") add_definitions(-DHTTP_DIR="${GUI_SERVER_DIR}") protobuf_generate_cpp(PROTO_SRCS PROTO_HDRS graph.proto) add_executable(j1939GUI src/J1939GUI.cpp src/graph.cpp ${PROTO_SRCS} ${PROTO_HDRS} ) #Visible for frontend configure_file(graph.proto ../frontend/graph.proto COPYONLY) target_include_directories(j1939GUI PUBLIC . ${J1939_SOURCE_DIR}/include ${Can_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ${LIBWEBSOCKETS_INCLUDE_DIR} ${Protobuf_INCLUDE_DIRS} ) link_directories(${LIBWEBSOCKETS_LIBRARY_DIR}) target_link_libraries(j1939GUI PUBLIC J1939 Can rt -rdynamic jsoncpp ${LIBWEBSOCKETS_LIBRARIES} ${Protobuf_LIBRARIES} ) install (TARGETS j1939GUI DESTINATION bin) install (DIRECTORY ../frontend/ DESTINATION ${GUI_SERVER_DIR}) ================================================ FILE: GUI_WEB/backend/graph.proto ================================================ syntax = "proto3"; message Graph { string title = 1; int32 number = 2; Axis axisX = 3; Axis axisY = 4; repeated Sample samples = 5; message Sample { double x = 1; double y = 2; } } message Axis { string units = 1; double max = 2; double min = 3; } ================================================ FILE: GUI_WEB/backend/src/J1939GUI.cpp ================================================ extern "C" { #include #include } #include #include #include #include #include #include #include #include #include //J1939 libraries #include #include #include #include #include #include #include #include #include #include //Can includes #include #include "graph.h" #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif #define J1939_RX_BUFFER_BYTES (1024) //Bitrate for J1939 protocol #define BAUD_250K 250000 #define LIST_FRAMES_REQUEST "list frames" #define ADD_FRAME_REQUEST "add frame" #define SET_FRAME_REQUEST "set frame" #define DELETE_FRAME_REQUEST "delete frame" #define LIST_INTERFACES "list interfaces" #define SHOW_RAW "show raw" bool processRequest(const Json::Value& request, Json::Value& response); void resetReceiver(); bool sentFramesToJson(Json::Value& jsonVal); int callback_http(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); int callback_j1939(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); std::string rcvRequest; std::queue jsonResponses; Json::Value rxFrames; bool showRaw; //Option to show packets not able to decode static struct lws_protocols protocols[] = { /* The first protocol must always be the HTTP handler */ { "http-only", /* name */ callback_http, /* callback */ 0, /* No per session data. */ 0, /* max frame size / rx buffer */ }, { "j1939-protocol", callback_j1939, 0, J1939_RX_BUFFER_BYTES, }, { "graph-protocol", callback_graph, sizeof(void*), //Enough to store our pointer to graph J1939_RX_BUFFER_BYTES, }, { NULL, NULL, 0, 0 } /* terminator */ }; using namespace J1939; using namespace Can; using namespace Utils; bool isFrameSent(const J1939Frame* frame, const std::string& interface); void sendFrameThroughInterface(const J1939Frame* j1939Frame, u32 period, const std::string& interface); void unsendFrameThroughInterface(const J1939Frame* j1939Frame, const std::string& interface); Json::Value frameToJson(const J1939Frame* frame); void onRcv(const CanFrame& frame, const TimeStamp&, const std::string& interface, void*); bool onTimeout(); //Map of the created frames to be sent to the CAN interface std::vector framesToSend; //Map to specify the period for the different frames (in millis) std::map framePeriods; //To reassemble frames fragmented by means of Broadcast Announce Message protocol BamReassembler reassembler; //Thread in charge of sniffing the Can Network std::unique_ptr rxThread = nullptr; std::mutex rxLock; //Cached received frames to avoid processing frames that did not change std::map rcvFramesCache; //To track how many frames have been received std::map rcvFramesCount; static const lws_protocol_vhost_options mimetypes= { nullptr, nullptr, ".proto", "application/x-protobuf" }; //Used to serve files from http server static lws_http_mount mount; int callback_http(lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { switch (reason) { case LWS_CALLBACK_HTTP: break; default: break; } return 0; } int callback_j1939(lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { switch (reason) { case LWS_CALLBACK_ESTABLISHED: { resetReceiver(); } break; case LWS_CALLBACK_RECEIVE: { Json::Value rcvjson; Json::Value respjson; Json::CharReaderBuilder builder; Json::CharReader *jSonReader = builder.newCharReader(); std::string errs; rcvRequest.append((char*)in, len); if(jSonReader->parse(rcvRequest.c_str(), rcvRequest.c_str() + rcvRequest.size(), &rcvjson, &errs)) { //Verify if we received the whole Json string lwsl_info("Json request: %s\n", rcvRequest.c_str()); rcvRequest.clear(); if(rcvjson.isMember("command") && rcvjson["command"].isString()) { if(rcvjson["command"] == "reset rx") { resetReceiver(); } else if(rcvjson["command"] == "check rx") { rxLock.lock(); jsonResponses.push(rxFrames); rxFrames["rx"].clear(); rxLock.unlock(); lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); } else if(processRequest(rcvjson, respjson)) { sentFramesToJson(respjson["frames"]); jsonResponses.push(respjson); lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); } } } delete jSonReader; } break; case LWS_CALLBACK_SERVER_WRITEABLE: { if(!jsonResponses.empty()) { //Check if there are enqueued responses to send std::stringstream sstr; sstr << jsonResponses.front(); lwsl_info("Response: %s", sstr.str().c_str()); char *buff = new char[LWS_SEND_BUFFER_PRE_PADDING + sstr.str().size() + LWS_SEND_BUFFER_POST_PADDING]; memcpy(buff + LWS_SEND_BUFFER_PRE_PADDING, sstr.str().c_str(), sstr.str().size()); lws_write(wsi, (unsigned char*)(buff + LWS_SEND_BUFFER_PRE_PADDING), sstr.str().size(), LWS_WRITE_TEXT); jsonResponses.pop(); delete[] buff; } if(!jsonResponses.empty()) { //Check again if there are enqueued responses to have another chance to send them lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); } } break; default: break; } return 0; } bool processRequest(const Json::Value& request, Json::Value& response) { response["command"] = request["command"].asString(); unsigned int i = 0; //Activate show raw functionality if(request["command"] == SHOW_RAW) { if(request.isMember("raw") && request["raw"].isBool()) { showRaw = request["raw"].asBool(); } return false; //No need to send a response } if(request["command"] == LIST_FRAMES_REQUEST) { std::set pgns = J1939Factory::getInstance().getAllRegisteredPGNs(); for(auto pgn = pgns.begin(); pgn != pgns.end(); ++pgn) { std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(*pgn); //Only add to the list if it is a generic frame if(frame->isGenericFrame()) { response["data"][i]["pgn"] = std::to_string(*pgn); response["data"][i]["name"] = frame->getName(); ++i; } } return true; } if(request["command"] == ADD_FRAME_REQUEST) { if(!request.isMember("data") || !request["data"].isUInt()) return false; u32 pgn = request["data"].asUInt(); std::unique_ptr frameToAdd = J1939Factory::getInstance().getJ1939Frame(pgn); if(!frameToAdd || !frameToAdd->isGenericFrame()) { lwsl_err("Frame not recognized...\n"); return false; } framesToSend.push_back(frameToAdd.release()); return true; } //Delete frame if(request["command"] == DELETE_FRAME_REQUEST) { //Sanity checks... if(!request.isMember("index") || !request["index"].isUInt()) { lwsl_err("Index not specified"); return false; } size_t index = request["index"].asUInt(); if(index >= framesToSend.size()) { lwsl_err("Index out of range... Someone is playing maliciously with the frontend..."); return false; } J1939Frame *frame = framesToSend[index]; //At this point, if the frame is being sent, refresh the information to the sender const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::shared_ptr sender = CanEasy::getSender(*iter); if(isFrameSent(frame, *iter)) { auto period = framePeriods.find(frame); if(period != framePeriods.end()) { unsendFrameThroughInterface(frame, *iter); } else { lwsl_err("Period not defined"); } } } framesToSend.erase(framesToSend.begin() + index); return true; } if(request["command"] == SET_FRAME_REQUEST) { //Sanity checks... if(!request.isMember("index") || !request["index"].isUInt()) { lwsl_err("Index not specified"); return false; } size_t index = request["index"].asUInt(); if(index >= framesToSend.size()) { lwsl_err("Index out of range... Someone is playing maliciously with the frontend..."); return false; } J1939Frame *frame = framesToSend[index]; //Change priority if(request.isMember("prio") && request["prio"].isUInt() && ((request["prio"].asUInt() & J1939_PRIORITY_MASK) == request["prio"].asUInt())) { u8 prio = request["prio"].asUInt(); frame->setPriority(prio); lwsl_info("Prio set"); } //Change source if(request.isMember("src") && request["src"].isUInt() && ((request["src"].asUInt() & J1939_SRC_ADDR_MASK) == request["src"].asUInt())) { u8 src = request["src"].asUInt(); frame->setSrcAddr(src); lwsl_info("Src set"); } //Change destination if(request.isMember("dest") && request["dest"].isUInt() && ((request["dest"].asUInt() & J1939_DST_ADDR_MASK) == request["dest"].asUInt())) { u8 dest = request["dest"].asUInt(); frame->setDstAddr(dest); lwsl_info("Dest set"); } //Change period if(request.isMember("period") && request["period"].isUInt()) { u32 period = request["period"].asUInt(); framePeriods[frame] = period; lwsl_info("Period set"); } //Change spn value if(frame->isGenericFrame()) { //Sanity check... GenericFrame *genFrame = static_cast(frame); if(request.isMember("spn") && request["spn"].isUInt() && genFrame->hasSPN(request["spn"].asUInt())) { SPN *spn = genFrame->getSPN(request["spn"].asUInt()); switch(spn->getType()) { case SPN::SPN_STATUS: { SPNStatus * status = static_cast(spn); if(request.isMember("value") && request["value"].isUInt() && ((request["value"].asUInt() & 0xFF) == request["value"].asUInt())) { u8 value = request["value"].asUInt(); status->setValue(value); } } break; case SPN::SPN_NUMERIC: { SPNNumeric *numeric = static_cast(spn); if(request.isMember("value") && request["value"].isDouble()) { double value = request["value"].asDouble(); numeric->setFormattedValue(value); } } break; case SPN::SPN_STRING: { SPNString *strSpn = static_cast(spn); if(request.isMember("value") && request["value"].isString()) { std::string value = request["value"].asString(); strSpn->setValue(value); } } break; default: break; } } } //At this point, if the frame is being sent, refresh the information to the sender const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::shared_ptr sender = CanEasy::getSender(*iter); if(isFrameSent(frame, *iter)) { auto period = framePeriods.find(frame); if(period != framePeriods.end()) { sendFrameThroughInterface(frame, period->second, *iter); } else { lwsl_err("Period not defined"); } } } //Send/unsend frame through the interface if(request.isMember("interface") && request["interface"].isObject()) { std::vector members = request["interface"].getMemberNames(); for(auto member = members.begin(); member != members.end(); ++member) { if(request["interface"][*member].isBool()) { if(request["interface"][*member].asBool()) { //Send frame through interface auto period = framePeriods.find(frame); if(period != framePeriods.end()) { sendFrameThroughInterface(frame, period->second, *member); } else { lwsl_err("Period not defined"); } } else { //Unsend frame unsendFrameThroughInterface(frame, *member); } } } lwsl_info("Period set"); } return true; } return false; } int main(int argc, char *argv[]) { lws_context_creation_info info; showRaw = false; memset(&info, 0, sizeof(info)); memset(&mount, 0, sizeof(mount)); mount.mountpoint = "/"; mount.origin = HTTP_DIR; mount.def = "index.html"; mount.extra_mimetypes = &mimetypes; mount.origin_protocol = LWSMPRO_FILE; mount.mountpoint_len = 1; info.port = 8000; info.protocols = protocols; info.mounts = &mount; info.gid = -1; info.uid = -1; lws_context *context = lws_create_context(&info); //lws_set_log_level(LLL_INFO | LLL_ERR | LLL_WARN | LLL_NOTICE, NULL); lwsl_info("LWSMPRO_FILE: %d", LWSMPRO_FILE); //Initialization of J1939 Framework //Read database if available J1939DataBase database; if(!database.parseJsonFile(DATABASE_PATH)) { std::cerr << "Database not found in " << DATABASE_PATH << std::endl; return 1; } const std::vector& ddbbFrames = database.getParsedFrames(); //Register all the frames listed in the database for(auto iter = ddbbFrames.begin(); iter != ddbbFrames.end(); ++iter) { J1939Factory::getInstance().registerFrame(*iter); } //Initialize can CanEasy::initialize(BAUD_250K, onRcv, onTimeout); const std::set& ifaces = CanEasy::getInitializedCanIfaces(); if(ifaces.empty()) { std::cerr << "No interfaces initialized" << std::endl; return 2; } rxFrames["command"] = "check rx"; //Websockets work int n; do { n = lws_service(context, /* timeout_ms = */1000); rcvRequest.clear(); //Clean the request string } while(n >= 0); lws_context_destroy(context); return 0; } bool sentFramesToJson(Json::Value& jsonVal) { unsigned int i = 0; for(auto iter = framesToSend.begin(); iter != framesToSend.end(); ++iter) { J1939Frame* frame = *iter; jsonVal[i] = frameToJson(frame); auto period = framePeriods.find(frame); if(period != framePeriods.end()) { jsonVal[i]["period"] = period->second; } const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iface = ifaces.begin(); iface != ifaces.end(); ++iface) { jsonVal[i]["interfaces"][*iface] = isFrameSent(frame, *iface); } ++i; } return true; } bool listInterfaces(Json::Value& response) { unsigned int i = 0; const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iface = ifaces.begin(); iface != ifaces.end(); ++iface) { response["interfaces"][i++] = *iface; } return true; } bool isFrameSent(const J1939Frame* frame, const std::string& interface) { std::vector ids; std::shared_ptr sender = CanEasy::getSender(interface); if(!sender) return false; //If the frame is bigger than 8 bytes, we use BAM if(frame->getDataLength() > MAX_CAN_DATA_SIZE) { BamFragmenter fragmenter; fragmenter.fragment(*frame); const TPCMFrame& connFrame = fragmenter.getConnFrame(); ids.push_back(connFrame.getIdentifier()); std::vector dataFrames = fragmenter.getDataFrames(); for(auto iter = dataFrames.begin(); iter != dataFrames.end(); ++iter) { ids.push_back(iter->getIdentifier()); } } else { //Can be sent in one frame ids.push_back(frame->getIdentifier()); } return sender->isSent(ids); } void sendFrameThroughInterface(const J1939Frame* j1939Frame, u32 period, const std::string& interface) { //Sanity check. We do not trust the foreground app std::shared_ptr sender = CanEasy::getSender(interface); if(!sender) return; //Send the frame with the configured periodicity size_t length = j1939Frame->getDataLength(); CanFrame canFrame; u32 id; u8* buff; std::string data; //J1939 data is always transmitted in extended format canFrame.setExtendedFormat(true); //If the frame is bigger than 8 bytes, we use BAM if(length > MAX_CAN_DATA_SIZE) { std::vector canFrames; BamFragmenter fragmenter; fragmenter.fragment(*j1939Frame); const TPCMFrame& connFrame = fragmenter.getConnFrame(); length = connFrame.getDataLength(); buff = new u8[length]; connFrame.encode(id, buff, length); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; canFrames.push_back(canFrame); std::vector dataFrames = fragmenter.getDataFrames(); for(auto iter = dataFrames.begin(); iter != dataFrames.end(); ++iter) { length = iter->getDataLength(); buff = new u8[length]; iter->encode(id, buff, length); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; canFrames.push_back(canFrame); } sender->sendFrames(canFrames, period); } else { //Can be sent in one frame buff = new u8[length]; j1939Frame->encode(id, buff, length); //Set identifier canFrame.setId(id); //Set data std::string data; data.append((char*)buff, length); canFrame.setData(data); delete[] buff; sender->sendFrame(canFrame, period); } } void unsendFrameThroughInterface(const J1939Frame* j1939Frame, const std::string& interface) { std::vector ids; bool found = false; //If the frame is bigger than 8 bytes, we use BAM if(j1939Frame->getDataLength() > MAX_CAN_DATA_SIZE) { BamFragmenter fragmenter; fragmenter.fragment(*j1939Frame); const TPCMFrame& connFrame = fragmenter.getConnFrame(); ids.push_back(connFrame.getIdentifier()); std::vector dataFrames = fragmenter.getDataFrames(); for(auto iter = dataFrames.begin(); iter != dataFrames.end(); ++iter) { ids.push_back(iter->getIdentifier()); } } else { //Can be sent in one frame ids.push_back(j1939Frame->getIdentifier()); } const std::set& ifaces = CanEasy::getInitializedCanIfaces(); for(auto iter = ifaces.begin(); iter != ifaces.end(); ++iter) { std::shared_ptr sender = CanEasy::getSender(*iter); if(interface.empty() || interface == *iter) { sender->unSendFrames(ids); found = true; } } if(!found) { std::cerr << "Frame not sent through the given interface..." << std::endl; } } void onRcv(const CanFrame& frame, const TimeStamp& ts, const std::string& interface, void*) { rxLock.lock(); rxFrames["rx"][std::to_string(frame.getId())]["count"] = ++rcvFramesCount[frame.getId()]; rxLock.unlock(); if(rcvFramesCache.find(frame.getId()) != rcvFramesCache.end() && frame.getData() == rcvFramesCache[frame.getId()].getData()) { //The raw data is exactly the same. return; } //At least a SPN has changed std::unique_ptr j1939Frame = J1939Factory::getInstance(). getJ1939Frame(frame.getId(), (const u8*)(frame.getData().c_str()), frame.getData().size()); if(!j1939Frame.get()) { //Frame not registered in the factory. if(showRaw) { std::unique_lock lock(rxLock); rxFrames["rx"][std::to_string(frame.getId())]["raw"] = frame.hexDump(); rcvFramesCache[frame.getId()] = frame; } return; } if(reassembler.toBeHandled(*j1939Frame)) { //Check if the frame is part of a fragmented frame (BAM protocol) //Actually it is, reassembler will handle it. reassembler.handleFrame(*j1939Frame); if(reassembler.reassembledFramesPending()) { j1939Frame = reassembler.dequeueReassembledFrame(); //For frames that have been decoded from BAM protocol. rxLock.lock(); rxFrames["rx"][std::to_string(j1939Frame->getIdentifier())]["count"] = ++rcvFramesCount[j1939Frame->getIdentifier()]; rxLock.unlock(); } else { return; //Frame handled by reassembler but the original frame to be reassembled is not complete. } } else { //Store in the history if(j1939Frame->isGenericFrame()) { GenericFrame *genFrame = static_cast(j1939Frame.get()); std::set spns = genFrame->compare(frame.getData(), rcvFramesCache[frame.getId()].getData()); for(auto iter = spns.begin(); iter != spns.end(); ++iter) { saveToHistory(j1939Frame->getIdentifier(), **iter, ts); } } //Only save in cache unfragmented frames to avoid filtering frames that are part of BAM protocol. //Frames whose length is bigger than 8 bytes are not cached, because the TX rate is usually several seconds. rcvFramesCache[frame.getId()] = frame; } //At this point we have either a simple frame or a reassembled frame. u32 j1939ID = j1939Frame->getIdentifier(); rxLock.lock(); rxFrames["rx"][std::to_string(j1939ID)]["frame"] = frameToJson(j1939Frame.get()); rxLock.unlock(); } bool onTimeout() { return true; } Json::Value frameToJson(const J1939Frame* frame) { Json::Value jsonVal; jsonVal["pgn"] = (u32)(frame->getPGN()); jsonVal["name"] = frame->getName(); jsonVal["priority"] = frame->getPriority(); jsonVal["source"] = frame->getSrcAddr(); //Only the first froup has destination address if(frame->getPDUFormatGroup() == PDU_FORMAT_1) { jsonVal["dest"] = frame->getDstAddr(); } //If generic frame, list SPNs if(frame->isGenericFrame()) { unsigned int j = 0; const GenericFrame *genFrame = static_cast(frame); std::set spnNumbers = genFrame->getSPNNumbers(); for(auto spnNumber = spnNumbers.begin(); spnNumber != spnNumbers.end(); ++spnNumber) { const SPN *spn = genFrame->getSPN(*spnNumber); jsonVal["spns"][j]["number"] = *spnNumber; jsonVal["spns"][j]["name"] = spn->getName(); jsonVal["spns"][j]["type"] = spn->getType(); switch(spn->getType()) { case SPN::SPN_NUMERIC: { const SPNNumeric *spnNum = static_cast(spn); jsonVal["spns"][j]["value"] = spnNum->getFormattedValue(); jsonVal["spns"][j]["units"] = spnNum->getUnits(); } break; case SPN::SPN_STATUS: { const SPNStatus *spnStat = static_cast(spn); jsonVal["spns"][j]["value"] = spnStat->getValue(); SPNStatus::DescMap descriptions = spnStat->getValueDescriptionsMap(); for(auto desc = descriptions.begin(); desc != descriptions.end(); ++desc) { jsonVal["spns"][j]["descriptions"][desc->first] = desc->second; } } break; case SPN::SPN_STRING: { const SPNString *spnStr = static_cast(spn); jsonVal["spns"][j]["value"] = spnStr->getValue(); } break; default: break; } ++j; } } if(frame->getPGN() == DM1_PGN) { const DM1* dm1 = static_cast(frame); const std::vector& dtcs = dm1->getDTCs(); unsigned int i = 0; for(auto dtc = dtcs.begin(); dtc != dtcs.end(); ++dtc) { jsonVal["dtcs"][i]["spn"] = dtc->getSpn(); jsonVal["dtcs"][i]["fmi"] = static_cast(dtc->getFmi()); jsonVal["dtcs"][i]["oc"] = static_cast(dtc->getOc()); ++i; } } return jsonVal; } void resetReceiver() { CanSniffer& sniffer = CanEasy::getSniffer(); //Stop receive thread to clean the cache of received frames. Not done the first time. if(rxThread.get()) { sniffer.finish(); rxThread->join(); rcvFramesCache.clear(); rcvFramesCount.clear(); } //Once the cache is cleaned or it it the first initialization, reinitialize a new thread sniffer.reset(); rxThread = std::unique_ptr(new std::thread([&sniffer](){ sniffer.sniff(1000); })); } ================================================ FILE: GUI_WEB/backend/src/graph.cpp ================================================ #include #include #include #include #include "graph.h" #include "graph.pb.h" Graph getGraphFromSPN(u32 id, u32 spn, u32 samples, u32 period, double time); using namespace J1939; using namespace Utils; std::unordered_map> 32*/,SPNHistory> historyMap; typedef struct { Graph graph; bool toSend; } UserData; TimeStamp startUpTime = TimeStamp::now(); void saveToHistory(u32 id, const SPN& spn, const TimeStamp& timestamp) { //Compose the key for the map. Key = canid + spnNumber u64 key = (u64)(id) | ((u64)(spn.getSpnNumber()) << 32); SPNHistory& history = historyMap[key]; history.addSample(TimeStamp::now() ,spn); } int callback_graph(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { switch (reason) { case LWS_CALLBACK_ESTABLISHED: { GOOGLE_PROTOBUF_VERIFY_VERSION; UserData *userData = new UserData(); *(static_cast(user)) = userData; } break; case LWS_CALLBACK_CLOSED: { UserData *userData = (*(static_cast(user))); delete userData; } break; case LWS_CALLBACK_RECEIVE: { Json::Value rcvjson; Json::Value respjson; Json::CharReaderBuilder builder; Json::CharReader *jSonReader = builder.newCharReader(); std::string errs; UserData *userData = (*(static_cast(user))); std::string graphRequest; graphRequest.append((char*)in, len); if(jSonReader->parse(graphRequest.c_str(), graphRequest.c_str() + graphRequest.size(), &rcvjson, &errs)) { //Verify if we received the whole Json string lwsl_info("Json request: %s\n", graphRequest.c_str()); graphRequest.clear(); if(rcvjson.isMember("command") && rcvjson["command"].isString()) { //Checks if graphis requested if(rcvjson["command"] == "get graph") { //Check corresponding fields if(rcvjson.isMember("id") && rcvjson["id"].isUInt() && rcvjson.isMember("spn") && rcvjson["spn"].isUInt() && rcvjson.isMember("samples") && rcvjson["samples"].isUInt() && rcvjson.isMember("period") && rcvjson["period"].isUInt() ) { if(rcvjson["samples"] > 10000) rcvjson["samples"] = 10000; //No more than 10000 samples double time = 0; if(rcvjson.isMember("time") && rcvjson["time"].isDouble()) { time = rcvjson["time"].asDouble(); } userData->graph = getGraphFromSPN(rcvjson["id"].asUInt(), rcvjson["spn"].asUInt(), rcvjson["samples"].asUInt(), rcvjson["period"].asUInt(), time); userData->toSend = true; lws_callback_on_writable_all_protocol(lws_get_context(wsi), lws_get_protocol(wsi)); } } } } delete jSonReader; } break; case LWS_CALLBACK_SERVER_WRITEABLE: { UserData *userData = (*(static_cast(user))); if(!userData->toSend) return 0; //We have to ensure that this callback is not called by the library with this flag. userData->toSend = false; std::string output; userData->graph.SerializeToString(&output); char *buff = new char[LWS_SEND_BUFFER_PRE_PADDING + output.size() + LWS_SEND_BUFFER_POST_PADDING]; memcpy(buff + LWS_SEND_BUFFER_PRE_PADDING, output.c_str(), output.size()); int written = lws_write(wsi, (unsigned char*)(buff + LWS_SEND_BUFFER_PRE_PADDING), output.size(), LWS_WRITE_BINARY); delete[] buff; } break; default: break; } return 0; } Graph getGraphFromSPN(u32 id, u32 spn, u32 number, u32 period, double time) { Graph graph; //Compose the key for the map. Key = canid + spnNumber u64 key = (u64)(id) | ((u64)(spn) << 32); auto iter = historyMap.find(key); //No history for SPN if(iter == historyMap.end()) return graph; SPNHistory& history = iter->second; //At the moment, only visualize Numeric SPN if(history.getNumericSpec() == nullptr) return graph; std::shared_ptr spec = history.getNumericSpec(); Axis *axisX = graph.mutable_axisx(); //Timestamps since epoch TimeStamp current; if(time > 0) { //A specific time has been requested //Time is relative to the start up of the program. Current is relative to the epoch time. //We need to add startUpTime. current = startUpTime + TimeStamp(time, (u32)(time * 1000000) % 1000000); } else { //Retrieve the last stored values current = TimeStamp::now(); } TimeStamp start = current - period; std::cout << "current. Sec: " << current.getSeconds() << "Micro: " << current.getMicroSec() << std::endl; //Timestamps since the beginning of the application TimeStamp relCurrent = current - startUpTime; TimeStamp relStart = start - startUpTime; //X axis which is the timestamp in seconds. axisX->set_units("s"); axisX->set_max((double)(relCurrent.getSeconds()) + (double)(relCurrent.getMicroSec()) /1000000); axisX->set_min((double)(relStart.getSeconds()) + (double)(relStart.getMicroSec()) /1000000); Axis *axisY = graph.mutable_axisy(); axisY->set_units(spec->getUnits()); axisY->set_max(spec->getMaxFormattedValue()); axisY->set_min(spec->getMinFormattedValue()); std::vector samples = history.getWindow(current, period, number); for(auto iter = samples.begin(); iter != samples.end(); ++iter) { Graph_Sample *sample = graph.add_samples(); TimeStamp relTime = iter->getTimeStamp() - startUpTime; sample->set_x((double)(relTime.getSeconds()) + (double)(relTime.getMicroSec()) /1000000); sample->set_y(iter->getNumeric()); } return graph; } ================================================ FILE: GUI_WEB/backend/src/graph.h ================================================ #ifndef GRAPH_H_ #define GRAPH_H_ extern "C" { #include } namespace J1939 { class SPN; }; namespace Utils { class TimeStamp; }; void saveToHistory(u32 id, const J1939::SPN& spn, const Utils::TimeStamp& timestamp); int callback_graph(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); #endif ================================================ FILE: GUI_WEB/frontend/graph.html ================================================ Graph Your browser does not support the HTML5 canvas tag. ================================================ FILE: GUI_WEB/frontend/index.html ================================================ J1939GUI
Transmission
  • {{ frame.name }} ({{ frame.pgn | hex | uppercase }}) Priority: Source: Dest: Period: {{ iface }}
    • SPN {{ spn.number }} ({{ spn.name }}) Value: {{ spn.units }}

Reception
  • {{ frame.name }} ({{ frame.pgn | hex | uppercase }}) Priority: {{ frame.priority }} Source: {{ frame.source }} Dest: {{ frame.dest }} Count: {{ frame.count }}
    SPN {{ spn.number }} ({{ spn.name }}) Value: {{ spn.descriptions[spn.value] }} ({{spn.value}}) {{ spn.value }} {{ spn.units }}
    DTC SPN: {{ dtc.spn }} FMI: {{ dtc.fmi }} OC: {{ dtc.oc }}
Show raw
    {{ id | hex }}: {{ raw.data }} Count: {{ raw.count }}
================================================ FILE: GUI_WEB/frontend/js/graph.js ================================================ var max_plots = 5000; function spn_graph(margin, dom_canvas_id) { this.graphRect = { x: margin, y: margin, w: window.innerWidth - 2*margin, h: window.innerHeight - 2*margin }; this.plots = []; this.canvas = document.getElementById(dom_canvas_id); this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; } spn_graph.prototype.set_plots = function(plots) { if(this.plots.length <= max_plots) { this.plots = plots; } } spn_graph.prototype.clear_plots = function(plots) { this.plots = []; } spn_graph.prototype.get_xspec = function() { return this.xspec; } spn_graph.prototype.get_graphRect = function() { return this.graphRect; } spn_graph.prototype.set_xspec = function(xspec) { //To change the minumum and maximum values of the x axis if(xspec.min < xspec.max) { this.xspec = xspec; } } spn_graph.prototype.set_yspec = function(yspec) { //To change the minumum and maximum values of the y axis if(yspec.min < yspec.max) { this.yspec = yspec; } } spn_graph.prototype.draw = function() { var ctx = this.canvas.getContext("2d"); var maxAxisX = this.xspec.max - this.xspec.min; var maxAxisY = this.yspec.max - this.yspec.min; var i; ctx.font = "20px Arial"; //Clear canvas for redrawing ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); ctx.lineWidth = 1; ctx.strokeStyle = "grey"; //Draw X axis ctx.beginPath(); ctx.moveTo(this.graphRect.x, this.graphRect.y); ctx.lineTo(this.graphRect.x, this.graphRect.y + this.graphRect.h); ctx.stroke(); //Draw x units ctx.fillText(this.xspec.units, 3/2 * this.graphRect.x + this.graphRect.w, this.graphRect.y + this.graphRect.h); //Draw y units ctx.fillText(this.yspec.units, this.graphRect.x/3, this.graphRect.y/2); ctx.font = "15px Arial"; //Draw some values along the y axis var numYVal = 5; for(i = 0; i <= numYVal; ++i) { ctx.fillText("" + (Math.round((i * maxAxisY / numYVal + this.yspec.min) * 100) / 100), this.graphRect.x/4, this.graphRect.y + this.graphRect.h * (1 - i/numYVal)); } //Draw Y axis ctx.beginPath(); ctx.moveTo(this.graphRect.x, this.graphRect.y + this.graphRect.h); ctx.lineTo(this.graphRect.x + this.graphRect.w, this.graphRect.y + this.graphRect.h); ctx.stroke(); //Draw some values along the x axis var numXVal = 7; for(i = 0; i <= numXVal; ++i) { ctx.fillText("" + (Math.round((i * maxAxisX / numXVal + this.xspec.min) * 1000) / 1000), this.graphRect.x + this.graphRect.w * i/numYVal, 3/2 * this.graphRect.y + this.graphRect.h); } //Draw plots ctx.lineWidth = 2; ctx.strokeStyle = "black"; for (i = 0; i < this.plots.length - 1; i++) { var offsets = { x1: this.plots[i][0] - this.xspec.min, y1: this.plots[i][1] - this.yspec.min, x2: this.plots[i+1][0] - this.xspec.min, y2: this.plots[i+1][1] - this.yspec.min, } if( offsets.x1 < 0 || offsets.x1 > maxAxisX ) continue; if( offsets.y1 < 0 || offsets.y1 > maxAxisY ) continue; if( offsets.x2 < 0 || offsets.x2 > maxAxisX ) continue; if( offsets.y2 < 0 || offsets.y2 > maxAxisY ) continue; var line = {}; line.x1 = this.graphRect.x + offsets.x1 * this.graphRect.w / maxAxisX; line.y1 = this.graphRect.y + this.graphRect.h - (offsets.y1 * this.graphRect.h / maxAxisY); line.x2 = this.graphRect.x + offsets.x2 * this.graphRect.w / maxAxisX; line.y2 = this.graphRect.y + this.graphRect.h - (offsets.y2 * this.graphRect.h / maxAxisY); //Draw line ctx.beginPath(); ctx.moveTo(line.x1, line.y1); ctx.lineTo(line.x2, line.y2); ctx.stroke(); } } ================================================ FILE: J1939/.cproject ================================================ ================================================ FILE: J1939/.gitignore ================================================ /Debug/ ================================================ FILE: J1939/.settings/org.eclipse.cdt.codan.core.prefs ================================================ eclipse.preferences.version=1 org.eclipse.cdt.codan.checkers.errnoreturn=Warning org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.checkers.errreturnvalue=Error org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.checkers.noreturn=Error org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},implicit\=>false} org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=-Error org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=-Error org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false} org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},unknown\=>false,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=-Error org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},skip\=>true} org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidArguments=-Error org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=-Error org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=-Error org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},pattern\=>"^[a-z]",macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.OverloadProblem=-Error org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=-Error org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>()} org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},paramNot\=>false} org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},else\=>false,afterelse\=>false} org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true} org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},macro\=>true,exceptions\=>("@(\#)","$Id")} org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=-Error org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true}} useParentScope=false ================================================ FILE: J1939/Addressing/AddressClaimFrame.cpp ================================================ /* * AdressClaimFrame.cpp * * Created on: Oct 17, 2017 * Author: famez */ #include #include #include #include #define ADDRESS_CLAIM_NAME "Address Claim" namespace J1939 { AddressClaimFrame::AddressClaimFrame() : J1939Frame(ADDRESS_CLAIM_PGN) { mName = ADDRESS_CLAIM_NAME; } AddressClaimFrame::AddressClaimFrame(EcuName name) : J1939Frame(ADDRESS_CLAIM_PGN), mEcuName(name) { mName = ADDRESS_CLAIM_NAME; } AddressClaimFrame::~AddressClaimFrame() { } void AddressClaimFrame::decodeData(const u8* buffer, size_t length) { if(length != ADDRESS_FRAME_LENGTH) { //Check the length first throw J1939DecodeException("[AdressClaimFrame::decodeData] Buffer length does " "not match the expected length. Buffer length:" + std::to_string(length) + ". Expected length: " + std::to_string(ADDRESS_FRAME_LENGTH)); } u32 idNumber = ( buffer[0] | (buffer[1] << 8) | ((buffer[2] & 0x1F) << 16)); u16 manufacturerCode = ( ((buffer[2] & 0xE0) >> 5) | (buffer[3] << 3) ); u8 ecuInstance = (buffer[4] & 0x07); u8 functionInstance = ((buffer[4] & 0xF8) >> 3); u8 function = buffer[5]; u8 vehicleSystem = (buffer[6] >> 1); u8 vehicleSystemInstance = (buffer[7] & 0x0F); u8 industryGroup = ((buffer[7] >> 4) & 0x07); bool arbitraryAddressCapable = (buffer[7] >> 7); mEcuName = EcuName(idNumber, manufacturerCode, ecuInstance, functionInstance, function, vehicleSystem, vehicleSystemInstance, industryGroup, arbitraryAddressCapable); } void AddressClaimFrame::encodeData(u8* buffer, size_t length) const { buffer[0] = (mEcuName.getIdNumber() & 0xFF); buffer[1] = ((mEcuName.getIdNumber() >> 8) & 0xFF); buffer[2] = ((mEcuName.getIdNumber() >> 16) & 0x1F) | ((mEcuName.getManufacturerCode() << 5) & 0xFF); buffer[3] = ((mEcuName.getManufacturerCode() >> 3) & 0xFF); buffer[4] = ((mEcuName.getEcuInstance() & ECU_INSTANCE_MASK) | ((mEcuName.getFunctionInstance() << 3) & 0xFF)); buffer[5] = mEcuName.getFunction(); buffer[6] = (mEcuName.getVehicleSystem() << 1); buffer[7] = (mEcuName.getVehicleSystemInstance() & VEHICLE_SYSTEM_INTERFACE_MASK) | ((mEcuName.getIndustryGroup() & INDUSTRY_GROUP_MASK) << 4) | ((mEcuName.isArbitraryAddressCapable() ? 1 : 0) << 7); } std::string AddressClaimFrame::toString() const { std::string retVal = J1939Frame::toString(); std::stringstream sstr; sstr << "Id Number: " << mEcuName.getIdNumber() << std::endl; sstr << "Manufacturer Code: " << static_cast(mEcuName.getManufacturerCode()) << std::endl; sstr << "ECU Intance: " << static_cast(mEcuName.getEcuInstance()) << std::endl; sstr << "Function Instance: " << static_cast(mEcuName.getFunctionInstance()) << std::endl; sstr << "Function: " << static_cast(mEcuName.getFunction()) << std::endl; sstr << "Vehicle System: " << static_cast(mEcuName.getVehicleSystem()) << std::endl; sstr << "Vehicle System Instance: " << static_cast(mEcuName.getVehicleSystemInstance()) << std::endl; sstr << "Industry Group: " << static_cast(mEcuName.getIndustryGroup()) << std::endl; sstr << "Address Capable: " << (mEcuName.isArbitraryAddressCapable() ? "yes" : "no") << std::endl; return retVal + sstr.str(); } } /* namespace J1939 */ ================================================ FILE: J1939/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(J1939) add_library(J1939 SHARED ./J1939Common.cpp ./Diagnosis/Frames/DM1.cpp ./Diagnosis/DTC.cpp ./FMS/TellTale/TellTale.cpp ./FMS/TellTale/FMS1Frame.cpp ./J1939Factory.cpp ./GenericFrame.cpp ./SPN/SPN.cpp ./SPN/SPNString.cpp ./SPN/SPNStatus.cpp ./SPN/SPNNumeric.cpp ./SPN/SPNSpec/SPNSpec.cpp ./SPN/SPNSpec/SPNNumericSpec.cpp ./SPN/SPNSpec/SPNStatusSpec.cpp ./SPN/SPNHistory.cpp ./J1939DataBase.cpp ./J1939Frame.cpp ./Addressing/AddressClaimFrame.cpp ./Frames/RequestFrame.cpp ./Transport/TPCMFrame.cpp ./Transport/BAM/BamReassembler.cpp ./Transport/BAM/BamFragmenter.cpp #./Transport/RTSCTS/RTSCTSConnectionManager.cpp ./Transport/TPDTFrame.cpp ) target_include_directories(J1939 PRIVATE include ${Common_SOURCE_DIR}/include ) target_link_libraries(J1939 PUBLIC Common jsoncpp ) install (TARGETS J1939 EXPORT J1939FrameworkTargets LIBRARY DESTINATION lib) install(DIRECTORY include/ DESTINATION include COMPONENT Devel ) ================================================ FILE: J1939/Diagnosis/DTC.cpp ================================================ /* * DTC.cpp * * Created on: Sep 2, 2018 * Author: fernado */ #include #include #include #include namespace J1939 { DTC::DTC() : mSPN(0), mFMI(0), mOC(0) { } DTC::DTC(u32 spn, u8 fmi, u8 oc) : mSPN(spn), mFMI(fmi), mOC(oc) { } DTC::~DTC() { } void DTC::decode(const u8* buffer) { if(buffer[3] & DTC_CM_MASK) { throw J1939DecodeException("Unknown conversion method"); } mSPN = buffer[0]; mSPN |= (buffer[1] << 8); mSPN |= ((buffer[2] & (~DTC_FMI_MASK)) << 11); mFMI = (buffer[2] & DTC_FMI_MASK); mOC = (buffer[3] & DTC_OC_MASK); } void DTC::encode(u8* buffer) const { buffer[0] = mSPN & 0xFF; buffer[1] = (mSPN >> 8) & 0xFF; buffer[2] = (mSPN >> 11) & (~DTC_FMI_MASK); buffer[2] |= (mFMI & DTC_FMI_MASK); buffer[3] = (mOC & DTC_OC_MASK) & (~DTC_CM_MASK); } std::string DTC::toString() const { std::stringstream sstr; sstr << "Diagnosis Trouble Code -> Spn: " << mSPN << " Failure Mode Identifier: " << static_cast(mFMI) << "Occurrence Count: " << static_cast(mOC) << std::endl; return sstr.str(); } } /* namespace J1939 */ ================================================ FILE: J1939/Diagnosis/Frames/DM1.cpp ================================================ /* * DM1.cpp * * Created on: Oct 15, 2017 * Author: famez */ #include #include #include #include #define DM1_MINIMUM_LENGTH 8 namespace J1939 { DM1::DM1() : GenericFrame(DM1_PGN) { { SPNStatus lampStatus(1213, "Malfunction indicator Lamp Status", 0, 6, 2); registerSPN(lampStatus); } { SPNStatus lampStatus(623, "Red Stop Lamp Status", 0, 4, 2); registerSPN(lampStatus); } { SPNStatus lampStatus(624, "Amber Warning Lamp Status", 0, 2, 2); registerSPN(lampStatus); } { SPNStatus lampStatus(987, "Protect Lamp Status", 0, 0, 2); registerSPN(lampStatus); } { SPNStatus lampStatus(3038, "Flash Malfunction indicator Lamp Status", 1, 6, 2); registerSPN(lampStatus); } { SPNStatus lampStatus(3039, "Flash Red Stop Lamp Status", 1, 4, 2); registerSPN(lampStatus); } { SPNStatus lampStatus(3040, "Flash Amber Warning Lamp Status", 1, 2, 2); registerSPN(lampStatus); } { SPNStatus lampStatus(3041, "Flash Protect Lamp Status", 1, 0, 2); registerSPN(lampStatus); } mName = DM1_NAME; } DM1::~DM1() { } void DM1::decodeData(const u8* buffer, size_t length) { size_t lampStatLength = GenericFrame::getDataLength(); //Decode Lamp Status (SPNs) GenericFrame::decodeData(buffer, lampStatLength); size_t offset = lampStatLength; DTC dtc; while(offset + DTC_SIZE <= length) { dtc.decode(buffer + offset); //To avoid adding a DTC when there are no faults (a DTC set all to 0s is sent which is not a valid DTC) if(dtc.getSpn() != 0) { mDtcs.push_back(dtc); } offset += DTC_SIZE; } } void DM1::encodeData(u8* buffer, size_t length) const { //Encode SPNs for bytes 0-1 size_t lampStatLength = GenericFrame::getDataLength(); GenericFrame::encodeData(buffer, lampStatLength); size_t offset = lampStatLength; //Must be 2 if(lampStatLength != 2) { throw J1939EncodeException("[DM1::encodeData] SPNs are not expected to fit within " "more than 2 bytes"); } for(auto dtc = mDtcs.begin(); dtc != mDtcs.end(); ++dtc) { dtc->encode(buffer + offset); offset += DTC_SIZE; } //According to the protocol, if there are no DTCs, we need to send the bytes 2-5 as 00 and bytes 6-7 as FF. if(mDtcs.empty()) { memset(buffer + offset, 0x00, 4); memset(buffer + offset + 4, 0xFF, 2); } } size_t DM1::getDataLength() const { //The length of DM1 frame is at least 8 bytes return J1939_MAX(GenericFrame::getDataLength() + DTC_SIZE * mDtcs.size(), DM1_MINIMUM_LENGTH); } std::string DM1::toString() const { std::string retVal = GenericFrame::toString(); for(auto dtc = mDtcs.begin(); dtc != mDtcs.end(); ++dtc) { retVal += dtc->toString(); } return retVal; } void DM1::copy(const J1939Frame& other) { GenericFrame::copy(other); //Verify that we deal with a DM1 frame if(other.getPGN() == getPGN()) { const DM1 *otherDM1 = static_cast(&other); mDtcs = otherDM1->mDtcs; } } } /* namespace J1939 */ ================================================ FILE: J1939/FMS/TellTale/FMS1Frame.cpp ================================================ /* * FMS1Frame.cpp * * Created on: Mar 11, 2018 * Author: famez */ #include #include #include #include #include #include #define FMS1_NAME "FMS1" namespace J1939 { FMS1Frame::FMS1Frame() : J1939Frame(FMS1_PGN), mBlockID(NUMBER_OF_BLOCKS) { mName = FMS1_NAME; } FMS1Frame::FMS1Frame(u8 blockID) : J1939Frame(FMS1_PGN), mBlockID(blockID) { mName = FMS1_NAME; ASSERT(mBlockID < NUMBER_OF_BLOCKS); for(u8 i = mBlockID * TTSS_PER_BLOCK + 1; i < (mBlockID + 1) * TTSS_PER_BLOCK + 1; ++i) { mTTSs[i] = TellTale(i, TellTale::TTS_STATUS_NOT_AVAILABLE); } } FMS1Frame::~FMS1Frame() { } void FMS1Frame::decodeData(const u8* buffer, size_t length) { if(length != FMS1_FRAME_LENGTH) { //Check the length first throw J1939DecodeException("[FMS1Frame::decodeData] Buffer length does " "not match the expected length. Buffer length:" + std::to_string(length) + ". Expected length: " + std::to_string(FMS1_FRAME_LENGTH)); } u8 blockID = buffer[0] & BLOCKID_MASK; if(blockID >= NUMBER_OF_BLOCKS) { throw J1939DecodeException(std::string("[FMS1Frame::decodeData] Block ID higher than the maximum permitted. Max: ") + std::to_string(NUMBER_OF_BLOCKS -1)); } //If block ID changes, clear mTTSs to not accumulate the previous decoded TTSs //related to the previous block. if(mBlockID != blockID) { mTTSs.clear(); } mBlockID = blockID; u8 tts1Number = TTSS_PER_BLOCK * mBlockID + 1; TellTale tts1(tts1Number, (buffer[0] >> TTS_HIGH_PART_SHIFT) & TTS_MASK); mTTSs[tts1Number] = tts1; for(unsigned int i = 1; i < FMS1_FRAME_LENGTH; ++i) { u8 ttsLowPartNumber = TTSS_PER_BLOCK * mBlockID + 2*i; u8 ttsHighPartNumber = TTSS_PER_BLOCK * mBlockID + 2*i + 1; u8 ttsLowPartStatus = buffer[i] & TTS_MASK; u8 ttsHighPartStatus = (buffer[i] >> TTS_HIGH_PART_SHIFT) & TTS_MASK; mTTSs[ttsLowPartNumber] = TellTale(ttsLowPartNumber, ttsLowPartStatus); mTTSs[ttsHighPartNumber] = TellTale(ttsHighPartNumber, ttsHighPartStatus); } } void FMS1Frame::encodeData(u8* buffer, size_t length) const { //Not necessary to check length if getDataLength() returns the proper value as the base class will already do the check if(mTTSs.size() != TTSS_PER_BLOCK) { //Check if we have the right number of TTSs. throw J1939EncodeException("[FMS1Frame::encodeData] There are not " + std::to_string(TTSS_PER_BLOCK) + " defined"); } //Check if the number for every TTS is the right one. if(mTTSs.begin()->first <= mBlockID * TTSS_PER_BLOCK || mTTSs.rbegin()->first > (mBlockID + 1) * TTSS_PER_BLOCK) { throw J1939EncodeException("[FMS1Frame::encodeData] TTS numbers are not the proper ones for this block"); } u8 tts1Number = TTSS_PER_BLOCK * mBlockID + 1; buffer[0] = (mBlockID & BLOCKID_MASK) | ((mTTSs.at(tts1Number).getStatus() | TTS_ENCODING_MASK) << TTS_HIGH_PART_SHIFT); for(unsigned int i = 1; i < FMS1_FRAME_LENGTH; ++i) { u8 ttsLowPartNumber = TTSS_PER_BLOCK * mBlockID + 2*i; u8 ttsHighPartNumber = TTSS_PER_BLOCK * mBlockID + 2*i + 1; buffer[i] = (mTTSs.at(ttsLowPartNumber).getStatus() | TTS_ENCODING_MASK) | ((mTTSs.at(ttsHighPartNumber).getStatus() | TTS_ENCODING_MASK) << TTS_HIGH_PART_SHIFT); } } std::string FMS1Frame::toString() const { std::string retVal = J1939Frame::toString(); std::stringstream sstr; sstr << "Block ID: " << static_cast(mBlockID) << std::endl; for(auto tts = mTTSs.begin(); tts != mTTSs.end(); ++tts) { sstr << tts->second.toString(); } return retVal + sstr.str(); } bool FMS1Frame::hasTTS(u8 number) { return (mTTSs.find(number) != mTTSs.end()); } TellTale FMS1Frame::getTTS(u8 number) { auto iter = mTTSs.find(number); if(iter != mTTSs.end()) { return iter->second; } return TellTale(number, TellTale::TTS_STATUS_NOT_AVAILABLE); } bool FMS1Frame::setTTS(u8 number, u8 status) { auto iter = mTTSs.find(number); //If tts number does not belong to FMS1 frame, the tts is not set if(iter != mTTSs.end()) { iter->second = TellTale(number, status); return true; } return false; } } ================================================ FILE: J1939/FMS/TellTale/TellTale.cpp ================================================ /* * TellTale.cpp * * Created on: Mar 11, 2018 * Author: famez */ #include #include TellTale::TellTale() : mNumber(0), mStatus(0) { } TellTale::TellTale(u8 number, u8 status) : mNumber(number), mStatus(status) { } TellTale::~TellTale() { } std::string TellTale::toString() const { std::stringstream sstr; sstr << "TTS " << static_cast(mNumber) << ": " << TellTale::getNameForTTSNumber(mNumber) << " -> Status: " << TellTale::getSatusname(mStatus) << " (" << static_cast(mStatus) << ")" << std::endl; return sstr.str(); } std::map TellTale::initializeNTNMap() { std::map retVal; retVal[1] = "Cooling Air Conditioning"; retVal[2] = "High beam, main beam"; retVal[3] = "Low beam, dipped beam"; retVal[4] = "Turn Signals"; retVal[5] = "Hazard Warning"; retVal[6] = "Provision for the disabled or handicapped persons"; retVal[7] = "Parking Brake"; retVal[8] = "Brake failure/brake system malfunction"; retVal[9] = "Hatch open"; retVal[10] = "Fuel level"; retVal[11] = "Engine coolant temperature"; retVal[12] = "Battery charging condition"; retVal[13] = "Engine oil"; retVal[14] = "Position lights,side lights"; retVal[15] = "Front fog light"; retVal[16] = "Rear fog light"; retVal[17] = "Park Heating"; retVal[18] = "Engine / Mil indicator"; retVal[19] = "Service, call for maintenance"; retVal[20] = "Transmission fluid temperature"; retVal[21] = "Transmission failure/malfunction"; retVal[22] = "Anti-lock brake system failure"; retVal[23] = "Worn brake linings"; retVal[24] = "Windscreen washer fluid/windshield"; retVal[25] = "Tire failure/malfunction"; retVal[26] = "Malfunction/general failure"; retVal[27] = "Engine oil temperature"; retVal[28] = "Engine oil level"; retVal[29] = "Engine coolant level"; retVal[30] = "Steering fluid level"; retVal[31] = "Steering failure"; retVal[32] = "Height Control (Levelling)"; retVal[33] = "Retarder"; retVal[34] = "Engine Emission system failure (Mil indicator)"; retVal[35] = "ESC indication"; retVal[36] = "Brake lights"; retVal[37] = "Articulation"; retVal[38] = "Stop Request"; retVal[39] = "Pram request"; retVal[40] = "Bus stop brake"; retVal[41] = "AdBlue level"; retVal[42] = "Raising"; retVal[43] = "Lowering"; retVal[44] = "Kneeling"; retVal[45] = "Engine compartment temperature"; retVal[46] = "Auxillary air pressure"; retVal[47] = "Air filter clogged"; retVal[48] = "Fuel filter differential pressure"; retVal[49] = "Seat belt"; retVal[50] = "EBS"; retVal[51] = "Lane departure indication"; retVal[52] = "Advanced emergency braking system"; retVal[53] = "ACC"; retVal[54] = "Trailer connected washer fluid"; retVal[55] = "ABS Trailer"; retVal[56] = "Airbag"; retVal[57] = "EBS Trailer"; retVal[58] = "Tachograph indication"; retVal[59] = "ESC switched off"; retVal[60] = "Lane departure warning switched off"; return retVal; } std::map TellTale::initializeSNMap() { std::map retVal; retVal[TTS_STATUS_OFF] = "OFF"; retVal[TTS_STATUS_RED] = "RED"; retVal[TTS_STATUS_YELLOW] = "YELLOW"; retVal[TTS_STATUS_INFO] = "INFO"; retVal[TTS_STATUS_NOT_AVAILABLE] = "NOT AVAILABLE"; return retVal; } std::map TellTale::mNumberToName = initializeNTNMap(); std::map TellTale::mStatusName = initializeSNMap(); ================================================ FILE: J1939/Frames/RequestFrame.cpp ================================================ /* * RequestFrame.cpp * * Created on: Dec 20, 2018 * Author: famez */ #include #include #include #include #include #define REQUEST_FRAME_NAME "Request" namespace J1939 { RequestFrame::RequestFrame() : J1939Frame(REQUEST_PGN) { mName = REQUEST_FRAME_NAME; } RequestFrame::RequestFrame(u32 requestPGN) : RequestFrame() { mRequestPGN = requestPGN; } RequestFrame::~RequestFrame() { } void RequestFrame::decodeData(const u8* buffer, size_t length) { if(length != REQUEST_FRAME_LENGTH) { //Check the length first throw J1939DecodeException("[RequestFrame::decodeData] Buffer length does " "not match the expected length. Buffer length:" + std::to_string(length) + ". Expected length: " + std::to_string(REQUEST_FRAME_LENGTH)); } mRequestPGN = buffer[0]; mRequestPGN |= (buffer[1] << 8); mRequestPGN |= (buffer[2] << 16); mRequestPGN &= J1939_PGN_MASK; } void RequestFrame::encodeData(u8* buffer, size_t length) const { buffer[0] = (mRequestPGN & 0xFF); buffer[1] = ((mRequestPGN >> 8) & 0xFF); buffer[2] = ((mRequestPGN >> 16) & (J1939_PGN_MASK >> 16)); } std::string RequestFrame::toString() const { std::string retVal = J1939Frame::toString(); std::stringstream sstr; //Try to get information related to the PGN. std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(mRequestPGN); sstr << "Request PGN: " << std::hex << mRequestPGN << " " << (frame.get() ? frame->getName() : "") << std::endl; return retVal + sstr.str(); } } /* namespace J1939 */ ================================================ FILE: J1939/GenericFrame.cpp ================================================ /* * GenericFrame.cpp * * Created on: Nov 3, 2017 * Author: root */ #include #include #include #include #include #include #include "GenericFrame.h" namespace J1939 { GenericFrame::GenericFrame(u32 pgn) : J1939Frame(pgn), mLength(0) { } GenericFrame::GenericFrame(const GenericFrame& other) : J1939Frame(other), mLength(other.mLength) { for(auto spn = other.mSPNs.begin(); spn != other.mSPNs.end(); ++spn) { mSPNs[spn->first] = spn->second->clone(); mSPNs[spn->first]->setOwner(this); } } GenericFrame::~GenericFrame() { for(auto spn = mSPNs.begin(); spn != mSPNs.end(); ++spn) { delete spn->second; } } void GenericFrame::recalculateStringOffsets() { for(auto spn = mSPNs.begin(); spn != mSPNs.end(); ++spn) { auto nextSpn = spn; ++nextSpn; if(nextSpn != mSPNs.end()) { nextSpn->second->setOffset(spn->second->getOffset() + spn->second->getByteSize()); } } } void GenericFrame::decodeData(const u8* buffer, size_t length) { const u8* spnBuf; size_t offset; for(auto spn = mSPNs.begin(); spn != mSPNs.end(); ++spn) { offset = spn->second->getOffset(); if(offset >= length) { throw J1939DecodeException("[GenericFrame::decodeData] Offset of spn is higher than frame length"); } spnBuf = buffer + offset; spn->second->decode(spnBuf, length - offset); } } void GenericFrame::encodeData(u8* buffer, size_t length) const { u8* spnBuf; size_t offset; for(auto spn = mSPNs.begin(); spn != mSPNs.end(); ++spn) { offset = spn->second->getOffset(); if(offset >= length) { throw J1939EncodeException("[GenericFrame::encodeData] Offset of spn is higher than frame length: SPN number: " + std::to_string(spn->second->getSpnNumber()) + " offset: " + std::to_string(offset) + " length: " + std::to_string(length)); } spnBuf = buffer + offset; spn->second->encode(spnBuf, length - offset); } } size_t GenericFrame::getDataLength() const { size_t maxOffset = 0; size_t sizeLastSpn = 1; for(auto spn = mSPNs.rbegin(); spn != mSPNs.rend(); ++spn) { if(maxOffset > spn->second->getOffset()) continue; maxOffset = spn->second->getOffset(); sizeLastSpn = spn->second->getByteSize(); } //If we have specified a length, return the maximum value between the real size and the specified one. //This is done if not all spns for this frame are defined and we can have a length smaller than the one necessary to transmit the frame. return J1939_MAX(mLength, maxOffset + sizeLastSpn); } SPN *GenericFrame::registerSPN(const SPN& spn) { SPN* retVal = NULL; auto spnIter = mSPNs.find(spn.getSpnNumber()); //Assertion to ensure that a generic frame only contains //either SPNs of type string or SPNs of another type than string ASSERT(mSPNs.empty() ? true : ((mSPNs.begin()->second->getType() == SPN::SPN_STRING) == (spn.getType() == SPN::SPN_STRING))); if(spnIter == mSPNs.end()) { retVal = spn.clone(); retVal->setOwner(this); mSPNs[spn.getSpnNumber()] = retVal; } else { retVal = spnIter->second; } if(spn.getType() == SPN::SPN_STRING) { recalculateStringOffsets(); } return retVal; } SPN* GenericFrame::getSPN(u32 number) { SPN* ret = NULL; auto spn = mSPNs.find(number); if(spn != mSPNs.end()) { return spn->second; } return ret; } const SPN* GenericFrame::getSPN(u32 number) const { SPN* ret = NULL; auto spn = mSPNs.find(number); if(spn != mSPNs.end()) { return spn->second; } return ret; } std::set GenericFrame::getSPNNumbers() const { std::set ret; for(auto iter = mSPNs.begin(); iter != mSPNs.end(); ++iter) { ret.insert(iter->first); } return ret; } bool GenericFrame::deleteSPN(u32 number) { auto iter = mSPNs.find(number); if(iter != mSPNs.end()) { delete iter->second; mSPNs.erase(iter); return true; } return false; } std::string GenericFrame::toString() const { std::string retVal = J1939Frame::toString(); for(auto iter = mSPNs.begin(); iter != mSPNs.end(); ++iter) { retVal += iter->second->toString(); } return retVal; } std::set GenericFrame::compare(const std::string& newData, const std::string oldData) { std::set retVal; for(auto iter = mSPNs.begin(); iter != mSPNs.end(); ++iter) { size_t maxLength = iter->second->getOffset() + iter->second->getByteSize(); if(newData.size() >= maxLength || oldData.size() >= maxLength) { if(memcmp(newData.c_str() + iter->second->getOffset(), oldData.c_str() + iter->second->getOffset(), iter->second->getByteSize()) != 0) { //SPNs differ retVal.insert(iter->second); } } else { //If length not respected we consider the SPN has changed //(there are missing bytes in one of the strings) retVal.insert(iter->second); } } return retVal; } void GenericFrame::copy(const J1939Frame& other) { if(!other.isGenericFrame()) { //Nothing to do return; } const GenericFrame *genOther = static_cast(&other); auto otherIter = genOther->mSPNs.begin(); for(auto iter = mSPNs.begin(); iter != mSPNs.end(); ++iter) { if(otherIter == genOther->mSPNs.end() || otherIter->second->getSpec() != iter->second->getSpec()) { return; } iter->second->copy(*(otherIter->second)); ++otherIter; } } } /* namespace J1939 */ ================================================ FILE: J1939/J1939Common.cpp ================================================ #include "J1939Common.h" ================================================ FILE: J1939/J1939DataBase.cpp ================================================ /* * JsonParser.cpp * * Created on: Nov 7, 2017 * Author: root */ #include "J1939DataBase.h" #include #include #include #include #include "GenericFrame.h" #include "SPN/SPNNumeric.h" #include "SPN/SPNStatus.h" #include "SPN/SPNString.h" #include #define PGN_KEY "pgn" #define SPNS_KEY "spns" #define NAME_KEY "name" #define NUMBER_KEY "number" #define TYPE_KEY "type" #define OFFSET_KEY "offset" #define LENGTH_KEY "length" #define NUM_FORMAT_GAIN_KEY "formatGain" #define NUM_FORMAT_OFFSET_KEY "formatOffset" #define NUM_BYTE_SIZE_KEY "byteSize" #define NUM_UNITS_KEY "units" #define STAT_BIT_OFFSET_KEY "bitOffset" #define STAT_BIT_SIZE_KEY "bitSize" #define STAT_DESCRIPTIONS_KEY "descriptions" namespace J1939 { J1939DataBase::J1939DataBase() : mErrorCode(ERROR_OK) { } J1939DataBase::~J1939DataBase() { } bool J1939DataBase::parseJsonFile(const std::string& file) { Json::Value data; std::ifstream fileStream(file.c_str()); if(!fileStream.is_open()) { mErrorCode = ERROR_FILE_NOT_FOUND; return false; } Json::CharReaderBuilder rbuilder; std::string errs; if(!Json::parseFromStream(rbuilder, fileStream, &data, &errs)) { mErrorCode = ERROR_JSON_SYNTAX; return false; } //If we return in the middle of the method, there was a problem with some of the tokens mErrorCode = ERROR_UNEXPECTED_TOKENS; if(!data.isArray()) { return false; } for(unsigned int i = 0; i < data.size(); ++i) { const Json::Value& jsonFrame = data[i]; if(!jsonFrame.isMember(PGN_KEY) || !jsonFrame[PGN_KEY].isUInt()) { return false; } u32 pgn = jsonFrame[PGN_KEY].asUInt(); switch(pgn) { case DM1_PGN: //Not permitted this token. Dangerous for the good performance of the software. mErrorCode = ERROR_FORBIDDEN_TOKEN; return false; default: break; } GenericFrame frame(pgn); if(jsonFrame.isMember(NAME_KEY) && jsonFrame[NAME_KEY].isString()) { //Optional frame.setName(jsonFrame[NAME_KEY].asString()); } if(jsonFrame.isMember(LENGTH_KEY) && jsonFrame[LENGTH_KEY].isUInt()) { //Optional frame.setLength(jsonFrame[LENGTH_KEY].asUInt()); } if(jsonFrame.isMember(SPNS_KEY)) { const Json::Value& spns = jsonFrame[SPNS_KEY]; if(!spns.isArray()) { return false; } for(unsigned int j = 0; j < spns.size(); ++j) { const Json::Value& spn = spns[j]; if(!spn.isMember(NAME_KEY) || !spn.isMember(NUMBER_KEY) || !spn.isMember(TYPE_KEY)) { return false; } const Json::Value& name = spn[NAME_KEY]; const Json::Value& number = spn[NUMBER_KEY]; const Json::Value& type = spn[TYPE_KEY]; const Json::Value offset = spn[OFFSET_KEY]; if(!name.isString() || !number.isUInt() || !type.isUInt()) { return false; } switch(type.asUInt()) { case SPN::SPN_NUMERIC: if(!parseSPNNumeric(frame, spn)) { return false; } break; case SPN::SPN_STATUS: if(!parseSPNStatus(frame, spn)) { return false; } break; case SPN::SPN_STRING: if(!parseSPNString(frame, spn)) { return false; } break; default: mErrorCode = ERROR_UNKNOWN_SPN_TYPE; return false; break; } } } mFrames.push_back(frame); } mErrorCode = ERROR_OK; return true; } bool J1939DataBase::writeJsonFile(const std::string& file) { Json::Value data; std::ofstream ofs (file.c_str(), std::ofstream::out | std::ofstream::trunc); if(!ofs.is_open()) { return false; } unsigned int pos; for(std::vector::const_iterator frame = mFrames.begin(); frame != mFrames.end(); ++frame) { pos = frame - mFrames.begin(); Json::Value& jsonFrame = data[pos]; jsonFrame[PGN_KEY] = frame->getPGN(); jsonFrame[NAME_KEY] = frame->getName(); jsonFrame[LENGTH_KEY] = frame->getDataLength(); std::set spns = frame->getSPNNumbers(); if(!spns.empty()) { Json::Value& jsonSPNs = jsonFrame[SPNS_KEY]; int i = 0; for(std::set::const_iterator iter = spns.begin(); iter != spns.end(); ++iter) { Json::Value& jsonSPN = jsonSPNs[i++]; const SPN* spn = frame->getSPN(*iter); jsonSPN[TYPE_KEY] = spn->getType(); jsonSPN[NUMBER_KEY] = spn->getSpnNumber(); jsonSPN[NAME_KEY] = spn->getName(); jsonSPN[OFFSET_KEY] = spn->getOffset(); switch(spn->getType()) { case SPN::SPN_NUMERIC: writeSPNNumeric(spn, jsonSPN); break; case SPN::SPN_STATUS: writeSPNStatus(spn, jsonSPN); break; default: break; } } } } ofs << data; return true; } const std::vector& J1939DataBase::getParsedFrames() const { return mFrames; } bool J1939DataBase::parseSPNNumeric(GenericFrame& frame, const Json::Value& spn) { if(!spn.isMember(NUM_FORMAT_GAIN_KEY) || !spn.isMember(NUM_FORMAT_OFFSET_KEY) || !spn.isMember(NUM_BYTE_SIZE_KEY) || !spn.isMember(NUM_UNITS_KEY) || !spn.isMember(OFFSET_KEY)) { return false; } const Json::Value& name = spn[NAME_KEY]; const Json::Value& number = spn[NUMBER_KEY]; const Json::Value& offset = spn[OFFSET_KEY]; const Json::Value& formatGain = spn[NUM_FORMAT_GAIN_KEY]; const Json::Value& formatOffset = spn[NUM_FORMAT_OFFSET_KEY]; const Json::Value& byteSize = spn[NUM_BYTE_SIZE_KEY]; const Json::Value& units = spn[NUM_UNITS_KEY]; if(!formatGain.isDouble() || !formatOffset.isDouble() || !byteSize.isUInt() || !units.isString() || !offset.isUInt()) { return false; } //Check if bytesize does not exceed the permitted values if(byteSize.asUInt() > SPN_NUMERIC_MAX_BYTE_SYZE || byteSize.asUInt() == 0) { mErrorCode = ERROR_OUT_OF_RANGE; return false; } SPNNumeric spnNum(number.asUInt(), name.asString(), offset.asUInt(), formatGain.asDouble(), formatOffset.asDouble(), byteSize.asUInt(), units.asString()); frame.registerSPN(spnNum); return true; } bool J1939DataBase::parseSPNStatus(GenericFrame& frame, const Json::Value& spn) { if(!spn.isMember(STAT_BIT_OFFSET_KEY) || !spn.isMember(STAT_BIT_SIZE_KEY) || !spn.isMember(OFFSET_KEY)) { return false; } const Json::Value& name = spn[NAME_KEY]; const Json::Value& number = spn[NUMBER_KEY]; const Json::Value& offset = spn[OFFSET_KEY]; const Json::Value& bitOffset = spn[STAT_BIT_OFFSET_KEY]; const Json::Value& bitSize = spn[STAT_BIT_SIZE_KEY]; if(!bitOffset.isUInt() || !bitSize.isUInt() || !offset.isUInt()) { return false; } //Check the limits of the input values if(bitSize.asUInt() == 0 || (bitSize.asUInt() + bitOffset.asUInt()) > 8) { mErrorCode = ERROR_OUT_OF_RANGE; return false; } //Check for added descriptions SPNStatusSpec::DescMap valueToDesc; if(spn.isMember(STAT_DESCRIPTIONS_KEY)) { const Json::Value& descriptions = spn[STAT_DESCRIPTIONS_KEY]; for(unsigned int i = 0; i < descriptions.size(); ++i) { if(descriptions[i].isNull()){ continue; } valueToDesc[i] = descriptions[i].asString(); } } SPNStatus spnStat(number.asUInt(), name.asString(), offset.asUInt(), bitOffset.asUInt(), bitSize.asUInt(), valueToDesc); frame.registerSPN(spnStat); return true; } bool J1939DataBase::parseSPNString(GenericFrame& frame, const Json::Value& spn) { const Json::Value& name = spn[NAME_KEY]; const Json::Value& number = spn[NUMBER_KEY]; SPNString spnStr(number.asUInt(), name.asString()); frame.registerSPN(spnStr); return true; } void J1939DataBase::writeSPNNumeric(const SPN* spn, Json::Value& jsonSpn) { const SPNNumeric* spnNum = (SPNNumeric*)spn; jsonSpn[NUM_BYTE_SIZE_KEY] = spnNum->getByteSize(); jsonSpn[NUM_FORMAT_GAIN_KEY] = spnNum->getFormatGain(); jsonSpn[NUM_FORMAT_OFFSET_KEY] = spnNum->getFormatOffset(); jsonSpn[NUM_UNITS_KEY] = spnNum->getUnits(); } void J1939DataBase::writeSPNStatus(const SPN* spn, Json::Value& jsonSpn) { const SPNStatus* spnStat = (SPNStatus*)spn; jsonSpn[STAT_BIT_OFFSET_KEY] = spnStat->getBitOffset(); jsonSpn[STAT_BIT_SIZE_KEY] = spnStat->getBitSize(); //Check if there are descriptions defined for the different status numbers SPNStatus::DescMap descriptions = spnStat->getValueDescriptionsMap(); if(descriptions.empty()) { return; } Json::Value& description = jsonSpn[STAT_DESCRIPTIONS_KEY]; for(auto iter = descriptions.begin(); iter != descriptions.end(); ++iter) { description[iter->first] = iter->second; } } void J1939DataBase::addFrame(const GenericFrame& frame) { mFrames.push_back(frame); } void J1939DataBase::clear() { mFrames.clear(); mErrorCode = ERROR_OK; } } /* namespace J1939 */ //Json::CharReaderBuilder rbuilder; ================================================ FILE: J1939/J1939Factory.cpp ================================================ /* * J1939Factory.cpp * * Created on: Sep 23, 2017 * Author: famez */ #include #include #include #include #include #include #include #include #include #include namespace J1939 { J1939Factory::J1939Factory() { registerPredefinedFrames(); } J1939Factory::~J1939Factory() { unregisterAllFrames(); } void J1939Factory::unregisterAllFrames() { for(std::map::const_iterator iter = mFrames.begin(); iter != mFrames.end(); iter++) { if(iter->second) delete iter->second; } mFrames.clear(); } std::unique_ptr J1939Factory::getJ1939Frame(u32 id, const u8* data, size_t length) { J1939Frame* frame = NULL, *retFrame = NULL; u32 pgn = ((id >> J1939_PGN_OFFSET) & J1939_PGN_MASK); //Check if PDU format belongs to the first group if(((pgn >> J1939_PDU_FMT_OFFSET) & J1939_PDU_FMT_MASK) < PDU_FMT_DELIMITER) { pgn &= (J1939_PDU_FMT_MASK << J1939_PDU_FMT_OFFSET); } std::map::iterator iter = mFrames.find(pgn); if(iter == mFrames.end() || ((frame = iter->second) == NULL)) { return std::unique_ptr(nullptr); } retFrame = frame->clone(); if(retFrame) { retFrame->decode(id, data, length); } return std::unique_ptr(retFrame); } std::unique_ptr J1939Factory::getJ1939Frame(u32 pgn) { J1939Frame* frame = nullptr, *retFrame = nullptr; std::map::iterator iter = mFrames.find(pgn); if(iter == mFrames.end() || ((frame = iter->second) == NULL)) { //printf("Pgn: %u not found", pgn); return std::unique_ptr(nullptr); } retFrame = frame->clone(); return std::unique_ptr(retFrame); } std::unique_ptr J1939Factory::getJ1939Frame(const std::string& name) { for(auto iter = mFrames.begin(); iter != mFrames.end(); ++iter) { if(iter->second != nullptr && iter->second->getName() == name) { return std::unique_ptr(iter->second->clone()); } } return std::unique_ptr(nullptr); } bool J1939Factory::registerFrame(const J1939Frame& frame) { if(mFrames.find(frame.getPGN()) == mFrames.end()) { mFrames[frame.getPGN()] = frame.clone(); return true; } else { return false; } } void J1939Factory::registerPredefinedFrames() { { TPCMFrame frame; registerFrame(frame); } { TPDTFrame frame; registerFrame(frame); } { FMS1Frame frame; registerFrame(frame); } { DM1 frame; registerFrame(frame); } { AddressClaimFrame frame; registerFrame(frame); } { RequestFrame frame; registerFrame(frame); } } std::set J1939Factory::getAllRegisteredPGNs() const { std::set pgns; for(std::map::const_iterator iter = mFrames.begin(); iter != mFrames.end(); iter++) { pgns.insert(iter->first); } return pgns; } void J1939Factory::unRegisterFrame(u32 pgn) { auto iter = mFrames.find(pgn); if(iter != mFrames.end()) { delete iter->second; mFrames.erase(iter); } } bool J1939Factory::registerDatabaseFrames(const std::string& file) { //Read database if available J1939DataBase database; if(!database.parseJsonFile(file)) { return false; } const std::vector& ddbbFrames = database.getParsedFrames(); //Register all the frames listed in the database for(auto iter = ddbbFrames.begin(); iter != ddbbFrames.end(); ++iter) { registerFrame(*iter); } return true; } } /* namespace J1939 */ ================================================ FILE: J1939/J1939Frame.cpp ================================================ /* * J1939Frame.cpp * * Created on: Sep 23, 2017 * Author: famez */ #include #include #include #include #include #include #include #define UNKNOWN_FRAME "Unknown" namespace std { template inline basic_ostream<_CharT, _Traits> & tab(basic_ostream<_CharT, _Traits> &__os) { return __os.put(__os.widen('\t')); } } namespace J1939 { J1939Frame::J1939Frame(u32 pgn) : mPriority(0), mSrcAddr(J1939_INVALID_ADDRESS), mPgn(pgn), mDstAddr(J1939_INVALID_ADDRESS), mName(UNKNOWN_FRAME) { ASSERT((getPDUFormatGroup() == PDU_FORMAT_2) ? true : ((((mPgn & J1939_DST_ADDR_MASK)) >> J1939_DST_ADDR_OFFSET) == 0)) } J1939Frame::~J1939Frame() { } void J1939Frame::decode(u32 identifier, const u8* buffer, size_t length) { u32 pgn = ((identifier >> J1939_PGN_OFFSET) & J1939_PGN_MASK); //Check if PDU format belongs to the fisrt group if(((pgn >> J1939_PDU_FMT_OFFSET) & J1939_PDU_FMT_MASK) < PDU_FMT_DELIMITER) { mDstAddr = ((pgn >> J1939_DST_ADDR_OFFSET) & J1939_DST_ADDR_MASK); pgn &= (J1939_PDU_FMT_MASK << J1939_PDU_FMT_OFFSET); } if(pgn != mPgn) { throw J1939DecodeException("[J1939Frame::decode] Pgn does not match"); } mSrcAddr = identifier & J1939_SRC_ADDR_MASK; identifier >>= J1939_PRIORITY_OFFSET; mPriority = identifier & J1939_PRIORITY_MASK; //Leave data decoding to inherited class decodeData(buffer, length); } void J1939Frame::encode(u32& identifier, u8* buffer, size_t& length) const { u8 prio = (mPriority & J1939_PRIORITY_MASK); if(prio != mPriority) { throw J1939EncodeException("[J1939Frame::encode] Priority exceeded its range"); } if(length < getDataLength()) { throw J1939EncodeException("[J1939Frame::encode] Length smaller than expected"); } identifier = mSrcAddr; u32 aux = mPgn; if(getPDUFormatGroup() == PDU_FORMAT_1) { //Group 1 aux = mPgn | ((mDstAddr & J1939_DST_ADDR_MASK) << J1939_DST_ADDR_OFFSET); } identifier |= ((aux & J1939_PGN_MASK) << J1939_PGN_OFFSET); identifier |= (prio << J1939_PRIORITY_OFFSET); memset(buffer, 0xFF, length); encodeData(buffer, length); length = getDataLength(); } u32 J1939Frame::getIdentifier() const { u32 identifier; identifier = mSrcAddr; u32 aux = mPgn; if(getPDUFormatGroup() == PDU_FORMAT_1) { //Group 1 aux = mPgn | ((mDstAddr & J1939_DST_ADDR_MASK) << J1939_DST_ADDR_OFFSET); } identifier |= ((aux & J1939_PGN_MASK) << J1939_PGN_OFFSET); identifier |= (mPriority << J1939_PRIORITY_OFFSET); return identifier; } void J1939Frame::copy(const J1939Frame& other) { u8* buffer; size_t length = other.getDataLength(); u32 identifier; buffer = new u8[length]; other.encode(identifier, buffer, length); decode(identifier, buffer, length); delete[] buffer; } std::string J1939Frame::getHeader() const { std::stringstream sstr; sstr << "Name" << std::tab << "PGN" << std::tab << "Source Address" << std::tab << "PDU format" << std::tab; if(getPDUFormatGroup() == PDU_FORMAT_1) { sstr << "Dest Address" << std::tab; } sstr << "Priority" << std::tab; sstr << std::endl; sstr << mName << std::tab << std::uppercase << std::hex << mPgn << std::tab << static_cast(mSrcAddr) << std::tab << std::tab << ((getPDUFormatGroup() == PDU_FORMAT_1) ? "1" : "2") << std::tab << std::tab; if(getPDUFormatGroup() == PDU_FORMAT_1) { sstr << static_cast(mDstAddr) << std::tab; } sstr << static_cast(mPriority) << std::tab; sstr << std::endl; return sstr.str(); } } /* namespace J1939 */ ================================================ FILE: J1939/README.md ================================================ # Developing with libJ1939 ## Loading the database ```c++ #include using namespace J1939; #ifndef DATABASE_PATH #define DATABASE_PATH "/usr/local/etc/j1939/frames.json" #endif void main() { if(!J1939Factory::getInstance().registerDatabaseFrames(DATABASE_PATH)) { std::cerr << "Database not found in " << DATABASE_PATH << std::endl; exit(1); } } ``` ## Obtaining a frame ```c++ #include using namespace J1939; void main() { //Loading the database .... u8 encodedCCVS[] = {0xFF, 0x00, 0x50, 0x9F, 0xFF, 0xFF, 0x1F, 0xFF}; u32 id = 0x18FEF120; //CCVS frame //A J1939Frame represents a frame specified in the protocol J1939. All frames inherit from this one. std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, encodedCCVS, sizeof(encodedCCVS)); if(!frame->isGenericFrame()) { exit(2); } //A GenericFrame is a frame defined in the database which is generic in the sense that it contains fields called SPNs const GenericFrame *ccvsFrame = static_cast(frame.get()); //One can obtain the source address, dest address if any, priority, values for SPNs... u8 src = ccvsFrame->getSrcAddr(); u8 prio = ccvsFrame->getPriority(); u32 pgn = ccvsFrame->getPGN(); const SPNNumeric* wheelSpeed = static_cast(ccvs.getSPN(84)); //Vehicle Speed double speed = wheelSpeed->getFormattedValue(); } ``` ## Encoding a frame ```c++ #include using namespace J1939; void main() { //Loading the database .... //Obtain frame to encode std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame("CCVS"); if(!frame->isGenericFrame()) { exit(2); } const GenericFrame *ccvsFrame = static_cast(frame.get()); SPNNumeric* wheelSpeed = static_cast(ccvs.getSPN(84)); wheelSpeed->setFormattedValue(50); //50 kph ccvsFrame->setSrcAddr(0x50); ccvsFrame->setPriority(7); u32 id; size_t length = ccvsFrame->getDataLength(); u8* buff = new u8[length]; try { ccvsFrame->encode(id, buff, length); } catch(J1939EncodeException &) { // } //Work with buff... For example send as payload of a CanFrame. delete[] buff; } ``` ### J1939/ This folder contains the core of the framework. The project is configured to be compiled as a dynamic library. This library is composed by the following components: - #### J1939Frame Abstract class that represents a frame with the common attributes to all the frames (PGN, Source Address, Priority, etc). All the frames should inherit from this base class. This class provides two methods to be overloaded by the inherited classes: encodeData when it is required that the frame encodes the data into a buffer (probably used to send the data to the CAN bus) and decodeData when the data must be extracted from a buffer (likely, data from a CAN interface). - #### GenericFrame This class inherits from J1939Frame and lets define the 90% of the frames defined in J1939 protocol with the help of a Json database that is loaded (or written) by the class J1939Database. - ##### SPN An abstract class that represents a SPN (Suspect Parameter Number). A GenericFrame is composed by several SPNs. - ###### SPN/SPNStatus An implementation of the class SPN that represents the SPNs that reports the status of a component (for example, brake released, depressed). - ###### SPN/SPNNumeric An implementation of the class SPN that represents the SPNs which reports a quantity (for example, wheel speed). - ###### SPN/SPNString An implementation of the class SPN that represents the SPNs which reports a string of characters (for example, VIN, Vehicle Number Identifier). - ##### Transport/ Implementation of the transport layer of the J1939 protocol - ###### Transport/Bam Implementation of the Broadcast Announcement Message protocol that belongs to the transport layer of the J1939 protocol - ##### Diagnosis/ Implementation of the Dianosis specification that belongs to the transport layer of the J1939 protocol. - ###### Diagnosis/Frames/DM1 Implementation of DM1 diagnosis message - ##### FMS/TellTale/ Implementation of the frame whose PGN is FDE7 (FMS1), with the status of the different Tell Tales. ================================================ FILE: J1939/SPN/SPN.cpp ================================================ /* * ISPN.cpp * * Created on: Oct 24, 2017 * Author: root */ #include #include #include #include #include #include #include namespace J1939 { SPN::SPN(u32 number, const std::string& name, size_t offset) { //When calling the copy constructor, spec object will be passed by reference to keep only one copy of //the characteristics of the SPN which are the same for the SPN to be cloned as well as for the cloned SPN. mSpec = std::make_shared( SPNSpec(number, name, offset) ); } SPN::~SPN() { } std::string SPN::toString() const { std::stringstream sstr; sstr << "SPN " << getSpnNumber() << ": " << getName(); return sstr.str(); } } /* namespace J1939 */ ================================================ FILE: J1939/SPN/SPNHistory.cpp ================================================ /* * SPNHistory.cpp * * Created on: 12 Feb 2019 * Author: fernando */ #include #include #include #include #define MAX_NUMBER_OF_SAMPLES 50000 namespace J1939 { SPNHistory::SPNHistory() { mSamples.reserve(MAX_NUMBER_OF_SAMPLES); } SPNHistory::~SPNHistory() { } void SPNHistory::addSample(const Utils::TimeStamp& timeStamp, const SPN& spn) { //We assume that the timestamp is always increasing if(mSamples.size() == mSamples.capacity()) return; //Capacity exceeded if(!mGeneralSpec) { //First time we add a SPN mGeneralSpec = spn.getSpec(); switch(spn.getType()) { case SPN::SPN_NUMERIC: { const SPNNumeric *spnNum = static_cast(&spn); mSpecificSpec.numeric = spnNum->getNumericSpec(); } break; case SPN::SPN_STATUS: { const SPNStatus *spnStat = static_cast(&spn); mSpecificSpec.status = spnStat->getStatusSpec(); } break; default: break; } } if(mGeneralSpec != spn.getSpec()) { return; //Not the same spn } //Build the sample Sample sample; switch(spn.getType()) { case SPN::SPN_NUMERIC: { const SPNNumeric *spnNum = static_cast(&spn); if(!mSamples.empty() && mSamples.back().getNumeric() == spnNum->getFormattedValue()) return; //Value did not change sample = Sample(timeStamp, spnNum->getFormattedValue()); } break; case SPN::SPN_STATUS: { const SPNStatus *spnStat = static_cast(&spn); if(!mSamples.empty() && mSamples.back().getStatus() == spnStat->getValue()) return; //Value did not change sample = Sample(timeStamp, spnStat->getValue()); } break; default: return; //Unknown, nothing to do break; } mSamples.push_back(sample); } std::vector SPNHistory::getWindow(const Utils::TimeStamp& timeStamp, u32 milliseconds, u32 samples) const { std::vector window; if(mSamples.size() < 2) return window; u32 period = milliseconds / samples; Utils::TimeStamp current = timeStamp - milliseconds; //Get the beginning Sample sample(current, static_cast(0)); size_t pos = std::upper_bound(mSamples.begin(), mSamples.end(), sample) - mSamples.begin(); if(pos != 0) --pos; //Start with the sample whose timestamp is lower or equals to the beginning of the window while(current <= timeStamp) { Sample mySample(mSamples[pos]); mySample.setTimeStamp(current); //Modify timestamp window.push_back(mySample); current = current + period; while(pos+1 < mSamples.size() && current >= mSamples[pos+1].getTimeStamp()) { //TODO check variation of more of 10% and maximums and minimums. ++pos; } } return window; } } /* namespace J1939 */ ================================================ FILE: J1939/SPN/SPNNumeric.cpp ================================================ /* * SPNNumeric.cpp * * Created on: Oct 24, 2017 * Author: root */ #include #include #include #include #include #include #include #include namespace J1939 { SPNNumeric::SPNNumeric(u32 number, const std::string& name, size_t offset, double formatGain, double formatOffset, u8 byteSize, const std::string& units) : SPN(number, name, offset), mValue(0xFFFFFFFF) { mNumSpec = std::make_shared( SPNNumericSpec(formatGain, formatOffset, byteSize, units) ); } SPNNumeric::~SPNNumeric() { } void SPNNumeric::decode(const u8* buffer, size_t length) { if(getByteSize() > length || getByteSize() > SPN_NUMERIC_MAX_BYTE_SYZE) { //mValue can hold only 4 bytes cause it is of type u32 throw J1939DecodeException("[SPNNumeric::decode] Spn length is bigger than expected"); } mValue = 0; for(int i = 0; i < getByteSize(); ++i) { mValue |= (buffer[i] << (i * 8)); } } void SPNNumeric::encode(u8* buffer, size_t length) const { if(getByteSize() > length || getByteSize() > SPN_NUMERIC_MAX_BYTE_SYZE) { //mValue can hold only 4 bytes cause it is of type u32 throw J1939EncodeException("[SPNNumeric::encode] Spn length is bigger than expected"); } for(int i = 0; i < getByteSize(); ++i) { buffer[i] = ((mValue >> (i * 8)) & 0xFF); } } double SPNNumeric::getFormattedValue() const { double aux = mValue; //Apply gain and offset return aux * getFormatGain() + getFormatOffset(); } bool SPNNumeric::setFormattedValue(double value) { double aux = (value - getFormatOffset()) / getFormatGain(); u64 threshold = (((u64)(1)) << (getByteSize()*8)); if(aux >= 0 && (aux < threshold)) { mValue = static_cast(aux); return true; } return false; } std::string SPNNumeric::toString() const { std::string retval = SPN::toString(); std::stringstream sstr; sstr << " -> Value: " << std::fixed << getFormattedValue() << " " << getUnits() << std::endl; retval += sstr.str(); return retval; } void SPNNumeric::copy(const SPN& other) { const SPNNumeric *numOther = static_cast(&other); mValue = numOther->mValue; } } /* namespace J1939 */ ================================================ FILE: J1939/SPN/SPNSpec/SPNNumericSpec.cpp ================================================ /* * SPNNumericSpec.cpp * * Created on: Oct 24, 2017 * Author: root */ #include #include #include #include #include #include #include #include namespace J1939 { SPNNumericSpec::SPNNumericSpec(double formatGain, double formatOffset, u8 byteSize, const std::string& units) : mFormatGain(formatGain), mFormatOffset(formatOffset), mByteSize(byteSize), mUnits(units) { ASSERT(byteSize > 0) ASSERT(byteSize <= SPN_NUMERIC_MAX_BYTE_SYZE) } SPNNumericSpec::~SPNNumericSpec() { } u32 SPNNumericSpec::getMaxValue() const { return 0xFAFFFFFF >> (4 - mByteSize) * 8; } double SPNNumericSpec::formatValue(u32 value) const { double aux = value; //Apply gain and offset return aux * mFormatGain + mFormatOffset; } } /* namespace J1939 */ ================================================ FILE: J1939/SPN/SPNSpec/SPNSpec.cpp ================================================ /* * SPNSpec.cpp * * Created on: Oct 24, 2017 * Author: root */ #include #include #include #include #include #include #include namespace J1939 { SPNSpec::SPNSpec(u32 number, const std::string& name, size_t offset) : mSPNNumber(number), mName(name), mOffset(offset) { ASSERT((number < (1 << SPN_NUMBER_MAX_BITS))) } SPNSpec::~SPNSpec() { } } /* namespace J1939 */ ================================================ FILE: J1939/SPN/SPNSpec/SPNStatusSpec.cpp ================================================ /* * SPNStatusSpec.cpp * * Created on: Oct 24, 2017 * Author: root */ #include #include #include #include #include #include #include #include namespace J1939 { SPNStatusSpec::SPNStatusSpec(u8 bitOffset, u8 bitSize, SPNStatusSpec::DescMap valueToDesc) : mBitOffset(bitOffset), mBitSize(bitSize) { ASSERT(mBitSize > 0) ASSERT(mBitOffset + mBitSize <= 8) mValueToDesc = valueToDesc; } SPNStatusSpec::~SPNStatusSpec() { } void SPNStatusSpec::setValueDescription(u8 value, const std::string& desc) { mValueToDesc[value] = desc; } std::string SPNStatusSpec::getValueDescription(u8 value) const { std::string retVal; auto iter = mValueToDesc.find(value); if(iter != mValueToDesc.end()) { retVal = iter->second; } return retVal; } void SPNStatusSpec::clearValueDescriptions() { mValueToDesc.clear(); } SPNStatusSpec::DescMap SPNStatusSpec::getValueDescriptionsMap() const { return mValueToDesc; } } /* namespace J1939 */ ================================================ FILE: J1939/SPN/SPNStatus.cpp ================================================ /* * SPNStatus.cpp * * Created on: Oct 24, 2017 * Author: root */ #include #include #include #include #include #include #include #include namespace J1939 { SPNStatus::SPNStatus(u32 number, const std::string& name, size_t offset, u8 bitOffset, u8 bitSize, SPNStatusSpec::DescMap valueToDesc) : SPN(number, name, offset) { mStatSpec = std::make_shared( SPNStatusSpec(bitOffset, bitSize, valueToDesc) ); mValue = (0xFF >> (8 - getBitSize())); //Always initialized to invalid value } SPNStatus::~SPNStatus() { } void SPNStatus::decode(const u8* buffer, size_t) { if(getBitOffset() > 7 || getBitSize() > 8 || getBitOffset() + getBitSize() > 8) { throw J1939DecodeException("[SPNStatus::decode] Format incorrect to decode properly this spn"); } u8 mask = 0xFF >> (8 - getBitSize()); mValue = ((*buffer >> getBitOffset()) & mask); } void SPNStatus::encode(u8* buffer, size_t) const { if(getBitOffset() > 7 || getBitSize() > 8 || getBitOffset() + getBitSize() > 8) { throw J1939EncodeException("[SPNStatus::encode] Format incorrect to encode properly this spn"); } u8 mask = (0xFF >> (8 - getBitSize())) << getBitOffset(); u8 value = mValue << getBitOffset(); if((value & mask) != value) { throw J1939EncodeException("[SPNStatus::encode] Value to encode is bigger than expected"); } //Clear the bits from the buffer *buffer = *buffer & ~mask; //Set the new value *buffer = *buffer | value; } std::string SPNStatus::toString() const { std::string retval = SPN::toString(); std::stringstream sstr; SPNStatusSpec::DescMap valueToDesc = getValueDescriptionsMap(); sstr << " -> Status: " << ((valueToDesc.find(mValue) != valueToDesc.end()) ? valueToDesc[mValue] : "") << " (" << static_cast(mValue) << ")" << std::endl; retval += sstr.str(); return retval; } bool SPNStatus::setValue(u8 value) { if(value < (1 << getBitSize())) { mValue = value; return true; } return false; } void SPNStatus::copy(const SPN& other) { const SPNStatus *numOther = static_cast(&other); mValue = numOther->mValue; } } /* namespace J1939 */ ================================================ FILE: J1939/SPN/SPNString.cpp ================================================ /* * SPNString.cpp * * Created on: 6 juil. 2018 * Author: fernando */ #include #include #include #include #include #include #include namespace J1939 { SPNString::SPNString(u32 number, const std::string& name) : SPN(number, name, 0/*Stub, not used by SPNString*/) { } SPNString::~SPNString() { } void SPNString::setValue(std::string value) { for(char c : value) { if(c & 0x80) { return; //This is not ASCII. We cannot assign a string that is not an ASCII string... } } mValue = value; if(mOwner) { //The offsets for SPN of type string may have changed. mOwner->recalculateStringOffsets(); } } void SPNString::decode(const u8* buffer, size_t length) { char *terminator = (char*) memchr(buffer, J1939_STR_TERMINATOR, length); mValue.clear(); if(!terminator) { throw J1939DecodeException("[SPNString::decode] '*' terminator not found"); } for(const char *c = (const char*)(buffer); c != terminator; ++c) { if(*c & 0x80) { throw J1939DecodeException("[SPNString::decode] String is not ASCII"); } } size_t strLength = terminator - (const char*)(buffer); mValue.append((const char*)(buffer), strLength); if(mOwner) { //The size of this SPN has changed, the offsets must be recalculated mOwner->recalculateStringOffsets(); } } void SPNString::encode(u8* buffer, size_t length) const { if(mValue.size() >= length) { throw J1939EncodeException("[SPNString::encode] Not enough length to encode the string"); } //Copy string to the buffer strcpy((char*)(buffer), mValue.c_str()); //Add string terminator to need of the string buffer[mValue.size()] = J1939_STR_TERMINATOR; } std::string SPNString::toString() const { std::string retval = SPN::toString(); std::stringstream sstr; sstr << " -> Value: " << mValue << std::endl; retval += sstr.str(); return retval; } void SPNString::copy(const SPN& other) { const SPNString *numOther = static_cast(&other); mValue = numOther->mValue; } } /* namespace J1939 */ ================================================ FILE: J1939/Transport/BAM/BamFragmenter.cpp ================================================ /* * BamFragmenter.cpp * * Created on: Apr 22, 2018 * Author: fernado */ #include #include namespace J1939 { BamFragmenter::BamFragmenter() { } BamFragmenter::~BamFragmenter() { } bool BamFragmenter::fragment(const J1939Frame& frame) { size_t length = frame.getDataLength(); if(length <= J1939_MAX_SIZE) { //Not necessary to fragment the frame return false; } //Clear previous fragmented frames clear(); //Bam type mCMFrame.setCtrlType(CTRL_TPCM_BAM); //Same source and priority as the original frame mCMFrame.setPriority(frame.getPriority()); mCMFrame.setSrcAddr(frame.getSrcAddr()); //Encoded pgn mCMFrame.setDataPgn(frame.getPGN()); //Always broadcast mCMFrame.setDstAddr(J1939_BROADCAST_ADDRESS); size_t sq = 0; u8* data = new u8[length]; u32 unused; frame.encode(unused, data, length); for(size_t offset = 0 ; offset < length; offset += TP_DT_PACKET_SIZE) { TPDTFrame dataFrame(++sq, data + offset, J1939_MIN(TP_DT_PACKET_SIZE, length - offset)); //Same source and priority as the original frame dataFrame.setPriority(frame.getPriority()); dataFrame.setSrcAddr(frame.getSrcAddr()); //Always broadcast dataFrame.setDstAddr(J1939_BROADCAST_ADDRESS); mDTFrames[sq] = dataFrame; } delete[] data; mCMFrame.setTotalPackets(mDTFrames.size()); mCMFrame.setTotalMsgSize(length); return true; } std::vector BamFragmenter::getDataFrames() const { std::vector frames; for(auto iter = mDTFrames.begin(); iter != mDTFrames.end(); ++iter) { frames.push_back(iter->second); } return frames; } } /* namespace J1939 */ ================================================ FILE: J1939/Transport/BAM/BamReassembler.cpp ================================================ /* * BamFrameSet.cpp * * Created on: Oct 2, 2017 * Author: famez */ #include //Common #include #include //J1939 #include #include namespace J1939 { u8 BamReassembler::BAMFragments::getLastSQ() const{ if(mDTFrames.empty()) { return 0; } return mDTFrames.back().getSq(); } BamReassembler::BamReassembler() : mLastError(BAM_ERROR_OK) { } BamReassembler::~BamReassembler() { clear(); } void BamReassembler::reassemble(const BAMFragments& fragments, u8** data, size_t& length) { size_t offset = 0; length = fragments.getCmFrame().getTotalMsgSize(); *data = new u8[length]; for(std::vector::const_iterator iter = fragments.getDtFrames().begin(); iter != fragments.getDtFrames().end(); iter++) { memcpy((void*)(*data + offset), (void*)iter->getData(), J1939_MIN(length - offset, TP_DT_PACKET_SIZE)); offset += TP_DT_PACKET_SIZE; if(offset >= length) { break; } } } bool BamReassembler::toBeHandled(const J1939Frame& frame) const { const TPCMFrame* conn; if(frame.getDstAddr() != J1939_BROADCAST_ADDRESS) { //Does not have a broadcast address return false; } switch(frame.getPGN()) { case TP_CM_PGN: { conn = static_cast(&frame); return (conn->getCtrlType() == CTRL_TPCM_BAM); //If the ctrl type is not BAM, discard it. } break; case TP_DT_PGN: { return true; } break; default: { return false; } break; } } size_t BamReassembler::handleFrame(const J1939Frame& frame) { const TPDTFrame* data = NULL; const TPCMFrame* conn = NULL; size_t expectedSize = 0; u8 srcAddr = frame.getSrcAddr(); if(frame.getDstAddr() != J1939_BROADCAST_ADDRESS) { //The frame does not have a broadcast address setError(BAM_ERROR_NOT_BCAST_ADDR); return 0; } switch(frame.getPGN()) { case TP_CM_PGN: //Conn management reception { conn = static_cast(&frame); //Not the right Connection Manager frame... if(conn->getCtrlType() != CTRL_TPCM_BAM) { setError(BAM_ERROR_UNEXPECTED_FRAME); break; } //New TP.CM frame but not all previous TP.DT frames were received if(mFragments.find(srcAddr) != mFragments.end()) { mFragments.erase(srcAddr); setError(BAM_ERROR_INCOMPLETE_FRAME); } else { setError(BAM_ERROR_OK); } mFragments[srcAddr].setCmFrame(*conn); expectedSize = conn->getTotalMsgSize(); } break; case TP_DT_PGN: //Data reception { if(mFragments.find(srcAddr) == mFragments.end()) { setError(BAM_ERROR_UNEXPECTED_FRAME); break; } data = static_cast(&frame); u8 sq = data->getSq(); const TPCMFrame& connFrame = mFragments[srcAddr].getCmFrame(); expectedSize = connFrame.getTotalMsgSize(); if((connFrame.getTotalPackets() >= sq) && (mFragments[srcAddr].getLastSQ() + 1 == sq) ) { mFragments[srcAddr].addDtFrame(*data); } else { setError(BAM_ERROR_UNEXPECTED_FRAME); break; } if(sq == connFrame.getTotalPackets()) { //Time to reassemble the packet u8* fullData; size_t length; reassemble(mFragments[srcAddr], &fullData, length); u32 newId = (((connFrame.getPriority() & J1939_PRIORITY_MASK) << J1939_PRIORITY_OFFSET) | (connFrame.getDataPgn() << J1939_PGN_OFFSET) | (connFrame.getSrcAddr() & J1939_SRC_ADDR_MASK)); std::unique_ptr decodedFrame = J1939Factory::getInstance().getJ1939Frame(newId, fullData, length); delete[] fullData; //Fragments reassembled, we can destroy the fragments mFragments.erase(srcAddr); if(decodedFrame.get()) { mReassembledFrames.push(decodedFrame.release()); } else { setError(BAM_ERROR_DECODING); return 0; } setError(BAM_ERROR_OK); } } break; default: { //Not a frame for BAM protocol setError(BAM_ERROR_UNEXPECTED_FRAME); } break; } return expectedSize; } std::unique_ptr BamReassembler::dequeueReassembledFrame() { J1939Frame* retVal = NULL; retVal = mReassembledFrames.front(); mReassembledFrames.pop(); return std::unique_ptr(retVal); } void BamReassembler::clear() { mFragments.clear(); mLastError = BAM_ERROR_OK; //Clear queue while(!mReassembledFrames.empty()) { delete mReassembledFrames.front(); mReassembledFrames.pop(); } } } /* namespace J1939 */ ================================================ FILE: J1939/Transport/RTSCTS/RTSCTSConnectionManager.cpp ================================================ /* * RTSCTSConnectionManager.cpp * * Created on: Oct 15, 2017 * Author: famez */ #include "RTSCTSConnectionManager.h" namespace J1939 { RTSCTSConnectionManager::RTSCTSConnectionManager() { // TODO Auto-generated constructor stub } RTSCTSConnectionManager::~RTSCTSConnectionManager() { // TODO Auto-generated destructor stub } void RTSCTSConnectionManager::consumeFrame(const J1939Frame&) { } } /* namespace J1939 */ ================================================ FILE: J1939/Transport/TPCMFrame.cpp ================================================ /* * BAMHeaderFrame.cpp * * Created on: Oct 2, 2017 * Author: famez */ #include #include #define TPCM_NAME "Transport Connection Management" namespace J1939 { TPCMFrame::TPCMFrame() : J1939Frame(TP_CM_PGN), mCtrlType(0), mTotalMsgSize(0), mTotalPackets(0), mMaxPackets(0), mPacketsToTx(0), mNextPacket(0), mAbortReason(0), mDataPgn(0){ mName = TPCM_NAME; } TPCMFrame::~TPCMFrame() { } void TPCMFrame::decodeData(const u8* buffer, size_t length) { if(length != TP_CM_SIZE) { throw J1939DecodeException("[TPCMFrame::decodeData] Buffer length does not match the expected " "length. Buffer length:" + std::to_string(length) + ". Expected length: " + std::to_string(TP_CM_SIZE)); } mCtrlType = buffer[0]; switch(mCtrlType) { case CTRL_TPCM_RTS: decodeRTS(buffer + 1); break; case CTRL_TPCM_CTS: decodeCTS(buffer + 1); break; case CTRL_TPCM_ACK: decodeEndOfMsgACK(buffer + 1); break; case CTRL_TPCM_ABORT: decodeConnAbort(buffer + 1); break; case CTRL_TPCM_BAM: decodeBAM(buffer + 1); break; default: throw J1939DecodeException("[TPCMFrame::decodeData] Unknown Ctrl type"); break; } mDataPgn = buffer[5] | (buffer[6] << 8) | (buffer[7] << 16); } void TPCMFrame::encodeData(u8* buffer, size_t ) const { /* * If reserved, set to 0xFF */ memset(buffer, 0xFF, TP_CM_SIZE); buffer[0] = mCtrlType; switch(mCtrlType) { case CTRL_TPCM_RTS: encodeRTS(buffer + 1); break; case CTRL_TPCM_CTS: encodeCTS(buffer + 1); break; case CTRL_TPCM_ACK: encodeEndOfMsgACK(buffer + 1); break; case CTRL_TPCM_ABORT: encodeConnAbort(buffer + 1); break; case CTRL_TPCM_BAM: encodeBAM(buffer + 1); break; default: throw J1939DecodeException("[TPCMFrame::encodeData] Unknown Ctrl Type"); break; } buffer[5] = mDataPgn & 0xFF; buffer[6] = (mDataPgn >> 8) & 0xFF; buffer[7] = (mDataPgn >> 16) & 0xFF; } void TPCMFrame::clear() { mCtrlType = 0; mTotalMsgSize = 0; mTotalPackets = 0; mMaxPackets = 0; mPacketsToTx = 0; mNextPacket = 0; mAbortReason = 0; mDataPgn = 0; setPriority(0); setSrcAddr(0); setDstAddr(J1939_BROADCAST_ADDRESS); } void TPCMFrame::decodeRTS(const u8* buffer) { mTotalMsgSize = buffer[0] | (buffer[1] << 8); mTotalPackets = buffer[2]; mMaxPackets = buffer[3]; } void TPCMFrame::decodeCTS(const u8* buffer) { mPacketsToTx = buffer[0]; mNextPacket = buffer[1]; } void TPCMFrame::decodeEndOfMsgACK(const u8* buffer) { mTotalMsgSize = buffer[0] | (buffer[1] << 8); mTotalPackets = buffer[2]; } void TPCMFrame::decodeConnAbort(const u8* buffer) { mAbortReason = buffer[0]; } void TPCMFrame::decodeBAM(const u8* buffer) { mTotalMsgSize = buffer[0] | (buffer[1] << 8); mTotalPackets = buffer[2]; } void TPCMFrame::encodeRTS(u8* buffer) const { buffer[0] = mTotalMsgSize & 0xFF; buffer[1] = (mTotalMsgSize >> 8) & 0xFF; buffer[2] = mTotalPackets; buffer[3] = mMaxPackets; } void TPCMFrame::encodeCTS(u8* buffer) const { buffer[0] = mPacketsToTx; buffer[1] = mNextPacket; } void TPCMFrame::encodeEndOfMsgACK(u8* buffer) const { buffer[0] = mTotalMsgSize & 0xFF; buffer[1] = (mTotalMsgSize >> 8) & 0xFF; buffer[2] = mTotalPackets; } void TPCMFrame::encodeConnAbort(u8* buffer) const { buffer[0] = mAbortReason; } void TPCMFrame::encodeBAM(u8* buffer) const { buffer[0] = mTotalMsgSize & 0xFF; buffer[1] = (mTotalMsgSize >> 8) & 0xFF; buffer[2] = mTotalPackets; } } /* namespace J1939 */ ================================================ FILE: J1939/Transport/TPDTFrame.cpp ================================================ /* * BamDataframe.cpp * * Created on: Oct 2, 2017 * Author: famez */ #include #include #include #define TPDT_NAME "Transport Data" namespace J1939 { TPDTFrame::TPDTFrame() : J1939Frame(TP_DT_PGN), mSQ(0) { memset(mData, 0xFF, TP_DT_PACKET_SIZE); mName = TPDT_NAME; } TPDTFrame::TPDTFrame(u8 sq, u8* data, size_t length) : TPDTFrame() { mSQ = sq; memcpy(mData, data, J1939_MIN(length, TP_DT_PACKET_SIZE)); } TPDTFrame::~TPDTFrame() { } void TPDTFrame::decodeData(const u8* buffer, size_t length) { if(length != BAM_DT_SIZE) { throw J1939DecodeException("[TPDTFrame::decodeData] Buffer length does not match the expected length. Buffer length:" + std::to_string(length) + ". Expected length: " + std::to_string(TP_DT_PACKET_SIZE)); } mSQ = *buffer++; memcpy(mData, buffer, TP_DT_PACKET_SIZE); } void TPDTFrame::encodeData(u8* buffer, size_t) const { *buffer++ = mSQ; memcpy(buffer, mData, TP_DT_PACKET_SIZE); } } /* namespace J1939 */ ================================================ FILE: J1939/include/Addressing/AddressClaimFrame.h ================================================ /* * AdressClaimFrame.h * * Created on: Oct 17, 2017 * Author: famez */ #ifndef ADDRESSING_ADRESSCLAIMFRAME_H_ #define ADDRESSING_ADRESSCLAIMFRAME_H_ #include #define ADDRESS_CLAIM_PGN 0x00EE00 #define ADDRESS_FRAME_LENGTH 8 #define ARBITRARY_ADDR_CAPABLE_MASK 0x1 #define ARBITRARY_ADDR_CAPABLE_OFFSET 63 #define INDUSTRY_GROUP_MASK 0x7 #define INDUSTRY_GROUP_OFFSET 60 #define VEHICLE_SYSTEM_INTERFACE_MASK 0xF #define VEHICLE_SYSTEM_INTERFACE_OFFSET 56 #define VEHICLE_SYSTEM_MASK 0x7F #define VEHICLE_SYSTEM_OFFSET 49 #define FUNCTION_MASK 0xFF #define FUNCTION_OFFSET 40 #define FUNCTION_INSTANCE_MASK 0x1F #define FUNCTION_INSTANCE_OFFSET 35 #define ECU_INSTANCE_MASK 0x7 #define ECU_INSTANCE_OFFSET 32 #define MANUFACTURER_CODE_MASK 0x7FF #define MANUFACTURER_CODE_OFFSET 21 #define IDENTITY_NUMBER_MASK 0x1FFFFF #define IDENTITY_NUMBER_OFFSET 0 namespace J1939 { class EcuName { private: u32 mIdNumber = 0; u16 mManufacturerCode = 0; u8 mEcuInstance = 0; u8 mFunctionInstance = 0; u8 mFunction = 0; u8 mVehicleSystem = 0; u8 mVehicleSystemInstance = 0; u8 mIndustryGroup = 0; bool mArbitraryAddressCapable = false; public: EcuName() {} EcuName(u32 idNumber, u16 manufacturerCode, u8 ecuInstance, u8 functionInstance, u8 function, u8 vehicleSystem, u8 vehicSystemInstance, u8 industryGroup, bool arbitraryAddressCapable) : mIdNumber(idNumber), mManufacturerCode(manufacturerCode), mEcuInstance(ecuInstance), mFunctionInstance(functionInstance), mFunction(function), mVehicleSystem(vehicleSystem), mVehicleSystemInstance(vehicSystemInstance), mIndustryGroup(industryGroup), mArbitraryAddressCapable(arbitraryAddressCapable){ } virtual ~EcuName() {} u64 getValue() const { return ((u64)(mArbitraryAddressCapable ? ARBITRARY_ADDR_CAPABLE_MASK : 0) << ARBITRARY_ADDR_CAPABLE_OFFSET) | ((u64)(mIndustryGroup & INDUSTRY_GROUP_MASK) << INDUSTRY_GROUP_OFFSET) | ((u64)(mVehicleSystemInstance & VEHICLE_SYSTEM_INTERFACE_MASK) << VEHICLE_SYSTEM_INTERFACE_OFFSET) | ((u64)(mVehicleSystem & VEHICLE_SYSTEM_MASK) << VEHICLE_SYSTEM_OFFSET) | ((u64)(mFunction & FUNCTION_MASK) << FUNCTION_OFFSET) | ((u64)(mFunctionInstance & FUNCTION_INSTANCE_MASK) << FUNCTION_INSTANCE_OFFSET) | ((u64)(mEcuInstance & ECU_INSTANCE_MASK) << ECU_INSTANCE_OFFSET) | ((u64)(mManufacturerCode & MANUFACTURER_CODE_MASK) << MANUFACTURER_CODE_OFFSET) | ((u64)(mIdNumber & IDENTITY_NUMBER_MASK) << IDENTITY_NUMBER_OFFSET); } bool operator<(const EcuName& other) const { return getValue() < other.getValue(); } bool operator>(const EcuName& other) const { return getValue() > other.getValue(); } bool isArbitraryAddressCapable() const { return mArbitraryAddressCapable; } void setArbitraryAddressCapable(bool arbitraryAddressCapable) { mArbitraryAddressCapable = arbitraryAddressCapable; } u8 getEcuInstance() const { return mEcuInstance; } void setEcuInstance(u8 ecuInstance) { mEcuInstance = ecuInstance; } u8 getFunction() const { return mFunction; } void setFunction(u8 function) { mFunction = function; } u8 getFunctionInstance() const { return mFunctionInstance; } void setFunctionInstance(u8 functionInstance) { mFunctionInstance = functionInstance; } u32 getIdNumber() const { return mIdNumber; } void setIdNumber(u32 idNumber) { mIdNumber = idNumber; } u8 getIndustryGroup() const { return mIndustryGroup; } void setIndustryGroup(u8 industryGroup) { mIndustryGroup = industryGroup; } u16 getManufacturerCode() const { return mManufacturerCode; } void setManufacturerCode(u16 manufacturerCode) { mManufacturerCode = manufacturerCode; } u8 getVehicleSystem() const { return mVehicleSystem; } void setVehicleSystem(u8 vehicleSystem) { mVehicleSystem = vehicleSystem; } u8 getVehicleSystemInstance() const { return mVehicleSystemInstance; } void setVehicleSystemInstance(u8 vehicleSystemInstance) { mVehicleSystemInstance = vehicleSystemInstance; } }; class AddressClaimFrame: public J1939Frame { private: EcuName mEcuName; protected: void decodeData(const u8* buffer, size_t length); void encodeData(u8* buffer, size_t length) const; public: AddressClaimFrame(); AddressClaimFrame(EcuName name); virtual ~AddressClaimFrame(); size_t getDataLength() const override { return ADDRESS_FRAME_LENGTH; } const EcuName& getEcuName() { return mEcuName; } std::string toString() const; IMPLEMENT_CLONEABLE(J1939Frame,AddressClaimFrame); }; } /* namespace J1939 */ #endif /* ADDRESSING_ADRESSCLAIMFRAME_H_ */ ================================================ FILE: J1939/include/Diagnosis/DTC.h ================================================ /* * DTC.h * * Created on: Sep 2, 2018 * Author: fernado */ #ifndef DIAGNOSIS_DTC_H_ #define DIAGNOSIS_DTC_H_ #include #include #define DTC_SIZE 4 #define DTC_CM_MASK 0x80 #define DTC_OC_MASK 0x7F #define DTC_FMI_MASK 0x1F namespace J1939 { enum { FMI_DATA_ABOVE_RANGE, FMI_DATA_BELOW_RANGE, FMI_DATA_INTERMITENT_OR_INCORRECT, FMI_VOLTAGE_ABOVE_RANGE, FMI_VOLTAGE_BELOW_RANGE, FMI_CURRENT_BELOW_RANGE, FMI_CURRENT_ABOVE_RANGE, FMI_SYS_NOT_RESPONDING, FMI_ABNORMAL_FREQUENCY, FMI_ABNORMAL_UPDATE_RATE, FMI_ABNORMAL_RATE_OF_CHARGE, FMI_NOT_KNOWN, FMI_BAD_DEVICE, FMI_OUT_OF_CALIBRATION, FMI_SPECIAL_INSTRUCTIONS, FMI_DATA_VALID_BUT_ABOVE_RANGE_LEAST, FMI_DATA_VALID_BUT_ABOVE_RANGE_MODERATELY, FMI_DATA_VALID_BUT_BELOW_RANGE_LEAST, FMI_DATA_VALID_BUT_BELOW_RANGE_MODERATELY, FMI_RECEIVED_NETWORK_DATA_IN_ERROR, FMI_DATA_DRIFTED_HIGH, FMI_DATA_DRIFTED_LOW, FMI_CONDITION_EXISTS = 31 }; class DTC { private: u32 mSPN; u8 mFMI; u8 mOC; public: DTC(); DTC(u32 spn, u8 fmi, u8 oc); virtual ~DTC(); void decode(const u8* buffer); void encode(u8* buffer) const; std::string toString() const; u8 getFmi() const { return mFMI; } void setFmi(u8 fmi) { mFMI = fmi; } u8 getOc() const { return mOC; } void setOc(u8 oc) { mOC = oc; } u32 getSpn() const { return mSPN; } void setSpn(u32 spn) { mSPN = spn; } }; } /* namespace J1939 */ #endif /* DIAGNOSIS_DTC_H_ */ ================================================ FILE: J1939/include/Diagnosis/Frames/DM1.h ================================================ /* * DM1.h * * Created on: Oct 15, 2017 * Author: famez */ #ifndef DIAGNOSIS_FRAMES_DM1_H_ #define DIAGNOSIS_FRAMES_DM1_H_ #include #include #include #define DM1_PGN 0x00FECA #define DM1_NAME "DM1" namespace J1939 { class DM1: public GenericFrame { private: std::vector mDtcs; protected: void decodeData(const u8* buffer, size_t length); void encodeData(u8* buffer, size_t length) const; public: DM1(); virtual ~DM1(); void addDTC(DTC&& dtc) { mDtcs.push_back(dtc); }; bool deleteDTC(size_t pos) { if(pos >= mDtcs.size()) return false; mDtcs.erase(mDtcs.begin() + pos); return true; } bool setDTC(size_t pos, DTC&& dtc) { if(pos >= mDtcs.size()) return false; mDtcs[pos] = dtc; return true; } const std::vector& getDTCs() const { return mDtcs; } size_t getDataLength() const override; std::string toString() const override; void copy(const J1939Frame& other) override; IMPLEMENT_CLONEABLE(J1939Frame,DM1); }; } /* namespace J1939 */ #endif /* DIAGNOSIS_FRAMES_DM1_H_ */ ================================================ FILE: J1939/include/FMS/TellTale/FMS1Frame.h ================================================ /* * FMS1Frame.h * * Created on: Mar 11, 2018 * Author: famez */ #ifndef TELLTALE_FMS1FRAME_H_ #define TELLTALE_FMS1FRAME_H_ #include #include #include "../../J1939Frame.h" #include "TellTale.h" #define TTS_MASK 0x7 #define BLOCKID_MASK 0xF #define TTS_HIGH_PART_SHIFT 4 #define TTSS_PER_BLOCK 15 #define TTS_ENCODING_MASK 0x8 #define NUMBER_OF_BLOCKS 4 #define FMS1_FRAME_LENGTH 8 #define FMS1_PGN 0xFD7D namespace J1939 { class FMS1Frame : public J1939Frame { private: u8 mBlockID; std::map mTTSs; protected: void decodeData(const u8* buffer, size_t length) override; void encodeData(u8* buffer, size_t length) const override; public: FMS1Frame(); FMS1Frame(u8 blockID); virtual ~FMS1Frame(); bool hasTTS(u8 number); TellTale getTTS(u8 number); bool setTTS(u8 number, u8 status); u8 getBlockID() const { return mBlockID; } size_t getDataLength() const override { return FMS1_FRAME_LENGTH; } std::string toString() const override; IMPLEMENT_CLONEABLE(J1939Frame,FMS1Frame); }; } #endif /* TELLTALE_FMS1FRAME_H_ */ ================================================ FILE: J1939/include/FMS/TellTale/TellTale.h ================================================ /* * TellTale.h * * Created on: Mar 11, 2018 * Author: famez */ #ifndef TELLTALE_TELLTALE_H_ #define TELLTALE_TELLTALE_H_ #include #include #include class TellTale { private: u8 mNumber; u8 mStatus; static std::map mNumberToName; static std::map mStatusName; static std::map initializeNTNMap(); static std::map initializeSNMap(); public: enum { TTS_STATUS_OFF, TTS_STATUS_RED, TTS_STATUS_YELLOW, TTS_STATUS_INFO, TTS_STATUS_NOT_AVAILABLE = 0x7, }; TellTale(u8 number, u8 status); TellTale(); virtual ~TellTale(); u8 getNumber() const { return mNumber; } void setNumber(u8 number) { mNumber = number; } u8 getStatus() const { return mStatus; } void setStatus(u8 status) { mStatus = status; } std::string toString() const; static std::string getNameForTTSNumber(u8 number) { return (mNumberToName.find(number) != mNumberToName.end()) ? mNumberToName.at(number) : ""; } static std::string getSatusname(u8 status) { return (mStatusName.find(status) != mStatusName.end()) ? mStatusName.at(status) : "RESERVED"; } }; #endif /* TELLTALE_TELLTALE_H_ */ ================================================ FILE: J1939/include/Frames/RequestFrame.h ================================================ /* * RequestFrame.h * * Created on: Dec 20, 2018 * Author: famez */ #ifndef ADDRESSING_REQUESTFRAME_H_ #define ADDRESSING_REQUESTFRAME_H_ #include #define REQUEST_FRAME_LENGTH 3 #define REQUEST_PGN 0xEA00 namespace J1939 { class RequestFrame: public J1939Frame { private: u32 mRequestPGN = 0; protected: void decodeData(const u8* buffer, size_t length); void encodeData(u8* buffer, size_t length) const; public: RequestFrame(); RequestFrame(u32 requestPGN); virtual ~RequestFrame(); size_t getDataLength() const override { return REQUEST_FRAME_LENGTH; } u32 getRequestPGN() const { return mRequestPGN; } void setRequestPGN(u32 requestPGN) { mRequestPGN = requestPGN; } std::string toString() const; IMPLEMENT_CLONEABLE(J1939Frame,RequestFrame); }; } /* namespace J1939 */ #endif /* ADDRESSING_REQUESTFRAME_H_ */ ================================================ FILE: J1939/include/GenericFrame.h ================================================ /* * GenericFrame.h * * Created on: Nov 3, 2017 * Author: root */ #ifndef GENERICFRAME_H_ #define GENERICFRAME_H_ #include #include #include "J1939Frame.h" #include "SPN/SPN.h" namespace J1939 { class GenericFrame : public J1939Frame { private: size_t mLength; std::map mSPNs; protected: virtual void decodeData(const u8* buffer, size_t length); virtual void encodeData(u8* buffer, size_t length) const; public: GenericFrame(u32 pgn); GenericFrame(const GenericFrame& other); virtual ~GenericFrame(); //This method is called when there is a need to recalculate the offsets for SPNs of type String. void recalculateStringOffsets(); /** * The copy-assingment and move-assignment are forbidden here. Use clone instead. */ GenericFrame& operator=(const GenericFrame& other) = delete; GenericFrame& operator=(GenericFrame&& other) = delete; SPN* registerSPN(const SPN& spn); bool deleteSPN(u32 number); SPN* getSPN(u32); const SPN* getSPN(u32) const; bool hasSPN(u32 number) const { return ( mSPNs.find(number) != mSPNs.end()); } std::set getSPNNumbers() const; std::map getSPNs() { return mSPNs; }; virtual size_t getDataLength() const; void setLength(size_t length) { mLength = length; } void setName(const std::string& name) { mName = name; } bool isGenericFrame() const { return true; } virtual std::string toString() const; /* * Returns a set of SPNs that have changed with respect to the data */ std::set compare(const std::string& newData, const std::string oldData); void copy(const J1939Frame& other) override; IMPLEMENT_CLONEABLE(J1939Frame,GenericFrame); }; } /* namespace J1939 */ #endif /* GENERICFRAME_H_ */ ================================================ FILE: J1939/include/J1939Common.h ================================================ #ifndef J1939COMMON_H #define J1939COMMON_H #include #include #define J1939_MAX_SIZE 8 #define J1939_PGN_OFFSET 8 #define J1939_PGN_MASK 0x3FFFF #define J1939_PDU_SPECIFIC_MASK 0xFF #define J1939_DST_ADDR_MASK 0xFF #define J1939_SRC_ADDR_MASK 0xFF #define J1939_SRC_ADDR_OFFSET 0 #define J1939_DST_ADDR_OFFSET 0 #define J1939_PDU_FMT_MASK 0xFF #define J1939_PDU_FMT_OFFSET 8 #define J1939_DATA_PAGE_MASK 1 #define J1939_DATA_PAGE_OFFSET 16 #define J1939_EXT_DATA_PAGE_MASK 1 #define J1939_EXT_DATA_PAGE_OFFSET 17 #define J1939_PRIORITY_OFFSET 26 #define J1939_PRIORITY_MASK 7 #define J1939_STATUS_MASK 3 #define J1939_STR_TERMINATOR '*' #define NULL_TERMINATOR 0 #define TP_DT_PACKET_SIZE 7 #define PDU_FMT_DELIMITER 0xF0 #define J1939_INVALID_ADDRESS 0xFE #define J1939_BROADCAST_ADDRESS 0xFF #define SPN_NUMBER_MAX_BITS 19 #define SPN_NUMERIC_MAX_BYTE_SYZE 4 namespace J1939 { class J1939DecodeException : public std::exception { private: std::string mMsg; public: J1939DecodeException(const std::string& msg) : mMsg(msg) {} ~J1939DecodeException() throw() {} const std::string& getMessage() const { return mMsg; } }; class J1939EncodeException : public std::exception { private: std::string mMsg; public: J1939EncodeException(const std::string& msg) : mMsg(msg) {} ~J1939EncodeException() throw() {} const std::string& getMessage() const { return mMsg; } }; } #endif // J1939COMMON_H ================================================ FILE: J1939/include/J1939DataBase.h ================================================ /* * JsonParser.h * * Created on: Nov 7, 2017 * Author: root */ #ifndef SRC_J1939DATABASE_H_ #define SRC_J1939DATABASE_H_ #include #include namespace Json { class Value; } namespace J1939 { class GenericFrame; class SPN; class J1939DataBase { public: enum EErrorCode { ERROR_OK, //Everything ok ERROR_FILE_NOT_FOUND, //The file is not found in the path or cannot be opened ERROR_JSON_SYNTAX, //The syntax of the json file is not correct ERROR_UNEXPECTED_TOKENS, //There are tokens in json file that do not match with the expected ones ERROR_OUT_OF_RANGE, //There is at least one value that exceeds the permitted range of values ERROR_UNKNOWN_SPN_TYPE, //The type of spn is not recognized ERROR_FORBIDDEN_TOKEN, //A token that should not be present in the database which is reserved for internal use in the software //and could cause misbehaviour (example: PGN=FECA which corresponds to reserved PGN for DM1 frame, casts to this class are done after verifying the PGN) }; private: std::vector mFrames; EErrorCode mErrorCode; bool parseSPNNumeric(GenericFrame& frame, const Json::Value& spn); bool parseSPNStatus(GenericFrame& frame, const Json::Value& spn); bool parseSPNString(GenericFrame& frame, const Json::Value& spn); void writeSPNNumeric(const SPN* spn, Json::Value& jsonSpn); void writeSPNStatus(const SPN* spn, Json::Value& jsonSpn); public: J1939DataBase(); virtual ~J1939DataBase(); bool parseJsonFile(const std::string& file); bool writeJsonFile(const std::string& file); const std::vector& getParsedFrames() const; void addFrame(const GenericFrame&); EErrorCode getLastError() { return mErrorCode; } void clear(); }; } /* namespace J1939 */ #endif /* SRC_JSONPARSER_H_ */ ================================================ FILE: J1939/include/J1939Factory.h ================================================ /* * J1939Factory.h * * Created on: Sep 23, 2017 * Author: famez */ /* MIT License Copyright (c) 2018 Fernando Ámez García Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef J1939FACTORY_H_ #define J1939FACTORY_H_ #include #include #include #include #include namespace J1939 { class J1939Frame; class J1939Factory : public ISingleton { SINGLETON_ACCESS; virtual ~J1939Factory(); private: J1939Factory(); std::map mFrames; /* * Registers the predefined frames that we can find in J1939Protocol */ void registerPredefinedFrames(); public: /* * Returns the corresponding frame (if registered) from the given id and decodes the information from data and length */ std::unique_ptr getJ1939Frame(u32 id, const u8* data, size_t length); /* * Returns the corresponding frame (if registered) from the given PGN */ std::unique_ptr getJ1939Frame(u32 pgn); /* * Returns the corresponding frame (if registered) from the given name */ std::unique_ptr getJ1939Frame(const std::string& name); /* * Registers the given frame in the factory letting the factory to create a copy of it and, if neccesary, decoding it. */ bool registerFrame(const J1939Frame&); void unRegisterFrame(u32 pgn); bool registerDatabaseFrames(const std::string& ddbbFile); void unregisterAllFrames(); std::set getAllRegisteredPGNs() const; }; } /* namespace J1939 */ #endif /* J1939FACTORY_H_ */ ================================================ FILE: J1939/include/J1939Frame.h ================================================ /* * J1939Frame.h * * Created on: Sep 23, 2017 * Author: famez * * * */ /* MIT License Copyright (c) 2018 Fernando Ámez García Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef J1939FRAME_H_ #define J1939FRAME_H_ #include #include #include #include "J1939Common.h" namespace J1939 { enum EJ1939Status { J1939_STATUS_OFF = 0, J1939_STATUS_ON = 1, J1939_STATUS_ERROR = 2, J1939_STATUS_NOT_AVAILABLE = 3, }; enum EJ1939PduFormat { PDU_FORMAT_1, PDU_FORMAT_2 }; class J1939Frame : public ICloneable { private: u8 mPriority; u8 mSrcAddr; u32 mPgn; u8 mDstAddr; protected: std::string mName; public: J1939Frame(u32 pgn); virtual ~J1939Frame(); u8 getPriority() const { return mPriority; } bool setPriority(u8 priority) { mPriority = (priority & J1939_PRIORITY_MASK); return (mPriority == priority); } u8 getExtDataPage() const { return ((mPgn >> J1939_EXT_DATA_PAGE_OFFSET) & J1939_EXT_DATA_PAGE_MASK); } u8 getDataPage() const { return ((mPgn >> J1939_DATA_PAGE_OFFSET) & J1939_DATA_PAGE_MASK); } u8 getPDUFormat() const { return ((mPgn >> J1939_PDU_FMT_OFFSET) & J1939_PDU_FMT_MASK); } u8 getPDUSpecific() const { return mPgn & J1939_PDU_SPECIFIC_MASK; } u8 getSrcAddr() const { return mSrcAddr; } void setSrcAddr(u8 src) { mSrcAddr = src; } /* * If the value in the PDU Format segment is < 240, the content of PDU Specific is interpreted as the destination address. * One speaks here of a PGN in PDU Format 1 or of a specific PGN. A PGN in PDU Format 1 can be sent explicitly to a destination address * using point-to-point communication, but the global address (255) can also be used. In this way a specific PGN can also be transmitted globally, i.e., to all network nodes. * If the PDU Format segment has a value >= 240, the PDU Specific segment is interpreted as a group extension. * This means that there is no destination address and the message will always be transmitted to all network nodes. * PDU Format and PDU Specific represent a 16-bit value that corresponds to the PGN. In this case, the PGN has PDU Format 2 and is called global PGN. */ EJ1939PduFormat getPDUFormatGroup() const { if(getPDUFormat() < PDU_FMT_DELIMITER) { return PDU_FORMAT_1; } return PDU_FORMAT_2; } u8 getDstAddr() const { return mDstAddr; } bool setDstAddr(u8 dst) { if(getPDUFormatGroup() == PDU_FORMAT_1) { mDstAddr = dst; return true; } return false; } //Methods to decode/encode data void decode(u32 identifier, const u8* buffer, size_t length); void encode(u32& identifier, u8* buffer, size_t& length) const; u32 getIdentifier() const; protected: /** * Decodes the given data */ virtual void decodeData(const u8* buffer, size_t length) = 0; /** * Encodes the data field in the given buffer * Length is used as input to check the length of the buffer and then set to the number of encoded bytes (which is always less or equal than the given length) */ virtual void encodeData(u8* buffer, size_t length) const = 0; public: u32 getPGN() const { return mPgn; } /** * Method to know how long our buffer should be to encode properly this message */ virtual size_t getDataLength() const = 0; /** * Method to get the frame name */ const std::string& getName() const { return mName; } /** * Method to copy one frame to another. The frames must be exactly of the same type * * */ virtual void copy(const J1939Frame& other); virtual bool isGenericFrame() const { return false; } /* * Returns a string with the following information: * Name * PGN * Source Address * PDU format * Dest Address * Priority */ std::string getHeader() const; /* * Returns a string with the header and further details (generally the contents of the frame, such as SPNs if it applies) */ virtual std::string toString() const { return getHeader(); } }; } /* namespace J1939 */ #endif /* J1939FRAME_H_ */ ================================================ FILE: J1939/include/SPN/SPN.h ================================================ /* * SPN.h * * Created on: Oct 24, 2017 * Author: root */ #ifndef SPN_H_ #define SPN_H_ #include #include #include #include #include namespace J1939 { class GenericFrame; class SPN : public ICloneable { public: enum EType { SPN_NUMERIC = 0, SPN_STATUS = 1, SPN_STRING = 2 }; private: std::shared_ptr mSpec; protected: GenericFrame *mOwner = nullptr; //Owner of this spn public: SPN(u32 number, const std::string& name, size_t offset); virtual ~SPN(); virtual size_t getOffset() const { return mSpec->getOffset(); } virtual void setOffset(size_t offset) {} //Do nothing u32 getSpnNumber() const { return mSpec->getSpnNumber(); } const std::string& getName() const { return mSpec->getName(); } std::shared_ptr getSpec() const { return mSpec; } void setOwner(GenericFrame* owner) { mOwner = owner; } //To implement by inherited classes virtual EType getType() const = 0; virtual void decode(const u8* buffer, size_t length) = 0; virtual void encode(u8* buffer, size_t length) const = 0; virtual std::string toString() const; virtual u8 getByteSize() const = 0; virtual void copy(const SPN& other) = 0; }; } /* namespace J1939 */ #endif /* SPN_H_ */ ================================================ FILE: J1939/include/SPN/SPNHistory.h ================================================ /* * SPNHistory.h * * Created on: 12 Feb 2019 * Author: fernando * * For the moment, it only supports history for SPN status and SPN numeric */ #ifndef J1939_HISTORIC_SPNHISTORY_H_ #define J1939_HISTORIC_SPNHISTORY_H_ #include #include #include #include #include #include #include #include namespace J1939 { class SPNHistory { public: std::shared_ptr mGeneralSpec; struct { std::shared_ptr numeric; std::shared_ptr status; } mSpecificSpec; class Sample { private: Utils::TimeStamp mTimeStamp; union { double numeric; u8 status; } mValue; public: Sample() = default; Sample(const Utils::TimeStamp timeStamp, double value) : mTimeStamp(timeStamp) { mValue.numeric = value; } Sample(const Utils::TimeStamp timeStamp, u8 status) : mTimeStamp(timeStamp) { mValue.status = status; } void setTimeStamp(const Utils::TimeStamp& timestamp) { mTimeStamp = timestamp; } const Utils::TimeStamp& getTimeStamp() const { return mTimeStamp; } double getNumeric() const { return mValue.numeric; } u8 getStatus() const { return mValue.status; } bool operator<(const Sample& other) const { return mTimeStamp < other.mTimeStamp; } }; private: std::vector mSamples; //All the samples that have been stored public: SPNHistory(); virtual ~SPNHistory(); void addSample(const Utils::TimeStamp& timeStamp, const SPN& spn); /* * Gets the window of stored samples. * [in] milliseconds indicates the duration of the window in millis * [in] samples indicates the number of samples contained in the window * [in] timeStamp indicates the final timestamp of the window */ std::vector getWindow(const Utils::TimeStamp& timeStamp, u32 milliseconds, u32 samples) const; std::shared_ptr getGeneralSpec() const { return mGeneralSpec; } std::shared_ptr getNumericSpec() const { return mSpecificSpec.numeric; } std::shared_ptr getStatusSpec() const { return mSpecificSpec.status; } }; } /* namespace J1939 */ #endif /* J1939_HISTORIC_SPNHISTORY_H_ */ ================================================ FILE: J1939/include/SPN/SPNNumeric.h ================================================ /* * SPNNumeric.h * * Created on: Oct 24, 2017 * Author: root */ #ifndef SPN_SPNNUMERIC_H_ #define SPN_SPNNUMERIC_H_ #include #include namespace J1939 { class SPNNumeric: public SPN { private: std::shared_ptr mNumSpec; u32 mValue; public: SPNNumeric(u32 number, const std::string& name = "", size_t offset = 0, double formatGain = 0, double formatOffset = 0, u8 byteSize = 0, const std::string& units = ""); virtual ~SPNNumeric(); double getFormattedValue() const ; bool setFormattedValue(double value); void decode(const u8* buffer, size_t length); void encode(u8* buffer, size_t length) const; EType getType() const { return SPN_NUMERIC; } u8 getByteSize() const { return mNumSpec->getByteSize(); } double getFormatGain() const { return mNumSpec->getFormatGain(); } double getFormatOffset() const { return mNumSpec->getFormatOffset(); } const std::string& getUnits() const { return mNumSpec->getUnits(); } u32 getValue() const { return mValue; } void setValue(u32 value) { mValue = value; } /* * Returns the maximum value for the given spn */ u32 getMaxValue() const { return mNumSpec->getMaxValue(); } std::string toString() const override; std::shared_ptr getNumericSpec() const { return mNumSpec; } void copy(const SPN& other) override; IMPLEMENT_CLONEABLE(SPN, SPNNumeric); }; } /* namespace J1939 */ #endif /* SPN_SPNNUMERIC_H_ */ ================================================ FILE: J1939/include/SPN/SPNSpec/SPNNumericSpec.h ================================================ /* * SPNNumericSpec.h * * Created on: Oct 24, 2017 * Author: root */ #ifndef SPN_SPNNUMERIC_SPEC_H_ #define SPN_SPNNUMERIC_SPEC_H_ #include #include namespace J1939 { class SPNNumericSpec { private: double mFormatGain; double mFormatOffset; u8 mByteSize; std::string mUnits; public: SPNNumericSpec(double formatGain = 0, double formatOffset = 0, u8 byteSize = 0, const std::string& units = ""); virtual ~SPNNumericSpec(); u8 getByteSize() const { return mByteSize; } void setByteSize(u8 size) { mByteSize = size; } double getFormatGain() const { return mFormatGain; } void setFormatGain(double gain) { mFormatGain = gain; } double getFormatOffset() const { return mFormatOffset; } void setFormatOffset(double formatOffset) { mFormatOffset = formatOffset; } const std::string& getUnits() const { return mUnits; } void setUnits(const std::string& units) { mUnits = units; } /* * Returns the maximum value for the given spn */ u32 getMaxValue() const; double formatValue(u32 value) const; double getMaxFormattedValue() const { return formatValue(getMaxValue()); } double getMinFormattedValue() const { return formatValue(0); } }; } /* namespace J1939 */ #endif /* SPN_SPNNUMERIC_SPEC_H_ */ ================================================ FILE: J1939/include/SPN/SPNSpec/SPNSpec.h ================================================ /* * SPNSpec.h * * Created on: Oct 24, 2017 * Author: root */ #ifndef SPN_SPEC_H_ #define SPN_SPEC_H_ #include #include namespace J1939 { class SPNSpec { private: u32 mSPNNumber; std::string mName; size_t mOffset; public: SPNSpec(u32 number, const std::string& name, size_t offset); virtual ~SPNSpec(); size_t getOffset() const { return mOffset; } void setOffset(size_t offset) { mOffset = offset; } u32 getSpnNumber() const { return mSPNNumber; } void setSpnNumber(u32 spnNumber) { mSPNNumber = spnNumber; } const std::string& getName() const { return mName; } void setName(const std::string& name) { mName = name; } }; } /* namespace J1939 */ #endif /* SPN_SPEC_H_ */ ================================================ FILE: J1939/include/SPN/SPNSpec/SPNStatusSpec.h ================================================ /* * SPNStatusSpec.h * * Created on: Oct 24, 2017 * Author: root */ #ifndef SPN_SPNSTATUS_SPEC_H_ #define SPN_SPNSTATUS_SPEC_H_ #include #include namespace J1939 { class SPNStatusSpec { public: typedef std::map DescMap; private: u8 mBitOffset; u8 mBitSize; /* * Convertion from the status number to its description */ DescMap mValueToDesc; public: SPNStatusSpec(u8 bitOffset = 0, u8 bitSize = 0, SPNStatusSpec::DescMap valueToDesc = SPNStatusSpec::DescMap()); virtual ~SPNStatusSpec(); u8 getBitOffset() const { return mBitOffset; } void setBitOffset(u8 off) { mBitOffset = off; } u8 getBitSize() const { return mBitSize; } void setBitSize(u8 size) { mBitSize = size; } u8 getBitMask() const { return (0xFF >> (8 - mBitSize)) << mBitOffset; } /* * Methods to add/get descriptions over the different status numbers */ void setValueDescription(u8 value, const std::string& desc); std::string getValueDescription(u8 value) const; void clearValueDescriptions(); DescMap getValueDescriptionsMap() const; }; } /* namespace J1939 */ #endif /* SPN_SPNSTATUS_SPEC_H_ */ ================================================ FILE: J1939/include/SPN/SPNStatus.h ================================================ /* * SPNStatus.h * * Created on: Oct 24, 2017 * Author: root */ #ifndef SPN_SPNSTATUS_H_ #define SPN_SPNSTATUS_H_ #include #include #include namespace J1939 { class SPNStatus: public SPN { public: typedef std::map DescMap; private: u8 mValue; std::shared_ptr mStatSpec; public: SPNStatus(u32 number, const std::string& name = "", size_t offset = 0, u8 bitOffset = 0, u8 bitSize = 0, SPNStatusSpec::DescMap valueToDesc = SPNStatusSpec::DescMap()); virtual ~SPNStatus(); void decode(const u8* buffer, size_t length); void encode(u8* buffer, size_t length) const; EType getType() const { return SPN_STATUS; } u8 getBitOffset() const { return mStatSpec->getBitOffset(); } u8 getBitSize() const { return mStatSpec->getBitSize(); } u8 getValue() const { return mValue; } bool setValue(u8 value); std::string toString() const override; u8 getByteSize() const override { return 1; } //Spn status has always size of 1 std::string getValueDescription(u8 value) const { return mStatSpec->getValueDescription(value); } DescMap getValueDescriptionsMap() const { return mStatSpec->getValueDescriptionsMap(); } std::shared_ptr getStatusSpec() const { return mStatSpec; } void copy(const SPN& other) override; IMPLEMENT_CLONEABLE(SPN, SPNStatus); }; } /* namespace J1939 */ #endif /* SPN_SPNSTATUS_H_ */ ================================================ FILE: J1939/include/SPN/SPNString.h ================================================ /* * SPNString.h * * Created on: 6 juil. 2018 * Author: fernando */ #ifndef SPN_SPNSTRING_H_ #define SPN_SPNSTRING_H_ #include #include namespace J1939 { class SPNString: public SPN { private: size_t mOffset = 0; std::string mValue; public: SPNString(u32 number, const std::string& name); virtual ~SPNString(); void decode(const u8* buffer, size_t length) override; void encode(u8* buffer, size_t length) const override; EType getType() const { return SPN_STRING; } std::string toString() const override; u8 getByteSize() const override { return mValue.size() + 1; } //Include the * terminator std::string getValue() const { return mValue; } void setValue(std::string value); size_t getOffset() const override { //SPNString have variable offsets return mOffset; } void setOffset(size_t offset) override { mOffset = offset; } void copy(const SPN& other) override; IMPLEMENT_CLONEABLE(SPN, SPNString); }; } /* namespace J1939 */ #endif /* SPN_SPNSTRING_H_ */ ================================================ FILE: J1939/include/Transport/BAM/BamFragmenter.h ================================================ /* * BamFragmenter.h * * Created on: Apr 22, 2018 * Author: fernado */ #ifndef TRANSPORT_BAM_BAMFRAGMENTER_H_ #define TRANSPORT_BAM_BAMFRAGMENTER_H_ #include #include #include "../TPCMFrame.h" #include "../TPDTFrame.h" namespace J1939 { class BamFragmenter{ private: TPCMFrame mCMFrame; std::map mDTFrames; public: BamFragmenter(); virtual ~BamFragmenter(); bool fragment(const J1939Frame& frame); void clear() { mDTFrames.clear(); mCMFrame.clear(); } const TPCMFrame& getConnFrame() const { return mCMFrame; } std::vector getDataFrames() const; }; } /* namespace J1939 */ #endif /* TRANSPORT_BAM_BAMFRAGMENTER_H_ */ ================================================ FILE: J1939/include/Transport/BAM/BamReassembler.h ================================================ /* * * Created on: Oct 2, 2017 * Author: famez * * * Global communication – Broadcast Announce Message (BAM): * The sender alone manages the flow control. * The message is always sent to all nodes. * A receiver cannot intervene in the communication. * If the receiver misses a message, it cannot signal this. * The receiver must wait for a new message, if necessary. * Because the receiver is not able to influence the flow control in the BAM protocol, the sender must maintain a minimum interval between the individual packets. * This is 50-200 ms. This allows possible slow network nodes to follow the communication. * */ #ifndef BAMFRAMESET_H_ #define BAMFRAMESET_H_ #include #include #include #include #include "../TPCMFrame.h" #include "../TPDTFrame.h" namespace J1939 { class BamReassembler { public: enum EBamError { BAM_ERROR_INCOMPLETE_FRAME, BAM_ERROR_UNEXPECTED_FRAME, BAM_ERROR_NOT_BCAST_ADDR, BAM_ERROR_DECODING, BAM_ERROR_OK, }; private: class BAMFragments { private: TPCMFrame mCMFrame; std::vector mDTFrames; public: const TPCMFrame& getCmFrame() const { return mCMFrame; } void setCmFrame(const TPCMFrame& cmFrame) { mCMFrame = cmFrame; } const std::vector& getDtFrames() const { return mDTFrames; } void addDtFrame(const TPDTFrame& dtFrame) { mDTFrames.push_back(dtFrame); } u8 getLastSQ() const; }; //Fragments of frames sortered by Source Address std::map mFragments; EBamError mLastError; std::queue mReassembledFrames; void reassemble(const BAMFragments& fragments, u8** data, size_t& length); public: BamReassembler(); virtual ~BamReassembler(); bool toBeHandled(const J1939Frame&) const; size_t handleFrame(const J1939Frame&); void clear(); void setError(EBamError status) { mLastError = status; } EBamError getLastError() { return mLastError; } bool reassembledFramesPending() const { return !mReassembledFrames.empty(); } std::unique_ptr dequeueReassembledFrame(); }; } /* namespace J1939 */ #endif /* BAMFRAMESET_H_ */ ================================================ FILE: J1939/include/Transport/RTSCTS/RTSCTSConnectionManager.h ================================================ /* * RTSCTSConnectionManager.h * * Created on: Oct 15, 2017 * Author: famez * * Specific communication – Connection Mode Data Transfer: * With this protocol the sender establishes a connection to the receiver. * The receiver has the option of controlling and influencing the flow control of the individual data packets. * Both the receiver and sender can abort the connection (e.g. in case of errors). * The Connection Mode Data Transfer protocol is not subject to any time limitation. * All nodes potentially exchange their data with one another at their maximum possible speed. * */ #ifndef TRANSPORT_RTSCTS_RTSCTSCONNECTIONMANAGER_H_ #define TRANSPORT_RTSCTS_RTSCTSCONNECTIONMANAGER_H_ #include "../ConnectionManager.h" namespace J1939 { class RTSCTSConnectionManager : public ConnectionManager { public: RTSCTSConnectionManager(); virtual ~RTSCTSConnectionManager(); void consumeFrame(const J1939Frame&); }; } /* namespace J1939 */ #endif /* TRANSPORT_RTSCTS_RTSCTSCONNECTIONMANAGER_H_ */ ================================================ FILE: J1939/include/Transport/TPCMFrame.h ================================================ /* * BAMHeaderFrame.h * * Created on: Oct 2, 2017 * Author: famez */ #ifndef FRAMES_BAMHEADERFRAME_H_ #define FRAMES_BAMHEADERFRAME_H_ #include "../J1939Frame.h" #define TP_CM_PGN 0x00EC00 #define TP_CM_SIZE 8 //CM Types /* * The TP.CM_RTS message informs a node that another node on the network wishes to open a virtual connection with it. * The TP.CM_RTS is a message with the source address field set to that of the originating node, the destination address * field set to that of the intended recipient of a large message, and the remaining fields set appropriately for the Parameter * Group Number being sent. * Byte 5 of this message allows the originator to limit the responder’s number of packets specified in the Clear To Send * message. When the responder complies with this limit, it ensures that the * originator can always retransmit packets that the responder may have not received for whatever reason. * If multiple RTSs are received from the same source address for the same PGN, then the most recent RTS shall be acted * on and the previous RTSs will be abandoned. No abort message shall be sent for the abandoned RTSs in this specific * case. * TP.CM_RTS is only transmitted by the originator. */ #define CTRL_TPCM_RTS 16 /* * The TP.CM_CTS message is used to respond to the Request To Send message. It informs the peer node that it is ready for a certain amount of large message data. The amount of large message data cleared to send shall be no larger than the smaller of the two values in byte 4 and byte 5 of the originator’s TP.CM_RTS message. If multiple CTSs are received after a connection is already established, then the connection shall be aborted. When the originator aborts the connection, it shall send the Connection Abort message with abort reason 4 from Table 7. The responder will not send the next CTS until it has received the last data packet from the previous CTS or it has timed out. In the case of time out the responder has the choice whether to send a connection abort or to send a CTS. The following cases exist when data transfer happens with errors: When the CTS is used to request the retransmission of data packet(s), it is recommended not to use more than 2 retransmit requests. When this limit is reached, a connection abort with abort reason 5 from Table 7 shall be sent. If a CTS is received while a connection is not established, it shall be ignored. CTSs not only control the flow but also confirm correct receipt of any data packet prior to that CTS packet’s number. Therefore if information for the previous CTS was corrupted, then a CTS for the corrupted information shall be sent before continuing on to the next sequential packets to be sent. Because of this requirement, the originator of a large message transmission may use byte 5 of the TP.CM_RTS message as a way to ensure the possibility of retransmission of a packet within the last set of packets cleared to send. */ #define CTRL_TPCM_CTS 17 /* * The TP.CM_EndOfMsgACK message is passed from the recipient of a large message to its originator indicating that the entire message was received and reassembled correctly. The responder can keep the connection open after the last Data Transfer of the session by not immediately sending the TP.CM_EndOfMsgACK. This allows the responder to get a packet resent if necessary. If an End of Message Acknowledgment is received by the originator prior to the final Data Transfer, then the originator ignores it. One End of Message Acknowledgment is sent to show the originator that the large message transfer has been received and assembled correctly. TP.CM_EndOfMsgACK is only transmitted by the responder. */ #define CTRL_TPCM_ACK 19 /* * The TP.CM_BAM is used to inform all the nodes of the network that a large message is about to be broadcast. It defines the parameter group and the number of bytes to be sent. After TP.CM_BAM is sent, the Data Transfer Messages are sent and they contain the packetized broadcast data. TP.CM_BAM is only transmitted by the originator. */ #define CTRL_TPCM_BAM 32 /* * The TP.Conn_Abort message is used by either node involved in a virtual connection to close the connection without completing the transfer of the message or to prevent a connection from being initialized. Upon receipt of a Connection Mode Request To Send message, a node must determine if there are sufficient resources available to deal with the message for which this connection is sought. For example if the device must acquire memory from the system heap, it may not be able to claim enough to accept the entire message; or a device may simply be too occupied doing other things to expend processor cycles to handle a large message. In these cases a Connection Abort message may be sent even though the connection has not been established. This may be done in order to allow the originator to attempt another virtual connection without first having to wait for a timeout to occur. SAE J1939-21 DEC2010 Revised - A missing or errant packet(s) is detected and the last packet is successfully received, then the responder will send a CTS requesting retransmission starting from the missing packet. - Missing packet(s) including the last packet will lead to time out T1. In this case, the responder decides on sending a CTS or a connection abort. When either the originator or responder decides to close out a connection for any reason, prior to completing the data transfer, including a timeout, it shall send a Connection Abort message with the appropriate Connection Abort reason. */ #define CTRL_TPCM_ABORT 255 //Connection abort reason /* * Already in one or more connection managed sessions and cannot support another. */ #define CONN_ABORT_NOT_SUPPORTED 1 /* * System resources were needed for another task so this connection managed session was terminated. */ #define CONN_ABORT_TERMINATED 2 /* * A timeout occurred and this is the connection abort to close the session. */ #define CONN_ABORT_TIMEOUT 3 /* * CTS messages received when data transfer is in progress. */ #define CONN_ABORT_IN_PROGRESS 4 /* * Maximum retransmit request limit reached */ #define CONN_ABORT_RTX_LIMIT 5 namespace J1939 { class TPCMFrame: public J1939Frame { public: private: /* * Control byte */ u8 mCtrlType; /* * Total message size, number of bytes */ u16 mTotalMsgSize; /* * Total number of packets */ u8 mTotalPackets; /* * Maximum number of packets that can be sent in response to one CTS. FF16 indicates that no limit exists for the originator. */ u8 mMaxPackets; /* * Number of packets that can be sent. This value shall be no larger than the smaller of the two values in byte 4 and byte 5 of the RTS message. */ u8 mPacketsToTx; /* * Next packet number to be sent */ u8 mNextPacket; /* * Connection Abort reason */ u8 mAbortReason; /* * Parameter Group Number of the packeted message */ u32 mDataPgn; void decodeRTS(const u8* buffer); void decodeCTS(const u8* buffer); void decodeEndOfMsgACK(const u8* buffer); void decodeConnAbort(const u8* buffer); void decodeBAM(const u8* buffer); void encodeRTS(u8* buffer) const; void encodeCTS(u8* buffer) const; void encodeEndOfMsgACK(u8* buffer) const; void encodeConnAbort(u8* buffer) const; void encodeBAM(u8* buffer) const; public: TPCMFrame(); virtual ~TPCMFrame(); void clear(); //Implements J1939Frame methods void decodeData(const u8* buffer, size_t length); void encodeData(u8* buffer, size_t length) const; size_t getDataLength() const { return TP_CM_SIZE; } u8 getAbortReason() const { return mAbortReason; } u8 getCtrlType() const { return mCtrlType; } u32 getDataPgn() const { return mDataPgn; } u8 getMaxPackets() const { return mMaxPackets; } u8 getNextPacket() const { return mNextPacket; } u8 getPacketsToTx() const { return mPacketsToTx; } u16 getTotalMsgSize() const { return mTotalMsgSize; } u8 getTotalPackets() const { return mTotalPackets; } void setAbortReason(u8 abortReason) { mAbortReason = abortReason; } void setCtrlType(u8 ctrlType) { mCtrlType = ctrlType; } void setDataPgn(u32 dataPgn) { mDataPgn = dataPgn; } void setMaxPackets(u8 maxPackets) { mMaxPackets = maxPackets; } void setNextPacket(u8 nextPacket) { mNextPacket = nextPacket; } void setPacketsToTx(u8 packetsToTx) { mPacketsToTx = packetsToTx; } void setTotalMsgSize(u16 totalMsgSize) { mTotalMsgSize = totalMsgSize; } void setTotalPackets(u8 totalPackets) { mTotalPackets = totalPackets; } IMPLEMENT_CLONEABLE(J1939Frame,TPCMFrame); }; } /* namespace J1939 */ #endif /* FRAMES_BAMHEADERFRAME_H_ */ ================================================ FILE: J1939/include/Transport/TPDTFrame.h ================================================ /* * BamDataframe.h * * Created on: Oct 2, 2017 * Author: famez */ #ifndef FRAMES_BAMDATAFRAME_H_ #define FRAMES_BAMDATAFRAME_H_ #include #include "../J1939Frame.h" #define TP_DT_PGN 0x00EB00 #define BAM_DT_SIZE 8 namespace J1939 { class TPDTFrame : public J1939Frame { private: u8 mSQ; u8 mData[TP_DT_PACKET_SIZE]; public: TPDTFrame(); TPDTFrame(u8 sq, u8* data, size_t length); virtual ~TPDTFrame(); //Implements J1939Frame methods void decodeData(const u8* buffer, size_t length); void encodeData(u8* buffer, size_t length) const; size_t getDataLength() const { return BAM_DT_SIZE; } const u8* getData() const { return mData; } void setData(u8* data) { memcpy(mData, data, TP_DT_PACKET_SIZE); } u8 getSq() const { return mSQ; } void setSq(u8 sq) { mSQ = sq; } IMPLEMENT_CLONEABLE(J1939Frame,TPDTFrame); }; } /* namespace J1939 */ #endif /* FRAMES_BAMDATAFRAME_H_ */ ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2018 Fernando Ámez García (famez) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![Travis Build Status](https://travis-ci.org/famez/J1939-Framework.svg?branch=master)](https://travis-ci.com/famez/J1939-Framework) # J1939-Framework Framework to work with J1939 protocol. J1939 protocol is a standard used in different systems compliant with CAN 2.0B specification. The framework has been developed in C++ in a Linux distribution and compiled using the GNU toolchain. No dependencies are required for the compilation of any of the projects except from SocketCan compiled in the Linux Kernel. ## What can you do with J1939-Framework - Save can frames from the Can Bus into recordings in TRC format with BinUtils/TRCDumper. - Play can frames from recordings in TRC format into the Can Bus with BinUtils/TRCPlayer. - Convert TRC files into pcap files readable by wireshark with BinUtils/TRCToCap. - Dissect pcap files with wireshark and the J1939 plugin dissector (wireshark/dissector). - Sniff frames from the Can Bus compliant with J1939 protocol with BinUtils/j1939Sniffer. - Decode raw J1939 data to human readable data with BinUtils/j1939Decoder. - Craft your own J1939 frames and send them to the Can Bus with BinUtils/j1939Sender. The functionality can be extended with the help of bash scripts located in Scripts (some examples are listed). - Visualize what is going on in the Can Bus with GUI_WEB. You will be able to craft, send and visualize the frames that are flowing in the Bus as well as visualizing graphics of their content (SPNs). - Discover J1939 devices with BinUtils/j1939AddressMapper. - Simulation of the Address Claim Process with BinUtils/j1939AddrClaim. ### And of course, develop!!: - In CAN/ folder we can find a library in C++ (libCAN.so) with methods to generate and sniff can frames with support for PeakCan and SocketCan. - In J1939/ folder we can find a library in C++ (libJ1939.so) to easily manipulate J1939 frames and work with the J1939 protocol. Some features are: Support of BAM protocol. A factory class in charge of generating the J1939 frames. A database loaded by the factory located in Database/frames.json with a list of the most used Application Layer frames (including the FMS protocol). Coding/Decoding DM1 (Diagnosis), FMS1 (TTS), Request and Address Claim frames. Coding/Decoding of SPNs (String, status and numeric). ## Installing and compiling # Installation: Ubuntu 18.10 or higher ```bash sudo apt-get install libgtest-dev protobuf-compiler libprotobuf-dev libncurses-dev libwebsockets cmake cd /usr/src/gtest sudo env "PATH=$PATH" cmake CMakeLists.txt sudo make sudo cp *.a /usr/lib git clone https://github.com/open-source-parsers/jsoncpp.git cd jsoncpp git checkout 863aa36165acfdbaf22447f4934f5adc327692a0 cmake -DCMAKE_POSITION_INDEPENDENT_CODE=ON sudo make install cd ~ git clone https://github.com/famez/J1939-Framework.git cd J1939-Framework cmake . cmake --build . sudo make install ``` # Usage with SocketCan: To make SocketCan available in your system, you should execute the following commands: ```bash sudo apt-get install can-utils ``` To enable the drivers if they not enabled by default: ```bash sudo modprobe can sudo modprobe vcan ``` To generate a virtual interface for test purposes. ```bash sudo ip link add name vcan0 type vcan ``` To test over vcan0. ```bash ./Scripts/gear_level.sh vcan0 | j1939Sender --silent --file Scripts/define_frames.j1939 candump vcan0 ``` ## TRCPlayer ![alt text](https://github.com/famez/J1939-Framework/blob/master/BinUtils/TRCPlayer/TRCPlayer.png) ## Wireshark dissector ![alt text](https://github.com/famez/J1939-Framework/blob/master/wireshark/dissector/J1939-plugin.png) ## J1939GUI ![alt text](https://github.com/famez/J1939-Framework/blob/master/Graph.png) ![alt text](https://github.com/famez/J1939-Framework/blob/master/GUI_WEB/J1939GUI.png) ================================================ FILE: Scripts/Readme.md ================================================ To run a script (i.e gear_level.sh): ```bash ./gear_level.sh vcan0 | j1939Sender --silent --file define_frames.j1939 ``` ================================================ FILE: Scripts/define_frames.j1939 ================================================ create frame name: ccvs title: CCVS create frame name: eec1 title: EEC1 create frame name: lfc title: LFC create frame name: vdhr title: VDHR create frame name: dd title: DD create frame name: efl title: EFL create frame name: vin title: VI create frame name: di title: DI create frame name: tco1 title: TCO1 create frame name: et1 title: ET1 create frame name: trf1 title: TRF1 create frame name: eec2 title: EEC2 create frame name: vdc2 title: VDC2 create frame name: ic1 title: IC1 create frame name: amb title: AMB create frame name: rf title: RF create frame name: hours title: HOURS create frame name: etc1 title: ETC1 create frame name: etc2 title: ETC2 create frame name: erc1 title: ERC1 create frame name: ebc1 title: EBC1 create frame name: as title: AS create frame name: at1t1i title: AT1T1I create frame name: dc1 title: DC1 create frame name: dc2 title: DC2 create frame name: ptode title: PTODE set frame ccvs period: 100 priority: 2 source: 20 set frame ptode period: 100 priority: 2 source: 20 set frame eec1 period: 20 priority: 2 source: 20 set frame lfc period: 1000 priority: 2 source: 20 set frame vdhr period: 1000 priority: 2 source: 20 set frame dd period: 1000 priority: 2 source: 20 set frame dc1 period: 100 priority: 2 source: 20 set frame dc2 period: 100 priority: 2 source: 20 set frame efl period: 500 priority: 2 source: 20 set frame et1 period: 1000 priority: 2 source: 20 set frame tco1 period: 50 priority: 2 source: 20 set frame vin period: 10000 priority: 2 source: 20 set frame di period: 10000 priority: 2 source: 20 set frame trf1 period: 1000 priority: 2 source: 20 set frame eec2 period: 50 priority: 2 source: 20 set frame vdc2 period: 10 priority: 2 source: 20 set frame ic1 period: 500 priority: 2 source: 20 set frame amb period: 1000 priority: 2 source: 20 set frame rf period: 1000 priority: 2 source: 20 set frame hours period: 5000 priority: 2 source: 20 set frame etc1 period: 10 priority: 2 source: 20 set frame etc2 period: 10 priority: 2 source: 20 set frame erc1 period: 50 priority: 2 source: 20 set frame ebc1 period: 100 priority: 2 source: 20 set frame as period: 1000 priority: 2 source: 20 set frame at1t1i period: 1000 priority: 2 source: 20 ================================================ FILE: Scripts/gear_level.sh ================================================ echo "set frame etc2 spn: 523 value: 0" echo "send frame etc2 interface: ${1}" sleep 5 echo "set frame etc2 spn: 523 value: 1" sleep 5 echo "set frame etc2 spn: 523 value: 2" sleep 5 echo "set frame etc2 spn: 523 value: 3" sleep 5 echo "set frame etc2 spn: 523 value: 4" sleep 5 echo "set frame etc2 spn: 523 value: 5" sleep 5 echo "set frame etc2 spn: 523 value: 6" sleep 5 echo "set frame etc2 spn: 523 value: -1" sleep 5 echo "set frame etc2 spn: 523 value: -5" sleep 5 ================================================ FILE: Scripts/simulate_dtc.sh ================================================ #!/bin/bash echo "create frame name: dm1 title: DM1" echo "set frame dm1 period: 1000 source: 10" echo "add dtc dm1 oc: 3 fmi: 5 spn: 52" echo "add dtc dm1 oc: 4 fmi: 6 spn: 78" echo "add dtc dm1 oc: 0 fmi: 7 spn: 145" echo "send frame dm1 interface: $1" sleep 5 echo "unsend frame dm1" echo "set dtc dm1 2 oc: 3 fmi: 7 spn: 145" echo "send frame dm1 interface: $1" sleep 5 echo "unsend frame dm1" echo "delete dtc dm1 2" echo "send frame dm1 interface: $1" sleep 10 ================================================ FILE: Scripts/simulate_fuel_cons.sh ================================================ #!/bin/bash cat < #include #include #include #include #include using namespace J1939; BamReassembler reassembler; TEST(BAM_test, BamFragmenter) { TestFrame frame1(0xF005); u32 id; { id = 0x00F00550; u8 raw[] = {0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF}; frame1.decode(id, raw, sizeof(raw)); } BamFragmenter fragmenter; fragmenter.fragment(frame1); const TPCMFrame& connFrame = fragmenter.getConnFrame(); { ASSERT_EQ(connFrame.getPGN(), TP_CM_PGN); ASSERT_EQ(connFrame.getSrcAddr(), 0x50); u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; size_t length = connFrame.getDataLength(); u8* buff = new u8[length]; connFrame.encode(id, buff, length); ASSERT_EQ(id, 0x00ECFF50); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } std::vector dataFrames = fragmenter.getDataFrames(); ASSERT_EQ(dataFrames.size(), 3); { ASSERT_EQ(dataFrames[0].getPGN(), TP_DT_PGN); ASSERT_EQ(dataFrames[0].getSrcAddr(), 0x50); u8 raw[] = {0x01, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; size_t length = dataFrames[0].getDataLength(); u8* buff = new u8[length]; dataFrames[0].encode(id, buff, length); ASSERT_EQ(id, 0x00EBFF50); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } { ASSERT_EQ(dataFrames[1].getPGN(), TP_DT_PGN); ASSERT_EQ(dataFrames[1].getSrcAddr(), 0x50); u8 raw[] = {0x02, 0x89, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF}; size_t length = dataFrames[1].getDataLength(); u8* buff = new u8[length]; dataFrames[1].encode(id, buff, length); ASSERT_EQ(id, 0x00EBFF50); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } { ASSERT_EQ(dataFrames[2].getPGN(), TP_DT_PGN); ASSERT_EQ(dataFrames[2].getSrcAddr(), 0x50); u8 raw[] = {0x03, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, 0xFF}; size_t length = dataFrames[2].getDataLength(); u8* buff = new u8[length]; dataFrames[2].encode(id, buff, length); ASSERT_EQ(id, 0x00EBFF50); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } } TEST(BAM_test, BamReassembler_ok) { u32 id; TestFrame frame1(0xF005); //We need to register the frame in the factory so that it can be reassembled by the BamReassembler class J1939Factory::getInstance().registerFrame(frame1); { id = 0x00ECFF50; u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_CM_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x01, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x02, 0x89, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x03, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, 0xFF}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_TRUE(reassembler.reassembledFramesPending()); } //Clean up the factory for following tests... J1939Factory::getInstance().unRegisterFrame(0xF005); } TEST(BAM_test, BamReassembler_ko1) { u32 id; TestFrame frame1(0xF005); reassembler.clear(); //We need to register the frame in the factory so that it can be reassembled by the BamReassembler class J1939Factory::getInstance().registerFrame(frame1); { id = 0x00ECFF50; u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_CM_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x02, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_UNEXPECTED_FRAME); } //Clean up the factory for following tests... J1939Factory::getInstance().unRegisterFrame(0xF005); } TEST(BAM_test, BamReassembler_ko2) { u32 id; TestFrame frame1(0xF005); reassembler.clear(); //We need to register the frame in the factory so that it can be reassembled by the BamReassembler class J1939Factory::getInstance().registerFrame(frame1); { id = 0x00ECFF50; u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_CM_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x01, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); } { id = 0x00EBFF50; u8 raw[] = {0x02, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); } { id = 0x00EBFF50; u8 raw[] = {0x03, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, 0xFF}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); } { id = 0x00EBFF50; u8 raw[] = {0x04, 0x1B, 0xC4, 0x7F, 0x7B, 0xCD, 0xEF, 0xFF}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_UNEXPECTED_FRAME); } //Clean up the factory for following tests... J1939Factory::getInstance().unRegisterFrame(0xF005); } TEST(BAM_test, BamReassembler_ko3) { u32 id; TestFrame frame1(0xF005); reassembler.clear(); //We need to register the frame in the factory so that it can be reassembled by the BamReassembler class J1939Factory::getInstance().registerFrame(frame1); { id = 0x00ECFF50; u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_CM_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x01, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); } //Receive a TP CM frame before receiving the rest of TP DT frames { id = 0x00ECFF50; u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_CM_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); //Error relative to the fact that the frame could not be assembled and we received another TP CM frame //that indicates the beginning of a frame to be reassembled. ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_INCOMPLETE_FRAME); ASSERT_FALSE(reassembler.reassembledFramesPending()); } //Clean up the factory for following tests... J1939Factory::getInstance().unRegisterFrame(0xF005); } TEST(BAM_test, BamReassembler_ko4) { u32 id; TestFrame frame1(0xF005); reassembler.clear(); //This time, we do not register the frame in the factory { id = 0x00ECFF50; u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_CM_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x01, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x02, 0x89, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } { id = 0x00EBFF50; u8 raw[] = {0x03, 0xAB, 0xCD, 0xEF, 0xAB, 0xCD, 0xEF, 0xFF}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_DECODING); ASSERT_FALSE(reassembler.reassembledFramesPending()); } } TEST(BAM_test, BamReassembler_ko5) { u32 id; TestFrame frame1(0xF005); reassembler.clear(); //We need to register the frame in the factory so that it can be reassembled by the BamReassembler class J1939Factory::getInstance().registerFrame(frame1); { id = 0x00ECFF50; u8 raw[] = {0x20, 0x14, 0x00, 0x03, 0xFF, 0x05, 0xF0, 0x00}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_CM_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_OK); ASSERT_FALSE(reassembler.reassembledFramesPending()); } //Receive frame from another source address { id = 0x00EBFF70; u8 raw[] = {0x01, 0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_NE(frame, nullptr); ASSERT_EQ(frame->getPGN(), TP_DT_PGN); ASSERT_TRUE(reassembler.toBeHandled(*frame)); reassembler.handleFrame(*frame); ASSERT_EQ(reassembler.getLastError(), BamReassembler::BAM_ERROR_UNEXPECTED_FRAME); } //Clean up the factory for following tests... J1939Factory::getInstance().unRegisterFrame(0xF005); } ================================================ FILE: Tests/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) # Locate GTest find_package(GTest REQUIRED) include_directories( include ${GTEST_INCLUDE_DIRS} ${J1939_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) add_executable(execTests main.cpp TestFrame.cpp SPNNumeric_test.cpp SPNStatus_test.cpp SPNString_test.cpp genericframe_test.cpp request_frame.cpp fms1_frame_test.cpp address_claim_frame_test.cpp j1939Factory_test.cpp database_test.cpp BAM_test.cpp ) target_link_libraries(execTests ${GTEST_LIBRARIES} pthread J1939 rt jsoncpp -rdynamic ) ================================================ FILE: Tests/SPNNumeric_test.cpp ================================================ #include #include #include using namespace J1939; TEST(SPNNumeric_test, constructor) { //Test constructor SPNNumeric numeric(100, "test_numeric", 3, 2.5, -2, 2, "%"); ASSERT_EQ(numeric.getSpnNumber(), 100); ASSERT_EQ(numeric.getName(), "test_numeric"); ASSERT_EQ(numeric.getOffset(), 3); ASSERT_EQ(numeric.getFormatGain(), 2.5); ASSERT_EQ(numeric.getFormatOffset(), -2); ASSERT_EQ(numeric.getByteSize(), 2); ASSERT_EQ(numeric.getUnits(), "%"); ASSERT_EQ(numeric.getType(), SPN::SPN_NUMERIC); } TEST(SPNNumeric_test, copy_constructor) { SPNNumeric numeric(100, "test_numeric", 3, 2.5, -2, 2, "%"); numeric.setFormattedValue(38); //Test copy constructor SPNNumeric numeric2(numeric); ASSERT_EQ(numeric2.getSpnNumber(), 100); ASSERT_EQ(numeric2.getName(), "test_numeric"); ASSERT_EQ(numeric2.getOffset(), 3); ASSERT_EQ(numeric2.getFormatGain(), 2.5); ASSERT_EQ(numeric2.getFormatOffset(), -2); ASSERT_EQ(numeric2.getByteSize(), 2); ASSERT_EQ(numeric2.getUnits(), "%"); ASSERT_EQ(numeric2.getType(), SPN::SPN_NUMERIC); ASSERT_EQ(numeric2.getFormattedValue(), 38); } TEST(SPNNumeric_test, encode) { SPNNumeric numeric(10, "test_byte_size_1", 2, 2, -10, 1, "L"); ASSERT_EQ(numeric.getByteSize(), 1); //Test encoding numeric.setFormattedValue(56); { size_t length = numeric.getByteSize(); u8* buff = new u8[length]; numeric.encode(buff, length); u8 raw[] = {0x21}; ASSERT_EQ(memcmp(buff, raw, length), 0); delete[] buff; } SPNNumeric numeric1(100, "test_byte_size_2", 3, 2.5, -2, 2, "%"); ASSERT_EQ(numeric1.getByteSize(), 2); //Test encoding numeric1.setFormattedValue(38); { size_t length = numeric1.getByteSize(); u8* buff = new u8[length]; numeric1.encode(buff, length); u8 raw[] = {0x10, 0x00}; ASSERT_EQ(memcmp(buff, raw, length), 0); delete[] buff; } SPNNumeric numeric2(200, "test_byte_size_3", 3, 2.1, -225, 3, "m"); ASSERT_EQ(numeric2.getByteSize(), 3); //Test encoding numeric2.setFormattedValue(5211937.2); { size_t length = numeric2.getByteSize(); u8* buff = new u8[length]; numeric2.encode(buff, length); u8 raw[] = {0x3E, 0xDF, 0x25}; ASSERT_EQ(memcmp(buff, raw, length), 0); delete[] buff; } SPNNumeric numeric3(250, "test_byte_size_4", 4, 0.05, 3000, 4, "Pa"); ASSERT_EQ(numeric3.getByteSize(), 4); //Test encoding numeric3.setFormattedValue(65495982.25); { size_t length = numeric3.getByteSize(); u8* buff = new u8[length]; numeric3.encode(buff, length); u8 raw[] = {0x3D, 0xDF, 0x12, 0x4E}; ASSERT_EQ(memcmp(buff, raw, length), 0); delete[] buff; } } TEST(SPNNumeric_test, decode) { try { SPNNumeric numeric(10, "test_byte_size_1", 2, 2, -10, 1, "L"); ASSERT_EQ(numeric.getByteSize(), 1); //Test decoding { u8 raw[] = {0x21}; numeric.decode(raw, sizeof(raw)); ASSERT_EQ(numeric.getFormattedValue(), 56); } SPNNumeric numeric1(100, "test_byte_size_2", 3, 2.5, -2, 2, "%"); ASSERT_EQ(numeric1.getByteSize(), 2); //Test decoding { u8 raw[] = {0x10, 0x00}; numeric1.decode(raw, sizeof(raw)); ASSERT_EQ(numeric1.getFormattedValue(), 38); } SPNNumeric numeric2(200, "test_byte_size_3", 3, 2.1, -225, 3, "m"); ASSERT_EQ(numeric2.getByteSize(), 3); //Test decoding { u8 raw[] = {0x3E, 0xDF, 0x25}; numeric2.decode(raw, sizeof(raw)); ASSERT_EQ(numeric2.getFormattedValue(), 5211937.2); } SPNNumeric numeric3(250, "test_byte_size_4", 4, 0.05, 3000, 4, "Pa"); ASSERT_EQ(numeric3.getByteSize(), 4); //Test decoding { u8 raw[] = {0x3D, 0xDF, 0x12, 0x4E}; numeric3.decode(raw, sizeof(raw)); ASSERT_EQ(numeric3.getFormattedValue(), 65495982.25); } SUCCEED(); } catch(J1939DecodeException&) { FAIL(); } try { SPNNumeric numeric3(250, "test_byte_size_4", 4, 0.05, 3000, 4, "Pa"); ASSERT_EQ(numeric3.getByteSize(), 4); //Test decoding { u8 raw[] = {0x3D, 0xDF, 0x12, 0x4E}; numeric3.decode(raw, 3); ASSERT_EQ(numeric3.getFormattedValue(), 65495982.25); } FAIL(); } catch(J1939DecodeException&) { SUCCEED(); } } ================================================ FILE: Tests/SPNStatus_test.cpp ================================================ #include #include #include using namespace J1939; TEST(SPNStatus_test, constructor) { //Test constructor SPNStatusSpec::DescMap valueToDesc { {0, "Desc 0"}, {1, "Desc 1"}, {2, "Desc 2"}, {3, "Desc 3"}, }; SPNStatus status(100, "test_status", 4, 2, 2, valueToDesc); ASSERT_EQ(status.getSpnNumber(), 100); ASSERT_EQ(status.getName(), "test_status"); ASSERT_EQ(status.getOffset(), 4); ASSERT_EQ(status.getBitOffset(), 2); ASSERT_EQ(status.getBitSize(), 2); ASSERT_EQ(status.getType(), SPN::SPN_STATUS); ASSERT_EQ(status.getValueDescription(0), "Desc 0"); ASSERT_EQ(status.getValueDescription(1), "Desc 1"); ASSERT_EQ(status.getValueDescription(2), "Desc 2"); ASSERT_EQ(status.getValueDescription(3), "Desc 3"); } TEST(SPNStatus_test, copy_constructor) { SPNStatusSpec::DescMap valueToDesc { {0, "Desc 0"}, {1, "Desc 1"}, {2, "Desc 2"}, {3, "Desc 3"}, }; SPNStatus status(100, "test_status", 4, 2, 2, valueToDesc); ASSERT_EQ(status.getBitSize(), 2); status.setValue(3); //Test copy constructor SPNStatus status2(status); ASSERT_EQ(status2.getSpnNumber(), 100); ASSERT_EQ(status2.getName(), "test_status"); ASSERT_EQ(status2.getOffset(), 4); ASSERT_EQ(status2.getBitOffset(), 2); ASSERT_EQ(status2.getBitSize(), 2); ASSERT_EQ(status2.getType(), SPN::SPN_STATUS); ASSERT_EQ(status2.getValueDescription(0), "Desc 0"); ASSERT_EQ(status2.getValueDescription(1), "Desc 1"); ASSERT_EQ(status2.getValueDescription(2), "Desc 2"); ASSERT_EQ(status2.getValueDescription(3), "Desc 3"); ASSERT_EQ(status2.getValue(), 3); } TEST(SPNStatus_test, encode) { try { { SPNStatus status1(1, "test_status1", 4, 0, 2); SPNStatus status2(2, "test_status2", 4, 2, 2); SPNStatus status3(3, "test_status3", 4, 4, 2); SPNStatus status4(4, "test_status4", 4, 6, 2); u8 val; //Test encoding status1.setValue(0); status2.setValue(1); status3.setValue(2); status4.setValue(3); status1.encode(&val, 1); status2.encode(&val, 1); status3.encode(&val, 1); status4.encode(&val, 1); ASSERT_EQ(val, 0xE4); } { SPNStatus status1(10, "test_status1", 1, 0, 2); SPNStatus status2(20, "test_status2", 1, 2, 2); SPNStatus status3(30, "test_status3", 1, 4, 4); u8 val; //Test encoding status1.setValue(2); status2.setValue(1); status3.setValue(7); status1.encode(&val, 1); status2.encode(&val, 1); status3.encode(&val, 1); ASSERT_EQ(val, 0x76); } { SPNStatus status1(10, "test_status1", 1, 0, 5); SPNStatus status2(20, "test_status2", 1, 5, 3); u8 val; //Test encoding status1.setValue(30); status2.setValue(6); status1.encode(&val, 1); status2.encode(&val, 1); ASSERT_EQ(val, 0xDE); } SUCCEED(); } catch(J1939EncodeException&) { FAIL(); } } ================================================ FILE: Tests/SPNString_test.cpp ================================================ #include #include #include using namespace J1939; TEST(SPNString_test, constructor) { //Test constructor SPNString string1(50, "string_example"); ASSERT_EQ(string1.getSpnNumber(), 50); ASSERT_EQ(string1.getName(), "string_example"); ASSERT_EQ(string1.getType(), SPN::SPN_STRING); ASSERT_TRUE(string1.getValue().empty()); } TEST(SPNString_test, copy_constructor) { std::string example = "my_example"; SPNString string1(50, "string_example"); string1.setValue(example); //Test copy constructor SPNString string2(string1); ASSERT_EQ(string2.getSpnNumber(), 50); ASSERT_EQ(string2.getName(), "string_example"); ASSERT_EQ(string2.getType(), SPN::SPN_STRING); ASSERT_EQ(string2.getValue(), example); ASSERT_EQ(string2.getByteSize(), example.size() + 1); } TEST(SPNString_test, offset) { SPNString string1(50, "string_example"); string1.setOffset(5); ASSERT_EQ(string1.getOffset(), 5); } TEST(SPNString_test, encode) { u8* buff; std::string testStr = "abcdefghijklmnopqrstuvwxyz"; SPNString string1(21, "string_example"); string1.setValue(testStr); size_t length = string1.getByteSize(); ASSERT_EQ(length, testStr.size() + 1); buff = new u8[length]; try { string1.encode(buff, length); ASSERT_EQ(memcmp(buff, (testStr + "*").c_str(), length), 0); SUCCEED(); } catch(J1939EncodeException&) { FAIL(); } delete[] buff; length = 5; buff = new u8[length]; try { string1.encode(buff, length); FAIL(); } catch(J1939EncodeException&) { SUCCEED(); } delete[] buff; } TEST(SPNString_test, decode) { std::string testStr = "zyxwvutsrqponmlkjihgfedcba"; SPNString string1(14, "string_example"); try { string1.decode((const u8*)((testStr + "*").c_str()), (testStr + "*").size()); ASSERT_EQ(testStr, string1.getValue()); SUCCEED(); } catch(J1939DecodeException&) { FAIL(); } try { string1.decode((const u8*)(testStr.c_str()), testStr.size()); FAIL(); } catch(J1939DecodeException&) { SUCCEED(); } } ================================================ FILE: Tests/TestFrame.cpp ================================================ /* * TestFrame.cpp */ #include #include namespace J1939 { void TestFrame::decodeData(const u8* buffer, size_t length) { mRaw.clear(); mRaw.append(buffer, length); } void TestFrame::encodeData(u8* buffer, size_t length) const { memcpy(buffer, mRaw.c_str(), length); } } /* namespace J1939 */ ================================================ FILE: Tests/address_claim_frame_test.cpp ================================================ #include #include using namespace J1939; TEST(AddressClaimFrame_test, encode) { { EcuName name(25898, 1256, 5, 16, 241, 90, 15, 2, false); AddressClaimFrame frame(name); u32 id; frame.setSrcAddr(0x35); frame.setDstAddr(0x25); frame.setPriority(5); size_t length = frame.getDataLength(); ASSERT_EQ(length, 8); u8 raw[] = {0x2A, 0x65, 0x00, 0x9D, 0x85, 0xF1, 0xB4, 0x2F}; u8 *buff = new u8[length]; frame.encode(id, buff, length); ASSERT_EQ(id, 0x14EE2535); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } } TEST(AddressClaimFrame_test, decode) { { AddressClaimFrame frame; u32 id = 0x14EE2535; size_t length = frame.getDataLength(); u8 raw[] = {0x2A, 0x65, 0x00, 0x9D, 0x85, 0xF1, 0xB4, 0x2F}; frame.decode(id, raw, length); ASSERT_EQ(frame.getDataLength(), 8); ASSERT_EQ(frame.getSrcAddr(), 0x35); ASSERT_EQ(frame.getDstAddr(), 0x25); ASSERT_EQ(frame.getPriority(), 5); const EcuName& name = frame.getEcuName(); ASSERT_EQ(name.getIdNumber(), 25898); ASSERT_EQ(name.getManufacturerCode(), 1256); ASSERT_EQ(name.getEcuInstance(), 5); ASSERT_EQ(name.getFunctionInstance(), 16); ASSERT_EQ(name.getFunction(), 241); ASSERT_EQ(name.getVehicleSystem(), 90); ASSERT_EQ(name.getVehicleSystemInstance(), 15); ASSERT_EQ(name.getIndustryGroup(), 2); ASSERT_EQ(name.getEcuInstance(), 5); ASSERT_EQ(name.isArbitraryAddressCapable(), false); } } ================================================ FILE: Tests/database/test1.json ================================================ [ { "name" : "Frame1", "pgn" : 65000, "length" : 0, "spns" : [ { "name" : "spn_string1", "number" : 30, "type" : 2 }, { "name" : "spn_string2", "number" : 62, "type" : 2 }, { "name" : "spn_string3", "number" : 72, "type" : 2 } ] }, { "name" : "Frame2", "pgn" : 65235, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number1", "number" : 260, "offset" : 2, "type" : 0, "units" : "%" }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status1", "number" : 2323, "offset" : 5, "type" : 1 } ] }, { "name" : "Frame3", "pgn" : 65262, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number2", "number" : 150, "offset" : 2, "type" : 0, "units" : "%" }, { "byteSize" : 2, "formatGain" : 10, "formatOffset" : 200, "name" : "spn_number3", "number" : 320, "offset" : 0, "type" : 0, "units" : "Pa" } }, { "name" : "Frame4", "pgn" : 44288, "length" : 6, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status2", "number" : 2323, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 3, "descriptions" : [ "description 1", "description 2", "description 3", "description 4", "description 5", "description 6", "description 7", "description 8" ], "name" : "spn_status3", "number" : 2525, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 4, "name" : "spn_status4", "number" : 1515, "offset" : 1, "type" : 1 }, ] } ] ================================================ FILE: Tests/database/test2.json ================================================ [ { "name" : "Frame1", "pgn" : 65000, "length" : 0, "spns" : [ { "name" : "spn_string1", "number" : 30, "type" : 2 }, { "name" : "spn_string2", "number" : 62, "type" : 2 }, { "name" : "spn_string3", "number" : 72, "type" : 2 } ] }, { "name" : "Frame2", "pgn" : 65235, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number1", "number" : 260, "offset" : 2, "type" : 0, "units" : "%" }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status1", "number" : 2323, "offset" : 5, "type" : 1 } ] }, { "name" : "Frame3", "length" : 0, "spns" : { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number2", "number" : 150, "offset" : 2, "type" : 0, "units" : "%" } }, { "name" : "Frame4", "pgn" : 44288, "length" : 6, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status2", "number" : 2323, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 3, "descriptions" : [ "description 1", "description 2", "description 3", "description 4", "description 5", "description 6", "description 7", "description 8" ], "name" : "spn_status3", "number" : 2525, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 4, "name" : "spn_status4", "number" : 1515, "offset" : 1, "type" : 1 } ] } ] ================================================ FILE: Tests/database/test3.json ================================================ [ { "name" : "Frame1", "pgn" : 65000, "length" : 0, "spns" : [ { "name" : "spn_string1", "number" : 30, "type" : 2 }, { "name" : "spn_string2", "number" : 62, "type" : 2 }, { "name" : "spn_string3", "number" : 72, "type" : 2 } ] }, { "name" : "Frame2", "pgn" : 65235, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number1", "number" : 260, "offset" : 2, "type" : 0, "units" : "%" }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status1", "number" : 2323, "offset" : 5, "type" : 1 } ] }, { "name" : "Frame3", "pgn" : 65262, "length" : 0, "spns" : [ { "byteSize" : 8, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number2", "number" : 150, "offset" : 2, "type" : 0, "units" : "%" }, { "byteSize" : 9, "formatGain" : 10, "formatOffset" : 200, "name" : "spn_number3", "number" : 320, "offset" : 0, "type" : 0, "units" : "Pa" } ] }, { "name" : "Frame4", "pgn" : 44288, "length" : 6, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status2", "number" : 2323, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 3, "descriptions" : [ "description 1", "description 2", "description 3", "description 4", "description 5", "description 6", "description 7", "description 8" ], "name" : "spn_status3", "number" : 2525, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 4, "name" : "spn_status4", "number" : 1515, "offset" : 1, "type" : 1 } ] } ] ================================================ FILE: Tests/database/test4.json ================================================ [ { "name" : "Frame1", "pgn" : 65000, "length" : 0, "spns" : [ { "name" : "spn_string1", "number" : 30, "type" : 2 }, { "name" : "spn_string2", "number" : 62, "type" : 2 }, { "name" : "spn_string3", "number" : 72, "type" : 2 } ] }, { "name" : "Frame2", "pgn" : 65235, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number1", "number" : 260, "offset" : 2, "type" : 4, "units" : "%" }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status1", "number" : 2323, "offset" : 5, "type" : 1 } ] }, { "name" : "Frame3", "pgn" : 65262, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number2", "number" : 150, "offset" : 2, "type" : 0, "units" : "%" }, { "byteSize" : 2, "formatGain" : 10, "formatOffset" : 200, "name" : "spn_number3", "number" : 320, "offset" : 0, "type" : 0, "units" : "Pa" } ] }, { "name" : "Frame4", "pgn" : 44288, "length" : 6, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status2", "number" : 2424, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 3, "descriptions" : [ "description 1", "description 2", "description 3", "description 4", "description 5", "description 6", "description 7", "description 8" ], "name" : "spn_status3", "number" : 2525, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 4, "name" : "spn_status4", "number" : 5151, "offset" : 1, "type" : 1 } ] } ] ================================================ FILE: Tests/database/test5.json ================================================ [ { "name" : "Frame1", "pgn" : 65000, "length" : 0, "spns" : [ { "name" : "spn_string1", "number" : 30, "type" : 2 }, { "name" : "spn_string2", "number" : 62, "type" : 2 }, { "name" : "spn_string3", "number" : 72, "type" : 2 } ] }, { "name" : "Frame2", "pgn" : 65235, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number1", "number" : 260, "offset" : 2, "type" : 0, "units" : "%" }, { "bitOffset" : 2, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status1", "number" : 2323, "offset" : 5, "type" : 1 } ] }, { "name" : "Frame3", "pgn" : 65262, "length" : 0, "spns" : [ { "byteSize" : 3, "formatGain" : 0.55, "formatOffset" : -200, "name" : "spn_number2", "number" : 150, "offset" : 2, "type" : 0, "units" : "%" }, { "byteSize" : 2, "formatGain" : 10, "formatOffset" : 200, "name" : "spn_number3", "number" : 320, "offset" : 0, "type" : 0, "units" : "Pa" } ] }, { "name" : "Frame4", "pgn" : 44288, "length" : 6, "spns" : [ { "bitOffset" : 0, "bitSize" : 2, "descriptions" : [ "description 1", "description 2", "description 3", "description 4" ], "name" : "spn_status2", "number" : 2424, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 3, "descriptions" : [ "description 1", "description 2", "description 3", "description 4", "description 5", "description 6", "description 7", "description 8" ], "name" : "spn_status3", "number" : 2525, "offset" : 2, "type" : 1 }, { "bitOffset" : 2, "bitSize" : 4, "name" : "spn_status4", "number" : 5151, "offset" : 1, "type" : 1 } ] } ] ================================================ FILE: Tests/database_test.cpp ================================================ #include #include #include #include #include #include using namespace J1939; TEST(J1939DataBase_test, readDataBase) { //Load database J1939DataBase ddbb; ASSERT_FALSE(ddbb.parseJsonFile("Tests/database/test_not_found.json")); ASSERT_EQ(ddbb.getLastError(), J1939DataBase::ERROR_FILE_NOT_FOUND); ddbb.clear(); //Right bracket removed in line 86 and comma added in line 136. ASSERT_FALSE(ddbb.parseJsonFile("Tests/database/test1.json")); ASSERT_EQ(ddbb.getLastError(), J1939DataBase::ERROR_JSON_SYNTAX); ddbb.clear(); //PGN key removed in line 61 and SPNs is an object in line 63 instead of array ASSERT_FALSE(ddbb.parseJsonFile("Tests/database/test2.json")); ASSERT_EQ(ddbb.getLastError(), J1939DataBase::ERROR_UNEXPECTED_TOKENS); ddbb.clear(); //Bytesize = 8 in line 67 and bytesize = 9 in line 77. ASSERT_FALSE(ddbb.parseJsonFile("Tests/database/test3.json")); ASSERT_EQ(ddbb.getLastError(), J1939DataBase::ERROR_OUT_OF_RANGE); ddbb.clear(); //Type 4 in line 39 ASSERT_FALSE(ddbb.parseJsonFile("Tests/database/test4.json")); ASSERT_EQ(ddbb.getLastError(), J1939DataBase::ERROR_UNKNOWN_SPN_TYPE); ddbb.clear(); //Database OK ASSERT_TRUE(ddbb.parseJsonFile("Tests/database/test5.json")); ASSERT_EQ(ddbb.getLastError(), J1939DataBase::ERROR_OK); const std::vector& frames = ddbb.getParsedFrames(); ASSERT_EQ(frames.size(), 4); ASSERT_EQ(frames[0].getPGN(), 65000); ASSERT_EQ(frames[0].getName(), "Frame1"); { std::set spnNumbers = frames[0].getSPNNumbers(); auto iter = spnNumbers.begin(); ASSERT_EQ(spnNumbers.size(), 3); ASSERT_EQ(*iter, 30); const SPN *spn1 = frames[0].getSPN(*iter++); ASSERT_EQ(spn1->getSpnNumber(), 30); ASSERT_EQ(spn1->getType(), SPN::SPN_STRING); ASSERT_EQ(spn1->getName(), "spn_string1"); ASSERT_EQ(*iter, 62); const SPN *spn2 = frames[0].getSPN(*iter++); ASSERT_EQ(spn2->getSpnNumber(), 62); ASSERT_EQ(spn2->getType(), SPN::SPN_STRING); ASSERT_EQ(spn2->getName(), "spn_string2"); ASSERT_EQ(*iter, 72); const SPN *spn3 = frames[0].getSPN(*iter++); ASSERT_EQ(spn3->getSpnNumber(), 72); ASSERT_EQ(spn3->getType(), SPN::SPN_STRING); ASSERT_EQ(spn3->getName(), "spn_string3"); } ASSERT_EQ(frames[1].getPGN(), 65235); ASSERT_EQ(frames[1].getName(), "Frame2"); { std::set spnNumbers = frames[1].getSPNNumbers(); auto iter = spnNumbers.begin(); ASSERT_EQ(spnNumbers.size(), 2); ASSERT_EQ(*iter, 260); const SPN *spn1 = frames[1].getSPN(*iter++); ASSERT_EQ(spn1->getSpnNumber(), 260); ASSERT_EQ(spn1->getType(), SPN::SPN_NUMERIC); ASSERT_EQ(spn1->getName(), "spn_number1"); const SPNNumeric *spnNum = static_cast(spn1); ASSERT_EQ(spnNum->getOffset(), 2); ASSERT_EQ(spnNum->getFormatGain(), 0.55); ASSERT_EQ(spnNum->getFormatOffset(), -200); ASSERT_EQ(spnNum->getByteSize(), 3); ASSERT_EQ(spnNum->getUnits(), "%"); ASSERT_EQ(*iter, 2323); const SPN *spn2 = frames[1].getSPN(*iter++); ASSERT_EQ(spn2->getSpnNumber(), 2323); ASSERT_EQ(spn2->getType(), SPN::SPN_STATUS); ASSERT_EQ(spn2->getName(), "spn_status1"); const SPNStatus *spnStat = static_cast(spn2); ASSERT_EQ(spnStat->getOffset(), 5); ASSERT_EQ(spnStat->getBitOffset(), 2); ASSERT_EQ(spnStat->getBitSize(), 2); ASSERT_EQ(spnStat->getValueDescription(0), "description 1"); ASSERT_EQ(spnStat->getValueDescription(1), "description 2"); ASSERT_EQ(spnStat->getValueDescription(2), "description 3"); ASSERT_EQ(spnStat->getValueDescription(3), "description 4"); } ASSERT_EQ(frames[2].getPGN(), 65262); ASSERT_EQ(frames[2].getName(), "Frame3"); { std::set spnNumbers = frames[2].getSPNNumbers(); auto iter = spnNumbers.begin(); ASSERT_EQ(spnNumbers.size(), 2); ASSERT_EQ(*iter, 150); const SPN *spn1 = frames[2].getSPN(*iter++); ASSERT_EQ(spn1->getSpnNumber(), 150); ASSERT_EQ(spn1->getType(), SPN::SPN_NUMERIC); ASSERT_EQ(spn1->getName(), "spn_number2"); const SPNNumeric *spnNum1 = static_cast(spn1); ASSERT_EQ(spnNum1->getOffset(), 2); ASSERT_EQ(spnNum1->getFormatGain(), 0.55); ASSERT_EQ(spnNum1->getFormatOffset(), -200); ASSERT_EQ(spnNum1->getByteSize(), 3); ASSERT_EQ(spnNum1->getUnits(), "%"); ASSERT_EQ(*iter, 320); const SPN *spn2 = frames[2].getSPN(*iter++); ASSERT_EQ(spn2->getSpnNumber(), 320); ASSERT_EQ(spn2->getType(), SPN::SPN_NUMERIC); ASSERT_EQ(spn2->getName(), "spn_number3"); const SPNNumeric *spnNum2 = static_cast(spn2); ASSERT_EQ(spnNum2->getOffset(), 0); ASSERT_EQ(spnNum2->getFormatGain(), 10); ASSERT_EQ(spnNum2->getFormatOffset(), 200); ASSERT_EQ(spnNum2->getByteSize(), 2); ASSERT_EQ(spnNum2->getUnits(), "Pa"); } ASSERT_EQ(frames[3].getPGN(), 44288); ASSERT_EQ(frames[3].getName(), "Frame4"); { std::set spnNumbers = frames[3].getSPNNumbers(); auto iter = spnNumbers.begin(); ASSERT_EQ(spnNumbers.size(), 3); ASSERT_EQ(*iter, 2424); const SPN *spn1 = frames[3].getSPN(*iter++); ASSERT_EQ(spn1->getSpnNumber(), 2424); ASSERT_EQ(spn1->getType(), SPN::SPN_STATUS); ASSERT_EQ(spn1->getName(), "spn_status2"); const SPNStatus *spnStat1 = static_cast(spn1); ASSERT_EQ(spnStat1->getOffset(), 2); ASSERT_EQ(spnStat1->getBitOffset(), 0); ASSERT_EQ(spnStat1->getBitSize(), 2); ASSERT_EQ(spnStat1->getValueDescription(0), "description 1"); ASSERT_EQ(spnStat1->getValueDescription(1), "description 2"); ASSERT_EQ(spnStat1->getValueDescription(2), "description 3"); ASSERT_EQ(spnStat1->getValueDescription(3), "description 4"); ASSERT_EQ(*iter, 2525); const SPN *spn2 = frames[3].getSPN(*iter++); ASSERT_EQ(spn2->getSpnNumber(), 2525); ASSERT_EQ(spn2->getType(), SPN::SPN_STATUS); ASSERT_EQ(spn2->getName(), "spn_status3"); const SPNStatus *spnStat2 = static_cast(spn2); ASSERT_EQ(spnStat2->getOffset(), 2); ASSERT_EQ(spnStat2->getBitOffset(), 2); ASSERT_EQ(spnStat2->getBitSize(), 3); ASSERT_EQ(spnStat2->getValueDescription(0), "description 1"); ASSERT_EQ(spnStat2->getValueDescription(1), "description 2"); ASSERT_EQ(spnStat2->getValueDescription(2), "description 3"); ASSERT_EQ(spnStat2->getValueDescription(3), "description 4"); ASSERT_EQ(spnStat2->getValueDescription(4), "description 5"); ASSERT_EQ(spnStat2->getValueDescription(5), "description 6"); ASSERT_EQ(spnStat2->getValueDescription(6), "description 7"); ASSERT_EQ(spnStat2->getValueDescription(7), "description 8"); ASSERT_EQ(*iter, 5151); const SPN *spn3 = frames[3].getSPN(*iter++); ASSERT_EQ(spn3->getSpnNumber(), 5151); ASSERT_EQ(spn3->getType(), SPN::SPN_STATUS); ASSERT_EQ(spn3->getName(), "spn_status4"); const SPNStatus *spnStat3 = static_cast(spn3); ASSERT_EQ(spnStat3->getOffset(), 1); ASSERT_EQ(spnStat3->getBitOffset(), 2); ASSERT_EQ(spnStat3->getBitSize(), 4); } } ================================================ FILE: Tests/fms1_frame_test.cpp ================================================ #include #include using namespace J1939; TEST(FMS1Frame_test, encode) { { FMS1Frame frame(0); u32 id; frame.setSrcAddr(0x35); frame.setPriority(5); ASSERT_EQ(frame.getDataLength(), 8); ASSERT_EQ(frame.getBlockID(), 0); ASSERT_TRUE(frame.hasTTS(5)); ASSERT_FALSE(frame.hasTTS(16)); ASSERT_FALSE(frame.hasTTS(35)); ASSERT_FALSE(frame.hasTTS(50)); ASSERT_TRUE(frame.setTTS(5, TellTale::TTS_STATUS_RED)); ASSERT_TRUE(frame.setTTS(4, TellTale::TTS_STATUS_OFF)); ASSERT_TRUE(frame.setTTS(8, TellTale::TTS_STATUS_YELLOW)); ASSERT_TRUE(frame.setTTS(1, TellTale::TTS_STATUS_INFO)); size_t length = frame.getDataLength(); u8 raw[] = {0xB0, 0xFF, 0x98, 0xFF, 0xFA, 0xFF, 0xFF, 0xFF}; u8 *buff = new u8[length]; frame.encode(id, buff, length); ASSERT_EQ(id, 0x14FD7D35); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } { FMS1Frame frame(1); u32 id; frame.setSrcAddr(0x44); frame.setPriority(1); ASSERT_EQ(frame.getDataLength(), 8); ASSERT_EQ(frame.getBlockID(), 1); ASSERT_FALSE(frame.hasTTS(5)); ASSERT_TRUE(frame.hasTTS(16)); ASSERT_FALSE(frame.hasTTS(35)); ASSERT_FALSE(frame.hasTTS(50)); ASSERT_TRUE(frame.setTTS(20, TellTale::TTS_STATUS_RED)); ASSERT_TRUE(frame.setTTS(21, TellTale::TTS_STATUS_OFF)); ASSERT_TRUE(frame.setTTS(17, TellTale::TTS_STATUS_YELLOW)); ASSERT_TRUE(frame.setTTS(30, TellTale::TTS_STATUS_INFO)); size_t length = frame.getDataLength(); u8 raw[] = {0xF1, 0xFA, 0x9F, 0xF8, 0xFF, 0xFF, 0xFF, 0xBF}; u8 *buff = new u8[length]; frame.encode(id, buff, length); ASSERT_EQ(id, 0x04FD7D44); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } { FMS1Frame frame(2); u32 id; frame.setSrcAddr(0x36); frame.setPriority(7); ASSERT_EQ(frame.getDataLength(), 8); ASSERT_EQ(frame.getBlockID(), 2); ASSERT_FALSE(frame.hasTTS(5)); ASSERT_FALSE(frame.hasTTS(16)); ASSERT_TRUE(frame.hasTTS(35)); ASSERT_TRUE(frame.setTTS(35, TellTale::TTS_STATUS_RED)); ASSERT_TRUE(frame.setTTS(40, TellTale::TTS_STATUS_OFF)); ASSERT_TRUE(frame.setTTS(41, TellTale::TTS_STATUS_YELLOW)); ASSERT_TRUE(frame.setTTS(45, TellTale::TTS_STATUS_INFO)); size_t length = frame.getDataLength(); u8 raw[] = {0xF2, 0xFF, 0x9F, 0xFF, 0xFF, 0xA8, 0xFF, 0xBF}; u8 *buff = new u8[length]; frame.encode(id, buff, length); ASSERT_EQ(id, 0x1CFD7D36); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } { FMS1Frame frame(3); u32 id; frame.setSrcAddr(0x25); frame.setPriority(5); ASSERT_EQ(frame.getDataLength(), 8); ASSERT_EQ(frame.getBlockID(), 3); ASSERT_FALSE(frame.hasTTS(5)); ASSERT_FALSE(frame.hasTTS(16)); ASSERT_FALSE(frame.hasTTS(35)); ASSERT_TRUE(frame.hasTTS(50)); ASSERT_TRUE(frame.setTTS(46, TellTale::TTS_STATUS_RED)); ASSERT_TRUE(frame.setTTS(49, TellTale::TTS_STATUS_OFF)); ASSERT_TRUE(frame.setTTS(50, TellTale::TTS_STATUS_YELLOW)); ASSERT_TRUE(frame.setTTS(60, TellTale::TTS_STATUS_INFO)); size_t length = frame.getDataLength(); u8 raw[] = {0x93, 0xFF, 0xA8, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF}; u8 *buff = new u8[length]; frame.encode(id, buff, length); ASSERT_EQ(id, 0x14FD7D25); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; } } TEST(FMS1Frame_test, decode) { { FMS1Frame frame; u32 id = 0x14FD7D35; u8 raw[] = {0xB0, 0xFF, 0x98, 0xFF, 0xFA, 0xFF, 0xFF, 0xFF}; frame.decode(id, raw, sizeof(raw)); ASSERT_EQ(frame.getSrcAddr(), 0x35); ASSERT_EQ(frame.getPriority(), 5); ASSERT_EQ(frame.getBlockID(), 0); ASSERT_EQ(frame.getTTS(5).getStatus(), TellTale::TTS_STATUS_RED); ASSERT_EQ(frame.getTTS(4).getStatus(), TellTale::TTS_STATUS_OFF); ASSERT_EQ(frame.getTTS(8).getStatus(), TellTale::TTS_STATUS_YELLOW); ASSERT_EQ(frame.getTTS(1).getStatus(), TellTale::TTS_STATUS_INFO); } { FMS1Frame frame; u32 id = 0x04FD7D44; u8 raw[] = {0xF1, 0xFA, 0x9F, 0xF8, 0xFF, 0xFF, 0xFF, 0xBF}; frame.decode(id, raw, sizeof(raw)); ASSERT_EQ(frame.getSrcAddr(), 0x44); ASSERT_EQ(frame.getPriority(), 1); ASSERT_EQ(frame.getBlockID(), 1); ASSERT_EQ(frame.getTTS(20).getStatus(), TellTale::TTS_STATUS_RED); ASSERT_EQ(frame.getTTS(21).getStatus(), TellTale::TTS_STATUS_OFF); ASSERT_EQ(frame.getTTS(17).getStatus(), TellTale::TTS_STATUS_YELLOW); ASSERT_EQ(frame.getTTS(30).getStatus(), TellTale::TTS_STATUS_INFO); } { FMS1Frame frame; u32 id = 0x1CFD7D36; u8 raw[] = {0xF2, 0xFF, 0x9F, 0xFF, 0xFF, 0xA8, 0xFF, 0xBF}; frame.decode(id, raw, sizeof(raw)); ASSERT_EQ(frame.getSrcAddr(), 0x36); ASSERT_EQ(frame.getPriority(), 7); ASSERT_EQ(frame.getBlockID(), 2); ASSERT_EQ(frame.getTTS(35).getStatus(), TellTale::TTS_STATUS_RED); ASSERT_EQ(frame.getTTS(40).getStatus(), TellTale::TTS_STATUS_OFF); ASSERT_EQ(frame.getTTS(41).getStatus(), TellTale::TTS_STATUS_YELLOW); ASSERT_EQ(frame.getTTS(45).getStatus(), TellTale::TTS_STATUS_INFO); } { FMS1Frame frame; u32 id = 0x14FD7D25; u8 raw[] = {0x93, 0xFF, 0xA8, 0xFF, 0xFF, 0xFF, 0xFF, 0xBF}; frame.decode(id, raw, sizeof(raw)); ASSERT_EQ(frame.getSrcAddr(), 0x25); ASSERT_EQ(frame.getPriority(), 5); ASSERT_EQ(frame.getBlockID(), 3); ASSERT_EQ(frame.getTTS(46).getStatus(), TellTale::TTS_STATUS_RED); ASSERT_EQ(frame.getTTS(49).getStatus(), TellTale::TTS_STATUS_OFF); ASSERT_EQ(frame.getTTS(50).getStatus(), TellTale::TTS_STATUS_YELLOW); ASSERT_EQ(frame.getTTS(60).getStatus(), TellTale::TTS_STATUS_INFO); } } ================================================ FILE: Tests/genericframe_test.cpp ================================================ #include #include #include #include #include using namespace J1939; class GenericFrame_test : public testing::Test { public: GenericFrame ccvs; GenericFrame vin; GenericFrame_test() : ccvs(0xFEF1), vin(0xFEEC) {} virtual void SetUp() { //Create frame CCVS ccvs.setName("CCVS"); ccvs.setLength(8); SPNNumeric spnNum(84, "Wheel Speed", 1, 0.00390625, 0, 2, "km/h"); ccvs.registerSPN(spnNum); { SPNStatusSpec::DescMap valueToDesc; valueToDesc[0] = "Pedal Released"; valueToDesc[1] = "Pedal Depressed"; valueToDesc[2] = "Error"; valueToDesc[3] = "Not Available"; SPNStatus spnStat(597, "Brake Switch", 3, 4, 2, valueToDesc); ccvs.registerSPN(spnStat); } { SPNStatusSpec::DescMap valueToDesc; valueToDesc[0] = "Pedal Released"; valueToDesc[1] = "Pedal Depressed"; valueToDesc[2] = "Error"; valueToDesc[3] = "Not Available"; SPNStatus spnStat(598, "Clutch Switch", 3, 6, 2, valueToDesc); ccvs.registerSPN(spnStat); } { SPNStatusSpec::DescMap valueToDesc; valueToDesc[0] = "Off"; valueToDesc[5] = "Set"; SPNStatus spnStat(976, "PTO State", 6, 0, 5, valueToDesc); ccvs.registerSPN(spnStat); } vin.setName("VIN"); SPNString vinSpn(237, "Vehicle Number Identifier"); vin.registerSPN(vinSpn); } virtual void TearDown() { } }; TEST_F(GenericFrame_test, spn) { ASSERT_TRUE(ccvs.hasSPN(84)); ASSERT_EQ(ccvs.getSPN(84)->getType(), SPN::SPN_NUMERIC); ASSERT_TRUE(ccvs.hasSPN(597)); ASSERT_EQ(ccvs.getSPN(597)->getType(), SPN::SPN_STATUS); ASSERT_TRUE(ccvs.hasSPN(598)); ASSERT_EQ(ccvs.getSPN(598)->getType(), SPN::SPN_STATUS); ASSERT_TRUE(ccvs.hasSPN(976)); ASSERT_EQ(ccvs.getSPN(976)->getType(), SPN::SPN_STATUS); ASSERT_TRUE(vin.hasSPN(237)); ASSERT_EQ(vin.getSPN(237)->getType(), SPN::SPN_STRING); } TEST_F(GenericFrame_test, encode) { SPNNumeric* wheelSpeed = static_cast(ccvs.getSPN(84)); wheelSpeed->setFormattedValue(50); //50 kph { SPNStatus* brakeSwitch = static_cast(ccvs.getSPN(597)); brakeSwitch->setValue(2); //Error } { SPNStatus* clutchSwitch = static_cast(ccvs.getSPN(598)); clutchSwitch->setValue(1); //Pedal Depressed } { SPNStatus* ptoState = static_cast(ccvs.getSPN(976)); ptoState->setValue(5); //Set } ccvs.setSrcAddr(0x50); ccvs.setPriority(7); u32 id; size_t length = ccvs.getDataLength(); ASSERT_EQ(length, 8); u8* buff = new u8[length]; try { ccvs.encode(id, buff, length); //Everything should work fine SUCCEED(); } catch(J1939EncodeException &) { FAIL(); } ASSERT_EQ(id, 0x1CFEF150); u8 encodedCCVS[] = {0xFF, 0x00, 0x32, 0x6F, 0xFF, 0xFF, 0xE5, 0xFF}; ASSERT_EQ(memcmp(buff, encodedCCVS, length), 0); try { size_t length2 = 7; ccvs.encode(id, buff, length2); FAIL(); } catch(J1939EncodeException &) { //Length not enough SUCCEED(); } delete[] buff; SPNString* vinSpn = static_cast(vin.getSPN(237)); vinSpn->setValue("abcdefghjk1234"); length = vin.getDataLength(); ASSERT_EQ(length, 15); //Size of string + '*' terminator vin.setSrcAddr(0x30); vin.setPriority(6); buff = new u8[length]; try { vin.encode(id, buff, length); //Everything should work fine SUCCEED(); } catch(J1939EncodeException &) { FAIL(); } ASSERT_EQ(id, 0x18FEEC30); ASSERT_EQ(memcmp(buff, "abcdefghjk1234*", length), 0); delete[] buff; } TEST_F(GenericFrame_test, decode) { u8 encodedCCVS[] = {0xFF, 0x00, 0x50, 0x9F, 0xFF, 0xFF, 0x1F, 0xFF}; u32 id = 0x18FEF120; try { ccvs.decode(id, encodedCCVS, sizeof(encodedCCVS)); SUCCEED(); } catch (J1939DecodeException &) { FAIL(); } ASSERT_EQ(ccvs.getSrcAddr(), 0x20); ASSERT_EQ(ccvs.getPriority(), 6); const SPNNumeric* wheelSpeed = static_cast(ccvs.getSPN(84)); ASSERT_EQ(wheelSpeed->getFormattedValue(), 80); { const SPNStatus* brakeSwitch = static_cast(ccvs.getSPN(597)); ASSERT_EQ(brakeSwitch->getValue(), 1); //Error } { const SPNStatus* clutchSwitch = static_cast(ccvs.getSPN(598)); ASSERT_EQ(clutchSwitch->getValue(), 2); //Pedal Depressed } { const SPNStatus* ptoState = static_cast(ccvs.getSPN(976)); ASSERT_EQ(ptoState->getValue(), 0x1F); //Set } id = 0x18FEF320; //Throw exception if we try to decode an id whose PGN does not correspond to the PGN of the frame try { ccvs.decode(id, encodedCCVS, sizeof(encodedCCVS)); FAIL(); } catch (J1939DecodeException& e) { SUCCEED(); } id = 0x04FEEC15; try { vin.decode(id, (u8 *)("ghijklmnopqrs*"), sizeof("ghijklmnopqrs*")); SUCCEED(); } catch (J1939DecodeException &) { FAIL(); } ASSERT_EQ(vin.getSrcAddr(), 0x15); ASSERT_EQ(vin.getPriority(), 1); const SPNString* vinSpn = static_cast(vin.getSPN(237)); ASSERT_EQ(strncmp(vinSpn->getValue().c_str(), "ghijklmnopqrs", sizeof("ghijklmnopqrs")), 0); } ================================================ FILE: Tests/include/TestFrame.h ================================================ #ifndef TESTFRAME_H_ #define TESTFRAME_H_ #include namespace J1939 { class TestFrame : public J1939Frame { private: std::basic_string mRaw; protected: void decodeData(const u8* buffer, size_t length) override; void encodeData(u8* buffer, size_t length) const override; public: TestFrame(u32 pgn) : J1939Frame(pgn) {} virtual ~TestFrame() {} const std::basic_string getRaw() const { return mRaw; } size_t getDataLength() const override { return mRaw.size(); } IMPLEMENT_CLONEABLE(J1939Frame,TestFrame); }; } /* namespace J1939 */ #endif /* TESTFRAME_H_ */ ================================================ FILE: Tests/j1939Factory_test.cpp ================================================ #include #include #include using namespace J1939; class J1939Factory_test : public testing::Test { public: J1939Factory_test() {} virtual void SetUp() { TestFrame frame1(0xDE00); TestFrame frame2(0xAF00); TestFrame frame3(0xFEEF); //Now, our factory is able to build our custom frames J1939Factory::getInstance().registerFrame(frame1); J1939Factory::getInstance().registerFrame(frame2); J1939Factory::getInstance().registerFrame(frame3); } virtual void TearDown() { //Keep a clear state for the factory J1939Factory::getInstance().unRegisterFrame(0xDE00); J1939Factory::getInstance().unRegisterFrame(0xAF00); J1939Factory::getInstance().unRegisterFrame(0xFEEF); } }; TEST_F(J1939Factory_test, getJ1939Frame) { u32 id; try { { id = 0x00DEAA50; u8 raw[] = {0xAB, 0xCD, 0xEF, 0x01, 0x23, 0x45, 0x67, 0x89}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_EQ(frame->getPGN(), 0xDE00); ASSERT_EQ(frame->getSrcAddr(), 0x50); ASSERT_EQ(frame->getDstAddr(), 0xAA); TestFrame *testFrame = static_cast(frame.get()); ASSERT_EQ(memcmp(raw, testFrame->getRaw().c_str(), sizeof(raw)), 0); } { id = 0x00AFCC60; u8 raw[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0x23, 0x45, 0x67, 0x89, 0x67, 0x89, 0x67, 0x89}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_EQ(frame->getPGN(), 0xAF00); ASSERT_EQ(frame->getSrcAddr(), 0x60); ASSERT_EQ(frame->getDstAddr(), 0xCC); TestFrame *testFrame = static_cast(frame.get()); ASSERT_EQ(memcmp(raw, testFrame->getRaw().c_str(), sizeof(raw)), 0); } { id = 0x00FEEF40; u8 raw[] = {0xBA, 0xDC, 0xFE, 0x10, 0x32, 0x54, 0x76, 0x98, 0x67, 0xAA, 0xBB, 0xEE, 0xDD}; std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(id, raw, sizeof(raw)); ASSERT_EQ(frame->getPGN(), 0xFEEF); ASSERT_EQ(frame->getSrcAddr(), 0x40); TestFrame *testFrame = static_cast(frame.get()); ASSERT_EQ(memcmp(raw, testFrame->getRaw().c_str(), sizeof(raw)), 0); } } catch(J1939DecodeException &) { FAIL(); } } ================================================ FILE: Tests/main.cpp ================================================ #include int main(int argc, char **argv) { testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } ================================================ FILE: Tests/request_frame.cpp ================================================ #include #include using namespace J1939; TEST(RequestFrame_test, encode) { RequestFrame frame(0xFE05); u32 id; frame.setSrcAddr(0x30); frame.setDstAddr(0x24); frame.setPriority(4); ASSERT_EQ(frame.getDataLength(), 3); ASSERT_EQ(frame.getRequestPGN(), 0xFE05); size_t length = 3; u8 raw[] = {0x05, 0xFE, 0x00}; u8 *buff = new u8[length]; frame.encode(id, buff, length); ASSERT_EQ(id, 0x10EA2430); ASSERT_EQ(memcmp(raw, buff, length), 0); delete[] buff; frame.setRequestPGN(0xBA00); ASSERT_EQ(frame.getRequestPGN(), 0xBA00); u8 raw2[] = {0x00, 0xBA, 0x00}; buff = new u8[length]; frame.encode(id, buff, length); ASSERT_EQ(id, 0x10EA2430); ASSERT_EQ(memcmp(raw2, buff, length), 0); delete[] buff; } TEST(RequestFrame_test, decode) { RequestFrame frame; u32 id = 0x10EA5821; { u8 raw[] = {0x05, 0xFE, 0x00}; frame.decode(id, raw, 3); ASSERT_EQ(frame.getSrcAddr(), 0x21); ASSERT_EQ(frame.getDstAddr(), 0x58); ASSERT_EQ(frame.getPriority(), 4); ASSERT_EQ(frame.getRequestPGN(), 0xFE05); } { u8 raw[] = {0x00, 0xBA, 0x00}; frame.decode(id, raw, 3); ASSERT_EQ(frame.getSrcAddr(), 0x21); ASSERT_EQ(frame.getDstAddr(), 0x58); ASSERT_EQ(frame.getPriority(), 4); ASSERT_EQ(frame.getRequestPGN(), 0xBA00); } } ================================================ FILE: cmake/FindLibWebSockets.cmake ================================================ # This module tries to find libWebsockets library and include files # # LIBWEBSOCKETS_INCLUDE_DIR, path where to find libwebsockets.h # LIBWEBSOCKETS_LIBRARY_DIR, path where to find libwebsockets.so # LIBWEBSOCKETS_LIBRARIES, the library to link against # LIBWEBSOCKETS_FOUND, If false, do not try to use libWebSockets # # This currently works probably only for Linux FIND_PATH ( LIBWEBSOCKETS_INCLUDE_DIR libwebsockets.h /usr/local/include /usr/include ) FIND_LIBRARY ( LIBWEBSOCKETS_LIBRARIES websockets /usr/local/lib /usr/lib ) GET_FILENAME_COMPONENT( LIBWEBSOCKETS_LIBRARY_DIR ${LIBWEBSOCKETS_LIBRARIES} PATH ) SET ( LIBWEBSOCKETS_FOUND "NO" ) IF ( LIBWEBSOCKETS_INCLUDE_DIR ) IF ( LIBWEBSOCKETS_LIBRARIES ) SET ( LIBWEBSOCKETS_FOUND "YES" ) ENDIF ( LIBWEBSOCKETS_LIBRARIES ) ENDIF ( LIBWEBSOCKETS_INCLUDE_DIR ) MARK_AS_ADVANCED( LIBWEBSOCKETS_LIBRARY_DIR LIBWEBSOCKETS_INCLUDE_DIR LIBWEBSOCKETS_LIBRARIES ) ================================================ FILE: cmake/J1939FrameworkConfig.cmake ================================================ include("${CMAKE_CURRENT_LIST_DIR}/J1939FrameworkTargets.cmake") set(J1939_Database ${CMAKE_INSTALL_PREFIX}/etc/j1939/frames.json) ================================================ FILE: j1939AddressClaimer/AddressClaimer.cpp ================================================ /* * AddressClaimer.cpp * * Created on: Jan 7, 2019 * Author: famez */ #include #include #include #include namespace J1939 { AddressClaimer::AddressClaimer(const EcuName& name, const std::queue& preferred) : mEcuName(name), mPreferred(preferred) { ASSERT(!preferred.empty()); mThread = std::unique_ptr(new std::thread(&AddressClaimer::exec, this)); } AddressClaimer::~AddressClaimer() { mThread->join(); } bool AddressClaimer::toBeHandled(const J1939Frame& frame) { switch(frame.getPGN()) { case REQUEST_PGN: { const RequestFrame &reqFrame = static_cast(frame); if(reqFrame.getRequestPGN() == ADDRESS_CLAIM_PGN) return true; } break; //We could really know if this frame should be treated by this object, //but it would take more time to check and it is not worth it (check preferred address...) case ADDRESS_CLAIM_PGN: { //The object will check in its own thread whether this frame should be treated. return true; } break; default: break; } return false; } void AddressClaimer::flush() { std::unique_lock lk(mMutex); mFrames.clear(); } FrameSharedPtr AddressClaimer::read(u32 timeout) { std::unique_lock lk(mMutex); FrameSharedPtr retVal; if(mFrames.empty()) { mCondVar.wait_for(lk, std::chrono::milliseconds(timeout)); } if(!mFrames.empty()) { retVal = mFrames.begin()->second; mFrames.erase(mFrames.begin()); } return retVal; } void AddressClaimer::receive(const J1939Frame& frame) { FrameSharedPtr frameSmartPtr(frame.clone()); std::unique_lock lk(mMutex); mFrames[frame.getIdentifier()] = frameSmartPtr; mCondVar.notify_all(); } bool AddressClaimer::claimAddressStep() { mCurrentSrc = J1939_INVALID_ADDRESS; //Notify that we do not have an available address onSrcAddrChanged(J1939_INVALID_ADDRESS); size_t pos = 0; flush(); //Avoid processing accumulated frames while(pos++ < mPreferred.size()) { //Get the address to try u8 address = mPreferred.front(); //Introduce again the address at the end of the queue mPreferred.pop(); mPreferred.push(address); AddressClaimer::ClaimResult result; //Retry until result fails or succeeds do { result = tryObtainAddress(address); } while(result == CLAIM_RETRY); if(result == CLAIM_ADDRESS_OBTAINED) { //Ok, we do not try anymore return true; } } //We tried all the possible addresses and all of them failed //Send cannot claim address AddressClaimFrame frame(mEcuName); frame.setSrcAddr(J1939_INVALID_ADDRESS); frame.setDstAddr(J1939_BROADCAST_ADDRESS); sendFrame(frame); return false; } AddressClaimer::ClaimResult AddressClaimer::tryObtainAddress(u8 address) { AddressClaimFrame addrClaim(mEcuName); addrClaim.setSrcAddr(address); addrClaim.setDstAddr(J1939_BROADCAST_ADDRESS); sendFrame(addrClaim); s32 timeToWait = 250; //Millis FrameSharedPtr frame; do { auto start = std::chrono::steady_clock::now(); frame = read(timeToWait); if(frame && frame->getPGN() == ADDRESS_CLAIM_PGN) { AddressClaimFrame *rcvFrame = static_cast(frame.get()); if(rcvFrame->getSrcAddr() == addrClaim.getSrcAddr()) { //Contending frame!! if(addrClaim.getEcuName() < rcvFrame->getEcuName()) { //Win return CLAIM_RETRY; } else { //Lose return CLAIM_FAILED; //Check next preferred address if any } } else { //No contending frame timeToWait = 250; //Reset time to wait continue; } } auto end = std::chrono::steady_clock::now(); std::chrono::milliseconds elapsed = std::chrono::duration_cast(end - start); timeToWait -= elapsed.count(); } while(timeToWait > 0 && frame); //Notify the new address ready to use mCurrentSrc = address; //Timeout ok onSrcAddrChanged(address); return CLAIM_ADDRESS_OBTAINED; } void AddressClaimer::exec() { while(true) { claimAddressStep(); bool conflict = false; while(!conflict) { FrameSharedPtr frame = read(5000); if(frame) { switch(frame->getPGN()) { case REQUEST_PGN: { RequestFrame *reqFrame = static_cast(frame.get()); if((reqFrame->getRequestPGN() == ADDRESS_CLAIM_PGN) && ((reqFrame->getDstAddr() == J1939_BROADCAST_ADDRESS) || (reqFrame->getDstAddr() == mCurrentSrc))) { //Send claim address AddressClaimFrame frame(mEcuName); frame.setSrcAddr(mCurrentSrc); frame.setDstAddr(J1939_BROADCAST_ADDRESS); sendFrame(frame); } break; } case ADDRESS_CLAIM_PGN: { AddressClaimFrame *rcvFrame = static_cast(frame.get()); if(rcvFrame->getSrcAddr() != J1939_INVALID_ADDRESS && rcvFrame->getSrcAddr() == mCurrentSrc) { //Contending frame!! if(mEcuName < rcvFrame->getEcuName()) { //Win //Send claim address AddressClaimFrame frame(mEcuName); frame.setSrcAddr(mCurrentSrc); frame.setDstAddr(J1939_BROADCAST_ADDRESS); sendFrame(frame); } else { //Lose //Come back to claimAddressStep conflict = true; } } } break; default: break; } } } } } } /* namespace J1939 */ ================================================ FILE: j1939AddressClaimer/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(J1939AddressClaimer) add_library(J1939AddressClaimer SHARED ./AddressClaimer.cpp ) target_include_directories(J1939AddressClaimer PUBLIC include ${J1939_SOURCE_DIR}/include ${Common_SOURCE_DIR}/include ) target_link_libraries(J1939AddressClaimer PUBLIC J1939 pthread ) install (TARGETS J1939AddressClaimer LIBRARY DESTINATION lib) install(DIRECTORY include/ DESTINATION include ) ================================================ FILE: j1939AddressClaimer/include/AddressClaimer.h ================================================ /* * AddressClaimer.h * * Created on: Jan 7, 2019 * Author: famez */ #ifndef ADDRESSCLAIMER_H_ #define ADDRESSCLAIMER_H_ #include #include #include //Threading #include #include #include #include #include #include namespace J1939 { class J1939Frame; class EcuName; typedef std::shared_ptr FrameSharedPtr; class AddressClaimer { private: enum ClaimResult { CLAIM_ADDRESS_OBTAINED, CLAIM_RETRY, CLAIM_FAILED, }; std::map mFrames; std::unique_ptr mThread = nullptr; std::mutex mMutex; std::condition_variable mCondVar; std::queue mPreferred; EcuName mEcuName; u8 mCurrentSrc = J1939_INVALID_ADDRESS; FrameSharedPtr read(u32 timeout); void flush(); ClaimResult tryObtainAddress(u8 address); /* * Returns true if success, otherwise false. */ bool claimAddressStep(); /** * Starts the machine state in a thread */ void exec(); //Methods to override by the inherited class protected: virtual void onSrcAddrChanged(u8 newAddr) = 0; virtual void sendFrame(const J1939Frame&) = 0; public: AddressClaimer(const EcuName& name, const std::queue& preferred); virtual ~AddressClaimer(); bool toBeHandled(const J1939Frame& frame); /** * This method does not take the ownership of the frame passed as argument. */ void receive(const J1939Frame& frame); }; } /* namespace J1939 */ #endif /* ADDRESSCLAIMER_H_ */ ================================================ FILE: wireshark/dissector/CMakeLists.txt ================================================ # CMakeLists.txt # # Wireshark - Network traffic analyzer # By Gerald Combs # Copyright 1998 Gerald Combs # # SPDX-License-Identifier: GPL-2.0-or-later # cmake_minimum_required(VERSION 3.5) include(WiresharkPlugin) find_package(J1939Framework REQUIRED) add_definitions(-DDATABASE_PATH="${J1939_Database}") # Plugin name and version info (major minor micro extra) set_module_info(j1939 0 0 1 0) set(DISSECTOR_SRC packet-j1939.cpp ) set(PLUGIN_FILES plugin.c ${DISSECTOR_SRC} ) set_source_files_properties( ${PLUGIN_FILES} PROPERTIES COMPILE_FLAGS "${WERROR_COMMON_FLAGS}" ) set (CMAKE_CXX_STANDARD 11) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) register_plugin_files(plugin.c plugin ${DISSECTOR_SRC} ) add_plugin_library(j1939 epan) target_link_libraries(j1939 epan J1939 -rdynamic) install_plugin(j1939 epan) file(GLOB DISSECTOR_HEADERS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "*.h") CHECKAPI( NAME j1939 SWITCHES -g abort -g termoutput -build SOURCES ${DISSECTOR_SRC} ${DISSECTOR_HEADERS} ) # # Editor modelines - http://www.wireshark.org/tools/modelines.html # # Local variables: # c-basic-offset: 8 # tab-width: 8 # indent-tabs-mode: t # End: # # vi: set shiftwidth=8 tabstop=8 noexpandtab: # :indentSize=8:tabSize=8:noTabs=false: # ================================================ FILE: wireshark/dissector/Readme.md ================================================ ### J1939 Dissector for wireshark #### To install the plugin ``` sudo apt-get install glib2.0 libgcrypt11-dev qt5-default qttools5-dev qtdeclarative5-dev qtmultimedia5-dev libqt5svg5-dev ``` - Install the J1939 framework as explained in the README.md at the top level of the repository. - Download and extract the source code of wireshark located in https://www.wireshark.org/download/src/. Minimum version 3.0.1. - Copy the folder in which this README.md file is located into the following path inside the wireshark source code (creating the folder j1939): **plugins/epan/j1939/** - Modify the file CMakeListsCustoms.txt located in the root of the wireshark source code as follows: ``` set(CUSTOM_PLUGIN_SRC_DIR plugins/epan/j1939 ) ``` - Configure (using **CMake**), build and install wireshark from the source code: ```bash cd **wireshark source code** cmake . cd plugins/epan/j1939/ make && sudo make install ``` ![alt text](https://github.com/famez/J1939-Framework/blob/master/wireshark/dissector/J1939-plugin.png) ================================================ FILE: wireshark/dissector/packet-j1939.cpp ================================================ extern "C" { #include "config.h" #include #include #include //For BAM reassemble #include void proto_register_j1939(void); void proto_reg_handoff_j1939(void); } #include #include #include #include #include #include #include #include #include #include #include #ifndef DATABASE_PATH #define DATABASE_PATH "/etc/j1939/frames.json" #endif using namespace J1939; int dissect_J1939(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data); void dissect_generic_frame(tvbuff_t *tvb, proto_tree *j1939_tree, proto_item *ti, GenericFrame *genFrame); void dissect_fms1_frame(tvbuff_t *tvb, proto_tree *j1939_tree, proto_item *ti, const FMS1Frame *fms1Frame); void dissect_dm1_frame(tvbuff_t *tvb, proto_tree *j1939_tree, proto_item *ti, const DM1 *dm1Frame); static int hf_j1939_frame = -1; static int hf_j1939_spn = -1; static int hf_j1939_dtc = -1; static int hf_j1939_fmi = -1; static int hf_j1939_oc = -1; static int hf_j1939_blockId = -1; static int proto_j1939 = -1; static gint ett_j1939 = -1; static gint ett_j1939_can = -1; static gint ett_j1939_dtc = -1; static gint ett_j1939_message = -1; static gint ett_bam_fragment = -1; static gint ett_bam_fragments = -1; static gint hf_bam_fragments = -1; static gint hf_bam_fragment = -1; static gint hf_bam_fragment_overlap = -1; static gint hf_bam_fragment_overlap_conflict = -1; static gint hf_bam_fragment_multiple_tails = -1; static gint hf_bam_fragment_too_long_fragment = -1; static gint hf_bam_fragment_error = -1; static gint hf_bam_fragment_count = -1; static gint hf_bam_reassembled_in = -1; static gint hf_bam_reassembled_length = -1; static gint hf_bam_reassembled_data = -1; static dissector_handle_t j1939_handle; static reassembly_table bam_reassembly_table; static const fragment_items bam_frag_items = { &ett_bam_fragment, &ett_bam_fragments, &hf_bam_fragments, &hf_bam_fragment, &hf_bam_fragment_overlap, &hf_bam_fragment_overlap_conflict, &hf_bam_fragment_multiple_tails, &hf_bam_fragment_too_long_fragment, &hf_bam_fragment_error, &hf_bam_fragment_count, &hf_bam_reassembled_in, &hf_bam_reassembled_length, &hf_bam_reassembled_data, "BAM fragments" }; static const value_string tts_status[] = { { 0, "Off" }, { 1, "Red" }, { 2, "Yellow" }, { 3, "Info" }, { 7, "Not available" } }; BamReassembler bamReassembler; std::map spnNumToHinfoId; std::map ttsNumToHinfoId; void proto_register_j1939(void) { static hf_register_info hf[] = { { &hf_j1939_frame, {"Frame", "j1939.frame", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_j1939_spn, {"Spn", "j1939.spn", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_j1939_dtc, {"Diagnosis Trouble Code", "j1939.dtc", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_j1939_oc, {"Ocurrence Count", "j1939.oc", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_j1939_fmi, {"Failure Mode Identifier", "j1939.fmi", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_j1939_blockId, {"Block ID", "j1939.fms1.blockId", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } }, { &hf_bam_fragment_overlap, { "Fragment overlap", "bam.fragment.overlap", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Fragment overlaps with other fragments", HFILL }}, { &hf_bam_fragment_overlap_conflict, { "Conflicting data in fragment overlap", "bam.fragment.overlap.conflict", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Overlapping fragments contained conflicting data", HFILL }}, { &hf_bam_fragment_multiple_tails, { "Multiple tail fragments found", "bam.fragment.multipletails", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Several tails were found when defragmenting the packet", HFILL }}, { &hf_bam_fragment_too_long_fragment, { "Fragment too long", "bam.fragment.toolongfragment", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "Fragment contained data past end of packet", HFILL }}, { &hf_bam_fragment_error, { "Defragmentation error", "bam.fragment.error", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "Defragmentation error due to illegal fragments", HFILL }}, { &hf_bam_fragment_count, { "Fragment count", "bam.fragment.count", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_bam_fragment, { "BAM Fragment", "bam.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_bam_fragments, { "BAM Fragments", "bam.fragments", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_bam_reassembled_in, { "Reassembled BAM in frame", "bam.reassembled_in", FT_FRAMENUM, BASE_NONE, NULL, 0x0, "This BAM packet is reassembled in this frame", HFILL }}, { &hf_bam_reassembled_length, { "Reassembled BAM length", "bam.reassembled.length", FT_UINT32, BASE_DEC, NULL, 0x0, "The total length of the reassembled payload", HFILL }}, { &hf_bam_reassembled_data, { "Reassembled BAM data", "bam.reassembled.data", FT_BYTES, BASE_NONE, NULL, 0x0, "The reassembled payload", HFILL }} }; static gint *ett[] = { &ett_j1939, &ett_j1939_can, &ett_j1939_dtc, &ett_j1939_message, &ett_bam_fragment, &ett_bam_fragments }; reassembly_table_register(&bam_reassembly_table, &addresses_reassembly_table_functions); proto_j1939 = proto_register_protocol("j1939framework Protocol", "j1939framework", "j1939framework"); proto_register_field_array(proto_j1939, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); } void proto_reg_handoff_j1939(void) { j1939_handle = create_dissector_handle( dissect_J1939, proto_j1939 ); //Load database J1939DataBase ddbb; if(!ddbb.parseJsonFile(DATABASE_PATH)) { //Something went wrong return; } const std::vector& ddbbFrames = ddbb.getParsedFrames(); //Register all the frames listed in the database for(auto iter = ddbbFrames.begin(); iter != ddbbFrames.end(); ++iter) { J1939Factory::getInstance().registerFrame(*iter); } header_field_info* info; std::string abbrev; std::string tts_name; for(unsigned int tts = 1; tts <= NUMBER_OF_BLOCKS * TTSS_PER_BLOCK; ++tts) { info = (header_field_info*)g_malloc0(sizeof(header_field_info)); info->id = -1; info->ref_type = HF_REF_TYPE_NONE; info->same_name_prev_id = -1; abbrev = std::string("j1939.tts.") + std::to_string(tts); tts_name = std::string("TTS ") + std::to_string(tts) + std::string(" Status"); info->name = g_strdup(tts_name.c_str()); info->abbrev = g_strdup(abbrev.c_str()); info->type = FT_UINT8; info->display = BASE_DEC; info->strings = VALS(tts_status); info->bitmask = 0x7; if((((tts - 1) % TTSS_PER_BLOCK) % 2) == 0) { //If TTS number is even, the bitmask corresponds to the 4 most significant bits. Otherwise, it corresponds to the 4 least significant bits. info->bitmask <<= 4; } proto_register_fields_section(proto_j1939, info, 1); ttsNumToHinfoId[tts] = info->id; } std::set pgns = J1939Factory::getInstance().getAllRegisteredPGNs(); //Register dissectors for all the known frames for(auto pgn = pgns.begin(); pgn != pgns.end(); ++pgn) { dissector_add_uint("j1939.pgn", *pgn, j1939_handle); std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(*pgn); if(!frame->isGenericFrame()) { continue; } GenericFrame *genFrame = static_cast(frame.get()); //Also, we have to register SPN specific fields std::set spnNumbers = genFrame->getSPNNumbers(); for(auto spnNumber = spnNumbers.begin(); spnNumber != spnNumbers.end(); ++spnNumber) { const SPN* spn = genFrame->getSPN(*spnNumber); info = (header_field_info*)g_malloc0(sizeof(header_field_info)); info->id = -1; info->ref_type = HF_REF_TYPE_NONE; info->same_name_prev_id = -1; abbrev = std::string("j1939.spn.") + std::to_string(*spnNumber); info->name = g_strdup(spn->getName().c_str()); info->abbrev = g_strdup(abbrev.c_str()); switch(spn->getType()) { case SPN::SPN_STATUS: { info->type = FT_UINT8; info->display = BASE_DEC; } break; case SPN::SPN_NUMERIC: { info->type = FT_DOUBLE; info->display = BASE_NONE; } break; case SPN::SPN_STRING: { info->type = FT_STRING; info->display = BASE_NONE; } break; default: break; } proto_register_fields_section(proto_j1939, info, 1); spnNumToHinfoId[*spnNumber] = info->id; } } } int dissect_J1939(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void*) { proto_item *ti; proto_tree *j1939_tree; //Get the buffer from the upper layer so that we can obtain the ID for the Can Frame tvbuff_t *canIdTvb = tvb_get_ds_tvb(tvb); //Obtain the length of the concerning frame guint32 data_length = tvb_reported_length(tvb); //Allocate a buffer where to store the DLC (payload of the can frame) guint8* content = (guint8*)wmem_alloc(pinfo->pool, data_length); //Obtain the can id guint32 canId; if(tvb_reported_length(canIdTvb) >= 20) { //Not reassembled frames guint32 leCanId = tvb_get_ntohl(canIdTvb, 0x10); canId = (leCanId >> 24) | ((leCanId >> 8)&0xFF00) | ((leCanId << 8)&0xFF0000) | ((leCanId << 24) &0xFF000000); } else if(tvb_reported_length(canIdTvb) >= 4) { //For reassembled frames canId = tvb_get_ntohl(canIdTvb, 0); } else { std::cout << "tvb buffer too short" << std::endl; return -1; } canId &= 0x1FFFFFFF; //Obtain the payload of the Can Frame tvb_memcpy(tvb, content, 0, data_length); //Generate the frame from the frame factory. As arguments, we pass the ID, the data and the length. std::unique_ptr frame = J1939Factory::getInstance().getJ1939Frame(canId, content, data_length); if(!frame.get()) { //Bad decoding... std::cout << "dissect_J1939 error" << std::endl; return -1; } col_set_str(pinfo->cinfo, COL_PROTOCOL, "J1939"); col_clear(pinfo->cinfo, COL_INFO); //In the info column, publish the name of the frame col_add_fstr(pinfo->cinfo, COL_INFO, "Frame: %s", frame->getName().c_str()); //Add item to the tree with the name of the frame ti = proto_tree_add_string(tree, hf_j1939_frame, canIdTvb, 1, 2, frame->getName().c_str()); j1939_tree = proto_item_add_subtree(ti, ett_j1939); if(frame->getPGN() == DM1_PGN) { DM1 *dm1Frame = static_cast(frame.get()); dissect_dm1_frame(tvb, j1939_tree, ti, dm1Frame); } if(frame->isGenericFrame()) { //If the frame is a generic frame (this means that it contains several defined SPNs), call a proper function to handle it GenericFrame * genFrame = (GenericFrame *)(frame.get()); dissect_generic_frame(tvb, j1939_tree, ti, genFrame); } else if(bamReassembler.toBeHandled(*frame)) { //This frame is part of BAM protocol if(tvb_get_guint8(canIdTvb, 1) == 1) { return 0; //Broadcast frames are not reassembled } //We let our reassembler to handle them guint expected_size = 0; fragment_head *bam_head = NULL; tvbuff_t *def_tvb = NULL; tvbuff_t *next_tvb; guint sq = 0; gboolean update_col_info = TRUE; guint32 frag_data_len = 0; if(!pinfo->fd->visited) { //Only treat the frames if it is the first time we see them!!! try { expected_size = bamReassembler.handleFrame(*frame); } catch(J1939EncodeException &) { return -1; } switch(frame->getPGN()) { case TP_CM_PGN: { TPCMFrame *connFrame = (TPCMFrame *)(frame.get()); guint8 *canIdTVBData = (guint8 *)g_malloc(sizeof(guint32)); guint32 realCanId = (connFrame->getPriority() << J1939_PRIORITY_OFFSET) | (connFrame->getDataPgn() << J1939_PGN_OFFSET) | (connFrame->getSrcAddr() << J1939_SRC_ADDR_OFFSET); frag_data_len = sizeof(guint32); phton32(canIdTVBData, realCanId); def_tvb = tvb_new_real_data(canIdTVBData, frag_data_len, frag_data_len); tvb_set_free_cb(def_tvb, g_free); } break; case TP_DT_PGN: { TPDTFrame *dataFrame = (TPDTFrame *)(frame.get()); sq = dataFrame->getSq(); frag_data_len = TP_DT_PACKET_SIZE; gint trailing_bytes = TP_DT_PACKET_SIZE * sq - expected_size; if(trailing_bytes >= TP_DT_PACKET_SIZE) { return -1; } if(trailing_bytes > 0) { frag_data_len -= trailing_bytes; } def_tvb = tvb_new_subset_remaining(tvb, 1); } break; default: break; } } bam_head = fragment_add_seq_check(&bam_reassembly_table, def_tvb, 0, pinfo, 0, NULL, sq, frag_data_len, !bamReassembler.reassembledFramesPending()); if(bamReassembler.reassembledFramesPending()) { bamReassembler.dequeueReassembledFrame(); } next_tvb = process_reassembled_data(tvb, 0, pinfo, "Reassembled BAM", bam_head, &bam_frag_items, &update_col_info, j1939_tree); if(next_tvb) { call_dissector(j1939_handle, tvb_new_subset_remaining(next_tvb, sizeof(guint32)), pinfo, j1939_tree); } } else { //Another type of frame than the generic ones and the ones that belong to BAM protocol switch(frame->getPGN()) { case FMS1_PGN: { FMS1Frame* fms1 = (FMS1Frame *)(frame.get()); dissect_fms1_frame(tvb, j1939_tree, ti, fms1); } break; default: break; } } return tvb_captured_length(tvb); } void dissect_generic_frame(tvbuff_t *tvb, proto_tree *j1939_tree, proto_item *ti, GenericFrame * genFrame) { proto_tree *spn_tree; std::set spnNumbers = genFrame->getSPNNumbers(); for(auto iter = spnNumbers.begin(); iter != spnNumbers.end(); ++iter) { const SPN *spn = genFrame->getSPN(*iter); ti = proto_tree_add_uint(j1939_tree, hf_j1939_spn, tvb, spn->getOffset(), spn->getByteSize(), *iter); spn_tree = proto_item_add_subtree(ti, ett_j1939_can); switch(spn->getType()) { case SPN::SPN_NUMERIC: { const SPNNumeric *spnNum = (SPNNumeric *)(spn); ti = proto_tree_add_double_format(spn_tree, spnNumToHinfoId[*iter], tvb, spn->getOffset(), spnNum->getByteSize(), spnNum->getFormattedValue(), "%s: %.10g %s", spn->getName().c_str(), spnNum->getFormattedValue(), spnNum->getUnits().c_str()); } break; case SPN::SPN_STATUS: { const SPNStatus *spnStatus = (SPNStatus *)(spn); ti = proto_tree_add_uint_bits_format_value(spn_tree, spnNumToHinfoId[*iter], tvb, (spn->getOffset() << 3) + 8 - spnStatus->getBitOffset() - spnStatus->getBitSize()/* + spnStatus->getBitOffset()*/, spnStatus->getBitSize(), spnStatus->getValue(), "%s (%u)" , spnStatus->getValueDescription(spnStatus->getValue()).c_str(), spnStatus->getValue()); } break; case SPN::SPN_STRING: { const SPNString *spnStr = (SPNString *)(spn); ti = proto_tree_add_item(spn_tree, spnNumToHinfoId[*iter], tvb, spn->getOffset(), spnStr->getValue().size(), ENC_NA); } break; default: break; } } } void dissect_fms1_frame(tvbuff_t *tvb, proto_tree *j1939_tree, proto_item *, const FMS1Frame *fms1Frame) { u8 firstTts = TTSS_PER_BLOCK * fms1Frame->getBlockID() + 1; proto_tree_add_item(j1939_tree, hf_j1939_blockId, tvb, 0, 1, ENC_NA); for(int tts = firstTts; tts < firstTts + TTSS_PER_BLOCK; ++tts) { proto_tree_add_item(j1939_tree, ttsNumToHinfoId[tts], tvb, (((tts - 1) % TTSS_PER_BLOCK) + 1)/ 2, 1, ENC_NA); } } void dissect_dm1_frame(tvbuff_t *tvb, proto_tree *j1939_tree, proto_item *ti, const DM1 *dm1Frame) { const std::vector& dtcs = dm1Frame->getDTCs(); proto_tree *dtc_tree; size_t offset = 2; for(auto dtc = dtcs.begin(); dtc != dtcs.end(); ++dtc) { ti = proto_tree_add_item(j1939_tree, hf_j1939_dtc, tvb, offset, DTC_SIZE, ENC_NA); dtc_tree = proto_item_add_subtree(ti, ett_j1939_dtc); ti = proto_tree_add_uint_bits_format_value(dtc_tree, hf_j1939_spn, tvb, (offset << 3), SPN_NUMBER_MAX_BITS, dtc->getSpn(), "%u" , dtc->getSpn()); ti = proto_tree_add_uint_bits_format_value(dtc_tree, hf_j1939_fmi, tvb, (offset << 3) + SPN_NUMBER_MAX_BITS, 5, dtc->getFmi(), "%u" , dtc->getFmi()); ti = proto_tree_add_uint_bits_format_value(dtc_tree, hf_j1939_oc, tvb, (offset << 3) + SPN_NUMBER_MAX_BITS + 5 + 1, 7, dtc->getOc(), "%u" , dtc->getOc()); offset += DTC_SIZE; } } ================================================ FILE: wireshark/dissector/plugin.c ================================================ #include "config.h" #include /* plugins are DLLs */ #define WS_BUILD_DLL #include "ws_symbol_export.h" #include "epan/proto.h" void proto_register_j1939(void); void proto_reg_handoff_j1939(void); WS_DLL_PUBLIC_DEF const gchar plugin_version[] = "1.0.0"; WS_DLL_PUBLIC_DEF const gchar plugin_release[] = "1.0.0"; WS_DLL_PUBLIC void plugin_register(void); void plugin_register(void) { static proto_plugin plug_j1939; plug_j1939.register_protoinfo = proto_register_j1939; plug_j1939.register_handoff = proto_reg_handoff_j1939; proto_register_plugin(&plug_j1939); }