Skip to content
This repository has been archived by the owner on Sep 1, 2022. It is now read-only.

Commit

Permalink
Merge pull request #339 from dnp3/feature/distinct-soehandler
Browse files Browse the repository at this point in the history
Distinct SOE handler
  • Loading branch information
emgre committed Oct 4, 2019
2 parents 60200e9 + 928093b commit fd78464
Show file tree
Hide file tree
Showing 34 changed files with 361 additions and 186 deletions.
3 changes: 2 additions & 1 deletion cpp/examples/master-gprs/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <opendnp3/ConsoleLogger.h>
#include <opendnp3/DNP3Manager.h>
#include <opendnp3/LogLevels.h>
#include <opendnp3/master/PrintingSOEHandler.h>

#include <functional>
#include <iostream>
Expand Down Expand Up @@ -164,7 +165,7 @@ void init_handlers(command_handler_map_t& map)
const auto session = callbacks.get_outstation_session(address);
if (session)
{
session->ScanClasses(ClassField::AllClasses());
session->ScanClasses(ClassField::AllClasses(), PrintingSOEHandler::Create());
}
else
{
Expand Down
10 changes: 6 additions & 4 deletions cpp/examples/master-udp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,22 @@ int main(int argc, char* argv[])
stackConfig.link.LocalAddr = 1;
stackConfig.link.RemoteAddr = 10;

auto soeHandler = PrintingSOEHandler::Create();

// Create a new master on a previously declared port, with a
// name, log level, command acceptor, and config info. This
// returns a thread-safe interface used for sending commands.
auto master = channel->AddMaster("master", // id for logging
PrintingSOEHandler::Create(), // callback for data processing
soeHandler, // callback for data processing
DefaultMasterApplication::Create(), // master application instance
stackConfig // stack configuration
);

// do an integrity poll (Class 3/2/1/0) once per minute
auto integrityScan = master->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1));
auto integrityScan = master->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1), soeHandler);

// do a Class 1 exception poll every 5 seconds
auto exceptionScan = master->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2));
auto exceptionScan = master->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2), soeHandler);

// Enable the master. This will start communications.
master->Enable();
Expand All @@ -95,7 +97,7 @@ int main(int argc, char* argv[])
switch (cmd)
{
case ('a'):
master->ScanRange(GroupVariationID(1, 2), 0, 3);
master->ScanRange(GroupVariationID(1, 2), 0, 3, soeHandler);
break;
case ('d'):
master->PerformFunction("disable unsol", FunctionCode::DISABLE_UNSOLICITED,
Expand Down
28 changes: 25 additions & 3 deletions cpp/examples/master/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@
using namespace std;
using namespace opendnp3;

class TestSOEHandler : public ISOEHandler
{
virtual void begin_fragment(const ResponseInfo& info) {};
virtual void end_fragment(const ResponseInfo& info) {};

virtual void Process(const HeaderInfo& info, const ICollection<Indexed<Binary>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<DoubleBitBinary>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<Analog>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<Counter>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<FrozenCounter>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<BinaryOutputStatus>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<AnalogOutputStatus>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<OctetString>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<TimeAndInterval>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<BinaryCommandEvent>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<AnalogCommandEvent>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<SecurityStat>>& values) {};
virtual void Process(const HeaderInfo& info, const ICollection<DNPTime>& values) {};
};

int main(int argc, char* argv[])
{
// Specify what log levels to use. NORMAL is warning and above
Expand Down Expand Up @@ -65,11 +85,13 @@ int main(int argc, char* argv[])
stackConfig // stack configuration
);

auto test_soe_handler = std::make_shared<TestSOEHandler>();

// do an integrity poll (Class 3/2/1/0) once per minute
auto integrityScan = master->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1));
auto integrityScan = master->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1), test_soe_handler);

// do a Class 1 exception poll every 5 seconds
auto exceptionScan = master->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2));
auto exceptionScan = master->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2), test_soe_handler);

// Enable the master. This will start communications.
master->Enable();
Expand All @@ -95,7 +117,7 @@ int main(int argc, char* argv[])
switch (cmd)
{
case ('a'):
master->ScanRange(GroupVariationID(1, 2), 0, 3);
master->ScanRange(GroupVariationID(1, 2), 0, 3, test_soe_handler);
break;
case ('d'):
master->PerformFunction("disable unsol", FunctionCode::DISABLE_UNSOLICITED,
Expand Down
14 changes: 8 additions & 6 deletions cpp/examples/tls/master/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,20 +78,22 @@ int main(int argc, char* argv[])
stackConfig.link.LocalAddr = 1;
stackConfig.link.RemoteAddr = 10;

auto soeHandler = PrintingSOEHandler::Create();

// Create a new master on a previously declared port, with a
// name, log level, command acceptor, and config info. This
// returns a thread-safe interface used for sending commands.
auto master = channel->AddMaster("master", // id for logging
PrintingSOEHandler::Create(), // callback for data processing
auto master = channel->AddMaster("master", // id for logging
soeHandler, // callback for data processing
DefaultMasterApplication::Create(), // master application instance
stackConfig // stack configuration
stackConfig // stack configuration
);

// do an integrity poll (Class 3/2/1/0) once per minute
auto integrityScan = master->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1));
auto integrityScan = master->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1), soeHandler);

