Merge pull request #120 from garretfick/feature/ENIP_SERVICE
Make ENIP follow the same service-based approach
This commit is contained in:
commit
9c925c590d
|
@ -232,7 +232,7 @@ if(OPLC_MAIN_PROGRAM)
|
|||
message("Compile main program enabled")
|
||||
|
||||
set(OPLC_USER_DIR ${PROJECT_SOURCE_DIR}/etc/src)
|
||||
file(GLOB oplc_SRC runtime/core/*.cpp runtime/core/dnp3s/*.cpp runtime/core/modbusslave/*.cpp runtime/core/modbusmaster/*.cpp runtime/core/service/*.cpp runtime/vendor/inih-r46/*.c)
|
||||
file(GLOB oplc_SRC runtime/core/*.cpp runtime/core/dnp3s/*.cpp runtime/core/modbusslave/*.cpp runtime/core/modbusmaster/*.cpp runtime/core/service/*.cpp runtime/core/enip/*.cpp runtime/vendor/inih-r46/*.c)
|
||||
|
||||
include_directories(${OPLC_USER_DIR})
|
||||
include_directories(runtime/core)
|
||||
|
|
|
@ -26,7 +26,7 @@ endif()
|
|||
include_directories(lib)
|
||||
|
||||
# The primary source is everything in this directory
|
||||
file(GLOB oplc_SRC *.cpp dnp3s/*.cpp service/*.cpp modbusslave/*.cpp modbusmaster/*.cpp)
|
||||
file(GLOB oplc_SRC *.cpp dnp3s/*.cpp service/*.cpp modbusslave/*.cpp modbusmaster/*.cpp enip/*.cpp)
|
||||
|
||||
message("In runtime")
|
||||
message(${oplc_SRC})
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,26 +1,49 @@
|
|||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2019 Thiago Alves
|
||||
// This file is part of the OpenPLC Software Stack.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http ://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
// This file contains the structured used by enip.cpp to process
|
||||
// EtherNet/IP requests.
|
||||
// UAH, Sep 2019
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
*/
|
||||
|
||||
/** @} */
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright 2019 Thiago Alves
|
||||
// Copyright 2019 Garret Fick
|
||||
// This file is part of the OpenPLC Software Stack.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http ://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
// This file contains the structured used by enip.cpp to process
|
||||
// EtherNet/IP requests.
|
||||
// UAH, Sep 2019
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#ifndef RUNTIME_CORE_ENIP_ENIP_H_
|
||||
#define RUNTIME_CORE_ENIP_ENIP_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
*/
|
||||
|
||||
class GlueVariablesBinding;
|
||||
|
||||
/// @brief Start the enip server.
|
||||
///
|
||||
/// @param glue_variables The glue variables that may be bound into this
|
||||
/// server.
|
||||
/// @param run A signal for running this server. This server terminates when
|
||||
/// this signal is false.
|
||||
/// @param config The custom configuration for this service.
|
||||
void enip_service_run(const GlueVariablesBinding& binding,
|
||||
volatile bool& run, const char* config);
|
||||
|
||||
std::int16_t enip_process_message(unsigned char *buffer, std::int16_t buffer_size, void* user_data);
|
||||
std::uint16_t processPCCCMessage(unsigned char *buffer, int buffer_size);
|
||||
|
||||
/** @} */
|
||||
|
||||
#endif // RUNTIME_CORE_ENIP_ENIP_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -52,14 +52,8 @@
|
|||
const uint16_t BUFFER_MAX_SIZE(1024);
|
||||
std::mutex command_mutex;
|
||||
|
||||
// TODO Globals to move into services
|
||||
bool run_enip = 0;
|
||||
uint16_t enip_port = 44818;
|
||||
time_t start_time;
|
||||
|
||||
// Global Threads
|
||||
pthread_t enip_thread;
|
||||
|
||||
// Log Buffer
|
||||
#define LOG_BUFFER_SIZE 1000000
|
||||
unsigned char log_buffer[LOG_BUFFER_SIZE];
|
||||
|
@ -67,43 +61,6 @@ std::shared_ptr<buffered_sink> log_sink;
|
|||
|
||||
using namespace std;
|
||||
|
||||
/// @brief Start the Enip Thread
|
||||
/// @param *arg
|
||||
void *enipThread(void *arg)
|
||||
{
|
||||
startServer(enip_port, run_enip, &processEnipMessage, nullptr);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// @brief Read the argument from a command function
|
||||
/// @param *command
|
||||
int readCommandArgument(const char *command)
|
||||
{
|
||||
int i = 0;
|
||||
int j = 0;
|
||||
char argument[BUFFER_MAX_SIZE];
|
||||
|
||||
while (command[i] != '(' && command[i] != '\0')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
if (command[i] == '(')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
|
||||
while (command[i] != ')' && command[i] != '\0')
|
||||
{
|
||||
argument[j] = command[i];
|
||||
i++;
|
||||
j++;
|
||||
argument[j] = '\0';
|
||||
}
|
||||
|
||||
return atoi(reinterpret_cast<char *>(argument));
|
||||
}
|
||||
|
||||
/// Copy the configuration argument from the command into the buffer
|
||||
/// @param source The source of the command. This should be pointing to the
|
||||
/// character right after the opening "("
|
||||
|
@ -141,6 +98,30 @@ std::int8_t copy_command_config(const char *source, char target[],
|
|||
return 0;
|
||||
}
|
||||
|
||||
/// @brief Get the name of the service from the command.
|
||||
/// @param command The command, starting with the character after "_"
|
||||
/// @return The start of the configuration information.
|
||||
const char* get_service_name(const char* command, char target[], size_t target_size)
|
||||
{
|
||||
size_t end_index = 0;
|
||||
while (command[end_index] != '(' && command[end_index] != '\0')
|
||||
{
|
||||
end_index++;
|
||||
}
|
||||
|
||||
// Now we want to copy everything from the beginning of source
|
||||
// into target, up to the length of target. This may or many not
|
||||
// fill our target, but the size ensure we don't go over the buffer
|
||||
// size.
|
||||
std::memcpy(target, command, min(end_index, target_size));
|
||||
|
||||
// And set the final null terminating character in target. In general
|
||||
// this is replacing the null terminating character with the right end.
|
||||
target[end_index] = '\0';
|
||||
|
||||
return command + end_index + 1;
|
||||
}
|
||||
|
||||
/// @brief Create the socket and bind it.
|
||||
/// @param port
|
||||
/// @return the file descriptor for the socket, or less than 0 if a socket
|
||||
|
@ -240,91 +221,55 @@ void interactive_client_command(const char* command, int client_fd)
|
|||
return;
|
||||
}
|
||||
|
||||
spdlog::trace("Process command received {}", command);
|
||||
spdlog::trace("Command received {}", command);
|
||||
|
||||
if (strncmp(command, "quit()", 6) == 0)
|
||||
{
|
||||
spdlog::info("Issued quit() command");
|
||||
run_openplc = 0;
|
||||
}
|
||||
else if (strncmp(command, "start_modbus(", 13) == 0)
|
||||
else if (strncmp(command, "start_", 6) == 0)
|
||||
{
|
||||
ServiceDefinition* def = services_find("modbusslave");
|
||||
if (def && copy_command_config(command + 13, command_buffer, BUFFER_MAX_SIZE) == 0)
|
||||
char service_name[MAX_SERVICE_NAME_SIZE];
|
||||
auto config_start = get_service_name(command + 6, service_name, MAX_SERVICE_NAME_SIZE);
|
||||
|
||||
ServiceDefinition* def = services_find(service_name);
|
||||
if (!def)
|
||||
{
|
||||
spdlog::error("Unrecognized service name {}", service_name);
|
||||
int count_char = sprintf(command_buffer, "Error: unrecognized service name '%s'\n", service_name);
|
||||
write(client_fd, command_buffer, count_char);
|
||||
return;
|
||||
}
|
||||
|
||||
if (copy_command_config(config_start, command_buffer, BUFFER_MAX_SIZE) == 0)
|
||||
{
|
||||
def->start(command_buffer);
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "stop_modbus()", 13) == 0)
|
||||
{
|
||||
ServiceDefinition* def = services_find("modbusslave");
|
||||
if (def)
|
||||
else
|
||||
{
|
||||
def->stop();
|
||||
spdlog::error("Unable to start service due {} to missing config", service_name);
|
||||
}
|
||||
|
||||
}
|
||||
#ifdef OPLC_DNP3_OUTSTATION
|
||||
else if (strncmp(command, "start_dnp3(", 11) == 0)
|
||||
else if (strncmp(command, "stop_", 5) == 0)
|
||||
{
|
||||
ServiceDefinition* def = services_find("dnp3s");
|
||||
if (def && copy_command_config(command + 11, command_buffer, BUFFER_MAX_SIZE) == 0)
|
||||
char service_name[MAX_SERVICE_NAME_SIZE];
|
||||
auto config_start = get_service_name(command + 5, service_name, MAX_SERVICE_NAME_SIZE);
|
||||
|
||||
ServiceDefinition* def = services_find(service_name);
|
||||
if (!def)
|
||||
{
|
||||
def->start(command_buffer);
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "stop_dnp3()", 11) == 0)
|
||||
{
|
||||
ServiceDefinition* def = services_find("dnp3s");
|
||||
if (def)
|
||||
{
|
||||
def->stop();
|
||||
}
|
||||
}
|
||||
#endif // OPLC_DNP3_OUTSTATION
|
||||
else if (strncmp(command, "start_enip(", 11) == 0)
|
||||
{
|
||||
spdlog::info("Issued start_enip() command to start on port: {}", readCommandArgument(command));
|
||||
enip_port = readCommandArgument(command);
|
||||
if (run_enip) {
|
||||
spdlog::info("EtherNet/IP server already active. Restarting on port: {}", enip_port);
|
||||
//Stop Enip server
|
||||
run_enip = 0;
|
||||
pthread_join(enip_thread, NULL);
|
||||
spdlog::info("EtherNet/IP server was stopped");
|
||||
}
|
||||
//Start Enip server
|
||||
run_enip = 1;
|
||||
pthread_create(&enip_thread, NULL, enipThread, NULL);
|
||||
}
|
||||
else if (strncmp(command, "stop_enip()", 11) == 0)
|
||||
{
|
||||
spdlog::info("Issued stop_enip() command");
|
||||
if (run_enip)
|
||||
{
|
||||
run_enip = 0;
|
||||
pthread_join(enip_thread, NULL);
|
||||
spdlog::info("EtherNet/IP server was stopped");
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "start_pstorage(", 15) == 0)
|
||||
{
|
||||
ServiceDefinition* def = services_find("pstorage");
|
||||
if (def && copy_command_config(command + 15, command_buffer, BUFFER_MAX_SIZE) == 0)
|
||||
{
|
||||
def->start(command_buffer);
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "stop_pstorage()", 15) == 0)
|
||||
{
|
||||
ServiceDefinition* def = services_find("pstorage");
|
||||
if (def)
|
||||
{
|
||||
def->stop();
|
||||
int count_char = sprintf(command_buffer, "Error: unrecognized service name '%s'\n", service_name);
|
||||
write(client_fd, command_buffer, count_char);
|
||||
return;
|
||||
}
|
||||
|
||||
def->stop();
|
||||
}
|
||||
else if (strncmp(command, "runtime_logs()", 14) == 0)
|
||||
{
|
||||
spdlog::debug("Issued runtime_logs() command");
|
||||
spdlog::trace("Issued runtime_logs() command");
|
||||
std::string data = log_sink->data();
|
||||
write(client_fd, data.c_str(), data.size());
|
||||
return;
|
||||
|
@ -339,6 +284,7 @@ void interactive_client_command(const char* command, int client_fd)
|
|||
}
|
||||
else
|
||||
{
|
||||
spdlog::error("Unrecognized command {}", command_buffer);
|
||||
int count_char = sprintf(command_buffer, "Error: unrecognized command\n");
|
||||
write(client_fd, command_buffer, count_char);
|
||||
return;
|
||||
|
@ -401,7 +347,7 @@ void* interactive_client_run(void* arguments)
|
|||
}
|
||||
}
|
||||
|
||||
closeSocket(client_args->client_fd);
|
||||
close_socket(client_args->client_fd);
|
||||
spdlog::trace("Interactive server connection completed");
|
||||
delete client_args;
|
||||
pthread_exit(NULL);
|
||||
|
@ -436,7 +382,7 @@ void interactive_run(oplc::config_stream& cfg_stream,
|
|||
auto args = new ClientArgs { .client_fd = client_fd, .run = &run };
|
||||
|
||||
spdlog::trace("Interactive Server: Client accepted! Creating thread for the new client ID: {}", client_fd);
|
||||
int ret = pthread_create(&thread, NULL, interactive_client_run, args);
|
||||
int ret = pthread_create(&thread, nullptr, interactive_client_run, args);
|
||||
if (ret == 0)
|
||||
{
|
||||
pthread_detach(thread);
|
||||
|
@ -447,7 +393,7 @@ void interactive_run(oplc::config_stream& cfg_stream,
|
|||
}
|
||||
}
|
||||
|
||||
closeSocket(socket_fd);
|
||||
close_socket(socket_fd);
|
||||
}
|
||||
|
||||
void interactive_service_run(const GlueVariablesBinding& binding,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
// Copyright 2015 Thiago Alves
|
||||
// Copyright 2019 Garret Fick
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
|
@ -82,11 +83,6 @@ extern unsigned long long common_ticktime__;
|
|||
struct GlueVariable;
|
||||
class GlueVariablesBinding;
|
||||
|
||||
extern const std::uint16_t OPLCGLUE_GLUE_SIZE;
|
||||
extern const GlueVariable oplc_glue_vars[];
|
||||
extern const char OPLCGLUE_MD5_DIGEST[];
|
||||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// FUNCTION PROTOTYPES
|
||||
//----------------------------------------------------------------------
|
||||
|
@ -119,26 +115,17 @@ extern int ignored_int_outputs[];
|
|||
void sleep_until(struct timespec *ts, int delay);
|
||||
bool pinNotPresent(int *ignored_vector, int vector_size, int pinNumber);
|
||||
extern uint8_t run_openplc;
|
||||
void handleSpecialFunctions();
|
||||
|
||||
// server.cpp
|
||||
typedef std::int16_t (*process_message_fn)(unsigned char *buffer, std::int16_t buffer_size, void* user_data);
|
||||
void startServer(uint16_t port, volatile bool& run_server, process_message_fn process_message, void* user_data);
|
||||
int getSO_ERROR(int fd);
|
||||
void closeSocket(int fd);
|
||||
void start_server(uint16_t port, volatile bool& run_server, process_message_fn process_message, void* user_data);
|
||||
void close_socket(int fd);
|
||||
bool SetSocketBlockingEnabled(int fd, bool blocking);
|
||||
|
||||
// interactive_server.cpp
|
||||
void initialize_logging(int argc, char **argv);
|
||||
extern bool run_enip;
|
||||
extern time_t start_time;
|
||||
|
||||
// enip.cpp
|
||||
std::int16_t processEnipMessage(unsigned char *buffer, std::int16_t buffer_size, void* user_data);
|
||||
|
||||
// pccc.cpp ADDED Ulmer
|
||||
uint16_t processPCCCMessage(unsigned char *buffer, int buffer_size);
|
||||
|
||||
void bootstrap();
|
||||
|
||||
/** @}*/
|
||||
|
|
|
@ -116,7 +116,7 @@ void disableOutputs()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Special Functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void handleSpecialFunctions()
|
||||
void handle_special_functions()
|
||||
{
|
||||
// Current time [%ML1024]
|
||||
struct tm *current_time;
|
||||
|
@ -191,14 +191,11 @@ int main(int argc, char **argv)
|
|||
updateBuffersIn();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
// Make sure the buffer pointers are correct and
|
||||
// attached to the user variables
|
||||
glueVars();
|
||||
|
||||
updateCustomIn();
|
||||
// Update input image table with data from slave devices
|
||||
services_before_cycle();
|
||||
handleSpecialFunctions();
|
||||
handle_special_functions();
|
||||
// Execute plc program logic
|
||||
config_run__(__tick++);
|
||||
updateCustomOut();
|
||||
|
|
|
@ -482,7 +482,7 @@ int8_t modbus_slave_run(oplc::config_stream& cfg_stream,
|
|||
}
|
||||
|
||||
spdlog::info("Starting modbus slave on port {}", config.port);
|
||||
startServer(config.port, run, &modbus_process_message, &strategy);
|
||||
start_server(config.port, run, &modbus_process_message, &strategy);
|
||||
|
||||
pthread_join(exchange_data_thread, nullptr);
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ using namespace std;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Verify if all errors were cleared on a socket
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int getSO_ERROR(int fd)
|
||||
int get_socket_error(int fd)
|
||||
{
|
||||
int err = 1;
|
||||
socklen_t len = sizeof err;
|
||||
|
@ -58,11 +58,11 @@ int getSO_ERROR(int fd)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Properly close a socket
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void closeSocket(int fd)
|
||||
void close_socket(int fd)
|
||||
{
|
||||
if (fd >= 0)
|
||||
{
|
||||
getSO_ERROR(fd); // first clear any errors, which can cause close to fail
|
||||
get_socket_error(fd); // first clear any errors, which can cause close to fail
|
||||
if (shutdown(fd, SHUT_RDWR) < 0) // secondly, terminate the 'reliable' delivery
|
||||
if (errno != ENOTCONN && errno != EINVAL) // SGI causes EINVAL
|
||||
perror("shutdown");
|
||||
|
@ -88,7 +88,7 @@ bool SetSocketBlockingEnabled(int fd, bool blocking)
|
|||
/// @param port
|
||||
/// @return the file descriptor for the socket created
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int createSocket(uint16_t port)
|
||||
int create_socket(uint16_t port)
|
||||
{
|
||||
int socket_fd;
|
||||
struct sockaddr_in server_addr;
|
||||
|
@ -136,7 +136,7 @@ int createSocket(uint16_t port)
|
|||
/// @param run_server A flag to terminate this client.
|
||||
/// @return file descriptor to communicate with the client
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int waitForClient(int socket_fd, volatile bool& run_server)
|
||||
int wait_for_client(int socket_fd, volatile bool& run_server)
|
||||
{
|
||||
int client_fd;
|
||||
struct sockaddr_in client_addr;
|
||||
|
@ -167,7 +167,7 @@ int waitForClient(int socket_fd, volatile bool& run_server)
|
|||
/// @param buffer
|
||||
/// @return the number of bytes received.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
int listenToClient(int client_fd, unsigned char *buffer)
|
||||
int listen_to_client(int client_fd, unsigned char *buffer)
|
||||
{
|
||||
bzero(buffer, NET_BUFFER_SIZE);
|
||||
int n = read(client_fd, buffer, NET_BUFFER_SIZE);
|
||||
|
@ -190,19 +190,18 @@ struct ServerArgs
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Thread to handle requests for each connected client
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void *handleConnections(void *arguments)
|
||||
void *handle_connections(void *arguments)
|
||||
{
|
||||
auto args = reinterpret_cast<ServerArgs*>(arguments);
|
||||
|
||||
unsigned char buffer[NET_BUFFER_SIZE];
|
||||
int message_size;
|
||||
int client_fd = args->client_fd;
|
||||
|
||||
spdlog::debug("Server: Thread created for client ID: {}", client_fd);
|
||||
|
||||
while(*args->run)
|
||||
{
|
||||
message_size = listenToClient(client_fd, buffer);
|
||||
auto message_size = listen_to_client(client_fd, buffer);
|
||||
if (message_size <= 0 || message_size > NET_BUFFER_SIZE)
|
||||
{
|
||||
// something has gone wrong or the client has closed connection
|
||||
|
@ -230,7 +229,7 @@ void *handleConnections(void *arguments)
|
|||
spdlog::trace("Closing client socket and calling pthread_exit");
|
||||
close(args->client_fd);
|
||||
spdlog::trace("Terminating server connections thread");
|
||||
pthread_exit(NULL);
|
||||
pthread_exit(nullptr);
|
||||
delete args;
|
||||
|
||||
return nullptr;
|
||||
|
@ -244,15 +243,15 @@ void *handleConnections(void *arguments)
|
|||
/// @param port The port to listen on.
|
||||
/// @param process_message A function to run to process socket messages.
|
||||
/// @param user_data Passed into the process_message function as client data.
|
||||
void startServer(uint16_t port, volatile bool& run_server, process_message_fn process_message, void* user_data)
|
||||
void start_server(uint16_t port, volatile bool& run_server, process_message_fn process_message, void* user_data)
|
||||
{
|
||||
int socket_fd, client_fd;
|
||||
|
||||
socket_fd = createSocket(port);
|
||||
socket_fd = create_socket(port);
|
||||
|
||||
while(run_server)
|
||||
{
|
||||
client_fd = waitForClient(socket_fd, run_server); //block until a client connects
|
||||
client_fd = wait_for_client(socket_fd, run_server); //block until a client connects
|
||||
if (client_fd < 0)
|
||||
{
|
||||
spdlog::error("Server: Error accepting client!");
|
||||
|
@ -268,7 +267,7 @@ void startServer(uint16_t port, volatile bool& run_server, process_message_fn pr
|
|||
.user_data=user_data
|
||||
};
|
||||
spdlog::trace("Server: Client accepted on {}! Creating thread for the new client ID: {}...", port, client_fd);
|
||||
int success = pthread_create(&thread, NULL, handleConnections, args);
|
||||
int success = pthread_create(&thread, NULL, handle_connections, args);
|
||||
if (success == 0)
|
||||
{
|
||||
pthread_detach(thread);
|
||||
|
|
|
@ -14,10 +14,19 @@
|
|||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
#include "service_definition.h"
|
||||
#include "glue.h"
|
||||
#include "ladder.h"
|
||||
|
||||
// These definitions are provided by the glueVars.cpp
|
||||
// implementation file. They define the information about
|
||||
// glue variables that we pass to services during a lifecycle
|
||||
// function.
|
||||
extern std::mutex bufferLock;
|
||||
extern const std::uint16_t OPLCGLUE_GLUE_SIZE;
|
||||
extern const GlueVariable oplc_glue_vars[];
|
||||
extern const char OPLCGLUE_MD5_DIGEST[];
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "service_registry.h"
|
||||
#include "interactive_server.h"
|
||||
#include "pstorage.h"
|
||||
#include "enip/enip.h"
|
||||
#include "modbusslave/slave.h"
|
||||
#include "modbusmaster/master_indexed.h"
|
||||
#include "dnp3s/dnp3.h"
|
||||
|
@ -34,6 +35,7 @@ ServiceDefinition* services[] = {
|
|||
#ifdef OPLC_DNP3_OUTSTATION
|
||||
new ServiceDefinition("dnp3s", dnp3s_service_run),
|
||||
#endif
|
||||
new ServiceDefinition("enip", enip_service_run)
|
||||
};
|
||||
|
||||
ServiceDefinition* services_find(const char* name)
|
||||
|
|
|
@ -15,12 +15,16 @@
|
|||
#ifndef RUNTIME_CORE_SERVICE_SERVICE_REGISTRY_H_
|
||||
#define RUNTIME_CORE_SERVICE_SERVICE_REGISTRY_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
*/
|
||||
|
||||
class ServiceDefinition;
|
||||
|
||||
const std::size_t MAX_SERVICE_NAME_SIZE = 512;
|
||||
|
||||
/// @brief Finds the service in the registry by the name of the service.
|
||||
/// @param name The identifier for the service.
|
||||
/// @return The service if found, or nullptr if there is no such service.
|
||||
|
|
|
@ -442,7 +442,7 @@ void generate_bool_groups(ostream& glueVars, list<IecVar>& all_vars) {
|
|||
|
||||
void generate_integrated_glue(ostream& glueVars, const list<IecVar>& all_vars) {
|
||||
glueVars << "/// The size of the array of glue variables.\n";
|
||||
glueVars << "extern std::size_t const OPLCGLUE_GLUE_SIZE(";
|
||||
glueVars << "extern const std::size_t OPLCGLUE_GLUE_SIZE(";
|
||||
glueVars << all_vars.size() << ");\n";
|
||||
|
||||
glueVars << "/// The packed glue variables.\n";
|
||||
|
|
|
@ -29,19 +29,19 @@ using namespace std;
|
|||
#define PREFIX "void glueVars()\n{\n"
|
||||
#define POSTFIX "}\n\n"
|
||||
#define EMPTY_INPUT "/// The size of the array of input variables.\n\
|
||||
extern std::size_t const OPLCGLUE_INPUT_SIZE(0);\n\
|
||||
extern const std::size_t OPLCGLUE_INPUT_SIZE(0);\n\
|
||||
GlueVariable oplc_input_vars[] = {\n\
|
||||
};\n\n"
|
||||
#define EMPTY_OUTPUT "/// The size of the array of output variables.\n\
|
||||
extern std::size_t const OPLCGLUE_OUTPUT_SIZE(0);\n\
|
||||
extern const std::size_t OPLCGLUE_OUTPUT_SIZE(0);\n\
|
||||
GlueVariable oplc_output_vars[] = {\n\
|
||||
};\n\n"
|
||||
#define EMPTY_INPUT_BOOL "/// Size of the array of input boolean variables.\n\
|
||||
extern std::size_t const OPLCGLUE_INPUT_BOOL_SIZE(0);\n\
|
||||
extern const std::size_t OPLCGLUE_INPUT_BOOL_SIZE(0);\n\
|
||||
GlueBoolGroup oplc_bool_input_vars[] = {\n\
|
||||
};\n\n"
|
||||
#define EMPTY_OUTPUT_BOOL "/// Size of the array of output boolean variables.\n\
|
||||
extern std::size_t const OPLCGLUE_OUTPUT_BOOL_SIZE(0);\n\
|
||||
extern const std::size_t OPLCGLUE_OUTPUT_BOOL_SIZE(0);\n\
|
||||
GlueBoolGroup oplc_bool_output_vars[] = {\n\
|
||||
};\n\n"
|
||||
#define GLUE_PREFIX "/// The size of the array of glue variables.\n"
|
||||
|
@ -79,7 +79,7 @@ SCENARIO("", "") {
|
|||
"GlueBoolGroup ___IG0 { .index=0, .values={ __IX0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, } };\n"
|
||||
"GlueBoolGroup* __IG0(&___IG0);\n"
|
||||
GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_BIT, 0, 0, IECVT_BOOL, __IG0 },\n"
|
||||
|
@ -94,7 +94,7 @@ SCENARIO("", "") {
|
|||
"GlueBoolGroup ___QG0 { .index=0, .values={ __QX0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, } };\n"
|
||||
"GlueBoolGroup* __QG0(&___QG0);\n"
|
||||
GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_OUT, IECLST_BIT, 0, 0, IECVT_BOOL, __QG0 },\n"
|
||||
|
@ -111,7 +111,7 @@ SCENARIO("", "") {
|
|||
"GlueBoolGroup ___QG1 { .index=1, .values={ nullptr, nullptr, nullptr, __QX1_3, nullptr, nullptr, nullptr, nullptr, } };\n"
|
||||
"GlueBoolGroup* __QG1(&___QG1);\n"
|
||||
GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(2);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(2);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_OUT, IECLST_BIT, 0, 0, IECVT_BOOL, __QG0 },\n"
|
||||
|
@ -124,7 +124,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(BYTE,__IB0,I,B,0)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX"\tbyte_input[0] = __IB0;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_BYTE, 0, 0, IECVT_BYTE, __IB0 },\n"
|
||||
|
@ -136,7 +136,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(SINT,__IB1,I,B,1)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tbyte_input[1] = __IB1;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_BYTE, 1, 0, IECVT_SINT, __IB1 },\n"
|
||||
|
@ -148,7 +148,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(SINT,__QB1,Q,B,1)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tbyte_output[1] = __QB1;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_OUT, IECLST_BYTE, 1, 0, IECVT_SINT, __QB1 },\n"
|
||||
|
@ -160,7 +160,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(USINT,__IB2,I,B,2)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tbyte_input[2] = __IB2;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_BYTE, 2, 0, IECVT_USINT, __IB2 },\n"
|
||||
|
@ -172,7 +172,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(WORD,__IW0,I,W,0)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tint_input[0] = __IW0;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_WORD, 0, 0, IECVT_WORD, __IW0 },\n"
|
||||
|
@ -184,7 +184,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(WORD,__QW0,Q,W,0)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tint_output[0] = __QW0;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_OUT, IECLST_WORD, 0, 0, IECVT_WORD, __QW0 },\n"
|
||||
|
@ -196,7 +196,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(INT,__IW1,I,W,1)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tint_input[1] = __IW1;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_WORD, 1, 0, IECVT_INT, __IW1 },\n"
|
||||
|
@ -209,7 +209,7 @@ SCENARIO("", "") {
|
|||
generate_body(input_stream, output_stream, digest);
|
||||
|
||||
const char* expected = PREFIX "\tint_input[2] = __IW2;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_WORD, 2, 0, IECVT_UINT, __IW2 },\n"
|
||||
|
@ -223,7 +223,7 @@ SCENARIO("", "") {
|
|||
|
||||
// Note that the type-separate glue does not support REAL types
|
||||
const char* expected = PREFIX POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(2);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(2);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_IN, IECLST_DOUBLEWORD, 0, 0, IECVT_REAL, __ID0 },\n"
|
||||
|
@ -236,7 +236,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(INT,__MW2,M,W,2)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tint_memory[2] = __MW2;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_MEM, IECLST_WORD, 2, 0, IECVT_INT, __MW2 },\n"
|
||||
|
@ -248,7 +248,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(DWORD,__MD2,M,D,2)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tdint_memory[2] = (IEC_DINT *)__MD2;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_MEM, IECLST_DOUBLEWORD, 2, 0, IECVT_DWORD, __MD2 },\n"
|
||||
|
@ -260,7 +260,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1,M,L,1)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tlint_memory[1] = (IEC_LINT *)__ML1;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_MEM, IECLST_LONGWORD, 1, 0, IECVT_LINT, __ML1 },\n"
|
||||
|
@ -272,7 +272,7 @@ SCENARIO("", "") {
|
|||
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1024,M,L,1024)");
|
||||
generate_body(input_stream, output_stream, digest);
|
||||
const char* expected = PREFIX "\tspecial_functions[0] = (IEC_LINT *)__ML1024;\n" POSTFIX GLUE_PREFIX
|
||||
"extern std::size_t const OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"extern const std::size_t OPLCGLUE_GLUE_SIZE(1);\n"
|
||||
"/// The packed glue variables.\n"
|
||||
"extern const GlueVariable oplc_glue_vars[] = {\n"
|
||||
" { IECLDT_MEM, IECLST_LONGWORD, 1024, 0, IECVT_LINT, __ML1024 },\n"
|
||||
|
|
|
@ -167,7 +167,7 @@ class runtime:
|
|||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(('localhost', 43628))
|
||||
s.send(b'start_modbus(' + str(port_num) + ')\n')
|
||||
s.send(b'start_modbusslave(' + str(port_num) + ')\n')
|
||||
data = s.recv(1000)
|
||||
s.close()
|
||||
except:
|
||||
|
@ -178,7 +178,7 @@ class runtime:
|
|||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(('localhost', 43628))
|
||||
s.send(b'stop_modbus()\n')
|
||||
s.send(b'stop_modbusslave()\n')
|
||||
data = s.recv(1000)
|
||||
s.close()
|
||||
except:
|
||||
|
@ -189,7 +189,7 @@ class runtime:
|
|||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(('localhost', 43628))
|
||||
s.send(b'start_dnp3(' + str(port_num) + ')\n')
|
||||
s.send(b'start_dnp3s(' + str(port_num) + ')\n')
|
||||
data = s.recv(1000)
|
||||
s.close()
|
||||
except:
|
||||
|
@ -200,7 +200,7 @@ class runtime:
|
|||
try:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.connect(('localhost', 43628))
|
||||
s.send(b'stop_dnp3()\n')
|
||||
s.send(b'stop_dnp3s()\n')
|
||||
data = s.recv(1000)
|
||||
s.close()
|
||||
except:
|
||||
|
|
Loading…
Reference in New Issue