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

Commit

Permalink
Mising tsmode -> tsquality.
Browse files Browse the repository at this point in the history
  • Loading branch information
emgre committed Jun 20, 2019
1 parent bc56488 commit dba372f
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 109 deletions.
132 changes: 39 additions & 93 deletions cpp/examples/outstation/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,87 +21,24 @@
#include <opendnp3/DNP3Manager.h>
#include <opendnp3/LogLevels.h>
#include <opendnp3/channel/PrintingChannelListener.h>
#include <opendnp3/outstation/IUpdateHandler.h>
#include <opendnp3/outstation/SimpleCommandHandler.h>
#include <opendnp3/outstation/UpdateBuilder.h>

#include <chrono>
#include <iostream>
#include <string>
#include <thread>

using namespace std;
using namespace opendnp3;

class OutstationApplication : public IOutstationApplication
{
public:
virtual bool SupportsWriteAbsoluteTime()
{
return true;
}

/// Write the time to outstation, only called if SupportsWriteAbsoluteTime return true
/// @return boolean value indicating if the time value supplied was accepted. Returning
/// false will cause the outstation to set IIN 2.3 (PARAM_ERROR) in its response.
/// The outstation should clear its NEED_TIME field when handling this response
virtual bool WriteAbsoluteTime(const UTCTimestamp& timestamp)
{
this->lastTimestamp = timestamp;
this->lastUpdate = std::chrono::steady_clock::now();
return true;
}

/// Queries whether the outstation supports the writing of TimeAndInterval
/// If this function returns false, WriteTimeAndInterval will never be called
/// and the outstation will return IIN 2.1 (FUNC_NOT_SUPPORTED) when it receives this request
virtual bool SupportsWriteTimeAndInterval()
{
return false;
}

/// Write one or more TimeAndInterval values. Only called if SupportsWriteTimeAndInterval returns true.
/// The outstation application code is reponsible for updating TimeAndInterval values in the database if this
/// behavior is desired
/// @return boolean value indicating if the values supplied were accepted. Returning
/// false will cause the outstation to set IIN 2.3 (PARAM_ERROR) in its response.
virtual bool WriteTimeAndInterval(const ICollection<Indexed<TimeAndInterval>>& values)
{
return false;
}

/// Returns the application-controlled IIN field
virtual ApplicationIIN GetApplicationIIN() const
{
ApplicationIIN result;
result.needTime = !IsTimeValid();
return result;
}

DNPTime GetCurrentTime() const
{
auto result = DNPTime(lastTimestamp.msSinceEpoch + std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - lastUpdate).count());
result.quality = IsTimeValid() ? TimestampQuality::SYNCHRONIZED : TimestampQuality::UNSYNCHRONIZED;
return result;
}

bool IsTimeValid() const
{
return std::chrono::steady_clock::now() - lastUpdate <= std::chrono::seconds(10);
}

UTCTimestamp lastTimestamp;
std::chrono::steady_clock::time_point lastUpdate = std::chrono::steady_clock::time_point(std::chrono::milliseconds(0));
};

DatabaseConfig ConfigureDatabase()
{
DatabaseConfig config(10); // 10 of each type

config.analog_input[0].clazz = PointClass::Class2;
config.analog_input[0].svariation = StaticAnalogVariation::Group30Var5;
config.analog_input[0].evariation = EventAnalogVariation::Group32Var7;

return config;
}

Expand All @@ -111,52 +48,69 @@ struct State
double value = 0;
bool binary = false;
DoubleBit dbit = DoubleBit::DETERMINED_OFF;
uint8_t octetStringValue = 1;
};

void AddUpdates(UpdateBuilder& builder, State& state, const OutstationApplication& app, const std::string& arguments);
void AddUpdates(UpdateBuilder& builder, State& state, const std::string& arguments);