// do a Class 1 exception poll every 5 seconds
auto exceptionScan = master->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2));
auto exceptionScan = master->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2), soeHandler);

// Enable the master. This will start communications.
master->Enable();
Expand All @@ -112,7 +114,7 @@ int main(int argc, char* argv[])
switch (cmd)
{
case ('a'):
master->ScanRange(GroupVariationID(1, 2), 0, 3);
master->ScanRange(GroupVariationID(1, 2), 0, 3, soeHandler);
break;
case ('d'):
master->PerformFunction("disable unsol", FunctionCode::DISABLE_UNSOLICITED,
Expand Down
22 changes: 19 additions & 3 deletions cpp/lib/include/opendnp3/master/IMasterOperations.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "opendnp3/master/ICommandProcessor.h"
#include "opendnp3/master/IMasterScan.h"
#include "opendnp3/master/RestartOperationResult.h"
#include "opendnp3/master/ISOEHandler.h"
#include "opendnp3/master/TaskConfig.h"

#include <log4cpp/LogLevels.h>
Expand Down Expand Up @@ -60,6 +61,7 @@ class IMasterOperations : public ICommandProcessor
*/
virtual std::shared_ptr<IMasterScan> AddScan(TimeDuration period,
const std::vector<Header>& headers,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

Expand All @@ -69,6 +71,7 @@ class IMasterOperations : public ICommandProcessor
*/
virtual std::shared_ptr<IMasterScan> AddAllObjectsScan(GroupVariationID gvId,
TimeDuration period,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

Expand All @@ -78,6 +81,7 @@ class IMasterOperations : public ICommandProcessor
*/
virtual std::shared_ptr<IMasterScan> AddClassScan(const ClassField& field,
TimeDuration period,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

Expand All @@ -89,29 +93,41 @@ class IMasterOperations : public ICommandProcessor
uint16_t start,
uint16_t stop,
TimeDuration period,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

/**
* Initiate a single user defined scan via a vector of headers
*/
virtual void Scan(const std::vector<Header>& headers, const TaskConfig& config = TaskConfig::Default()) = 0;
virtual void Scan(const std::vector<Header>& headers,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

/**
* Initiate a single scan that requests all objects (0x06 qualifier code) for a certain group and variation
*/
virtual void ScanAllObjects(GroupVariationID gvId, const TaskConfig& config = TaskConfig::Default()) = 0;
virtual void ScanAllObjects(GroupVariationID gvId,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

/**
* Initiate a single class-based scan
*/
virtual void ScanClasses(const ClassField& field, const TaskConfig& config = TaskConfig::Default()) = 0;
virtual void ScanClasses(const ClassField& field,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

/**
* Initiate a single start/stop (range) scan
*/
virtual void ScanRange(GroupVariationID gvId,
uint16_t start,
uint16_t stop,
std::shared_ptr<ISOEHandler> soe_handler,
const TaskConfig& config = TaskConfig::Default())
= 0;

Expand Down
4 changes: 2 additions & 2 deletions cpp/lib/include/opendnp3/master/ISOEHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class ISOEHandler
public:
virtual ~ISOEHandler() = default;

virtual void begin_fragment(const ResponseInfo& info) = 0;
virtual void begin_fragment(const ResponseInfo& info) = 0;
virtual void end_fragment(const ResponseInfo& info) = 0;

virtual void Process(const HeaderInfo& info, const ICollection<Indexed<Binary>>& values) = 0;
Expand All @@ -63,7 +63,7 @@ class ISOEHandler
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<BinaryCommandEvent>>& values) = 0;
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<AnalogCommandEvent>>& values) = 0;
virtual void Process(const HeaderInfo& info, const ICollection<Indexed<SecurityStat>>& values) = 0;
virtual void Process(const HeaderInfo& info, const ICollection<DNPTime>& values) = 0;
virtual void Process(const HeaderInfo& info, const ICollection<DNPTime>& values) = 0;
};

} // namespace opendnp3
Expand Down
4 changes: 2 additions & 2 deletions cpp/lib/src/master/EventScanTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ namespace opendnp3

EventScanTask::EventScanTask(const std::shared_ptr<TaskContext>& context,
IMasterApplication& application,
ISOEHandler& soeHandler,
std::shared_ptr<ISOEHandler> soeHandler,
ClassField classes,
const log4cpp::Logger& logger)
: PollTaskBase(context, application, soeHandler, TaskBehavior::ReactsToIINOnly(), logger, TaskConfig::Default()),
: PollTaskBase(context, application, std::move(soeHandler), TaskBehavior::ReactsToIINOnly(), logger, TaskConfig::Default()),
classes(classes.OnlyEventClasses())
{
}
Expand Down
2 changes: 1 addition & 1 deletion cpp/lib/src/master/EventScanTask.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class EventScanTask final : public PollTaskBase
public:
EventScanTask(const std::shared_ptr<TaskContext>& context,
IMasterApplication& application,
ISOEHandler& soeHandler,
std::shared_ptr<ISOEHandler> soeHandler,
ClassField classes,
const log4cpp::Logger& logger);

Expand Down
59 changes: 41 additions & 18 deletions cpp/lib/src/master/MasterContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ MContext::MContext(const Addresses& addresses,
SOEHandler(SOEHandler),
application(application),
scheduler(std::move(scheduler)),
tasks(params, logger, *application, *SOEHandler),
tasks(params, logger, *application, SOEHandler),
txBuffer(params.maxTxFragSize),
tstate(TaskState::IDLE)
{
Expand Down Expand Up @@ -294,67 +294,90 @@ void MContext::StartResponseTimer()
this->responseTimer = this->executor->start(this->params.responseTimeout.value, timeout);
}

std::shared_ptr<IMasterTask> MContext::AddScan(TimeDuration period, const HeaderBuilderT& builder, TaskConfig config)
std::shared_ptr<IMasterTask> MContext::AddScan(TimeDuration period,
const HeaderBuilderT& builder,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
auto task = std::make_shared<UserPollTask>(
this->tasks.context, builder,
TaskBehavior::ImmediatePeriodic(period, params.taskRetryPeriod, params.maxTaskRetryPeriod), true, *application,
*SOEHandler, logger, config);
soe_handler, logger, config);
this->ScheduleRecurringPollTask(task);
return task;
}

std::shared_ptr<IMasterTask> MContext::AddClassScan(const ClassField& field, TimeDuration period, TaskConfig config)
std::shared_ptr<IMasterTask> MContext::AddClassScan(const ClassField& field,
TimeDuration period,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
auto build = [field](HeaderWriter& writer) -> bool { return build::WriteClassHeaders(writer, field); };
return this->AddScan(period, build, config);
return this->AddScan(period, build, soe_handler, config);
}

std::shared_ptr<IMasterTask> MContext::AddAllObjectsScan(GroupVariationID gvId, TimeDuration period, TaskConfig config)
std::shared_ptr<IMasterTask> MContext::AddAllObjectsScan(GroupVariationID gvId,
TimeDuration period,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
auto build = [gvId](HeaderWriter& writer) -> bool { return writer.WriteHeader(gvId, QualifierCode::ALL_OBJECTS); };
return this->AddScan(period, build, config);
return this->AddScan(period, build, soe_handler, config);
}

std::shared_ptr<IMasterTask> MContext::AddRangeScan(
GroupVariationID gvId, uint16_t start, uint16_t stop, TimeDuration period, TaskConfig config)
std::shared_ptr<IMasterTask> MContext::AddRangeScan(GroupVariationID gvId,
uint16_t start,
uint16_t stop,
TimeDuration period,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
auto build = [gvId, start, stop](HeaderWriter& writer) -> bool {
return writer.WriteRangeHeader<ser4cpp::UInt16>(QualifierCode::UINT16_START_STOP, gvId, start, stop);
};
return this->AddScan(period, build, config);
return this->AddScan(period, build, soe_handler, config);
}

void MContext::Scan(const HeaderBuilderT& builder, TaskConfig config)
void MContext::Scan(const HeaderBuilderT& builder,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
const auto timeout = Timestamp(this->executor->get_time()) + params.taskStartTimeout;

auto task
= std::make_shared<UserPollTask>(this->tasks.context, builder, TaskBehavior::SingleExecutionNoRetry(timeout),
false, *application, *SOEHandler, logger, config);
false, *application, soe_handler, logger, config);

this->ScheduleAdhocTask(task);
}

void MContext::ScanClasses(const ClassField& field, TaskConfig config)
void MContext::ScanClasses(const ClassField& field,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
auto configure = [field](HeaderWriter& writer) -> bool { return build::WriteClassHeaders(writer, field); };
this->Scan(configure, config);
this->Scan(configure, soe_handler, config);
}

void MContext::ScanAllObjects(GroupVariationID gvId, TaskConfig config)
void MContext::ScanAllObjects(GroupVariationID gvId,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
auto configure
= [gvId](HeaderWriter& writer) -> bool { return writer.WriteHeader(gvId, QualifierCode::ALL_OBJECTS); };
this->Scan(configure, config);
this->Scan(configure, soe_handler, config);
}

void MContext::ScanRange(GroupVariationID gvId, uint16_t start, uint16_t stop, TaskConfig config)
void MContext::ScanRange(GroupVariationID gvId,
uint16_t start,
uint16_t stop,
std::shared_ptr<ISOEHandler> soe_handler,
TaskConfig config)
{
auto configure = [gvId, start, stop](HeaderWriter& writer) -> bool {
return writer.WriteRangeHeader<ser4cpp::UInt16>(QualifierCode::UINT16_START_STOP, gvId, start, stop);
};
this->Scan(configure, config);
this->Scan(configure, soe_handler, config);
}

void MContext::Write(const TimeAndInterval& value, uint16_t index, TaskConfig config)
Expand Down

0 comments on commit fd78464

Please sign in to comment.