Skip to content

Commit

Permalink
Added support for offset on DNP3
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagoralves authored Dec 27, 2018
1 parent 1d40db5 commit 2c330b5
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 18 deletions.
73 changes: 73 additions & 0 deletions webserver/core/dnp3.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# ----------------------------------------------------------------
# Configuration file for DNP3
#-----------------------------------------------------------------


# Use this file to fill out DNP3 settings for your Open PLC
# Uncomment settings as you want them


# Link Settings
#-----------------------------------------------------------------

# local address
local_address = 10

# master address allowed
remote_address = 1

# keep alive timeout
# time (s) or MAX
# keep_alive_timeout = MAX


# Parameters
#-----------------------------------------------------------------

# enable unsolicited reporting if master allows it
# True or False
enable_unsolicited = True

# how long (seconds) the outstation will allow a operate
# to follow a select
# select_timeout = 10

# max control commands for a single APDU
# max_controls_per_request = 16

# maximum fragment size the outstation will recieve
# default is the max value
# max_rx_frag_size = 2048

# maximum fragment size the outstation will send if
# it needs to fragment. Default is the max falue
# max_tx_frag_size = 2048

# size of the event buffer
event_buffer_size = 10

# number of values the outstation will report at once
# AKA database size
database_size = 8

# First data point offset for DI - required if slave device used (the address should represent 1st data point of slave device)
offset_di = 800

# First data point offset for DO - required if slave device used (the address should represent 1st data point of slave device)
offset_do = 800

# First data point offset for AI - required if slave device used (the address should represent 1st data point of slave device)
offset_ai = 100

# First data point offset for AO - required if slave device used (the address should represent 1st data point of slave device)
offset_ao = 100

#Timeout for solicited confirms
# in MS
# sol_confirm_timeout = 5000

#Timeout for unsolicited confirms (ms)
# unsol_conrfirm_timeout = 5000

#Timeout for unsolicited retries (ms)
# unsol_retry_timeout = 5000
66 changes: 48 additions & 18 deletions webserver/core/dnp3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@

#define OPLC_CYCLE 50000000


// Initial offset parameters (yurgen1975)
int offset_di = 0;
int offset_do = 0;
int offset_ai = 0;
int offset_ao = 0;

using namespace std;
using namespace opendnp3;
Expand Down Expand Up @@ -84,18 +88,22 @@ static inline std::string &trim(std::string &s) {
class CommandCallback: public ICommandHandler {
public:

//CROB
//CROB - changed to support offsets (yurgen1975)
virtual CommandStatus Select(const ControlRelayOutputBlock& command, uint16_t index) {
index = index + offset_di;
return CommandStatus::SUCCESS;
}
virtual CommandStatus Operate(const ControlRelayOutputBlock& command, uint16_t index, OperateType opType) {
index = index + offset_di;
auto code = command.functionCode;
CommandStatus return_val;



if(code == ControlCode::LATCH_ON || code == ControlCode::LATCH_OFF) {
return_val = CommandStatus::SUCCESS;

IEC_BOOL crob_val = (code == ControlCode::LATCH_ON);

pthread_mutex_lock(&bufferLock);
if(bool_output[index/8][index%8] != NULL) {
*bool_output[index/8][index%8] = crob_val;
Expand All @@ -105,15 +113,16 @@ class CommandCallback: public ICommandHandler {
else {
return_val = CommandStatus::NOT_SUPPORTED;
}

return return_val;
}

//Analog Out
//Analog Out - changed to support offsets (yurgen1975)
virtual CommandStatus Select(const AnalogOutputInt16& command, uint16_t index) {
index = index + offset_ao;
return CommandStatus::SUCCESS;
}
virtual CommandStatus Operate(const AnalogOutputInt16& command, uint16_t index, OperateType opType) {
index = index + offset_ao;
auto ao_val = command.value;
pthread_mutex_lock(&bufferLock);
if(index < MIN_16B_RANGE && int_output[index] != NULL) {
Expand Down Expand Up @@ -194,27 +203,28 @@ class CommandCallback: public ICommandHandler {

//------------------------------------------------------------------
// Function to update DNP3 values every time they may have changed
// Updated by Yurgen1975 to support slave devices: DI/DO address 800 and AI/AO address 100
//------------------------------------------------------------------
void update_vals(std::shared_ptr<IOutstation> outstation){
UpdateBuilder builder;
// Update Discrete input (Binary input)
for(int i = 1; i < MAX_DISCRETE_INPUT; i++) {
builder.Update(Binary((bool)(*bool_input[i/8][i%8])), i);

// Update Discrete input (Binary input) - changed to support offsets (yurgen1975)
for(int i = offset_di; i < MAX_DISCRETE_INPUT; i++) {
builder.Update(Binary((bool)(*bool_input[i/8][i%8])), i-offset_di);
}
// Update Coils (Binary Output)
for(int i = 0; i < MAX_COILS; i++) {
builder.Update(BinaryOutputStatus((bool)(*bool_output[i/8][i%8])), i);

// Update Coils (Binary Output) - changed to support offsets (yurgen1975)
for(int i = offset_do; i < MAX_COILS; i++) {
builder.Update(BinaryOutputStatus((bool)(*bool_output[i/8][i%8])), i-offset_do);
}
// Update Input Registers (Analog Input)
for (int i = 0; i < MAX_INP_REGS; i++) {
builder.Update(Analog((int)(*int_input[i])), i);

// Update Input Registers (Analog Input) - changed to support offsets (yurgen1975)
for (int i = offset_ai; i < MAX_INP_REGS; i++) {
builder.Update(Analog((int)(*int_input[i])), i-offset_ai);
}
// Update Holding Registers (Analog Output)
for (int i = 0; i < MIN_16B_RANGE; i++) {
builder.Update(AnalogOutputStatus((int)(*int_output[i])), i);

// Update Holding Registers (Analog Output) - changed to support offsets (yurgen1975)
for (int i = offset_ao; i < MIN_16B_RANGE; i++) {
builder.Update(AnalogOutputStatus((int)(*int_output[i])), i-offset_ao);
}
// Update Holding registers for memory
for (int i = MIN_16B_RANGE; i < MAX_16B_RANGE; i++) {
Expand Down Expand Up @@ -338,6 +348,25 @@ OutstationStackConfig parseDNP3Config() {
getline(iss, token, '=');
config.outstation.eventBufferConfig =
EventBufferConfig::AllTypes(atoi(token.c_str()));

// get offsets from dnp.cfg (yurgen1975)
} else if (token == "offset_di") {
getline(iss, token, '=');
offset_di = atoi(token.c_str());

} else if (token == "offset_do") {
getline(iss, token, '=');
offset_do = atoi(token.c_str());

} else if (token == "offset_ai") {
getline(iss, token, '=');
offset_ai = atoi(token.c_str());

} else if (token == "offset_ao") {
getline(iss, token, '=');
offset_ao = atoi(token.c_str());
// -------------------------------------------------------------------

} else if (token == "sol_confirm_timeout") {
getline(iss, token, '=');
config.outstation.params.solConfirmTimeout =
Expand All @@ -364,6 +393,7 @@ OutstationStackConfig parseDNP3Config() {
}
}
}

return config;
}

Expand Down

0 comments on commit 2c330b5

Please sign in to comment.