int main(int argc, char* argv[])
{
if (argc != 4)
{
std::cout << "usage: master-gprs-tls-demo <ca certificate> <certificate chain> <private key>" << std::endl;
return -1;
}

std::string caCertificate(argv[1]);
std::string certificateChain(argv[2]);
std::string privateKey(argv[3]);

std::cout << "Using CA certificate: " << caCertificate << std::endl;
std::cout << "Using certificate chain: " << certificateChain << std::endl;
std::cout << "Using private key file: " << privateKey << std::endl;

// Specify what log levels to use. NORMAL is warning and above
// You can add all the comms logging by uncommenting below.
const auto logLevels = levels::NORMAL | levels::ALL_COMMS;

// This is the main point of interaction with the stack
// Allocate a single thread to the pool since this is a single outstation
// Log messages to the console
DNP3Manager manager(1, ConsoleLogger::Create());

std::error_code ec;

// Create a TCP server (listener)
auto channel = manager.AddTCPServer("server", logLevels, ServerAcceptMode::CloseExisting, IPEndpoint("0.0.0.0", 20000),
PrintingChannelListener::Create());
auto channel = manager.AddTLSServer("server", logLevels, ServerAcceptMode::CloseExisting, IPEndpoint("0.0.0.0", 20001),
TLSConfig(caCertificate, certificateChain, privateKey, 2),
PrintingChannelListener::Create(), ec);

if (ec)
{
std::cout << "Unable to create tls server: " << ec.message() << std::endl;
return ec.value();
}

// The main object for a outstation. The defaults are useable,
// but understanding the options are important.
OutstationStackConfig config(ConfigureDatabase());
OutstationStackConfig stackConfig(ConfigureDatabase());

// Specify the maximum size of the event buffers
config.outstation.eventBufferConfig = EventBufferConfig::AllTypes(10);
// specify the maximum size of the event buffers
stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(10);

// you can override an default outstation parameters here
// in this example, we've enabled the oustation to use unsolicted reporting
// if the master enables it
config.outstation.params.allowUnsolicited = false;
stackConfig.outstation.params.allowUnsolicited = true;

// You can override the default link layer settings here
// in this example we've changed the default link layer addressing
config.link.LocalAddr = 10;
config.link.RemoteAddr = 1;
config.link.KeepAliveTimeout = TimeDuration::Max();

auto app = std::make_shared<OutstationApplication>();
stackConfig.link.LocalAddr = 10;
stackConfig.link.RemoteAddr = 1;

// Create a new outstation with a log level, command handler, and
// config info this returns a thread-safe interface used for
// updating the outstation's database.
auto outstation = channel->AddOutstation("outstation", SuccessCommandHandler::Create(),
app, config);
DefaultOutstationApplication::Create(), stackConfig);

// Enable the outstation and start communications
outstation->Enable();
Expand All @@ -168,24 +122,23 @@ int main(int argc, char* argv[])
while (true)
{
std::cout << "Enter one or more measurement changes then press <enter>" << std::endl;
std::cout << "c = counter, b = binary, d = doublebit, a = analog, o = octet string, 'quit' = exit" << std::endl;
std::cout << "c = counter, b = binary, d = doublebit, a = analog, 'quit' = exit" << std::endl;
std::cin >> input;

if (input == "quit")
return 0; // DNP3Manager destructor cleanups up everything automatically
return 0;
else
{
// update measurement values based on input string
UpdateBuilder builder;
AddUpdates(builder, state, *app, input);
AddUpdates(builder, state, input);
outstation->Apply(builder.Build());
}
}

return 0;
}

