Skip to content

Commit 2d0ac3e

Browse files
authored
Merge pull request #3 from naresh97/feature/configureDevice
Feature: Configure Device
2 parents 2a06194 + f09fd98 commit 2d0ac3e

9 files changed

Lines changed: 398 additions & 70 deletions

File tree

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

CMakeLists.txt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
cmake_minimum_required(VERSION 3.20)
22
project(OpenProfinet)
33
set(CMAKE_PROJECT_VERSION_MAJOR 0)
4-
set(CMAKE_PROJECT_VERSION_MINOR 8)
5-
set(CMAKE_PROJECT_VERSION_PATCH 1)
4+
set(CMAKE_PROJECT_VERSION_MINOR 9)
5+
set(CMAKE_PROJECT_VERSION_PATCH 2)
66

77
set(CMAKE_CXX_STANDARD 20)
88

@@ -20,6 +20,9 @@ set(CPACK_GENERATOR DEB)
2020
set(CPACK_DEBIAN_PACKAGE_NAME OpenProfinet)
2121
set(CPACK_DEBIAN_PACKAGE_MAINTAINER "Nareshkumar Rao <contact@nareshkumarrao.com>")
2222
set(CPACK_DEBIAN_PACKAGE_DEPENDS "libpcap0.8-dev")
23-
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "OpenProfinet is a collection of tools for working with Profinet on Linux")
24-
set(CPACK_DEBIAN_FILE_NAME OpenProfinet-${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}-${CMAKE_PROJECT_VERSION_PATCH}.deb)
23+
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "
24+
OpenProfinet is a collection of tools for working with Profinet on Linux
25+
https://github.com/naresh97/OpenProfinet
26+
")
27+
set(CPACK_DEBIAN_FILE_NAME OpenProfinet-${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH}.deb)
2528
include(CPack)

README.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
A collection of tools for configuring Profinet devices on Linux based systems.
44
Uses libpcap library for creating Ethernet frames.
55

6-
## Usage
6+
## Usage: pntool
77

8+
### pntool search
89
```
910
Search for Profinet devices on the network.
1011
Usage: pntool search [OPTIONS]
@@ -16,6 +17,24 @@ Options:
1617
1718
```
1819

20+
### pntool configure
21+
```
22+
Configure Profinet devices on the network.
23+
Usage: pntool configure [OPTIONS] device
24+
25+
Positionals:
26+
device REQUIRED The current name of the device to configure
27+
28+
Options:
29+
-h,--help Print this help message and exit
30+
-t,--timeout INT Time to search for devices in milliseconds
31+
-n,--name TEXT Set a new name for the device
32+
-i,--ip TEXT New IP Address
33+
-s,--subnet TEXT New Subnet Mask
34+
-g,--gateway TEXT New Gateway Address
35+
36+
```
37+
1938
## Building
2039

2140
Uses CMake as build system.
@@ -29,4 +48,5 @@ make
2948

3049
## Licensing
3150

32-
This software is open-source and free to use as specified in the GPLv3 license. However, commercial use of this software is only allowed with explicit permission from the author.
51+
This software is open-source and free to use as specified in the GPLv3 license for **non-commercial** use only.
52+
Permission for commercial use require explicit permission of the author.

src/ProfinetTool.cpp

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,35 @@
66

77
#include <thread>
88
#include <iostream>
9+
#include <cstring>
910
#include "ProfinetTool.h"
1011

