mirror of
https://github.com/intrepidcs/libicsneo.git
synced 2026-09-20 16:08:37 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a47b6f179 | ||
|
|
ff1a78c4eb | ||
|
|
6bae630463 | ||
|
|
4530bd14c0 | ||
|
|
8833617fe5 | ||
|
|
3621184696 | ||
|
|
d49fba7d81 | ||
|
|
ba7228d4ba | ||
|
|
9175a7bacf | ||
|
|
56d191b438 | ||
|
|
a465d5d6c6 | ||
|
|
e081724da6 | ||
|
|
0dae7d4354 | ||
|
|
288ff3565e | ||
|
|
f3cd9ac94b | ||
|
|
424d3d98a3 |
@@ -6,3 +6,4 @@ Thumbs.db
|
||||
third-party/concurrentqueue/benchmarks
|
||||
third-party/concurrentqueue/tests
|
||||
*.bak
|
||||
.vs
|
||||
+16
-2
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.2)
|
||||
project(libicsneo VERSION 0.1.0)
|
||||
project(libicsneo VERSION 0.1.2)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
@@ -116,7 +116,19 @@ execute_process(
|
||||
ERROR_VARIABLE GIT_DESCRIBE
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
if(${BUILD_METADATA})
|
||||
set(BUILD_METADATA_PLUS +${BUILD_METADATA})
|
||||
endif()
|
||||
if(NOT ${GIT_BRANCH} STREQUAL "master")
|
||||
set(BUILD_GIT_INFO "${GIT_BRANCH} @ ")
|
||||
endif()
|
||||
string(SUBSTRING GIT_DESCRIBE 0 1 GIT_DESCRIBE_FIRST)
|
||||
if(NOT ${GIT_DESCRIBE_FIRST} STREQUAL "v")
|
||||
string(APPEND BUILD_GIT_INFO "${GIT_DESCRIBE}")
|
||||
endif()
|
||||
|
||||
configure_file(api/icsneocpp/buildinfo.h.template ${CMAKE_CURRENT_BINARY_DIR}/generated/buildinfo.h)
|
||||
configure_file(api/icsneoc/version.rc.template ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_library(icsneocpp
|
||||
@@ -154,7 +166,7 @@ if(WIN32)
|
||||
add_definitions(-DWPCAP -DHAVE_REMOTE -DWIN32_LEAN_AND_MEAN)
|
||||
endif(WIN32)
|
||||
|
||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp)
|
||||
add_library(icsneoc SHARED api/icsneoc/icsneoc.cpp ${CMAKE_CURRENT_BINARY_DIR}/generated/icsneoc/version.rc)
|
||||
target_include_directories(icsneoc
|
||||
PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
|
||||
@@ -199,6 +211,8 @@ if(NOT WIN32)
|
||||
set_property(TARGET ftdi1-static PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_link_libraries(icsneocpp PUBLIC ftdi1-static)
|
||||
target_link_libraries(icsneocpp PUBLIC ${CMAKE_THREAD_LIBS_INIT})
|
||||
find_package(PCAP REQUIRED)
|
||||
target_link_libraries(icsneocpp PUBLIC ${PCAP_LIBRARY})
|
||||
endif()
|
||||
|
||||
set(CPACK_PROJECT_NAME ${PROJECT_NAME})
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
#define VER_FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@
|
||||
#define VER_FILEVERSION_STR "v@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@.@PROJECT_VERSION_PATCH@@BUILD_METADATA_PLUS@ @BUILD_GIT_INFO@"
|
||||
|
||||
#define VER_PRODUCTVERSION VER_FILEVERSION
|
||||
#define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR
|
||||
|
||||
#ifndef DEBUG
|
||||
#define VER_DEBUG 0
|
||||
#else
|
||||
#define VER_DEBUG VS_FF_DEBUG
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION VER_FILEVERSION
|
||||
PRODUCTVERSION VER_PRODUCTVERSION
|
||||
FILEFLAGSMASK (VS_FF_DEBUG)
|
||||
FILEFLAGS (VER_DEBUG)
|
||||
FILEOS VOS__WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904E4"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Intrepid Control Systems, Inc."
|
||||
VALUE "FileDescription", "Intrepid Control Systems Open Device Communication C API"
|
||||
VALUE "FileVersion", VER_FILEVERSION_STR
|
||||
VALUE "InternalName", "icsneoc.dll"
|
||||
VALUE "LegalCopyright", "Intrepid Control Systems, Inc. (C) 2018-2019"
|
||||
VALUE "OriginalFilename", "icsneoc.dll"
|
||||
VALUE "ProductName", "libicsneo"
|
||||
VALUE "ProductVersion", VER_PRODUCTVERSION_STR
|
||||
END
|
||||
END
|
||||
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
/* The following line should only be modified for localized versions. */
|
||||
/* It consists of any number of WORD,WORD pairs, with each pair */
|
||||
/* describing a language,codepage combination supported by the file. */
|
||||
/* */
|
||||
/* For example, a file might have values "0x409,1252" indicating that it */
|
||||
/* supports English language (0x409) in the Windows ANSI codepage (1252). */
|
||||
|
||||
VALUE "Translation", 0x409, 1252
|
||||
|
||||
END
|
||||
END
|
||||
@@ -0,0 +1,74 @@
|
||||
# - Try to find libpcap include dirs and libraries
|
||||
#
|
||||
# Usage of this module as follows:
|
||||
#
|
||||
# find_package(PCAP)
|
||||
#
|
||||
# Variables used by this module, they can change the default behaviour and need
|
||||
# to be set before calling find_package:
|
||||
#
|
||||
# PCAP_ROOT_DIR Set this variable to the root installation of
|
||||
# libpcap if the module has problems finding the
|
||||
# proper installation path.
|
||||
#
|
||||
# Variables defined by this module:
|
||||
#
|
||||
# PCAP_FOUND System has libpcap, include and library dirs found
|
||||
# PCAP_INCLUDE_DIR The libpcap include directories.
|
||||
# PCAP_LIBRARY The libpcap library (possibly includes a thread
|
||||
# library e.g. required by pf_ring's libpcap)
|
||||
# HAVE_PF_RING If a found version of libpcap supports PF_RING
|
||||
|
||||
find_path(PCAP_ROOT_DIR
|
||||
NAMES include/pcap.h
|
||||
)
|
||||
|
||||
find_path(PCAP_INCLUDE_DIR
|
||||
NAMES pcap.h
|
||||
HINTS ${PCAP_ROOT_DIR}/include
|
||||
)
|
||||
|
||||
find_library(PCAP_LIBRARY
|
||||
NAMES pcap
|
||||
HINTS ${PCAP_ROOT_DIR}/lib
|
||||
)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(PCAP DEFAULT_MSG
|
||||
PCAP_LIBRARY
|
||||
PCAP_INCLUDE_DIR
|
||||
)
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
|
||||
check_c_source_compiles("int main() { return 0; }" PCAP_LINKS_SOLO)
|
||||
set(CMAKE_REQUIRED_LIBRARIES)
|
||||
|
||||
# check if linking against libpcap also needs to link against a thread library
|
||||
if (NOT PCAP_LINKS_SOLO)
|
||||
find_package(Threads)
|
||||
if (THREADS_FOUND)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||
check_c_source_compiles("int main() { return 0; }" PCAP_NEEDS_THREADS)
|
||||
set(CMAKE_REQUIRED_LIBRARIES)
|
||||
endif ()
|
||||
if (THREADS_FOUND AND PCAP_NEEDS_THREADS)
|
||||
set(_tmp ${PCAP_LIBRARY} ${CMAKE_THREAD_LIBS_INIT})
|
||||
list(REMOVE_DUPLICATES _tmp)
|
||||
set(PCAP_LIBRARY ${_tmp}
|
||||
CACHE STRING "Libraries needed to link against libpcap" FORCE)
|
||||
else ()
|
||||
message(FATAL_ERROR "Couldn't determine how to link against libpcap")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
include(CheckFunctionExists)
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${PCAP_LIBRARY})
|
||||
check_function_exists(pcap_get_pfring_id HAVE_PF_RING)
|
||||
set(CMAKE_REQUIRED_LIBRARIES)
|
||||
|
||||
mark_as_advanced(
|
||||
PCAP_ROOT_DIR
|
||||
PCAP_INCLUDE_DIR
|
||||
PCAP_LIBRARY
|
||||
)
|
||||
+9
-2
@@ -151,13 +151,15 @@ bool Device::open() {
|
||||
break;
|
||||
}
|
||||
if(!serial) {
|
||||
err(APIError::NoSerialNumber);
|
||||
err(APIError::NoSerialNumber); // Communication could not be established with the device. Perhaps it is not powered with 12 volts?
|
||||
com->close();
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string currentSerial = getNeoDevice().serial;
|
||||
if(currentSerial != serial->deviceSerial) {
|
||||
err(APIError::IncorrectSerialNumber);
|
||||
com->close();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -265,5 +267,10 @@ void Device::updateLEDState() {
|
||||
auto msg = std::make_shared<Message>();
|
||||
msg->network = Network::NetID::Device;
|
||||
msg->data = {0x00, 0x06, uint8_t(ledState)};
|
||||
transmit(msg);
|
||||
|
||||
std::vector<uint8_t> packet;
|
||||
if(!com->encoder->encode(packet, msg))
|
||||
return;
|
||||
|
||||
com->sendPacket(packet);
|
||||
}
|
||||
@@ -38,6 +38,10 @@ static std::vector<DeviceType> supportedDevices = {
|
||||
RADGalaxy::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADPLUTOUSB_H_
|
||||
RADPlutoUSB::DEVICE_TYPE,
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2ETH_H_
|
||||
RADStar2ETH::DEVICE_TYPE,
|
||||
#endif
|
||||
@@ -112,6 +116,10 @@ std::vector<std::shared_ptr<Device>> DeviceFinder::FindAll() {
|
||||
findResults.push_back(RADGalaxy::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __RADPLUTOUSB_H_
|
||||
findResults.push_back(RADPlutoUSB::Find());
|
||||
#endif
|
||||
|
||||
#ifdef __RADSTAR2ETH_H_
|
||||
findResults.push_back(RADStar2ETH::Find());
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef __RADPLUTO_H_
|
||||
#define __RADPLUTO_H_
|
||||
|
||||
#include "icsneo/device/device.h"
|
||||
#include "icsneo/device/devicetype.h"
|
||||
#include "icsneo/platform/pcap.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include "icsneo/communication/decoder.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class RADPluto : public Device {
|
||||
public:
|
||||
// Serial numbers start with PL
|
||||
static constexpr DeviceType::Enum DEVICE_TYPE = DeviceType::RADPluto;
|
||||
static constexpr const uint16_t PRODUCT_ID = 0x1104;
|
||||
static constexpr const char* SERIAL_START = "PL";
|
||||
|
||||
static const std::vector<Network>& GetSupportedNetworks() {
|
||||
static std::vector<Network> supportedNetworks = {
|
||||
Network::NetID::HSCAN,
|
||||
Network::NetID::HSCAN2,
|
||||
|
||||
Network::NetID::LIN,
|
||||
|
||||
Network::NetID::Ethernet,
|
||||
|
||||
Network::NetID::OP_Ethernet1,
|
||||
Network::NetID::OP_Ethernet2,
|
||||
Network::NetID::OP_Ethernet3,
|
||||
Network::NetID::OP_Ethernet4
|
||||
};
|
||||
return supportedNetworks;
|
||||
}
|
||||
|
||||
RADPluto(neodevice_t neodevice) : Device(neodevice) {
|
||||
getWritableNeoDevice().type = DEVICE_TYPE;
|
||||
productId = PRODUCT_ID;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void setupEncoder(Encoder& encoder) override {
|
||||
Device::setupEncoder(encoder);
|
||||
encoder.supportCANFD = true;
|
||||
}
|
||||
|
||||
virtual void setupDecoder(Decoder& decoder) override {
|
||||
Device::setupDecoder(decoder);
|
||||
decoder.timestampResolution = 10; // Timestamps are in 10ns increments instead of the usual 25ns
|
||||
}
|
||||
|
||||
virtual void setupSupportedRXNetworks(std::vector<Network>& rxNetworks) override {
|
||||
for(auto& netid : GetSupportedNetworks())
|
||||
rxNetworks.emplace_back(netid);
|
||||
}
|
||||
|
||||
// The supported TX networks are the same as the supported RX networks for this device
|
||||
virtual void setupSupportedTXNetworks(std::vector<Network>& txNetworks) override { setupSupportedRXNetworks(txNetworks); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
#ifndef __RADPLUTOUSB_H_
|
||||
#define __RADPLUTOUSB_H_
|
||||
|
||||
#include "icsneo/device/radpluto/radpluto.h"
|
||||
#include "icsneo/platform/stm32.h"
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class RADPlutoUSB : public RADPluto {
|
||||
public:
|
||||
// Serial numbers start with RP
|
||||
static std::vector<std::shared_ptr<Device>> Find() {
|
||||
std::vector<std::shared_ptr<Device>> found;
|
||||
|
||||
for(auto neodevice : STM32::FindByProduct(PRODUCT_ID))
|
||||
found.emplace_back(new RADPlutoUSB(neodevice));
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private:
|
||||
RADPlutoUSB(neodevice_t neodevice) : RADPluto(neodevice) {
|
||||
initialize<STM32>();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#if defined _WIN32
|
||||
#include "icsneo/platform/windows/pcap.h"
|
||||
// #elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||
// #include "icsneo/platform/posix/ftdi.h"
|
||||
#elif defined (__unix__) || (defined (__APPLE__) && defined (__MACH__))
|
||||
#include "icsneo/platform/posix/pcap.h"
|
||||
#else
|
||||
#warning "This platform is not supported by the PCAP driver"
|
||||
#endif
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
#include "icsneo/device/neoobd2pro/neoobd2pro.h"
|
||||
#include "icsneo/device/neoobd2sim/neoobd2sim.h"
|
||||
#include "icsneo/device/neovifire/neovifire.h"
|
||||
//#include "icsneo/device/neovifire2/neovifire2eth.h" Ethernet not yet supported
|
||||
//#include "icsneo/device/neovifire2/neovifire2eth.h"
|
||||
#include "icsneo/device/neovifire2/neovifire2usb.h"
|
||||
#include "icsneo/device/plasion/neoviion.h"
|
||||
#include "icsneo/device/plasion/neoviplasma.h"
|
||||
//#include "icsneo/device/radgalaxy/radgalaxy.h" Ethernet not yet supported
|
||||
//#include "icsneo/device/radstar2/radstar2eth.h" Ethernet not yet supported
|
||||
//#include "icsneo/device/radgalaxy/radgalaxy.h"
|
||||
#include "icsneo/device/radpluto/radplutousb.h"
|
||||
//#include "icsneo/device/radstar2/radstar2eth.h"
|
||||
#include "icsneo/device/radstar2/radstar2usb.h"
|
||||
#include "icsneo/device/radsupermoon/radsupermoon.h"
|
||||
#include "icsneo/device/valuecan3/valuecan3.h"
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
#ifndef __PCAP_POSIX_H_
|
||||
#define __PCAP_POSIX_H_
|
||||
|
||||
#include "icsneo/device/neodevice.h"
|
||||
#include "icsneo/communication/icommunication.h"
|
||||
#include "icsneo/api/errormanager.h"
|
||||
#include <string>
|
||||
#include <pcap.h>
|
||||
|
||||
namespace icsneo {
|
||||
|
||||
class PCAP : public ICommunication {
|
||||
public:
|
||||
class PCAPFoundDevice {
|
||||
public:
|
||||
neodevice_t device;
|
||||
std::vector<std::vector<uint8_t>> discoveryPackets;
|
||||
};
|
||||
|
||||
static std::vector<PCAPFoundDevice> FindAll();
|
||||
static std::string GetEthDevSerialFromMacAddress(uint8_t product, uint16_t macSerial);
|
||||
static bool IsHandleValid(neodevice_handle_t handle);
|
||||
|
||||
PCAP(device_errorhandler_t err, neodevice_t& forDevice);
|
||||
bool open();
|
||||
bool isOpen();
|
||||
bool close();
|
||||
private:
|
||||
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
|
||||
neodevice_t& device;
|
||||
uint8_t deviceMAC[6];
|
||||
bool openable = true;
|
||||
void readTask();
|
||||
void writeTask();
|
||||
|
||||
class NetworkInterface {
|
||||
public:
|
||||
uint8_t uuid;
|
||||
uint8_t macAddress[8];
|
||||
std::string nameFromWinPCAP;
|
||||
std::string descriptionFromWinPCAP;
|
||||
std::string fullName;
|
||||
pcap_t* fp = nullptr;
|
||||
pcap_stat stats;
|
||||
};
|
||||
static std::vector<NetworkInterface> knownInterfaces;
|
||||
NetworkInterface interface;
|
||||
|
||||
class EthernetPacket {
|
||||
public: // Don't worry about endian when setting fields, this is all taken care of in getBytestream
|
||||
EthernetPacket() {};
|
||||
EthernetPacket(const std::vector<uint8_t>& bytestream);
|
||||
EthernetPacket(const uint8_t* data, size_t size);
|
||||
int loadBytestream(const std::vector<uint8_t>& bytestream);
|
||||
std::vector<uint8_t> getBytestream() const;
|
||||
uint8_t errorWhileDecodingFromBytestream = 0; // Not part of final bytestream, only for checking the result of the constructor
|
||||
uint8_t destMAC[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
uint8_t srcMAC[6] = { 0x00, 0xFC, 0x70, 0xFF, 0xFF, 0xFF };
|
||||
uint16_t etherType = 0xCAB1; // Big endian, Should be 0xCAB1 or 0xCAB2
|
||||
uint32_t icsEthernetHeader = 0xAAAA5555; // Big endian, Should be 0xAAAA5555
|
||||
// At this point in the packet, there is a 16-bit payload size, little endian
|
||||
// This is calculated from payload size in getBytestream
|
||||
uint16_t packetNumber = 0;
|
||||
bool firstPiece = true; // These booleans make up a 16-bit bitfield, packetInfo
|
||||
bool lastPiece = true;
|
||||
bool bufferHalfFull = false;
|
||||
std::vector<uint8_t> payload;
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "icsneo/device/plasion/neoviion.h"
|
||||
#include "icsneo/device/plasion/neoviplasma.h"
|
||||
#include "icsneo/device/radgalaxy/radgalaxy.h"
|
||||
#include "icsneo/device/radpluto/radplutousb.h"
|
||||
#include "icsneo/device/radstar2/radstar2eth.h"
|
||||
#include "icsneo/device/radstar2/radstar2usb.h"
|
||||
#include "icsneo/device/radsupermoon/radsupermoon.h"
|
||||
|
||||
@@ -30,9 +30,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable:4127) // ICS: Warnings generated by this file
|
||||
#pragma warning(disable:4706)
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
// Disable -Wconversion warnings (spuriously triggered when Traits::size_t and
|
||||
@@ -3638,4 +3640,6 @@ inline void swap(typename ConcurrentQueue<T, Traits>::ImplicitProducerKVP& a, ty
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
#pragma warning(pop)
|
||||
#if defined(_MSC_VAR)
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
@@ -0,0 +1,347 @@
|
||||
#include "icsneo/platform/posix/pcap.h"
|
||||
#include "icsneo/communication/network.h"
|
||||
#include "icsneo/communication/communication.h"
|
||||
#include "icsneo/communication/packetizer.h"
|
||||
#include <codecvt>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netpacket/packet.h>
|
||||
|
||||
using namespace icsneo;
|
||||
|
||||
static const uint8_t BROADCAST_MAC[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
|
||||
std::vector<PCAP::NetworkInterface> PCAP::knownInterfaces;
|
||||
|
||||
std::vector<PCAP::PCAPFoundDevice> PCAP::FindAll() {
|
||||
std::vector<PCAPFoundDevice> foundDevices;
|
||||
|
||||
// First we ask WinPCAP to give us all of the devices
|
||||
pcap_if_t* alldevs;
|
||||
char errbuf[PCAP_ERRBUF_SIZE] = { 0 };
|
||||
bool success = false;
|
||||
// Calling pcap_findalldevs too quickly can cause various errors. Retry a few times in this case.
|
||||
for(auto retry = 0; retry < 10; retry++) {
|
||||
auto ret = pcap_findalldevs(&alldevs, errbuf);
|
||||
if(ret == 0) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!success) {
|
||||
ErrorManager::GetInstance().add(APIError::PCAPCouldNotFindDevices);
|
||||
return std::vector<PCAPFoundDevice>();
|
||||
}
|
||||
|
||||
std::vector<NetworkInterface> interfaces;
|
||||
for(pcap_if_t* dev = alldevs; dev != nullptr; dev = dev->next) {
|
||||
if(dev->name == nullptr)
|
||||
continue;
|
||||
if(dev->addresses == nullptr) {
|
||||
//std::cout << dev->name << " has no addresses" << std::endl;
|
||||
continue;
|
||||
}
|
||||
NetworkInterface netif;
|
||||
netif.nameFromWinPCAP = dev->name;
|
||||
if(dev->description)
|
||||
netif.descriptionFromWinPCAP = dev->description;
|
||||
pcap_addr* currentAddress = dev->addresses;
|
||||
bool hasAddress = false;
|
||||
while(!hasAddress && currentAddress != nullptr) {
|
||||
if(currentAddress->addr && currentAddress->addr->sa_family == AF_PACKET) {
|
||||
struct sockaddr_ll* s = (struct sockaddr_ll*)currentAddress->addr;
|
||||
memcpy(netif.macAddress, s->sll_addr, sizeof(netif.macAddress));
|
||||
hasAddress = true;
|
||||
break;
|
||||
}
|
||||
currentAddress = currentAddress->next;
|
||||
}
|
||||
|
||||
if(!hasAddress)
|
||||
continue;
|
||||
|
||||
interfaces.push_back(netif);
|
||||
}
|
||||
|
||||
pcap_freealldevs(alldevs);
|
||||
|
||||
for(auto& interface : interfaces) {
|
||||
bool exists = false;
|
||||
for(auto& known : knownInterfaces)
|
||||
if(memcmp(interface.macAddress, known.macAddress, sizeof(interface.macAddress)) == 0)
|
||||
exists = true;
|
||||
if(!exists)
|
||||
knownInterfaces.emplace_back(interface);
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < knownInterfaces.size(); i++) {
|
||||
auto& interface = knownInterfaces[i];
|
||||
// if(interface.fullName.length() == 0)
|
||||
// continue; // Win32 did not find this interface in the previous step
|
||||
|
||||
errbuf[0] = '\0';
|
||||
interface.fp = pcap_open_live(interface.nameFromWinPCAP.c_str(), UINT16_MAX, 1, 0, errbuf);
|
||||
if(strlen(errbuf) != 0) { // This means a warning
|
||||
std::cout << "Warning for " << interface.nameFromWinPCAP << " " << errbuf << std::endl;
|
||||
}
|
||||
|
||||
if(interface.fp == nullptr) {
|
||||
std::cout << "pcap_open_live failed for " << interface.nameFromWinPCAP << " with " << errbuf << std::endl;
|
||||
continue; // Could not open the interface
|
||||
}
|
||||
|
||||
pcap_setnonblock(interface.fp, 1, errbuf);
|
||||
|
||||
EthernetPacket requestPacket;
|
||||
requestPacket.payload.reserve(4);
|
||||
requestPacket.payload = {
|
||||
((1 << 4) | (uint8_t)Network::NetID::Main51), // Packet size of 1 on NETID_MAIN51
|
||||
(uint8_t)Command::RequestSerialNumber
|
||||
};
|
||||
requestPacket.payload.push_back(Packetizer::ICSChecksum(requestPacket.payload));
|
||||
requestPacket.payload.insert(requestPacket.payload.begin(), 0xAA);
|
||||
|
||||
auto bs = requestPacket.getBytestream();
|
||||
pcap_sendpacket(interface.fp, bs.data(), (int)bs.size());
|
||||
|
||||
auto timeout = std::chrono::high_resolution_clock::now() + std::chrono::milliseconds(50);
|
||||
while(std::chrono::high_resolution_clock::now() <= timeout) { // Wait up to 5ms for the response
|
||||
struct pcap_pkthdr* header;
|
||||
const uint8_t* data;
|
||||
auto res = pcap_next_ex(interface.fp, &header, &data);
|
||||
if(res < 0) {
|
||||
std::cout << "pcapnextex failed with " << res << std::endl;
|
||||
break;
|
||||
}
|
||||
if(res == 0)
|
||||
continue; // Keep waiting for that packet
|
||||
|
||||
EthernetPacket packet(data, header->caplen);
|
||||
// Is this an ICS response packet (0xCAB2) from an ICS MAC, either to broadcast or directly to us?
|
||||
if(packet.etherType == 0xCAB2 && packet.srcMAC[0] == 0x00 && packet.srcMAC[1] == 0xFC && packet.srcMAC[2] == 0x70 && (
|
||||
memcmp(packet.destMAC, interface.macAddress, sizeof(packet.destMAC)) == 0 ||
|
||||
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) == 0
|
||||
)) {
|
||||
/* We have received a packet from a device. We don't know if this is the device we're
|
||||
* looking for, we don't know if it's actually a response to our RequestSerialNumber
|
||||
* or not, we just know we got something.
|
||||
*
|
||||
* Unlike most transport layers, we can't get the serial number here as we actually
|
||||
* need to parse this message that has been returned. Some devices parse messages
|
||||
* differently, so we need to use their communication layer. We could technically
|
||||
* create a communication layer to parse the packet we have in `payload` here, but
|
||||
* we'd need to be given a packetizer and decoder for the device. I'm intentionally
|
||||
* avoiding passing that information down here for code quality's sake. Instead, pass
|
||||
* the packet we received back up so the device can handle it.
|
||||
*/
|
||||
neodevice_handle_t handle = (neodevice_handle_t)((i << 24) | (packet.srcMAC[3] << 16) | (packet.srcMAC[4] << 8) | (packet.srcMAC[5]));
|
||||
PCAPFoundDevice* alreadyExists = nullptr;
|
||||
for(auto& dev : foundDevices)
|
||||
if(dev.device.handle == handle)
|
||||
alreadyExists = &dev;
|
||||
|
||||
if(alreadyExists == nullptr) {
|
||||
PCAPFoundDevice foundDevice;
|
||||
foundDevice.device.handle = handle;
|
||||
foundDevice.discoveryPackets.push_back(std::move(packet.payload));
|
||||
foundDevices.push_back(foundDevice);
|
||||
} else {
|
||||
alreadyExists->discoveryPackets.push_back(std::move(packet.payload));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pcap_close(interface.fp);
|
||||
interface.fp = nullptr;
|
||||
}
|
||||
|
||||
return foundDevices;
|
||||
}
|
||||
|
||||
bool PCAP::IsHandleValid(neodevice_handle_t handle) {
|
||||
uint8_t netifIndex = (uint8_t)(handle >> 24);
|
||||
return (netifIndex < knownInterfaces.size());
|
||||
}
|
||||
|
||||
PCAP::PCAP(device_errorhandler_t err, neodevice_t& forDevice) : ICommunication(err), device(forDevice) {
|
||||
if(IsHandleValid(device.handle)) {
|
||||
interface = knownInterfaces[(device.handle >> 24) & 0xFF];
|
||||
interface.fp = nullptr; // We're going to open our own connection to the interface. This should already be nullptr but just in case.
|
||||
|
||||
deviceMAC[0] = 0x00;
|
||||
deviceMAC[1] = 0xFC;
|
||||
deviceMAC[2] = 0x70;
|
||||
deviceMAC[3] = (device.handle >> 16) & 0xFF;
|
||||
deviceMAC[4] = (device.handle >> 8) & 0xFF;
|
||||
deviceMAC[5] = device.handle & 0xFF;
|
||||
} else {
|
||||
openable = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool PCAP::open() {
|
||||
if(!openable)
|
||||
return false;
|
||||
|
||||
if(isOpen())
|
||||
return false;
|
||||
|
||||
// Open the interface
|
||||
interface.fp = pcap_open_live(interface.nameFromWinPCAP.c_str(), INT16_MAX, 1, 0, errbuf);
|
||||
if(interface.fp == nullptr) {
|
||||
err(APIError::DriverFailedToOpen);
|
||||
return false;
|
||||
}
|
||||
|
||||
pcap_setnonblock(interface.fp, 1, errbuf);
|
||||
|
||||
// Create threads
|
||||
readThread = std::thread(&PCAP::readTask, this);
|
||||
writeThread = std::thread(&PCAP::writeTask, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PCAP::isOpen() {
|
||||
return interface.fp != nullptr;
|
||||
}
|
||||
|
||||
bool PCAP::close() {
|
||||
if(!isOpen())
|
||||
return false;
|
||||
|
||||
closing = true; // Signal the threads that we are closing
|
||||
readThread.join();
|
||||
writeThread.join();
|
||||
closing = false;
|
||||
|
||||
pcap_close(interface.fp);
|
||||
interface.fp = nullptr;
|
||||
|
||||
uint8_t flush;
|
||||
WriteOperation flushop;
|
||||
while(readQueue.try_dequeue(flush)) {}
|
||||
while(writeQueue.try_dequeue(flushop)) {}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PCAP::readTask() {
|
||||
struct pcap_pkthdr* header;
|
||||
const uint8_t* data;
|
||||
while(!closing) {
|
||||
auto readBytes = pcap_next_ex(interface.fp, &header, &data);
|
||||
if(readBytes < 0) {
|
||||
err(APIError::FailedToRead);
|
||||
break;
|
||||
}
|
||||
if(readBytes == 0)
|
||||
continue; // Keep waiting for that packet
|
||||
|
||||
EthernetPacket packet(data, header->caplen);
|
||||
|
||||
if(packet.etherType != 0xCAB2)
|
||||
continue; // Not a packet to host
|
||||
|
||||
if(memcmp(packet.destMAC, interface.macAddress, sizeof(packet.destMAC)) != 0 &&
|
||||
memcmp(packet.destMAC, BROADCAST_MAC, sizeof(packet.destMAC)) != 0)
|
||||
continue; // Packet is not addressed to us or broadcast
|
||||
|
||||
if(memcmp(packet.srcMAC, deviceMAC, sizeof(deviceMAC)) != 0)
|
||||
continue; // Not a packet from the device we're concerned with
|
||||
|
||||
readQueue.enqueue_bulk(packet.payload.data(), packet.payload.size());
|
||||
}
|
||||
}
|
||||
|
||||
void PCAP::writeTask() {
|
||||
WriteOperation writeOp;
|
||||
uint16_t sequence = 0;
|
||||
EthernetPacket sendPacket;
|
||||
|
||||
// Set MAC address of packet
|
||||
memcpy(sendPacket.srcMAC, interface.macAddress, sizeof(sendPacket.srcMAC));
|
||||
memcpy(sendPacket.destMAC, deviceMAC, sizeof(deviceMAC));
|
||||
|
||||
while(!closing) {
|
||||
if(!writeQueue.wait_dequeue_timed(writeOp, std::chrono::milliseconds(100)))
|
||||
continue;
|
||||
|
||||
sendPacket.packetNumber = sequence++;
|
||||
sendPacket.payload = std::move(writeOp.bytes);
|
||||
auto bs = sendPacket.getBytestream();
|
||||
if(!closing)
|
||||
pcap_sendpacket(interface.fp, bs.data(), (int)bs.size());
|
||||
// TODO Handle packet send errors
|
||||
}
|
||||
}
|
||||
|
||||
PCAP::EthernetPacket::EthernetPacket(const std::vector<uint8_t>& bytestream) {
|
||||
loadBytestream(bytestream);
|
||||
}
|
||||
|
||||
PCAP::EthernetPacket::EthernetPacket(const uint8_t* data, size_t size) {
|
||||
std::vector<uint8_t> bs(size);
|
||||
for(size_t i = 0; i < size; i++)
|
||||
bs[i] = data[i];
|
||||
loadBytestream(bs);
|
||||
}
|
||||
|
||||
int PCAP::EthernetPacket::loadBytestream(const std::vector<uint8_t>& bytestream) {
|
||||
errorWhileDecodingFromBytestream = 0;
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
destMAC[i] = bytestream[i];
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
srcMAC[i] = bytestream[i + 6];
|
||||
etherType = (bytestream[12] << 8) | bytestream[13];
|
||||
icsEthernetHeader = (bytestream[14] << 24) | (bytestream[15] << 16) | (bytestream[16] << 8) | bytestream[17];
|
||||
uint16_t payloadSize = bytestream[18] | (bytestream[19] << 8);
|
||||
packetNumber = bytestream[20] | (bytestream[21] << 8);
|
||||
uint16_t packetInfo = bytestream[22] | (bytestream[23] << 8);
|
||||
firstPiece = packetInfo & 1;
|
||||
lastPiece = (packetInfo >> 1) & 1;
|
||||
bufferHalfFull = (packetInfo >> 2) & 2;
|
||||
payload = std::vector<uint8_t>(bytestream.begin() + 24, bytestream.end());
|
||||
size_t payloadActualSize = payload.size();
|
||||
if(payloadActualSize < payloadSize)
|
||||
errorWhileDecodingFromBytestream = 1;
|
||||
payload.resize(payloadSize);
|
||||
return errorWhileDecodingFromBytestream;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> PCAP::EthernetPacket::getBytestream() const {
|
||||
size_t payloadSize = payload.size();
|
||||
std::vector<uint8_t> bytestream;
|
||||
bytestream.reserve(6 + 6 + 2 + 4 + 2 + 2 + 2 + payloadSize);
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
bytestream.push_back(destMAC[i]);
|
||||
for(size_t i = 0; i < 6; i++)
|
||||
bytestream.push_back(srcMAC[i]);
|
||||
// EtherType should be put into the bytestream as big endian
|
||||
bytestream.push_back((uint8_t)(etherType >> 8));
|
||||
bytestream.push_back((uint8_t)(etherType));
|
||||
// Our Ethernet header should be put into the bytestream as big endian
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader >> 24));
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader >> 16));
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader >> 8));
|
||||
bytestream.push_back((uint8_t)(icsEthernetHeader));
|
||||
// The payload size comes next, it's little endian
|
||||
bytestream.push_back((uint8_t)(payloadSize));
|
||||
bytestream.push_back((uint8_t)(payloadSize >> 8));
|
||||
// Packet number is little endian
|
||||
bytestream.push_back((uint8_t)(packetNumber));
|
||||
bytestream.push_back((uint8_t)(packetNumber >> 8));
|
||||
// Packet info gets assembled into a bitfield
|
||||
uint16_t packetInfo = 0;
|
||||
packetInfo |= firstPiece & 1;
|
||||
packetInfo |= (lastPiece & 1) << 1;
|
||||
packetInfo |= (bufferHalfFull & 1) << 2;
|
||||
bytestream.push_back((uint8_t)(packetInfo));
|
||||
bytestream.push_back((uint8_t)(packetInfo >> 8));
|
||||
bytestream.insert(bytestream.end(), payload.begin(), payload.end());
|
||||
return bytestream;
|
||||
}
|
||||
@@ -200,9 +200,10 @@ bool STM32::open() {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct termios tty;
|
||||
struct termios tty = {};
|
||||
struct termios compare = {};
|
||||
|
||||
if(tcgetattr(fd, &tty) < 0) {
|
||||
if(tcgetattr(fd, &tty) != 0) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
@@ -229,6 +230,11 @@ bool STM32::open() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(tcgetattr(fd, &compare) != 0 || memcmp(&tty, &compare, sizeof(struct termios)) != 0) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create threads
|
||||
readThread = std::thread(&STM32::readTask, this);
|
||||
writeThread = std::thread(&STM32::writeTask, this);
|
||||
|
||||
Reference in New Issue
Block a user