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

Add Freeze support #320

Merged
merged 9 commits into from
Jul 8, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cpp/examples/outstation-udp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <opendnp3/DNP3Manager.h>
#include <opendnp3/LogLevels.h>
#include <opendnp3/channel/PrintingChannelListener.h>
#include <opendnp3/outstation/DefaultOutstationApplication.h>
#include <opendnp3/outstation/IUpdateHandler.h>
#include <opendnp3/outstation/SimpleCommandHandler.h>
#include <opendnp3/outstation/UpdateBuilder.h>
Expand Down
70 changes: 34 additions & 36 deletions cpp/examples/outstation/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <opendnp3/DNP3Manager.h>
#include <opendnp3/LogLevels.h>
#include <opendnp3/channel/PrintingChannelListener.h>
#include <opendnp3/outstation/DefaultOutstationApplication.h>
#include <opendnp3/outstation/IUpdateHandler.h>
#include <opendnp3/outstation/SimpleCommandHandler.h>
#include <opendnp3/outstation/UpdateBuilder.h>

Expand All @@ -34,11 +36,11 @@ using namespace opendnp3;
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 @@ -48,69 +50,52 @@ struct State
double value = 0;
bool binary = false;
DoubleBit dbit = DoubleBit::DETERMINED_OFF;
uint8_t octetStringValue = 1;
};

auto app = DefaultOutstationApplication::Create();

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.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();
}
auto channel = manager.AddTCPServer("server", logLevels, ServerAcceptMode::CloseExisting, IPEndpoint("0.0.0.0", 20000),
PrintingChannelListener::Create());

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

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

// 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
stackConfig.outstation.params.allowUnsolicited = true;
config.outstation.params.allowUnsolicited = true;

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

// 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(),
DefaultOutstationApplication::Create(), stackConfig);
app, config);

// Enable the outstation and start communications
outstation->Enable();
Expand All @@ -122,13 +107,14 @@ 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, 'quit' = exit" << std::endl;
std::cout << "c = counter, b = binary, d = doublebit, a = analog, o = octet string, 'quit' = exit" << std::endl;
std::cin >> input;

