Merge pull request #101 from smartergridsolutions/feature/PR-786
Try to follow a consistent code style across recently contributed code
This commit is contained in:
commit
70ed7d68b2
|
@ -41,10 +41,9 @@ struct PlcConfig {
|
|||
/// Handle reading values from the configuration file
|
||||
int config_handler(void* user_data, const char* section,
|
||||
const char* name, const char* value) {
|
||||
|
||||
auto config = reinterpret_cast<PlcConfig*>(user_data);
|
||||
|
||||
if (ini_matches("logging", "level", section, name)) {
|
||||
if (oplc::ini_matches("logging", "level", section, name)) {
|
||||
if (strcmp(value, "trace") == 0) {
|
||||
spdlog::set_level(spdlog::level::trace);
|
||||
} else if (strcmp(value, "debug") == 0) {
|
||||
|
@ -56,11 +55,15 @@ int config_handler(void* user_data, const char* section,
|
|||
} else if (strcmp(value, "error") == 0) {
|
||||
spdlog::set_level(spdlog::level::err);
|
||||
}
|
||||
} else if (strcmp("enabled", name) == 0 && ini_atob(value) && services_find(section)) {
|
||||
} else if (strcmp("enabled", name) == 0
|
||||
&& oplc::ini_atob(value)
|
||||
&& services_find(section)) {
|
||||
// This is the name of service that we can enable, so add it
|
||||
// to the list of services that we will enable.
|
||||
config->services.push_back(section);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Initialize the PLC runtime
|
||||
|
@ -130,16 +133,14 @@ void bootstrap() {
|
|||
struct sched_param sp;
|
||||
sp.sched_priority = 30;
|
||||
spdlog::info("Setting main thread priority to RT");
|
||||
if(pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp))
|
||||
{
|
||||
spdlog::warn("WARNING: Failed to set main thread to real-time priority");
|
||||
if (pthread_setschedparam(pthread_self(), SCHED_FIFO, &sp)) {
|
||||
spdlog::warn("Failed to set main thread to real-time priority");
|
||||
}
|
||||
|
||||
// Lock memory to ensure no swapping is done.
|
||||
spdlog::info("Locking main thread memory");
|
||||
if(mlockall(MCL_FUTURE|MCL_CURRENT))
|
||||
{
|
||||
spdlog::warn("WARNING: Failed to lock memory");
|
||||
if (mlockall(MCL_FUTURE|MCL_CURRENT)) {
|
||||
spdlog::warn("Failed to lock memory");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -150,8 +151,7 @@ void bootstrap() {
|
|||
// Our next step here is to start the main loop, so start any
|
||||
// services that we want now.
|
||||
|
||||
for (auto it = config.services.begin(); it != config.services.end(); ++it)
|
||||
{
|
||||
for (auto it = config.services.begin(); it != config.services.end(); ++it) {
|
||||
const char* service_config = "";
|
||||
ServiceDefinition* def = services_find(it->c_str());
|
||||
def->start(service_config);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <tuple>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <asiodnp3/DNP3Manager.h>
|
||||
#include <asiodnp3/PrintingSOEHandler.h>
|
||||
|
@ -73,18 +74,15 @@ using namespace asiodnp3;
|
|||
/// Implements the ILogHandler interface from OpenDNP3 to forward log messages
|
||||
/// from OpenDNP3 to spdlog. This allows those log messages to be accessed
|
||||
/// via the log message API.
|
||||
class Dnp3ToSpdLogger final : public openpal::ILogHandler, private openpal::Uncopyable
|
||||
{
|
||||
public:
|
||||
|
||||
class Dnp3ToSpdLogger final : public openpal::ILogHandler, private openpal::Uncopyable {
|
||||
public:
|
||||
virtual void Log(const openpal::LogEntry& entry) override {
|
||||
spdlog::info("{}", entry.message);
|
||||
}
|
||||
|
||||
static std::shared_ptr<openpal::ILogHandler>Create()
|
||||
{
|
||||
static std::shared_ptr<openpal::ILogHandler>Create() {
|
||||
return std::make_shared<Dnp3ToSpdLogger>();
|
||||
};
|
||||
}
|
||||
|
||||
Dnp3ToSpdLogger() {}
|
||||
};
|
||||
|
@ -199,7 +197,7 @@ void bind_variables(const vector<string>& binding_defs,
|
|||
|
||||
const GlueVariable* var = binding.find(name);
|
||||
if (!var) {
|
||||
spdlog::error("Unable to bind DNP3 location {} because it is not defined in the application", name);
|
||||
spdlog::error("Unable to bind DNP3 location {} because it is not defined in the application", name);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -316,7 +314,7 @@ int dnp3s_cfg_handler(void* user_data, const char* section,
|
|||
config->link.KeepAliveTimeout = openpal::TimeDuration::Seconds(atoi(value));
|
||||
}
|
||||
} else if (strcmp(name, "enable_unsolicited") == 0) {
|
||||
config->outstation.params.allowUnsolicited = ini_atob(value);
|
||||
config->outstation.params.allowUnsolicited = oplc::ini_atob(value);
|
||||
} else if (strcmp(name, "select_timeout") == 0) {
|
||||
config->outstation.params.selectTimeout = openpal::TimeDuration::Seconds(atoi(value));
|
||||
} else if (strcmp(name, "max_controls_per_request") == 0) {
|
||||
|
@ -358,7 +356,8 @@ OutstationStackConfig dnp3_create_config(istream& cfg_stream,
|
|||
// everything into a map, then get the database size, and finally
|
||||
// process the remaining items
|
||||
Dnp3Config dnp3_config;
|
||||
ini_parse_stream(istream_fgets, &cfg_stream, dnp3s_cfg_handler, &dnp3_config);
|
||||
ini_parse_stream(oplc::istream_fgets, &cfg_stream,
|
||||
dnp3s_cfg_handler, &dnp3_config);
|
||||
|
||||
// We need to know the size of the DNP3 database (the size of each of the
|
||||
// groups) before we can create the configuration. We can figure that out
|
||||
|
@ -368,15 +367,14 @@ OutstationStackConfig dnp3_create_config(istream& cfg_stream,
|
|||
analog_commands, measurements);
|
||||
|
||||
auto config = asiodnp3::OutstationStackConfig(DatabaseSizes(
|
||||
measurements.group_size(1), // Binary
|
||||
measurements.group_size(3), // Double binary
|
||||
measurements.group_size(30), // Analog
|
||||
measurements.group_size(20), // Counter
|
||||
measurements.group_size(21), // Frozen counter
|
||||
measurements.group_size(10), // Binary output status
|
||||
measurements.group_size(40), // Analog output status
|
||||
measurements.group_size(50) // Time and interval
|
||||
));
|
||||
measurements.group_size(1), // Binary
|
||||
measurements.group_size(3), // Double binary
|
||||
measurements.group_size(30), // Analog
|
||||
measurements.group_size(20), // Counter
|
||||
measurements.group_size(21), // Frozen counter
|
||||
measurements.group_size(10), // Binary output status
|
||||
measurements.group_size(40), // Analog output status
|
||||
measurements.group_size(50))); // Time and interval
|
||||
|
||||
config.outstation = dnp3_config.outstation;
|
||||
config.link = dnp3_config.link;
|
||||
|
|
|
@ -12,16 +12,19 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_DNP3S_DNP3_H_
|
||||
#define CORE_DNP3S_DNP3_H_
|
||||
#ifndef RUNTIME_CORE_DNP3S_DNP3_H_
|
||||
#define RUNTIME_CORE_DNP3S_DNP3_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "ini_util.h"
|
||||
|
||||
namespace asiodnp3 {
|
||||
class OutstationStackConfig;
|
||||
}
|
||||
|
@ -100,20 +103,23 @@ struct Dnp3MappedGroup {
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief The mapping of glue variables into this DNP3 outstation.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
struct Dnp3BoundGlueVariables {
|
||||
class Dnp3BoundGlueVariables {
|
||||
public:
|
||||
Dnp3BoundGlueVariables(
|
||||
std::mutex* buffer_lock,
|
||||
std::uint16_t binary_commands_size,
|
||||
std::uint16_t analog_commands_size,
|
||||
std::uint16_t measurements_size
|
||||
) :
|
||||
|
||||
std::uint16_t measurements_size) :
|
||||
buffer_lock(buffer_lock),
|
||||
binary_commands({ .size=0, .items=nullptr }),
|
||||
analog_commands({ .size=0, .items=nullptr }),
|
||||
measurements({ .size=0, .items=nullptr })
|
||||
binary_commands({ .size = 0, .items = nullptr }),
|
||||
analog_commands({ .size = 0, .items = nullptr }),
|
||||
measurements({ .size = 0, .items = nullptr })
|
||||
{}
|
||||
|
||||
private:
|
||||
Dnp3BoundGlueVariables(Dnp3BoundGlueVariables&);
|
||||
|
||||
public:
|
||||
/// @brief Mutex for the glue variables associated with this structures.
|
||||
std::mutex* buffer_lock;
|
||||
|
||||
|
@ -156,7 +162,7 @@ asiodnp3::OutstationStackConfig dnp3_create_config(std::istream& cfg_stream,
|
|||
/// this signal is false.
|
||||
/// @param glue_variables The glue variables that may be bound into this
|
||||
/// server.
|
||||
void dnp3s_start_server(std::unique_ptr<std::istream, std::function<void(std::istream*)>>& cfg_stream,
|
||||
void dnp3s_start_server(oplc::config_stream& cfg_stream,
|
||||
const char* cfg_overrides,
|
||||
volatile bool& run,
|
||||
const GlueVariablesBinding& glue_variables);
|
||||
|
@ -176,4 +182,4 @@ void dnp3s_service_run(const GlueVariablesBinding& binding,
|
|||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_DNP3S_DNP3_H_
|
||||
#endif // RUNTIME_CORE_DNP3S_DNP3_H_
|
||||
|
|
|
@ -121,7 +121,6 @@ uint32_t Dnp3Publisher::ExchangeGlue() {
|
|||
} else {
|
||||
builder.Update(BinaryOutputStatus(*(bool_group->values[0])), point_index_number);
|
||||
}
|
||||
|
||||
} else if (group == GROUP_ANALOG_INPUT || group == GROUP_ANALOG_OUTPUT_STATUS) {
|
||||
double double_val = cast_variable<double>(var);
|
||||
if (group == GROUP_ANALOG_INPUT) {
|
||||
|
@ -129,7 +128,6 @@ uint32_t Dnp3Publisher::ExchangeGlue() {
|
|||
} else {
|
||||
builder.Update(AnalogOutputStatus(double_val), point_index_number);
|
||||
}
|
||||
|
||||
} else if (group == GROUP_COUNTER || group == GROUP_FROZEN_COUNTER) {
|
||||
uint32_t int_val = cast_variable<uint32_t>(var);
|
||||
if (group == GROUP_COUNTER) {
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_DNP3S_DNP3_PUBLISHER_H_
|
||||
#define CORE_DNP3s_DNP3_PUBLISHER_H_
|
||||
#ifndef RUNTIME_CORE_DNP3S_DNP3_PUBLISHER_H_
|
||||
#define RUNTIME_CORE_DNP3s_DNP3_PUBLISHER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
@ -60,6 +60,6 @@ class Dnp3Publisher {
|
|||
const Dnp3MappedGroup& measurements;
|
||||
};
|
||||
|
||||
#endif // CORE_DNP3S_DNP3_PUBLISHER_H_
|
||||
/** @}*/
|
||||
|
||||
/** @}*/
|
||||
#endif // RUNTIME_CORE_DNP3S_DNP3_PUBLISHER_H_
|
||||
|
|
|
@ -300,4 +300,4 @@ void Dnp3Receiver::End() {
|
|||
|
||||
#endif // OPLC_DNP3_OUTSTATION
|
||||
|
||||
/** @}*/
|
||||
/** @}*/
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_DNP3_DNP3S_RECEIVER_H_
|
||||
#define CORE_DNP3_DNP3S_RECEIVER_H_
|
||||
#ifndef RUNTIME_CORE_DNP3_DNP3S_RECEIVER_H_
|
||||
#define RUNTIME_CORE_DNP3_DNP3S_RECEIVER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <opendnp3/outstation/SimpleCommandHandler.h>
|
||||
|
@ -84,6 +84,6 @@ class Dnp3Receiver : public opendnp3::ICommandHandler {
|
|||
CacheItem<double>* const analog_commands_cache;
|
||||
};
|
||||
|
||||
#endif // CORE_DNP3S_DNP3_RECEIVER_H_
|
||||
|
||||
/** @}*/
|
||||
|
||||
#endif // RUNTIME_CORE_DNP3S_DNP3_RECEIVER_H_
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,7 +35,8 @@ const GlueVariable* GlueVariablesBinding::find(IecLocationDirection dir,
|
|||
uint8_t lsi) const {
|
||||
for (uint16_t i = 0; i < this->size; ++i) {
|
||||
const GlueVariable& cur_var = glue_variables[i];
|
||||
if (cur_var.dir == dir && cur_var.size == size && cur_var.msi == msi && cur_var.lsi == lsi) {
|
||||
if (cur_var.dir == dir && cur_var.size == size
|
||||
&& cur_var.msi == msi && cur_var.lsi == lsi) {
|
||||
return &glue_variables[i];
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +86,7 @@ const GlueVariable* GlueVariablesBinding::find(const string& location) const {
|
|||
}
|
||||
|
||||
char* end_msi;
|
||||
long msi = strtol(location.c_str() + 3, &end_msi, 10);
|
||||
uint16_t msi = strtol(location.c_str() + 3, &end_msi, 10);
|
||||
|
||||
// Do we have more characters left in the string to read for lsi?
|
||||
size_t start_lsi = end_msi + 1 - location.c_str();
|
||||
|
@ -94,7 +95,7 @@ const GlueVariable* GlueVariablesBinding::find(const string& location) const {
|
|||
}
|
||||
|
||||
char* end_lsi;
|
||||
long lsi = strtol(end_msi + 1, &end_lsi, 10);
|
||||
uint8_t lsi = strtol(end_msi + 1, &end_lsi, 10);
|
||||
|
||||
return find(direction, size, msi, lsi);
|
||||
}
|
||||
|
|
|
@ -5,20 +5,21 @@
|
|||
// 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.
|
||||
|
||||
#ifndef CORE_GLUE_H
|
||||
#define CORE_GLUE_H
|
||||
#ifndef RUNTIME_CORE_GLUE_H_
|
||||
#define RUNTIME_CORE_GLUE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "iec_types.h"
|
||||
|
||||
|
@ -84,7 +85,7 @@ enum IecGlueValueType {
|
|||
/// and have a way to indicate a value that is not assigned a type.
|
||||
IECVT_UNASSIGNED
|
||||
};
|
||||
#endif // OPLC_IEC_GLUE_VALUE_TYPE
|
||||
#endif // OPLC_IEC_GLUE_VALUE_TYPE
|
||||
|
||||
#ifndef OPLC_GLUE_BOOL_GROUP
|
||||
#define OPLC_GLUE_BOOL_GROUP
|
||||
|
@ -102,14 +103,14 @@ struct GlueBoolGroup {
|
|||
/// value at the index points to nullptr.
|
||||
IEC_BOOL* values[8];
|
||||
};
|
||||
#endif // OPLC_GLUE_BOOL_GROUP
|
||||
#endif // OPLC_GLUE_BOOL_GROUP
|
||||
|
||||
#ifndef OPLC_GLUE_VARIABLE
|
||||
#define OPLC_GLUE_VARIABLE
|
||||
|
||||
|
||||
/// @brief Defines the mapping for a glued variable. This defines a simple,
|
||||
/// space efficient lookup table. It has all of the mapping information that
|
||||
/// space efficient lookup table. It has all of the mapping information that
|
||||
/// you need to find the variable based on the location name (e.g. %IB1.1).
|
||||
/// While this is space efficient, this should be searched once to construct a
|
||||
/// fast lookup into this table used for the remainder of the application
|
||||
|
@ -142,8 +143,7 @@ struct GlueVariable {
|
|||
/// allows a straightforward way to inject definitions into tests, so it is
|
||||
/// preferred to use this structure rather than globals.
|
||||
class GlueVariablesBinding {
|
||||
|
||||
public:
|
||||
public:
|
||||
/// Initialize a new instance of the glue bindings.
|
||||
/// @param buffer_lock A lock for accessing the value part of bindings
|
||||
/// @param size The number of bindings we have
|
||||
|
@ -159,11 +159,11 @@ class GlueVariablesBinding {
|
|||
checksum(checksum)
|
||||
{}
|
||||
|
||||
private:
|
||||
private:
|
||||
// Don't allow copying of the bindings.
|
||||
GlueVariablesBinding(const GlueVariablesBinding& copy);
|
||||
|
||||
public:
|
||||
public:
|
||||
/// @brief Mutex for the glue variables
|
||||
std::mutex* buffer_lock;
|
||||
|
||||
|
@ -189,9 +189,10 @@ class GlueVariablesBinding {
|
|||
|
||||
/// @brief Find a glue variable based on the location of the variable, for
|
||||
/// example %IX0.1
|
||||
/// @param loc The location name to find.
|
||||
/// @return the variable or null if there is no variable that matches all
|
||||
/// criteria in the specification.
|
||||
const GlueVariable* find(const std::string& location) const;
|
||||
const GlueVariable* find(const std::string& loc) const;
|
||||
|
||||
/// @brief Find the maximum most significant index for glued variables
|
||||
/// that match the specified type and direction.
|
||||
|
@ -203,6 +204,6 @@ class GlueVariablesBinding {
|
|||
IecLocationDirection dir) const;
|
||||
};
|
||||
|
||||
#endif // CORE_GLUE_H
|
||||
#endif // RUNTIME_CORE_GLUE_H_
|
||||
|
||||
/** @}*/
|
||||
|
|
|
@ -60,17 +60,17 @@ void finalizeHardware()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void updateBuffersIn()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(bufferLock); //lock mutex
|
||||
std::lock_guard<std::mutex> lock(bufferLock); //lock mutex
|
||||
|
||||
/*********READING AND WRITING TO I/O**************
|
||||
/*********READING AND WRITING TO I/O**************
|
||||
|
||||
*bool_input[0][0] = read_digital_input(0);
|
||||
write_digital_output(0, *bool_output[0][0]);
|
||||
*bool_input[0][0] = read_digital_input(0);
|
||||
write_digital_output(0, *bool_output[0][0]);
|
||||
|
||||
*int_input[0] = read_analog_input(0);
|
||||
write_analog_output(0, *int_output[0]);
|
||||
*int_input[0] = read_analog_input(0);
|
||||
write_analog_output(0, *int_output[0]);
|
||||
|
||||
**************************************************/
|
||||
**************************************************/
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -82,17 +82,17 @@ void updateBuffersIn()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void updateBuffersOut()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(bufferLock); //lock mutex
|
||||
std::lock_guard<std::mutex> lock(bufferLock); //lock mutex
|
||||
|
||||
/*********READING AND WRITING TO I/O**************
|
||||
/*********READING AND WRITING TO I/O**************
|
||||
|
||||
*bool_input[0][0] = read_digital_input(0);
|
||||
write_digital_output(0, *bool_output[0][0]);
|
||||
*bool_input[0][0] = read_digital_input(0);
|
||||
write_digital_output(0, *bool_output[0][0]);
|
||||
|
||||
*int_input[0] = read_analog_input(0);
|
||||
write_analog_output(0, *int_output[0]);
|
||||
*int_input[0] = read_analog_input(0);
|
||||
write_analog_output(0, *int_output[0]);
|
||||
|
||||
**************************************************/
|
||||
**************************************************/
|
||||
}
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -12,15 +12,19 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_INI_UTIL_H_
|
||||
#define CORE_INI_UTIL_H_
|
||||
#ifndef RUNTIME_CORE_INI_UTIL_H_
|
||||
#define RUNTIME_CORE_INI_UTIL_H_
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
|
||||
namespace oplc {
|
||||
|
||||
/// Convert a boolean value in the INI file to a boolean.
|
||||
/// The value must be "true", otherwise it is interpreted as false.
|
||||
|
@ -61,6 +65,22 @@ static char* istream_fgets(char* str, int num, void* stream) {
|
|||
return str;
|
||||
}
|
||||
|
||||
typedef std::unique_ptr<std::istream, std::function<void(std::istream*)>> config_stream;
|
||||
|
||||
/// Open the standard configuration file as an closable stream.
|
||||
/// @return A stream for the configuration file.
|
||||
inline config_stream open_config() {
|
||||
return config_stream(
|
||||
new std::ifstream("../etc/config.ini"),
|
||||
[] (std::istream* s) {
|
||||
reinterpret_cast<std::ifstream*>(s)->close();
|
||||
delete s;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace oplc
|
||||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_INI_UTIL_H_
|
||||
#endif // RUNTIME_CORE_INI_UTIL_H_
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
// 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.
|
||||
|
@ -15,13 +15,18 @@
|
|||
|
||||
|
||||
// This is the file for the interactive server. It has procedures to create
|
||||
// a socket, bind it, start network communication, and process commands. The
|
||||
// a socket, bind it, start network communication, and process commands. The
|
||||
// interactive server only responds to localhost and it is used to communicate
|
||||
// with the Python webserver GUI only.
|
||||
//
|
||||
// Thiago Alves, Jun 2018
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
|
@ -29,17 +34,14 @@
|
|||
#include <istream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "glue.h"
|
||||
#include "ini_util.h"
|
||||
#include "ladder.h"
|
||||
#include "logsink.h"
|
||||
#include "interactive_server.h"
|
||||
#include "service/service_definition.h"
|
||||
#include "service/service_registry.h"
|
||||
|
||||
|
@ -55,47 +57,46 @@ bool run_enip = 0;
|
|||
uint16_t enip_port = 44818;
|
||||
time_t start_time;
|
||||
|
||||
//Global Threads
|
||||
// Global Threads
|
||||
pthread_t enip_thread;
|
||||
|
||||
//Log Buffer
|
||||
// Log Buffer
|
||||
#define LOG_BUFFER_SIZE 1000000
|
||||
unsigned char log_buffer[LOG_BUFFER_SIZE];
|
||||
std::shared_ptr<buffered_sink> log_sink;
|
||||
|
||||
using namespace std;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// @brief Start the Enip Thread
|
||||
/// @param *arg
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
void *enipThread(void *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 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')
|
||||
{
|
||||
|
||||
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((char *)argument);
|
||||
|
||||
return atoi(reinterpret_cast<char *>(argument));
|
||||
}
|
||||
|
||||
/// Copy the configuration argument from the command into the buffer
|
||||
|
@ -106,8 +107,7 @@ int readCommandArgument(const char *command)
|
|||
/// fails, it marks the target as an empty string so it is still safe
|
||||
/// to read the string but it will be empty.
|
||||
std::int8_t copy_command_config(const char *source, char target[],
|
||||
size_t target_size)
|
||||
{
|
||||
size_t target_size) {
|
||||
// Search through source until we find the closing ")"
|
||||
size_t end_index = 0;
|
||||
while (source[end_index] != ')' && source[end_index] != '\0') {
|
||||
|
@ -138,41 +138,42 @@ std::int8_t copy_command_config(const char *source, char target[],
|
|||
/// @param port
|
||||
/// @return the file descriptor for the socket, or less than 0 if a socket
|
||||
/// if an error occurred.
|
||||
int interactive_open_socket(uint16_t port)
|
||||
{
|
||||
int interactive_open_socket(uint16_t port) {
|
||||
int socket_fd;
|
||||
struct sockaddr_in server_addr;
|
||||
|
||||
//Create TCP Socket
|
||||
// Create TCP Socket
|
||||
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socket_fd < 0)
|
||||
{
|
||||
spdlog::error("Interactive Server: error creating stream socket => {}", strerror(errno));
|
||||
if (socket_fd < 0) {
|
||||
spdlog::error("Interactive Server: error creating stream socket => {}",
|
||||
strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
//Set SO_REUSEADDR
|
||||
|
||||
// Set SO_REUSEADDR
|
||||
int enable = 1;
|
||||
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) {
|
||||
if (setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&enable, sizeof(int)) < 0) {
|
||||
perror("setsockopt(SO_REUSEADDR) failed");
|
||||
}
|
||||
|
||||
|
||||
SetSocketBlockingEnabled(socket_fd, false);
|
||||
|
||||
//Initialize Server Struct
|
||||
|
||||
// Initialize Server Struct
|
||||
memset(&server_addr, 0, sizeof(server_addr));
|
||||
server_addr.sin_family = AF_INET;
|
||||
server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||||
server_addr.sin_port = htons(port);
|
||||
|
||||
//Bind socket
|
||||
if (bind(socket_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
|
||||
{
|
||||
spdlog::error("Interactive Server: error binding socket => {}", strerror(errno));
|
||||
// Bind socket
|
||||
if (bind(socket_fd, (struct sockaddr *)&server_addr,
|
||||
sizeof(server_addr)) < 0) {
|
||||
spdlog::error("Interactive Server: error binding socket => {}",
|
||||
strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
// we accept max 5 pending connections
|
||||
// We accept max 5 pending connections
|
||||
listen(socket_fd, 5);
|
||||
spdlog::info("Interactive Server: Listening on port {}", port);
|
||||
|
||||
|
@ -186,8 +187,7 @@ int interactive_open_socket(uint16_t port)
|
|||
/// @param run A flag that is set to false when we should stop polling.
|
||||
/// @param socket_fd The socket file descriptor we are listening on.
|
||||
/// @return the client file descriptor.
|
||||
int interactive_wait_new_client(volatile bool& run, int socket_fd)
|
||||
{
|
||||
int interactive_wait_new_client(volatile bool& run, int socket_fd) {
|
||||
int client_fd;
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_len;
|
||||
|
@ -195,11 +195,12 @@ int interactive_wait_new_client(volatile bool& run, int socket_fd)
|
|||
spdlog::trace("Interactive Server: waiting for new client...");
|
||||
|
||||
client_len = sizeof(client_addr);
|
||||
while (run)
|
||||
{
|
||||
client_fd = accept(socket_fd, (struct sockaddr *)&client_addr, &client_len); //non-blocking call
|
||||
if (client_fd > 0)
|
||||
{
|
||||
while (run) {
|
||||
// Non-blocking call
|
||||
client_fd = accept(socket_fd, (struct sockaddr *)&client_addr,
|
||||
&client_len);
|
||||
|
||||
if (client_fd > 0) {
|
||||
SetSocketBlockingEnabled(client_fd, true);
|
||||
break;
|
||||
}
|
||||
|
@ -219,7 +220,7 @@ void interactive_client_command(const char* command, int client_fd) {
|
|||
|
||||
std::unique_lock<std::mutex> lock(command_mutex, std::defer_lock);
|
||||
if (!lock.try_lock()) {
|
||||
spdlog::trace("Process command skipped because already processing {}", command);
|
||||
spdlog::trace("Command skipped - already processing {}", command);
|
||||
int count_char = sprintf(command_buffer, "Another command in progress...\n");
|
||||
write(client_fd, command_buffer, count_char);
|
||||
return;
|
||||
|
@ -227,47 +228,40 @@ void interactive_client_command(const char* command, int client_fd) {
|
|||
|
||||
spdlog::trace("Process command received {}", command);
|
||||
|
||||
if (strncmp(command, "quit()", 6) == 0)
|
||||
{
|
||||
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_modbus(", 13) == 0) {
|
||||
ServiceDefinition* def = services_find("modbusslave");
|
||||
if (def && copy_command_config(command + 13, command_buffer, BUFFER_MAX_SIZE) == 0) {
|
||||
def->start(command_buffer);
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "stop_modbus()", 13) == 0)
|
||||
{
|
||||
else if (strncmp(command, "stop_modbus()", 13) == 0) {
|
||||
ServiceDefinition* def = services_find("modbusslave");
|
||||
if (def) {
|
||||
def->stop();
|
||||
}
|
||||
}
|
||||
#ifdef OPLC_DNP3_OUTSTATION
|
||||
else if (strncmp(command, "start_dnp3(", 11) == 0)
|
||||
{
|
||||
else if (strncmp(command, "start_dnp3(", 11) == 0) {
|
||||
ServiceDefinition* def = services_find("dnp3s");
|
||||
if (def && copy_command_config(command + 11, command_buffer, BUFFER_MAX_SIZE) == 0) {
|
||||
def->start(command_buffer);
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "stop_dnp3()", 11) == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (run_enip) {
|
||||
spdlog::info("EtherNet/IP server already active. Restarting on port: {}", enip_port);
|
||||
//Stop Enip server
|
||||
run_enip = 0;
|
||||
|
@ -278,8 +272,7 @@ void interactive_client_command(const char* command, int client_fd) {
|
|||
run_enip = 1;
|
||||
pthread_create(&enip_thread, NULL, enipThread, NULL);
|
||||
}
|
||||
else if (strncmp(command, "stop_enip()", 11) == 0)
|
||||
{
|
||||
else if (strncmp(command, "stop_enip()", 11) == 0) {
|
||||
spdlog::info("Issued stop_enip() command");
|
||||
if (run_enip)
|
||||
{
|
||||
|
@ -288,42 +281,37 @@ void interactive_client_command(const char* command, int client_fd) {
|
|||
spdlog::info("EtherNet/IP server was stopped");
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "start_pstorage(", 15) == 0)
|
||||
{
|
||||
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)
|
||||
{
|
||||
else if (strncmp(command, "stop_pstorage()", 15) == 0) {
|
||||
ServiceDefinition* def = services_find("pstorage");
|
||||
if (def) {
|
||||
def->stop();
|
||||
}
|
||||
}
|
||||
else if (strncmp(command, "runtime_logs()", 14) == 0)
|
||||
{
|
||||
else if (strncmp(command, "runtime_logs()", 14) == 0) {
|
||||
spdlog::debug("Issued runtime_logs() command");
|
||||
std::string data = log_sink->data();
|
||||
write(client_fd, data.c_str(), data.size());
|
||||
return;
|
||||
}
|
||||
else if (strncmp(command, "exec_time()", 11) == 0)
|
||||
{
|
||||
else if (strncmp(command, "exec_time()", 11) == 0) {
|
||||
time_t end_time;
|
||||
time(&end_time);
|
||||
int count_char = sprintf(command_buffer, "%llu\n", (unsigned long long)difftime(end_time, start_time));
|
||||
write(client_fd, command_buffer, count_char);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
int count_char = sprintf(command_buffer, "Error: unrecognized command\n");
|
||||
write(client_fd, command_buffer, count_char);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
int count_char = sprintf(command_buffer, "OK\n");
|
||||
write(client_fd, command_buffer, count_char);
|
||||
}
|
||||
|
@ -385,10 +373,10 @@ void* interactive_client_run(void* arguments) {
|
|||
/// Run the interactive server for responding to control and measurement
|
||||
/// requests through the socket API. This is the primary means for
|
||||
/// controlling the runtime.
|
||||
int8_t interactive_run(std::unique_ptr<istream, function<void(istream*)>>& cfg_stream,
|
||||
const char* cfg_overrides,
|
||||
const GlueVariablesBinding& bindings,
|
||||
volatile bool& run) {
|
||||
void interactive_run(oplc::config_stream& cfg_stream,
|
||||
const char* cfg_overrides,
|
||||
const GlueVariablesBinding& bindings,
|
||||
volatile bool& run) {
|
||||
const uint16_t port = 43628;
|
||||
int socket_fd = interactive_open_socket(port);
|
||||
|
||||
|
@ -403,14 +391,14 @@ int8_t interactive_run(std::unique_ptr<istream, function<void(istream*)>>& cfg_s
|
|||
}
|
||||
|
||||
pthread_t thread;
|
||||
auto client_args = new ClientArgs { .client_fd=client_fd, .run=&run };
|
||||
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, client_args);
|
||||
int ret = pthread_create(&thread, NULL, interactive_client_run, args);
|
||||
if (ret == 0) {
|
||||
pthread_detach(thread);
|
||||
} else {
|
||||
delete client_args;
|
||||
delete args;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -419,17 +407,11 @@ int8_t interactive_run(std::unique_ptr<istream, function<void(istream*)>>& cfg_s
|
|||
|
||||
void interactive_service_run(const GlueVariablesBinding& binding,
|
||||
volatile bool& run, const char* config) {
|
||||
unique_ptr<istream, function<void(istream*)>> cfg_stream(new ifstream("../etc/config.ini"), [](istream* s)
|
||||
{
|
||||
reinterpret_cast<ifstream*>(s)->close();
|
||||
delete s;
|
||||
});
|
||||
|
||||
auto cfg_stream = oplc::open_config();
|
||||
interactive_run(cfg_stream, config, binding, run);
|
||||
}
|
||||
|
||||
void initialize_logging(int argc,char **argv)
|
||||
{
|
||||
void initialize_logging(int argc, char **argv) {
|
||||
log_sink.reset(new buffered_sink(log_buffer, LOG_BUFFER_SIZE));
|
||||
spdlog::default_logger()->sinks().push_back(log_sink);
|
||||
}
|
||||
|
|
|
@ -6,15 +6,15 @@
|
|||
// 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.
|
||||
|
||||
#ifndef CORE_INTERACTIVE_SERVER_H_
|
||||
#define CORE_INTERACTIVE_SERVER_H_
|
||||
#ifndef RUNTIME_CORE_INTERACTIVE_SERVER_H_
|
||||
#define RUNTIME_CORE_INTERACTIVE_SERVER_H_
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
|
@ -34,4 +34,4 @@ void interactive_service_run(const GlueVariablesBinding& binding,
|
|||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_INTERACTIVE_SERVER_H_
|
||||
#endif // RUNTIME_CORE_INTERACTIVE_SERVER_H_
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// 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.
|
||||
|
@ -26,9 +26,9 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
//Internal buffers for I/O and memory. These buffers are defined in the
|
||||
//auto-generated glueVars.cpp file
|
||||
#define BUFFER_SIZE 1024
|
||||
// Internal buffers for I/O and memory. These buffers are defined in the
|
||||
// auto-generated glueVars.cpp file
|
||||
#define BUFFER_SIZE 1024
|
||||
/*********************/
|
||||
/* IEC Types defs */
|
||||
/*********************/
|
||||
|
@ -53,30 +53,30 @@ typedef uint64_t IEC_LWORD;
|
|||
typedef float IEC_REAL;
|
||||
typedef double IEC_LREAL;
|
||||
|
||||
//Booleans
|
||||
// Booleans
|
||||
extern IEC_BOOL *bool_input[BUFFER_SIZE][8];
|
||||
extern IEC_BOOL *bool_output[BUFFER_SIZE][8];
|
||||
|
||||
//Bytes
|
||||
// Bytes
|
||||
extern IEC_BYTE *byte_input[BUFFER_SIZE];
|
||||
extern IEC_BYTE *byte_output[BUFFER_SIZE];
|
||||
|
||||
//Analog I/O
|
||||
// Analog I/O
|
||||
extern IEC_UINT *int_input[BUFFER_SIZE];
|
||||
extern IEC_UINT *int_output[BUFFER_SIZE];
|
||||
|
||||
//Memory
|
||||
// Memory
|
||||
extern IEC_UINT *int_memory[BUFFER_SIZE];
|
||||
extern IEC_DINT *dint_memory[BUFFER_SIZE];
|
||||
extern IEC_LINT *lint_memory[BUFFER_SIZE];
|
||||
|
||||
//Special Functions
|
||||
// Special Functions
|
||||
extern IEC_LINT *special_functions[BUFFER_SIZE];
|
||||
|
||||
//lock for the buffer
|
||||
// lock for the buffer
|
||||
extern std::mutex bufferLock;
|
||||
|
||||
//Common task timer
|
||||
// Common task timer
|
||||
extern unsigned long long common_ticktime__;
|
||||
|
||||
struct GlueVariable;
|
||||
|
@ -88,25 +88,25 @@ extern const char OPLCGLUE_MD5_DIGEST[];
|
|||
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
//FUNCTION PROTOTYPES
|
||||
// FUNCTION PROTOTYPES
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
//MatIEC Compiler
|
||||
// MatIEC Compiler
|
||||
extern "C" {
|
||||
void config_run__(unsigned long tick);
|
||||
void config_init__(void);
|
||||
}
|
||||
//glueVars.cpp
|
||||
// glueVars.cpp
|
||||
void glueVars();
|
||||
void updateTime();
|
||||
|
||||
//hardware_layer.cpp
|
||||
// hardware_layer.cpp
|
||||
void initializeHardware();
|
||||
void finalizeHardware();
|
||||
void updateBuffersIn();
|
||||
void updateBuffersOut();
|
||||
|
||||
//custom_layer.h
|
||||
// custom_layer.h
|
||||
void initCustomLayer();
|
||||
void updateCustomIn();
|
||||
void updateCustomOut();
|
||||
|
@ -115,31 +115,31 @@ extern int ignored_bool_outputs[];
|
|||
extern int ignored_int_inputs[];
|
||||
extern int ignored_int_outputs[];
|
||||
|
||||
//main.cpp
|
||||
// main.cpp
|
||||
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
|
||||
// server.cpp
|
||||
typedef int (*process_message_fn)(unsigned char *buffer, int 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);
|
||||
bool SetSocketBlockingEnabled(int fd, bool blocking);
|
||||
|
||||
//interactive_server.cpp
|
||||
void initialize_logging(int argc,char **argv);
|
||||
// interactive_server.cpp
|
||||
void initialize_logging(int argc, char **argv);
|
||||
extern bool run_enip;
|
||||
extern time_t start_time;
|
||||
|
||||
//enip.cpp
|
||||
// enip.cpp
|
||||
int processEnipMessage(unsigned char *buffer, int buffer_size, void* user_data);
|
||||
|
||||
//pccc.cpp ADDED Ulmer
|
||||
// pccc.cpp ADDED Ulmer
|
||||
uint16_t processPCCCMessage(unsigned char *buffer, int buffer_size);
|
||||
|
||||
//modbus_master.cpp
|
||||
// modbus_master.cpp
|
||||
void initializeMB();
|
||||
void *querySlaveDevices(void *arg);
|
||||
void updateBuffersIn_MB();
|
||||
|
|
|
@ -5,15 +5,15 @@
|
|||
// 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.
|
||||
|
||||
#ifndef CORE_LOGSINK_H
|
||||
#define CORE_LOGSINK_H
|
||||
#ifndef RUNTIME_CORE_LOGSINK_H_
|
||||
#define RUNTIME_CORE_LOGSINK_H_
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <algorithm>
|
||||
|
@ -33,31 +33,29 @@
|
|||
/// use to query logs in memory so that they can be provided to a front
|
||||
/// end in an efficient (but not necessarily complete) manner.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
class buffered_sink : public spdlog::sinks::base_sink <std::mutex>
|
||||
{
|
||||
public:
|
||||
/// \brief Initialize a new instance of the sink with the provided buffer.
|
||||
///
|
||||
/// We require supplying the buffer as an argument so that in normal use
|
||||
/// this can be statically allocated, but for the purpose of testing
|
||||
/// the size is easy to configure.
|
||||
/// \param buffer The buffer to write this. this sink maintains a mutex
|
||||
/// will only read/write the buffer when the lock is acquired.
|
||||
/// \param buffer_size The number of characters in the buffer.
|
||||
buffered_sink(unsigned char* buffer, std::uint32_t buffer_size) :
|
||||
buffer(buffer),
|
||||
buffer_size(buffer_size)
|
||||
{}
|
||||
class buffered_sink : public spdlog::sinks::base_sink<std::mutex> {
|
||||
public:
|
||||
/// \brief Initialize a new instance of the sink with the provided buffer.
|
||||
///
|
||||
/// We require supplying the buffer as an argument so that in normal use
|
||||
/// this can be statically allocated, but for the purpose of testing
|
||||
/// the size is easy to configure.
|
||||
/// \param buffer The buffer to write this. this sink maintains a mutex
|
||||
/// will only read/write the buffer when the lock is acquired.
|
||||
/// \param buffer_size The number of characters in the buffer.
|
||||
buffered_sink(unsigned char* buffer, std::uint32_t buffer_size) :
|
||||
buffer(buffer),
|
||||
buffer_size(buffer_size)
|
||||
{}
|
||||
|
||||
/// \brief Gets the data from this sink.
|
||||
/// \return The data formatted as a string.
|
||||
/// \return The data formatted as a string.
|
||||
std::string data() {
|
||||
return std::string(reinterpret_cast<char*>(this->buffer));
|
||||
}
|
||||
|
||||
protected:
|
||||
void sink_it_(const spdlog::details::log_msg& msg) override
|
||||
{
|
||||
protected:
|
||||
void sink_it_(const spdlog::details::log_msg& msg) override {
|
||||
fmt::memory_buffer formatted;
|
||||
sink::formatter_->format(msg, formatted);
|
||||
|
||||
|
@ -72,28 +70,28 @@ protected:
|
|||
}
|
||||
|
||||
// Add this message onto the buffer
|
||||
std::size_t copy_len = std::min(msg_size, buffer_size - end_pos - 1);
|
||||
std::size_t copy_len = std::min(msg_size, buffer_size - end_pos - 1);
|
||||
std::memcpy(buffer + end_pos, formatted.data(), copy_len);
|
||||
end_pos += copy_len;
|
||||
// Just make sure that the string is null terminated so we don't run off the end.
|
||||
end_pos += copy_len;
|
||||
// Just make sure that the string is null terminated so we don't run
|
||||
// off the end.
|
||||
buffer[end_pos + 1] = '\0';
|
||||
}
|
||||
|
||||
void flush_() override
|
||||
{}
|
||||
|
||||
private:
|
||||
|
||||
// The current end of the string position. This is where we start inserting.
|
||||
private:
|
||||
// The current end of the string position. This is where we start inserting.
|
||||
std::size_t end_pos = 0;
|
||||
|
||||
// The buffer that we are writing to.
|
||||
// The buffer that we are writing to.
|
||||
unsigned char* const buffer;
|
||||
|
||||
// The size of the buffer in characters.
|
||||
const std::size_t buffer_size;;
|
||||
// The size of the buffer in characters.
|
||||
const std::size_t buffer_size;;
|
||||
};
|
||||
|
||||
#endif // CORE_LOGSINK_H
|
||||
|
||||
/** @}*/
|
||||
|
||||
#endif // RUNTIME_CORE_LOGSINK_H_
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
// 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.
|
||||
|
@ -41,14 +41,13 @@
|
|||
#define OPLC_CYCLE 50000000
|
||||
|
||||
extern int opterr;
|
||||
//extern int common_ticktime__;
|
||||
IEC_BOOL __DEBUG;
|
||||
|
||||
IEC_LINT cycle_counter = 0;
|
||||
|
||||
unsigned long __tick = 0;
|
||||
std::mutex bufferLock; //mutex for the internal buffers
|
||||
uint8_t run_openplc = 1; //Variable to control OpenPLC Runtime execution
|
||||
std::mutex bufferLock; // Mutex for the internal buffers
|
||||
uint8_t run_openplc = 1; // Variable to control OpenPLC Runtime execution
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Helper function - Makes the running thread sleep for the amount of
|
||||
|
@ -56,18 +55,15 @@ uint8_t run_openplc = 1; //Variable to control OpenPLC Runtime execution
|
|||
/// \param ts
|
||||
/// \param delay in milliseconds
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void sleep_until(struct timespec *ts, int delay)
|
||||
{
|
||||
void sleep_until(struct timespec *ts, int delay) {
|
||||
ts->tv_nsec += delay;
|
||||
if(ts->tv_nsec >= 1000*1000*1000)
|
||||
{
|
||||
if (ts->tv_nsec >= 1000*1000*1000) {
|
||||
ts->tv_nsec -= 1000*1000*1000;
|
||||
ts->tv_sec++;
|
||||
}
|
||||
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, ts, NULL);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Verify if pin is present in one of the ignored vectors
|
||||
/// \param
|
||||
|
@ -75,40 +71,34 @@ void sleep_until(struct timespec *ts, int delay)
|
|||
/// \param
|
||||
/// \return
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
bool pinNotPresent(int *ignored_vector, int vector_size, int pinNumber)
|
||||
{
|
||||
for (int i = 0; i < vector_size; i++)
|
||||
{
|
||||
if (ignored_vector[i] == pinNumber)
|
||||
bool pinNotPresent(int *ignored_vector, int vector_size, int pinNumber) {
|
||||
for (int i = 0; i < vector_size; i++) {
|
||||
if (ignored_vector[i] == pinNumber) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Disables all outputs
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void disableOutputs()
|
||||
{
|
||||
//Disable digital outputs
|
||||
for (int i = 0; i < BUFFER_SIZE; i++)
|
||||
{
|
||||
for (int j = 0; j < 8; j++)
|
||||
{
|
||||
void disableOutputs() {
|
||||
// Disable digital outputs
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
for (int j = 0; j < 8; j++) {
|
||||
if (bool_output[i][j] != NULL) *bool_output[i][j] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Disable byte outputs
|
||||
for (int i = 0; i < BUFFER_SIZE; i++)
|
||||
{
|
||||
|
||||
// Disable byte outputs
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
if (byte_output[i] != NULL) *byte_output[i] = 0;
|
||||
}
|
||||
|
||||
//Disable analog outputs
|
||||
for (int i = 0; i < BUFFER_SIZE; i++)
|
||||
{
|
||||
|
||||
// Disable analog outputs
|
||||
for (int i = 0; i < BUFFER_SIZE; i++) {
|
||||
if (int_output[i] != NULL) *int_output[i] = 0;
|
||||
}
|
||||
}
|
||||
|
@ -116,33 +106,34 @@ void disableOutputs()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
/// \brief Special Functions
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
void handleSpecialFunctions()
|
||||
{
|
||||
//current time [%ML1024]
|
||||
void handleSpecialFunctions() {
|
||||
// Current time [%ML1024]
|
||||
struct tm *current_time;
|
||||
time_t rawtime;
|
||||
|
||||
|
||||
tzset();
|
||||
time(&rawtime);
|
||||
current_time = localtime(&rawtime);
|
||||
|
||||
rawtime = rawtime - timezone;
|
||||
if (current_time->tm_isdst > 0) rawtime = rawtime + 3600;
|
||||
|
||||
if (special_functions[0] != NULL) *special_functions[0] = rawtime;
|
||||
|
||||
//number of cycles [%ML1025]
|
||||
cycle_counter++;
|
||||
if (special_functions[1] != NULL) *special_functions[1] = cycle_counter;
|
||||
|
||||
//comm error counter [%ML1026]
|
||||
/* Implemented in modbus_master.cpp */
|
||||
|
||||
//insert other special functions below
|
||||
rawtime = rawtime - timezone;
|
||||
if (current_time->tm_isdst > 0) {
|
||||
rawtime = rawtime + 3600;
|
||||
}
|
||||
|
||||
if (special_functions[0] != NULL) {
|
||||
*special_functions[0] = rawtime;
|
||||
}
|
||||
|
||||
// Number of cycles [%ML1025]
|
||||
cycle_counter++;
|
||||
if (special_functions[1] != NULL) {
|
||||
*special_functions[1] = cycle_counter;
|
||||
}
|
||||
|
||||
// Insert other special functions below
|
||||
}
|
||||
|
||||
int main(int argc,char **argv)
|
||||
{
|
||||
int main(int argc, char **argv) {
|
||||
initialize_logging(argc, argv);
|
||||
spdlog::info("OpenPLC Runtime starting...");
|
||||
|
||||
|
@ -153,7 +144,7 @@ int main(int argc,char **argv)
|
|||
// automatically start
|
||||
bootstrap();
|
||||
|
||||
//gets the starting point for the clock
|
||||
// Gets the starting point for the clock
|
||||
spdlog::debug("Getting current time");
|
||||
struct timespec timer_start;
|
||||
clock_gettime(CLOCK_MONOTONIC, &timer_start);
|
||||
|
@ -161,31 +152,33 @@ int main(int argc,char **argv)
|
|||
//======================================================
|
||||
// MAIN LOOP
|
||||
//======================================================
|
||||
while(run_openplc)
|
||||
{
|
||||
//make sure the buffer pointers are correct and
|
||||
//attached to the user variables
|
||||
glueVars();
|
||||
|
||||
updateBuffersIn(); //read input image
|
||||
|
||||
while (run_openplc) {
|
||||
// Read input image - this method tries to get the lock
|
||||
// so don't put it in the lock context.
|
||||
updateBuffersIn();
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
updateCustomIn();
|
||||
updateBuffersIn_MB(); //update input image table with data from slave devices
|
||||
// Update input image table with data from slave devices
|
||||
updateBuffersIn_MB();
|
||||
handleSpecialFunctions();
|
||||
config_run__(__tick++); // execute plc program logic
|
||||
// Execute plc program logic
|
||||
config_run__(__tick++);
|
||||
updateCustomOut();
|
||||
updateBuffersOut_MB(); //update slave devices with data from the output image table
|
||||
// Update slave devices with data from the output image table
|
||||
updateBuffersOut_MB();
|
||||
}
|
||||
|
||||
updateBuffersOut(); //write output image
|
||||
// Write output image - this method tries to get the lock
|
||||
// so don't put it in the lock context.
|
||||
updateBuffersOut();
|
||||
|
||||
updateTime();
|
||||
|
||||
sleep_until(&timer_start, common_ticktime__);
|
||||
}
|
||||
|
||||
|
||||
//======================================================
|
||||
// SHUTTING DOWN OPENPLC RUNTIME
|
||||
//======================================================
|
||||
|
|
|
@ -42,9 +42,9 @@
|
|||
* @{
|
||||
*/
|
||||
|
||||
#define MB_TCP 1
|
||||
#define MB_RTU 2
|
||||
#define MAX_MB_IO 400
|
||||
#define MB_TCP 1
|
||||
#define MB_RTU 2
|
||||
#define MAX_MB_IO 400
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -57,29 +57,29 @@ pthread_mutex_t ioLock;
|
|||
|
||||
struct MB_address
|
||||
{
|
||||
uint16_t start_address;
|
||||
uint16_t num_regs;
|
||||
uint16_t start_address;
|
||||
uint16_t num_regs;
|
||||
};
|
||||
|
||||
struct MB_device
|
||||
{
|
||||
modbus_t *mb_ctx;
|
||||
char dev_name[100];
|
||||
uint8_t protocol;
|
||||
char dev_address[100];
|
||||
uint16_t ip_port;
|
||||
int rtu_baud;
|
||||
char rtu_parity;
|
||||
int rtu_data_bit;
|
||||
int rtu_stop_bit;
|
||||
uint8_t dev_id;
|
||||
bool isConnected;
|
||||
modbus_t *mb_ctx;
|
||||
char dev_name[100];
|
||||
uint8_t protocol;
|
||||
char dev_address[100];
|
||||
uint16_t ip_port;
|
||||
int rtu_baud;
|
||||
char rtu_parity;
|
||||
int rtu_data_bit;
|
||||
int rtu_stop_bit;
|
||||
uint8_t dev_id;
|
||||
bool isConnected;
|
||||
|
||||
struct MB_address discrete_inputs;
|
||||
struct MB_address coils;
|
||||
struct MB_address input_registers;
|
||||
struct MB_address holding_read_registers;
|
||||
struct MB_address holding_registers;
|
||||
struct MB_address discrete_inputs;
|
||||
struct MB_address coils;
|
||||
struct MB_address input_registers;
|
||||
struct MB_address holding_read_registers;
|
||||
struct MB_address holding_registers;
|
||||
};
|
||||
|
||||
struct MB_device *mb_devices;
|
||||
|
@ -96,22 +96,22 @@ uint16_t timeout = 1000;
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void getData(char *line, char *buf, char separator1, char separator2)
|
||||
{
|
||||
int i=0, j=0;
|
||||
buf[j] = '\0';
|
||||
int i=0, j=0;
|
||||
buf[j] = '\0';
|
||||
|
||||
while (line[i] != separator1 && line[i] != '\0')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
while (line[i] != separator1 && line[i] != '\0')
|
||||
{
|
||||
i++;
|
||||
}
|
||||
i++;
|
||||
|
||||
while (line[i] != separator2 && line[i] != '\0')
|
||||
{
|
||||
buf[j] = line[i];
|
||||
i++;
|
||||
j++;
|
||||
buf[j] = '\0';
|
||||
}
|
||||
while (line[i] != separator2 && line[i] != '\0')
|
||||
{
|
||||
buf[j] = line[i];
|
||||
i++;
|
||||
j++;
|
||||
buf[j] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -121,18 +121,18 @@ void getData(char *line, char *buf, char separator1, char separator2)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
int getDeviceNumber(char *line)
|
||||
{
|
||||
char temp[5];
|
||||
int i = 0, j = 6;
|
||||
char temp[5];
|
||||
int i = 0, j = 6;
|
||||
|
||||
while (line[j] != '.')
|
||||
{
|
||||
temp[i] = line[j];
|
||||
i++;
|
||||
j++;
|
||||
temp[i] = '\0';
|
||||
}
|
||||
while (line[j] != '.')
|
||||
{
|
||||
temp[i] = line[j];
|
||||
i++;
|
||||
j++;
|
||||
temp[i] = '\0';
|
||||
}
|
||||
|
||||
return(atoi(temp));
|
||||
return(atoi(temp));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -142,21 +142,21 @@ int getDeviceNumber(char *line)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void getFunction(char *line, char *parameter)
|
||||
{
|
||||
int i = 0, j = 0;
|
||||
int i = 0, j = 0;
|
||||
|
||||
while (line[j] != '.')
|
||||
{
|
||||
j++;
|
||||
}
|
||||
j++;
|
||||
while (line[j] != '.')
|
||||
{
|
||||
j++;
|
||||
}
|
||||
j++;
|
||||
|
||||
while (line[j] != ' ' && line[j] != '=' && line[j] != '(')
|
||||
{
|
||||
parameter[i] = line[j];
|
||||
i++;
|
||||
j++;
|
||||
parameter[i] = '\0';
|
||||
}
|
||||
while (line[j] != ' ' && line[j] != '=' && line[j] != '(')
|
||||
{
|
||||
parameter[i] = line[j];
|
||||
i++;
|
||||
j++;
|
||||
parameter[i] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -164,190 +164,190 @@ void getFunction(char *line, char *parameter)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void parseConfig()
|
||||
{
|
||||
string line;
|
||||
char line_str[1024];
|
||||
ifstream cfgfile("mbconfig.cfg");
|
||||
string line;
|
||||
char line_str[1024];
|
||||
ifstream cfgfile("mbconfig.cfg");
|
||||
|
||||
if (cfgfile.is_open())
|
||||
{
|
||||
while (getline(cfgfile, line))
|
||||
{
|
||||
strncpy(line_str, line.c_str(), 1024);
|
||||
if (line_str[0] != '#' && strlen(line_str) > 1)
|
||||
{
|
||||
if (!strncmp(line_str, "Num_Devices", 11))
|
||||
{
|
||||
char temp_buffer[5];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
num_devices = atoi(temp_buffer);
|
||||
mb_devices = (struct MB_device *)malloc(num_devices*sizeof(struct MB_device));
|
||||
}
|
||||
if (cfgfile.is_open())
|
||||
{
|
||||
while (getline(cfgfile, line))
|
||||
{
|
||||
strncpy(line_str, line.c_str(), 1024);
|
||||
if (line_str[0] != '#' && strlen(line_str) > 1)
|
||||
{
|
||||
if (!strncmp(line_str, "Num_Devices", 11))
|
||||
{
|
||||
char temp_buffer[5];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
num_devices = atoi(temp_buffer);
|
||||
mb_devices = (struct MB_device *)malloc(num_devices*sizeof(struct MB_device));
|
||||
}
|
||||
else if (!strncmp(line_str, "Polling_Period", 14))
|
||||
{
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
polling_period = atoi(temp_buffer);
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
polling_period = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(line_str, "Timeout", 7))
|
||||
{
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
timeout = atoi(temp_buffer);
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
timeout = atoi(temp_buffer);
|
||||
}
|
||||
|
||||
else if (!strncmp(line_str, "device", 6))
|
||||
{
|
||||
int deviceNumber = getDeviceNumber(line_str);
|
||||
char functionType[100];
|
||||
getFunction(line_str, functionType);
|
||||
else if (!strncmp(line_str, "device", 6))
|
||||
{
|
||||
int deviceNumber = getDeviceNumber(line_str);
|
||||
char functionType[100];
|
||||
getFunction(line_str, functionType);
|
||||
|
||||
if (!strncmp(functionType, "name", 4))
|
||||
{
|
||||
getData(line_str, mb_devices[deviceNumber].dev_name, '"', '"');
|
||||
}
|
||||
else if (!strncmp(functionType, "protocol", 8))
|
||||
{
|
||||
char temp_buffer[5];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
if (!strncmp(functionType, "name", 4))
|
||||
{
|
||||
getData(line_str, mb_devices[deviceNumber].dev_name, '"', '"');
|
||||
}
|
||||
else if (!strncmp(functionType, "protocol", 8))
|
||||
{
|
||||
char temp_buffer[5];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
|
||||
if (!strncmp(temp_buffer, "TCP", 3))
|
||||
mb_devices[deviceNumber].protocol = MB_TCP;
|
||||
else if (!strncmp(temp_buffer, "RTU", 3))
|
||||
mb_devices[deviceNumber].protocol = MB_RTU;
|
||||
}
|
||||
else if (!strncmp(functionType, "slave_id", 8))
|
||||
{
|
||||
char temp_buffer[5];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].dev_id = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "address", 7))
|
||||
{
|
||||
getData(line_str, mb_devices[deviceNumber].dev_address, '"', '"');
|
||||
}
|
||||
else if (!strncmp(functionType, "IP_Port", 7))
|
||||
{
|
||||
char temp_buffer[6];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].ip_port = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Baud_Rate", 13))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_baud = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Parity", 10))
|
||||
{
|
||||
char temp_buffer[3];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_parity = temp_buffer[0];
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Data_Bits", 13))
|
||||
{
|
||||
char temp_buffer[6];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_data_bit = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Stop_Bits", 13))
|
||||
{
|
||||
char temp_buffer[20];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_stop_bit = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Discrete_Inputs_Start", 21))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].discrete_inputs.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Discrete_Inputs_Size", 20))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].discrete_inputs.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Coils_Start", 11))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].coils.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Coils_Size", 10))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].coils.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Input_Registers_Start", 21))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].input_registers.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Input_Registers_Size", 20))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].input_registers.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Read_Start", 28))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_read_registers.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Read_Size", 27))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_read_registers.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Start", 23))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_registers.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Size", 22))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_registers.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!strncmp(temp_buffer, "TCP", 3))
|
||||
mb_devices[deviceNumber].protocol = MB_TCP;
|
||||
else if (!strncmp(temp_buffer, "RTU", 3))
|
||||
mb_devices[deviceNumber].protocol = MB_RTU;
|
||||
}
|
||||
else if (!strncmp(functionType, "slave_id", 8))
|
||||
{
|
||||
char temp_buffer[5];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].dev_id = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "address", 7))
|
||||
{
|
||||
getData(line_str, mb_devices[deviceNumber].dev_address, '"', '"');
|
||||
}
|
||||
else if (!strncmp(functionType, "IP_Port", 7))
|
||||
{
|
||||
char temp_buffer[6];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].ip_port = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Baud_Rate", 13))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_baud = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Parity", 10))
|
||||
{
|
||||
char temp_buffer[3];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_parity = temp_buffer[0];
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Data_Bits", 13))
|
||||
{
|
||||
char temp_buffer[6];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_data_bit = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "RTU_Stop_Bits", 13))
|
||||
{
|
||||
char temp_buffer[20];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].rtu_stop_bit = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Discrete_Inputs_Start", 21))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].discrete_inputs.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Discrete_Inputs_Size", 20))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].discrete_inputs.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Coils_Start", 11))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].coils.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Coils_Size", 10))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].coils.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Input_Registers_Start", 21))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].input_registers.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Input_Registers_Size", 20))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].input_registers.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Read_Start", 28))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_read_registers.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Read_Size", 27))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_read_registers.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Start", 23))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_registers.start_address = atoi(temp_buffer);
|
||||
}
|
||||
else if (!strncmp(functionType, "Holding_Registers_Size", 22))
|
||||
{
|
||||
char temp_buffer[10];
|
||||
getData(line_str, temp_buffer, '"', '"');
|
||||
mb_devices[deviceNumber].holding_registers.num_regs = atoi(temp_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
spdlog::info("Skipping configuration of Slave Devices (mbconfig.cfg file not found)");
|
||||
spdlog::info("Skipping configuration of Slave Devices (mbconfig.cfg file not found)");
|
||||
}
|
||||
|
||||
//Parser Debug
|
||||
///*
|
||||
for (int i = 0; i < num_devices; i++)
|
||||
{
|
||||
printf("Device %d\n", i);
|
||||
printf("Name: %s\n", mb_devices[i].dev_name);
|
||||
printf("Protocol: %d\n", mb_devices[i].protocol);
|
||||
printf("Address: %s\n", mb_devices[i].dev_address);
|
||||
printf("IP Port: %d\n", mb_devices[i].ip_port);
|
||||
printf("Baud rate: %d\n", mb_devices[i].rtu_baud);
|
||||
printf("Parity: %c\n", mb_devices[i].rtu_parity);
|
||||
printf("Data Bits: %d\n", mb_devices[i].rtu_data_bit);
|
||||
printf("Stop Bits: %d\n", mb_devices[i].rtu_stop_bit);
|
||||
printf("DI Start: %d\n", mb_devices[i].discrete_inputs.start_address);
|
||||
printf("DI Size: %d\n", mb_devices[i].discrete_inputs.num_regs);
|
||||
printf("Coils Start: %d\n", mb_devices[i].coils.start_address);
|
||||
printf("Coils Size: %d\n", mb_devices[i].coils.num_regs);
|
||||
printf("IR Start: %d\n", mb_devices[i].input_registers.start_address);
|
||||
printf("IR Size: %d\n", mb_devices[i].input_registers.num_regs);
|
||||
printf("HR Start: %d\n", mb_devices[i].holding_registers.start_address);
|
||||
printf("HR Size: %d\n", mb_devices[i].holding_registers.num_regs);
|
||||
printf("\n\n");
|
||||
}
|
||||
//*/
|
||||
//Parser Debug
|
||||
///*
|
||||
for (int i = 0; i < num_devices; i++)
|
||||
{
|
||||
printf("Device %d\n", i);
|
||||
printf("Name: %s\n", mb_devices[i].dev_name);
|
||||
printf("Protocol: %d\n", mb_devices[i].protocol);
|
||||
printf("Address: %s\n", mb_devices[i].dev_address);
|
||||
printf("IP Port: %d\n", mb_devices[i].ip_port);
|
||||
printf("Baud rate: %d\n", mb_devices[i].rtu_baud);
|
||||
printf("Parity: %c\n", mb_devices[i].rtu_parity);
|
||||
printf("Data Bits: %d\n", mb_devices[i].rtu_data_bit);
|
||||
printf("Stop Bits: %d\n", mb_devices[i].rtu_stop_bit);
|
||||
printf("DI Start: %d\n", mb_devices[i].discrete_inputs.start_address);
|
||||
printf("DI Size: %d\n", mb_devices[i].discrete_inputs.num_regs);
|
||||
printf("Coils Start: %d\n", mb_devices[i].coils.start_address);
|
||||
printf("Coils Size: %d\n", mb_devices[i].coils.num_regs);
|
||||
printf("IR Start: %d\n", mb_devices[i].input_registers.start_address);
|
||||
printf("IR Size: %d\n", mb_devices[i].input_registers.num_regs);
|
||||
printf("HR Start: %d\n", mb_devices[i].holding_registers.start_address);
|
||||
printf("HR Size: %d\n", mb_devices[i].holding_registers.num_regs);
|
||||
printf("\n\n");
|
||||
}
|
||||
//*/
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -367,10 +367,10 @@ void *querySlaveDevices(void *arg)
|
|||
//Verify if device is connected
|
||||
if (!mb_devices[i].isConnected)
|
||||
{
|
||||
spdlog::info("Device {} is disconnected. Attempting to reconnect...", mb_devices[i].dev_name);
|
||||
spdlog::info("Device {} is disconnected. Attempting to reconnect...", mb_devices[i].dev_name);
|
||||
if (modbus_connect(mb_devices[i].mb_ctx) == -1)
|
||||
{
|
||||
spdlog::error("Connection failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
spdlog::error("Connection failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
|
||||
if (special_functions[2] != NULL) *special_functions[2]++;
|
||||
|
||||
|
@ -383,7 +383,7 @@ void *querySlaveDevices(void *arg)
|
|||
}
|
||||
else
|
||||
{
|
||||
spdlog::info("Connected to MB device {}", mb_devices[i].dev_name);
|
||||
spdlog::info("Connected to MB device {}", mb_devices[i].dev_name);
|
||||
mb_devices[i].isConnected = true;
|
||||
}
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ void *querySlaveDevices(void *arg)
|
|||
mb_devices[i].isConnected = false;
|
||||
}
|
||||
|
||||
spdlog::info("Modbus Read Discrete Input Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
spdlog::info("Modbus Read Discrete Input Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
bool_input_index += (mb_devices[i].discrete_inputs.num_regs);
|
||||
if (special_functions[2] != NULL) *special_functions[2]++;
|
||||
}
|
||||
|
@ -465,7 +465,7 @@ void *querySlaveDevices(void *arg)
|
|||
uint16_t *tempBuff;
|
||||
tempBuff = (uint16_t *)malloc(2*mb_devices[i].input_registers.num_regs);
|
||||
nanosleep(&ts, NULL);
|
||||
int return_val = modbus_read_input_registers( mb_devices[i].mb_ctx, mb_devices[i].input_registers.start_address,
|
||||
int return_val = modbus_read_input_registers( mb_devices[i].mb_ctx, mb_devices[i].input_registers.start_address,
|
||||
mb_devices[i].input_registers.num_regs, tempBuff);
|
||||
if (return_val == -1)
|
||||
{
|
||||
|
@ -475,7 +475,7 @@ void *querySlaveDevices(void *arg)
|
|||
mb_devices[i].isConnected = false;
|
||||
}
|
||||
|
||||
spdlog::error("Modbus Read Input Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
spdlog::error("Modbus Read Input Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
int_input_index += (mb_devices[i].input_registers.num_regs);
|
||||
if (special_functions[2] != NULL) *special_functions[2]++;
|
||||
}
|
||||
|
@ -508,7 +508,7 @@ void *querySlaveDevices(void *arg)
|
|||
modbus_close(mb_devices[i].mb_ctx);
|
||||
mb_devices[i].isConnected = false;
|
||||
}
|
||||
spdlog::error("Modbus Read Holding Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
spdlog::error("Modbus Read Holding Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
int_input_index += (mb_devices[i].holding_read_registers.num_regs);
|
||||
if (special_functions[2] != NULL) *special_functions[2]++;
|
||||
}
|
||||
|
@ -551,7 +551,7 @@ void *querySlaveDevices(void *arg)
|
|||
mb_devices[i].isConnected = false;
|
||||
}
|
||||
|
||||
spdlog::error("Modbus Write Holding Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
spdlog::error("Modbus Write Holding Registers failed on MB device {}: {}", mb_devices[i].dev_name, modbus_strerror(errno));
|
||||
if (special_functions[2] != NULL) *special_functions[2]++;
|
||||
}
|
||||
|
||||
|
@ -562,7 +562,7 @@ void *querySlaveDevices(void *arg)
|
|||
this_thread::sleep_for(chrono::milliseconds(polling_period));
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -571,29 +571,29 @@ void *querySlaveDevices(void *arg)
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void initializeMB()
|
||||
{
|
||||
parseConfig();
|
||||
parseConfig();
|
||||
|
||||
for (int i = 0; i < num_devices; i++)
|
||||
{
|
||||
if (mb_devices[i].protocol == MB_TCP)
|
||||
{
|
||||
mb_devices[i].mb_ctx = modbus_new_tcp(mb_devices[i].dev_address, mb_devices[i].ip_port);
|
||||
}
|
||||
else if (mb_devices[i].protocol == MB_RTU)
|
||||
{
|
||||
mb_devices[i].mb_ctx = modbus_new_rtu( mb_devices[i].dev_address, mb_devices[i].rtu_baud,
|
||||
mb_devices[i].rtu_parity, mb_devices[i].rtu_data_bit,
|
||||
mb_devices[i].rtu_stop_bit);
|
||||
}
|
||||
for (int i = 0; i < num_devices; i++)
|
||||
{
|
||||
if (mb_devices[i].protocol == MB_TCP)
|
||||
{
|
||||
mb_devices[i].mb_ctx = modbus_new_tcp(mb_devices[i].dev_address, mb_devices[i].ip_port);
|
||||
}
|
||||
else if (mb_devices[i].protocol == MB_RTU)
|
||||
{
|
||||
mb_devices[i].mb_ctx = modbus_new_rtu( mb_devices[i].dev_address, mb_devices[i].rtu_baud,
|
||||
mb_devices[i].rtu_parity, mb_devices[i].rtu_data_bit,
|
||||
mb_devices[i].rtu_stop_bit);
|
||||
}
|
||||
|
||||
//slave id
|
||||
modbus_set_slave(mb_devices[i].mb_ctx, mb_devices[i].dev_id);
|
||||
modbus_set_slave(mb_devices[i].mb_ctx, mb_devices[i].dev_id);
|
||||
|
||||
//timeout
|
||||
uint32_t to_sec = timeout / 1000;
|
||||
uint32_t to_usec = (timeout % 1000) * 1000;
|
||||
modbus_set_response_timeout(mb_devices[i].mb_ctx, to_sec, to_usec);
|
||||
}
|
||||
}
|
||||
|
||||
//Initialize comm error counter
|
||||
if (special_functions[2] != NULL) *special_functions[2] = 0;
|
||||
|
@ -615,15 +615,15 @@ void initializeMB()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void updateBuffersIn_MB()
|
||||
{
|
||||
pthread_mutex_lock(&ioLock);
|
||||
pthread_mutex_lock(&ioLock);
|
||||
|
||||
for (int i = 0; i < MAX_MB_IO; i++)
|
||||
{
|
||||
if (bool_input[100+(i/8)][i%8] != NULL) *bool_input[100+(i/8)][i%8] = bool_input_buf[i];
|
||||
if (int_input[100+i] != NULL) *int_input[100+i] = int_input_buf[i];
|
||||
}
|
||||
for (int i = 0; i < MAX_MB_IO; i++)
|
||||
{
|
||||
if (bool_input[100+(i/8)][i%8] != NULL) *bool_input[100+(i/8)][i%8] = bool_input_buf[i];
|
||||
if (int_input[100+i] != NULL) *int_input[100+i] = int_input_buf[i];
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&ioLock);
|
||||
pthread_mutex_unlock(&ioLock);
|
||||
}
|
||||
|
||||
|
||||
|
@ -633,15 +633,15 @@ void updateBuffersIn_MB()
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
void updateBuffersOut_MB()
|
||||
{
|
||||
pthread_mutex_lock(&ioLock);
|
||||
pthread_mutex_lock(&ioLock);
|
||||
|
||||
for (int i = 0; i < MAX_MB_IO; i++)
|
||||
{
|
||||
if (bool_output[100+(i/8)][i%8] != NULL) bool_output_buf[i] = *bool_output[100+(i/8)][i%8];
|
||||
if (int_output[100+i] != NULL) int_output_buf[i] = *int_output[100+i];
|
||||
}
|
||||
for (int i = 0; i < MAX_MB_IO; i++)
|
||||
{
|
||||
if (bool_output[100+(i/8)][i%8] != NULL) bool_output_buf[i] = *bool_output[100+(i/8)][i%8];
|
||||
if (int_output[100+i] != NULL) int_output_buf[i] = *int_output[100+i];
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&ioLock);
|
||||
pthread_mutex_unlock(&ioLock);
|
||||
}
|
||||
|
||||
/** @}*/
|
||||
|
|
|
@ -74,7 +74,7 @@ IndexedStrategy::IndexedStrategy(const GlueVariablesBinding& bindings) :
|
|||
// values. The caches ensure that we are unlikely to have to wait
|
||||
// for a lock.
|
||||
|
||||
// Allocate a big enough read and write buffers. For the "int" types,
|
||||
// Allocate big enough read and write buffers. For the "int" types,
|
||||
// we know the size in advance. For the boolean types, we don't, so
|
||||
// figure out how big of a buffer we need for boolean types.
|
||||
const int32_t max_coil_index = bindings.find_max_msi(IECVT_BOOL, IECLDT_OUT);
|
||||
|
@ -143,10 +143,11 @@ void exchange(array<PendingValue<T>, NUM_REGISTER_VALUES>& write_buffer,
|
|||
continue;
|
||||
}
|
||||
|
||||
// If there was a write that hasn't be written, then transfer the
|
||||
// If there was a write that hasn't been written, then transfer the
|
||||
// value to the read buffer.
|
||||
if (write_buffer[index].has_pending) {
|
||||
*read_buffer[index].value = write_buffer[index].value;
|
||||
write_buffer[index].has_pending = false;
|
||||
}
|
||||
|
||||
// Finally, update our cached value so that we can read the value
|
||||
|
@ -163,10 +164,11 @@ void IndexedStrategy::Exchange() {
|
|||
// handle populating writes into the structure.
|
||||
|
||||
// Update the read caches for coils and discrete inputs.
|
||||
// Only the coils can be set, so we only only to check for pending
|
||||
// Only the coils can be set, so we only need to check for pending
|
||||
// writes to those.
|
||||
for (size_t index = 0; index < coil_write_buffer.size(); ++index) {
|
||||
if (coil_write_buffer[index].has_pending) {
|
||||
coil_write_buffer[index].has_pending = false;
|
||||
*coil_read_buffer[index].value = coil_write_buffer[index].value;
|
||||
}
|
||||
coil_read_buffer[index].update_cache();
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_MODBUSSLAVE_MB_UTIL_H_
|
||||
#define CORE_MODBUSSLAVE_MB_UTIL_H_
|
||||
#ifndef RUNTIME_CORE_MODBUSSLAVE_MB_UTIL_H_
|
||||
#define RUNTIME_CORE_MODBUSSLAVE_MB_UTIL_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
|
@ -37,4 +37,4 @@ inline std::uint8_t mb_high_byte(std::uint16_t w) {
|
|||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_MODBUSSLAVE_MB_UTIL_H_
|
||||
#endif // RUNTIME_CORE_MODBUSSLAVE_MB_UTIL_H_
|
||||
|
|
|
@ -412,13 +412,13 @@ int modbus_slave_cfg_handler(void* user_data, const char* section,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int8_t modbus_slave_run(std::unique_ptr<std::istream, std::function<void(std::istream*)>>& cfg_stream,
|
||||
int8_t modbus_slave_run(oplc::config_stream& cfg_stream,
|
||||
const char* cfg_overrides,
|
||||
const GlueVariablesBinding& bindings,
|
||||
volatile bool& run) {
|
||||
ModbusSlaveConfig config;
|
||||
|
||||
ini_parse_stream(istream_fgets, cfg_stream.get(), modbus_slave_cfg_handler, &config);
|
||||
ini_parse_stream(oplc::istream_fgets, cfg_stream.get(), modbus_slave_cfg_handler, &config);
|
||||
|
||||
cfg_stream.reset(nullptr);
|
||||
|
||||
|
@ -449,12 +449,7 @@ int8_t modbus_slave_run(std::unique_ptr<std::istream, std::function<void(std::is
|
|||
void modbus_slave_service_run(const GlueVariablesBinding& binding,
|
||||
volatile bool& run, const char* config) {
|
||||
|
||||
unique_ptr<istream, function<void(istream*)>> cfg_stream(new ifstream("../etc/config.ini"), [](istream* s)
|
||||
{
|
||||
reinterpret_cast<ifstream*>(s)->close();
|
||||
delete s;
|
||||
});
|
||||
|
||||
auto cfg_stream = oplc::open_config();
|
||||
modbus_slave_run(cfg_stream, config, binding, run);
|
||||
}
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_MODBUSSLAVE_SLAVE_H_
|
||||
#define CORE_MODBUSSLAVE_SLAVE_H_
|
||||
#ifndef RUNTIME_CORE_MODBUSSLAVE_SLAVE_H_
|
||||
#define RUNTIME_CORE_MODBUSSLAVE_SLAVE_H_
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
|
@ -38,4 +38,4 @@ void modbus_slave_service_run(const GlueVariablesBinding& binding,
|
|||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_MODBUSSLAVE_SLAVE_H_
|
||||
#endif // RUNTIME_CORE_MODBUSSLAVE_SLAVE_H_
|
||||
|
|
|
@ -46,15 +46,15 @@
|
|||
#define MAX_32B_RANGE 2047 //Holding Register Size 32bit (memory)
|
||||
|
||||
/*------------File Type for PCCC--------------*/
|
||||
#define PCCC_INPUT_LOGICAL_SLOT 0x8c
|
||||
#define PCCC_OUTPUT_LOGICAL_SLOT 0x8b
|
||||
#define PCCC_INTEGER 0x89
|
||||
#define PCCC_FLOATING_POINT 0x8A
|
||||
#define PCCC_INPUT_LOGICAL_SLOT 0x8c
|
||||
#define PCCC_OUTPUT_LOGICAL_SLOT 0x8b
|
||||
#define PCCC_INTEGER 0x89
|
||||
#define PCCC_FLOATING_POINT 0x8A
|
||||
|
||||
#define PCCC_FN_OUTPUT 0x00
|
||||
#define PCCC_FN_INPUT 0x01
|
||||
#define PCCC_FN_INT 0x07
|
||||
#define PCCC_FN_FLOAT 0x08
|
||||
#define PCCC_FN_OUTPUT 0x00
|
||||
#define PCCC_FN_INPUT 0x01
|
||||
#define PCCC_FN_INT 0x07
|
||||
#define PCCC_FN_FLOAT 0x08
|
||||
|
||||
/*----------------Define functions for bit/byte operations-------------------*/
|
||||
#define bitRead(value, bit) (((value) >> (bit)) & 0x01)
|
||||
|
@ -78,30 +78,30 @@ using namespace std;
|
|||
//-----------------------------------------------------------Structure Defines--------------------------------------------------//
|
||||
struct pccc_header //Structure for the Header Information for EthernetIP
|
||||
{
|
||||
unsigned char *Data;
|
||||
unsigned char *Data_Size;
|
||||
|
||||
unsigned char *Data;
|
||||
unsigned char *Data_Size;
|
||||
|
||||
unsigned char *HD_length = 5;//[5] -> Typical Header Length for Command; Response Header Length is 4
|
||||
unsigned char *HD_CMD_Code;//[1] -> Command Code
|
||||
unsigned char *HD_CMD_Code;//[1] -> Command Code
|
||||
unsigned char *HD_Status;//[1] -> Status Code
|
||||
unsigned char *HD_TransactionNum;//[2] -> Transaction Number
|
||||
unsigned char *HD_Data_Function_Code;//[1] -> Function code MSB
|
||||
unsigned char *HD_Ext_Status; //Ext Status -> only appended if Status = 0x0f[1]
|
||||
unsigned char *HD_Data_Function_Code;//[1] -> Function code MSB
|
||||
unsigned char *HD_Ext_Status; //Ext Status -> only appended if Status = 0x0f[1]
|
||||
|
||||
unsigned char resp_cod_hex = 0x4f; //Response Hex Value
|
||||
unsigned char *RP_CMD_Code = &resp_cod_hex;//[1] -> Reply Command Code = 0x4f
|
||||
unsigned char resp_cod_hex = 0x4f; //Response Hex Value
|
||||
unsigned char *RP_CMD_Code = &resp_cod_hex;//[1] -> Reply Command Code = 0x4f
|
||||
};
|
||||
|
||||
struct protected_logical_read_command //Struct for Reply and Command values of Read
|
||||
{
|
||||
unsigned char *CMD_Byte_Size;//[1]
|
||||
unsigned char *RP_EXT_Status;//[1]* -> Ext Status -> only appended if Status = 0x0f[1]
|
||||
{
|
||||
unsigned char *CMD_Byte_Size;//[1]
|
||||
unsigned char *RP_EXT_Status;//[1]* -> Ext Status -> only appended if Status = 0x0f[1]
|
||||
};
|
||||
|
||||
struct protected_logical_write_command
|
||||
{
|
||||
unsigned char *CMD_Byte_Size;//[1]*
|
||||
unsigned char *RP_EXT_Status;//[1]* -> Ext Status -> only appended if Status = 0x0f[1]
|
||||
{
|
||||
unsigned char *CMD_Byte_Size;//[1]*
|
||||
unsigned char *RP_EXT_Status;//[1]* -> Ext Status -> only appended if Status = 0x0f[1]
|
||||
};
|
||||
//--------------------------------------------------------------------------------------------------------------------------------------//
|
||||
|
||||
|
@ -127,148 +127,148 @@ int an_word_pccc(unsigned char byte1, unsigned char byte2);
|
|||
//This function takes in the data from enip.cpp and places the data in the appropriate structure variables
|
||||
uint16_t processPCCCMessage(unsigned char *buffer, int buffer_size)
|
||||
{
|
||||
/* Variables */
|
||||
int new_pccc_length; //New PCCC Length
|
||||
pccc_header header;
|
||||
header.Data = buffer;
|
||||
header.Data_Size = buffer_size;
|
||||
|
||||
/*Determine the new pccc length*/
|
||||
new_pccc_length = ParsePCCCData(buffer,buffer_size);
|
||||
return new_pccc_length; //Return the length to enip.cpp
|
||||
/* Variables */
|
||||
int new_pccc_length; //New PCCC Length
|
||||
pccc_header header;
|
||||
header.Data = buffer;
|
||||
header.Data_Size = buffer_size;
|
||||
|
||||
/*Determine the new pccc length*/
|
||||
new_pccc_length = ParsePCCCData(buffer,buffer_size);
|
||||
return new_pccc_length; //Return the length to enip.cpp
|
||||
}
|
||||
|
||||
uint16_t ParsePCCCData(unsigned char *buffer, int buffer_size)
|
||||
{
|
||||
/*Variables*/
|
||||
int new_pccc_length; //Variable for new PCCC length
|
||||
pccc_header header;
|
||||
|
||||
header.HD_CMD_Code = &buffer[0];//[1] -> Command Code
|
||||
header.HD_Status = &buffer[1];////[1] -> Status Code
|
||||
header.HD_TransactionNum = &buffer[2];//[2] -> Transaction Number
|
||||
header.HD_Data_Function_Code = &buffer[4];//[1] -> Data Function Code
|
||||
|
||||
/*Determine what command is being requested*/
|
||||
new_pccc_length = Command_Protocol(header,buffer,buffer_size);
|
||||
|
||||
return new_pccc_length; //Return the new pccc length
|
||||
{
|
||||
/*Variables*/
|
||||
int new_pccc_length; //Variable for new PCCC length
|
||||
pccc_header header;
|
||||
|
||||
header.HD_CMD_Code = &buffer[0];//[1] -> Command Code
|
||||
header.HD_Status = &buffer[1];////[1] -> Status Code
|
||||
header.HD_TransactionNum = &buffer[2];//[2] -> Transaction Number
|
||||
header.HD_Data_Function_Code = &buffer[4];//[1] -> Data Function Code
|
||||
|
||||
/*Determine what command is being requested*/
|
||||
new_pccc_length = Command_Protocol(header,buffer,buffer_size);
|
||||
|
||||
return new_pccc_length; //Return the new pccc length
|
||||
}
|
||||
|
||||
/* Determine the Command that is being requested to execute */
|
||||
uint16_t Command_Protocol(pccc_header header, unsigned char *buffer, int buffer_size)
|
||||
{
|
||||
uint16_t var_pccc_length;
|
||||
|
||||
/*If Statement to determine the command code from the Command Packet*/
|
||||
if(((unsigned int)*header.HD_CMD_Code == 0x0f) && ((unsigned int)*header.HD_Data_Function_Code == 0xA2))//Protected Logical Read
|
||||
{
|
||||
var_pccc_length = Protected_Logical_Read_Reply(header,buffer,buffer_size);
|
||||
return var_pccc_length;
|
||||
}
|
||||
else if(((unsigned int)*header.HD_CMD_Code == 0x0f) && ( ((unsigned int)*header.HD_Data_Function_Code == 0xAA) || ((unsigned int)*header.HD_Data_Function_Code == 0xAB)))//Protected Logical Write
|
||||
{
|
||||
var_pccc_length = Protected_Logical_Write_Reply(header,buffer,buffer_size);
|
||||
return var_pccc_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*initialize logging system*/
|
||||
unsigned char log_msg[1000];
|
||||
unsigned char *p = log_msg;
|
||||
spdlog::info("PCCC: Unsupported Command/Data Function Code!");
|
||||
return -1;
|
||||
}//return length as -1 to signify that the CMD Code/Function Code was not recognize
|
||||
uint16_t var_pccc_length;
|
||||
|
||||
/*If Statement to determine the command code from the Command Packet*/
|
||||
if(((unsigned int)*header.HD_CMD_Code == 0x0f) && ((unsigned int)*header.HD_Data_Function_Code == 0xA2))//Protected Logical Read
|
||||
{
|
||||
var_pccc_length = Protected_Logical_Read_Reply(header,buffer,buffer_size);
|
||||
return var_pccc_length;
|
||||
}
|
||||
else if(((unsigned int)*header.HD_CMD_Code == 0x0f) && ( ((unsigned int)*header.HD_Data_Function_Code == 0xAA) || ((unsigned int)*header.HD_Data_Function_Code == 0xAB)))//Protected Logical Write
|
||||
{
|
||||
var_pccc_length = Protected_Logical_Write_Reply(header,buffer,buffer_size);
|
||||
return var_pccc_length;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*initialize logging system*/
|
||||
unsigned char log_msg[1000];
|
||||
unsigned char *p = log_msg;
|
||||
spdlog::info("PCCC: Unsupported Command/Data Function Code!");
|
||||
return -1;
|
||||
}//return length as -1 to signify that the CMD Code/Function Code was not recognize
|
||||
}
|
||||
|
||||
uint16_t Protected_Logical_Read_Reply(pccc_header header, unsigned char *buffer, int buffer_size)
|
||||
{
|
||||
/*Variables*/
|
||||
protected_logical_read_command protected_LR;
|
||||
protected_LR.CMD_Byte_Size = &buffer[5];//Byte Size of data to be read
|
||||
|
||||
/*Determining Data Length*/
|
||||
unsigned int len_resp = 4;
|
||||
len_resp = len_resp + (unsigned int)*protected_LR.CMD_Byte_Size;
|
||||
|
||||
/*check if the message is long enough- Left in for future error handling setup*/
|
||||
/*if (buffer_size < 8)
|
||||
{
|
||||
//PCCC Error Handling; Make sure that the buffer size is at least 8
|
||||
}*/
|
||||
/*Variables*/
|
||||
protected_logical_read_command protected_LR;
|
||||
protected_LR.CMD_Byte_Size = &buffer[5];//Byte Size of data to be read
|
||||
|
||||
/*Determining Data Length*/
|
||||
unsigned int len_resp = 4;
|
||||
len_resp = len_resp + (unsigned int)*protected_LR.CMD_Byte_Size;
|
||||
|
||||
/*check if the message is long enough- Left in for future error handling setup*/
|
||||
/*if (buffer_size < 8)
|
||||
{
|
||||
//PCCC Error Handling; Make sure that the buffer size is at least 8
|
||||
}*/
|
||||
|
||||
//****************** Read Coils **********************//
|
||||
if(buffer[6] == PCCC_FN_OUTPUT && buffer[7] == PCCC_OUTPUT_LOGICAL_SLOT) // Done/Tested
|
||||
{
|
||||
Pccc_ReadCoils(buffer, buffer_size);
|
||||
}
|
||||
|
||||
//*************** Read Discrete Inputs ***************//
|
||||
else if(buffer[6] == PCCC_FN_INPUT && buffer[7] == PCCC_INPUT_LOGICAL_SLOT)// Done/Tested
|
||||
{
|
||||
Pccc_ReadDiscreteInputs(buffer, buffer_size);
|
||||
}
|
||||
//****************** Read Coils **********************//
|
||||
if(buffer[6] == PCCC_FN_OUTPUT && buffer[7] == PCCC_OUTPUT_LOGICAL_SLOT) // Done/Tested
|
||||
{
|
||||
Pccc_ReadCoils(buffer, buffer_size);
|
||||
}
|
||||
|
||||
//*************** Read Discrete Inputs ***************//
|
||||
else if(buffer[6] == PCCC_FN_INPUT && buffer[7] == PCCC_INPUT_LOGICAL_SLOT)// Done/Tested
|
||||
{
|
||||
Pccc_ReadDiscreteInputs(buffer, buffer_size);
|
||||
}
|
||||
|
||||
//****************** Read Holding Registers[PURE, 16Bit Mem, 32bit MEM] ******************//
|
||||
else if((buffer[6] == PCCC_FN_INT || buffer[6] == PCCC_FN_FLOAT) && (buffer[7] == PCCC_INTEGER || buffer[7] == PCCC_FLOATING_POINT))//Done/Tested
|
||||
{
|
||||
Pccc_ReadHoldingRegisters(buffer, buffer_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char log_msg[1000];
|
||||
unsigned char *p = log_msg;
|
||||
spdlog::info("PCCC: Error occurred while processing Protected Logical Read");
|
||||
return -1;
|
||||
}//return length as -1 to signify that the CMD Code/Function Code was not recognize
|
||||
|
||||
/*Creating the reply packet and memcpy the data into the buffer*/
|
||||
memmove(&buffer[0], (unsigned int)header.RP_CMD_Code, 1); //0x4f Response Code
|
||||
//****************** Read Holding Registers[PURE, 16Bit Mem, 32bit MEM] ******************//
|
||||
else if((buffer[6] == PCCC_FN_INT || buffer[6] == PCCC_FN_FLOAT) && (buffer[7] == PCCC_INTEGER || buffer[7] == PCCC_FLOATING_POINT))//Done/Tested
|
||||
{
|
||||
Pccc_ReadHoldingRegisters(buffer, buffer_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned char log_msg[1000];
|
||||
unsigned char *p = log_msg;
|
||||
spdlog::info("PCCC: Error occurred while processing Protected Logical Read");
|
||||
return -1;
|
||||
}//return length as -1 to signify that the CMD Code/Function Code was not recognize
|
||||
|
||||
/*Creating the reply packet and memcpy the data into the buffer*/
|
||||
memmove(&buffer[0], (unsigned int)header.RP_CMD_Code, 1); //0x4f Response Code
|
||||
memmove(&buffer[1], (unsigned int)header.HD_Status, 1); //Same from COMMAND REQUEST
|
||||
memmove(&buffer[2], (unsigned int)header.HD_TransactionNum, 2);//Same from COMMAND REQUEST
|
||||
|
||||
return len_resp; //Return the Resonse Packet Length for PCCC
|
||||
|
||||
return len_resp; //Return the Resonse Packet Length for PCCC
|
||||
}
|
||||
|
||||
uint16_t Protected_Logical_Write_Reply(pccc_header header,unsigned char *buffer, int buffer_size) // Connected
|
||||
{
|
||||
/*Variables*/
|
||||
protected_logical_write_command protected_LW;
|
||||
protected_LW.CMD_Byte_Size = &buffer[5];//Byte Size of data to be read
|
||||
|
||||
/*Determining link of new PCCC Packet*/
|
||||
uint16_t len_resp = header.HD_length - 1;
|
||||
|
||||
/*Creating the reply packet and memcpy the data into the buffer*/
|
||||
memmove(&buffer[0], (unsigned int)header.RP_CMD_Code, 1);
|
||||
{
|
||||
/*Variables*/
|
||||
protected_logical_write_command protected_LW;
|
||||
protected_LW.CMD_Byte_Size = &buffer[5];//Byte Size of data to be read
|
||||
|
||||
/*Determining link of new PCCC Packet*/
|
||||
uint16_t len_resp = header.HD_length - 1;
|
||||
|
||||
/*Creating the reply packet and memcpy the data into the buffer*/
|
||||
memmove(&buffer[0], (unsigned int)header.RP_CMD_Code, 1);
|
||||
memmove(&buffer[1], (unsigned int)header.HD_Status, 1);
|
||||
memmove(&buffer[2], (unsigned int)header.HD_TransactionNum, 2);
|
||||
|
||||
/*check if the message is long enough- Left in for future error handling setup*/
|
||||
/*if (buffer_size < 8)
|
||||
{
|
||||
//PCCC Error Handling; Make sure that the buffer size is at least 8
|
||||
}*/
|
||||
|
||||
//****************** Write Coil **********************//
|
||||
if(buffer[6] == PCCC_FN_OUTPUT && buffer[7] == PCCC_OUTPUT_LOGICAL_SLOT)// Done/Tested
|
||||
{
|
||||
Pccc_WriteCoil(buffer, buffer_size);
|
||||
}
|
||||
|
||||
/*check if the message is long enough- Left in for future error handling setup*/
|
||||
/*if (buffer_size < 8)
|
||||
{
|
||||
//PCCC Error Handling; Make sure that the buffer size is at least 8
|
||||
}*/
|
||||
|
||||
//****************** Write Coil **********************//
|
||||
if(buffer[6] == PCCC_FN_OUTPUT && buffer[7] == PCCC_OUTPUT_LOGICAL_SLOT)// Done/Tested
|
||||
{
|
||||
Pccc_WriteCoil(buffer, buffer_size);
|
||||
}
|
||||
|
||||
//****************** Write Register ******************//
|
||||
else if((buffer[6] == PCCC_FN_FLOAT || buffer[6] == PCCC_FN_INT) && (buffer[7] == PCCC_INTEGER || buffer[7] == PCCC_FLOATING_POINT))//Done/Tested
|
||||
{
|
||||
Pccc_WriteRegister(buffer, buffer_size);
|
||||
}
|
||||
//****************** Write Register ******************//
|
||||
else if((buffer[6] == PCCC_FN_FLOAT || buffer[6] == PCCC_FN_INT) && (buffer[7] == PCCC_INTEGER || buffer[7] == PCCC_FLOATING_POINT))//Done/Tested
|
||||
{
|
||||
Pccc_WriteRegister(buffer, buffer_size);
|
||||
}
|
||||
|
||||
//****************** Function Code Error ******************/
|
||||
/*Left in for future error handling setup*/
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling; Make sure that the buffer size is at least 8. If none of the defined File Type and File Numbers match, error unrecognized File Type and File Number.
|
||||
}
|
||||
return len_resp;
|
||||
//****************** Function Code Error ******************/
|
||||
/*Left in for future error handling setup*/
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling; Make sure that the buffer size is at least 8. If none of the defined File Type and File Numbers match, error unrecognized File Type and File Number.
|
||||
}
|
||||
return len_resp;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -276,20 +276,20 @@ uint16_t Protected_Logical_Write_Reply(pccc_header header,unsigned char *buffer,
|
|||
//-----------------------------------------------------------------------------
|
||||
int word_pccc(unsigned char byte1, unsigned char byte2)
|
||||
{
|
||||
int returnValue;
|
||||
returnValue = (int)(byte1) | (int)byte2;
|
||||
int returnValue;
|
||||
returnValue = (int)(byte1) | (int)byte2;
|
||||
|
||||
return returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
//-----------------------------------------------------------------------------
|
||||
// Concatenate two bytes into an int
|
||||
//-----------------------------------------------------------------------------
|
||||
int an_word_pccc(unsigned char byte1, unsigned char byte2)
|
||||
{
|
||||
int returnValue;
|
||||
returnValue = (int)(byte1) | (int)(byte2 << 8);
|
||||
int returnValue;
|
||||
returnValue = (int)(byte1) | (int)(byte2 << 8);
|
||||
|
||||
return returnValue;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -297,49 +297,49 @@ int an_word_pccc(unsigned char byte1, unsigned char byte2)
|
|||
//-----------------------------------------------------------------------------
|
||||
void Pccc_ReadCoils(unsigned char *buffer, int buffer_size) //Working QX Read
|
||||
{
|
||||
int Start, ByteDataLength, Mask;
|
||||
|
||||
/*check if the message is long enough- Left in for future error handling setup*/
|
||||
/*if (buffer_size < 10)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at least 10 bytes. If it doesn't, its a corrupted message
|
||||
}*/
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]); //Start based on the Element and Subelemnt values in the Command Packet
|
||||
Mask = log2( word_pccc(buffer[10],buffer[11]) ); //Save the byte size or byte data length to the variable from the command packet
|
||||
ByteDataLength = buffer[5];
|
||||
int Start, ByteDataLength, Mask;
|
||||
|
||||
/*check if the message is long enough- Left in for future error handling setup*/
|
||||
/*if (buffer_size < 10)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at least 10 bytes. If it doesn't, its a corrupted message
|
||||
}*/
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]); //Start based on the Element and Subelemnt values in the Command Packet
|
||||
Mask = log2( word_pccc(buffer[10],buffer[11]) ); //Save the byte size or byte data length to the variable from the command packet
|
||||
ByteDataLength = buffer[5];
|
||||
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
|
||||
/*----Reading the values from the PLC bool_output buffer and writing to the PCCC buffer based on position----*/
|
||||
for (int i = 0; i < ByteDataLength; i++)
|
||||
{
|
||||
for(int j = 0; j < 8; j++)
|
||||
{
|
||||
int position = Start + i * 8 + j;
|
||||
if (position < MAX_COILS)
|
||||
{
|
||||
if(bool_output[position/8][position%8] != NULL)
|
||||
{
|
||||
bitWrite(buffer[4+i], j, *bool_output[position/8][position%8]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitWrite(buffer[4+i],j,0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); If the position is greater than the MAX COILS, ERROR Overflow?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Left in for future error handling setup*/
|
||||
/*if (pccc_error != ERR_NONE)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); Deetermine if there was an error:
|
||||
}*/
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
|
||||
/*----Reading the values from the PLC bool_output buffer and writing to the PCCC buffer based on position----*/
|
||||
for (int i = 0; i < ByteDataLength; i++)
|
||||
{
|
||||
for(int j = 0; j < 8; j++)
|
||||
{
|
||||
int position = Start + i * 8 + j;
|
||||
if (position < MAX_COILS)
|
||||
{
|
||||
if(bool_output[position/8][position%8] != NULL)
|
||||
{
|
||||
bitWrite(buffer[4+i], j, *bool_output[position/8][position%8]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitWrite(buffer[4+i],j,0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); If the position is greater than the MAX COILS, ERROR Overflow?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Left in for future error handling setup*/
|
||||
/*if (pccc_error != ERR_NONE)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); Deetermine if there was an error:
|
||||
}*/
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -347,130 +347,130 @@ void Pccc_ReadCoils(unsigned char *buffer, int buffer_size) //Working QX Read
|
|||
//-----------------------------------------------------------------------------
|
||||
void Pccc_ReadDiscreteInputs(unsigned char *buffer, int buffer_size) //Working IX Read Only
|
||||
{
|
||||
int Start, ByteDataLength;
|
||||
|
||||
/*This Request must have at least 10 bytes. If it doesn't, its a corrupted messageLeft in for future error handling setup*/
|
||||
/*if (buffer_size < 10)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at least 10 bytes. If it doesn't, its a corrupted message
|
||||
}*/
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
ByteDataLength = buffer[5];//Save the byte size or byte data length to the variable from the command packet
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
|
||||
/*--------Reading the values from the PLC bool_input buffer and writing to the PCCC buffer based on position--------*/
|
||||
for (int i = 0; i < ByteDataLength; i++)
|
||||
{
|
||||
for(int j = 0; j < 8; j++)
|
||||
{
|
||||
int position = Start + i * 8 + j;
|
||||
if (position < MAX_DISCRETE_INPUT)
|
||||
{
|
||||
if(bool_input[position/8][position%8] != NULL)
|
||||
{
|
||||
bitWrite(buffer[4+i], j, *bool_input[position/8][position%8]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitWrite(buffer[4+i],j,0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); If the position is greater than the MAX, ERROR Overflow?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Left in for future error handling setup*/
|
||||
/*if (mb_error != ERR_NONE)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); Deetermine if there was an error:
|
||||
}*/
|
||||
|
||||
int Start, ByteDataLength;
|
||||
|
||||
/*This Request must have at least 10 bytes. If it doesn't, its a corrupted messageLeft in for future error handling setup*/
|
||||
/*if (buffer_size < 10)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at least 10 bytes. If it doesn't, its a corrupted message
|
||||
}*/
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
ByteDataLength = buffer[5];//Save the byte size or byte data length to the variable from the command packet
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
|
||||
/*--------Reading the values from the PLC bool_input buffer and writing to the PCCC buffer based on position--------*/
|
||||
for (int i = 0; i < ByteDataLength; i++)
|
||||
{
|
||||
for(int j = 0; j < 8; j++)
|
||||
{
|
||||
int position = Start + i * 8 + j;
|
||||
if (position < MAX_DISCRETE_INPUT)
|
||||
{
|
||||
if(bool_input[position/8][position%8] != NULL)
|
||||
{
|
||||
bitWrite(buffer[4+i], j, *bool_input[position/8][position%8]);
|
||||
}
|
||||
else
|
||||
{
|
||||
bitWrite(buffer[4+i],j,0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); If the position is greater than the MAX, ERROR Overflow?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Left in for future error handling setup*/
|
||||
/*if (mb_error != ERR_NONE)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); Deetermine if there was an error:
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Implementation of PCCC Read Holding Registers
|
||||
//-----------------------------------------------------------------------------
|
||||
void Pccc_ReadHoldingRegisters(unsigned char *buffer, int buffer_size) // QW Read
|
||||
{
|
||||
int Start, an_Start, WordDataLength, ByteDataLength;
|
||||
{
|
||||
int Start, an_Start, WordDataLength, ByteDataLength;
|
||||
|
||||
/*this request must have at least 10 bytes. If it doesn't, it's a corrupted message - Left in for future error handling setup*/
|
||||
/*if (buffer_size < 10)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at least 10 bytes. If it doesn't, its a corrupted message
|
||||
}*/
|
||||
/*this request must have at least 10 bytes. If it doesn't, it's a corrupted message - Left in for future error handling setup*/
|
||||
/*if (buffer_size < 10)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at least 10 bytes. If it doesn't, its a corrupted message
|
||||
}*/
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
ByteDataLength = buffer[5];//Save the byte size or byte data length to the variable from the command packet
|
||||
WordDataLength = ByteDataLength / 2;//Calculate the word data length based on the byte data length
|
||||
unsigned int Temp_FileT = buffer[7];//Value will be changed potentially during this process, save the File Type Value from command packet
|
||||
unsigned int Temp_FileN = buffer[6];//Value will be changed potentially during this process, save the File Number Value from command packet
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
ByteDataLength = buffer[5];//Save the byte size or byte data length to the variable from the command packet
|
||||
WordDataLength = ByteDataLength / 2;//Calculate the word data length based on the byte data length
|
||||
unsigned int Temp_FileT = buffer[7];//Value will be changed potentially during this process, save the File Type Value from command packet
|
||||
unsigned int Temp_FileN = buffer[6];//Value will be changed potentially during this process, save the File Number Value from command packet
|
||||
|
||||
/*asked for too many registers - Left in for future error handling setup*/
|
||||
/*if (ByteDataLength > 255)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at greater than 255 bytes. If it does, its a corrupted message
|
||||
//return;
|
||||
}*/
|
||||
/*asked for too many registers - Left in for future error handling setup*/
|
||||
/*if (ByteDataLength > 255)
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); This Request must have at greater than 255 bytes. If it does, its a corrupted message
|
||||
//return;
|
||||
}*/
|
||||
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
/*--------Reading the values from the PLC int_output, int_memory, and dint_memory buffer and writing to the PCCC buffer based on position--------*/
|
||||
for(int i = 0; i < WordDataLength; i++)
|
||||
{
|
||||
int position = Start + i;
|
||||
//int an_position = an_Start + i;
|
||||
if ((position <= MIN_16B_RANGE) && (Temp_FileN == PCCC_FN_INT && Temp_FileT == PCCC_INTEGER))
|
||||
{
|
||||
if (int_output[position] != NULL)
|
||||
{
|
||||
buffer[ 4 + position * 2] = lowByte(*int_output[position]);
|
||||
buffer[5 + position * 2] = highByte(*int_output[position]);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[ 4 + position * 2] = 0;
|
||||
buffer[5 + position * 2] = 0;
|
||||
}
|
||||
}
|
||||
//accessing memory
|
||||
//16-bit registers
|
||||
else if ((position >= MIN_16B_RANGE && position <= MAX_16B_RANGE) && (Temp_FileN == PCCC_FN_INT && Temp_FileT == PCCC_INTEGER))
|
||||
{
|
||||
if (int_memory[position - MIN_16B_RANGE] != NULL)
|
||||
{
|
||||
buffer[ 4 + position * 2] = lowByte(*int_memory[position - MIN_16B_RANGE]);
|
||||
buffer[5 + position * 2] = highByte(*int_memory[position - MIN_16B_RANGE]);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[ 4 + position * 2] = 0;
|
||||
buffer[5 + position * 2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//32-bit registers
|
||||
else if (Temp_FileN == PCCC_FN_FLOAT && Temp_FileT == PCCC_FLOATING_POINT && (position % 2 == 0))
|
||||
{
|
||||
position = position/2;
|
||||
uint32_t tempValue = *dint_memory[position];
|
||||
|
||||
buffer[4+(4*position)] = tempValue;
|
||||
buffer[5+(4*position)] = tempValue >> 8;
|
||||
buffer[6+(4*position)] = tempValue >> 16;
|
||||
buffer[7+(4*position)] = tempValue >> 24;
|
||||
|
||||
}
|
||||
/*Left in for future error handling setup-Invalid Address*/
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); If none of the above are recognized, error
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
/*--------Reading the values from the PLC int_output, int_memory, and dint_memory buffer and writing to the PCCC buffer based on position--------*/
|
||||
for(int i = 0; i < WordDataLength; i++)
|
||||
{
|
||||
int position = Start + i;
|
||||
//int an_position = an_Start + i;
|
||||
if ((position <= MIN_16B_RANGE) && (Temp_FileN == PCCC_FN_INT && Temp_FileT == PCCC_INTEGER))
|
||||
{
|
||||
if (int_output[position] != NULL)
|
||||
{
|
||||
buffer[ 4 + position * 2] = lowByte(*int_output[position]);
|
||||
buffer[5 + position * 2] = highByte(*int_output[position]);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[ 4 + position * 2] = 0;
|
||||
buffer[5 + position * 2] = 0;
|
||||
}
|
||||
}
|
||||
//accessing memory
|
||||
//16-bit registers
|
||||
else if ((position >= MIN_16B_RANGE && position <= MAX_16B_RANGE) && (Temp_FileN == PCCC_FN_INT && Temp_FileT == PCCC_INTEGER))
|
||||
{
|
||||
if (int_memory[position - MIN_16B_RANGE] != NULL)
|
||||
{
|
||||
buffer[ 4 + position * 2] = lowByte(*int_memory[position - MIN_16B_RANGE]);
|
||||
buffer[5 + position * 2] = highByte(*int_memory[position - MIN_16B_RANGE]);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[ 4 + position * 2] = 0;
|
||||
buffer[5 + position * 2] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//32-bit registers
|
||||
else if (Temp_FileN == PCCC_FN_FLOAT && Temp_FileT == PCCC_FLOATING_POINT && (position % 2 == 0))
|
||||
{
|
||||
position = position/2;
|
||||
uint32_t tempValue = *dint_memory[position];
|
||||
|
||||
buffer[4+(4*position)] = tempValue;
|
||||
buffer[5+(4*position)] = tempValue >> 8;
|
||||
buffer[6+(4*position)] = tempValue >> 16;
|
||||
buffer[7+(4*position)] = tempValue >> 24;
|
||||
|
||||
}
|
||||
/*Left in for future error handling setup-Invalid Address*/
|
||||
else
|
||||
{
|
||||
//PCCC Error Handling (Fill in?); If none of the above are recognized, error
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -478,45 +478,45 @@ void Pccc_ReadHoldingRegisters(unsigned char *buffer, int buffer_size) // QW Rea
|
|||
//-----------------------------------------------------------------------------
|
||||
void Pccc_WriteCoil(unsigned char *buffer, int buffer_size) //QX Write NEEDS WRITE MULTIPLE
|
||||
{
|
||||
int Start, Mask;
|
||||
int mask_offset = 0;
|
||||
|
||||
/*Left in for future error handling setup*/
|
||||
/*if(buffer_size < 10)
|
||||
{
|
||||
//ModbusError(buffer, ERR_ILLEGAL_DATA_ADDRESS);
|
||||
//return;
|
||||
}
|
||||
*/
|
||||
|
||||
/*For the Write Mask, there has to be a maskoffset due to an extra two bytes */
|
||||
if((unsigned int)buffer[4] == 0xAB)
|
||||
{
|
||||
mask_offset = buffer[5]; //Byte Size
|
||||
}
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
Mask = log2( word_pccc(buffer[10],buffer[11]) );// Maskoffset based on the mask value in Masked Protected Write Command Packet
|
||||
|
||||
/*--------Determines if the values inside the PCCC data has a 1 or 0 in it. Writes that value to the bool_output based on the contents of the data in PCCC Buffer-------*/
|
||||
if(Start < MAX_COILS)
|
||||
{
|
||||
unsigned char value;
|
||||
if(word_pccc(buffer[10 + mask_offset],buffer[11 + mask_offset]) > 0)
|
||||
{
|
||||
value = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
if(bool_output[Start][Mask] != NULL)
|
||||
{
|
||||
*bool_output[Start][Mask] = value;
|
||||
}
|
||||
}
|
||||
|
||||
int Start, Mask;
|
||||
int mask_offset = 0;
|
||||
|
||||
/*Left in for future error handling setup*/
|
||||
/*if(buffer_size < 10)
|
||||
{
|
||||
//ModbusError(buffer, ERR_ILLEGAL_DATA_ADDRESS);
|
||||
//return;
|
||||
}
|
||||
*/
|
||||
|
||||
/*For the Write Mask, there has to be a maskoffset due to an extra two bytes */
|
||||
if((unsigned int)buffer[4] == 0xAB)
|
||||
{
|
||||
mask_offset = buffer[5]; //Byte Size
|
||||
}
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
Mask = log2( word_pccc(buffer[10],buffer[11]) );// Maskoffset based on the mask value in Masked Protected Write Command Packet
|
||||
|
||||
/*--------Determines if the values inside the PCCC data has a 1 or 0 in it. Writes that value to the bool_output based on the contents of the data in PCCC Buffer-------*/
|
||||
if(Start < MAX_COILS)
|
||||
{
|
||||
unsigned char value;
|
||||
if(word_pccc(buffer[10 + mask_offset],buffer[11 + mask_offset]) > 0)
|
||||
{
|
||||
value = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = 0;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
if(bool_output[Start][Mask] != NULL)
|
||||
{
|
||||
*bool_output[Start][Mask] = value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -525,47 +525,47 @@ void Pccc_ReadHoldingRegisters(unsigned char *buffer, int buffer_size) // QW Rea
|
|||
//-----------------------------------------------------------------------------
|
||||
void Pccc_WriteRegister(unsigned char *buffer, int buffer_size) // QW Write
|
||||
{
|
||||
int Start, WordDataLength, ByteDataLength;
|
||||
int Start, WordDataLength, ByteDataLength;
|
||||
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
int an_Start = an_word_pccc(buffer[8],buffer[9]);//Different Start method for INTs based on the Element and Subelemnt values in the Command Packet
|
||||
ByteDataLength = buffer[5];//Save the byte size or byte data length to the variable from the command packet
|
||||
WordDataLength = ByteDataLength / 2;//Calculate the word data length based on the byte data length
|
||||
unsigned int Temp_FileT = buffer[7];//Value will be changed potentially during this process, save the File Type Value from command packet
|
||||
unsigned int Temp_FileN = buffer[6];//Value will be changed potentially during this process, save the File Number Value from command packet
|
||||
Start = word_pccc(buffer[8],buffer[9]);//Start based on the Element and Subelemnt values in the Command Packet
|
||||
int an_Start = an_word_pccc(buffer[8],buffer[9]);//Different Start method for INTs based on the Element and Subelemnt values in the Command Packet
|
||||
ByteDataLength = buffer[5];//Save the byte size or byte data length to the variable from the command packet
|
||||
WordDataLength = ByteDataLength / 2;//Calculate the word data length based on the byte data length
|
||||
unsigned int Temp_FileT = buffer[7];//Value will be changed potentially during this process, save the File Type Value from command packet
|
||||
unsigned int Temp_FileN = buffer[6];//Value will be changed potentially during this process, save the File Number Value from command packet
|
||||
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
|
||||
/*--------Determines if the values inside the PCCC data has data. Writes that value to the appropriate PLC Buffer based on the contents of the data in PCCC Buffer-------*/
|
||||
for(int i = 0; i < WordDataLength; i++)
|
||||
{
|
||||
int position = Start + i;
|
||||
//analog outputs
|
||||
if ((position <= MIN_16B_RANGE) && (Temp_FileN == PCCC_FN_INT && (Temp_FileT == PCCC_INTEGER)))
|
||||
{
|
||||
if (int_output[position] != NULL) *int_output[position] = an_word_pccc(buffer[10 + i], buffer[11 + i]);//look at this closer
|
||||
}
|
||||
//accessing memory
|
||||
//16-bit registers
|
||||
else if ((position >= MIN_16B_RANGE && position <= MAX_16B_RANGE) && (Temp_FileN == PCCC_FN_OUTPUT && (Temp_FileT == PCCC_INTEGER)))
|
||||
{
|
||||
if (int_memory[position - MIN_16B_RANGE] != NULL) *int_memory[position - MIN_16B_RANGE] = an_word_pccc(buffer[10 + i], buffer[11 + i]);//look at this closer
|
||||
}
|
||||
//32-bit registers
|
||||
if (Temp_FileN == PCCC_FN_FLOAT && (Temp_FileT == PCCC_FLOATING_POINT))
|
||||
{
|
||||
if (dint_memory[position] != NULL)
|
||||
{
|
||||
uint32_t tempValue = buffer[10 + i] | buffer[11 + i] << 8 | buffer[12 + i] << 16 | buffer[13 + i] <<24;//look at this closer
|
||||
|
||||
*dint_memory[position] = tempValue;
|
||||
|
||||
i += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
pccc_holding_regs[position] = an_word_pccc(buffer[10 + i], buffer[11 + i]);//look at this closer might need to copy from temp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(bufferLock);
|
||||
|
||||
/*--------Determines if the values inside the PCCC data has data. Writes that value to the appropriate PLC Buffer based on the contents of the data in PCCC Buffer-------*/
|
||||
for(int i = 0; i < WordDataLength; i++)
|
||||
{
|
||||
int position = Start + i;
|
||||
//analog outputs
|
||||
if ((position <= MIN_16B_RANGE) && (Temp_FileN == PCCC_FN_INT && (Temp_FileT == PCCC_INTEGER)))
|
||||
{
|
||||
if (int_output[position] != NULL) *int_output[position] = an_word_pccc(buffer[10 + i], buffer[11 + i]);//look at this closer
|
||||
}
|
||||
//accessing memory
|
||||
//16-bit registers
|
||||
else if ((position >= MIN_16B_RANGE && position <= MAX_16B_RANGE) && (Temp_FileN == PCCC_FN_OUTPUT && (Temp_FileT == PCCC_INTEGER)))
|
||||
{
|
||||
if (int_memory[position - MIN_16B_RANGE] != NULL) *int_memory[position - MIN_16B_RANGE] = an_word_pccc(buffer[10 + i], buffer[11 + i]);//look at this closer
|
||||
}
|
||||
//32-bit registers
|
||||
if (Temp_FileN == PCCC_FN_FLOAT && (Temp_FileT == PCCC_FLOATING_POINT))
|
||||
{
|
||||
if (dint_memory[position] != NULL)
|
||||
{
|
||||
uint32_t tempValue = buffer[10 + i] | buffer[11 + i] << 8 | buffer[12 + i] << 16 | buffer[13 + i] <<24;//look at this closer
|
||||
|
||||
*dint_memory[position] = tempValue;
|
||||
|
||||
i += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
pccc_holding_regs[position] = an_word_pccc(buffer[10 + i], buffer[11 + i]);//look at this closer might need to copy from temp
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
// 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.
|
||||
|
@ -19,21 +19,23 @@
|
|||
// Thiago Alves, Jun 2019
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <ini.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <istream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
#include <ini.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "glue.h"
|
||||
#include "ini_util.h"
|
||||
#include "ladder.h"
|
||||
#include "pstorage.h"
|
||||
#include "lib/iec_types_all.h"
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
|
@ -158,9 +160,9 @@ size_t pstorage_copy_glue(const GlueVariablesBinding& bindings, char* buffer) {
|
|||
/// This is populated with values from the config file.
|
||||
struct PstorageConfig {
|
||||
PstorageConfig() :
|
||||
poll_interval(std::chrono::seconds(10))
|
||||
poll_interval(chrono::seconds(10))
|
||||
{}
|
||||
std::chrono::seconds poll_interval;
|
||||
chrono::seconds poll_interval;
|
||||
};
|
||||
|
||||
int pstorage_cfg_handler(void* user_data, const char* section,
|
||||
|
@ -172,9 +174,9 @@ int pstorage_cfg_handler(void* user_data, const char* section,
|
|||
auto config = reinterpret_cast<PstorageConfig*>(user_data);
|
||||
|
||||
if (strcmp(name, "poll_interval") == 0) {
|
||||
// We do not allow a poll period of less than 1 second as that
|
||||
// We do not allow a poll period of less than 1 second as that
|
||||
// might cause lock contention problems.
|
||||
config->poll_interval = std::chrono::seconds(max(1, atoi(value)));
|
||||
config->poll_interval = chrono::seconds(max(1, atoi(value)));
|
||||
} else if (strcmp(name, "enabled") == 0) {
|
||||
// Nothing to do here - we already know this is enabled
|
||||
} else {
|
||||
|
@ -185,21 +187,21 @@ int pstorage_cfg_handler(void* user_data, const char* section,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int8_t pstorage_run(std::unique_ptr<std::istream, std::function<void(std::istream*)>>& cfg_stream,
|
||||
int8_t pstorage_run(oplc::config_stream& cfg_stream,
|
||||
const char* cfg_overrides,
|
||||
const GlueVariablesBinding& bindings,
|
||||
volatile bool& run,
|
||||
function<std::ostream*(void)> stream_fn)
|
||||
{
|
||||
function<ostream*(void)> stream_fn) {
|
||||
PstorageConfig config;
|
||||
ini_parse_stream(istream_fgets, cfg_stream.get(), pstorage_cfg_handler, &config);
|
||||
ini_parse_stream(oplc::istream_fgets, cfg_stream.get(),
|
||||
pstorage_cfg_handler, &config);
|
||||
|
||||
// We are done with the file, so release the unique ptr. Normally this
|
||||
// will close the reference to the file
|
||||
cfg_stream.reset(nullptr);
|
||||
|
||||
if (strlen(cfg_overrides) > 0) {
|
||||
config.poll_interval = std::chrono::seconds(max(1, atoi(cfg_overrides)));
|
||||
config.poll_interval = chrono::seconds(max(1, atoi(cfg_overrides)));
|
||||
}
|
||||
|
||||
const char endianness_header[2] = { IS_BIG_ENDIAN, '\n'};
|
||||
|
@ -270,8 +272,7 @@ inline int8_t read_and_check(istream& input_stream, const char header[],
|
|||
}
|
||||
|
||||
int8_t pstorage_read(istream& input_stream,
|
||||
const GlueVariablesBinding& bindings)
|
||||
{
|
||||
const GlueVariablesBinding& bindings) {
|
||||
// Read the file header - we define the file header as a constant that
|
||||
// must be present as the header. We don't allow UTF BOMs here.
|
||||
char header_check[FILE_HEADER_SIZE];
|
||||
|
@ -314,7 +315,7 @@ int8_t pstorage_read(istream& input_stream,
|
|||
switch (glue.size) {
|
||||
case IECLST_BIT:
|
||||
num_bytes = 1;
|
||||
break;
|
||||
break;
|
||||
case IECLST_BYTE:
|
||||
num_bytes = 1;
|
||||
break;
|
||||
|
@ -383,17 +384,13 @@ void pstorage_service_finalize(const GlueVariablesBinding& binding) {
|
|||
|
||||
void pstorage_service_run(const GlueVariablesBinding& binding,
|
||||
volatile bool& run, const char* config) {
|
||||
|
||||
// We don't allow a poll duration of less than one second otherwise
|
||||
// that can have detrimental effects on performance
|
||||
auto create_stream = []() { return new ofstream("persistent.file", ios::binary); };
|
||||
|
||||
unique_ptr<istream, function<void(istream*)>> cfg_stream(new ifstream("../etc/config.ini"), [](istream* s)
|
||||
{
|
||||
reinterpret_cast<ifstream*>(s)->close();
|
||||
delete s;
|
||||
});
|
||||
auto create_stream = []() {
|
||||
return new ofstream("persistent.file", ios::binary);
|
||||
};
|
||||
|
||||
auto cfg_stream = oplc::open_config();
|
||||
pstorage_run(cfg_stream, config, binding, run, create_stream);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,13 +12,11 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_PSTORAGE_H_
|
||||
#define CORE_PSTORAGE_H_
|
||||
#ifndef RUNTIME_CORE_PSTORAGE_H_
|
||||
#define RUNTIME_CORE_PSTORAGE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include "ini_util.h"
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
|
@ -77,7 +75,7 @@ std::int8_t pstorage_read(std::istream& input_stream,
|
|||
/// @param run A flag that indicates if we should terminate the process.
|
||||
/// @return Zero on success, otherwise non-zero. This function may fail
|
||||
/// part way through. Failure does not mean no variables have been set.
|
||||
std::int8_t pstorage_run(std::unique_ptr<std::istream, std::function<void(std::istream*)>>& cfg_stream,
|
||||
std::int8_t pstorage_run(oplc::config_stream& cfg_stream,
|
||||
const char* custom_config,
|
||||
const GlueVariablesBinding& bindings,
|
||||
volatile bool& run,
|
||||
|
@ -85,4 +83,4 @@ std::int8_t pstorage_run(std::unique_ptr<std::istream, std::function<void(std::i
|
|||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_PSTORAGE_H_
|
||||
#endif // RUNTIME_CORE_PSTORAGE_H_
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "service_definition.h"
|
||||
#include "../glue.h"
|
||||
|
@ -62,12 +62,14 @@ ServiceDefinition::ServiceDefinition(const char* name,
|
|||
{}
|
||||
|
||||
void ServiceDefinition::initialize() {
|
||||
GlueVariablesBinding bindings(&bufferLock, OPLCGLUE_GLUE_SIZE, oplc_glue_vars, OPLCGLUE_MD5_DIGEST);
|
||||
GlueVariablesBinding bindings(&bufferLock, OPLCGLUE_GLUE_SIZE,
|
||||
oplc_glue_vars, OPLCGLUE_MD5_DIGEST);
|
||||
this->init_fn(bindings);
|
||||
}
|
||||
|
||||
void ServiceDefinition::finalize() {
|
||||
GlueVariablesBinding bindings(&bufferLock, OPLCGLUE_GLUE_SIZE, oplc_glue_vars, OPLCGLUE_MD5_DIGEST);
|
||||
GlueVariablesBinding bindings(&bufferLock, OPLCGLUE_GLUE_SIZE,
|
||||
oplc_glue_vars, OPLCGLUE_MD5_DIGEST);
|
||||
this->finalize_fn(bindings);
|
||||
}
|
||||
|
||||
|
@ -81,12 +83,14 @@ void ServiceDefinition::start(const char* config) {
|
|||
|
||||
size_t config_len = strlen(config);
|
||||
if (config_len > MAX_INTERACTIVE_CONFIG_SIZE - 1) {
|
||||
spdlog::warn("{} cannot be started because config is longer than {}.", this->name, MAX_INTERACTIVE_CONFIG_SIZE);
|
||||
spdlog::warn("{} cannot be started because config is longer than {}.",
|
||||
this->name, MAX_INTERACTIVE_CONFIG_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Copy the configuration information into our configuration buffer
|
||||
strncpy(this->config_buffer, config, min(config_len, MAX_INTERACTIVE_CONFIG_SIZE));
|
||||
strncpy(this->config_buffer, config,
|
||||
min(config_len, MAX_INTERACTIVE_CONFIG_SIZE));
|
||||
|
||||
spdlog::info("Starting service {}", this->name);
|
||||
|
||||
|
@ -117,4 +121,6 @@ void* ServiceDefinition::run_service(void* user_data) {
|
|||
service->start_fn(bindings, service->running, service->config_buffer);
|
||||
|
||||
service->running = false;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -12,12 +12,12 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
#define CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
#ifndef RUNTIME_CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
#define RUNTIME_CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
|
||||
#include <pthread.h>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <pthread.h>
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
|
@ -46,7 +46,7 @@ typedef std::function<void(const GlueVariablesBinding& binding, volatile bool& r
|
|||
/// or from start to stop. That's only because we haven't had such a need
|
||||
/// yet. If that comes up, then we'll add that.
|
||||
class ServiceDefinition final {
|
||||
public:
|
||||
public:
|
||||
/// Initialize a new instance of a service definition that can be started
|
||||
/// and stopped but does not participate in initialize or finalize.
|
||||
/// @param name The unique name of this service.
|
||||
|
@ -91,11 +91,12 @@ class ServiceDefinition final {
|
|||
/// service.
|
||||
const char* config() const { return this->config_buffer; }
|
||||
|
||||
private:
|
||||
private:
|
||||
// Hide the copy constructor
|
||||
ServiceDefinition(ServiceDefinition &);
|
||||
static void* run_service(void* user_data);
|
||||
private:
|
||||
|
||||
private:
|
||||
/// The type name of the service.
|
||||
const char* name;
|
||||
/// The function to initialize the service.
|
||||
|
@ -115,4 +116,4 @@ class ServiceDefinition final {
|
|||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
#endif // RUNTIME_CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "../modbusslave/slave.h"
|
||||
#include "../dnp3s/dnp3.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
ServiceInitFunction pstorage_init_fn(pstorage_service_init);
|
||||
ServiceStartFunction pstorage_start_service_fn(pstorage_service_run);
|
||||
ServiceStartFunction dnp3s_start_service_fn(dnp3s_service_run);
|
||||
|
@ -38,27 +40,27 @@ ServiceDefinition* services[] = {
|
|||
};
|
||||
|
||||
ServiceDefinition* services_find(const char* name) {
|
||||
ServiceDefinition** item = std::find_if(std::begin(services), std::end(services), [name] (ServiceDefinition* def) {
|
||||
ServiceDefinition** item = find_if(begin(services), end(services), [name] (ServiceDefinition* def) {
|
||||
return strcmp(def->id(), name) == 0;
|
||||
});
|
||||
|
||||
return (item != std::end(services)) ? *item : nullptr;
|
||||
return (item != end(services)) ? *item : nullptr;
|
||||
}
|
||||
|
||||
void services_stop() {
|
||||
std::for_each(std::begin(services), std::end(services), [] (ServiceDefinition* def){
|
||||
for_each(begin(services), end(services), [] (ServiceDefinition* def) {
|
||||
def->stop();
|
||||
});
|
||||
}
|
||||
|
||||
void services_init() {
|
||||
std::for_each(std::begin(services), std::end(services), [] (ServiceDefinition* def){
|
||||
for_each(begin(services), end(services), [] (ServiceDefinition* def) {
|
||||
def->initialize();
|
||||
});
|
||||
}
|
||||
|
||||
void services_finalize() {
|
||||
std::for_each(std::begin(services), std::end(services), [] (ServiceDefinition* def){
|
||||
for_each(begin(services), end(services), [] (ServiceDefinition* def) {
|
||||
def->finalize();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,8 @@
|
|||
// See the License for the specific language governing permissionsand
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef CORE_SERVICE_SERVICE_REGISTRY_H_
|
||||
#define CORE_SERVICE_SERVICE_REGISTRY_H_
|
||||
#ifndef RUNTIME_CORE_SERVICE_SERVICE_REGISTRY_H_
|
||||
#define RUNTIME_CORE_SERVICE_SERVICE_REGISTRY_H_
|
||||
|
||||
/** \addtogroup openplc_runtime
|
||||
* @{
|
||||
|
@ -37,4 +37,4 @@ void services_finalize();
|
|||
|
||||
/** @}*/
|
||||
|
||||
#endif // CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
#endif // RUNTIME_CORE_SERVICE_SERVICE_DEFINITION_H_
|
||||
|
|
Loading…
Reference in New Issue