From 7d821b9745affd6eb4b06d1ee27e534dd06ea49a Mon Sep 17 00:00:00 2001 From: Paul Hollinsky Date: Mon, 8 Oct 2018 16:32:51 -0400 Subject: [PATCH] Revamp the way that ethernet devices are found Also add RADStar2 Ethernet support --- device/device.cpp | 7 +- device/devicefinder.cpp | 8 +- device/include/device.h | 2 - device/neovifire2/include/neovifire2.h | 1 + device/neovifire2/include/neovifire2eth.h | 36 +++++---- device/plasion/include/plasion.h | 7 +- device/radgalaxy/include/radgalaxy.h | 47 ++++++++---- device/radstar2/include/radstar2.h | 15 +--- device/radstar2/include/radstar2eth.h | 62 +++++++++++++++ device/radstar2/include/radstar2usb.h | 32 ++++++++ platform/windows/include/devices.h | 7 +- platform/windows/include/pcap.h | 8 +- platform/windows/pcap.cpp | 91 +++++++++-------------- 13 files changed, 207 insertions(+), 116 deletions(-) create mode 100644 device/radstar2/include/radstar2eth.h create mode 100644 device/radstar2/include/radstar2usb.h diff --git a/device/device.cpp b/device/device.cpp index e767302..4d643af 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -139,11 +139,8 @@ bool Device::open() { } std::string currentSerial = getNeoDevice().serial; - if(currentSerial == SERIAL_FIND_ON_OPEN) { - strncpy(getWritableNeoDevice().serial, serial->deviceSerial.c_str(), sizeof(getNeoDevice().serial)); - getWritableNeoDevice().serial[sizeof(getWritableNeoDevice().serial) - 1] = '\0'; - } else if(currentSerial != serial->deviceSerial) { - std::cout << "NeoDevice has serial " << getNeoDevice().serial << " but device has serial " << serial->deviceSerial.c_str() << "!" << std::endl; + if(currentSerial != serial->deviceSerial) { + std::cout << "Found device had serial " << getNeoDevice().serial << " but connected device has serial " << serial->deviceSerial.c_str() << "!" << std::endl; return false; } diff --git a/device/devicefinder.cpp b/device/devicefinder.cpp index 2acfe07..e6815dc 100644 --- a/device/devicefinder.cpp +++ b/device/devicefinder.cpp @@ -39,8 +39,12 @@ std::vector> DeviceFinder::FindAll() { findResults.push_back(RADGalaxy::Find()); #endif - #ifdef __RADSTAR2_H_ - findResults.push_back(RADStar2::Find()); + #ifdef __RADSTAR2ETH_H_ + findResults.push_back(RADStar2ETH::Find()); + #endif + + #ifdef __RADSTAR2USB_H_ + findResults.push_back(RADStar2USB::Find()); #endif #ifdef __RADSUPERMOON_H_ diff --git a/device/include/device.h b/device/include/device.h index 75f9e6b..c1184bf 100644 --- a/device/include/device.h +++ b/device/include/device.h @@ -17,8 +17,6 @@ namespace icsneo { class Device { public: - static constexpr const char* SERIAL_FIND_ON_OPEN = "xxxxxx"; - Device(neodevice_t neodevice = { 0 }) { data = neodevice; data.device = this; diff --git a/device/neovifire2/include/neovifire2.h b/device/neovifire2/include/neovifire2.h index 8a45219..26ee04a 100644 --- a/device/neovifire2/include/neovifire2.h +++ b/device/neovifire2/include/neovifire2.h @@ -10,6 +10,7 @@ namespace icsneo { class NeoVIFIRE2 : public Device { public: static constexpr DeviceType::Enum DEVICE_TYPE = DeviceType::FIRE2; + static constexpr const char* SERIAL_START = "CY"; NeoVIFIRE2(neodevice_t neodevice) : Device(neodevice) { getWritableNeoDevice().type = DEVICE_TYPE; } diff --git a/device/neovifire2/include/neovifire2eth.h b/device/neovifire2/include/neovifire2eth.h index f406ac4..e755da4 100644 --- a/device/neovifire2/include/neovifire2eth.h +++ b/device/neovifire2/include/neovifire2eth.h @@ -23,21 +23,29 @@ public: static std::vector> Find() { std::vector> found; - - for(auto neodevice : PCAP::FindByProduct(PRODUCT_ID)) { - { // Scope created so that we don't have two of the same device at once - strncpy(neodevice.serial, SERIAL_FIND_ON_OPEN, sizeof(neodevice.serial)); - neodevice.serial[sizeof(neodevice.serial) - 1] = '\0'; - auto device = std::make_shared(neodevice); - if(!device->open()) // We will get the serial number on open - continue; // If the open failed, we won't display the device as an option to connect to - const char* serial = device->getNeoDevice().serial; - if(serial[0] != 'C' || serial[1] != 'Y') - continue; // The device is not a FIRE 2 - strncpy(neodevice.serial, device->getNeoDevice().serial, sizeof(neodevice.serial)); - neodevice.serial[sizeof(neodevice.serial) - 1] = '\0'; + + for(auto& foundDev : PCAP::FindAll()) { + auto packetizer = std::make_shared(); + auto decoder = std::unique_ptr(new Decoder()); + for(auto& payload : foundDev.discoveryPackets) + packetizer->input(payload); + for(auto& packet : packetizer->output()) { + auto msg = decoder->decodePacket(packet); + if(!msg || msg->network.getNetID() != Network::NetID::Main51) + continue; // Not a message we care about + auto sn = std::dynamic_pointer_cast(msg); + if(!sn) + continue; // Not a serial number message + + if(sn->deviceSerial.length() < 2) + continue; + if(sn->deviceSerial.substr(0, 2) != SERIAL_START) + continue; // Not a FIRE 2 + + foundDev.device.serial[sn->deviceSerial.copy(foundDev.device.serial, sizeof(foundDev.device.serial))] = '\0'; + found.push_back(std::make_shared(foundDev.device)); + break; } - found.push_back(std::make_shared(neodevice)); } return found; diff --git a/device/plasion/include/plasion.h b/device/plasion/include/plasion.h index 3aa8542..a2ad641 100644 --- a/device/plasion/include/plasion.h +++ b/device/plasion/include/plasion.h @@ -10,10 +10,11 @@ namespace icsneo { class Plasion : public Device { public: Plasion(neodevice_t neodevice) : Device(neodevice) { - auto transport = std::make_shared(getWritableNeoDevice()); + auto transport = std::unique_ptr(new FTDI(getWritableNeoDevice())); auto packetizer = std::make_shared(); - auto decoder = std::make_shared(); - com = std::make_shared(transport, packetizer, decoder); + auto encoder = std::unique_ptr(new Encoder(packetizer)); + auto decoder = std::unique_ptr(new Decoder()); + com = std::make_shared(std::move(transport), packetizer, std::move(encoder), std::move(decoder)); } }; diff --git a/device/radgalaxy/include/radgalaxy.h b/device/radgalaxy/include/radgalaxy.h index fb4d3e7..3c39f64 100644 --- a/device/radgalaxy/include/radgalaxy.h +++ b/device/radgalaxy/include/radgalaxy.h @@ -14,11 +14,18 @@ public: // Serial numbers start with RG static constexpr DeviceType::Enum DEVICE_TYPE = DeviceType::RADGalaxy; static constexpr const uint16_t PRODUCT_ID = 0x0003; - RADGalaxy(neodevice_t neodevice) : Device(neodevice) { - auto transport = std::unique_ptr(new PCAP(getWritableNeoDevice())); + static constexpr const char* SERIAL_START = "RG"; + + static std::shared_ptr MakePacketizer() { auto packetizer = std::make_shared(); packetizer->disableChecksum = true; packetizer->align16bit = false; + return packetizer; + } + + RADGalaxy(neodevice_t neodevice) : Device(neodevice) { + auto transport = std::unique_ptr(new PCAP(getWritableNeoDevice())); + auto packetizer = MakePacketizer(); auto encoder = std::unique_ptr(new Encoder(packetizer)); auto decoder = std::unique_ptr(new Decoder()); com = std::make_shared(std::move(transport), packetizer, std::move(encoder), std::move(decoder)); @@ -28,21 +35,29 @@ public: static std::vector> Find() { std::vector> found; - - for(auto neodevice : PCAP::FindByProduct(PRODUCT_ID)) { - { // Scope created so that we don't have two of the same device at once - strncpy(neodevice.serial, SERIAL_FIND_ON_OPEN, sizeof(neodevice.serial)); - neodevice.serial[sizeof(neodevice.serial) - 1] = '\0'; - auto device = std::make_shared(neodevice); - if(!device->open()) // We will get the serial number on open - continue; // If the open failed, we won't display the device as an option to connect to - const char* serial = device->getNeoDevice().serial; - if(serial[0] != 'R' || serial[1] != 'G') - continue; // The device is not a RADGalaxy - strncpy(neodevice.serial, device->getNeoDevice().serial, sizeof(neodevice.serial)); - neodevice.serial[sizeof(neodevice.serial) - 1] = '\0'; + + for(auto& foundDev : PCAP::FindAll()) { + auto packetizer = MakePacketizer(); + auto decoder = std::unique_ptr(new Decoder()); + for(auto& payload : foundDev.discoveryPackets) + packetizer->input(payload); + for(auto& packet : packetizer->output()) { + auto msg = decoder->decodePacket(packet); + if(!msg || msg->network.getNetID() != Network::NetID::Main51) + continue; // Not a message we care about + auto sn = std::dynamic_pointer_cast(msg); + if(!sn) + continue; // Not a serial number message + + if(sn->deviceSerial.length() < 2) + continue; + if(sn->deviceSerial.substr(0, 2) != SERIAL_START) + continue; // Not a RADGalaxy + + foundDev.device.serial[sn->deviceSerial.copy(foundDev.device.serial, sizeof(foundDev.device.serial))] = '\0'; + found.push_back(std::make_shared(foundDev.device)); + break; } - found.push_back(std::make_shared(neodevice)); } return found; diff --git a/device/radstar2/include/radstar2.h b/device/radstar2/include/radstar2.h index 26060dc..f88855a 100644 --- a/device/radstar2/include/radstar2.h +++ b/device/radstar2/include/radstar2.h @@ -3,7 +3,6 @@ #include "device/include/device.h" #include "device/include/devicetype.h" -#include "platform/include/ftdi.h" namespace icsneo { @@ -12,23 +11,11 @@ public: // Serial numbers start with RS static constexpr DeviceType::Enum DEVICE_TYPE = DeviceType::RADStar2; static constexpr const uint16_t PRODUCT_ID = 0x0005; + static constexpr const char* SERIAL_START = "RS"; RADStar2(neodevice_t neodevice) : Device(neodevice) { - auto transport = std::make_shared(getWritableNeoDevice()); - auto packetizer = std::make_shared(); - auto decoder = std::make_shared(); - com = std::make_shared(transport, packetizer, decoder); getWritableNeoDevice().type = DEVICE_TYPE; productId = PRODUCT_ID; } - - static std::vector> Find() { - std::vector> found; - - for(auto neodevice : FTDI::FindByProduct(PRODUCT_ID)) - found.push_back(std::make_shared(neodevice)); - - return found; - } }; } diff --git a/device/radstar2/include/radstar2eth.h b/device/radstar2/include/radstar2eth.h new file mode 100644 index 0000000..2f709e1 --- /dev/null +++ b/device/radstar2/include/radstar2eth.h @@ -0,0 +1,62 @@ +#ifndef __RADSTAR2ETH_H_ +#define __RADSTAR2ETH_H_ + +#include "device/radstar2/include/radstar2.h" +#include "communication/include/network.h" +#include "communication/message/include/serialnumbermessage.h" +#include "platform/include/pcap.h" + +namespace icsneo { + +class RADStar2ETH : public RADStar2 { +public: + static std::shared_ptr MakePacketizer() { + auto packetizer = std::make_shared(); + packetizer->disableChecksum = true; + packetizer->align16bit = false; + return packetizer; + } + + // Serial numbers start with RS + RADStar2ETH(neodevice_t neodevice) : RADStar2(neodevice) { + auto transport = std::unique_ptr(new PCAP(getWritableNeoDevice())); + auto packetizer = MakePacketizer(); + auto encoder = std::unique_ptr(new Encoder(packetizer)); + auto decoder = std::unique_ptr(new Decoder()); + com = std::make_shared(std::move(transport), packetizer, std::move(encoder), std::move(decoder)); + } + + static std::vector> Find() { + std::vector> found; + + for(auto& foundDev : PCAP::FindAll()) { + auto packetizer = MakePacketizer(); + auto decoder = std::unique_ptr(new Decoder()); + for(auto& payload : foundDev.discoveryPackets) + packetizer->input(payload); + for(auto& packet : packetizer->output()) { + auto msg = decoder->decodePacket(packet); + if(!msg || msg->network.getNetID() != Network::NetID::Main51) + continue; // Not a message we care about + auto sn = std::dynamic_pointer_cast(msg); + if(!sn) + continue; // Not a serial number message + + if(sn->deviceSerial.length() < 2) + continue; + if(sn->deviceSerial.substr(0, 2) != SERIAL_START) + continue; // Not a RADStar2 + + foundDev.device.serial[sn->deviceSerial.copy(foundDev.device.serial, sizeof(foundDev.device.serial))] = '\0'; + found.push_back(std::make_shared(foundDev.device)); + break; + } + } + + return found; + } +}; + +} + +#endif \ No newline at end of file diff --git a/device/radstar2/include/radstar2usb.h b/device/radstar2/include/radstar2usb.h new file mode 100644 index 0000000..f7340d4 --- /dev/null +++ b/device/radstar2/include/radstar2usb.h @@ -0,0 +1,32 @@ +#ifndef __RADSTAR2USB_H_ +#define __RADSTAR2USB_H_ + +#include "device/radstar2/include/radstar2.h" +#include "platform/include/ftdi.h" + +namespace icsneo { + +class RADStar2USB : public RADStar2 { +public: + // Serial numbers start with RS + RADStar2USB(neodevice_t neodevice) : RADStar2(neodevice) { + auto transport = std::unique_ptr(new FTDI(getWritableNeoDevice())); + auto packetizer = std::make_shared(); + auto encoder = std::unique_ptr(new Encoder(packetizer)); + auto decoder = std::unique_ptr(new Decoder()); + com = std::make_shared(std::move(transport), packetizer, std::move(encoder), std::move(decoder)); + } + + static std::vector> Find() { + std::vector> found; + + for(auto neodevice : FTDI::FindByProduct(PRODUCT_ID)) + found.push_back(std::make_shared(neodevice)); + + return found; + } +}; + +} + +#endif \ No newline at end of file diff --git a/platform/windows/include/devices.h b/platform/windows/include/devices.h index 7b6deca..a2255f2 100644 --- a/platform/windows/include/devices.h +++ b/platform/windows/include/devices.h @@ -6,10 +6,11 @@ // #include "device/neovifire/include/neovifire.h" #include "device/neovifire2/include/neovifire2eth.h" #include "device/neovifire2/include/neovifire2usb.h" -// #include "device/plasion/include/neoviion.h" -// #include "device/plasion/include/neoviplasma.h" +#include "device/plasion/include/neoviion.h" +#include "device/plasion/include/neoviplasma.h" #include "device/radgalaxy/include/radgalaxy.h" -// #include "device/radstar2/include/radstar2.h" +#include "device/radstar2/include/radstar2eth.h" +#include "device/radstar2/include/radstar2usb.h" // #include "device/radsupermoon/include/radsupermoon.h" // #include "device/valuecan3/include/valuecan3.h" // #include "device/valuecan4/include/valuecan4.h" diff --git a/platform/windows/include/pcap.h b/platform/windows/include/pcap.h index c7aefd1..da03a8e 100644 --- a/platform/windows/include/pcap.h +++ b/platform/windows/include/pcap.h @@ -10,7 +10,13 @@ namespace icsneo { class PCAP : public ICommunication { public: - static std::vector FindByProduct(int product); + class PCAPFoundDevice { + public: + neodevice_t device; + std::vector> discoveryPackets; + }; + + static std::vector FindAll(); static std::string GetEthDevSerialFromMacAddress(uint8_t product, uint16_t macSerial); static bool IsHandleValid(neodevice_handle_t handle); diff --git a/platform/windows/pcap.cpp b/platform/windows/pcap.cpp index 1070ed1..ad806be 100644 --- a/platform/windows/pcap.cpp +++ b/platform/windows/pcap.cpp @@ -16,12 +16,12 @@ static const uint8_t BROADCAST_MAC[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; std::vector PCAP::knownInterfaces; -std::vector PCAP::FindByProduct(int product) { - std::vector foundDevices; +std::vector PCAP::FindAll() { + std::vector foundDevices; PCAPDLL pcap; if(!pcap.ok()) { std::cout << "PCAP not okay" << std::endl; - return std::vector(); + return std::vector(); } // First we ask WinPCAP to give us all of the devices @@ -39,7 +39,7 @@ std::vector PCAP::FindByProduct(int product) { if(!success) { std::cout << "PCAP FindAllDevs_Ex not okay " << errbuf << std::endl; - return std::vector(); + return std::vector(); } std::vector interfaces; @@ -56,13 +56,13 @@ std::vector PCAP::FindByProduct(int product) { ULONG size = 0; if(GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, nullptr, &size) != ERROR_BUFFER_OVERFLOW) { std::cout << "GetAdaptersAddresses size query not okay" << std::endl; - return std::vector(); + return std::vector(); } std::vector adapterAddressBuffer; adapterAddressBuffer.resize(size); if(GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, nullptr, (IP_ADAPTER_ADDRESSES*)adapterAddressBuffer.data(), &size) != ERROR_SUCCESS) { std::cout << "GetAdaptersAddresses not okay" << std::endl; - return std::vector(); + return std::vector(); } // aa->AdapterName constains a unique name of the interface like "{3B1D2791-435A-456F-8A7B-9CB0EEE5DAB3}" @@ -131,28 +131,37 @@ std::vector PCAP::FindByProduct(int product) { continue; // Keep waiting for that packet EthernetPacket packet(data, header->caplen); - if(packet.etherType == 0xCAB2 && packet.srcMAC[0] == 0x00 && packet.srcMAC[1] == 0xFC && packet.srcMAC[2] == 0x70) { - /* Here we could check packet.srcMAC[3] against the PID, however for some devices - * this is not correct. For this reason, we don't check the product here and instead - * check the serial number that comes back later. + // 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_t neodevice; - /* Unlike other transport layers, we can't get the serial number here as we - * actually need to open the device in the find method and then request it - * over a working communication layer. We could technically create a communication - * layer to parse the packet we have in `data` at this point, but we'd need to - * know information about the device to correctly instantiate a packetizer and - * decoder. I'm intentionally avoiding passing that information down here for - * code quality's sake. - */ - neodevice.handle = (neodevice_handle_t)((i << 24) | (packet.srcMAC[3] << 16) | (packet.srcMAC[4] << 8) | (packet.srcMAC[5])); - bool alreadyExists = false; + 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.handle == neodevice.handle) - alreadyExists = true; - if(!alreadyExists) - foundDevices.push_back(neodevice); + 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)); + } } } @@ -163,36 +172,6 @@ std::vector PCAP::FindByProduct(int product) { return foundDevices; } -std::string PCAP::GetEthDevSerialFromMacAddress(uint8_t product, uint16_t macSerial) { - constexpr uint16_t serialOffset = 0x30; - std::string serial; - switch(product) { - case 0x01: // cmProbe - serial += "CM"; - break; - case 0x03: // RADGalaxy - serial += "RG"; - break; - case 0x04: // FIRE 2 - serial += "CY"; - break; - case 0x05: // RADStar 2 - serial += "RS"; - break; - case 0x06: // RADGigalog - serial += "GL"; - break; - default: // Should never happen - serial += "XX"; - break; - } - for(int i = 1000; i > 0; i /= 10) { - serial += (char)(macSerial / i + serialOffset); - macSerial %= i; - } - return serial; -} - bool PCAP::IsHandleValid(neodevice_handle_t handle) { uint8_t netifIndex = (uint8_t)(handle >> 24); return (netifIndex < knownInterfaces.size());