11-
extern "C"{
12+
extern "C" {
1213
#include "pcapInterface.h"
1314
}
1415

15-
std::string getDefaultInterface(){
16+
#define LISTENING_THREAD_STARTUP_DELAY 500
17+
18+
std::string getDefaultInterface() {
1619
char interface[256];
1720
get_default_interface(interface);
1821
return interface;
1922
}
2023

21-
std::string MACToString(std::array<uint8_t, 6> mac){
24+
std::string MACToString(std::array<uint8_t, 6> mac) {
2225
char macStr[18];
2326
snprintf(macStr, sizeof(macStr), "%02X:%02X:%02X:%02X:%02X:%02X",
2427
mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
2528
return macStr;
2629
}
2730

28-
std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinetPacketArray){
31+
std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinetPacketArray) {
2932
profinet_device devices[PROFINET_DEVICE_LIST_SIZE];
3033
int count;
3134

3235
std::vector<ProfinetDevice> devicesVector;
3336
get_profinet_devices(&profinetPacketArray, devices, &count);
34-
for(int i = 0; i < count; ++i){
37+
for (int i = 0; i < count; ++i) {
3538
auto device = devices[i];
3639
char ipAddress[INET_ADDRSTRLEN];
3740
char subnet[INET_ADDRSTRLEN];
@@ -41,11 +44,11 @@ std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinet
4144
inet_ntop(AF_INET, &device.gateway, gateway, INET_ADDRSTRLEN);
4245

4346
ProfinetDevice newDevice{
44-
.deviceName = device.stationName,
45-
.deviceType = device.deviceType,
46-
.ipAddress = ipAddress,
47-
.subnetMask = subnet,
48-
.gateway = gateway,
47+
.deviceName = device.deviceName,
48+
.deviceType = device.deviceType,
49+
.ipAddress = ipAddress,
50+
.subnetMask = subnet,
51+
.gateway = gateway,
4952
};
5053
std::copy(std::begin(device.macAddress), std::end(device.macAddress), std::begin(newDevice.deviceMAC));
5154

@@ -54,23 +57,74 @@ std::vector<ProfinetDevice> getDevicesFromPackets(profinet_packet_array profinet
5457
return devicesVector;
5558
}
5659

57-
void ProfinetTool::searchForDevices() {
58-
profinet_packet_array profinetPacketArray{};
59-
auto listeningThread = std::thread([this, &profinetPacketArray](){
60+
std::thread ProfinetTool::listenForPackets() {
61+
auto listeningThread = std::thread([this]() {
6062
profinet_listen(interface.c_str(), &profinetPacketArray, searchTimeout);
6163
});
64+
std::this_thread::sleep_for(std::chrono::milliseconds(LISTENING_THREAD_STARTUP_DELAY));
65+
return listeningThread;
66+
}
67+
68+
std::vector<ProfinetDevice> ProfinetTool::searchForDevices(bool printFoundDevices) {
69+
profinetPacketArray = {.packets = {}, .size = 0};
70+
71+
auto listeningThread = listenForPackets();
6272

6373
discovery_request(interface.c_str());
6474

65-
std::cout << "Searching..." << std::endl;
75+
std::cout << "Searching for devices..." << std::endl;
76+
6677
listeningThread.join();
6778

6879
auto devices = getDevicesFromPackets(profinetPacketArray);
69-
for(const auto &device : devices){
70-
using namespace std;
71-
cout << "Device Name: " << device.deviceName << " - IP: " << device.ipAddress << ", Subnet Mask: " << device.subnetMask
72-
<< ", Gateway: " << device.gateway << ", Type: " << device.deviceType << endl;
80+
81+
if (printFoundDevices) {
82+
for (const auto &device: devices) {
83+
using namespace std;
84+
cout << "Device Name: " << device.deviceName << " - IP: " << device.ipAddress << ", Subnet Mask: "
85+
<< device.subnetMask
86+
<< ", Gateway: " << device.gateway << ", Type: " << device.deviceType << endl;
87+
}
7388
}
89+
90+
return devices;
91+
}
92+
93+
void ProfinetTool::configureDevices(const std::string &deviceName, const std::string &newName, const std::string &newIP,
94+
const std::string &newSubnet, const std::string &newGateway) {
95+
auto devices = searchForDevices(false);
96+
97+
ProfinetDevice device;
98+
bool found = false;
99+
for (auto const &loopDevice: devices) {
100+
if (loopDevice.deviceName == deviceName) {
101+
found = true;
102+
device = loopDevice;
103+
break;
104+
}
105+
}
106+
107+
if (!found) throw std::runtime_error("Device does not exist on network. Use 'search' to check the name.");
108+
109+
if (!newName.empty()) device.deviceName = newName;
110+
if (!newIP.empty()) device.ipAddress = newIP;
111+
if (!newSubnet.empty()) device.subnetMask = newSubnet;
112+
if (!newGateway.empty()) device.gateway = newGateway;
113+
114+
profinet_device device_p{};
115+
strcpy(device_p.deviceName, device.deviceName.c_str());
116+
strcpy(device_p.deviceType, device.deviceType.c_str());
117+
memcpy(device_p.macAddress, device.deviceMAC.data(), 6);
118+
inet_pton(AF_INET, device.ipAddress.c_str(), &device_p.ipAddress);
119+
inet_pton(AF_INET, device.subnetMask.c_str(), &device_p.subnetMask);
120+
inet_pton(AF_INET, device.gateway.c_str(), &device_p.gateway);
121+
122+
auto success = set_device_configuration(interface.c_str(), &device_p);
123+
if (success)
124+
std::cout << "Device Configuration: Success!" << std::endl << std::endl;
125+
else
126+
throw std::runtime_error("Configuration Failure. Did not receive response from the device.");
127+
searchForDevices(true);
74128
}
75129

76130
ProfinetTool::ProfinetTool(const std::string &interface, int timeout) : interface(interface), searchTimeout(timeout) {}

src/ProfinetTool.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
#include <string>
99
#include <vector>
1010
#include <array>
11+
#include <thread>
12+
#include "profinetTypes.h"
1113

12-
struct ProfinetDevice{
14+
struct ProfinetDevice {
1315
std::string deviceName;
1416
std::string deviceType;
1517
std::string ipAddress;
@@ -21,14 +23,22 @@ struct ProfinetDevice{
2123
class ProfinetTool {
2224
public:
2325
explicit ProfinetTool(int timeout = 5000);
26+
2427
ProfinetTool(const std::string &interface, int timeout);
2528

26-
void searchForDevices();
27-
void setDeviceProperties();
29+
// Commands
30+
std::vector<ProfinetDevice> searchForDevices(bool printFoundDevices = true);
31+
32+
void configureDevices(const std::string &deviceName, const std::string &newName, const std::string &newIP,
33+
const std::string &newSubnet, const std::string &newGateway);
2834

2935
private:
36+
std::thread listenForPackets();
37+
3038
std::string interface;
3139
int searchTimeout;
40+
41+
profinet_packet_array profinetPacketArray{};
3242
};
3343

3444

src/main.cpp

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,45 @@
44
int main(int argc, char **argv) {
55

66
CLI::App app{"pntool is part of the OpenProfinet project. It is used to configure Profinet networks."};
7+
app.require_subcommand();
78

89
CLI::App *search = app.add_subcommand("search", "Search for Profinet devices on the network.");
9-
app.require_subcommand();
1010
CLI::Option *interface = search->add_option("-i,--interface", "Interface to use");
11-
int timeout = 2000;
11+
int timeout = 1000;
1212
search->add_option("-t,--timeout", timeout, "Time to search for devices in milliseconds");
1313

14+
CLI::App *configure = app.add_subcommand("configure", "Configure Profinet devices on the network.");
15+
CLI::Option *device = configure->add_option("device", "The current name of the device to configure")
16+
->required(true);
17+
configure->add_option("-t,--timeout", timeout, "Time to search for devices in milliseconds");
18+
19+
std::string newName;
20+
configure->add_option("-n,--name", newName, "Set a new name for the device");
21+
22+
std::string newIP;
23+
configure->add_option("-i,--ip", newIP, "New IP Address");
24+
std::string newSubnet;
25+
configure->add_option("-s,--subnet", newSubnet, "New Subnet Mask");
26+
std::string newGateway;
27+
configure->add_option("-g,--gateway", newGateway, "New Gateway Address");
28+
1429
CLI11_PARSE(app, argc, argv);
1530

16-
if(*search){
17-
ProfinetTool profinetTool(timeout);
18-
if(!interface->empty()) profinetTool = ProfinetTool(interface->as<std::string>(), timeout);
19-
profinetTool.searchForDevices();
31+
try {
32+
if (*search) {
33+
ProfinetTool profinetTool(timeout);
34+
if (!interface->empty()) profinetTool = ProfinetTool(interface->as<std::string>(), timeout);
35+
profinetTool.searchForDevices();
36+
} else if (*configure) {
37+
ProfinetTool profinetTool(timeout);
38+
profinetTool.configureDevices(device->as<std::string>(), newName, newIP, newSubnet,
39+
newGateway);
40+
}
41+
} catch (const std::runtime_error &e) {
42+
std::cerr << "Could not run command: " << e.what() << std::endl;
43+
return EXIT_FAILURE;
2044
}
2145

46+
2247
return 0;
2348
}

0 commit comments

Comments
 (0)