if (input == "quit")
return 0;
return 0; // DNP3Manager destructor cleanups up everything automatically
else
{
// update measurement values based on input string
UpdateBuilder builder;
AddUpdates(builder, state, input);
outstation->Apply(builder.Build());
Expand All @@ -150,6 +136,11 @@ void AddUpdates(UpdateBuilder& builder, State& state, const std::string& argumen
++state.count;
break;
}
case ('f'):
{
builder.FreezeCounter(0, false);
break;
}
case ('a'):
{
builder.Update(Analog(state.value), 0);
Expand All @@ -158,7 +149,7 @@ void AddUpdates(UpdateBuilder& builder, State& state, const std::string& argumen
}
case ('b'):
{
builder.Update(Binary(state.binary), 0);
builder.Update(Binary(state.binary, Flags(0x01), app->Now()), 0);
state.binary = !state.binary;
break;
}
Expand All @@ -169,6 +160,13 @@ void AddUpdates(UpdateBuilder& builder, State& state, const std::string& argumen
= (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
1 change: 1 addition & 0 deletions cpp/examples/tls/outstation/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <opendnp3/DNP3Manager.h>
#include <opendnp3/LogLevels.h>
#include <opendnp3/channel/PrintingChannelListener.h>
#include <opendnp3/outstation/DefaultOutstationApplication.h>
#include <opendnp3/outstation/SimpleCommandHandler.h>
#include <opendnp3/outstation/UpdateBuilder.h>

Expand Down
11 changes: 8 additions & 3 deletions cpp/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -140,17 +140,19 @@ set(opendnp3_public_headers
./include/opendnp3/master/IUTCTimeSource.h
./include/opendnp3/master/MasterParams.h
./include/opendnp3/master/PrintingSOEHandler.h
./include/opendnp3/master/ResponseInfo.h
./include/opendnp3/master/ResponseInfo.h
./include/opendnp3/master/RestartOperationResult.h
./include/opendnp3/master/TaskConfig.h
./include/opendnp3/master/TaskId.h
./include/opendnp3/master/TaskInfo.h
./include/opendnp3/master/X509Info.h
./include/opendnp3/master/X509Info.h

./include/opendnp3/outstation/ApplicationIIN.h
./include/opendnp3/outstation/DatabaseConfig.h
./include/opendnp3/outstation/DatabaseConfig.h
./include/opendnp3/outstation/DefaultOutstationApplication.h
./include/opendnp3/outstation/EventBufferConfig.h
./include/opendnp3/outstation/ICommandHandler.h
./include/opendnp3/outstation/IDnpTimeSource.h
./include/opendnp3/outstation/IOutstation.h
./include/opendnp3/outstation/IOutstationApplication.h
./include/opendnp3/outstation/IUpdateHandler.h
Expand Down Expand Up @@ -370,6 +372,7 @@ set(opendnp3_private_headers
./src/outstation/Database.h
./src/outstation/DeferredRequest.h
./src/outstation/Event.h
./src/outstation/FreezeRequestHandler.h
./src/outstation/IClassAssigner.h
./src/outstation/ICommandAction.h
./src/outstation/IEventReceiver.h
Expand Down Expand Up @@ -630,8 +633,10 @@ set(opendnp3_src
./src/outstation/CommandResponseHandler.cpp
./src/outstation/Database.cpp
./src/outstation/DatabaseConfig.cpp
./src/outstation/DefaultOutstationApplication.cpp
./src/outstation/DeferredRequest.cpp
./src/outstation/EventBufferConfig.cpp
./src/outstation/FreezeRequestHandler.cpp
./src/outstation/IINHelpers.cpp
./src/outstation/IOutstationApplication.cpp
./src/outstation/OctetStringSerializer.cpp
Expand Down
77 changes: 77 additions & 0 deletions cpp/lib/include/opendnp3/outstation/DefaultOutstationApplication.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright 2013-2019 Automatak, LLC
*
* Licensed to Green Energy Corp (www.greenenergycorp.com) and Automatak
* LLC (www.automatak.com) under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. Green Energy Corp and Automatak LLC license
* this file to you under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. 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 permissions and
* limitations under the License.
*/
#ifndef OPENDNP3_DEFAULTOUTSTATIONAPPLICATION_H
#define OPENDNP3_DEFAULTOUTSTATIONAPPLICATION_H

#include "opendnp3/outstation/IOutstationApplication.h"
#include "opendnp3/util/TimeDuration.h"

#include <chrono>
#include <memory>

namespace opendnp3
{

/**
* A singleton with default setting useful for examples
*/
class DefaultOutstationApplication : public IOutstationApplication
{
public:
static std::shared_ptr<IOutstationApplication> Create(TimeDuration timeSyncRefreshRate = TimeDuration::Minutes(1))
{
return std::make_shared<DefaultOutstationApplication>(timeSyncRefreshRate);
}

DefaultOutstationApplication(TimeDuration timeSyncRefreshRate = TimeDuration::Minutes(1));
virtual ~DefaultOutstationApplication() = default;

// IDnpTimeSource
virtual DNPTime Now() override;

// IOutstationApplication
virtual bool SupportsWriteAbsoluteTime() override { return true; }
virtual bool WriteAbsoluteTime(const UTCTimestamp& timestamp) override;

virtual bool SupportsWriteTimeAndInterval() override { return false; }
virtual bool WriteTimeAndInterval(const ICollection<Indexed<TimeAndInterval>>& values) override { return false; }

virtual bool SupportsAssignClass() override { return true; }
virtual void RecordClassAssignment(AssignClassType type, PointClass clazz, uint16_t start, uint16_t stop) override {}

virtual ApplicationIIN GetApplicationIIN() const override;

virtual RestartMode ColdRestartSupport() const override { return RestartMode::UNSUPPORTED; }
virtual RestartMode WarmRestartSupport() const override { return RestartMode::UNSUPPORTED; }
virtual uint16_t ColdRestart() override { return 65535; }
virtual uint16_t WarmRestart() override { return 65535; }

private:
bool IsTimeValid() const;
bool NeedsTime() const;

TimeDuration refresh_rate;
UTCTimestamp last_timestamp = UTCTimestamp();
std::chrono::system_clock::time_point last_update = std::chrono::system_clock::time_point(std::chrono::milliseconds(0));
};

} // namespace opendnp3

#endif
47 changes: 47 additions & 0 deletions cpp/lib/include/opendnp3/outstation/IDnpTimeSource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright 2013-2019 Automatak, LLC
*
* Licensed to Green Energy Corp (www.greenenergycorp.com) and Automatak
* LLC (www.automatak.com) under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional information
* regarding copyright ownership. Green Energy Corp and Automatak LLC license
* this file to you under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. 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 permissions and
* limitations under the License.
*/
#ifndef OPENDNP3_IDNPTIMESOURCE_H
#define OPENDNP3_IDNPTIMESOURCE_H

#include "opendnp3/app/DNPTime.h"

namespace opendnp3
{

/**
* Interface that defines a method to get DNPTime source
*/
class IDnpTimeSource
{

public:
/**
* Returns a DNPTime of the current time.
* This value is used when freezing counters.
*/
virtual DNPTime Now()
{
return DNPTime(0, TimestampQuality::INVALID);
}
};

} // namespace opendnp3

#endif
19 changes: 3 additions & 16 deletions cpp/lib/include/opendnp3/outstation/IOutstationApplication.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
#include "opendnp3/gen/RestartMode.h"
#include "opendnp3/link/ILinkListener.h"
#include "opendnp3/outstation/ApplicationIIN.h"
#include "opendnp3/outstation/IDnpTimeSource.h"

#include <memory>

Expand All @@ -38,7 +39,7 @@ namespace opendnp3
/**
* Interface for all outstation application callback info except for control requests
*/
class IOutstationApplication : public ILinkListener
class IOutstationApplication : public ILinkListener, public IDnpTimeSource
{
public:
/// Queries whether the the outstation supports absolute time writes
Expand Down Expand Up @@ -126,21 +127,7 @@ class IOutstationApplication : public ILinkListener
return 65535;
}

virtual ~IOutstationApplication() {}
};

/**
* A singleton with default setting useful for examples
*/
class DefaultOutstationApplication : public IOutstationApplication
{
public:
static std::shared_ptr<IOutstationApplication> Create()
{
return std::make_shared<DefaultOutstationApplication>();
}

DefaultOutstationApplication() = default;
virtual ~IOutstationApplication() = default;
};

} // namespace opendnp3
Expand Down
6 changes: 3 additions & 3 deletions cpp/lib/include/opendnp3/outstation/IUpdateHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,13 @@ class IUpdateHandler
virtual bool Update(const Counter& meas, uint16_t index, EventMode mode = EventMode::Detect) = 0;

/**
* Update a FrozenCounter measurement
* @param meas measurement to be processed
* Freeze a Counter measurement
* @param index index of the measurement
* @param clear clear the original counter
* @param mode Describes how event generation is handled for this method
* @return true if the value exists and it was updated
*/
virtual bool Update(const FrozenCounter& meas, uint16_t index, EventMode mode = EventMode::Detect) = 0;
virtual bool FreezeCounter(uint16_t index, bool clear = false, EventMode mode = EventMode::Detect) = 0;

/**
* Update a BinaryOutputStatus measurement
Expand Down
Loading