void AddUpdates(UpdateBuilder& builder, State& state, const OutstationApplication& app, const std::string& arguments)
void AddUpdates(UpdateBuilder& builder, State& state, const std::string& arguments)
{
for (const char& c : arguments)
{
Expand All @@ -205,7 +158,7 @@ void AddUpdates(UpdateBuilder& builder, State& state, const OutstationApplicatio
}
case ('b'):
{
builder.Update(Binary(state.binary, Flags(0x81), app.GetCurrentTime()), 0);
builder.Update(Binary(state.binary), 0);
state.binary = !state.binary;
break;
}
Expand All @@ -216,13 +169,6 @@ void AddUpdates(UpdateBuilder& builder, State& state, const OutstationApplicatio
= (state.dbit == DoubleBit::DETERMINED_OFF) ? DoubleBit::DETERMINED_ON : DoubleBit::DETERMINED_OFF;
break;
}
case ('o'):
{
OctetString value(Buffer(&state.octetStringValue, 1));
builder.Update(value, 0);
state.octetStringValue += 1;
break;
}
default:
break;
}
Expand Down
8 changes: 4 additions & 4 deletions cpp/lib/include/opendnp3/master/HeaderInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@ class HeaderInfo
HeaderInfo()
: gv(GroupVariation::UNKNOWN),
qualifier(QualifierCode::UNDEFINED),
tsmode(TimestampQuality::INVALID),
tsquality(TimestampQuality::INVALID),
isEventVariation(false),
flagsValid(false),
headerIndex(0)
{
}

HeaderInfo(GroupVariation gv_, QualifierCode qualifier_, TimestampQuality tsmode_, uint32_t headerIndex_)
HeaderInfo(GroupVariation gv_, QualifierCode qualifier_, TimestampQuality tsquality_, uint32_t headerIndex_)
: gv(gv_),
qualifier(qualifier_),
tsmode(tsmode_),
tsquality(tsquality_),
isEventVariation(IsEvent(gv_)),
flagsValid(HasFlags(gv_)),
headerIndex(headerIndex_)
Expand All @@ -60,7 +60,7 @@ class HeaderInfo
/// The qualifier code enumeration for the header
QualifierCode qualifier;
/// Enumeration that provides information about the validity of timestamps on the associated objects
TimestampQuality tsmode;
TimestampQuality tsquality;
/// True if the specfied variation is an event variation
bool isEventVariation;
/// True if the flags on the value were present on underlying type, false if online is just assumed
Expand Down
4 changes: 2 additions & 2 deletions cpp/lib/include/opendnp3/master/PrintingSOEHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,10 @@ class PrintingSOEHandler final : public ISOEHandler
return oss.str();
}

static std::string GetTimeString(TimestampQuality tsmode)
static std::string GetTimeString(TimestampQuality tsquality)
{
std::ostringstream oss;
switch (tsmode)
switch (tsquality)
{
case (TimestampQuality::SYNCHRONIZED):
return "synchronized";
Expand Down
4 changes: 2 additions & 2 deletions cpp/lib/src/master/MeasurementHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ class MeasurementHandler final : public IAPDUHandler
}

template<class T>
IINField LoadValues(const HeaderRecord& record, TimestampQuality tsmode, const ICollection<Indexed<T>>& values)
IINField LoadValues(const HeaderRecord& record, TimestampQuality tsquality, const ICollection<Indexed<T>>& values)
{
this->CheckForTxStart();
HeaderInfo info(record.enumeration, record.GetQualifierCode(), tsmode, record.headerIndex);
HeaderInfo info(record.enumeration, record.GetQualifierCode(), tsquality, record.headerIndex);
this->pSOEHandler->Process(info, values);
return IINField();
}
Expand Down
4 changes: 2 additions & 2 deletions cpp/tests/unit/TestMaster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ TEST_CASE(SUITE("ReceiveCTOSynchronized"))
auto record = t.meas->binarySOE[7];
bool equal = record.meas == Binary(true, Flags(0x01), DNPTime(0x04, TimestampQuality::SYNCHRONIZED)); // timestamp is 4
REQUIRE(equal);
REQUIRE(record.info.tsmode == TimestampQuality::SYNCHRONIZED);
REQUIRE(record.info.tsquality == TimestampQuality::SYNCHRONIZED);
}

