PR-766 Fix several issues with bool group

This commit is contained in:
Garret Fick 2019-11-11 15:39:09 -05:00
parent ce1143f84c
commit 5481a31b04
8 changed files with 301 additions and 108 deletions

View File

@ -45,7 +45,9 @@ int config_handler(void* user_data, const char* section,
auto config = reinterpret_cast<PlcConfig*>(user_data);
if (ini_matches("logging", "level", section, name)) {
if (strcmp(value, "debug") == 0) {
if (strcmp(value, "trace") == 0) {
spdlog::set_level(spdlog::level::trace);
} else if (strcmp(value, "debug") == 0) {
spdlog::set_level(spdlog::level::debug);
} else if (strcmp(value, "info") == 0) {
spdlog::set_level(spdlog::level::info);

View File

@ -329,6 +329,8 @@ int dnp3s_cfg_handler(void* user_data, const char* section,
} else if (strcmp(name, "unsol_retry_timeout") == 0) {
config->outstation.params.unsolRetryTimeout =
openpal::TimeDuration::Milliseconds(atoi(value));
} else if (strcmp(name, "enabled") == 0) {
// Nothing to do here - we already know this is enabled
} else {
spdlog::warn("Unknown configuration item {}", name);
return -1;

View File

@ -282,11 +282,11 @@ void Dnp3Receiver::ExchangeGlue() {
}
void Dnp3Receiver::Start() {
spdlog::info("DNP3 receiver started");
spdlog::trace("DNP3 receiver started");
}
void Dnp3Receiver::End() {
spdlog::info("DNP3 receiver stopped");
spdlog::trace("DNP3 receiver stopped");
}
#endif // OPLC_DNP3_OUTSTATION

View File

@ -68,8 +68,8 @@ class Dnp3Receiver : public opendnp3::ICommandHandler {
template <typename T>
struct CacheItem {
bool has_value;
T value;
volatile bool has_value;
volatile T value;
};
private:

View File

@ -34,6 +34,7 @@
#include "glue.h"
#include "ini_util.h"
#include "ladder.h"
#include "lib/iec_types_all.h"
/** \addtogroup openplc_runtime
* @{
@ -97,6 +98,20 @@ size_t get_size_bytes(const GlueVariablesBinding& bindings) {
return size;
}
inline uint8_t mask_index(GlueBoolGroup* group, uint8_t i) {
if (!group->values[i]) {
return 0;
}
return (*group->values) ? (1 << i) : 0;
}
inline void set_index(GlueBoolGroup* group, uint8_t i, uint8_t v) {
if (group->values[i]) {
(*group->values[i]) = ((1 << i) & v) ? TRUE: FALSE;
}
}
/// Copy the glue values into the buffer.
/// @param bindings The bindings that we want to copy from.
/// @param buffer The buffer that we are copying into.
@ -115,8 +130,21 @@ size_t pstorage_copy_glue(const GlueVariablesBinding& bindings, char* buffer) {
uint8_t num_bytes = get_size_bytes(glue.size);
// Write the number of bytes to the buffer
memcpy(buffer, glue.value, num_bytes);
if (glue.size == IECLST_BIT) {
GlueBoolGroup* group = reinterpret_cast<GlueBoolGroup*>(glue.value);
uint8_t bools_as_byte = mask_index(group, 0)
| mask_index(group, 1)
| mask_index(group, 2)
| mask_index(group, 3)
| mask_index(group, 4)
| mask_index(group, 5)
| mask_index(group, 6)
| mask_index(group, 7);
memcpy(buffer, &bools_as_byte, 1);
} else {
// Write the number of bytes to the buffer
memcpy(buffer, glue.value, num_bytes);
}
// Advance the pointer to the next starting position
num_written += num_bytes;
@ -147,6 +175,8 @@ int pstorage_cfg_handler(void* user_data, const char* section,
// 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)));
} else if (strcmp(name, "enabled") == 0) {
// Nothing to do here - we already know this is enabled
} else {
spdlog::warn("Unknown configuration item {}", name);
return -1;
@ -315,7 +345,20 @@ int8_t pstorage_read(istream& input_stream,
// value or a group of booleans.
// We don't actually care what the contents are - we just populate as
// though they are raw bytes
memcpy(glue.value, buffer, num_bytes);
if (glue.size == IECLST_BIT) {
GlueBoolGroup* group = reinterpret_cast<GlueBoolGroup*>(glue.value);
uint8_t value = static_cast<uint8_t>(buffer[0]);
set_index(group, 0, value);
set_index(group, 1, value);
set_index(group, 2, value);
set_index(group, 3, value);
set_index(group, 4, value);
set_index(group, 5, value);
set_index(group, 6, value);
set_index(group, 7, value);
} else {
memcpy(glue.value, buffer, num_bytes);
}
}
spdlog::info("Initialized from persistent storage");

View File

@ -43,10 +43,11 @@ SCENARIO("pstorage_read", "") {
IEC_LWORD lword_var = 0;
IEC_SINT usint_var = 0;
IEC_BOOL bool_var = 0;
GlueBoolGroup grp { .index=0, .values = { &bool_var, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr} };
const GlueVariable glue_vars[] = {
{ IECLDT_MEM, IECLST_DOUBLEWORD, 0, 0, IECVT_LWORD, &lword_var },
{ IECLDT_MEM, IECLST_BYTE, 0, 0, IECVT_USINT, &usint_var },
{ IECLDT_MEM, IECLST_BIT, 0, 0, IECVT_BOOL, &bool_var },
{ IECLDT_MEM, IECLST_BIT, 0, 0, IECVT_BOOL, &grp },
};
GlueVariablesBinding bindings(&glue_mutex, 3, glue_vars, CHECKSUM_HEADER);
@ -90,6 +91,32 @@ SCENARIO("pstorage_read", "") {
REQUIRE(bool_var == 1);
}
}
GIVEN("one bool group") {
IEC_BOOL bool_var0 = 0;
IEC_BOOL bool_var1 = 0;
IEC_BOOL bool_var7 = 0;
GlueBoolGroup grp { .index=0, .values = { &bool_var0, &bool_var1, nullptr, nullptr, nullptr, nullptr, nullptr, &bool_var7} };
const GlueVariable glue_vars[] = {
{ IECLDT_MEM, IECLST_BIT, 0, 0, IECVT_BOOL, &grp },
};
GlueVariablesBinding bindings(&glue_mutex, 1, glue_vars, CHECKSUM_HEADER);
WHEN("data is valid and mixture of bits set") {
// We don't (in general) know the endianness to know
// the byte order, so we initialize the buffer based on
// the actual memory layout
char one_char = 0x81;
input_stream.write(&one_char, 1);
input_stream.seekg(0);
REQUIRE(pstorage_read(input_stream, bindings) == 0);
REQUIRE(bool_var0 == 1);
REQUIRE(bool_var1 == 0);
REQUIRE(bool_var7 == 1);
}
}
}
SCENARIO("pstorage_run") {
@ -105,10 +132,11 @@ SCENARIO("pstorage_run") {
IEC_LWORD lword_var = 1;
IEC_SINT usint_var = 2;
IEC_BOOL bool_var = 1;
GlueBoolGroup grp { .index=0, .values = { &bool_var, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr} };
const GlueVariable glue_vars[] = {
{ IECLDT_MEM, IECLST_DOUBLEWORD, 0, 0, IECVT_LWORD, &lword_var },
{ IECLDT_MEM, IECLST_BYTE, 0, 0, IECVT_USINT, &usint_var },
{ IECLDT_MEM, IECLST_BIT, 0, 0, IECVT_BOOL, &bool_var },
{ IECLDT_MEM, IECLST_BIT, 0, 0, IECVT_BOOL, &grp },
};
GlueVariablesBinding bindings(&glue_mutex, 3, glue_vars, CHECKSUM_HEADER);

View File

@ -15,9 +15,13 @@
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <array>
#include <iostream>
#include <fstream>
#include <list>
#include <map>
#include <memory>
#include <set>
#include <string>
#include "md5.h"
@ -331,6 +335,8 @@ const char* fromDirectionFlag(const char flag) {
const char* fromSizeFlag(const char flag) {
switch (flag) {
// G is a special flag we use to represent a boolean group
case 'G':
case 'X':
return "BIT";
case 'B':
@ -345,6 +351,93 @@ const char* fromSizeFlag(const char flag) {
}
}
void generateBoolGroups(ostream& glueVars, char direction, map<uint16_t, array<string, 8>>& items) {
if (items.empty()) {
return;
}
for (auto it_group = items.begin(); it_group != items.end(); ++it_group) {
string name;
name += direction;
name += 'G';
name += to_string(it_group->first);
glueVars << "GlueBoolGroup ___" << name;
glueVars << " { .index=" << it_group->first << ", .values={ ";
for (auto it_var = it_group->second.begin(); it_var != it_group->second.end(); ++it_var) {
if (it_var->empty()) {
glueVars << "nullptr, ";
} else {
glueVars << (*it_var) << ", ";
}
}
glueVars << "} };\n";
glueVars << "GlueBoolGroup* __" << name << "(&___" << name << ");\n";
}
}
/// Generate the boolean groups structures. These structures contain
/// boolean values that are grouped together. For example, %IX0.0 and %IX0.1
/// are generated as a group and then that group is referred to from
/// the integrated glue. This function generates the definitions
/// of the bool groups.
void generateBoolGroups(ostream& glueVars, list<IecVar>& all_vars) {
map<uint16_t, array<string, 8>> inputs;
map<uint16_t, array<string, 8>> outputs;
map<uint16_t, array<string, 8>> memory;
for (auto it_var = all_vars.begin(); it_var != all_vars.end(); ) {
const char sizeFlag = it_var->name[3];
if (sizeFlag != 'X') {
// We only care about the boolen locations here.
++it_var;
continue;
}
const char directionFlag = it_var->name[2];
map<uint16_t, array<string, 8>>* container;
switch (directionFlag) {
case 'I':
container = &inputs;
break;
case 'Q':
container = &outputs;
break;
default:
container = &memory;
break;
}
uint16_t pos1 = it_var->pos1;
uint16_t pos2 = it_var->pos2;
string name = it_var->name;
auto it_group = container->find(pos1);
if (it_group == container->end()) {
(*container)[pos1] = array<string, 8>();
// Replace the name with our special name
it_var->name = "__";
it_var->name += directionFlag;
it_var->name += 'G';
it_var->name += to_string(pos1);
it_var->pos2 = 0;
++it_var;
} else {
// Since we have seen this before, remove it from the
// list of variables so that when we generate the
// integrated glue, we only see the first item in
// the group, not each item
it_var = all_vars.erase(it_var);
}
(*container)[pos1][pos2] = name;
}
// Now that we have them grouped into the groups that we care about
// generate the intermediate glue bindings for the items
generateBoolGroups(glueVars, 'I', inputs);
generateBoolGroups(glueVars, 'Q', outputs);
generateBoolGroups(glueVars, 'M', memory);
}
void generateIntegratedGlue(ostream& glueVars, const list<IecVar>& all_vars) {
glueVars << "/// The size of the array of glue variables.\n";
glueVars << "extern std::uint16_t const OPLCGLUE_GLUE_SIZE(";
@ -353,16 +446,17 @@ void generateIntegratedGlue(ostream& glueVars, const list<IecVar>& all_vars) {
glueVars << "/// The packed glue variables.\n";
glueVars << "extern const GlueVariable oplc_glue_vars[] = {\n";
for (auto it = all_vars.begin(); it != all_vars.end(); ++it) {
const char directionFlag = (*it).name[2];
const char sizeFlag = (*it).name[3];
string name = it->name;
const char directionFlag = it->name[2];
const char sizeFlag = it->name[3];
glueVars << " {";
glueVars << " IECLDT_" << fromDirectionFlag(directionFlag) << ",";
glueVars << " IECLST_" << fromSizeFlag(sizeFlag) << ",";
glueVars << " " << (*it).pos1 << ",";
glueVars << " " << (*it).pos2 << ",";
glueVars << " IECVT_" << (*it).type << ", ";
glueVars << " " << (*it).name << " },\n";
glueVars << " " << it->pos1 << ",";
glueVars << " " << it->pos2 << ",";
glueVars << " IECVT_" << it->type << ", ";
glueVars << " " << name << " },\n";
}
glueVars << "};\n\n";
}
@ -421,6 +515,7 @@ void generateBody(istream& locatedVars, ostream& glueVars, md5_byte_t digest[16]
glueVars << "}\n\n";
// Generate the unified glue variables
generateBoolGroups(glueVars, all_vars);
generateIntegratedGlue(glueVars, all_vars);
// Finish the checksum value

View File

@ -69,109 +69,132 @@ SCENARIO("", "") {
WHEN("Contains single BOOL at %IX0") {
std::stringstream input_stream("__LOCATED_VAR(BOOL,__IX0,I,X,0)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tbool_input[0][0] = __IX0;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_BIT, 0, 0, IECVT_BOOL, __IX0 },\n\
};\n\n";
const char* expected = PREFIX "\tbool_input[0][0] = __IX0;\n" POSTFIX
"GlueBoolGroup ___IG0 { .index=0, .values={ __IX0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, } };\n"
"GlueBoolGroup* __IG0(&___IG0);\n"
GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_BIT, 0, 0, IECVT_BOOL, __IG0 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single BOOL at %QX0") {
std::stringstream input_stream("__LOCATED_VAR(BOOL,__QX0,Q,X,0)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tbool_output[0][0] = __QX0;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_OUT, IECLST_BIT, 0, 0, IECVT_BOOL, __QX0 },\n\
};\n\n";
const char* expected = PREFIX "\tbool_output[0][0] = __QX0;\n" POSTFIX
"GlueBoolGroup ___QG0 { .index=0, .values={ __QX0, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, } };\n"
"GlueBoolGroup* __QG0(&___QG0);\n"
GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_OUT, IECLST_BIT, 0, 0, IECVT_BOOL, __QG0 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains multiple BOOL at %QX0") {
std::stringstream input_stream("__LOCATED_VAR(BOOL,__QX0_0,Q,X,0,0)\n__LOCATED_VAR(BOOL,__QX0_2,Q,X,0,2)\n__LOCATED_VAR(BOOL,__QX1_3,Q,X,1,3)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tbool_output[0][0] = __QX0_0;\n\tbool_output[0][2] = __QX0_2;\n\tbool_output[1][3] = __QX1_3;\n" POSTFIX
"GlueBoolGroup ___QG0 { .index=0, .values={ __QX0_0, nullptr, __QX0_2, nullptr, nullptr, nullptr, nullptr, nullptr, } };\n"
"GlueBoolGroup* __QG0(&___QG0);\n"
"GlueBoolGroup ___QG1 { .index=1, .values={ nullptr, nullptr, nullptr, __QX1_3, nullptr, nullptr, nullptr, nullptr, } };\n"
"GlueBoolGroup* __QG1(&___QG1);\n"
GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(2);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_OUT, IECLST_BIT, 0, 0, IECVT_BOOL, __QG0 },\n"
" { IECLDT_OUT, IECLST_BIT, 1, 0, IECVT_BOOL, __QG1 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single BYTE at %IB0") {
std::stringstream input_stream("__LOCATED_VAR(BYTE,__IB0,I,B,0)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX"\tbyte_input[0] = __IB0;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_BYTE, 0, 0, IECVT_BYTE, __IB0 },\n\
};\n\n";
const char* expected = PREFIX"\tbyte_input[0] = __IB0;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_BYTE, 0, 0, IECVT_BYTE, __IB0 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single SINT at %IB1") {
std::stringstream input_stream("__LOCATED_VAR(SINT,__IB1,I,B,1)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tbyte_input[1] = __IB1;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_BYTE, 1, 0, IECVT_SINT, __IB1 },\n\
};\n\n";
const char* expected = PREFIX "\tbyte_input[1] = __IB1;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_BYTE, 1, 0, IECVT_SINT, __IB1 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single SINT at %QB1") {
std::stringstream input_stream("__LOCATED_VAR(SINT,__QB1,Q,B,1)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tbyte_output[1] = __QB1;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_OUT, IECLST_BYTE, 1, 0, IECVT_SINT, __QB1 },\n\
};\n\n";
const char* expected = PREFIX "\tbyte_output[1] = __QB1;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_OUT, IECLST_BYTE, 1, 0, IECVT_SINT, __QB1 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single USINT at %IB2") {
std::stringstream input_stream("__LOCATED_VAR(USINT,__IB2,I,B,2)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tbyte_input[2] = __IB2;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_BYTE, 2, 0, IECVT_USINT, __IB2 },\n\
};\n\n";
const char* expected = PREFIX "\tbyte_input[2] = __IB2;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_BYTE, 2, 0, IECVT_USINT, __IB2 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single WORD at %IW0") {
std::stringstream input_stream("__LOCATED_VAR(WORD,__IW0,I,W,0)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tint_input[0] = __IW0;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_WORD, 0, 0, IECVT_WORD, __IW0 },\n\
};\n\n";
const char* expected = PREFIX "\tint_input[0] = __IW0;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_WORD, 0, 0, IECVT_WORD, __IW0 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single WORD at %QW0") {
std::stringstream input_stream("__LOCATED_VAR(WORD,__QW0,Q,W,0)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tint_output[0] = __QW0;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_OUT, IECLST_WORD, 0, 0, IECVT_WORD, __QW0 },\n\
};\n\n";
const char* expected = PREFIX "\tint_output[0] = __QW0;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_OUT, IECLST_WORD, 0, 0, IECVT_WORD, __QW0 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single INT at %IW1") {
std::stringstream input_stream("__LOCATED_VAR(INT,__IW1,I,W,1)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tint_input[1] = __IW1;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_WORD, 1, 0, IECVT_INT, __IW1 },\n\
};\n\n";
const char* expected = PREFIX "\tint_input[1] = __IW1;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_WORD, 1, 0, IECVT_INT, __IW1 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
@ -179,12 +202,12 @@ extern const GlueVariable oplc_glue_vars[] = {\n\
std::stringstream input_stream("__LOCATED_VAR(UINT,__IW2,I,W,2)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tint_input[2] = __IW2;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_WORD, 2, 0, IECVT_UINT, __IW2 },\n\
};\n\n";
const char* expected = PREFIX "\tint_input[2] = __IW2;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_WORD, 2, 0, IECVT_UINT, __IW2 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
@ -193,61 +216,61 @@ extern const GlueVariable oplc_glue_vars[] = {\n\
generateBody(input_stream, output_stream, digest);
// Note that the type-separate glue does not support REAL types
const char* expected = PREFIX POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(2);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_IN, IECLST_DOUBLEWORD, 0, 0, IECVT_REAL, __ID0 },\n\
{ IECLDT_IN, IECLST_DOUBLEWORD, 10, 0, IECVT_REAL, __ID10 },\n\
};\n\n";
const char* expected = PREFIX POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(2);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_IN, IECLST_DOUBLEWORD, 0, 0, IECVT_REAL, __ID0 },\n"
" { IECLDT_IN, IECLST_DOUBLEWORD, 10, 0, IECVT_REAL, __ID10 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single INT at %MW2") {
std::stringstream input_stream("__LOCATED_VAR(INT,__MW2,M,W,2)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tint_memory[2] = __MW2;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_MEM, IECLST_WORD, 2, 0, IECVT_INT, __MW2 },\n\
};\n\n";
const char* expected = PREFIX "\tint_memory[2] = __MW2;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_MEM, IECLST_WORD, 2, 0, IECVT_INT, __MW2 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single DWORD at %MD0") {
std::stringstream input_stream("__LOCATED_VAR(DWORD,__MD2,M,D,2)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tdint_memory[2] = (IEC_DINT *)__MD2;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_MEM, IECLST_DOUBLEWORD, 2, 0, IECVT_DWORD, __MD2 },\n\
};\n\n";
const char* expected = PREFIX "\tdint_memory[2] = (IEC_DINT *)__MD2;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_MEM, IECLST_DOUBLEWORD, 2, 0, IECVT_DWORD, __MD2 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single LINT at %ML1") {
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1,M,L,1)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tlint_memory[1] = (IEC_LINT *)__ML1;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_MEM, IECLST_LONGWORD, 1, 0, IECVT_LINT, __ML1 },\n\
};\n\n";
const char* expected = PREFIX "\tlint_memory[1] = (IEC_LINT *)__ML1;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_MEM, IECLST_LONGWORD, 1, 0, IECVT_LINT, __ML1 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
WHEN("Contains single LINT at %ML1024") {
std::stringstream input_stream("__LOCATED_VAR(LINT,__ML1024,M,L,1024)");
generateBody(input_stream, output_stream, digest);
const char* expected = PREFIX "\tspecial_functions[0] = (IEC_LINT *)__ML1024;\n" POSTFIX GLUE_PREFIX\
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n\
/// The packed glue variables.\n\
extern const GlueVariable oplc_glue_vars[] = {\n\
{ IECLDT_MEM, IECLST_LONGWORD, 1024, 0, IECVT_LINT, __ML1024 },\n\
};\n\n";
const char* expected = PREFIX "\tspecial_functions[0] = (IEC_LINT *)__ML1024;\n" POSTFIX GLUE_PREFIX
"extern std::uint16_t const OPLCGLUE_GLUE_SIZE(1);\n"
"/// The packed glue variables.\n"
"extern const GlueVariable oplc_glue_vars[] = {\n"
" { IECLDT_MEM, IECLST_LONGWORD, 1024, 0, IECVT_LINT, __ML1024 },\n"
"};\n\n";
REQUIRE(output_stream.str() == expected);
}
}