diff --git a/api/icsneolegacy/icsneolegacy.cpp b/api/icsneolegacy/icsneolegacy.cpp index fc371e5..c1963d9 100644 --- a/api/icsneolegacy/icsneolegacy.cpp +++ b/api/icsneolegacy/icsneolegacy.cpp @@ -73,6 +73,25 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t return false; const neomessage_frame_t& frame = *reinterpret_cast(&newmsg); + + auto copyStatusData = [&]() { + oldmsg.NetworkID = static_cast(frame.netid); // Note: NetID remapping from the original API is not supported + oldmsg.NetworkID2 = static_cast(frame.netid >> 8); + oldmsg.DescriptionID = frame.description; + oldmsg.StatusBitField = frame.status.statusBitfield[0]; + oldmsg.StatusBitField2 = frame.status.statusBitfield[1]; + oldmsg.StatusBitField3 = frame.status.statusBitfield[2]; + oldmsg.StatusBitField4 = frame.status.statusBitfield[3]; + }; + + auto copyFrameData = [&]() { + oldmsg.ExtraDataPtr = (void*)frame.data; + oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0; + memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8)); + oldmsg.ArbIDOrHeader = *reinterpret_cast(frame.header); + copyStatusData(); + }; + switch (Network::Type(frame.type)) { case Network::Type::CAN: @@ -81,28 +100,42 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t oldmsg.Protocol = frame.status.canfdFDF ? SPY_PROTOCOL_CANFD : SPY_PROTOCOL_CAN; oldmsg.NumberBytesData = static_cast(std::min(frame.length, (size_t)255)); oldmsg.NumberBytesHeader = 4; + copyFrameData(); break; case Network::Type::Ethernet: oldmsg.Protocol = SPY_PROTOCOL_ETHERNET; oldmsg.NumberBytesData = static_cast(frame.length & 0xFF); oldmsg.NumberBytesHeader = static_cast(frame.length >> 8); + copyFrameData(); break; + case Network::Type::LIN: + { + const neomessage_lin_t& linFrame = *reinterpret_cast(&frame); + icsSpyMessageJ1850& linSpyMsg = *reinterpret_cast(&oldmsg); + linSpyMsg.Protocol = SPY_PROTOCOL_LIN; + linSpyMsg.NumberBytesHeader = static_cast(std::min(linFrame.length, static_cast(3))); + linSpyMsg.NumberBytesData = static_cast(linFrame.length - linSpyMsg.NumberBytesHeader); + oldmsg.ArbIDOrHeader = *reinterpret_cast(frame.header); + copyStatusData(); + if ((2 < linFrame.length) && (linFrame.length <= 10)) { + auto copyBytes = std::min(linSpyMsg.NumberBytesData, static_cast(6)); + std::memcpy(oldmsg.Data, frame.data, copyBytes); + oldmsg.Data[copyBytes] = linFrame.checksum; + } else if (2 == linFrame.length) { + std::memset(oldmsg.Data, 0, 8); + linSpyMsg.Header[linSpyMsg.NumberBytesHeader] = linFrame.checksum; + ++linSpyMsg.NumberBytesHeader; + } else { + std::memset(oldmsg.Data, 0, 8); + } + if (linFrame.linStatus.txCommander) + linSpyMsg.StatusBitField |= SPY_STATUS_INIT_MESSAGE; + break; + } default: return false; } - oldmsg.ExtraDataPtr = (void*)frame.data; - oldmsg.ExtraDataPtrEnabled = frame.length > 8 ? 1 : 0; - memcpy(oldmsg.Data, frame.data, std::min(frame.length, (size_t)8)); - oldmsg.ArbIDOrHeader = *reinterpret_cast(frame.header); - oldmsg.NetworkID = static_cast(frame.netid); // Note: NetID remapping from the original API is not supported - oldmsg.NetworkID2 = static_cast(frame.netid >> 8); - oldmsg.DescriptionID = frame.description; - oldmsg.StatusBitField = frame.status.statusBitfield[0]; - oldmsg.StatusBitField2 = frame.status.statusBitfield[1]; - oldmsg.StatusBitField3 = frame.status.statusBitfield[2]; - oldmsg.StatusBitField4 = frame.status.statusBitfield[3]; - // Timestamp - epoch = 1/1/2007 - 25ns per tick most of the time uint64_t t = frame.timestamp; uint16_t res = 0; @@ -127,6 +160,84 @@ static bool NeoMessageToSpyMessage(const neodevice_t* device, const neomessage_t return true; } +static bool SpyMessageToNeoMessage(const icsSpyMessage& oldmsg, neomessage_frame_t& frame, unsigned int& lNetworkID) +{ + frame.netid = static_cast(lNetworkID); + frame.description = oldmsg.DescriptionID; + + frame.status.statusBitfield[0] = oldmsg.StatusBitField; + frame.status.statusBitfield[1] = oldmsg.StatusBitField2; + frame.status.statusBitfield[2] = oldmsg.StatusBitField3; + frame.status.statusBitfield[3] = oldmsg.StatusBitField4; + + auto copyFrameDataPtr = [&]() { + memcpy(frame.header, &oldmsg.ArbIDOrHeader, sizeof(frame.header)); + if ((oldmsg.ExtraDataPtr != nullptr) && (oldmsg.ExtraDataPtrEnabled == 1)) + frame.data = reinterpret_cast(oldmsg.ExtraDataPtr); + else + frame.data = oldmsg.Data; + }; + + switch(oldmsg.Protocol) + { + case SPY_PROTOCOL_ETHERNET: + { + frame.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255); + copyFrameDataPtr(); + break; + } + case SPY_PROTOCOL_LIN: + { + neomessage_lin_t& linFrame = *reinterpret_cast(&frame); + const uint8_t numberBytesHeader = std::min(oldmsg.NumberBytesHeader, static_cast(3)); + const uint8_t numberBytesData = std::min(oldmsg.NumberBytesData, static_cast(7)); + frame.length = numberBytesHeader + numberBytesData; + linFrame.type = ICSNEO_NETWORK_TYPE_LIN; + if (oldmsg.StatusBitField & SPY_STATUS_INIT_MESSAGE) + linFrame.linStatus.txCommander = true; + else + linFrame.linStatus.txResponder = true; + + copyFrameDataPtr(); + if (frame.length > 2) { + size_t checksum = 0; + uint8_t* lastByte = nullptr; + for(size_t idx = 1; idx < (frame.length - 1); ++idx) { + if (idx < oldmsg.NumberBytesHeader) { + checksum += frame.header[idx]; + lastByte = (frame.header + idx + 1); + } else { + checksum += frame.data[idx-numberBytesHeader]; + lastByte = const_cast(frame.data) + idx - numberBytesHeader + 1; + } + if (checksum > 255) { checksum -= 255; } + } + size_t enhanced = frame.header[0] + checksum; + if (enhanced > 255) { enhanced -= 255; } + checksum ^= 0xff; + enhanced ^= 0xff; + + if ((lastByte != nullptr) && (*lastByte != checksum) && (*lastByte == enhanced)) + linFrame.linStatus.txChecksumEnhanced = true; + } + break; + } + case SPY_PROTOCOL_CANFD: + { + frame.status.canfdFDF = true; + copyFrameDataPtr(); + break; + } + default: + { + frame.length = oldmsg.NumberBytesData; + copyFrameDataPtr(); + break; + } + } + return true; +} + static inline bool Within(size_t value, size_t min, size_t max) { return ((min <= value) && (value < max)); @@ -396,25 +507,9 @@ int LegacyDLLExport icsneoTxMessagesEx(void* hObject, icsSpyMessage* pMsg, unsig *NumTxed = 0; for (unsigned int i = 0; i < lNumMessages; i++) { - const icsSpyMessage& oldmsg = pMsg[i]; newmsg = {}; - newmsg.netid = (uint16_t)lNetworkID; - newmsg.description = oldmsg.DescriptionID; - memcpy(newmsg.header, &oldmsg.ArbIDOrHeader, sizeof(newmsg.header)); - if (oldmsg.Protocol != SPY_PROTOCOL_ETHERNET) - newmsg.length = oldmsg.NumberBytesData; - else - newmsg.length = ((oldmsg.NumberBytesHeader & 255) << 8) | (oldmsg.NumberBytesData & 255); - if (oldmsg.ExtraDataPtr != nullptr && oldmsg.ExtraDataPtrEnabled == 1) - newmsg.data = reinterpret_cast(oldmsg.ExtraDataPtr); - else - newmsg.data = oldmsg.Data; - newmsg.status.statusBitfield[0] = oldmsg.StatusBitField; - newmsg.status.statusBitfield[1] = oldmsg.StatusBitField2; - newmsg.status.statusBitfield[2] = oldmsg.StatusBitField3; - newmsg.status.statusBitfield[3] = oldmsg.StatusBitField4; - if (oldmsg.Protocol == SPY_PROTOCOL_CANFD) - newmsg.status.canfdFDF = true; + const icsSpyMessage& oldMsg = pMsg[i]; + SpyMessageToNeoMessage(oldMsg, newmsg, lNetworkID); if (icsneo_transmit(device, reinterpret_cast(&newmsg))) (*NumTxed)++; } diff --git a/communication/message/linmessage.cpp b/communication/message/linmessage.cpp index 4dc68c8..835cd67 100644 --- a/communication/message/linmessage.cpp +++ b/communication/message/linmessage.cpp @@ -19,4 +19,12 @@ void LINMessage::calcChecksum(LINMessage& message) { message.checksum ^= 0xFFu; } +uint8_t LINMessage::calcProtectedID(uint8_t& id) { + uint8_t protID = id; + auto bit = [&](uint8_t pos)->uint8_t { return ((protID >> pos) & 0x1u); }; + protID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7); + protID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6); + return protID; +} + } //namespace icsneo \ No newline at end of file diff --git a/communication/message/neomessage.cpp b/communication/message/neomessage.cpp index 895144a..acaab67 100644 --- a/communication/message/neomessage.cpp +++ b/communication/message/neomessage.cpp @@ -2,6 +2,7 @@ #include "icsneo/communication/message/canmessage.h" #include "icsneo/communication/message/ethernetmessage.h" #include "icsneo/communication/message/canerrorcountmessage.h" +#include "icsneo/communication/message/linmessage.h" using namespace icsneo; @@ -17,6 +18,7 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr message) { neomessage_frame_t& frame = *(neomessage_frame_t*)&neomsg; auto framemsg = std::static_pointer_cast(message); const auto netType = framemsg->network.getType(); + frame.netid = (neonetid_t)framemsg->network.getNetID(); frame.type = (neonettype_t)netType; frame.description = framemsg->description; @@ -53,6 +55,47 @@ neomessage_t icsneo::CreateNeoMessage(const std::shared_ptr message) { //eth.status.xyz = ethmsg->noPadding; break; } + case Network::Type::LIN: { + neomessage_lin_t& lin = *(neomessage_lin_t*)&neomsg; + auto linmsg = std::static_pointer_cast(message); + if(!linmsg) { break; } + const auto linHdrBytes = std::min(linmsg->data.size(), static_cast(2)); + lin.header[0] = linmsg->protectedID; + std::copy(linmsg->data.begin(), linmsg->data.begin() + linHdrBytes, lin.header + 1); + linmsg->calcChecksum(*linmsg); + lin.checksum = linmsg->checksum; + lin.data = linmsg->data.data() + linHdrBytes; + if(linmsg->isEnhancedChecksum != linmsg->statusFlags.TxChecksumEnhanced) { + linmsg->isEnhancedChecksum = true; + linmsg->statusFlags.TxChecksumEnhanced = true; + } + if(linmsg->data.size()) + lin.length = linmsg->data.size() + 2; + else + lin.length = 1; + + lin.linStatus = { + linmsg->statusFlags.TxChecksumEnhanced, + linmsg->statusFlags.TxCommander, + linmsg->statusFlags.TxResponder, + linmsg->statusFlags.UpdateResponderOnce, + linmsg->statusFlags.HasUpdatedResponderOnce, + linmsg->statusFlags.BusRecovered, + linmsg->statusFlags.BreakOnly + }; + + lin.status.linJustBreakSync = linmsg->errFlags.ErrRxBreakSyncOnly; + lin.status.linErrorTXRXMismatch = linmsg->errFlags.ErrTxRxMismatch; + lin.status.linErrorRXBreakNotZero = linmsg->errFlags.ErrRxBreakNotZero; + lin.status.linErrorRXBreakTooShort = linmsg->errFlags.ErrRxBreakTooShort; + lin.status.linErrorRXSyncNot55 = linmsg->errFlags.ErrRxSyncNot55; + lin.status.linErrorRXDataGreaterEight = linmsg->errFlags.ErrRxDataLenOver8; + lin.status.linSyncFrameError = linmsg->errFlags.ErrFrameSync; + lin.status.linIDFrameError = linmsg->errFlags.ErrFrameMessageID; + lin.status.linSlaveByteError = linmsg->errFlags.ErrFrameResponderData; + lin.status.checksumError = linmsg->errFlags.ErrChecksumMatch; + break; + } default: // TODO Implement others break; @@ -105,6 +148,53 @@ std::shared_ptr icsneo::CreateMessageFromNeoMessage(const neomessage_t* ethmsg->data.insert(ethmsg->data.end(), eth.data, eth.data + eth.length); return ethmsg; } + case Network::Type::LIN: { + neomessage_lin_t& lin = *(neomessage_lin_t*)neomessage; + auto linmsg = std::make_shared(); + linmsg->network = network; + linmsg->description = lin.description; + linmsg->protectedID = lin.header[0]; + linmsg->ID = linmsg->protectedID & 0x3F; + linmsg->statusFlags = { + static_cast(lin.linStatus.txChecksumEnhanced), + static_cast(lin.linStatus.txCommander), + static_cast(lin.linStatus.txResponder), + static_cast(lin.linStatus.updateResponderOnce), + static_cast(lin.linStatus.hasUpdatedResponderOnce), + static_cast(lin.linStatus.busRecovered), + static_cast(lin.linStatus.breakOnly) + }; + linmsg->isEnhancedChecksum = linmsg->statusFlags.TxChecksumEnhanced; + linmsg->errFlags = { + static_cast(lin.linStatus.breakOnly), + static_cast(lin.status.linJustBreakSync), + static_cast(lin.status.linErrorTXRXMismatch), + static_cast(lin.status.linErrorRXBreakNotZero), + static_cast(lin.status.linErrorRXBreakTooShort), + static_cast(lin.status.linErrorRXSyncNot55), + static_cast(lin.status.linErrorRXDataGreaterEight), + static_cast(lin.status.linSyncFrameError), + static_cast(lin.status.linIDFrameError), + static_cast(lin.status.linSlaveByteError), + static_cast(lin.status.checksumError) + }; + if(lin.length > 1) { + auto numHeaderBytes = std::min(lin.length, static_cast(3)); + linmsg->data.insert(linmsg->data.end(), (lin.header + 1), (lin.header + numHeaderBytes)); + linmsg->data.insert(linmsg->data.end(), lin.data, (lin.data + (lin.length - numHeaderBytes))); + linmsg->checksum = linmsg->data.back(); + linmsg->data.pop_back(); + } + if (linmsg->statusFlags.TxCommander) { + if (linmsg->data.size()) + linmsg->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; + else + linmsg->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY; + } else if (linmsg->statusFlags.TxResponder) { + linmsg->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; + } + return linmsg; + } default: break; } break; diff --git a/communication/packet/linpacket.cpp b/communication/packet/linpacket.cpp index 026eb46..979a018 100644 --- a/communication/packet/linpacket.cpp +++ b/communication/packet/linpacket.cpp @@ -5,7 +5,6 @@ namespace icsneo { std::shared_ptr HardwareLINPacket::DecodeToMessage(const std::vector& bytestream) { - auto msg = std::make_shared(); const HardwareLINPacket* packet = reinterpret_cast(bytestream.data()); size_t numDataBytes = packet->CoreMiniBitsLIN.len; size_t numHeaderBytes = sizeof(HardwareLINPacket::CoreMiniBitsLIN); @@ -17,14 +16,13 @@ std::shared_ptr HardwareLINPacket::DecodeToMessage(const std::vector(static_cast(packet->CoreMiniBitsLIN.ID)); msg->network = Network::GetNetIDFromCoreMiniNetwork(static_cast(packet->networkID)); - msg->protectedID = packet->CoreMiniBitsLIN.ID; - msg->ID = (packet->CoreMiniBitsLIN.ID & 0x3Fu); msg->isEnhancedChecksum = static_cast(packet->CoreMiniBitsLIN.TxChkSumEnhanced); /* Minimum one responder byte and one checksum byte. */ if(2u > packet->CoreMiniBitsLIN.len) - msg->type = LINMessage::Type::LIN_ERROR; + msg->linMsgType = LINMessage::Type::LIN_ERROR; auto dataStart = bytestream.begin() + numHeaderBytes; std::copy(dataStart, (dataStart+numDataBytes), std::back_inserter(msg->data)); @@ -78,16 +76,16 @@ std::shared_ptr HardwareLINPacket::DecodeToMessage(const std::vector(packet->CoreMiniBitsLIN.BreakOnly) }; if(msg->statusFlags.TxCommander || msg->statusFlags.TxResponder) - msg->type = LINMessage::Type::LIN_COMMANDER_MSG; + msg->linMsgType = LINMessage::Type::LIN_COMMANDER_MSG; else if(msg->statusFlags.BreakOnly) - msg->type = LINMessage::Type::LIN_BREAK_ONLY; + msg->linMsgType = LINMessage::Type::LIN_BREAK_ONLY; if( msg->errFlags.ErrRxBreakOnly || msg->errFlags.ErrRxBreakSyncOnly || msg->errFlags.ErrTxRxMismatch || msg->errFlags.ErrRxBreakNotZero || msg->errFlags.ErrRxBreakTooShort || msg->errFlags.ErrRxSyncNot55 || msg->errFlags.ErrRxDataLenOver8 || msg->errFlags.ErrFrameSync || msg->errFlags.ErrFrameMessageID || msg->errFlags.ErrChecksumMatch || msg->errFlags.ErrFrameResponderData ) - { msg->type = LINMessage::Type::LIN_ERROR; } + { msg->linMsgType = LINMessage::Type::LIN_ERROR; } msg->timestamp = packet->timestamp; return msg; @@ -97,7 +95,7 @@ bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vector(8ul, message.data.size()) + 3ul) & 0xFu); if(size > 3) { ++size; } // add a checksum byte if there's data - switch(message.type) { + switch(message.linMsgType) { case LINMessage::Type::LIN_HEADER_ONLY: case LINMessage::Type::LIN_COMMANDER_MSG: { @@ -117,12 +115,7 @@ bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vectoruint8_t { return ((message.protectedID >> pos) & 0x1u); }; - message.protectedID |= (~(bit(1) ^ bit(3) ^ bit(4) ^ bit(5)) << 7); - message.protectedID |= ((bit(0) ^ bit(1) ^ bit(2) ^ bit(4)) << 6); - + message.calcProtectedID(message.ID); bytestream.insert(bytestream.end(), { static_cast(0x00u), @@ -132,7 +125,7 @@ bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vector(message.protectedID) }); - switch(message.type) { + switch(message.linMsgType) { case(LINMessage::Type::LIN_COMMANDER_MSG): case(LINMessage::Type::LIN_UPDATE_RESPONDER): { @@ -144,6 +137,7 @@ bool HardwareLINPacket::EncodeFromMessage(LINMessage& message, std::vector #include @@ -219,21 +219,21 @@ int main() { // Attempt to initialize the library and access its functions // This call searches for icsneoc.dll according to the standard dynamic-link library search order - int ret = icsneo_init(); - if(ret == 1) { - printf("The library was already initialized!\n"); - return ret; - } - - if(ret == 2) { - printf("The library could not be found!\n"); - return ret; - } - - if(ret == 3) { - printf("The library is missing functions!\n"); - return ret; - } + //int ret = icsneo_init(); + //if(ret == 1) { + // printf("The library was already initialized!\n"); + // return ret; + //} +// + //if(ret == 2) { + // printf("The library could not be found!\n"); + // return ret; + //} +// + //if(ret == 3) { + // printf("The library is missing functions!\n"); + // return ret; + //} neoversion_t ver = icsneo_getVersion(); printf("ICS icsneoc.dll version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch); @@ -487,6 +487,31 @@ int main() { printf("(%"PRIu64")\n", canMsg->timestamp); break; } + case ICSNEO_NETWORK_TYPE_LIN: { + neomessage_lin_t* linMsg = (neomessage_lin_t*)frame; + size_t frameLen = linMsg->length; + size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0; + size_t numberBytesHeader = (dataLen > 1) ? 3 : 1; + size_t numberBytesData = frameLen - numberBytesHeader; + if(linMsg->netid == ICSNEO_NETID_LIN) { + printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen); + } + else if (linMsg->netid == ICSNEO_NETID_LIN2) { + printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen); + } + + for(size_t i = 0; i < dataLen; ++i) { + if (i < 2) { + printf("%02x ", linMsg->header[i+1]); + } else { + printf("%02x ", linMsg->data[i-2]); + } + } + if(numberBytesData > 0) + printf("| Checksum: 0x%02x\n", linMsg->data[numberBytesData-1]); + else + printf("| Checksum: 0x%02x\n", linMsg->header[numberBytesHeader-1]); + } default: printf("\tMessage on netid %d with length %zu\n", frame->netid, frame->length); break; @@ -762,7 +787,7 @@ int main() { case 'X': case 'x': printf("Exiting program\n"); - return !icsneo_close(); + return 0; default: printf("Unexpected input, exiting!\n"); return 1; diff --git a/examples/c/legacy/CMakeLists.txt b/examples/c/legacy/CMakeLists.txt new file mode 100644 index 0000000..035be75 --- /dev/null +++ b/examples/c/legacy/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.2) +project(libicsneoc-legacy-lin-example VERSION 0.2.0) + +add_executable(libicsneoc-legacy-lin-example lin/main.c) +target_link_libraries(libicsneoc-legacy-lin-example icsneolegacy) \ No newline at end of file diff --git a/examples/c/legacy/README.md b/examples/c/legacy/README.md new file mode 100644 index 0000000..53b15ca --- /dev/null +++ b/examples/c/legacy/README.md @@ -0,0 +1,80 @@ +# libicsneo C Example + +This is an example console application that uses the icsneoc library to control an Intrepid Control Systems hardware device. + +## Cloning + +This will create a copy of the repository on your local machine. + +Run: + +```shell +git clone https://github.com/intrepidcs/libicsneo-examples -b v0.2.0-dev --recursive +``` + +Alternatively, if you cloned without the `--recursive` flag, you must enter the `libicsneo-examples` folder and run the following: + +```shell +git submodule update --recursive --init +``` + +If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build! + +## Windows using Visual Studio 2017+ + +### Building the DLL + +First, we are going to build the icsneoc library into a .dll file that we can later use in order to access the library functions. + +1. Launch Visual Studio and open the `libicsneo-examples` folder. +2. Choose `File->Open->Cmake...` +3. Navigate to `third-party/libicsneo` and select the `CMakeLists.txt` there. +4. Visual Studio will process the CMake project. +5. Select `Build->Rebuild All` +6. Visual Studio will generate the `icsneoc.dll` file, which can then be found by selecting `Project->Cmake Cache (x64-Debug Only)->Open in Explorer`. If the file cannot be found, search in `libicsneo-examples/third-party/libicsneo/out/build/x64-Debug` and double-check that the build succeeded in step 5. +7. Move the `icsneoc.dll` file to the `/C/Windows/System32` folder. This will allow it to be found by icsneo_init(), which loads all the library functions. + * Alternatively, the `icsneoc.dll` file can be placed in the same directory as `libicsneoc-example.exe`, which is typically `libicsneo-examples/libicsneoc-example/out/build/x64-Debug`, although this is not recommended. For more information, refer to [the Microsoft documentation](https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order). + +### Building the example program + +Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions. + +1. Choose `File->Open->Cmake...` +2. Navigate to `libicsneo-examples/libicsneoc-example` and select the `CMakeLists.txt` there. +3. Visual Studio will process the CMake project. +4. Select `Build->Rebuild All` +5. Click on the dropdown arrow attached to the green play button (labelled "Select Startup Item") and select `libicsneoc-example.exe` +6. Click on the green play button to run the example. + +## Ubuntu 18.04 LTS + +### Building the .so + +First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions. + +1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev` +2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build` +3. Enter the build directory with `cd build` +4. Run `cmake ..` to generate your Makefile. + * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. +5. Run `make` to build the library. + * Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. +6. Run `sudo cp libicsneoc.so /usr/lib` so that it can be found via the default ubuntu .so search path. For more information, see the [ld.so.8 man page](http://man7.org/linux/man-pages/man8/ld.so.8.html). + +### Building the example program + +Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions. + +1. Change directories to `libicsneo-examples/libicsneoc-example` +2. Create a build directory by running `mkdir -p build` +3. Enter the build directory with `cd build` +4. Run `cmake ..` to generate your Makefile. + * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. +5. Run `make` to build the library. + * Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. +6. Run `sudo ./libicsneoc-example` to run the example. + * Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices. + +## macOS + +Instructions coming soon™ diff --git a/examples/c/legacy/lin/main.c b/examples/c/legacy/lin/main.c new file mode 100644 index 0000000..334ffcd --- /dev/null +++ b/examples/c/legacy/lin/main.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +#if defined _WIN32 + #include "icsneo/platform/windows.h" + #define SLEEP(msecs) Sleep(msecs) +#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) + #include + #define SLEEP(msecs) do { \ + struct timespec ts; \ + ts.tv_sec = msecs/1000; \ + ts.tv_nsec = msecs%1000*1000; \ + nanosleep(&ts, NULL); \ + } while (0) +#else + #error "Platform unknown" +#endif + +// Get the PRIu64 macro for timestamps +#define __STDC_FORMAT_MACROS +#include + +// Include icsneo/icsneolegacy.h to access library functions +#include "icsneo/icsneolegacy.h" + +int main() { + int ver = icsneoGetDLLVersion(); + printf("ICS icsneolegacy.dll version %u\n\n", ver); + // Find and attempt to open device + //legacy open device + int numDevices = 10; + NeoDevice devices[10]; + void* hObject; // holds a handle to the neoVI object + int iRetVal = 0; + int deviceTypes = 0; + int iResult = 0; + + iRetVal = icsneoFindNeoDevices(deviceTypes, devices, &numDevices); + if(iRetVal) { + // Attempt to open the selected device, enable message polling, and go online + iRetVal = icsneoOpenNeoDevice(&devices[0], &hObject, NULL, 1, 0); + if(iRetVal) { + printf("Device found and opened!\n"); + } else { + printf("Device found but failed to open!\n"); + } + } else { + printf("No new devices found!\n"); + } + + // Send message LIN + { + //lin responder frame + icsSpyMessageJ1850 msg1 = {0}; + int lNetworkID; + msg1.Protocol = SPY_PROTOCOL_LIN; + msg1.StatusBitField = 0; + msg1.StatusBitField2 = 0; + lNetworkID = NETID_LIN2; + msg1.Header[0] = 0x11; //protected ID + msg1.Header[1] = 0xaa; + msg1.Header[2] = 0xbb; + msg1.Data[0] = 0xcc; + msg1.Data[1] = 0xdd; + msg1.Data[2] = 0x11; + msg1.Data[3] = 0x22; + msg1.Data[4] = 0x33; + msg1.Data[5] = 0x44; + msg1.Data[6] = 0x44; //checksum 0x33 enhanced + msg1.NumberBytesData = 7; + msg1.NumberBytesHeader = 3; + iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg1, lNetworkID, 1); + if(!iRetVal) + printf("Device failed to transmit LIN responder update\n"); + else + printf("Transmitted successfully!\n"); + + icsSpyMessageJ1850 msg2 = {0}; + msg2.Protocol = SPY_PROTOCOL_LIN; + msg2.StatusBitField = SPY_STATUS_INIT_MESSAGE; + lNetworkID = NETID_LIN; + msg2.Header[0] = 0x11; //protected ID + msg2.NumberBytesData = 0; + msg2.NumberBytesHeader = 1; + iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg2, lNetworkID, 1); + if(!iRetVal) + printf("Device failed to transmit LIN commander header\n"); + else + printf("Transmitted successfully!\n"); + + SLEEP(250); + + icsSpyMessageJ1850 msg3 = {0}; + msg3.Protocol = SPY_PROTOCOL_LIN; + msg3.StatusBitField = SPY_STATUS_INIT_MESSAGE; + msg3.StatusBitField2 = 0; + lNetworkID = NETID_LIN; + msg3.Header[0] = 0xe2; //protected ID + msg3.Header[1] = 0x44; + msg3.Header[2] = 0x33; + msg3.Data[0] = 0x22; + msg3.Data[1] = 0x11; + msg3.Data[2] = 0x11; + msg3.Data[3] = 0x22; + msg3.Data[4] = 0x33; + msg3.Data[5] = 0x44; + msg3.Data[6] = 0xc7; //checksum + msg3.NumberBytesData = 7; + msg3.NumberBytesHeader = 3; + iRetVal = icsneoTxMessages(hObject, (icsSpyMessage*)&msg3, lNetworkID, 1); + if(!iRetVal) + printf("Device failed to transmit LIN commander message\n"); + else + printf("Transmitted successfully!\n"); + } + SLEEP(1000); + // Get messages + { + static icsSpyMessage rxMsg[30000]; + int numMessages = 0; + int numErrors = 0; + iRetVal = icsneoGetMessages(hObject, rxMsg, &numMessages, &numErrors); + if(!iRetVal) + printf("Get Messages failed!\n"); + else + for(int idx = 0; idx < numMessages; ++idx) + { + if(rxMsg[idx].Protocol == SPY_PROTOCOL_LIN) { + const icsSpyMessageJ1850* linMsg = (icsSpyMessageJ1850*)&rxMsg[idx]; + size_t frameLen = (linMsg->NumberBytesHeader + linMsg->NumberBytesData); + size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0; + if(linMsg->NetworkID == NETID_LIN) { + printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen); + } + else if (linMsg->NetworkID == NETID_LIN2) { + printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->Header[0], dataLen); + } + + for(size_t i = 0; i < dataLen; ++i) { + if (i < 2) { + printf("%02x ", linMsg->Header[i+1]); + } else { + printf("%02x ", linMsg->Data[i-2]); + } + } + if(linMsg->NumberBytesData > 0) + printf("| Checksum: 0x%02x\n", linMsg->Data[linMsg->NumberBytesData-1]); + else + printf("| Checksum: 0x%02x\n", linMsg->Header[linMsg->NumberBytesHeader-1]); + } + } + } + + int iNumberOfErrors = 0; + // Attempt to close the device + { + // Close Communication + iResult = icsneoClosePort(hObject, &iNumberOfErrors); + } + printf("Exiting program\n"); + return iResult; +} diff --git a/examples/c/simple/CMakeLists.txt b/examples/c/simple/CMakeLists.txt new file mode 100644 index 0000000..be83cd0 --- /dev/null +++ b/examples/c/simple/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.2) +project(libicsneoc-simple-lin-example VERSION 0.2.0) + +include(GNUInstallDirs) + +# Include libicsneo's include directory +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../../include) + +if(UNIX) + set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS) +endif() + +add_executable(libicsneoc-simple-lin-example lin/main.c) +if(UNIX) + target_link_libraries(libicsneoc-simple-lin-example ${CMAKE_DL_LIBS}) +endif() +target_link_libraries(libicsneoc-simple-lin-example icsneoc) \ No newline at end of file diff --git a/examples/c/simple/README.md b/examples/c/simple/README.md new file mode 100644 index 0000000..53b15ca --- /dev/null +++ b/examples/c/simple/README.md @@ -0,0 +1,80 @@ +# libicsneo C Example + +This is an example console application that uses the icsneoc library to control an Intrepid Control Systems hardware device. + +## Cloning + +This will create a copy of the repository on your local machine. + +Run: + +```shell +git clone https://github.com/intrepidcs/libicsneo-examples -b v0.2.0-dev --recursive +``` + +Alternatively, if you cloned without the `--recursive` flag, you must enter the `libicsneo-examples` folder and run the following: + +```shell +git submodule update --recursive --init +``` + +If you haven't done this, `third-party/libicsneo` will be empty and you won't be able to build! + +## Windows using Visual Studio 2017+ + +### Building the DLL + +First, we are going to build the icsneoc library into a .dll file that we can later use in order to access the library functions. + +1. Launch Visual Studio and open the `libicsneo-examples` folder. +2. Choose `File->Open->Cmake...` +3. Navigate to `third-party/libicsneo` and select the `CMakeLists.txt` there. +4. Visual Studio will process the CMake project. +5. Select `Build->Rebuild All` +6. Visual Studio will generate the `icsneoc.dll` file, which can then be found by selecting `Project->Cmake Cache (x64-Debug Only)->Open in Explorer`. If the file cannot be found, search in `libicsneo-examples/third-party/libicsneo/out/build/x64-Debug` and double-check that the build succeeded in step 5. +7. Move the `icsneoc.dll` file to the `/C/Windows/System32` folder. This will allow it to be found by icsneo_init(), which loads all the library functions. + * Alternatively, the `icsneoc.dll` file can be placed in the same directory as `libicsneoc-example.exe`, which is typically `libicsneo-examples/libicsneoc-example/out/build/x64-Debug`, although this is not recommended. For more information, refer to [the Microsoft documentation](https://docs.microsoft.com/en-us/windows/desktop/dlls/dynamic-link-library-search-order). + +### Building the example program + +Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions. + +1. Choose `File->Open->Cmake...` +2. Navigate to `libicsneo-examples/libicsneoc-example` and select the `CMakeLists.txt` there. +3. Visual Studio will process the CMake project. +4. Select `Build->Rebuild All` +5. Click on the dropdown arrow attached to the green play button (labelled "Select Startup Item") and select `libicsneoc-example.exe` +6. Click on the green play button to run the example. + +## Ubuntu 18.04 LTS + +### Building the .so + +First, we are going to build the icsneoc library into a .so file that we can later use in order to access the library functions. + +1. Install dependencies with `sudo apt update` then `sudo apt install build-essential cmake libusb-1.0-0-dev libpcap0.8-dev` +2. Change directories to `libicsneo-examples/third-party/libicsneo` and create a build directory by running `mkdir -p build` +3. Enter the build directory with `cd build` +4. Run `cmake ..` to generate your Makefile. + * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. +5. Run `make` to build the library. + * Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. +6. Run `sudo cp libicsneoc.so /usr/lib` so that it can be found via the default ubuntu .so search path. For more information, see the [ld.so.8 man page](http://man7.org/linux/man-pages/man8/ld.so.8.html). + +### Building the example program + +Although the example program will build without successfully completing the steps above, it will exit immediately upon running due to a failure to load any library functions. + +1. Change directories to `libicsneo-examples/libicsneoc-example` +2. Create a build directory by running `mkdir -p build` +3. Enter the build directory with `cd build` +4. Run `cmake ..` to generate your Makefile. + * Hint! Running `cmake -DCMAKE_BUILD_TYPE=Debug ..` will generate the proper scripts to build debug, and `cmake -DCMAKE_BUILD_TYPE=Release ..` will generate the proper scripts to build with all optimizations on. +5. Run `make` to build the library. + * Hint! Speed up your build by using multiple processors! Use `make -j#` where `#` is the number of cores/threads your system has plus one. For instance, on a standard 8 thread Intel i7, you might use `-j9` for an ~8x speedup. +6. Run `sudo ./libicsneoc-example` to run the example. + * Hint! In order to run without sudo, you will need to set up the udev rules. Copy `libicsneo-examples/third-party/libicsneo/99-intrepidcs.rules` to `/etc/udev/rules.d`, then run `udevadm control --reload-rules && udevadm trigger` afterwards. While the program will still run without setting up these rules, it will fail to open any devices. + +## macOS + +Instructions coming soon™ diff --git a/examples/c/simple/lin/main.c b/examples/c/simple/lin/main.c new file mode 100644 index 0000000..ef4ad0d --- /dev/null +++ b/examples/c/simple/lin/main.c @@ -0,0 +1,427 @@ +// Signal to dynamically load the library +//#define ICSNEOC_DYNAMICLOAD + +#include +#include +#include +#include +#include + +// Get the PRIu64 macro for timestamps +#define __STDC_FORMAT_MACROS +#include + +// Include icsneo/icsneoc.h to access library functions +#include "icsneo/icsneoc.h" + +#ifdef _WIN32 + #define SLEEP(msecs) Sleep(msecs) +#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__)) + #include + #define SLEEP(msecs) do { \ + struct timespec ts; \ + ts.tv_sec = msecs/1000; \ + ts.tv_nsec = msecs%1000*1000; \ + nanosleep(&ts, NULL); \ + } while (0) +#else + #error "Platform unknown" +#endif + +size_t msgLimit = 50000; +size_t numDevices = 0; +neodevice_t devices[99]; +const neodevice_t* selectedDevice = NULL; + +/** + * \brief Prints all current known devices to output in the following format: + * [num] DeviceType SerialNum Connected: Yes/No Online: Yes/No Msg Polling: On/Off + * + * If any devices could not be described due to an error, they will appear in the following format: + * Description for device num not available! + */ +void printAllDevices() { + if(numDevices == 0) { + printf("No devices found! Please scan for new devices.\n"); + } + for(size_t i = 0; i < numDevices; i++) { + char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 }; + size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION; + + // Updates productDescription and descriptionLength for each device + if(icsneo_describeDevice(devices + i, productDescription, &descriptionLength)) { + printf("[%zd] %s\tConnected: ", i + 1, productDescription); + if(icsneo_isOpen(devices + i)) { + printf("Yes\t"); + } else printf("No\t"); + + printf("Online: "); + if(icsneo_isOnline(devices + i)) { + printf("Yes\t"); + } else printf("No\t"); + + printf("Msg Polling: "); + if(icsneo_isMessagePollingEnabled(devices + i)) { + printf("On\n"); + } else printf("Off\n"); + + } else { + printf("Description for device %zd not available!\n", i + 1); + } + } +} + +/** + * \brief Scans for any new devices, adding them to devices and updating numDevices accordingly + * A total of 99 devices may be stored at once + */ +size_t scanNewDevices() { + neodevice_t newDevices[99]; + size_t numNewDevices = 99; + icsneo_findAllDevices(newDevices, &numNewDevices); + + for(size_t i = 0; i < numNewDevices; ++i) { + devices[numDevices + i] = newDevices[i]; + } + numDevices += numNewDevices; + return numNewDevices; +} +void printLastError() { + neoevent_t error; + if(icsneo_getLastError(&error)) + printf("Error 0x%u: %s\n", error.eventNumber, error.description); + else + printf("No errors found!\n"); +} + +/** + * \brief Gets all current API events and prints them to output + * Flushes the API event cache, meaning future calls (barring any new events) will not detect any further API events + */ +void printAPIEvents() { + neoevent_t events[99]; + size_t eventCount = 99; + if(icsneo_getEvents(events, &eventCount)) { + if(eventCount == 1) { + printf("1 API event found!\n"); + printf("Event 0x%u: %s\n", events[0].eventNumber, events[0].description); + } else { + printf("%d API events found!\n", (int) eventCount); + for(size_t i = 0; i < eventCount; ++i) { + printf("Event 0x%u: %s\n", events[i].eventNumber, events[i].description); + } + } + } else { + printf("Failed to get API events!\n"); + } +} + +/** + * \brief Gets all current device events and prints them to output. If no device events were found, printAPIEvents() is called + * Flushes the device event cache, meaning future calls (barring any new events) will not detect any further device events for this device + */ +void printDeviceEvents(neodevice_t* device) { + neoevent_t events[99]; + size_t eventCount = 99; + if(icsneo_getDeviceEvents(selectedDevice, events, &eventCount)) { + if(eventCount == 1) { + printf("1 device event found!\n"); + printf("Event 0x%x: %s\n", events[0].eventNumber, events[0].description); + } else { + printf("%d device events found!\n", (int) eventCount); + for(size_t i = 0; i < eventCount; ++i) { + printf("Event 0x%x: %s\n", events[i].eventNumber, events[i].description); + } + } + } +} + +/** + * \brief Used to check character inputs for correctness (if they are found in an expected list) + * \param[in] numArgs the number of possible options for the expected character + * \param[in] ... the possible options for the expected character + * \returns the entered character + * + * This function repeatedly prompts the user for input until a matching input is entered + * Example usage: char input = getCharInput(5, 'F', 'u', 'b', 'a', 'r'); + */ +char getCharInput(int numArgs, ...) { + // 99 chars shold be more than enough to catch any typos + char input[99]; + bool found = false; + + va_list vaList; + va_start(vaList, numArgs); + + char* list = (char*) calloc(numArgs, sizeof(char)); + for(int i = 0; i < numArgs; ++i) { + *(list + i) = va_arg(vaList, int); + } + + va_end(vaList); + + while(!found) { + fgets(input, 99, stdin); + if(strlen(input) == 2) { + for(int i = 0; i < numArgs; ++i) { + if(input[0] == *(list + i)) { + found = true; + break; + } + } + } + + if(!found) { + printf("Input did not match expected options. Please try again.\n"); + } + } + + free(list); + + return input[0]; +} + +/** + * \brief Prompts the user to select a device from the list of currently known devices + * \returns a pointer to the device in devices[] selected by the user + * Requires an input from 1-9, so a maximum of 9 devices are supported + */ +const neodevice_t* selectDevice() { + printf("Please select a device:\n"); + printAllDevices(); + printf("\n"); + + size_t selectedDeviceNum = 10; + + while(selectedDeviceNum > numDevices) { + char deviceSelection = getCharInput(9, '1', '2', '3', '4', '5', '6', '7', '8', '9'); + if(deviceSelection < '0') { + printf("Selected device out of range!\n"); + continue; + } + selectedDeviceNum = deviceSelection - '0'; + if(selectedDeviceNum > numDevices) { + printf("Selected device out of range!\n"); + } + } + + printf("\n"); + + return devices + selectedDeviceNum - 1; +} + +int main() { + neoversion_t ver = icsneo_getVersion(); + printf("ICS icsneoc version %u.%u.%u\n\n", ver.major, ver.minor, ver.patch); + // Find and attempt to open device + size_t numNewDevices = scanNewDevices(); + if(numNewDevices == 1) { + printf("1 new device found!\n"); + } else { + printf("%d new devices found!\n", (int) numNewDevices); + } + printAllDevices(); + printf("\n"); + + // Select a device and get its description + if(numDevices == 0) { + printf("No devices found! Please scan for new devices.\n\n"); + return 1; + } + selectedDevice = &devices[0]; + + // Get the product description for the device + char productDescription[ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION] = { 0 }; + size_t descriptionLength = ICSNEO_DEVICETYPE_LONGEST_DESCRIPTION; + icsneo_describeDevice(selectedDevice, productDescription, &descriptionLength); + + // Attempt to open the selected device + { + if(icsneo_openDevice(selectedDevice)) { + printf("%s successfully opened!\n\n", productDescription); + } else { + printf("%s failed to open!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + // Attempt to go online + { + if(icsneo_goOnline(selectedDevice)) { + printf("%s successfully went online!\n\n", productDescription); + } else { + printf("%s failed to go online!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + // Attempt to enable message polling + { + if(icsneo_enableMessagePolling(selectedDevice)) { + printf("Successfully enabled message polling for %s!\n\n", productDescription); + } else { + printf("Failed to enable message polling for %s!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + // Send message LIN + { + // Start generating sample msg + uint8_t sendMessageData[8]; + sendMessageData[0] = 0x33; + sendMessageData[1] = 0x44; + sendMessageData[2] = 0x55; + sendMessageData[3] = 0x66; + sendMessageData[4] = 0x77; + sendMessageData[5] = 0x88; + sendMessageData[6] = 0x88; + + neomessage_lin_t msg = {0}; + + msg.header[0] = 0x11; //protected ID + msg.header[1] = 0x11; + msg.header[2] = 0x22; + msg.length = 10; + msg.netid = ICSNEO_NETID_LIN; + msg.data = sendMessageData; + msg.linStatus.txCommander = 1; + msg.linStatus.txChecksumEnhanced = 1; + msg.type = ICSNEO_NETWORK_TYPE_LIN; + msg.checksum = 0x88; + + // Attempt to transmit the sample msg + if(icsneo_transmit(selectedDevice, (const neomessage_t*) &msg)) { + printf("Message transmit successful!\n\n"); + } else { + printf("Failed to transmit message to %s!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + // Wait for a moment + SLEEP(1000); + // Get messages + { + // Prepare the array of neomessage_t ptrs for reading in the messages + neomessage_t* msgs = (neomessage_t*) malloc(msgLimit * sizeof(neomessage_t)); + + // Get messages + size_t msgCount = msgLimit; + + // Attempt to get messages + if(!icsneo_getMessages(selectedDevice, msgs, &msgCount, (uint64_t) 0)) { + printf("Failed to get messages for %s!\n\n", productDescription); + printLastError(); + free(msgs); + printf("\n"); + } + + if(msgCount == 1) { + printf("1 message received from %s!\n", productDescription); + } else { + printf("%d messages received from %s!\n", (int) msgCount, productDescription); + } + + // Print out the received messages + for(size_t i = 0; i < msgCount; i++) { + const neomessage_t* msg = &msgs[i]; + switch(msg->messageType) { + case ICSNEO_MESSAGE_TYPE_FRAME: { + const neomessage_frame_t* frame = (neomessage_frame_t*)msg; + switch(frame->type) { + case ICSNEO_NETWORK_TYPE_CAN: { + neomessage_can_t* canMsg = (neomessage_can_t*)frame; + printf("\t0x%03x [%zu] ", canMsg->arbid, canMsg->length); + for(size_t i = 0; i < canMsg->length; i++) { + printf("%02x ", canMsg->data[i]); + } + if(canMsg->status.transmitMessage) + printf("TX%s %04x ", canMsg->status.globalError ? " ERR" : "", canMsg->description); + printf("(%"PRIu64")\n", canMsg->timestamp); + break; + } + case ICSNEO_NETWORK_TYPE_LIN: { + neomessage_lin_t* linMsg = (neomessage_lin_t*)frame; + size_t frameLen = linMsg->length; + size_t dataLen = (frameLen > 2) ? (frameLen - 2) : 0; + size_t numberBytesHeader = (dataLen > 1) ? 3 : 1; + size_t numberBytesData = frameLen - numberBytesHeader; + if(linMsg->netid == ICSNEO_NETID_LIN) { + printf("LIN 1 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen); + } + else if (linMsg->netid == ICSNEO_NETID_LIN2) { + printf("LIN 2 | ID: 0x%02x [%zu] ", linMsg->header[0], dataLen); + } + + for(size_t i = 0; i < dataLen; ++i) { + if (i < 2) { + printf("%02x ", linMsg->header[i+1]); + } else { + printf("%02x ", linMsg->data[i-2]); + } + } + printf("| Checksum: 0x%02x\n", linMsg->checksum); + break; + } + } + break; + } + case ICSNEO_MESSAGE_TYPE_CAN_ERROR_COUNT: { + const neomessage_can_error_t* cec = (neomessage_can_error_t*)msg; + printf("\tCAN error counts changed, TEC=%d, REC=%d%s", cec->transmitErrorCount, cec->receiveErrorCount, + cec->status.canBusOff ? " (Bus Off)" : ""); + break; + } + } + } + printf("\n"); + + free(msgs); + } + // Attempt to disable message polling + { + if(icsneo_disableMessagePolling(selectedDevice)) { + printf("Successfully disabled message polling for %s!\n\n", productDescription); + } else { + printf("Failed to disable message polling limit for %s!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + // Attempt to go offline + { + if(icsneo_goOffline(selectedDevice)) { + printf("%s successfully went offline!\n\n", productDescription); + } else { + printf("%s failed to go offline!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + // Attempt to close the device + { + if(icsneo_closeDevice(selectedDevice)) { + numDevices--; + printf("Successfully closed %s!\n\n", productDescription); + + // Shifts everything after the removed device 1 index to the left + bool startResizing = false; + for(size_t i = 0; i < numDevices; ++i) { + if(selectedDevice == devices + i) + startResizing = true; + if(startResizing) + devices[i] = devices[i + 1]; + } + + selectedDevice = NULL; + } else { + printf("Failed to close %s!\n\n", productDescription); + printLastError(); + printf("\n"); + } + } + //exit + printf("Exiting program\n"); + return 0; +} diff --git a/examples/cpp/lin/src/LINExample.cpp b/examples/cpp/lin/src/LINExample.cpp index 4d007ca..0cf332a 100644 --- a/examples/cpp/lin/src/LINExample.cpp +++ b/examples/cpp/lin/src/LINExample.cpp @@ -99,7 +99,7 @@ int main() { auto lin_r = std::make_shared(); lin_r->network = icsneo::Network::NetID::LIN2; lin_r->ID = 0x11; - lin_r->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; + lin_r->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; lin_r->data = {0xaa, 0xbb, 0xcc, 0xdd, 0x11, 0x22, 0x33, 0x44}; ret = device->transmit(lin_r); // This will return false if the device does not support LIN std::cout << (ret ? "OK" : "FAIL") << std::endl; @@ -108,7 +108,7 @@ int main() { auto lin_c = std::make_shared(); lin_c->network = icsneo::Network::NetID::LIN; lin_c->ID = 0x11; - lin_c->type = icsneo::LINMessage::Type::LIN_HEADER_ONLY; + lin_c->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY; ret = device->transmit(lin_c); std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl; @@ -117,7 +117,7 @@ int main() { lin_d->network = icsneo::Network::NetID::LIN; lin_d->ID = 0x22; lin_d->isEnhancedChecksum = true; - lin_d->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; + lin_d->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; lin_d->data = {0x11, 0x22, 0x33, 0x44, 0xaa, 0xbb, 0xcc, 0xdd}; ret = device->transmit(lin_d); std::cout << (ret ? "OK" : "FAIL") << std::endl << std::endl; diff --git a/include/icsneo/communication/message/linmessage.h b/include/icsneo/communication/message/linmessage.h index 3832955..683fc1e 100644 --- a/include/icsneo/communication/message/linmessage.h +++ b/include/icsneo/communication/message/linmessage.h @@ -36,10 +36,8 @@ struct LINStatusFlags { class LINMessage : public Frame { public: - static void calcChecksum(LINMessage& message); - - enum class Type { - NOT_SET, + enum class Type : uint8_t { + NOT_SET = 0, LIN_COMMANDER_MSG, LIN_HEADER_ONLY, LIN_BREAK_ONLY, @@ -48,12 +46,17 @@ public: LIN_ERROR }; + static void calcChecksum(LINMessage& message); + uint8_t calcProtectedID(uint8_t& id); + + LINMessage() {}; + LINMessage(uint8_t id) : ID(id & 0x3Fu), protectedID(calcProtectedID(ID)) {}; + uint8_t ID = 0; uint8_t protectedID = 0; uint8_t checksum = 0; - LINMessage::Type type = Type::NOT_SET; + LINMessage::Type linMsgType = Type::NOT_SET; bool isEnhancedChecksum = false; - bool isLINStd2x = true; LINErrorFlags errFlags; LINStatusFlags statusFlags; }; diff --git a/include/icsneo/communication/message/neomessage.h b/include/icsneo/communication/message/neomessage.h index 0de8ec1..9739e1a 100644 --- a/include/icsneo/communication/message/neomessage.h +++ b/include/icsneo/communication/message/neomessage.h @@ -86,14 +86,24 @@ typedef union { // ethernetFrameTooShort // ethernetFCSAvailable // ~~~ End of bitfield 2 ~~~ - //uint32_t linJustBreakSync : 1; - //uint32_t linSlaveDataTooShort : 1; - //uint32_t linOnlyUpdateSlaveTableOnce : 1; - uint32_t canfdESI : 1; - uint32_t canfdIDE : 1; - uint32_t canfdRTR : 1; - uint32_t canfdFDF : 1; - uint32_t canfdBRS : 1; + union { + uint32_t status3; + struct { + uint32_t canfdESI : 1; + uint32_t canfdIDE : 1; + uint32_t canfdRTR : 1; + uint32_t canfdFDF : 1; + uint32_t canfdBRS : 1; + uint32_t : 27; + }; + struct { + uint32_t linJustBreakSync : 1; + uint32_t linSlaveDataTooShort : 1; + uint32_t linOnlyUpdateSlaveTableOnce : 1; + uint32_t : 29; + }; + }; + }; uint32_t statusBitfield[4]; } neomessage_statusbitfield_t; @@ -171,6 +181,33 @@ typedef struct { uint8_t _reserved1[12]; } neomessage_eth_t; +typedef struct { + uint8_t txChecksumEnhanced : 1; + uint8_t txCommander : 1; + uint8_t txResponder : 1; + uint8_t txAborted : 1; + uint8_t updateResponderOnce : 1; + uint8_t hasUpdatedResponderOnce : 1; + uint8_t busRecovered : 1; + uint8_t breakOnly : 1; +} neomessage_linstatus_t; + +typedef struct { + neomessage_statusbitfield_t status; + uint64_t timestamp; + uint64_t _reservedTimestamp; + const uint8_t* data; + size_t length; + uint8_t header[4]; + neonetid_t netid; + neonettype_t type; + neomessage_linstatus_t linStatus; + uint16_t description; + neomessagetype_t messageType; + uint8_t checksum; + uint8_t _reserved1[11]; +} neomessage_lin_t; + #pragma pack(pop) #ifdef __cplusplus @@ -182,6 +219,7 @@ static_assert(sizeof(neomessage_frame_t) == sizeof(neomessage_t), "All types of static_assert(sizeof(neomessage_can_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (CAN is not)"); static_assert(sizeof(neomessage_can_error_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (CAN error is not)"); static_assert(sizeof(neomessage_eth_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (Ethernet is not)"); +static_assert(sizeof(neomessage_lin_t) == sizeof(neomessage_t), "All types of neomessage_t must be the same size! (LIN is not)"); namespace icsneo { diff --git a/test/linencoderdecodertest.cpp b/test/linencoderdecodertest.cpp index 5c44830..b5e407d 100644 --- a/test/linencoderdecodertest.cpp +++ b/test/linencoderdecodertest.cpp @@ -86,10 +86,9 @@ std::vector testControllerWithData = TEST_F(LINEncoderDecoderTest, ProtectedIDCalcTest) { std::vector bytestream; - auto message = std::make_shared(); + auto message = std::make_shared(static_cast(0x22u)); message->network = icsneo::Network::NetID::LIN; - message->ID = 0x22; - message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; + message->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; message->isEnhancedChecksum = false; packetEncoder->encode(*packetizer, bytestream, message); EXPECT_EQ(message->protectedID, 0xE2); @@ -97,10 +96,9 @@ TEST_F(LINEncoderDecoderTest, ProtectedIDCalcTest) { TEST_F(LINEncoderDecoderTest, ChecksumCalcTestClassic) { std::vector bytestream; - auto message = std::make_shared(); + auto message = std::make_shared(static_cast(0x22u)); message->network = icsneo::Network::NetID::LIN2; - message->ID = 0x22; - message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; + message->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; message->isEnhancedChecksum = false; message->data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; packetEncoder->encode(*packetizer, bytestream, message); @@ -109,10 +107,9 @@ TEST_F(LINEncoderDecoderTest, ChecksumCalcTestClassic) { TEST_F(LINEncoderDecoderTest, ChecksumCalcTestEnhanced) { std::vector bytestream; - auto message = std::make_shared(); + auto message = std::make_shared(static_cast(0x22u)); message->network = icsneo::Network::NetID::LIN2; - message->ID = 0x22; - message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; + message->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; message->isEnhancedChecksum = true; message->data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; packetEncoder->encode(*packetizer, bytestream, message); @@ -121,10 +118,9 @@ TEST_F(LINEncoderDecoderTest, ChecksumCalcTestEnhanced) { TEST_F(LINEncoderDecoderTest, PacketEncoderResponderLoadTest) { std::vector bytestream; - auto message = std::make_shared(); + auto message = std::make_shared(static_cast(0x22u)); message->network = icsneo::Network::NetID::LIN2; - message->ID = 0x22; - message->type = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; + message->linMsgType = icsneo::LINMessage::Type::LIN_UPDATE_RESPONDER; message->isEnhancedChecksum = false; message->data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; packetEncoder->encode(*packetizer, bytestream, message); @@ -133,10 +129,9 @@ TEST_F(LINEncoderDecoderTest, PacketEncoderResponderLoadTest) { TEST_F(LINEncoderDecoderTest, PacketEncoderControllerHeaderTest) { std::vector bytestream; - auto message = std::make_shared(); + auto message = std::make_shared(static_cast(0x22u)); message->network = icsneo::Network::NetID::LIN; - message->ID = 0x22; - message->type = icsneo::LINMessage::Type::LIN_HEADER_ONLY; + message->linMsgType = icsneo::LINMessage::Type::LIN_HEADER_ONLY; message->isEnhancedChecksum = false; packetEncoder->encode(*packetizer, bytestream, message); EXPECT_EQ(bytestream, testControllerHeaderOnly); @@ -144,10 +139,9 @@ TEST_F(LINEncoderDecoderTest, PacketEncoderControllerHeaderTest) { TEST_F(LINEncoderDecoderTest, PacketEncoderControllerWithDataTest) { std::vector bytestream; - auto message = std::make_shared(); + auto message = std::make_shared(static_cast(0x11u)); message->network = icsneo::Network::NetID::LIN; - message->ID = 0x11; - message->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; + message->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; message->isEnhancedChecksum = false; message->data = {0xaa, 0xbb, 0xcc}; packetEncoder->encode(*packetizer, bytestream, message); @@ -156,19 +150,17 @@ TEST_F(LINEncoderDecoderTest, PacketEncoderControllerWithDataTest) { TEST_F(LINEncoderDecoderTest, PacketDecoderTest) { std::shared_ptr decodeMsg; - auto msg1 = std::make_shared(); - auto msg2 = std::make_shared(); + auto msg1 = std::make_shared(static_cast(0x22u)); msg1->network = icsneo::Network::NetID::LIN2; - msg1->ID = 0x22; - msg1->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; + msg1->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; msg1->isEnhancedChecksum = false; msg1->data = {0xaa, 0xbb, 0xcc}; msg1->checksum = 0xcc; + auto msg2 = std::make_shared(static_cast(0x22u)); msg2->network = icsneo::Network::NetID::LIN; - msg2->ID = 0x22; - msg2->type = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; + msg2->linMsgType = icsneo::LINMessage::Type::LIN_COMMANDER_MSG; msg2->isEnhancedChecksum = false; msg2->data = {0xaa, 0xbb, 0xcc}; msg2->checksum = 0xcc;