TEST_CASE(SUITE("ReceiveCTOUnsynchronized"))
Expand All @@ -594,7 +594,7 @@ TEST_CASE(SUITE("ReceiveCTOUnsynchronized"))
auto record = t.meas->binarySOE[7];
bool equal = record.meas == Binary(true, Flags(0x01), DNPTime(0x04, TimestampQuality::UNSYNCHRONIZED)); // timestamp is 4
REQUIRE(equal);
REQUIRE(record.info.tsmode == TimestampQuality::UNSYNCHRONIZED);
REQUIRE(record.info.tsquality == TimestampQuality::UNSYNCHRONIZED);
}

TEST_CASE(SUITE("ReceiveIINinResponses"))
Expand Down
6 changes: 3 additions & 3 deletions cpp/tests/unit/TestMeasurementHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ TEST_CASE(SUITE("parses g121v1 correctly"))

auto& stat = soe.securityStatSOE[2];

REQUIRE(stat.info.tsmode == TimestampQuality::INVALID);
REQUIRE(stat.info.tsquality == TimestampQuality::INVALID);
REQUIRE(stat.info.gv == GroupVariation::Group121Var1);
REQUIRE_FALSE(stat.info.isEventVariation);
REQUIRE(stat.meas.value.count == 8);
Expand All @@ -74,7 +74,7 @@ TEST_CASE(SUITE("parses g122v1 correctly"))

auto& stat = soe.securityStatSOE[3];

REQUIRE(stat.info.tsmode == TimestampQuality::INVALID);
REQUIRE(stat.info.tsquality == TimestampQuality::INVALID);
REQUIRE(stat.info.gv == GroupVariation::Group122Var1);
REQUIRE(stat.meas.value.count == 8);
REQUIRE(stat.meas.value.assocId == 7);
Expand All @@ -93,7 +93,7 @@ TEST_CASE(SUITE("parses g122v2 correctly"))

auto& stat = soe.securityStatSOE[3];

REQUIRE(stat.info.tsmode == TimestampQuality::SYNCHRONIZED);
REQUIRE(stat.info.tsquality == TimestampQuality::SYNCHRONIZED);
REQUIRE(stat.info.gv == GroupVariation::Group122Var2);
REQUIRE(stat.info.isEventVariation);
REQUIRE(stat.meas.value.count == 8);
Expand Down
2 changes: 1 addition & 1 deletion dotnet/CLRAdapter/src/SOEHandlerAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace Automatak
HeaderInfo ^ ConvertHeaderInfo(const opendnp3::HeaderInfo& info)
{
return gcnew HeaderInfo((GroupVariation)info.gv, (QualifierCode)info.qualifier,
(TimestampQuality)info.tsmode, info.isEventVariation, info.flagsValid,
(TimestampQuality)info.tsquality, info.isEventVariation, info.flagsValid,
info.headerIndex);
}

Expand Down
4 changes: 2 additions & 2 deletions java/cpp/adapters/SOEHandlerAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,10 +169,10 @@ LocalRef<jobject> SOEHandlerAdapter::Convert(JNIEnv* env, const opendnp3::Header
{
auto gv = jni::JCache::GroupVariation.fromType(env, GroupVariationSpec::to_type(info.gv));
auto qc = jni::JCache::QualifierCode.fromType(env, QualifierCodeSpec::to_type(info.qualifier));
auto tsmode = jni::JCache::TimestampQuality.fromType(env, static_cast<jint>(info.tsmode));
auto tsquality = jni::JCache::TimestampQuality.fromType(env, static_cast<jint>(info.tsquality));
jboolean isEvent = static_cast<jboolean>(info.isEventVariation);
jboolean flagsValid = static_cast<jboolean>(info.flagsValid);
jint headerIndex = info.headerIndex;

return jni::JCache::HeaderInfo.init6(env, gv, qc, tsmode, isEvent, flagsValid, headerIndex);
return jni::JCache::HeaderInfo.init6(env, gv, qc, tsquality, isEvent, flagsValid, headerIndex);
}

0 comments on commit dba372f

Please sign in to comment.