From 406fd5df58adcfb5e5ce1d736df9955890eabd90 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Mon, 31 Aug 2015 13:14:23 -0500 Subject: [PATCH 01/32] BAT-44 - add git submodule rapidjson, stub-in build section of readme --- .gitmodules | 4 ++++ README.md | 12 ++++++++++-- include/rapidjson | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .gitmodules create mode 160000 include/rapidjson diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..cbab79b53c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "include/rapidjson"] + path = include/rapidjson + url = ../rapidjson/ + branch = develop diff --git a/README.md b/README.md index 120ac0d2f1..ffd0b88d33 100755 --- a/README.md +++ b/README.md @@ -16,5 +16,13 @@ Overview Opendnp3 is a portable, scalable, and rigorously tested implementation of the DNP3 (www.dnp.org) protocol stack written in C++11. The library -is designed for high-performance applications like many concurrent TCP -sessions or huge device simulations. It also embeds very nicely on Linux. +can handle the largest front end processor loads, but can also be +ported to run on various microcontrollers. + +Langauge bindings are available. Consult the documentation. + +Build Steps +=========== +autoreconf -f -i +./configure +make -j6 diff --git a/include/rapidjson b/include/rapidjson new file mode 160000 index 0000000000..3d5848a7cd --- /dev/null +++ b/include/rapidjson @@ -0,0 +1 @@ +Subproject commit 3d5848a7cd3367c5cb451c6493165b7745948308 From 0b5326830d018bb6abd53796cb4c9606d2097d39 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Mon, 31 Aug 2015 17:36:55 -0500 Subject: [PATCH 02/32] BAT-44 - move rapidjson submodule to thirdparty/ to avoid confusion with include/ - added thirdparty/build.sh as hack to setup dependencies before running autotools - updated gitignore to include outstation app - update gitignore to include outstation app - implemented JSONCommandHandler that currently only prints out JSON representation of DNP3 commands - added analog output test function in master demo and code-formatted Conflicts: Makefile.am thirdparty/rapidjson --- .gitignore | 2 + .gitmodules | 4 - Makefile.am | 318 ++++++++++++++++++++++++++ README.md | 4 + cpp/examples/master/main.cpp | 108 +++++---- cpp/outstation/JSONCommandHandler.cpp | 241 +++++++++++++++++++ cpp/outstation/JSONCommandHandler.h | 55 +++++ cpp/outstation/Main.cpp | 160 +++++++++++++ include/rapidjson | 1 - thirdparty/build.sh | 7 + 10 files changed, 840 insertions(+), 60 deletions(-) create mode 100644 Makefile.am create mode 100644 cpp/outstation/JSONCommandHandler.cpp create mode 100644 cpp/outstation/JSONCommandHandler.h create mode 100644 cpp/outstation/Main.cpp delete mode 160000 include/rapidjson create mode 100755 thirdparty/build.sh diff --git a/.gitignore b/.gitignore index 56d753b686..9fa3173015 100755 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,5 @@ coverage.info cpp/demos/cover.info cpp/demos/cover_html/ +# apps +outstation diff --git a/.gitmodules b/.gitmodules index cbab79b53c..e69de29bb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +0,0 @@ -[submodule "include/rapidjson"] - path = include/rapidjson - url = ../rapidjson/ - branch = develop diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000000..1752a527fc --- /dev/null +++ b/Makefile.am @@ -0,0 +1,318 @@ +#copyright (c) 2013 Automatak, LLC + +ACLOCAL_AMFLAGS = -I m4 + +ASIO_CONFIG = -I $(ASIO_HOME) -DASIO_STANDALONE + +OPENPAL_INCLUDE = $(top_srcdir)/cpp/libs/openpal/src +OPENDNP3_INCLUDE = $(top_srcdir)/cpp/libs/opendnp3/src +ASIOPAL_INCLUDE = $(top_srcdir)/cpp/libs/asiopal/src +ASIODNP3_INCLUDE = $(top_srcdir)/cpp/libs/asiodnp3/src +CATCH_INCLUDE = $(top_srcdir)/cpp/tests/catch +RAPIDJSON_INCLUDE = $(top_srcdir)/thirdparty/rapidjson/include + +bin_PROGRAMS = masterdemo outstationdemo outstation + +lib_LTLIBRARIES = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la + +# ls cpp/libs/openpal/src/openpal/**/*.cpp -1 | awk '{print $0" \\"}' +libopenpal_la_LDFLAGS = -version-info 1:1:0 +libopenpal_la_CXXFLAGS = -I$(OPENPAL_INCLUDE) +libopenpal_la_SOURCES = \ +cpp/libs/openpal/src/openpal/container/DynamicBuffer.cpp \ +cpp/libs/openpal/src/openpal/container/ReadBufferView.cpp \ +cpp/libs/openpal/src/openpal/container/WriteBufferView.cpp \ +cpp/libs/openpal/src/openpal/executor/Action0.cpp \ +cpp/libs/openpal/src/openpal/executor/Erasure.cpp \ +cpp/libs/openpal/src/openpal/executor/MonotonicTimestamp.cpp \ +cpp/libs/openpal/src/openpal/executor/TimeDuration.cpp \ +cpp/libs/openpal/src/openpal/logging/LogEntry.cpp \ +cpp/libs/openpal/src/openpal/logging/Logger.cpp \ +cpp/libs/openpal/src/openpal/logging/LogRoot.cpp \ +cpp/libs/openpal/src/openpal/logging/StringFormatting.cpp \ +cpp/libs/openpal/src/openpal/serialization/ByteSerialization.cpp \ +cpp/libs/openpal/src/openpal/serialization/UInt48LE.cpp \ +cpp/libs/openpal/src/openpal/util/Limits.cpp \ +cpp/libs/openpal/src/openpal/util/ToHex.cpp + +#ls cpp/libs/asiopal/src/asiopal/*.cpp -1 | awk '{print $0" \\"}' +libasiopal_la_LDFLAGS = -version-info 1:1:0 +libasiopal_la_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) $(ASIO_CONFIG) +libasiopal_la_SOURCES = \ +cpp/libs/asiopal/src/asiopal/ASIOExecutor.cpp \ +cpp/libs/asiopal/src/asiopal/ASIOSerialHelpers.cpp \ +cpp/libs/asiopal/src/asiopal/IOServiceThreadPool.cpp \ +cpp/libs/asiopal/src/asiopal/LogFanoutHandler.cpp \ +cpp/libs/asiopal/src/asiopal/PhysicalLayerBase.cpp \ +cpp/libs/asiopal/src/asiopal/PhysicalLayerBaseTCP.cpp \ +cpp/libs/asiopal/src/asiopal/PhysicalLayerSerial.cpp \ +cpp/libs/asiopal/src/asiopal/PhysicalLayerTCPClient.cpp \ +cpp/libs/asiopal/src/asiopal/PhysicalLayerTCPServer.cpp \ +cpp/libs/asiopal/src/asiopal/SerialTypes.cpp \ +cpp/libs/asiopal/src/asiopal/TimerASIO.cpp \ +cpp/libs/asiopal/src/asiopal/UTCTimeSource.cpp + +# ls cpp/libs/opendnp3/src/opendnp3/**/*.cpp -1 | awk '{print $0" \\"}' +libopendnp3_la_LDFLAGS = -version-info 2:0:0 +libopendnp3_la_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) +libopendnp3_la_SOURCES = \ +cpp/libs/opendnp3/src/opendnp3/app/AnalogCommandEvent.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/AnalogOutput.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDUBuilders.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDUHandlerBase.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDUHeader.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDUHeaderParser.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDULogging.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDUParser.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDURequest.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDUResponse.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/APDUWrapper.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/AppControlField.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/BinaryCommandEvent.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/BitReader.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/ClassField.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/ControlRelayOutputBlock.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/EventTriggers.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/FunctionHelpers.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/GroupVariationRecord.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/IINField.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/MeasurementTypes.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/ObjectWriter.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/OctetData.cpp \ +cpp/libs/opendnp3/src/opendnp3/app/TimeAndInterval.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/ChannelState.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/CommandStatus.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/ControlCode.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/DoubleBit.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/FunctionCode.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/GroupVariation.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/IntervalUnits.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/LinkFunction.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/PointClass.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/QualifierCode.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/TaskCompletion.cpp \ +cpp/libs/opendnp3/src/opendnp3/gen/TimeSyncMode.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/CRC.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/IOpenDelayStrategy.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/LinkFrame.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/LinkHeader.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/LinkLayer.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/LinkLayerParser.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/PriLinkLayerStates.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/SecLinkLayerStates.cpp \ +cpp/libs/opendnp3/src/opendnp3/link/ShiftableBuffer.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/AssignClassTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/ClearRestartTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/CommandMarshaller.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/CommandResponse.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/CommandTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/DisableUnsolicitedTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/EnableUnsolicitedTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/EventScanTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/IMasterState.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/IMasterTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/ISOEHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/ITaskLock.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/MasterContext.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/Master.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/MasterParams.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/MasterScan.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/MasterScheduler.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/MasterTasks.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/MeasurementHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/PollTaskBase.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/SerialTimeSyncTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/StartupIntegrityPoll.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/UserPollTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/master/WriteTask.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group10.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group11.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group12.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group13.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group1.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group20.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group21.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group22.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group23.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group2.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group30.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group32.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group3.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group40.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group41.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group42.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group43.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group4.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group50.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group51.cpp \ +cpp/libs/opendnp3/src/opendnp3/objects/Group52.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/ApplicationIIN.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/AssignClassHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/ClassBasedRequestHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/CommandActionAdapter.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/CommandResponseHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/DatabaseBuffers.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/DatabaseConfigView.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/Database.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/EventBufferConfig.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/EventBuffer.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/EventCount.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/EventWriter.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/IINHelpers.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/IOutstationApplication.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/OutstationContext.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/Outstation.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/OutstationParams.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/OutstationSolicitedStates.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/OutstationUnsolicitedStates.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/ReadHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/ResponseContext.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/SelectedRanges.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/SimpleCommandHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/SOERecord.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/StaticBuffers.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/StaticLoadFunctions.cpp \ +cpp/libs/opendnp3/src/opendnp3/outstation/WriteHandler.cpp \ +cpp/libs/opendnp3/src/opendnp3/transport/TransportLayer.cpp \ +cpp/libs/opendnp3/src/opendnp3/transport/TransportRx.cpp \ +cpp/libs/opendnp3/src/opendnp3/transport/TransportStack.cpp \ +cpp/libs/opendnp3/src/opendnp3/transport/TransportTx.cpp + +#ls cpp/libs/asiodnp3/src/asiodnp3/*.cpp -1 | awk '{print $0" \\"}' +libasiodnp3_la_CPPFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) $(ASIO_CONFIG) +libasiodnp3_la_LDFLAGS = -version-info 1:1:0 +libasiodnp3_la_SOURCES = \ +cpp/libs/asiodnp3/src/asiodnp3/impl/LinkLayerRouter.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/impl/PhysicalLayerMonitor.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/impl/PhysicalLayerMonitorStates.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/BlockingCommandCallback.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/ChangeSet.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/ChannelSet.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/ConsoleLogger.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/DefaultMasterApplication.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/DestructorHook.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/DNP3Channel.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/DNP3Manager.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/HeaderTypes.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/MeasUpdate.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/MasterStackImpl.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/MultidropTaskLock.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/OutstationStackImpl.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/PrintingSOEHandler.cpp \ +cpp/libs/asiodnp3/src/asiodnp3/StackActionHandler.cpp + + +masterdemo_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed +masterdemo_LDFLAGS = -pthread +masterdemo_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la +masterdemo_SOURCES = cpp/examples/master/DemoMain.cpp + +outstationdemo_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed +outstationdemo_LDFLAGS = -pthread +outstationdemo_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la +outstationdemo_SOURCES = cpp/examples/outstation/DemoMain.cpp + +outstation_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) -I$(RAPIDJSON_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed +outstation_LDFLAGS = -pthread +outstation_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la +outstation_SOURCES = \ +cpp/outstation/JSONCommandHandler.cpp \ +cpp/outstation/Main.cpp + +check_PROGRAMS = openpaltest dnp3test +#TESTS = openpaltest dnp3test + +#ls cpp/tests/openpaltests/src/*.cpp -1 | awk '{print $0" \\"}' +openpaltest_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(CATCH_INCLUDE) +openpaltest_LDFLAGS = -pthread +openpaltest_LDADD = libopenpal.la +openpaltest_SOURCES = \ +cpp/tests/openpaltests/src/CatchTestStart.cpp \ +cpp/tests/openpaltests/src/FunctionalTestSuite.cpp \ +cpp/tests/openpaltests/src/LinkedListTestSuite.cpp \ +cpp/tests/openpaltests/src/ManagedPointerTestSuite.cpp + +#ls cpp/tests/opendnp3tests/src/*.cpp -1 | awk '{print $0" \\"}' +dnp3test_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) -I$(CATCH_INCLUDE) $(ASIO_CONFIG) +dnp3test_LDFLAGS = -pthread +dnp3test_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la +dnp3test_SOURCES = \ +cpp/tests/opendnp3tests/src/APDUHelpers.cpp \ +cpp/tests/opendnp3tests/src/APDUHexBuilders.cpp \ +cpp/tests/opendnp3tests/src/BufferHelpers.cpp \ +cpp/tests/opendnp3tests/src/BufferTestObject.cpp \ +cpp/tests/opendnp3tests/src/CatchTestStart.cpp \ +cpp/tests/opendnp3tests/src/CopyableBuffer.cpp \ +cpp/tests/opendnp3tests/src/DNPHelpers.cpp \ +cpp/tests/opendnp3tests/src/HexConversions.cpp \ +cpp/tests/opendnp3tests/src/LinkLayerRouterTest.cpp \ +cpp/tests/opendnp3tests/src/LinkLayerTest.cpp \ +cpp/tests/opendnp3tests/src/LogTester.cpp \ +cpp/tests/opendnp3tests/src/LoopbackPhysicalLayer.cpp \ +cpp/tests/opendnp3tests/src/LowerLayerToPhysAdapter.cpp \ +cpp/tests/opendnp3tests/src/MasterTestObject.cpp \ +cpp/tests/opendnp3tests/src/MockExecutor.cpp \ +cpp/tests/opendnp3tests/src/MockFrameSink.cpp \ +cpp/tests/opendnp3tests/src/MockLowerLayer.cpp \ +cpp/tests/opendnp3tests/src/MockPhysicalLayer.cpp \ +cpp/tests/opendnp3tests/src/MockPhysicalLayerMonitor.cpp \ +cpp/tests/opendnp3tests/src/MockTransportLayer.cpp \ +cpp/tests/opendnp3tests/src/MockUpperLayer.cpp \ +cpp/tests/opendnp3tests/src/OutstationTestObject.cpp \ +cpp/tests/opendnp3tests/src/PhysBaseTest.cpp \ +cpp/tests/opendnp3tests/src/PhysLoopback.cpp \ +cpp/tests/opendnp3tests/src/PhysTestObject.cpp \ +cpp/tests/opendnp3tests/src/ProtocolUtil.cpp \ +cpp/tests/opendnp3tests/src/RandomizedBuffer.cpp \ +cpp/tests/opendnp3tests/src/SerialTestObject.cpp \ +cpp/tests/opendnp3tests/src/StopWatch.cpp \ +cpp/tests/opendnp3tests/src/TestAPDUParsing.cpp \ +cpp/tests/opendnp3tests/src/TestAPDUWriting.cpp \ +cpp/tests/opendnp3tests/src/TestASIO.cpp \ +cpp/tests/opendnp3tests/src/TestASIOThreadPool.cpp \ +cpp/tests/opendnp3tests/src/TestCastLongLongDouble.cpp \ +cpp/tests/opendnp3tests/src/TestCRC.cpp \ +cpp/tests/opendnp3tests/src/TestDatabase.cpp \ +cpp/tests/opendnp3tests/src/TestDNP3Manager.cpp \ +cpp/tests/opendnp3tests/src/TestIndexSearch.cpp \ +cpp/tests/opendnp3tests/src/TestLazyCollection.cpp \ +cpp/tests/opendnp3tests/src/TestLinkFrame.cpp \ +cpp/tests/opendnp3tests/src/TestLinkLayer.cpp \ +cpp/tests/opendnp3tests/src/TestLinkLayerRouter.cpp \ +cpp/tests/opendnp3tests/src/TestLinkReceiver.cpp \ +cpp/tests/opendnp3tests/src/TestLinkRoute.cpp \ +cpp/tests/opendnp3tests/src/TestLog.cpp \ +cpp/tests/opendnp3tests/src/TestMasterAssignClass.cpp \ +cpp/tests/opendnp3tests/src/TestMasterCommandRequests.cpp \ +cpp/tests/opendnp3tests/src/TestMaster.cpp \ +cpp/tests/opendnp3tests/src/TestMasterMultidrop.cpp \ +cpp/tests/opendnp3tests/src/TestMasterUnsolBehaviors.cpp \ +cpp/tests/opendnp3tests/src/TestObjectASIO.cpp \ +cpp/tests/opendnp3tests/src/TestObject.cpp \ +cpp/tests/opendnp3tests/src/TestOutstationAssignClass.cpp \ +cpp/tests/opendnp3tests/src/TestOutstationCommandResponses.cpp \ +cpp/tests/opendnp3tests/src/TestOutstation.cpp \ +cpp/tests/opendnp3tests/src/TestOutstationEventResponses.cpp \ +cpp/tests/opendnp3tests/src/TestOutstationUnsolicitedResponses.cpp \ +cpp/tests/opendnp3tests/src/TestPhysicalLayerAsyncBase.cpp \ +cpp/tests/opendnp3tests/src/TestPhysicalLayerAsyncSerial.cpp \ +cpp/tests/opendnp3tests/src/TestPhysicalLayerAsyncTCP.cpp \ +cpp/tests/opendnp3tests/src/TestPhysicalLayerLoopback.cpp \ +cpp/tests/opendnp3tests/src/TestPhysicalLayerMonitor.cpp \ +cpp/tests/opendnp3tests/src/TestSerialization.cpp \ +cpp/tests/opendnp3tests/src/TestShiftableBuffer.cpp \ +cpp/tests/opendnp3tests/src/TestTime.cpp \ +cpp/tests/opendnp3tests/src/TestTimers.cpp \ +cpp/tests/opendnp3tests/src/TestTransportLayer.cpp \ +cpp/tests/opendnp3tests/src/TestTransportLoopback.cpp \ +cpp/tests/opendnp3tests/src/TestTypes.cpp \ +cpp/tests/opendnp3tests/src/TestUtil.cpp \ +cpp/tests/opendnp3tests/src/TestWriteConversions.cpp \ +cpp/tests/opendnp3tests/src/Timeout.cpp \ +cpp/tests/opendnp3tests/src/TransportIntegrationStack.cpp \ +cpp/tests/opendnp3tests/src/TransportLoopbackTestObject.cpp \ +cpp/tests/opendnp3tests/src/TransportScalabilityTestObject.cpp \ +cpp/tests/opendnp3tests/src/TransportStackPair.cpp \ +cpp/tests/opendnp3tests/src/TransportTestObject.cpp + + diff --git a/README.md b/README.md index ffd0b88d33..6eabd46237 100755 --- a/README.md +++ b/README.md @@ -23,6 +23,10 @@ Langauge bindings are available. Consult the documentation. Build Steps =========== +cd thirdparty +./build.sh +cd ../ + autoreconf -f -i ./configure make -j6 diff --git a/cpp/examples/master/main.cpp b/cpp/examples/master/main.cpp index 7ca6f2cb3c..7f51945193 100644 --- a/cpp/examples/master/main.cpp +++ b/cpp/examples/master/main.cpp @@ -34,8 +34,7 @@ using namespace asiopal; using namespace asiodnp3; using namespace opendnp3; -int main(int argc, char* argv[]) -{ +int main(int argc, char* argv[]) { // Specify what log levels to use. NORMAL is warning and above // You can add all the comms logging by uncommenting below @@ -47,15 +46,18 @@ int main(int argc, char* argv[]) // send log messages to the console manager.AddLogSubscriber(&ConsoleLogger::Instance()); - // Connect via a TCPClient socket to a outstation - auto pChannel = manager.AddTCPClient("tcpclient", FILTERS, ChannelRetry::Default(), "127.0.0.1", "0.0.0.0", 20000); + // Connect via a TCPClient socket to a outstation + auto pChannel = manager.AddTCPClient("tcpclient", FILTERS, + TimeDuration::Seconds(2), TimeDuration::Seconds(5), "127.0.0.1", + "0.0.0.0", 20000); // Optionally, you can bind listeners to the channel to get state change notifications // This listener just prints the changes to the console - pChannel->AddStateListener([](ChannelState state) - { - std::cout << "channel state: " << ChannelStateToString(state) << std::endl; - }); + pChannel->AddStateListener( + [](ChannelState state) + { + std::cout << "channel state: " << ChannelStateToString(state) << std::endl; + }); // The master config object for a master. The default are // useable, but understanding the options are important. @@ -74,25 +76,25 @@ int main(int argc, char* argv[]) // 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 pMaster = pChannel->AddMaster( - "master", // id for logging - PrintingSOEHandler::Instance(), // callback for data processing - asiodnp3::DefaultMasterApplication::Instance(), // master application instance - stackConfig // stack configuration - ); - + auto pMaster = pChannel->AddMaster("master", // id for logging + PrintingSOEHandler::Instance(), // callback for data processing + asiodnp3::DefaultMasterApplication::Instance(), // master application instance + stackConfig // stack configuration + ); // do an integrity poll (Class 3/2/1/0) once per minute - auto integrityScan = pMaster->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1)); + //auto integrityScan = pMaster->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1)); // do a Class 1 exception poll every 5 seconds - auto exceptionScan = pMaster->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2)); + auto exceptionScan = pMaster->AddClassScan(ClassField(ClassField::CLASS_1), + TimeDuration::Seconds(2)); // Enable the master. This will start communications. pMaster->Enable(); - do - { + auto pCommandProcessor = pMaster->GetCommandProcessor(); + + do { std::cout << "Enter a command" << std::endl; std::cout << "x - exits program" << std::endl; std::cout << "a - performs and ad-hoc range scan" << std::endl; @@ -101,56 +103,52 @@ int main(int argc, char* argv[]) std::cout << "d - diable unsolcited" << std::endl; std::cout << "r - cold restart" << std::endl; std::cout << "c - send crob" << std::endl; + std::cout << "o - send analog output int16" << std::endl; char cmd; std::cin >> cmd; - switch(cmd) - { - case('a') : + switch (cmd) { + case ('a'): pMaster->ScanRange(GroupVariationID(1, 2), 0, 3); break; - case('d') : - pMaster->PerformFunction("disable unsol", FunctionCode::DISABLE_UNSOLICITED, - { Header::AllObjects(60, 2), Header::AllObjects(60, 3), Header::AllObjects(60, 4) } - ); - break; - case('r') : - { - auto print = [](const RestartOperationResult& result) - { - if(result.summary == TaskCompletion::SUCCESS) - { - std::cout << "Success, Time: " << result.restartTime.GetMilliseconds() << std::endl; - } - else - { - std::cout << "Failure: " << TaskCompletionToString(result.summary) << std::endl; - } - }; - pMaster->Restart(RestartType::COLD, print); - break; - } - case('x'): + case ('x'): // C++ destructor on DNP3Manager cleans everything up for you return 0; - case('i'): - integrityScan.Demand(); + case ('i'): + //integrityScan.Demand(); break; - case('e'): + case ('e'): exceptionScan.Demand(); break; - case('c'): - { - ControlRelayOutputBlock crob(ControlCode::LATCH_ON); - pMaster->SelectAndOperate(crob, 0, PrintingCommandCallback::Get()); - break; - } + case ('c'): { + // This is an example of synchronously doing a control operation + ControlRelayOutputBlock crob(ControlCode::LATCH_ON); + BlockingCommandCallback handler; + pCommandProcessor->SelectAndOperate(crob, 0, handler); + auto response = handler.WaitForResult(); + std::cout << "Result: " + << TaskCompletionToString(response.GetResult()) + << " Status: " + << CommandStatusToString(response.GetStatus()) << std::endl; + break; + } + case ('o'): { + // This is an example of synchronously doing a control operation + AnalogOutputInt16 analogOutInt16(4242); + BlockingCommandCallback handler; + pCommandProcessor->SelectAndOperate(analogOutInt16, 0, handler); + auto response = handler.WaitForResult(); + std::cout << "Result: " + << TaskCompletionToString(response.GetResult()) + << " Status: " + << CommandStatusToString(response.GetStatus()) << std::endl; + break; + } default: std::cout << "Unknown action: " << cmd << std::endl; break; } - } - while(true); + } while (true); return 0; } diff --git a/cpp/outstation/JSONCommandHandler.cpp b/cpp/outstation/JSONCommandHandler.cpp new file mode 100644 index 0000000000..a744f46421 --- /dev/null +++ b/cpp/outstation/JSONCommandHandler.cpp @@ -0,0 +1,241 @@ +/** + * Licensed 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. + */ + +#include +#include +#include + +#include + +#include "JSONCommandHandler.h" + +using namespace opendnp3; +using namespace rapidjson; + +// TODO do more than just print out JSON! + +CommandStatus JSONCommandHandler::Select(const ControlRelayOutputBlock& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("select"); + writer.StartObject(); + writer.String("command"); + writer.String("ControlRelayOutputBlock"); + writer.String("index"); + writer.Int(aIndex); + writer.String("functionCode"); + writer.Int(static_cast(command.functionCode)); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} +CommandStatus JSONCommandHandler::Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("operate"); + writer.StartObject(); + writer.String("command"); + writer.String("ControlRelayOutputBlock"); + writer.String("index"); + writer.Int(aIndex); + writer.String("functionCode"); + writer.Int(static_cast(command.functionCode)); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} + +CommandStatus JSONCommandHandler::Select(const AnalogOutputInt16& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("select"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputInt16"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Int(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} +CommandStatus JSONCommandHandler::Operate(const AnalogOutputInt16& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("operate"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputInt16"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Int(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} + +CommandStatus JSONCommandHandler::Select(const AnalogOutputInt32& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("select"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputInt32"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Int(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} +CommandStatus JSONCommandHandler::Operate(const AnalogOutputInt32& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("operate"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputInt32"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Int(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} + +CommandStatus JSONCommandHandler::Select(const AnalogOutputFloat32& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("select"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputFloat32"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Double(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} +CommandStatus JSONCommandHandler::Operate(const AnalogOutputFloat32& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("select"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputFloat32"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Double(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} + +CommandStatus JSONCommandHandler::Select(const AnalogOutputDouble64& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("select"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputDouble64"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Double(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} +CommandStatus JSONCommandHandler::Operate(const AnalogOutputDouble64& command, uint16_t aIndex) { + StringBuffer s; + Writer writer(s); + + writer.StartObject(); + writer.String("operate"); + writer.StartObject(); + writer.String("command"); + writer.String("AnalogOutputDouble64"); + writer.String("index"); + writer.Int(aIndex); + writer.String("value"); + writer.Double(command.value); + writer.String("status"); + writer.Int(static_cast(command.status)); + writer.EndObject(); + writer.EndObject(); + + std::cout << s.GetString() << std::endl; + return CommandStatus::SUCCESS; +} diff --git a/cpp/outstation/JSONCommandHandler.h b/cpp/outstation/JSONCommandHandler.h new file mode 100644 index 0000000000..221a281889 --- /dev/null +++ b/cpp/outstation/JSONCommandHandler.h @@ -0,0 +1,55 @@ +/** + * Licensed 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_JSONCOMMANDHANDLER_H +#define OPENDNP3_JSONCOMMANDHANDLER_H + +#include + +namespace opendnp3 { + +class JSONCommandHandler: public ICommandHandler { +public: + + CommandStatus Select(const ControlRelayOutputBlock& command, uint16_t aIndex) override final; + CommandStatus Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) override final; + + CommandStatus Select(const AnalogOutputInt16& command, uint16_t aIndex) override final; + CommandStatus Operate(const AnalogOutputInt16& command, uint16_t aIndex) override final; + + CommandStatus Select(const AnalogOutputInt32& command, uint16_t aIndex) override final; + CommandStatus Operate(const AnalogOutputInt32& command, uint16_t aIndex) override final; + + CommandStatus Select(const AnalogOutputFloat32& command, uint16_t aIndex) override final; + CommandStatus Operate(const AnalogOutputFloat32& command, uint16_t aIndex) override final; + + CommandStatus Select(const AnalogOutputDouble64& command, uint16_t aIndex) override final; + CommandStatus Operate(const AnalogOutputDouble64& command, uint16_t aIndex) override final; + + static JSONCommandHandler& Instance() { + static JSONCommandHandler instance; + return instance; + } + +private: + JSONCommandHandler() { + } + + JSONCommandHandler(JSONCommandHandler const&) = delete; + void operator=(JSONCommandHandler const&) = delete; +}; + +} + +#endif + diff --git a/cpp/outstation/Main.cpp b/cpp/outstation/Main.cpp new file mode 100644 index 0000000000..379c38eb79 --- /dev/null +++ b/cpp/outstation/Main.cpp @@ -0,0 +1,160 @@ +/** + * Licensed to Green Energy Corp (www.greenenergycorp.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 licenses 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. + * + * This project was forked on 01/01/2013 by Automatak, LLC and modifications + * may have been made to this file. Automatak, LLC licenses these modifications + * to you under the terms of the License. + */ + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include + +#include "JSONCommandHandler.h" + +using namespace std; +using namespace opendnp3; +using namespace openpal; +using namespace asiopal; +using namespace asiodnp3; + +void ConfigureDatabase(DatabaseConfigView view) { + // example of configuring analog index 0 for Class2 with floating point variations by default + view.analogs[0].variation = StaticAnalogVariation::Group30Var5; + view.analogs[0].metadata.clazz = PointClass::Class2; + view.analogs[0].metadata.variation = EventAnalogVariation::Group32Var7; +} + +int main(int argc, char* argv[]) { + // Specify what log levels to use. NORMAL is warning and above + // You can add all the comms logging by uncommenting below. + const uint32_t FILTERS = 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 + DNP3Manager manager(1); + + // send log messages to the console + manager.AddLogSubscriber(&ConsoleLogger::Instance()); + + // Create a TCP server (listener) + auto pChannel = manager.AddTCPServer("server", FILTERS, + TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", + 20000); + + // Optionally, you can bind listeners to the channel to get state change notifications + // This listener just prints the changes to the console + pChannel->AddStateListener([](ChannelState state) + { + std::cout << "channel state: " << ChannelStateToString(state) << std::endl; + }); + + // The main object for a outstation. The defaults are useable, + // but understanding the options are important. + OutstationStackConfig stackConfig; + + // You must specify the shape of your database and the size of the event buffers + stackConfig.dbTemplate = DatabaseTemplate::AllTypes(10); + 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 + 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 + 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 pOutstation = pChannel->AddOutstation("outstation", + JSONCommandHandler::Instance(), + DefaultOutstationApplication::Instance(), stackConfig); + + // You can optionally change the default reporting variations or class assignment prior to enabling the outstation + ConfigureDatabase(pOutstation->GetConfigView()); + + // Enable the outstation and start communications + pOutstation->Enable(); + + // variables used in example loop + string input; + uint32_t count = 0; + double value = 0; + bool binary = false; + DoubleBit dbit = DoubleBit::DETERMINED_OFF; + + while (true) { + std::cout << "Enter one or more measurement changes then press " + << std::endl; + std::cout + << "c = counter, b = binary, d = doublebit, a = analog, x = exit" + << std::endl; + std::cin >> input; + + MeasUpdate tx(pOutstation); + + for (char& c : input) { + switch (c) { + case ('c'): { + tx.Update(Counter(count), 0); + ++count; + break; + } + case ('a'): { + tx.Update(Analog(value), 0); + value += 1; + break; + } + case ('b'): { + tx.Update(Binary(binary), 0); + binary = !binary; + break; + } + case ('d'): { + tx.Update(DoubleBitBinary(dbit), 0); + dbit = (dbit == DoubleBit::DETERMINED_OFF) ? + DoubleBit::DETERMINED_ON : DoubleBit::DETERMINED_OFF; + break; + } + case ('x'): + + // DNP3Manager destructor cleanups up everything automagically + return 0; + + default: + std::cout << "No action registered for: " << c << std::endl; + break; + } + } + + } + + return 0; +} diff --git a/include/rapidjson b/include/rapidjson deleted file mode 160000 index 3d5848a7cd..0000000000 --- a/include/rapidjson +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3d5848a7cd3367c5cb451c6493165b7745948308 diff --git a/thirdparty/build.sh b/thirdparty/build.sh new file mode 100755 index 0000000000..790184218a --- /dev/null +++ b/thirdparty/build.sh @@ -0,0 +1,7 @@ +#!/bin/sh +cd rapidjson +git submodule update --init +mkdir build +cd build +cmake .. +make -j6 From 3179861da049174565a02d1693f5ef5507fe5f9e Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 1 Sep 2015 09:53:44 -0500 Subject: [PATCH 03/32] BAT-44 - add eclipse cdt project and update git ignore with eclipse entries --- .cproject | 3179 ++++++++++++++++++++++++++++++++++++++++++++++++++++ .gitignore | 6 + .project | 33 + 3 files changed, 3218 insertions(+) create mode 100644 .cproject create mode 100644 .project diff --git a/.cproject b/.cproject new file mode 100644 index 0000000000..2432b63c57 --- /dev/null +++ b/.cproject @@ -0,0 +1,3179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + + all + true + true + false + + + make + + am--refresh + true + true + false + + + make + + check + true + true + false + + + make + + clean + true + true + false + + + make + + clean-cscope + true + true + false + + + make + + clean-libLTLIBRARIES + true + true + false + + + make + + clean-libtool + true + true + false + + + make + + cpp/examples/master/masterdemo-DemoMain.o + true + true + false + + + make + + cpp/examples/master/masterdemo-DemoMain.obj + true + true + false + + + make + + cpp/examples/outstation/outstationdemo-DemoMain.o + true + true + false + + + make + + cpp/examples/outstation/outstationdemo-DemoMain.obj + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-LinkLayerRouter.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-PhysicalLayerMonitor.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-PhysicalLayerMonitorStates.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-BlockingCommandCallback.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ChangeSet.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ChannelSet.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ConsoleLogger.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DefaultMasterApplication.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DestructorHook.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DNP3Channel.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DNP3Manager.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-HeaderTypes.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MasterStackImpl.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MeasUpdate.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MultidropTaskLock.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-OutstationStackImpl.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-PrintingSOEHandler.lo + true + true + false + + + make + + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-StackActionHandler.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-ASIOExecutor.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-ASIOSerialHelpers.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-IOServiceThreadPool.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-LogFanoutHandler.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerBase.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerBaseTCP.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerSerial.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerTCPClient.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerTCPServer.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-SerialTypes.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-TimerASIO.lo + true + true + false + + + make + + cpp/libs/asiopal/src/asiopal/libasiopal_la-UTCTimeSource.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AnalogCommandEvent.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AnalogOutput.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUBuilders.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHandlerBase.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHeader.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHeaderParser.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDULogging.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUParser.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDURequest.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUResponse.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUWrapper.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AppControlField.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-BinaryCommandEvent.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-BitReader.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ClassField.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ControlRelayOutputBlock.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-EventTriggers.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-FunctionHelpers.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-GroupVariationRecord.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-IINField.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-MeasurementTypes.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ObjectWriter.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-OctetData.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-TimeAndInterval.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-ChannelState.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-CommandStatus.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-ControlCode.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-DoubleBit.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-FunctionCode.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-GroupVariation.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-IntervalUnits.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-LinkFunction.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-PointClass.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-QualifierCode.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-TaskCompletion.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-TimeSyncMode.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-CRC.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-IOpenDelayStrategy.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkFrame.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkHeader.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkLayer.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkLayerParser.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-PriLinkLayerStates.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-SecLinkLayerStates.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-ShiftableBuffer.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-AssignClassTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ClearRestartTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandMarshaller.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandResponse.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-DisableUnsolicitedTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-EnableUnsolicitedTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-EventScanTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-IMasterState.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-IMasterTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ISOEHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ITaskLock.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-Master.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterContext.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterParams.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterScan.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterScheduler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterTasks.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MeasurementHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-PollTaskBase.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-SerialTimeSyncTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-StartupIntegrityPoll.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-UserPollTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-WriteTask.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group1.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group10.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group11.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group12.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group13.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group2.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group20.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group21.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group22.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group23.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group3.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group30.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group32.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group4.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group40.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group41.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group42.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group43.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group50.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group51.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group52.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ApplicationIIN.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-AssignClassHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ClassBasedRequestHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-CommandActionAdapter.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-CommandResponseHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-Database.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-DatabaseBuffers.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-DatabaseConfigView.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventBuffer.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventBufferConfig.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventCount.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventWriter.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-IINHelpers.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-IOutstationApplication.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-Outstation.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationContext.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationParams.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationSolicitedStates.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationUnsolicitedStates.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ReadHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ResponseContext.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SelectedRanges.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SimpleCommandHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SOERecord.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-StaticBuffers.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-StaticLoadFunctions.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-WriteHandler.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportLayer.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportRx.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportStack.lo + true + true + false + + + make + + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportTx.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/container/libopenpal_la-DynamicBuffer.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/container/libopenpal_la-ReadBufferView.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/container/libopenpal_la-WriteBufferView.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/executor/libopenpal_la-Action0.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/executor/libopenpal_la-Erasure.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/executor/libopenpal_la-MonotonicTimestamp.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/executor/libopenpal_la-TimeDuration.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/logging/libopenpal_la-LogEntry.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/logging/libopenpal_la-Logger.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/logging/libopenpal_la-LogRoot.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/logging/libopenpal_la-StringFormatting.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/serialization/libopenpal_la-ByteSerialization.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/serialization/libopenpal_la-UInt48LE.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/util/libopenpal_la-Limits.lo + true + true + false + + + make + + cpp/libs/openpal/src/openpal/util/libopenpal_la-ToHex.lo + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-APDUHelpers.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-APDUHelpers.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-APDUHexBuilders.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-APDUHexBuilders.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-BufferHelpers.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-BufferHelpers.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-BufferTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-BufferTestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-CatchTestStart.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-CatchTestStart.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-CopyableBuffer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-CopyableBuffer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-DNPHelpers.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-DNPHelpers.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-HexConversions.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-HexConversions.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerRouterTest.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerRouterTest.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerTest.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerTest.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LogTester.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LogTester.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LoopbackPhysicalLayer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LoopbackPhysicalLayer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LowerLayerToPhysAdapter.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-LowerLayerToPhysAdapter.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MasterTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MasterTestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockExecutor.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockExecutor.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockFrameSink.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockFrameSink.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockLowerLayer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockLowerLayer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayerMonitor.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayerMonitor.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockTransportLayer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockTransportLayer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockUpperLayer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-MockUpperLayer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-OutstationTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-OutstationTestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-PhysBaseTest.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-PhysBaseTest.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-PhysLoopback.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-PhysLoopback.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-PhysTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-PhysTestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-ProtocolUtil.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-ProtocolUtil.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-RandomizedBuffer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-RandomizedBuffer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-SerialTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-SerialTestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-StopWatch.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-StopWatch.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUParsing.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUParsing.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUWriting.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUWriting.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestASIO.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestASIO.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestASIOThreadPool.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestASIOThreadPool.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestCastLongLongDouble.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestCastLongLongDouble.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestCRC.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestCRC.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestDatabase.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestDatabase.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestDNP3Manager.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestDNP3Manager.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestIndexSearch.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestIndexSearch.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLazyCollection.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLazyCollection.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkFrame.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkFrame.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayerRouter.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayerRouter.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkReceiver.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkReceiver.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkRoute.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLinkRoute.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLog.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestLog.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMaster.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMaster.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterAssignClass.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterAssignClass.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterCommandRequests.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterCommandRequests.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterMultidrop.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterMultidrop.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterUnsolBehaviors.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestMasterUnsolBehaviors.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestObjectASIO.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestObjectASIO.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstation.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstation.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationAssignClass.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationAssignClass.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationCommandResponses.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationCommandResponses.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationEventResponses.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationEventResponses.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationUnsolicitedResponses.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationUnsolicitedResponses.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncBase.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncBase.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncSerial.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncSerial.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncTCP.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncTCP.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerLoopback.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerLoopback.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerMonitor.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerMonitor.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestSerialization.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestSerialization.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestShiftableBuffer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestShiftableBuffer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTime.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTime.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTimers.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTimers.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLayer.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLayer.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLoopback.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLoopback.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTypes.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestTypes.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestUtil.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestUtil.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestWriteConversions.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TestWriteConversions.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-Timeout.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-Timeout.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportIntegrationStack.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportIntegrationStack.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportLoopbackTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportLoopbackTestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportScalabilityTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportScalabilityTestObject.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportStackPair.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportStackPair.obj + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportTestObject.o + true + true + false + + + make + + cpp/tests/opendnp3tests/src/dnp3test-TransportTestObject.obj + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-CatchTestStart.o + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-CatchTestStart.obj + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-FunctionalTestSuite.o + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-FunctionalTestSuite.obj + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-LinkedListTestSuite.o + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-LinkedListTestSuite.obj + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-ManagedPointerTestSuite.o + true + true + false + + + make + + cpp/tests/openpaltests/src/openpaltest-ManagedPointerTestSuite.obj + true + true + false + + + make + + cscope + true + true + false + + + make + + cscope.files + true + true + false + + + make + + cscopelist + true + true + false + + + make + + ctags + true + true + false + + + make + + dist + true + true + false + + + make + + dist-all + true + true + false + + + make + + dist-bzip2 + true + true + false + + + make + + dist-gzip + true + true + false + + + make + + dist-lzip + true + true + false + + + make + + dist-shar + true + true + false + + + make + + dist-tarZ + true + true + false + + + make + + dist-xz + true + true + false + + + make + + dist-zip + true + true + false + + + make + + distcheck + true + true + false + + + make + + distclean + true + true + false + + + make + + distclean-compile + true + true + false + + + make + + distclean-libtool + true + true + false + + + make + + distclean-tags + true + true + false + + + make + + distcleancheck + true + true + false + + + make + + distdir + true + true + false + + + make + + distuninstallcheck + true + true + false + + + make + + dvi + true + true + false + + + make + + html + true + true + false + + + make + + info + true + true + false + + + make + + install + true + true + false + + + make + + install-data + true + true + false + + + make + + install-dvi + true + true + false + + + make + + install-exec + true + true + false + + + make + + install-html + true + true + false + + + make + + install-info + true + true + false + + + make + + install-libLTLIBRARIES + true + true + false + + + make + + install-man + true + true + false + + + make + + install-pdf + true + true + false + + + make + + install-ps + true + true + false + + + make + + install-strip + true + true + false + + + make + + installcheck + true + true + false + + + make + + installdirs + true + true + false + + + make + + libasiodnp3.la + true + true + false + + + make + + libasiopal.la + true + true + false + + + make + + libopendnp3.la + true + true + false + + + make + + libopenpal.la + true + true + false + + + make + + maintainer-clean + true + true + false + + + make + + Makefile + true + true + false + + + make + + mostlyclean + true + true + false + + + make + + mostlyclean-compile + true + true + false + + + make + + mostlyclean-libtool + true + true + false + + + make + + pdf + true + true + false + + + make + + ps + true + true + false + + + make + + tags + true + true + false + + + make + + uninstall + true + true + false + + + make + + uninstall-libLTLIBRARIES + true + true + false + + + + + + + + + diff --git a/.gitignore b/.gitignore index 9fa3173015..c3a4736b61 100755 --- a/.gitignore +++ b/.gitignore @@ -144,5 +144,11 @@ coverage.info cpp/demos/cover.info cpp/demos/cover_html/ +# eclipse generated files +RemoteSystemsTempFiles +.metadata +.settings +.autotools + # apps outstation diff --git a/.project b/.project new file mode 100644 index 0000000000..3d47e00a8a --- /dev/null +++ b/.project @@ -0,0 +1,33 @@ + + + dnp3 + + + + + + org.eclipse.cdt.autotools.core.genmakebuilderV2 + + + + + org.eclipse.cdt.managedbuilder.core.genmakebuilder + clean,full,incremental, + + + + + org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder + full,incremental, + + + + + + org.eclipse.cdt.core.cnature + org.eclipse.cdt.core.ccnature + org.eclipse.cdt.managedbuilder.core.managedBuildNature + org.eclipse.cdt.managedbuilder.core.ScannerConfigNature + org.eclipse.cdt.autotools.core.autotoolsNatureV2 + + From 2943dadb244806b8d742f0f464cd03f3cd6e9bd9 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 1 Sep 2015 10:35:53 -0500 Subject: [PATCH 04/32] BAT-44 - convert from demo outstation to new outstation app, add TODOs for implementation --- cpp/outstation/Main.cpp | 125 +++++++++------------------------------- 1 file changed, 27 insertions(+), 98 deletions(-) diff --git a/cpp/outstation/Main.cpp b/cpp/outstation/Main.cpp index 379c38eb79..887f44f8d0 100644 --- a/cpp/outstation/Main.cpp +++ b/cpp/outstation/Main.cpp @@ -1,10 +1,7 @@ /** - * Licensed to Green Energy Corp (www.greenenergycorp.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 licenses 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 + * Licensed 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 * @@ -13,10 +10,6 @@ * 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. - * - * This project was forked on 01/01/2013 by Automatak, LLC and modifications - * may have been made to this file. Automatak, LLC licenses these modifications - * to you under the terms of the License. */ #include @@ -32,6 +25,8 @@ #include #include #include +#include +#include #include "JSONCommandHandler.h" @@ -41,120 +36,54 @@ using namespace openpal; using namespace asiopal; using namespace asiodnp3; +namespace { +volatile std::sig_atomic_t gSignalStatus; +} + +void signal_handler(int signal) { + gSignalStatus = signal; +} + void ConfigureDatabase(DatabaseConfigView view) { - // example of configuring analog index 0 for Class2 with floating point variations by default + // TODO read from config file view.analogs[0].variation = StaticAnalogVariation::Group30Var5; view.analogs[0].metadata.clazz = PointClass::Class2; view.analogs[0].metadata.variation = EventAnalogVariation::Group32Var7; } int main(int argc, char* argv[]) { - // Specify what log levels to use. NORMAL is warning and above - // You can add all the comms logging by uncommenting below. - const uint32_t FILTERS = levels::NORMAL; // | levels::ALL_COMMS; + const uint32_t FILTERS = levels::NORMAL; - // This is the main point of interaction with the stack - // Allocate a single thread to the pool since this is a single outstation DNP3Manager manager(1); - - // send log messages to the console manager.AddLogSubscriber(&ConsoleLogger::Instance()); - // Create a TCP server (listener) - auto pChannel = manager.AddTCPServer("server", FILTERS, - TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", - 20000); - - // Optionally, you can bind listeners to the channel to get state change notifications - // This listener just prints the changes to the console + auto pChannel = manager.AddTCPServer("server", FILTERS, TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", 20000); pChannel->AddStateListener([](ChannelState state) { std::cout << "channel state: " << ChannelStateToString(state) << std::endl; }); - // The main object for a outstation. The defaults are useable, - // but understanding the options are important. OutstationStackConfig stackConfig; - - // You must specify the shape of your database and the size of the event buffers stackConfig.dbTemplate = DatabaseTemplate::AllTypes(10); 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 - 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 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 pOutstation = pChannel->AddOutstation("outstation", - JSONCommandHandler::Instance(), - DefaultOutstationApplication::Instance(), stackConfig); + // TODO configure and run (tcp or other) server for JSON commands/events + // 1. convert incoming JSON to MeasUpdate and apply to outstation instance + // 2. JSONCommandHandler incoming DNP3 commands should convert and forward via JSON - // You can optionally change the default reporting variations or class assignment prior to enabling the outstation - ConfigureDatabase(pOutstation->GetConfigView()); + auto pOutstation = pChannel->AddOutstation("outstation", JSONCommandHandler::Instance(), DefaultOutstationApplication::Instance(), stackConfig); - // Enable the outstation and start communications + // Configure and start outstation + ConfigureDatabase(pOutstation->GetConfigView()); pOutstation->Enable(); - // variables used in example loop - string input; - uint32_t count = 0; - double value = 0; - bool binary = false; - DoubleBit dbit = DoubleBit::DETERMINED_OFF; - - while (true) { - std::cout << "Enter one or more measurement changes then press " - << std::endl; - std::cout - << "c = counter, b = binary, d = doublebit, a = analog, x = exit" - << std::endl; - std::cin >> input; - - MeasUpdate tx(pOutstation); - - for (char& c : input) { - switch (c) { - case ('c'): { - tx.Update(Counter(count), 0); - ++count; - break; - } - case ('a'): { - tx.Update(Analog(value), 0); - value += 1; - break; - } - case ('b'): { - tx.Update(Binary(binary), 0); - binary = !binary; - break; - } - case ('d'): { - tx.Update(DoubleBitBinary(dbit), 0); - dbit = (dbit == DoubleBit::DETERMINED_OFF) ? - DoubleBit::DETERMINED_ON : DoubleBit::DETERMINED_OFF; - break; - } - case ('x'): - - // DNP3Manager destructor cleanups up everything automagically - return 0; - - default: - std::cout << "No action registered for: " << c << std::endl; - break; - } - } - - } + // Initiate shutdown when signal received + std::signal(SIGINT, signal_handler); + pause(); + pOutstation->Shutdown(); + manager.Shutdown(); return 0; } From 2e01c48c4658b09eb99232385ad7536ff27c04e0 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 1 Sep 2015 10:38:42 -0500 Subject: [PATCH 05/32] BAT-44 - syntax highlight on readme build steps --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6eabd46237..0b353e946d 100755 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Langauge bindings are available. Consult the documentation. Build Steps =========== +```bash cd thirdparty ./build.sh cd ../ @@ -30,3 +31,4 @@ cd ../ autoreconf -f -i ./configure make -j6 +``` From e72b1cdf778a82d11f618f18e0168ab530244b9a Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 1 Sep 2015 17:07:45 -0500 Subject: [PATCH 06/32] BAT-44 - add TCP server implementation, still needs major work to integrate with JSONCommandHandler --- .gitignore | 2 +- Makefile.am | 2 +- cpp/outstation/Main.cpp | 18 ++++++--- cpp/outstation/TCPServer.cpp | 39 ++++++++++++++++++ cpp/outstation/TCPSession.cpp | 76 +++++++++++++++++++++++++++++++++++ 5 files changed, 130 insertions(+), 7 deletions(-) create mode 100644 cpp/outstation/TCPServer.cpp create mode 100644 cpp/outstation/TCPSession.cpp diff --git a/.gitignore b/.gitignore index c3a4736b61..64f3ec4527 100755 --- a/.gitignore +++ b/.gitignore @@ -151,4 +151,4 @@ RemoteSystemsTempFiles .autotools # apps -outstation +/outstation diff --git a/Makefile.am b/Makefile.am index 1752a527fc..0f04712c8c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -213,7 +213,7 @@ outstationdemo_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la outstationdemo_SOURCES = cpp/examples/outstation/DemoMain.cpp outstation_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) -I$(RAPIDJSON_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed -outstation_LDFLAGS = -pthread +outstation_LDFLAGS = -pthread -lboost_system outstation_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la outstation_SOURCES = \ cpp/outstation/JSONCommandHandler.cpp \ diff --git a/cpp/outstation/Main.cpp b/cpp/outstation/Main.cpp index 887f44f8d0..48133e113d 100644 --- a/cpp/outstation/Main.cpp +++ b/cpp/outstation/Main.cpp @@ -29,6 +29,7 @@ #include #include "JSONCommandHandler.h" +#include "TCPServer.cpp" using namespace std; using namespace opendnp3; @@ -52,20 +53,18 @@ void ConfigureDatabase(DatabaseConfigView view) { } int main(int argc, char* argv[]) { - const uint32_t FILTERS = levels::NORMAL; - DNP3Manager manager(1); manager.AddLogSubscriber(&ConsoleLogger::Instance()); - auto pChannel = manager.AddTCPServer("server", FILTERS, TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", 20000); + auto pChannel = manager.AddTCPServer("server", levels::NORMAL, TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", 20000); pChannel->AddStateListener([](ChannelState state) { std::cout << "channel state: " << ChannelStateToString(state) << std::endl; }); OutstationStackConfig stackConfig; - stackConfig.dbTemplate = DatabaseTemplate::AllTypes(10); - stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(10); + stackConfig.dbTemplate = DatabaseTemplate::AllTypes(5); + stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(5); stackConfig.link.LocalAddr = 10; stackConfig.link.RemoteAddr = 1; @@ -79,6 +78,15 @@ int main(int argc, char* argv[]) { ConfigureDatabase(pOutstation->GetConfigView()); pOutstation->Enable(); + + try { + boost::asio::io_service io_service; + TCPServer s(io_service, 3384); + io_service.run(); + } catch (std::exception& e) { + std::cerr << "Exception: " << e.what() << "\n"; + } + // Initiate shutdown when signal received std::signal(SIGINT, signal_handler); pause(); diff --git a/cpp/outstation/TCPServer.cpp b/cpp/outstation/TCPServer.cpp new file mode 100644 index 0000000000..19d285350d --- /dev/null +++ b/cpp/outstation/TCPServer.cpp @@ -0,0 +1,39 @@ +/** + * Licensed 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. + */ + +#include "TCPSession.cpp" + +class TCPServer { +public: + TCPServer(boost::asio::io_service& io_service, short port) : + acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { + do_accept(); + } + +private: + void do_accept() { + acceptor_.async_accept(socket_, [this](boost::system::error_code ec) + { + if (!ec) + { + std::make_shared(std::move(socket_))->start(); + } + + do_accept(); + }); + } + + tcp::acceptor acceptor_; + tcp::socket socket_; +}; diff --git a/cpp/outstation/TCPSession.cpp b/cpp/outstation/TCPSession.cpp new file mode 100644 index 0000000000..c52fd26ec2 --- /dev/null +++ b/cpp/outstation/TCPSession.cpp @@ -0,0 +1,76 @@ +/** + * Licensed 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. + */ + +#include +#include +#include +#include +#include +#include + +using boost::asio::ip::tcp; + +class TCPSession: public std::enable_shared_from_this { +public: + TCPSession(tcp::socket socket) : + socket_(std::move(socket)) { + } + + void start() { + do_read(); + } + +private: + void do_read() { + auto self(shared_from_this()); + socket_.async_receive(boost::asio::buffer(data_, 1024), [this, self](boost::system::error_code error, std::size_t length) + { + if (!error) + { + sbuf.sputn(data_,length); + do_read(); + } else if (error.value() == boost::system::errc::no_such_file_or_directory) { + std::cout << sbuf.str() << std::endl; + void *array[10]; + size_t size; + + // get void*'s for all entries on the stack + size = backtrace(array, 10); + + // print out all the frames to stderr + fprintf(stderr, "Error:\n"); + backtrace_symbols_fd(array, size, STDERR_FILENO); + } + }); + } + + void do_write(std::size_t length) { + /*auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), [this, self](boost::system::error_code ec, std::size_t length) + { + if (!ec) + { + do_read(); + } + });*/ + } + + tcp::socket socket_; + enum { + buffer_size = 10 + }; + char data_[buffer_size]; + std::stringbuf sbuf; +} +; From 4edcaa881ae928305caee1376287d7e569196f41 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 2 Sep 2015 09:23:45 -0500 Subject: [PATCH 07/32] BAT-44 - addded TCPSession.cpp changes, cleaning out debug info --- cpp/outstation/TCPSession.cpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/cpp/outstation/TCPSession.cpp b/cpp/outstation/TCPSession.cpp index c52fd26ec2..b930e7b029 100644 --- a/cpp/outstation/TCPSession.cpp +++ b/cpp/outstation/TCPSession.cpp @@ -41,21 +41,14 @@ class TCPSession: public std::enable_shared_from_this { sbuf.sputn(data_,length); do_read(); } else if (error.value() == boost::system::errc::no_such_file_or_directory) { + // TODO deserialize JSON std::cout << sbuf.str() << std::endl; - void *array[10]; - size_t size; - - // get void*'s for all entries on the stack - size = backtrace(array, 10); - - // print out all the frames to stderr - fprintf(stderr, "Error:\n"); - backtrace_symbols_fd(array, size, STDERR_FILENO); } }); } void do_write(std::size_t length) { + // TODO serialize JSONCommand to this socket /*auto self(shared_from_this()); boost::asio::async_write(socket_, boost::asio::buffer(data_, length), [this, self](boost::system::error_code ec, std::size_t length) { @@ -68,7 +61,7 @@ class TCPSession: public std::enable_shared_from_this { tcp::socket socket_; enum { - buffer_size = 10 + buffer_size = 1024 }; char data_[buffer_size]; std::stringbuf sbuf; From d20775bab82a74cc8c1b3790a4a015b140ab1c53 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Thu, 3 Sep 2015 17:17:39 -0500 Subject: [PATCH 08/32] BAT-44 - preparing for some pub/sub action, not currently stable at all --- Makefile.am | 6 +- cpp/outstation/AsyncCommand.cpp | 64 +++++ cpp/outstation/AsyncCommandHandler.cpp | 72 ++++++ ...CommandHandler.h => AsyncCommandHandler.h} | 25 +- cpp/outstation/JSONCommandHandler.cpp | 241 ------------------ .../{TCPSession.cpp => JSONTCPSession.cpp} | 44 +++- cpp/outstation/Main.cpp | 21 +- ...Server.cpp => OutstationJSONTCPServer.cpp} | 22 +- 8 files changed, 211 insertions(+), 284 deletions(-) create mode 100644 cpp/outstation/AsyncCommand.cpp create mode 100644 cpp/outstation/AsyncCommandHandler.cpp rename cpp/outstation/{JSONCommandHandler.h => AsyncCommandHandler.h} (80%) delete mode 100644 cpp/outstation/JSONCommandHandler.cpp rename cpp/outstation/{TCPSession.cpp => JSONTCPSession.cpp} (55%) rename cpp/outstation/{TCPServer.cpp => OutstationJSONTCPServer.cpp} (57%) diff --git a/Makefile.am b/Makefile.am index 0f04712c8c..d83bd3f389 100644 --- a/Makefile.am +++ b/Makefile.am @@ -213,10 +213,10 @@ outstationdemo_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la outstationdemo_SOURCES = cpp/examples/outstation/DemoMain.cpp outstation_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) -I$(RAPIDJSON_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed -outstation_LDFLAGS = -pthread -lboost_system +outstation_LDFLAGS = -pthread -lboost_system -lboost_thread outstation_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la outstation_SOURCES = \ -cpp/outstation/JSONCommandHandler.cpp \ +cpp/outstation/AsyncCommandHandler.cpp \ cpp/outstation/Main.cpp check_PROGRAMS = openpaltest dnp3test @@ -314,5 +314,3 @@ cpp/tests/opendnp3tests/src/TransportLoopbackTestObject.cpp \ cpp/tests/opendnp3tests/src/TransportScalabilityTestObject.cpp \ cpp/tests/opendnp3tests/src/TransportStackPair.cpp \ cpp/tests/opendnp3tests/src/TransportTestObject.cpp - - diff --git a/cpp/outstation/AsyncCommand.cpp b/cpp/outstation/AsyncCommand.cpp new file mode 100644 index 0000000000..2bded7e3d1 --- /dev/null +++ b/cpp/outstation/AsyncCommand.cpp @@ -0,0 +1,64 @@ +/** + * Licensed 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. + */ + +using namespace opendnp3; + +class AsyncCommand { +public: + AsyncCommand() : + idx_(0) { + } + AsyncCommand(ControlRelayOutputBlock crob, uint16_t idx) : + crob_(crob), idx_(idx) { + } + AsyncCommand(AnalogOutputInt16 aoInt16, uint16_t idx) : + aoInt16_(aoInt16), idx_(idx) { + } + AsyncCommand(AnalogOutputInt32 aoInt32, uint16_t idx) : + aoInt32_(aoInt32), idx_(idx) { + } + AsyncCommand(AnalogOutputFloat32 aoFloat32, uint16_t idx) : + aoFloat32_(aoFloat32), idx_(idx) { + } + AsyncCommand(AnalogOutputDouble64 aoDouble64, uint16_t idx) : + aoDouble64_(aoDouble64), idx_(idx) { + } + + uint16_t Index() { + return idx_; + } + ControlRelayOutputBlock CROB() { + return crob_; + } + AnalogOutputInt16 AOInt16() { + return aoInt16_; + } + AnalogOutputInt32 AOInt32() { + return aoInt32_; + } + AnalogOutputFloat32 AOFloat32() { + return aoFloat32_; + } + AnalogOutputDouble64 AODouble64() { + return aoDouble64_; + } + +private: + uint16_t idx_; + ControlRelayOutputBlock crob_; + AnalogOutputInt16 aoInt16_; + AnalogOutputInt32 aoInt32_; + AnalogOutputFloat32 aoFloat32_; + AnalogOutputDouble64 aoDouble64_; +}; diff --git a/cpp/outstation/AsyncCommandHandler.cpp b/cpp/outstation/AsyncCommandHandler.cpp new file mode 100644 index 0000000000..fffc449fa4 --- /dev/null +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -0,0 +1,72 @@ +/** + * Licensed 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. + */ + +#include "AsyncCommandHandler.h" + +using namespace opendnp3; + +// TODO validate requests before returning CommandStatus + +AsyncCommandHandler::~AsyncCommandHandler() { +} + +CommandStatus AsyncCommandHandler::Select(const ControlRelayOutputBlock& command, uint16_t aIndex) { + return CommandStatus::SUCCESS; +} +CommandStatus AsyncCommandHandler::Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) { + push(AsyncCommand(command, aIndex)); + return CommandStatus::SUCCESS; +} + +CommandStatus AsyncCommandHandler::Select(const AnalogOutputInt16& command, uint16_t aIndex) { + return CommandStatus::SUCCESS; +} +CommandStatus AsyncCommandHandler::Operate(const AnalogOutputInt16& command, uint16_t aIndex) { + push(AsyncCommand(command, aIndex)); + return CommandStatus::SUCCESS; +} + +CommandStatus AsyncCommandHandler::Select(const AnalogOutputInt32& command, uint16_t aIndex) { + return CommandStatus::SUCCESS; +} +CommandStatus AsyncCommandHandler::Operate(const AnalogOutputInt32& command, uint16_t aIndex) { + push(AsyncCommand(command, aIndex)); + return CommandStatus::SUCCESS; +} + +CommandStatus AsyncCommandHandler::Select(const AnalogOutputFloat32& command, uint16_t aIndex) { + return CommandStatus::SUCCESS; +} +CommandStatus AsyncCommandHandler::Operate(const AnalogOutputFloat32& command, uint16_t aIndex) { + push(AsyncCommand(command, aIndex)); + return CommandStatus::SUCCESS; +} + +CommandStatus AsyncCommandHandler::Select(const AnalogOutputDouble64& command, uint16_t aIndex) { + return CommandStatus::SUCCESS; +} +CommandStatus AsyncCommandHandler::Operate(const AnalogOutputDouble64& command, uint16_t aIndex) { + push(AsyncCommand(command, aIndex)); + return CommandStatus::SUCCESS; +} + +AsyncCommand AsyncCommandHandler::pop() { + AsyncCommand* command = NULL; + queue_.pop(command); + return *command; +} + +bool AsyncCommandHandler::push(AsyncCommand command) { + return queue_.push(&command); +} diff --git a/cpp/outstation/JSONCommandHandler.h b/cpp/outstation/AsyncCommandHandler.h similarity index 80% rename from cpp/outstation/JSONCommandHandler.h rename to cpp/outstation/AsyncCommandHandler.h index 221a281889..152e3b4f24 100644 --- a/cpp/outstation/JSONCommandHandler.h +++ b/cpp/outstation/AsyncCommandHandler.h @@ -11,15 +11,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -#ifndef OPENDNP3_JSONCOMMANDHANDLER_H -#define OPENDNP3_JSONCOMMANDHANDLER_H +#ifndef OPENDNP3_ASYNCCOMMANDHANDLER_H +#define OPENDNP3_ASYNCCOMMANDHANDLER_H #include +#include +#include "AsyncCommand.cpp" -namespace opendnp3 { +using namespace opendnp3; -class JSONCommandHandler: public ICommandHandler { +class AsyncCommandHandler: public ICommandHandler { public: + ~AsyncCommandHandler() override final; CommandStatus Select(const ControlRelayOutputBlock& command, uint16_t aIndex) override final; CommandStatus Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) override final; @@ -36,20 +39,12 @@ class JSONCommandHandler: public ICommandHandler { CommandStatus Select(const AnalogOutputDouble64& command, uint16_t aIndex) override final; CommandStatus Operate(const AnalogOutputDouble64& command, uint16_t aIndex) override final; - static JSONCommandHandler& Instance() { - static JSONCommandHandler instance; - return instance; - } + AsyncCommand pop(); private: - JSONCommandHandler() { - } - - JSONCommandHandler(JSONCommandHandler const&) = delete; - void operator=(JSONCommandHandler const&) = delete; + bool push(AsyncCommand command); + boost::lockfree::queue queue_{32}; }; -} - #endif diff --git a/cpp/outstation/JSONCommandHandler.cpp b/cpp/outstation/JSONCommandHandler.cpp deleted file mode 100644 index a744f46421..0000000000 --- a/cpp/outstation/JSONCommandHandler.cpp +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Licensed 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. - */ - -#include -#include -#include - -#include - -#include "JSONCommandHandler.h" - -using namespace opendnp3; -using namespace rapidjson; - -// TODO do more than just print out JSON! - -CommandStatus JSONCommandHandler::Select(const ControlRelayOutputBlock& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("select"); - writer.StartObject(); - writer.String("command"); - writer.String("ControlRelayOutputBlock"); - writer.String("index"); - writer.Int(aIndex); - writer.String("functionCode"); - writer.Int(static_cast(command.functionCode)); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} -CommandStatus JSONCommandHandler::Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("operate"); - writer.StartObject(); - writer.String("command"); - writer.String("ControlRelayOutputBlock"); - writer.String("index"); - writer.Int(aIndex); - writer.String("functionCode"); - writer.Int(static_cast(command.functionCode)); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} - -CommandStatus JSONCommandHandler::Select(const AnalogOutputInt16& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("select"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputInt16"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Int(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} -CommandStatus JSONCommandHandler::Operate(const AnalogOutputInt16& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("operate"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputInt16"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Int(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} - -CommandStatus JSONCommandHandler::Select(const AnalogOutputInt32& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("select"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputInt32"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Int(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} -CommandStatus JSONCommandHandler::Operate(const AnalogOutputInt32& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("operate"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputInt32"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Int(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} - -CommandStatus JSONCommandHandler::Select(const AnalogOutputFloat32& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("select"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputFloat32"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Double(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} -CommandStatus JSONCommandHandler::Operate(const AnalogOutputFloat32& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("select"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputFloat32"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Double(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} - -CommandStatus JSONCommandHandler::Select(const AnalogOutputDouble64& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("select"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputDouble64"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Double(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} -CommandStatus JSONCommandHandler::Operate(const AnalogOutputDouble64& command, uint16_t aIndex) { - StringBuffer s; - Writer writer(s); - - writer.StartObject(); - writer.String("operate"); - writer.StartObject(); - writer.String("command"); - writer.String("AnalogOutputDouble64"); - writer.String("index"); - writer.Int(aIndex); - writer.String("value"); - writer.Double(command.value); - writer.String("status"); - writer.Int(static_cast(command.status)); - writer.EndObject(); - writer.EndObject(); - - std::cout << s.GetString() << std::endl; - return CommandStatus::SUCCESS; -} diff --git a/cpp/outstation/TCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp similarity index 55% rename from cpp/outstation/TCPSession.cpp rename to cpp/outstation/JSONTCPSession.cpp index b930e7b029..020a5d6cdd 100644 --- a/cpp/outstation/TCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -13,18 +13,26 @@ */ #include +#include #include #include #include #include #include +#include +#include +#include + using boost::asio::ip::tcp; -class TCPSession: public std::enable_shared_from_this { +using namespace asiodnp3; +using namespace rapidjson; + +class JSONTCPSession: public std::enable_shared_from_this { public: - TCPSession(tcp::socket socket) : - socket_(std::move(socket)) { + JSONTCPSession(tcp::socket socket, IOutstation* pOutstation) : + socket_(std::move(socket)), pOutstation_ { pOutstation } { } void start() { @@ -34,6 +42,8 @@ class TCPSession: public std::enable_shared_from_this { private: void do_read() { auto self(shared_from_this()); + std::cout << "nope!" << this << std::endl; + std::cout << "yep8" << &socket_ << std::endl; socket_.async_receive(boost::asio::buffer(data_, 1024), [this, self](boost::system::error_code error, std::size_t length) { if (!error) @@ -41,13 +51,34 @@ class TCPSession: public std::enable_shared_from_this { sbuf.sputn(data_,length); do_read(); } else if (error.value() == boost::system::errc::no_such_file_or_directory) { - // TODO deserialize JSON - std::cout << sbuf.str() << std::endl; + // Convert stringbuf to char[] and reset + sbuf.pubseekpos(0); + char chars[sbuf.in_avail()]; + sbuf.sgetn(chars, sbuf.in_avail()); + sbuf.str(std::string()); + + Document d; + d.ParseInsitu(chars); + + // Check if JSON object is valid and continue with logic + if (d.IsObject()) { + // TODO find specific values and act on them + if (d.HasMember("stars") && d["stars"].IsInt()) { + std::cout << d["stars"].GetInt() << std::endl; + } else { + std::cout << "nostars" << std::endl; + } + } else { + std::cerr << "not an object!" << std::endl; + } + } else { + std::cerr << "Error occurred in TCP session " << error << std::endl; } + }); } - void do_write(std::size_t length) { + void do_write() { // TODO serialize JSONCommand to this socket /*auto self(shared_from_this()); boost::asio::async_write(socket_, boost::asio::buffer(data_, length), [this, self](boost::system::error_code ec, std::size_t length) @@ -65,5 +96,6 @@ class TCPSession: public std::enable_shared_from_this { }; char data_[buffer_size]; std::stringbuf sbuf; + IOutstation* pOutstation_; } ; diff --git a/cpp/outstation/Main.cpp b/cpp/outstation/Main.cpp index 48133e113d..6267a7e4a8 100644 --- a/cpp/outstation/Main.cpp +++ b/cpp/outstation/Main.cpp @@ -18,7 +18,8 @@ #include #include - +#include +#include #include #include @@ -28,8 +29,6 @@ #include #include -#include "JSONCommandHandler.h" -#include "TCPServer.cpp" using namespace std; using namespace opendnp3; @@ -72,25 +71,27 @@ int main(int argc, char* argv[]) { // 1. convert incoming JSON to MeasUpdate and apply to outstation instance // 2. JSONCommandHandler incoming DNP3 commands should convert and forward via JSON - auto pOutstation = pChannel->AddOutstation("outstation", JSONCommandHandler::Instance(), DefaultOutstationApplication::Instance(), stackConfig); + AsyncCommandHandler handler; + IOutstation* pOutstation = pChannel->AddOutstation("outstation", handler, DefaultOutstationApplication::Instance(), stackConfig); // Configure and start outstation ConfigureDatabase(pOutstation->GetConfigView()); pOutstation->Enable(); - try { boost::asio::io_service io_service; - TCPServer s(io_service, 3384); + OutstationJSONTCPServer s(io_service, 3384, pOutstation); io_service.run(); + + // Initiate shutdown when signal received + std::signal(SIGINT, signal_handler); + pause(); + + io_service.stop(); } catch (std::exception& e) { std::cerr << "Exception: " << e.what() << "\n"; } - // Initiate shutdown when signal received - std::signal(SIGINT, signal_handler); - pause(); - pOutstation->Shutdown(); manager.Shutdown(); return 0; diff --git a/cpp/outstation/TCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp similarity index 57% rename from cpp/outstation/TCPServer.cpp rename to cpp/outstation/OutstationJSONTCPServer.cpp index 19d285350d..16ef0dcfed 100644 --- a/cpp/outstation/TCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -12,28 +12,34 @@ * limitations under the License. */ -#include "TCPSession.cpp" +#include +#include "JSONTCPSession.cpp" -class TCPServer { +using namespace asiodnp3; + +class OutstationJSONTCPServer { public: - TCPServer(boost::asio::io_service& io_service, short port) : - acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { + OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, IOutstation* pOutstation) : + pOutstation_ { pOutstation }, acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { do_accept(); } private: void do_accept() { - acceptor_.async_accept(socket_, [this](boost::system::error_code ec) + acceptor_.async_accept(socket_, [this](boost::system::error_code error) { - if (!ec) + if (!error) { - std::make_shared(std::move(socket_))->start(); + std::make_shared(std::move(socket_), pOutstation_)->start(); + } else { + std::cerr << "Unable to create TCP Session " << error << std::endl; } - do_accept(); }); } + IOutstation* pOutstation_; + AsyncCommandHandler handler_; tcp::acceptor acceptor_; tcp::socket socket_; }; From 04421d5d341ebb8eb5a10071662a8a4e9d475ea2 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 9 Sep 2015 11:13:35 -0500 Subject: [PATCH 09/32] BAT-44 - update docs with ubuntu build setup --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 0b353e946d..1c4bb08aa7 100755 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@ ported to run on various microcontrollers. Langauge bindings are available. Consult the documentation. +Ubuntu 14.04 Dependencies +========================= +```sudo apt-get install autoconf libtool cmake libasio-dev libboost-all-dev +``` + Build Steps =========== ```bash From 03b7b6f4f96f8abbb8500d2ff802303136da693c Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 9 Sep 2015 15:44:11 -0500 Subject: [PATCH 10/32] BAT-43 - half-baked threads, needs fixing --- Makefile.am | 2 +- cpp/outstation/Main.cpp | 98 ------------------------ cpp/outstation/OutstationApp.cpp | 124 +++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 99 deletions(-) delete mode 100644 cpp/outstation/Main.cpp create mode 100644 cpp/outstation/OutstationApp.cpp diff --git a/Makefile.am b/Makefile.am index d83bd3f389..0c8495004b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -217,7 +217,7 @@ outstation_LDFLAGS = -pthread -lboost_system -lboost_thread outstation_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la outstation_SOURCES = \ cpp/outstation/AsyncCommandHandler.cpp \ -cpp/outstation/Main.cpp +cpp/outstation/OutstationApp.cpp check_PROGRAMS = openpaltest dnp3test #TESTS = openpaltest dnp3test diff --git a/cpp/outstation/Main.cpp b/cpp/outstation/Main.cpp deleted file mode 100644 index 6267a7e4a8..0000000000 --- a/cpp/outstation/Main.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Licensed 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. - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -using namespace std; -using namespace opendnp3; -using namespace openpal; -using namespace asiopal; -using namespace asiodnp3; - -namespace { -volatile std::sig_atomic_t gSignalStatus; -} - -void signal_handler(int signal) { - gSignalStatus = signal; -} - -void ConfigureDatabase(DatabaseConfigView view) { - // TODO read from config file - view.analogs[0].variation = StaticAnalogVariation::Group30Var5; - view.analogs[0].metadata.clazz = PointClass::Class2; - view.analogs[0].metadata.variation = EventAnalogVariation::Group32Var7; -} - -int main(int argc, char* argv[]) { - DNP3Manager manager(1); - manager.AddLogSubscriber(&ConsoleLogger::Instance()); - - auto pChannel = manager.AddTCPServer("server", levels::NORMAL, TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", 20000); - pChannel->AddStateListener([](ChannelState state) - { - std::cout << "channel state: " << ChannelStateToString(state) << std::endl; - }); - - OutstationStackConfig stackConfig; - stackConfig.dbTemplate = DatabaseTemplate::AllTypes(5); - stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(5); - stackConfig.link.LocalAddr = 10; - stackConfig.link.RemoteAddr = 1; - - // TODO configure and run (tcp or other) server for JSON commands/events - // 1. convert incoming JSON to MeasUpdate and apply to outstation instance - // 2. JSONCommandHandler incoming DNP3 commands should convert and forward via JSON - - AsyncCommandHandler handler; - IOutstation* pOutstation = pChannel->AddOutstation("outstation", handler, DefaultOutstationApplication::Instance(), stackConfig); - - // Configure and start outstation - ConfigureDatabase(pOutstation->GetConfigView()); - pOutstation->Enable(); - - try { - boost::asio::io_service io_service; - OutstationJSONTCPServer s(io_service, 3384, pOutstation); - io_service.run(); - - // Initiate shutdown when signal received - std::signal(SIGINT, signal_handler); - pause(); - - io_service.stop(); - } catch (std::exception& e) { - std::cerr << "Exception: " << e.what() << "\n"; - } - - pOutstation->Shutdown(); - manager.Shutdown(); - return 0; -} diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp new file mode 100644 index 0000000000..d2c4fb85a9 --- /dev/null +++ b/cpp/outstation/OutstationApp.cpp @@ -0,0 +1,124 @@ +/** + * Licensed 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. + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include + +using namespace std; +using namespace opendnp3; +using namespace openpal; +using namespace asiopal; +using namespace asiodnp3; + +boost::condition_variable g_signal_cond; +boost::mutex g_signal_mutex; +volatile std::sig_atomic_t g_signal; + +void signal_handler(int signal_number) { + boost::unique_lock lock(g_signal_mutex); + g_signal = signal_number; + g_signal_cond.notify_all(); +} + +class OutstationApp { +public: + OutstationApp() { + } + + ~OutstationApp() { + std::cout << "ioservice stop()" << std::endl; + ioService_.stop(); + std::cout << "ioservice stop() stopped" << std::endl; + } + + void run() { + DNP3Manager manager(1); + manager.AddLogSubscriber(&ConsoleLogger::Instance()); + + IChannel* pChannel = manager.AddTCPServer("server", levels::NORMAL, TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", 20000); + pChannel->AddStateListener([](ChannelState state) + { + std::cout << "channel state: " << ChannelStateToString(state) << std::endl; + }); + + OutstationStackConfig stackConfig; + stackConfig.dbTemplate = DatabaseTemplate::AllTypes(5); + stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(5); + stackConfig.link.LocalAddr = 10; + stackConfig.link.RemoteAddr = 1; + + // TODO configure and run (tcp or other) server for JSON commands/events + // 1. convert incoming JSON to MeasUpdate and apply to outstation instance + // 2. JSONCommandHandler incoming DNP3 commands should convert and forward via JSON + + AsyncCommandHandler handler; + IOutstation* pOutstation = pChannel->AddOutstation("outstation", handler, DefaultOutstationApplication::Instance(), stackConfig); + + // Configure and start outstation + ConfigureDatabase(pOutstation->GetConfigView()); + pOutstation->Enable(); + try { + OutstationJSONTCPServer s(ioService_, 3384, pOutstation); + ioService_.run(); + } catch (std::exception& e) { + std::cerr << "Exception: " << e.what() << "\n"; + } + pOutstation->Shutdown(); + manager.Shutdown(); + std::cout << "run() finished" << std::endl; + } + +private: + void ConfigureDatabase(DatabaseConfigView view) { + // TODO read from config file + view.analogs[0].variation = StaticAnalogVariation::Group30Var5; + view.analogs[0].metadata.clazz = PointClass::Class2; + view.analogs[0].metadata.variation = EventAnalogVariation::Group32Var7; + } + + boost::asio::io_service ioService_; +}; + +int main(int argc, char* argv[]) { + std::signal(SIGINT, signal_handler); + + OutstationApp app; + std::thread t([&app] {app.run();}); + t.detach(); + + boost::unique_lock lock(g_signal_mutex); + while (!g_signal) { + g_signal_cond.wait(lock); + } + + std::cout << "goodbye" << std::endl; + return 0; +} From d4ed9a2cc66bed92720b7890ae9df7f82517114c Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 9 Sep 2015 17:15:55 -0500 Subject: [PATCH 11/32] BAT-43 - add stop/start actions on app - pass asynchandler to json tcp server correctly --- cpp/outstation/AsyncCommandHandler.cpp | 1 - cpp/outstation/OutstationApp.cpp | 33 +++++++--------------- cpp/outstation/OutstationJSONTCPServer.cpp | 6 ++-- 3 files changed, 13 insertions(+), 27 deletions(-) diff --git a/cpp/outstation/AsyncCommandHandler.cpp b/cpp/outstation/AsyncCommandHandler.cpp index fffc449fa4..0e1ed34dca 100644 --- a/cpp/outstation/AsyncCommandHandler.cpp +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -17,7 +17,6 @@ using namespace opendnp3; // TODO validate requests before returning CommandStatus - AsyncCommandHandler::~AsyncCommandHandler() { } diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index d2c4fb85a9..9e07b0d823 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -50,16 +50,7 @@ void signal_handler(int signal_number) { class OutstationApp { public: - OutstationApp() { - } - - ~OutstationApp() { - std::cout << "ioservice stop()" << std::endl; - ioService_.stop(); - std::cout << "ioservice stop() stopped" << std::endl; - } - - void run() { + void start() { DNP3Manager manager(1); manager.AddLogSubscriber(&ConsoleLogger::Instance()); @@ -85,17 +76,14 @@ class OutstationApp { // Configure and start outstation ConfigureDatabase(pOutstation->GetConfigView()); pOutstation->Enable(); - try { - OutstationJSONTCPServer s(ioService_, 3384, pOutstation); - ioService_.run(); - } catch (std::exception& e) { - std::cerr << "Exception: " << e.what() << "\n"; - } - pOutstation->Shutdown(); - manager.Shutdown(); - std::cout << "run() finished" << std::endl; + + OutstationJSONTCPServer s(io_service_, 3384, pOutstation, handler); + io_service_.run(); } + void stop() { + io_service_.stop(); + } private: void ConfigureDatabase(DatabaseConfigView view) { // TODO read from config file @@ -104,21 +92,20 @@ class OutstationApp { view.analogs[0].metadata.variation = EventAnalogVariation::Group32Var7; } - boost::asio::io_service ioService_; + boost::asio::io_service io_service_; }; int main(int argc, char* argv[]) { std::signal(SIGINT, signal_handler); OutstationApp app; - std::thread t([&app] {app.run();}); + std::thread t([&app] {app.start();}); t.detach(); boost::unique_lock lock(g_signal_mutex); while (!g_signal) { g_signal_cond.wait(lock); } - - std::cout << "goodbye" << std::endl; + app.stop(); return 0; } diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp index 16ef0dcfed..7aed9f45d1 100644 --- a/cpp/outstation/OutstationJSONTCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -19,8 +19,8 @@ using namespace asiodnp3; class OutstationJSONTCPServer { public: - OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, IOutstation* pOutstation) : - pOutstation_ { pOutstation }, acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { + OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, IOutstation* pOutstation, AsyncCommandHandler& handler) : + handler_(handler), pOutstation_ { pOutstation }, acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { do_accept(); } @@ -39,7 +39,7 @@ class OutstationJSONTCPServer { } IOutstation* pOutstation_; - AsyncCommandHandler handler_; + AsyncCommandHandler& handler_; tcp::acceptor acceptor_; tcp::socket socket_; }; From d92f96f77197c1057d24e5fd465cb78956a43dee Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Thu, 10 Sep 2015 15:17:05 -0500 Subject: [PATCH 12/32] BAT-44 - AsyncCommand refers to pointers to allow multiple types being set (NULL otherwise) - AsyncCommandHandler queues commands using blocking queue w/ one subscriber supported - OutstationJSONTCPServer tracks multiple sessions and publishes AsyncCommands to all - JSONTCPSession writes AsyncCommands received to stdout, needs to write to socket_ - lock/thread/queue updated to C++11 std implementations --- cpp/outstation/AsyncCommand.cpp | 38 ++++----- cpp/outstation/AsyncCommandHandler.cpp | 21 ++++- cpp/outstation/AsyncCommandHandler.h | 13 ++- cpp/outstation/JSONTCPSession.cpp | 98 +++++++++++++++------- cpp/outstation/OutstationApp.cpp | 26 ++---- cpp/outstation/OutstationJSONTCPServer.cpp | 32 ++++++- 6 files changed, 148 insertions(+), 80 deletions(-) diff --git a/cpp/outstation/AsyncCommand.cpp b/cpp/outstation/AsyncCommand.cpp index 2bded7e3d1..590f2066f9 100644 --- a/cpp/outstation/AsyncCommand.cpp +++ b/cpp/outstation/AsyncCommand.cpp @@ -16,49 +16,49 @@ using namespace opendnp3; class AsyncCommand { public: - AsyncCommand() : - idx_(0) { - } + /* AsyncCommand() : + idx_(0), crob_(NULL), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(NULL) { + }*/ AsyncCommand(ControlRelayOutputBlock crob, uint16_t idx) : - crob_(crob), idx_(idx) { + idx_(idx), crob_(&crob), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(NULL) { } AsyncCommand(AnalogOutputInt16 aoInt16, uint16_t idx) : - aoInt16_(aoInt16), idx_(idx) { + idx_(idx), crob_(NULL), aoInt16_(&aoInt16), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(NULL) { } AsyncCommand(AnalogOutputInt32 aoInt32, uint16_t idx) : - aoInt32_(aoInt32), idx_(idx) { + idx_(idx), crob_(NULL), aoInt16_(NULL), aoInt32_(&aoInt32), aoFloat32_(NULL), aoDouble64_(NULL) { } AsyncCommand(AnalogOutputFloat32 aoFloat32, uint16_t idx) : - aoFloat32_(aoFloat32), idx_(idx) { + idx_(idx), crob_(NULL), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(&aoFloat32), aoDouble64_(NULL) { } AsyncCommand(AnalogOutputDouble64 aoDouble64, uint16_t idx) : - aoDouble64_(aoDouble64), idx_(idx) { + idx_(idx), crob_(NULL), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(&aoDouble64) { } - uint16_t Index() { + uint16_t Idx() { return idx_; } - ControlRelayOutputBlock CROB() { + ControlRelayOutputBlock* CROB() { return crob_; } - AnalogOutputInt16 AOInt16() { + AnalogOutputInt16* AOInt16() { return aoInt16_; } - AnalogOutputInt32 AOInt32() { + AnalogOutputInt32* AOInt32() { return aoInt32_; } - AnalogOutputFloat32 AOFloat32() { + AnalogOutputFloat32* AOFloat32() { return aoFloat32_; } - AnalogOutputDouble64 AODouble64() { + AnalogOutputDouble64* AODouble64() { return aoDouble64_; } private: uint16_t idx_; - ControlRelayOutputBlock crob_; - AnalogOutputInt16 aoInt16_; - AnalogOutputInt32 aoInt32_; - AnalogOutputFloat32 aoFloat32_; - AnalogOutputDouble64 aoDouble64_; + ControlRelayOutputBlock* crob_; + AnalogOutputInt16* aoInt16_; + AnalogOutputInt32* aoInt32_; + AnalogOutputFloat32* aoFloat32_; + AnalogOutputDouble64* aoDouble64_; }; diff --git a/cpp/outstation/AsyncCommandHandler.cpp b/cpp/outstation/AsyncCommandHandler.cpp index 0e1ed34dca..0228e63c95 100644 --- a/cpp/outstation/AsyncCommandHandler.cpp +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -12,6 +12,8 @@ * limitations under the License. */ +#include + #include "AsyncCommandHandler.h" using namespace opendnp3; @@ -60,12 +62,23 @@ CommandStatus AsyncCommandHandler::Operate(const AnalogOutputDouble64& command, return CommandStatus::SUCCESS; } +/** + * Blocks on queue until one or more AsyncCommand elements are available. + * Assumes only one subscriber. + */ AsyncCommand AsyncCommandHandler::pop() { - AsyncCommand* command = NULL; - queue_.pop(command); + std::unique_lock lock(queue_mutex_); + while (queue_.empty()) { + queue_cond_.wait(lock); + } + AsyncCommand* command = queue_.front(); + queue_.pop(); return *command; } -bool AsyncCommandHandler::push(AsyncCommand command) { - return queue_.push(&command); +void AsyncCommandHandler::push(AsyncCommand command) { + std::unique_lock lock(queue_mutex_); + queue_.push(&command); + lock.unlock(); + queue_cond_.notify_one(); } diff --git a/cpp/outstation/AsyncCommandHandler.h b/cpp/outstation/AsyncCommandHandler.h index 152e3b4f24..d5628fbfd0 100644 --- a/cpp/outstation/AsyncCommandHandler.h +++ b/cpp/outstation/AsyncCommandHandler.h @@ -15,7 +15,11 @@ #define OPENDNP3_ASYNCCOMMANDHANDLER_H #include -#include + +#include +#include +#include + #include "AsyncCommand.cpp" using namespace opendnp3; @@ -42,8 +46,11 @@ class AsyncCommandHandler: public ICommandHandler { AsyncCommand pop(); private: - bool push(AsyncCommand command); - boost::lockfree::queue queue_{32}; + void push(AsyncCommand command); + + std::queue queue_; + std::mutex queue_mutex_; + std::condition_variable queue_cond_; }; #endif diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index 020a5d6cdd..691dcb36a6 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -13,12 +13,7 @@ */ #include -#include -#include -#include -#include #include -#include #include #include @@ -36,30 +31,85 @@ class JSONTCPSession: public std::enable_shared_from_this { } void start() { - do_read(); + read(); + } + + void write(AsyncCommand command) { + StringBuffer sb; + Writer writer(sb); + writer.StartObject(); + writer.String("index"); + writer.Int(command.Idx()); + if (command.AOInt16() != NULL) { + writer.String("type"); + writer.String("AnalogInt16"); + writer.String("value"); + writer.Int(command.AOInt16()->value); + writer.String("status"); + writer.Int(static_cast(command.AOInt16()->status)); + } else if (command.AOInt32() != NULL) { + writer.String("type"); + writer.String("AnalogInt32"); + writer.String("value"); + writer.Int(command.AOInt32()->value); + writer.String("status"); + writer.Int(static_cast(command.AOInt32()->status)); + } else if (command.AOFloat32() != NULL) { + writer.String("type"); + writer.String("AnalogFloat32"); + writer.String("value"); + writer.Double(command.AOFloat32()->value); + writer.String("status"); + writer.Int(static_cast(command.AOFloat32()->status)); + } else if (command.AODouble64() != NULL) { + writer.String("type"); + writer.String("AnalogDouble64"); + writer.String("value"); + writer.Double(command.AODouble64()->value); + writer.String("status"); + writer.Int(static_cast(command.AODouble64()->status)); + } else if (command.CROB() != NULL) { + // TODO handle all CROB attributes + writer.String("type"); + writer.String("ControlRelayOutputBlock"); + writer.String("value"); + writer.Int(static_cast(command.CROB()->functionCode)); + writer.String("status"); + writer.Int(static_cast(command.CROB()->status)); + } else { + //TODO exception handle! + } + writer.EndObject(); + std::cout << sb.GetString() << std::endl; + + //TODO write to socket + + /*auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(sb., length), [this, self](boost::system::error_code ec, std::size_t length) + { + });*/ } private: - void do_read() { + void read() { auto self(shared_from_this()); - std::cout << "nope!" << this << std::endl; - std::cout << "yep8" << &socket_ << std::endl; - socket_.async_receive(boost::asio::buffer(data_, 1024), [this, self](boost::system::error_code error, std::size_t length) + socket_.async_receive(boost::asio::buffer(data_, buffer_size), [this, self](boost::system::error_code error, std::size_t length) { if (!error) { - sbuf.sputn(data_,length); - do_read(); + data_sbuf_.sputn(data_,length); + read(); } else if (error.value() == boost::system::errc::no_such_file_or_directory) { // Convert stringbuf to char[] and reset - sbuf.pubseekpos(0); - char chars[sbuf.in_avail()]; - sbuf.sgetn(chars, sbuf.in_avail()); - sbuf.str(std::string()); + data_sbuf_.pubseekpos(0); + char chars[data_sbuf_.in_avail()]; + data_sbuf_.sgetn(chars, data_sbuf_.in_avail()); + data_sbuf_.str(std::string()); Document d; d.ParseInsitu(chars); + // TODO create MeasUpdate and apply to pOutstation_, threading issues? // Check if JSON object is valid and continue with logic if (d.IsObject()) { // TODO find specific values and act on them @@ -78,24 +128,12 @@ class JSONTCPSession: public std::enable_shared_from_this { }); } - void do_write() { - // TODO serialize JSONCommand to this socket - /*auto self(shared_from_this()); - boost::asio::async_write(socket_, boost::asio::buffer(data_, length), [this, self](boost::system::error_code ec, std::size_t length) - { - if (!ec) - { - do_read(); - } - });*/ - } - - tcp::socket socket_; enum { buffer_size = 1024 }; char data_[buffer_size]; - std::stringbuf sbuf; + std::stringbuf data_sbuf_; + tcp::socket socket_; IOutstation* pOutstation_; } ; diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index 9e07b0d823..ef8e6b8070 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -15,35 +15,24 @@ #include #include #include -#include - #include - -#include - #include #include - #include #include -#include -#include #include -#include +#include -using namespace std; using namespace opendnp3; using namespace openpal; -using namespace asiopal; -using namespace asiodnp3; -boost::condition_variable g_signal_cond; -boost::mutex g_signal_mutex; +std::condition_variable g_signal_cond; +std::mutex g_signal_mutex; volatile std::sig_atomic_t g_signal; void signal_handler(int signal_number) { - boost::unique_lock lock(g_signal_mutex); + std::unique_lock lock(g_signal_mutex); g_signal = signal_number; g_signal_cond.notify_all(); } @@ -66,10 +55,6 @@ class OutstationApp { stackConfig.link.LocalAddr = 10; stackConfig.link.RemoteAddr = 1; - // TODO configure and run (tcp or other) server for JSON commands/events - // 1. convert incoming JSON to MeasUpdate and apply to outstation instance - // 2. JSONCommandHandler incoming DNP3 commands should convert and forward via JSON - AsyncCommandHandler handler; IOutstation* pOutstation = pChannel->AddOutstation("outstation", handler, DefaultOutstationApplication::Instance(), stackConfig); @@ -82,6 +67,7 @@ class OutstationApp { } void stop() { + // TODO shutdown correctly io_service_.stop(); } private: @@ -102,7 +88,7 @@ int main(int argc, char* argv[]) { std::thread t([&app] {app.start();}); t.detach(); - boost::unique_lock lock(g_signal_mutex); + std::unique_lock lock(g_signal_mutex); while (!g_signal) { g_signal_cond.wait(lock); } diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp index 7aed9f45d1..011cc61e35 100644 --- a/cpp/outstation/OutstationJSONTCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -13,6 +13,8 @@ */ #include +#include + #include "JSONTCPSession.cpp" using namespace asiodnp3; @@ -21,25 +23,47 @@ class OutstationJSONTCPServer { public: OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, IOutstation* pOutstation, AsyncCommandHandler& handler) : handler_(handler), pOutstation_ { pOutstation }, acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { - do_accept(); + start(); } private: - void do_accept() { + void start() { + // TODO shutdown thread correctly on SIGINT + std::thread t([this] {subscribe();}); + t.detach(); + accept(); + } + + void subscribe() { + while (true) { + AsyncCommand command = handler_.pop(); + for (std::shared_ptr session : sessions_) { + // TODO remove old sessions + std::cout << session << " | " << session.use_count() << std::endl; + session.get()->write(command); + } + } + } + + void accept() { acceptor_.async_accept(socket_, [this](boost::system::error_code error) { if (!error) { - std::make_shared(std::move(socket_), pOutstation_)->start(); + std::shared_ptr pSession = std::make_shared(std::move(socket_), pOutstation_); + sessions_.insert(pSession); + pSession->start(); } else { std::cerr << "Unable to create TCP Session " << error << std::endl; } - do_accept(); + accept(); }); } IOutstation* pOutstation_; AsyncCommandHandler& handler_; + std::set> sessions_; + tcp::acceptor acceptor_; tcp::socket socket_; }; From 50f321848bb7e0fe9821d64e6d065c2cacda495a Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Thu, 10 Sep 2015 16:52:12 -0500 Subject: [PATCH 13/32] BAT-44 - update JSON processing to send size before json char array for supporting multiple objects on same session --- cpp/outstation/JSONTCPSession.cpp | 109 +++++++++++++++++------------- 1 file changed, 63 insertions(+), 46 deletions(-) diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index 691dcb36a6..831d904b1b 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -35,6 +35,68 @@ class JSONTCPSession: public std::enable_shared_from_this { } void write(AsyncCommand command) { + StringBuffer json_sb = toJSONStringBuffer(command); + std::string json_sb_size_str = std::to_string(json_sb.GetSize()); + + std::stringbuf data_sbuf(json_sb_size_str); + data_sbuf.pubseekoff(0, std::ios_base::end, std::ios_base::out); + data_sbuf.sputn(json_sb.GetString(), json_sb.GetSize()); + + data_sbuf.pubseekpos(0); + char data_char[data_sbuf.in_avail()]; + data_sbuf.pubseekpos(0); + data_sbuf.sgetn(data_char, data_sbuf.in_avail()); + data_sbuf.pubseekpos(0); + + auto self(shared_from_this()); + boost::asio::async_write(socket_, boost::asio::buffer(data_char, data_sbuf.in_avail()), + [this, self](boost::system::error_code ec, std::size_t length) { + // TODO handle errors + }); + } + +private: + void read() { + auto self(shared_from_this()); + socket_.async_receive(boost::asio::buffer(data_, buffer_size), [this, self](boost::system::error_code error, std::size_t length) + { + if (!error) + { + data_sbuf_.sputn(data_,length); + // TODO check if JSON object finished, reset data_sbuf_ + read(); + } else if (error.value() == boost::system::errc::no_such_file_or_directory) { + // Convert stringbuf to char[] and reset + data_sbuf_.pubseekpos(0); + char chars[data_sbuf_.in_avail()]; + data_sbuf_.sgetn(chars, data_sbuf_.in_avail()); + data_sbuf_.str(std::string()); + + Document d; + d.ParseInsitu(chars); + + // TODO create MeasUpdate and apply to pOutstation_, threading issues? + // Check if JSON object is valid and continue with logic + if (d.IsObject()) { + // TODO find specific values and act on them + if (d.HasMember("stars") && d["stars"].IsInt()) { + std::cout << d["stars"].GetInt() << std::endl; + } else { + std::cout << "nostars" << std::endl; + } + } else { + std::cerr << "not an object!" << std::endl; + } + } else { + std::cerr << "Error occurred in TCP session " << error << std::endl; + } + }); + } + + /** + * Takes input AsyncCommand and serializes JSON object to resulting StringBuffer + */ + StringBuffer toJSONStringBuffer(AsyncCommand command) { StringBuffer sb; Writer writer(sb); writer.StartObject(); @@ -80,52 +142,7 @@ class JSONTCPSession: public std::enable_shared_from_this { //TODO exception handle! } writer.EndObject(); - std::cout << sb.GetString() << std::endl; - - //TODO write to socket - - /*auto self(shared_from_this()); - boost::asio::async_write(socket_, boost::asio::buffer(sb., length), [this, self](boost::system::error_code ec, std::size_t length) - { - });*/ - } - -private: - void read() { - auto self(shared_from_this()); - socket_.async_receive(boost::asio::buffer(data_, buffer_size), [this, self](boost::system::error_code error, std::size_t length) - { - if (!error) - { - data_sbuf_.sputn(data_,length); - read(); - } else if (error.value() == boost::system::errc::no_such_file_or_directory) { - // Convert stringbuf to char[] and reset - data_sbuf_.pubseekpos(0); - char chars[data_sbuf_.in_avail()]; - data_sbuf_.sgetn(chars, data_sbuf_.in_avail()); - data_sbuf_.str(std::string()); - - Document d; - d.ParseInsitu(chars); - - // TODO create MeasUpdate and apply to pOutstation_, threading issues? - // Check if JSON object is valid and continue with logic - if (d.IsObject()) { - // TODO find specific values and act on them - if (d.HasMember("stars") && d["stars"].IsInt()) { - std::cout << d["stars"].GetInt() << std::endl; - } else { - std::cout << "nostars" << std::endl; - } - } else { - std::cerr << "not an object!" << std::endl; - } - } else { - std::cerr << "Error occurred in TCP session " << error << std::endl; - } - - }); + return sb; } enum { From b2e1e153b94d1fcc0881d0995947abe65681b381 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Fri, 11 Sep 2015 15:03:10 -0500 Subject: [PATCH 14/32] BAT-44 - sending AsyncCommands to sockets is nearly complete, started working on receiving JSON commands and converting to MeasUpdate --- cpp/outstation/JSONTCPSession.cpp | 110 ++++++++++++++++++------------ 1 file changed, 67 insertions(+), 43 deletions(-) diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index 831d904b1b..ef70b97e98 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -12,18 +12,23 @@ * limitations under the License. */ +#include #include -#include - #include #include #include +#include + using boost::asio::ip::tcp; using namespace asiodnp3; using namespace rapidjson; +/** + * JSONTCPSession reads/writes JSON objects to socket that represent outstation commands. + * Protocol is as follows: [int64_t size][size bytes of JSON] + */ class JSONTCPSession: public std::enable_shared_from_this { public: JSONTCPSession(tcp::socket socket, IOutstation* pOutstation) : @@ -35,58 +40,55 @@ class JSONTCPSession: public std::enable_shared_from_this { } void write(AsyncCommand command) { - StringBuffer json_sb = toJSONStringBuffer(command); - std::string json_sb_size_str = std::to_string(json_sb.GetSize()); + StringBuffer out_json_sb = toJSONStringBuffer(command); - std::stringbuf data_sbuf(json_sb_size_str); - data_sbuf.pubseekoff(0, std::ios_base::end, std::ios_base::out); - data_sbuf.sputn(json_sb.GetString(), json_sb.GetSize()); + // Get JSON string length, convert size to int64_t and write in character stream + int64_t json_size = out_json_sb.GetSize(); - data_sbuf.pubseekpos(0); - char data_char[data_sbuf.in_avail()]; - data_sbuf.pubseekpos(0); - data_sbuf.sgetn(data_char, data_sbuf.in_avail()); - data_sbuf.pubseekpos(0); + // Concat raw json_size bytes and JSON bytes + char out_data[sizeof(int64_t) + out_json_sb.GetSize()]; + memcpy(out_data, &json_size, sizeof(int64_t)); + memcpy(out_data + sizeof(int64_t), out_json_sb.GetString(), out_json_sb.GetSize()); + // Write data to socket auto self(shared_from_this()); - boost::asio::async_write(socket_, boost::asio::buffer(data_char, data_sbuf.in_avail()), - [this, self](boost::system::error_code ec, std::size_t length) { - // TODO handle errors - }); + boost::asio::async_write(socket_, boost::asio::buffer(out_data, sizeof(out_data)), [this, self](boost::system::error_code ec, std::size_t length) { + // TODO handle errors + }); } private: + /** + * Reads incoming JSON preceded by int64_t with number of bytes to read. + * TODO needs better error handling and array bound checking + */ void read() { auto self(shared_from_this()); - socket_.async_receive(boost::asio::buffer(data_, buffer_size), [this, self](boost::system::error_code error, std::size_t length) + socket_.async_receive(boost::asio::buffer(buf_, buf_sz_), [this, self](boost::system::error_code error, std::size_t length) { if (!error) { - data_sbuf_.sputn(data_,length); - // TODO check if JSON object finished, reset data_sbuf_ - read(); - } else if (error.value() == boost::system::errc::no_such_file_or_directory) { - // Convert stringbuf to char[] and reset - data_sbuf_.pubseekpos(0); - char chars[data_sbuf_.in_avail()]; - data_sbuf_.sgetn(chars, data_sbuf_.in_avail()); - data_sbuf_.str(std::string()); - - Document d; - d.ParseInsitu(chars); - - // TODO create MeasUpdate and apply to pOutstation_, threading issues? - // Check if JSON object is valid and continue with logic - if (d.IsObject()) { - // TODO find specific values and act on them - if (d.HasMember("stars") && d["stars"].IsInt()) { - std::cout << d["stars"].GetInt() << std::endl; - } else { - std::cout << "nostars" << std::endl; + // Initialize for new JSON object in stream + if (data_pos_ == 0) { + if (length < sizeof(int64_t)) { + // TODO throw exception, bad things happening } - } else { - std::cerr << "not an object!" << std::endl; + // Initialize data_sz_ + memcpy(&data_sz_, buf_, sizeof(int64_t)); + // Initialize data, copy first buffer's worth of char[] + data_ = new char[data_sz_]; + memcpy(&data_ + sizeof(int64_t), buf_, length - sizeof(int64_t)); + data_pos_ = length - sizeof(int64_t); + } else if(data_pos_ < data_sz_) { + + } + if (data_pos_ == data_sz_) { + //instantiate MeasUpdate and apply + applyUpdate(data_); } + read(); + } else if (error.value() == boost::system::errc::no_such_file_or_directory) { + // Socket is closed } else { std::cerr << "Error occurred in TCP session " << error << std::endl; } @@ -145,11 +147,33 @@ class JSONTCPSession: public std::enable_shared_from_this { return sb; } + /** + * Takes a char[] JSON object and de-serializes to a new MeasUpdate which is applied against pOustation_ + */ + void applyUpdate(char* pchar_json_data) { + Document d; + d.ParseInsitu(pchar_json_data); + if (d.IsObject()) { + MeasUpdate update(pOutstation_); + if (d.HasMember("stars") && d["stars"].IsInt()) { + std::cout << d["stars"].GetInt() << std::endl; + } else { + std::cout << "nostars" << std::endl; + } + } else { + std::cerr << "not an object!" << std::endl; + } + } + enum { - buffer_size = 1024 + buf_sz_ = 1024 }; - char data_[buffer_size]; - std::stringbuf data_sbuf_; + char buf_[buf_sz_]; + + char* data_ = new char; + int64_t data_pos_ = 0; + int64_t data_sz_ = 0; + tcp::socket socket_; IOutstation* pOutstation_; } From 17c1cc5f239229e81fa3c4fb3c4e4f792a96c5fe Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Fri, 11 Sep 2015 15:50:09 -0500 Subject: [PATCH 15/32] BAT-44 - add logging messages to figure out deserialization protocol --- cpp/outstation/JSONTCPSession.cpp | 17 +++++++++++------ cpp/tests/outstation/ao16_test.bin | Bin 0 -> 63 bytes cpp/tests/outstation/test.sh | 2 ++ 3 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 cpp/tests/outstation/ao16_test.bin create mode 100755 cpp/tests/outstation/test.sh diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index ef70b97e98..0091635228 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -68,21 +68,25 @@ class JSONTCPSession: public std::enable_shared_from_this { { if (!error) { + std::cout << "buf:" << buf_ << std::endl; + std::cout << "1 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; // Initialize for new JSON object in stream if (data_pos_ == 0) { - if (length < sizeof(int64_t)) { - // TODO throw exception, bad things happening - } // Initialize data_sz_ - memcpy(&data_sz_, buf_, sizeof(int64_t)); + memcpy(&data_sz_, &buf_, sizeof(int64_t)); // Initialize data, copy first buffer's worth of char[] data_ = new char[data_sz_]; - memcpy(&data_ + sizeof(int64_t), buf_, length - sizeof(int64_t)); data_pos_ = length - sizeof(int64_t); - } else if(data_pos_ < data_sz_) { + memcpy(data_, &buf_+ sizeof(int64_t), data_pos_); + std::cout << "2 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; + } else if(data_pos_ < data_sz_) { + std::cout << "3 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; + memcpy(&data_ + data_pos_, buf_, length); } + std::cout << "4 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; if (data_pos_ == data_sz_) { + std::cout << "yep3" << std::endl; //instantiate MeasUpdate and apply applyUpdate(data_); } @@ -151,6 +155,7 @@ class JSONTCPSession: public std::enable_shared_from_this { * Takes a char[] JSON object and de-serializes to a new MeasUpdate which is applied against pOustation_ */ void applyUpdate(char* pchar_json_data) { + std::cout << "applying update" << pchar_json_data << std::endl; Document d; d.ParseInsitu(pchar_json_data); if (d.IsObject()) { diff --git a/cpp/tests/outstation/ao16_test.bin b/cpp/tests/outstation/ao16_test.bin new file mode 100644 index 0000000000000000000000000000000000000000..d962be868c2abd2d2fdfe937667e6379fc57814f GIT binary patch literal 63 zcmYdgfPiYHlFEWqB`YPzyu_URbkDpJLo+2ErOdpP)Cwgl10AKZ#GF!~yor&HQgKOQ LNog@qw3Z711N#we literal 0 HcmV?d00001 diff --git a/cpp/tests/outstation/test.sh b/cpp/tests/outstation/test.sh new file mode 100755 index 0000000000..7a5ffa6aac --- /dev/null +++ b/cpp/tests/outstation/test.sh @@ -0,0 +1,2 @@ +#!/bin/sh +cat ao16_test.bin | nc localhost 3384 From 604f81ce8767e699642fcfb4eec3b525576f81df Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Mon, 14 Sep 2015 17:07:59 -0500 Subject: [PATCH 16/32] BAT-44 - remove newline from ao16_test.bin - fix up char[] buffering logic for read() - NEEDS proper JSON->MeasUpdate parsing --- cpp/outstation/JSONTCPSession.cpp | 63 ++++++++++++++++------------- cpp/tests/outstation/ao16_test.bin | Bin 63 -> 62 bytes 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index 0091635228..5f1f4b12a4 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -60,35 +60,34 @@ class JSONTCPSession: public std::enable_shared_from_this { private: /** * Reads incoming JSON preceded by int64_t with number of bytes to read. - * TODO needs better error handling and array bound checking */ void read() { auto self(shared_from_this()); - socket_.async_receive(boost::asio::buffer(buf_, buf_sz_), [this, self](boost::system::error_code error, std::size_t length) + socket_.async_receive(boost::asio::buffer(buf_, buf_sz_), [this, self](boost::system::error_code error, size_t length) { if (!error) { - std::cout << "buf:" << buf_ << std::endl; - std::cout << "1 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; // Initialize for new JSON object in stream if (data_pos_ == 0) { // Initialize data_sz_ memcpy(&data_sz_, &buf_, sizeof(int64_t)); - // Initialize data, copy first buffer's worth of char[] + + // Initialize data, copy first buffer's worth of chars data_ = new char[data_sz_]; data_pos_ = length - sizeof(int64_t); - memcpy(data_, &buf_+ sizeof(int64_t), data_pos_); - - std::cout << "2 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; + memcpy(data_, &buf_[sizeof(int64_t)], data_pos_); } else if(data_pos_ < data_sz_) { - std::cout << "3 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; - memcpy(&data_ + data_pos_, buf_, length); + // copy next buffer's worth of chars + memcpy(data_ + data_pos_, buf_, length); + data_pos_ += length; } - std::cout << "4 pos:" << data_pos_ << " sz:" << data_sz_ << std::endl; + // if all chars have been transferred, process JSON object if (data_pos_ == data_sz_) { - std::cout << "yep3" << std::endl; - //instantiate MeasUpdate and apply + // apply update from JSON chars, deallocate memory and reset pointers applyUpdate(data_); + delete[] data_; + data_pos_ = 0; + data_sz_ = 0; } read(); } else if (error.value() == boost::system::errc::no_such_file_or_directory) { @@ -155,31 +154,37 @@ class JSONTCPSession: public std::enable_shared_from_this { * Takes a char[] JSON object and de-serializes to a new MeasUpdate which is applied against pOustation_ */ void applyUpdate(char* pchar_json_data) { - std::cout << "applying update" << pchar_json_data << std::endl; + std::cout << "applying update: " << pchar_json_data << std::endl; Document d; d.ParseInsitu(pchar_json_data); if (d.IsObject()) { MeasUpdate update(pOutstation_); - if (d.HasMember("stars") && d["stars"].IsInt()) { - std::cout << d["stars"].GetInt() << std::endl; - } else { - std::cout << "nostars" << std::endl; - } + if (d.HasMember("type") && d["type"].IsString() && d.HasMember("index") && d["index"].IsInt()) { + std::cout << d["type"].GetString() << std::endl; + /* TODO FIX ME + * char* type = d["type"].GetString(); + if (type == "AnalogInt16") { + if (d.HasMember("value") && d["value"].IsInt()) { + update.Update(Analog(d["value"].GetInt()), d["index"].GetInt()); + }}*/ } else { - std::cerr << "not an object!" << std::endl; + std::cout << "no type" << std::endl; } + } else { + std::cerr << "not an object!" << std::endl; } +} - enum { - buf_sz_ = 1024 - }; - char buf_[buf_sz_]; +enum { + buf_sz_ = 1024 +}; +char buf_[buf_sz_]; - char* data_ = new char; - int64_t data_pos_ = 0; - int64_t data_sz_ = 0; +char* data_ = NULL; +size_t data_pos_ = 0; +int64_t data_sz_ = 0; - tcp::socket socket_; - IOutstation* pOutstation_; +tcp::socket socket_; +IOutstation* pOutstation_; } ; diff --git a/cpp/tests/outstation/ao16_test.bin b/cpp/tests/outstation/ao16_test.bin index d962be868c2abd2d2fdfe937667e6379fc57814f..14774f03c558b5cf4594378b40705a2e7ccffb9e 100644 GIT binary patch delta 7 OcmcDwV>Fw{XbS)a0|A5p delta 9 QcmcDsXH1>QXv@e2017Yxz5oCK From 94d4106eb9047b8e22594c55f4535d559261c3c6 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 15 Sep 2015 13:45:41 -0500 Subject: [PATCH 17/32] BAT-44 - clean up read() and applyUpdate() - added better test file with multiple updates in same tcp buffer (also tested changing buffer lower than message size) --- cpp/outstation/JSONTCPSession.cpp | 139 +++++++++++++++--------- cpp/tests/outstation/ao16_test.bin | Bin 62 -> 0 bytes cpp/tests/outstation/jsontcp-sample.bin | Bin 0 -> 263 bytes cpp/tests/outstation/jsontcp-test.sh | 2 + cpp/tests/outstation/test.sh | 2 - 5 files changed, 91 insertions(+), 52 deletions(-) delete mode 100644 cpp/tests/outstation/ao16_test.bin create mode 100644 cpp/tests/outstation/jsontcp-sample.bin create mode 100755 cpp/tests/outstation/jsontcp-test.sh delete mode 100755 cpp/tests/outstation/test.sh diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index 5f1f4b12a4..e5666c7656 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -58,40 +58,65 @@ class JSONTCPSession: public std::enable_shared_from_this { } private: + // Type constants (AsyncCommand) + static constexpr const char* AI16 = "AnalogInt16"; + static constexpr const char* AI32 = "AnalogInt32"; + static constexpr const char* AF32 = "AnalogFloat32"; + static constexpr const char* AD64 = "AnalogDouble64"; + static constexpr const char* CROB = "ControlRelayOutputBlock"; + + static constexpr const char* BIN = "Binary"; + static constexpr const char* DBBIN = "DoubleBitBinary"; + static constexpr const char* CI32 = "CounterInt32"; + static constexpr const char* FCI32 = "FrozenCounterInt32"; + /** * Reads incoming JSON preceded by int64_t with number of bytes to read. */ void read() { auto self(shared_from_this()); - socket_.async_receive(boost::asio::buffer(buf_, buf_sz_), [this, self](boost::system::error_code error, size_t length) + socket_.async_receive(boost::asio::buffer(buf_, buf_sz), [this, self](boost::system::error_code error, size_t length) { + if (!error) { - // Initialize for new JSON object in stream - if (data_pos_ == 0) { - // Initialize data_sz_ - memcpy(&data_sz_, &buf_, sizeof(int64_t)); - - // Initialize data, copy first buffer's worth of chars - data_ = new char[data_sz_]; - data_pos_ = length - sizeof(int64_t); - memcpy(data_, &buf_[sizeof(int64_t)], data_pos_); - } else if(data_pos_ < data_sz_) { - // copy next buffer's worth of chars - memcpy(data_ + data_pos_, buf_, length); - data_pos_ += length; - } - // if all chars have been transferred, process JSON object - if (data_pos_ == data_sz_) { - // apply update from JSON chars, deallocate memory and reset pointers - applyUpdate(data_); - delete[] data_; - data_pos_ = 0; - data_sz_ = 0; + size_t buf_pos = 0; + while (buf_pos < length) { + size_t buf_rem = length - buf_pos; + if (data_pos_ == 0) { + /** initialize data size*/ + memcpy(&data_sz_, &buf_[buf_pos], sizeof(int64_t)); + buf_pos += sizeof(int64_t); + buf_rem -= sizeof(int64_t); + + /** initialize data array */ + data_ = new char[data_sz_+1]; + data_[data_sz_] = '\0'; + + /** copy data from buffer (max(data_sz_,length)) */ + size_t size = buf_rem > data_sz_ ? data_sz_: buf_rem; + memcpy(data_, &buf_[buf_pos], size); + data_pos_ = size; + buf_pos += size; + } else if(data_pos_ < data_sz_) { + /** copy data from buffer (max(data_rem,buf_rem)) */ + size_t data_rem = data_sz_ - data_pos_; + size_t size = buf_rem > data_rem ? data_rem : buf_rem; + memcpy(data_ + data_pos_, &buf_[buf_pos], size); + buf_pos += size; + } + /** if data_sz has been transferred, process JSON object */ + if (data_pos_ == data_sz_) { + /** apply update from JSON chars, deallocate memory and reset pointers */ + applyUpdate(data_); + delete[] data_; + data_pos_ = 0; + data_sz_ = 0; + } } read(); } else if (error.value() == boost::system::errc::no_such_file_or_directory) { - // Socket is closed + /** Socket is closed */ } else { std::cerr << "Error occurred in TCP session " << error << std::endl; } @@ -109,28 +134,28 @@ class JSONTCPSession: public std::enable_shared_from_this { writer.Int(command.Idx()); if (command.AOInt16() != NULL) { writer.String("type"); - writer.String("AnalogInt16"); + writer.String(AI16); writer.String("value"); writer.Int(command.AOInt16()->value); writer.String("status"); writer.Int(static_cast(command.AOInt16()->status)); } else if (command.AOInt32() != NULL) { writer.String("type"); - writer.String("AnalogInt32"); + writer.String(AI32); writer.String("value"); writer.Int(command.AOInt32()->value); writer.String("status"); writer.Int(static_cast(command.AOInt32()->status)); } else if (command.AOFloat32() != NULL) { writer.String("type"); - writer.String("AnalogFloat32"); + writer.String(AF32); writer.String("value"); writer.Double(command.AOFloat32()->value); writer.String("status"); writer.Int(static_cast(command.AOFloat32()->status)); } else if (command.AODouble64() != NULL) { writer.String("type"); - writer.String("AnalogDouble64"); + writer.String(AD64); writer.String("value"); writer.Double(command.AODouble64()->value); writer.String("status"); @@ -138,13 +163,13 @@ class JSONTCPSession: public std::enable_shared_from_this { } else if (command.CROB() != NULL) { // TODO handle all CROB attributes writer.String("type"); - writer.String("ControlRelayOutputBlock"); + writer.String(CROB); writer.String("value"); writer.Int(static_cast(command.CROB()->functionCode)); writer.String("status"); writer.Int(static_cast(command.CROB()->status)); } else { - //TODO exception handle! + std::cerr << "JSONTCPSession: command not recognized" << std::endl; } writer.EndObject(); return sb; @@ -159,32 +184,46 @@ class JSONTCPSession: public std::enable_shared_from_this { d.ParseInsitu(pchar_json_data); if (d.IsObject()) { MeasUpdate update(pOutstation_); - if (d.HasMember("type") && d["type"].IsString() && d.HasMember("index") && d["index"].IsInt()) { - std::cout << d["type"].GetString() << std::endl; - /* TODO FIX ME - * char* type = d["type"].GetString(); - if (type == "AnalogInt16") { - if (d.HasMember("value") && d["value"].IsInt()) { - update.Update(Analog(d["value"].GetInt()), d["index"].GetInt()); - }}*/ + if (d.HasMember("type") && d["type"].IsString() && d.HasMember("index") && d["index"].IsInt() && d.HasMember("value")) { + const char* type = d["type"].GetString(); + if (!strcmp(AD64, type)) { + if (d["value"].IsDouble()) { + update.Update(Analog(d["value"].GetDouble()), d["index"].GetInt()); + } + } else if (!strcmp(BIN, type)) { + if (d["value"].IsBool()) { + update.Update(Binary(d["value"].GetBool()), d["index"].GetInt()); + } + } else if (!strcmp(CI32, type)) { + if (d["value"].IsInt()) { + update.Update(Counter(d["value"].GetInt()), d["index"].GetInt()); + } + } else if (!strcmp(FCI32, type)) { + if (d["value"].IsInt()) { + update.Update(FrozenCounter(d["value"].GetInt()), d["index"].GetInt()); + } + } else { + std::cerr << "JSONTCPSession: type attribute[" << type << "]not recognized" << std::endl; + } + } else { + std::cerr << "JSONTCPSession: object missing type attribute" << std::endl; + } } else { - std::cout << "no type" << std::endl; + std::cerr << "JSONTCPSession: not an object" << std::endl; } - } else { - std::cerr << "not an object!" << std::endl; } -} -enum { - buf_sz_ = 1024 -}; -char buf_[buf_sz_]; + enum { + buf_sz = 1024 + + }; + char buf_[buf_sz]; -char* data_ = NULL; -size_t data_pos_ = 0; -int64_t data_sz_ = 0; + char* data_ = NULL; + size_t data_pos_ = 0; + int64_t data_sz_ = 0; -tcp::socket socket_; -IOutstation* pOutstation_; + tcp::socket socket_; + IOutstation* pOutstation_; } ; diff --git a/cpp/tests/outstation/ao16_test.bin b/cpp/tests/outstation/ao16_test.bin deleted file mode 100644 index 14774f03c558b5cf4594378b40705a2e7ccffb9e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 62 zcmXqGfPiYHlFEWqB`YPzyu_URbkDpJLo+2ErOdpP)Cwgl10AKZ#GF!~yor&HQgKOQ KNog@qv=#v89T7AD diff --git a/cpp/tests/outstation/jsontcp-sample.bin b/cpp/tests/outstation/jsontcp-sample.bin new file mode 100644 index 0000000000000000000000000000000000000000..1f64547417552929cea6101cde5599b4468cd373 GIT binary patch literal 263 zcmcCvfPiYHlFEWqB`YPzyu_URbeH_nq?}YU6D1v`%)FG;3MDH89i_6woKm0?6C*tn zBORsUlEjkIVxUB=F}fK}nR$stmDu!_6qTkTYqdnz>YQJiSCU%fnO9 Date: Tue, 15 Sep 2015 14:39:24 -0500 Subject: [PATCH 18/32] BAT-44 - add proper session tracking by determining if JSONTCPSocket is active --- cpp/outstation/JSONTCPSession.cpp | 11 ++++++++--- cpp/outstation/OutstationJSONTCPServer.cpp | 12 ++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index e5666c7656..f13a9e76ad 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -57,6 +57,10 @@ class JSONTCPSession: public std::enable_shared_from_this { }); } + bool is_active() { + return socket_.is_open(); + } + private: // Type constants (AsyncCommand) static constexpr const char* AI16 = "AnalogInt16"; @@ -74,10 +78,10 @@ class JSONTCPSession: public std::enable_shared_from_this { * Reads incoming JSON preceded by int64_t with number of bytes to read. */ void read() { + // TODO add buffer size vs data size validation and error handling auto self(shared_from_this()); socket_.async_receive(boost::asio::buffer(buf_, buf_sz), [this, self](boost::system::error_code error, size_t length) { - if (!error) { size_t buf_pos = 0; @@ -116,7 +120,9 @@ class JSONTCPSession: public std::enable_shared_from_this { } read(); } else if (error.value() == boost::system::errc::no_such_file_or_directory) { - /** Socket is closed */ + /** read() returned EOF, shutdown entire socket **/ + socket_.shutdown(socket_.shutdown_both); + socket_.close(); } else { std::cerr << "Error occurred in TCP session " << error << std::endl; } @@ -179,7 +185,6 @@ class JSONTCPSession: public std::enable_shared_from_this { * Takes a char[] JSON object and de-serializes to a new MeasUpdate which is applied against pOustation_ */ void applyUpdate(char* pchar_json_data) { - std::cout << "applying update: " << pchar_json_data << std::endl; Document d; d.ParseInsitu(pchar_json_data); if (d.IsObject()) { diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp index 011cc61e35..8f5781f585 100644 --- a/cpp/outstation/OutstationJSONTCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -37,10 +37,14 @@ class OutstationJSONTCPServer { void subscribe() { while (true) { AsyncCommand command = handler_.pop(); - for (std::shared_ptr session : sessions_) { - // TODO remove old sessions - std::cout << session << " | " << session.use_count() << std::endl; - session.get()->write(command); + std::set>::iterator it; + for (it = sessions_.begin(); it != sessions_.end();) { + if (it->get()->is_active()) { + it->get()->write(command); + it++; + } else { + sessions_.erase(it++); + } } } } From 389bf60f6ba7d5a819820b67edd433eebe5688ed Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 15 Sep 2015 14:59:01 -0500 Subject: [PATCH 19/32] BAT-43 - update inline docs --- cpp/outstation/AsyncCommandHandler.cpp | 2 +- cpp/outstation/AsyncCommandHandler.h | 4 ++++ cpp/outstation/JSONTCPSession.cpp | 2 +- cpp/outstation/OutstationApp.cpp | 5 +++++ cpp/outstation/OutstationJSONTCPServer.cpp | 7 +++++++ 5 files changed, 18 insertions(+), 2 deletions(-) diff --git a/cpp/outstation/AsyncCommandHandler.cpp b/cpp/outstation/AsyncCommandHandler.cpp index 0228e63c95..baa9961168 100644 --- a/cpp/outstation/AsyncCommandHandler.cpp +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -64,7 +64,7 @@ CommandStatus AsyncCommandHandler::Operate(const AnalogOutputDouble64& command, /** * Blocks on queue until one or more AsyncCommand elements are available. - * Assumes only one subscriber. + * TODO Handle more than 1 subscriber */ AsyncCommand AsyncCommandHandler::pop() { std::unique_lock lock(queue_mutex_); diff --git a/cpp/outstation/AsyncCommandHandler.h b/cpp/outstation/AsyncCommandHandler.h index d5628fbfd0..ee3bfd20d6 100644 --- a/cpp/outstation/AsyncCommandHandler.h +++ b/cpp/outstation/AsyncCommandHandler.h @@ -24,6 +24,10 @@ using namespace opendnp3; +/** + * Custom command handler for asynchronously receiving AsyncCommands + * through blocking queue operations on a separate thread. + */ class AsyncCommandHandler: public ICommandHandler { public: ~AsyncCommandHandler() override final; diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index f13a9e76ad..3597b6b04e 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -26,7 +26,7 @@ using namespace asiodnp3; using namespace rapidjson; /** - * JSONTCPSession reads/writes JSON objects to socket that represent outstation commands. + * JSONTCPSession reads/writes JSON objects to socket that represent Outstation commands. * Protocol is as follows: [int64_t size][size bytes of JSON] */ class JSONTCPSession: public std::enable_shared_from_this { diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index ef8e6b8070..bd44eef445 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -37,6 +37,11 @@ void signal_handler(int signal_number) { g_signal_cond.notify_all(); } +/** + * Outstation Application providing secondary control channel over TCP (JSON payload). + * + * See OutstationJSONTCPServer for details of secondary control channel. + */ class OutstationApp { public: void start() { diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp index 8f5781f585..ffbcbb018c 100644 --- a/cpp/outstation/OutstationJSONTCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -19,6 +19,13 @@ using namespace asiodnp3; +/** + * OutstationJSONTCPServer handles TCP client sessions over which JSON is transferred. + * + * All client sessions are tracked for broadcast messages from the AsyncCommandHandler (i.e. Master server) + * Clients may send JSON commands which are converted to MeasUpdate by JSONTCPSession. + * + */ class OutstationJSONTCPServer { public: OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, IOutstation* pOutstation, AsyncCommandHandler& handler) : From 38b2737a679e8008ee081e3fabfaf6f956e1a8f2 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 6 Oct 2015 09:40:16 -0500 Subject: [PATCH 20/32] BAT-45 - added outstation app tests - created git submodule for asio includes - created outstation app build directives for cmake - fixed up compiler warnings for outstation app - better pointer handling in outstation app - --- .cproject | 822 +++++++++--------- .gitmodules | 8 + CMakeLists.txt | 45 +- Makefile.am | 316 ------- cpp/outstation/AsyncCommand.cpp | 50 +- cpp/outstation/AsyncCommandHandler.cpp | 28 +- cpp/outstation/AsyncCommandHandler.h | 9 +- cpp/outstation/JSONTCPSession.cpp | 46 +- cpp/outstation/OutstationApp.cpp | 7 +- cpp/outstation/OutstationJSONTCPServer.cpp | 6 +- .../outstation/src/OutstationAppTests.cpp | 37 + thirdparty/asio | 1 + 12 files changed, 590 insertions(+), 785 deletions(-) delete mode 100644 Makefile.am create mode 100644 cpp/tests/outstation/src/OutstationAppTests.cpp create mode 160000 thirdparty/asio diff --git a/.cproject b/.cproject index 2432b63c57..636e313787 100644 --- a/.cproject +++ b/.cproject @@ -47,11 +47,16 @@ + + + + + make - + -j6 all true true @@ -59,7 +64,7 @@ make - + -j6 am--refresh true true @@ -67,7 +72,7 @@ make - + -j6 check true true @@ -75,7 +80,7 @@ make - + -j6 clean true true @@ -83,7 +88,7 @@ make - + -j6 clean-cscope true true @@ -91,7 +96,7 @@ make - + -j6 clean-libLTLIBRARIES true true @@ -99,7 +104,7 @@ make - + -j6 clean-libtool true true @@ -107,7 +112,7 @@ make - + -j6 cpp/examples/master/masterdemo-DemoMain.o true true @@ -115,7 +120,7 @@ make - + -j6 cpp/examples/master/masterdemo-DemoMain.obj true true @@ -123,7 +128,7 @@ make - + -j6 cpp/examples/outstation/outstationdemo-DemoMain.o true true @@ -131,7 +136,7 @@ make - + -j6 cpp/examples/outstation/outstationdemo-DemoMain.obj true true @@ -139,7 +144,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-LinkLayerRouter.lo true true @@ -147,7 +152,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-PhysicalLayerMonitor.lo true true @@ -155,7 +160,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-PhysicalLayerMonitorStates.lo true true @@ -163,7 +168,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-BlockingCommandCallback.lo true true @@ -171,7 +176,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ChangeSet.lo true true @@ -179,7 +184,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ChannelSet.lo true true @@ -187,7 +192,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ConsoleLogger.lo true true @@ -195,7 +200,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DefaultMasterApplication.lo true true @@ -203,7 +208,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DestructorHook.lo true true @@ -211,7 +216,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DNP3Channel.lo true true @@ -219,7 +224,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DNP3Manager.lo true true @@ -227,7 +232,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-HeaderTypes.lo true true @@ -235,7 +240,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MasterStackImpl.lo true true @@ -243,7 +248,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MeasUpdate.lo true true @@ -251,7 +256,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MultidropTaskLock.lo true true @@ -259,7 +264,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-OutstationStackImpl.lo true true @@ -267,7 +272,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-PrintingSOEHandler.lo true true @@ -275,7 +280,7 @@ make - + -j6 cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-StackActionHandler.lo true true @@ -283,7 +288,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-ASIOExecutor.lo true true @@ -291,7 +296,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-ASIOSerialHelpers.lo true true @@ -299,7 +304,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-IOServiceThreadPool.lo true true @@ -307,7 +312,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-LogFanoutHandler.lo true true @@ -315,7 +320,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerBase.lo true true @@ -323,7 +328,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerBaseTCP.lo true true @@ -331,7 +336,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerSerial.lo true true @@ -339,7 +344,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerTCPClient.lo true true @@ -347,7 +352,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerTCPServer.lo true true @@ -355,7 +360,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-SerialTypes.lo true true @@ -363,7 +368,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-TimerASIO.lo true true @@ -371,7 +376,7 @@ make - + -j6 cpp/libs/asiopal/src/asiopal/libasiopal_la-UTCTimeSource.lo true true @@ -379,7 +384,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AnalogCommandEvent.lo true true @@ -387,7 +392,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AnalogOutput.lo true true @@ -395,7 +400,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUBuilders.lo true true @@ -403,7 +408,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHandlerBase.lo true true @@ -411,7 +416,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHeader.lo true true @@ -419,7 +424,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHeaderParser.lo true true @@ -427,7 +432,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDULogging.lo true true @@ -435,7 +440,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUParser.lo true true @@ -443,7 +448,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDURequest.lo true true @@ -451,7 +456,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUResponse.lo true true @@ -459,7 +464,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUWrapper.lo true true @@ -467,7 +472,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AppControlField.lo true true @@ -475,7 +480,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-BinaryCommandEvent.lo true true @@ -483,7 +488,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-BitReader.lo true true @@ -491,7 +496,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ClassField.lo true true @@ -499,7 +504,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ControlRelayOutputBlock.lo true true @@ -507,7 +512,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-EventTriggers.lo true true @@ -515,7 +520,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-FunctionHelpers.lo true true @@ -523,7 +528,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-GroupVariationRecord.lo true true @@ -531,7 +536,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-IINField.lo true true @@ -539,7 +544,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-MeasurementTypes.lo true true @@ -547,7 +552,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ObjectWriter.lo true true @@ -555,7 +560,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-OctetData.lo true true @@ -563,7 +568,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-TimeAndInterval.lo true true @@ -571,7 +576,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-ChannelState.lo true true @@ -579,7 +584,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-CommandStatus.lo true true @@ -587,7 +592,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-ControlCode.lo true true @@ -595,7 +600,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-DoubleBit.lo true true @@ -603,7 +608,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-FunctionCode.lo true true @@ -611,7 +616,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-GroupVariation.lo true true @@ -619,7 +624,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-IntervalUnits.lo true true @@ -627,7 +632,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-LinkFunction.lo true true @@ -635,7 +640,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-PointClass.lo true true @@ -643,7 +648,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-QualifierCode.lo true true @@ -651,7 +656,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-TaskCompletion.lo true true @@ -659,7 +664,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-TimeSyncMode.lo true true @@ -667,7 +672,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-CRC.lo true true @@ -675,7 +680,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-IOpenDelayStrategy.lo true true @@ -683,7 +688,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkFrame.lo true true @@ -691,7 +696,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkHeader.lo true true @@ -699,7 +704,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkLayer.lo true true @@ -707,7 +712,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkLayerParser.lo true true @@ -715,7 +720,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-PriLinkLayerStates.lo true true @@ -723,7 +728,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-SecLinkLayerStates.lo true true @@ -731,7 +736,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-ShiftableBuffer.lo true true @@ -739,7 +744,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-AssignClassTask.lo true true @@ -747,7 +752,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ClearRestartTask.lo true true @@ -755,7 +760,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandMarshaller.lo true true @@ -763,7 +768,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandResponse.lo true true @@ -771,7 +776,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandTask.lo true true @@ -779,7 +784,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-DisableUnsolicitedTask.lo true true @@ -787,7 +792,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-EnableUnsolicitedTask.lo true true @@ -795,7 +800,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-EventScanTask.lo true true @@ -803,7 +808,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-IMasterState.lo true true @@ -811,7 +816,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-IMasterTask.lo true true @@ -819,7 +824,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ISOEHandler.lo true true @@ -827,7 +832,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ITaskLock.lo true true @@ -835,7 +840,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-Master.lo true true @@ -843,7 +848,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterContext.lo true true @@ -851,7 +856,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterParams.lo true true @@ -859,7 +864,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterScan.lo true true @@ -867,7 +872,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterScheduler.lo true true @@ -875,7 +880,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterTasks.lo true true @@ -883,7 +888,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MeasurementHandler.lo true true @@ -891,7 +896,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-PollTaskBase.lo true true @@ -899,7 +904,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-SerialTimeSyncTask.lo true true @@ -907,7 +912,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-StartupIntegrityPoll.lo true true @@ -915,7 +920,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-UserPollTask.lo true true @@ -923,7 +928,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-WriteTask.lo true true @@ -931,7 +936,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group1.lo true true @@ -939,7 +944,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group10.lo true true @@ -947,7 +952,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group11.lo true true @@ -955,7 +960,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group12.lo true true @@ -963,7 +968,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group13.lo true true @@ -971,7 +976,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group2.lo true true @@ -979,7 +984,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group20.lo true true @@ -987,7 +992,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group21.lo true true @@ -995,7 +1000,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group22.lo true true @@ -1003,7 +1008,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group23.lo true true @@ -1011,7 +1016,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group3.lo true true @@ -1019,7 +1024,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group30.lo true true @@ -1027,7 +1032,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group32.lo true true @@ -1035,7 +1040,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group4.lo true true @@ -1043,7 +1048,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group40.lo true true @@ -1051,7 +1056,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group41.lo true true @@ -1059,7 +1064,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group42.lo true true @@ -1067,7 +1072,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group43.lo true true @@ -1075,7 +1080,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group50.lo true true @@ -1083,7 +1088,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group51.lo true true @@ -1091,7 +1096,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group52.lo true true @@ -1099,7 +1104,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ApplicationIIN.lo true true @@ -1107,7 +1112,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-AssignClassHandler.lo true true @@ -1115,7 +1120,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ClassBasedRequestHandler.lo true true @@ -1123,7 +1128,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-CommandActionAdapter.lo true true @@ -1131,7 +1136,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-CommandResponseHandler.lo true true @@ -1139,7 +1144,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-Database.lo true true @@ -1147,7 +1152,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-DatabaseBuffers.lo true true @@ -1155,7 +1160,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-DatabaseConfigView.lo true true @@ -1163,7 +1168,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventBuffer.lo true true @@ -1171,7 +1176,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventBufferConfig.lo true true @@ -1179,7 +1184,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventCount.lo true true @@ -1187,7 +1192,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventWriter.lo true true @@ -1195,7 +1200,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-IINHelpers.lo true true @@ -1203,7 +1208,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-IOutstationApplication.lo true true @@ -1211,7 +1216,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-Outstation.lo true true @@ -1219,7 +1224,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationContext.lo true true @@ -1227,7 +1232,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationParams.lo true true @@ -1235,7 +1240,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationSolicitedStates.lo true true @@ -1243,7 +1248,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationUnsolicitedStates.lo true true @@ -1251,7 +1256,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ReadHandler.lo true true @@ -1259,7 +1264,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ResponseContext.lo true true @@ -1267,7 +1272,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SelectedRanges.lo true true @@ -1275,7 +1280,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SimpleCommandHandler.lo true true @@ -1283,7 +1288,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SOERecord.lo true true @@ -1291,7 +1296,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-StaticBuffers.lo true true @@ -1299,7 +1304,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-StaticLoadFunctions.lo true true @@ -1307,7 +1312,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-WriteHandler.lo true true @@ -1315,7 +1320,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportLayer.lo true true @@ -1323,7 +1328,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportRx.lo true true @@ -1331,7 +1336,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportStack.lo true true @@ -1339,7 +1344,7 @@ make - + -j6 cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportTx.lo true true @@ -1347,7 +1352,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/container/libopenpal_la-DynamicBuffer.lo true true @@ -1355,7 +1360,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/container/libopenpal_la-ReadBufferView.lo true true @@ -1363,7 +1368,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/container/libopenpal_la-WriteBufferView.lo true true @@ -1371,7 +1376,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/executor/libopenpal_la-Action0.lo true true @@ -1379,7 +1384,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/executor/libopenpal_la-Erasure.lo true true @@ -1387,7 +1392,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/executor/libopenpal_la-MonotonicTimestamp.lo true true @@ -1395,7 +1400,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/executor/libopenpal_la-TimeDuration.lo true true @@ -1403,7 +1408,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/logging/libopenpal_la-LogEntry.lo true true @@ -1411,7 +1416,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/logging/libopenpal_la-Logger.lo true true @@ -1419,7 +1424,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/logging/libopenpal_la-LogRoot.lo true true @@ -1427,7 +1432,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/logging/libopenpal_la-StringFormatting.lo true true @@ -1435,7 +1440,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/serialization/libopenpal_la-ByteSerialization.lo true true @@ -1443,7 +1448,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/serialization/libopenpal_la-UInt48LE.lo true true @@ -1451,7 +1456,7 @@ make - + -j6 cpp/libs/openpal/src/openpal/util/libopenpal_la-Limits.lo true true @@ -1459,15 +1464,47 @@ make - + -j6 cpp/libs/openpal/src/openpal/util/libopenpal_la-ToHex.lo true true false + + make + -j6 + cpp/outstation/outstation-AsyncCommandHandler.o + true + true + false + + + make + -j6 + cpp/outstation/outstation-AsyncCommandHandler.obj + true + true + false + + + make + -j6 + cpp/outstation/outstation-OutstationApp.o + true + true + false + + + make + -j6 + cpp/outstation/outstation-OutstationApp.obj + true + true + false + make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-APDUHelpers.o true true @@ -1475,7 +1512,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-APDUHelpers.obj true true @@ -1483,7 +1520,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-APDUHexBuilders.o true true @@ -1491,7 +1528,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-APDUHexBuilders.obj true true @@ -1499,7 +1536,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-BufferHelpers.o true true @@ -1507,7 +1544,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-BufferHelpers.obj true true @@ -1515,7 +1552,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-BufferTestObject.o true true @@ -1523,7 +1560,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-BufferTestObject.obj true true @@ -1531,7 +1568,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-CatchTestStart.o true true @@ -1539,7 +1576,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-CatchTestStart.obj true true @@ -1547,7 +1584,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-CopyableBuffer.o true true @@ -1555,7 +1592,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-CopyableBuffer.obj true true @@ -1563,7 +1600,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-DNPHelpers.o true true @@ -1571,7 +1608,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-DNPHelpers.obj true true @@ -1579,7 +1616,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-HexConversions.o true true @@ -1587,7 +1624,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-HexConversions.obj true true @@ -1595,7 +1632,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LinkLayerRouterTest.o true true @@ -1603,7 +1640,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LinkLayerRouterTest.obj true true @@ -1611,7 +1648,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LinkLayerTest.o true true @@ -1619,7 +1656,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LinkLayerTest.obj true true @@ -1627,7 +1664,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LogTester.o true true @@ -1635,7 +1672,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LogTester.obj true true @@ -1643,7 +1680,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LoopbackPhysicalLayer.o true true @@ -1651,7 +1688,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LoopbackPhysicalLayer.obj true true @@ -1659,7 +1696,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LowerLayerToPhysAdapter.o true true @@ -1667,7 +1704,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-LowerLayerToPhysAdapter.obj true true @@ -1675,7 +1712,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MasterTestObject.o true true @@ -1683,7 +1720,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MasterTestObject.obj true true @@ -1691,7 +1728,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockExecutor.o true true @@ -1699,7 +1736,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockExecutor.obj true true @@ -1707,7 +1744,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockFrameSink.o true true @@ -1715,7 +1752,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockFrameSink.obj true true @@ -1723,7 +1760,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockLowerLayer.o true true @@ -1731,7 +1768,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockLowerLayer.obj true true @@ -1739,7 +1776,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayer.o true true @@ -1747,7 +1784,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayer.obj true true @@ -1755,7 +1792,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayerMonitor.o true true @@ -1763,7 +1800,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayerMonitor.obj true true @@ -1771,7 +1808,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockTransportLayer.o true true @@ -1779,7 +1816,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockTransportLayer.obj true true @@ -1787,7 +1824,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockUpperLayer.o true true @@ -1795,7 +1832,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-MockUpperLayer.obj true true @@ -1803,7 +1840,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-OutstationTestObject.o true true @@ -1811,7 +1848,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-OutstationTestObject.obj true true @@ -1819,7 +1856,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-PhysBaseTest.o true true @@ -1827,7 +1864,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-PhysBaseTest.obj true true @@ -1835,7 +1872,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-PhysLoopback.o true true @@ -1843,7 +1880,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-PhysLoopback.obj true true @@ -1851,7 +1888,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-PhysTestObject.o true true @@ -1859,7 +1896,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-PhysTestObject.obj true true @@ -1867,7 +1904,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-ProtocolUtil.o true true @@ -1875,7 +1912,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-ProtocolUtil.obj true true @@ -1883,7 +1920,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-RandomizedBuffer.o true true @@ -1891,7 +1928,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-RandomizedBuffer.obj true true @@ -1899,7 +1936,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-SerialTestObject.o true true @@ -1907,7 +1944,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-SerialTestObject.obj true true @@ -1915,7 +1952,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-StopWatch.o true true @@ -1923,7 +1960,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-StopWatch.obj true true @@ -1931,7 +1968,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestAPDUParsing.o true true @@ -1939,7 +1976,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestAPDUParsing.obj true true @@ -1947,7 +1984,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestAPDUWriting.o true true @@ -1955,7 +1992,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestAPDUWriting.obj true true @@ -1963,7 +2000,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestASIO.o true true @@ -1971,7 +2008,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestASIO.obj true true @@ -1979,7 +2016,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestASIOThreadPool.o true true @@ -1987,7 +2024,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestASIOThreadPool.obj true true @@ -1995,7 +2032,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestCastLongLongDouble.o true true @@ -2003,7 +2040,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestCastLongLongDouble.obj true true @@ -2011,7 +2048,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestCRC.o true true @@ -2019,7 +2056,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestCRC.obj true true @@ -2027,7 +2064,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestDatabase.o true true @@ -2035,7 +2072,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestDatabase.obj true true @@ -2043,7 +2080,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestDNP3Manager.o true true @@ -2051,7 +2088,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestDNP3Manager.obj true true @@ -2059,7 +2096,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestIndexSearch.o true true @@ -2067,7 +2104,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestIndexSearch.obj true true @@ -2075,7 +2112,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLazyCollection.o true true @@ -2083,7 +2120,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLazyCollection.obj true true @@ -2091,7 +2128,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkFrame.o true true @@ -2099,7 +2136,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkFrame.obj true true @@ -2107,7 +2144,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayer.o true true @@ -2115,7 +2152,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayer.obj true true @@ -2123,7 +2160,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayerRouter.o true true @@ -2131,7 +2168,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayerRouter.obj true true @@ -2139,7 +2176,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkReceiver.o true true @@ -2147,7 +2184,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkReceiver.obj true true @@ -2155,7 +2192,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkRoute.o true true @@ -2163,7 +2200,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLinkRoute.obj true true @@ -2171,7 +2208,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLog.o true true @@ -2179,7 +2216,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestLog.obj true true @@ -2187,7 +2224,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMaster.o true true @@ -2195,7 +2232,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMaster.obj true true @@ -2203,7 +2240,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterAssignClass.o true true @@ -2211,7 +2248,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterAssignClass.obj true true @@ -2219,7 +2256,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterCommandRequests.o true true @@ -2227,7 +2264,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterCommandRequests.obj true true @@ -2235,7 +2272,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterMultidrop.o true true @@ -2243,7 +2280,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterMultidrop.obj true true @@ -2251,7 +2288,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterUnsolBehaviors.o true true @@ -2259,7 +2296,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestMasterUnsolBehaviors.obj true true @@ -2267,7 +2304,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestObject.o true true @@ -2275,7 +2312,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestObject.obj true true @@ -2283,7 +2320,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestObjectASIO.o true true @@ -2291,7 +2328,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestObjectASIO.obj true true @@ -2299,7 +2336,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstation.o true true @@ -2307,7 +2344,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstation.obj true true @@ -2315,7 +2352,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationAssignClass.o true true @@ -2323,7 +2360,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationAssignClass.obj true true @@ -2331,7 +2368,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationCommandResponses.o true true @@ -2339,7 +2376,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationCommandResponses.obj true true @@ -2347,7 +2384,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationEventResponses.o true true @@ -2355,7 +2392,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationEventResponses.obj true true @@ -2363,7 +2400,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationUnsolicitedResponses.o true true @@ -2371,7 +2408,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestOutstationUnsolicitedResponses.obj true true @@ -2379,7 +2416,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncBase.o true true @@ -2387,7 +2424,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncBase.obj true true @@ -2395,7 +2432,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncSerial.o true true @@ -2403,7 +2440,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncSerial.obj true true @@ -2411,7 +2448,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncTCP.o true true @@ -2419,7 +2456,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncTCP.obj true true @@ -2427,7 +2464,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerLoopback.o true true @@ -2435,7 +2472,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerLoopback.obj true true @@ -2443,7 +2480,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerMonitor.o true true @@ -2451,7 +2488,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerMonitor.obj true true @@ -2459,7 +2496,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestSerialization.o true true @@ -2467,7 +2504,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestSerialization.obj true true @@ -2475,7 +2512,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestShiftableBuffer.o true true @@ -2483,7 +2520,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestShiftableBuffer.obj true true @@ -2491,7 +2528,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTime.o true true @@ -2499,7 +2536,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTime.obj true true @@ -2507,7 +2544,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTimers.o true true @@ -2515,7 +2552,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTimers.obj true true @@ -2523,7 +2560,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTransportLayer.o true true @@ -2531,7 +2568,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTransportLayer.obj true true @@ -2539,7 +2576,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTransportLoopback.o true true @@ -2547,7 +2584,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTransportLoopback.obj true true @@ -2555,7 +2592,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTypes.o true true @@ -2563,7 +2600,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestTypes.obj true true @@ -2571,7 +2608,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestUtil.o true true @@ -2579,7 +2616,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestUtil.obj true true @@ -2587,7 +2624,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestWriteConversions.o true true @@ -2595,7 +2632,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TestWriteConversions.obj true true @@ -2603,7 +2640,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-Timeout.o true true @@ -2611,7 +2648,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-Timeout.obj true true @@ -2619,7 +2656,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportIntegrationStack.o true true @@ -2627,7 +2664,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportIntegrationStack.obj true true @@ -2635,7 +2672,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportLoopbackTestObject.o true true @@ -2643,7 +2680,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportLoopbackTestObject.obj true true @@ -2651,7 +2688,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportScalabilityTestObject.o true true @@ -2659,7 +2696,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportScalabilityTestObject.obj true true @@ -2667,7 +2704,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportStackPair.o true true @@ -2675,7 +2712,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportStackPair.obj true true @@ -2683,7 +2720,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportTestObject.o true true @@ -2691,7 +2728,7 @@ make - + -j6 cpp/tests/opendnp3tests/src/dnp3test-TransportTestObject.obj true true @@ -2699,7 +2736,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-CatchTestStart.o true true @@ -2707,7 +2744,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-CatchTestStart.obj true true @@ -2715,7 +2752,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-FunctionalTestSuite.o true true @@ -2723,7 +2760,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-FunctionalTestSuite.obj true true @@ -2731,7 +2768,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-LinkedListTestSuite.o true true @@ -2739,7 +2776,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-LinkedListTestSuite.obj true true @@ -2747,7 +2784,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-ManagedPointerTestSuite.o true true @@ -2755,7 +2792,7 @@ make - + -j6 cpp/tests/openpaltests/src/openpaltest-ManagedPointerTestSuite.obj true true @@ -2763,7 +2800,7 @@ make - + -j6 cscope true true @@ -2771,7 +2808,7 @@ make - + -j6 cscope.files true true @@ -2779,7 +2816,7 @@ make - + -j6 cscopelist true true @@ -2787,7 +2824,7 @@ make - + -j6 ctags true true @@ -2795,7 +2832,7 @@ make - + -j6 dist true true @@ -2803,7 +2840,7 @@ make - + -j6 dist-all true true @@ -2811,7 +2848,7 @@ make - + -j6 dist-bzip2 true true @@ -2819,7 +2856,7 @@ make - + -j6 dist-gzip true true @@ -2827,7 +2864,7 @@ make - + -j6 dist-lzip true true @@ -2835,7 +2872,7 @@ make - + -j6 dist-shar true true @@ -2843,7 +2880,7 @@ make - + -j6 dist-tarZ true true @@ -2851,7 +2888,7 @@ make - + -j6 dist-xz true true @@ -2859,7 +2896,7 @@ make - + -j6 dist-zip true true @@ -2867,7 +2904,7 @@ make - + -j6 distcheck true true @@ -2875,7 +2912,7 @@ make - + -j6 distclean true true @@ -2883,7 +2920,7 @@ make - + -j6 distclean-compile true true @@ -2891,7 +2928,7 @@ make - + -j6 distclean-libtool true true @@ -2899,7 +2936,7 @@ make - + -j6 distclean-tags true true @@ -2907,7 +2944,7 @@ make - + -j6 distcleancheck true true @@ -2915,7 +2952,7 @@ make - + -j6 distdir true true @@ -2923,7 +2960,7 @@ make - + -j6 distuninstallcheck true true @@ -2931,7 +2968,7 @@ make - + -j6 dvi true true @@ -2939,7 +2976,7 @@ make - + -j6 html true true @@ -2947,7 +2984,7 @@ make - + -j6 info true true @@ -2955,7 +2992,7 @@ make - + -j6 install true true @@ -2963,7 +3000,7 @@ make - + -j6 install-data true true @@ -2971,7 +3008,7 @@ make - + -j6 install-dvi true true @@ -2979,7 +3016,7 @@ make - + -j6 install-exec true true @@ -2987,7 +3024,7 @@ make - + -j6 install-html true true @@ -2995,7 +3032,7 @@ make - + -j6 install-info true true @@ -3003,7 +3040,7 @@ make - + -j6 install-libLTLIBRARIES true true @@ -3011,7 +3048,7 @@ make - + -j6 install-man true true @@ -3019,7 +3056,7 @@ make - + -j6 install-pdf true true @@ -3027,7 +3064,7 @@ make - + -j6 install-ps true true @@ -3035,7 +3072,7 @@ make - + -j6 install-strip true true @@ -3043,7 +3080,7 @@ make - + -j6 installcheck true true @@ -3051,7 +3088,7 @@ make - + -j6 installdirs true true @@ -3059,7 +3096,7 @@ make - + -j6 libasiodnp3.la true true @@ -3067,7 +3104,7 @@ make - + -j6 libasiopal.la true true @@ -3075,7 +3112,7 @@ make - + -j6 libopendnp3.la true true @@ -3083,7 +3120,7 @@ make - + -j6 libopenpal.la true true @@ -3091,7 +3128,7 @@ make - + -j6 maintainer-clean true true @@ -3099,7 +3136,7 @@ make - + -j6 Makefile true true @@ -3107,7 +3144,7 @@ make - + -j6 mostlyclean true true @@ -3115,7 +3152,7 @@ make - + -j6 mostlyclean-compile true true @@ -3123,7 +3160,7 @@ make - + -j6 mostlyclean-libtool true true @@ -3131,7 +3168,7 @@ make - + -j6 pdf true true @@ -3139,7 +3176,7 @@ make - + -j6 ps true true @@ -3147,7 +3184,7 @@ make - + -j6 tags true true @@ -3155,7 +3192,7 @@ make - + -j6 uninstall true true @@ -3163,7 +3200,7 @@ make - + -j6 uninstall-libLTLIBRARIES true true @@ -3171,9 +3208,4 @@ - - - - - diff --git a/.gitmodules b/.gitmodules index e69de29bb2..72f81b2512 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,8 @@ +[submodule "thirdparty/asio"] + path = thirdparty/asio + url = ../asio/ + branch = develop +[submodule "thirdparty/rapidjson"] + path = thirdparty/rapidjson + url = ../rapidjson/ + branch = develop diff --git a/CMakeLists.txt b/CMakeLists.txt index d18fe63d1a..47cb83b78c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ set(OPENDNP3_MICRO_VERSION 0) set(OPENDNP3_VERSION ${OPENDNP3_MAJOR_VERSION}.${OPENDNP3_MINOR_VERSION}.${OPENDNP3_MICRO_VERSION}) # various optional libraries and projects +option(OUTSTATION "Build outstation application" OFF) option(DEMO "Build demo applications" OFF) option(TEST "Build tests" OFF) option(FULL "Build all optional projects (secauth, demos, tests)" OFF) @@ -18,6 +19,7 @@ option(STATICLIBS "Builds static versions of all installed libraries" OFF) option(COVERAGE "Builds the libraries with coverage info for gcov" OFF) if(FULL) + set(OUTSTATION ON) set(DEMO ON) set(TEST ON) set(SECAUTH ON) @@ -43,6 +45,20 @@ if(SECAUTH OR DNP3_TLS) endif() +if(OUTSTATION) + set(Boost_USE_STATIC_LIBS OFF) + set(Boost_USE_MULTITHREADED ON) + set(Boost_USE_STATIC_RUNTIME OFF) + find_package(Boost 1.54.0 COMPONENTS system thread) + + if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + #add_executable(progname file1.cxx file2.cxx) + #target_link_libraries(progname ${Boost_LIBRARIES}) +endif() + include_directories(./thirdparty/rapidjson/include) +endif() + if(WIN32) set_property(GLOBAL PROPERTY USE_FOLDERS ON) #allows the creation of solution folders @@ -93,17 +109,8 @@ endif() set(CMAKE_REQUIRED_FLAGS ${CMAKE_CXX_FLAGS}) -if (ASIO_HOME) - message("ASIO_HOME defined in cache: ${ASIO_HOME}") - include_directories(${ASIO_HOME}) -else() - if(DEFINED ENV{ASIO_HOME}) - message("ASIO_HOME defined in environment: $ENV{ASIO_HOME}") - include_directories($ENV{ASIO_HOME}) - else() - message("ASIO_HOME was not defined. ASIO expected to be on include path") - endif() -endif() +# include path for asio submodule +include_directories(./thirdparty/asio/asio/include) # include paths for all the local libraries include_directories(./cpp/libs/src) @@ -184,6 +191,18 @@ endif() set(INSTALL_ARGS FILES_MATCHING PATTERN "*.h" PATTERN ".deps" EXCLUDE PATTERN ".libs" EXCLUDE) install(DIRECTORY ./cpp/libs/include/ DESTINATION include ${INSTALL_ARGS}) +if(OUTSTATION) + + # ----- outstation executable ----- + add_executable(outstation ./cpp/outstation/OutstationApp.cpp) + target_link_libraries (outstation LINK_PUBLIC asiodnp3 ${PTHREAD} ${Boost_LIBRARIES}) +# target_link_libraries (outstation LINK_PUBLIC asiodnp3 ${PTHREAD}) +# target_link_libraries(outstation ${Boost_LIBRARIES}) + +# set_target_properties(outstation PROPERTIES FOLDER demos) + +endif() + if(DEMO) # ----- master demo executable ----- @@ -279,6 +298,10 @@ if(TEST) set_target_properties(testosslcrypto PROPERTIES FOLDER tests) endif() + + # ----- outstation app tests ----- + file(GLOB_RECURSE outstation_TESTSRC ./cpp/tests/outstation/src/*.cpp ./cpp/tests/outstation/src/*.h) + add_executable (testoutstation ${outstation_TESTSRC}) endif() diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index 0c8495004b..0000000000 --- a/Makefile.am +++ /dev/null @@ -1,316 +0,0 @@ -#copyright (c) 2013 Automatak, LLC - -ACLOCAL_AMFLAGS = -I m4 - -ASIO_CONFIG = -I $(ASIO_HOME) -DASIO_STANDALONE - -OPENPAL_INCLUDE = $(top_srcdir)/cpp/libs/openpal/src -OPENDNP3_INCLUDE = $(top_srcdir)/cpp/libs/opendnp3/src -ASIOPAL_INCLUDE = $(top_srcdir)/cpp/libs/asiopal/src -ASIODNP3_INCLUDE = $(top_srcdir)/cpp/libs/asiodnp3/src -CATCH_INCLUDE = $(top_srcdir)/cpp/tests/catch -RAPIDJSON_INCLUDE = $(top_srcdir)/thirdparty/rapidjson/include - -bin_PROGRAMS = masterdemo outstationdemo outstation - -lib_LTLIBRARIES = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la - -# ls cpp/libs/openpal/src/openpal/**/*.cpp -1 | awk '{print $0" \\"}' -libopenpal_la_LDFLAGS = -version-info 1:1:0 -libopenpal_la_CXXFLAGS = -I$(OPENPAL_INCLUDE) -libopenpal_la_SOURCES = \ -cpp/libs/openpal/src/openpal/container/DynamicBuffer.cpp \ -cpp/libs/openpal/src/openpal/container/ReadBufferView.cpp \ -cpp/libs/openpal/src/openpal/container/WriteBufferView.cpp \ -cpp/libs/openpal/src/openpal/executor/Action0.cpp \ -cpp/libs/openpal/src/openpal/executor/Erasure.cpp \ -cpp/libs/openpal/src/openpal/executor/MonotonicTimestamp.cpp \ -cpp/libs/openpal/src/openpal/executor/TimeDuration.cpp \ -cpp/libs/openpal/src/openpal/logging/LogEntry.cpp \ -cpp/libs/openpal/src/openpal/logging/Logger.cpp \ -cpp/libs/openpal/src/openpal/logging/LogRoot.cpp \ -cpp/libs/openpal/src/openpal/logging/StringFormatting.cpp \ -cpp/libs/openpal/src/openpal/serialization/ByteSerialization.cpp \ -cpp/libs/openpal/src/openpal/serialization/UInt48LE.cpp \ -cpp/libs/openpal/src/openpal/util/Limits.cpp \ -cpp/libs/openpal/src/openpal/util/ToHex.cpp - -#ls cpp/libs/asiopal/src/asiopal/*.cpp -1 | awk '{print $0" \\"}' -libasiopal_la_LDFLAGS = -version-info 1:1:0 -libasiopal_la_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) $(ASIO_CONFIG) -libasiopal_la_SOURCES = \ -cpp/libs/asiopal/src/asiopal/ASIOExecutor.cpp \ -cpp/libs/asiopal/src/asiopal/ASIOSerialHelpers.cpp \ -cpp/libs/asiopal/src/asiopal/IOServiceThreadPool.cpp \ -cpp/libs/asiopal/src/asiopal/LogFanoutHandler.cpp \ -cpp/libs/asiopal/src/asiopal/PhysicalLayerBase.cpp \ -cpp/libs/asiopal/src/asiopal/PhysicalLayerBaseTCP.cpp \ -cpp/libs/asiopal/src/asiopal/PhysicalLayerSerial.cpp \ -cpp/libs/asiopal/src/asiopal/PhysicalLayerTCPClient.cpp \ -cpp/libs/asiopal/src/asiopal/PhysicalLayerTCPServer.cpp \ -cpp/libs/asiopal/src/asiopal/SerialTypes.cpp \ -cpp/libs/asiopal/src/asiopal/TimerASIO.cpp \ -cpp/libs/asiopal/src/asiopal/UTCTimeSource.cpp - -# ls cpp/libs/opendnp3/src/opendnp3/**/*.cpp -1 | awk '{print $0" \\"}' -libopendnp3_la_LDFLAGS = -version-info 2:0:0 -libopendnp3_la_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -libopendnp3_la_SOURCES = \ -cpp/libs/opendnp3/src/opendnp3/app/AnalogCommandEvent.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/AnalogOutput.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDUBuilders.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDUHandlerBase.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDUHeader.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDUHeaderParser.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDULogging.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDUParser.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDURequest.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDUResponse.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/APDUWrapper.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/AppControlField.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/BinaryCommandEvent.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/BitReader.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/ClassField.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/ControlRelayOutputBlock.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/EventTriggers.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/FunctionHelpers.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/GroupVariationRecord.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/IINField.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/MeasurementTypes.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/ObjectWriter.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/OctetData.cpp \ -cpp/libs/opendnp3/src/opendnp3/app/TimeAndInterval.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/ChannelState.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/CommandStatus.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/ControlCode.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/DoubleBit.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/FunctionCode.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/GroupVariation.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/IntervalUnits.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/LinkFunction.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/PointClass.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/QualifierCode.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/TaskCompletion.cpp \ -cpp/libs/opendnp3/src/opendnp3/gen/TimeSyncMode.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/CRC.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/IOpenDelayStrategy.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/LinkFrame.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/LinkHeader.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/LinkLayer.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/LinkLayerParser.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/PriLinkLayerStates.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/SecLinkLayerStates.cpp \ -cpp/libs/opendnp3/src/opendnp3/link/ShiftableBuffer.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/AssignClassTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/ClearRestartTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/CommandMarshaller.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/CommandResponse.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/CommandTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/DisableUnsolicitedTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/EnableUnsolicitedTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/EventScanTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/IMasterState.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/IMasterTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/ISOEHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/ITaskLock.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/MasterContext.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/Master.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/MasterParams.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/MasterScan.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/MasterScheduler.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/MasterTasks.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/MeasurementHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/PollTaskBase.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/SerialTimeSyncTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/StartupIntegrityPoll.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/UserPollTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/master/WriteTask.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group10.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group11.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group12.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group13.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group1.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group20.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group21.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group22.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group23.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group2.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group30.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group32.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group3.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group40.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group41.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group42.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group43.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group4.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group50.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group51.cpp \ -cpp/libs/opendnp3/src/opendnp3/objects/Group52.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/ApplicationIIN.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/AssignClassHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/ClassBasedRequestHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/CommandActionAdapter.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/CommandResponseHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/DatabaseBuffers.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/DatabaseConfigView.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/Database.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/EventBufferConfig.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/EventBuffer.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/EventCount.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/EventWriter.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/IINHelpers.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/IOutstationApplication.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/OutstationContext.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/Outstation.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/OutstationParams.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/OutstationSolicitedStates.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/OutstationUnsolicitedStates.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/ReadHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/ResponseContext.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/SelectedRanges.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/SimpleCommandHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/SOERecord.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/StaticBuffers.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/StaticLoadFunctions.cpp \ -cpp/libs/opendnp3/src/opendnp3/outstation/WriteHandler.cpp \ -cpp/libs/opendnp3/src/opendnp3/transport/TransportLayer.cpp \ -cpp/libs/opendnp3/src/opendnp3/transport/TransportRx.cpp \ -cpp/libs/opendnp3/src/opendnp3/transport/TransportStack.cpp \ -cpp/libs/opendnp3/src/opendnp3/transport/TransportTx.cpp - -#ls cpp/libs/asiodnp3/src/asiodnp3/*.cpp -1 | awk '{print $0" \\"}' -libasiodnp3_la_CPPFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) $(ASIO_CONFIG) -libasiodnp3_la_LDFLAGS = -version-info 1:1:0 -libasiodnp3_la_SOURCES = \ -cpp/libs/asiodnp3/src/asiodnp3/impl/LinkLayerRouter.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/impl/PhysicalLayerMonitor.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/impl/PhysicalLayerMonitorStates.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/BlockingCommandCallback.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/ChangeSet.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/ChannelSet.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/ConsoleLogger.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/DefaultMasterApplication.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/DestructorHook.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/DNP3Channel.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/DNP3Manager.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/HeaderTypes.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/MeasUpdate.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/MasterStackImpl.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/MultidropTaskLock.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/OutstationStackImpl.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/PrintingSOEHandler.cpp \ -cpp/libs/asiodnp3/src/asiodnp3/StackActionHandler.cpp - - -masterdemo_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed -masterdemo_LDFLAGS = -pthread -masterdemo_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la -masterdemo_SOURCES = cpp/examples/master/DemoMain.cpp - -outstationdemo_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed -outstationdemo_LDFLAGS = -pthread -outstationdemo_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la -outstationdemo_SOURCES = cpp/examples/outstation/DemoMain.cpp - -outstation_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) -I$(RAPIDJSON_INCLUDE) $(ASIO_CONFIG) -Wl,--no-as-needed -outstation_LDFLAGS = -pthread -lboost_system -lboost_thread -outstation_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la -outstation_SOURCES = \ -cpp/outstation/AsyncCommandHandler.cpp \ -cpp/outstation/OutstationApp.cpp - -check_PROGRAMS = openpaltest dnp3test -#TESTS = openpaltest dnp3test - -#ls cpp/tests/openpaltests/src/*.cpp -1 | awk '{print $0" \\"}' -openpaltest_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(CATCH_INCLUDE) -openpaltest_LDFLAGS = -pthread -openpaltest_LDADD = libopenpal.la -openpaltest_SOURCES = \ -cpp/tests/openpaltests/src/CatchTestStart.cpp \ -cpp/tests/openpaltests/src/FunctionalTestSuite.cpp \ -cpp/tests/openpaltests/src/LinkedListTestSuite.cpp \ -cpp/tests/openpaltests/src/ManagedPointerTestSuite.cpp - -#ls cpp/tests/opendnp3tests/src/*.cpp -1 | awk '{print $0" \\"}' -dnp3test_CXXFLAGS = -I$(OPENPAL_INCLUDE) -I$(ASIOPAL_INCLUDE) -I $(OPENDNP3_INCLUDE) -I$(ASIODNP3_INCLUDE) -I$(CATCH_INCLUDE) $(ASIO_CONFIG) -dnp3test_LDFLAGS = -pthread -dnp3test_LDADD = libopenpal.la libasiopal.la libopendnp3.la libasiodnp3.la -dnp3test_SOURCES = \ -cpp/tests/opendnp3tests/src/APDUHelpers.cpp \ -cpp/tests/opendnp3tests/src/APDUHexBuilders.cpp \ -cpp/tests/opendnp3tests/src/BufferHelpers.cpp \ -cpp/tests/opendnp3tests/src/BufferTestObject.cpp \ -cpp/tests/opendnp3tests/src/CatchTestStart.cpp \ -cpp/tests/opendnp3tests/src/CopyableBuffer.cpp \ -cpp/tests/opendnp3tests/src/DNPHelpers.cpp \ -cpp/tests/opendnp3tests/src/HexConversions.cpp \ -cpp/tests/opendnp3tests/src/LinkLayerRouterTest.cpp \ -cpp/tests/opendnp3tests/src/LinkLayerTest.cpp \ -cpp/tests/opendnp3tests/src/LogTester.cpp \ -cpp/tests/opendnp3tests/src/LoopbackPhysicalLayer.cpp \ -cpp/tests/opendnp3tests/src/LowerLayerToPhysAdapter.cpp \ -cpp/tests/opendnp3tests/src/MasterTestObject.cpp \ -cpp/tests/opendnp3tests/src/MockExecutor.cpp \ -cpp/tests/opendnp3tests/src/MockFrameSink.cpp \ -cpp/tests/opendnp3tests/src/MockLowerLayer.cpp \ -cpp/tests/opendnp3tests/src/MockPhysicalLayer.cpp \ -cpp/tests/opendnp3tests/src/MockPhysicalLayerMonitor.cpp \ -cpp/tests/opendnp3tests/src/MockTransportLayer.cpp \ -cpp/tests/opendnp3tests/src/MockUpperLayer.cpp \ -cpp/tests/opendnp3tests/src/OutstationTestObject.cpp \ -cpp/tests/opendnp3tests/src/PhysBaseTest.cpp \ -cpp/tests/opendnp3tests/src/PhysLoopback.cpp \ -cpp/tests/opendnp3tests/src/PhysTestObject.cpp \ -cpp/tests/opendnp3tests/src/ProtocolUtil.cpp \ -cpp/tests/opendnp3tests/src/RandomizedBuffer.cpp \ -cpp/tests/opendnp3tests/src/SerialTestObject.cpp \ -cpp/tests/opendnp3tests/src/StopWatch.cpp \ -cpp/tests/opendnp3tests/src/TestAPDUParsing.cpp \ -cpp/tests/opendnp3tests/src/TestAPDUWriting.cpp \ -cpp/tests/opendnp3tests/src/TestASIO.cpp \ -cpp/tests/opendnp3tests/src/TestASIOThreadPool.cpp \ -cpp/tests/opendnp3tests/src/TestCastLongLongDouble.cpp \ -cpp/tests/opendnp3tests/src/TestCRC.cpp \ -cpp/tests/opendnp3tests/src/TestDatabase.cpp \ -cpp/tests/opendnp3tests/src/TestDNP3Manager.cpp \ -cpp/tests/opendnp3tests/src/TestIndexSearch.cpp \ -cpp/tests/opendnp3tests/src/TestLazyCollection.cpp \ -cpp/tests/opendnp3tests/src/TestLinkFrame.cpp \ -cpp/tests/opendnp3tests/src/TestLinkLayer.cpp \ -cpp/tests/opendnp3tests/src/TestLinkLayerRouter.cpp \ -cpp/tests/opendnp3tests/src/TestLinkReceiver.cpp \ -cpp/tests/opendnp3tests/src/TestLinkRoute.cpp \ -cpp/tests/opendnp3tests/src/TestLog.cpp \ -cpp/tests/opendnp3tests/src/TestMasterAssignClass.cpp \ -cpp/tests/opendnp3tests/src/TestMasterCommandRequests.cpp \ -cpp/tests/opendnp3tests/src/TestMaster.cpp \ -cpp/tests/opendnp3tests/src/TestMasterMultidrop.cpp \ -cpp/tests/opendnp3tests/src/TestMasterUnsolBehaviors.cpp \ -cpp/tests/opendnp3tests/src/TestObjectASIO.cpp \ -cpp/tests/opendnp3tests/src/TestObject.cpp \ -cpp/tests/opendnp3tests/src/TestOutstationAssignClass.cpp \ -cpp/tests/opendnp3tests/src/TestOutstationCommandResponses.cpp \ -cpp/tests/opendnp3tests/src/TestOutstation.cpp \ -cpp/tests/opendnp3tests/src/TestOutstationEventResponses.cpp \ -cpp/tests/opendnp3tests/src/TestOutstationUnsolicitedResponses.cpp \ -cpp/tests/opendnp3tests/src/TestPhysicalLayerAsyncBase.cpp \ -cpp/tests/opendnp3tests/src/TestPhysicalLayerAsyncSerial.cpp \ -cpp/tests/opendnp3tests/src/TestPhysicalLayerAsyncTCP.cpp \ -cpp/tests/opendnp3tests/src/TestPhysicalLayerLoopback.cpp \ -cpp/tests/opendnp3tests/src/TestPhysicalLayerMonitor.cpp \ -cpp/tests/opendnp3tests/src/TestSerialization.cpp \ -cpp/tests/opendnp3tests/src/TestShiftableBuffer.cpp \ -cpp/tests/opendnp3tests/src/TestTime.cpp \ -cpp/tests/opendnp3tests/src/TestTimers.cpp \ -cpp/tests/opendnp3tests/src/TestTransportLayer.cpp \ -cpp/tests/opendnp3tests/src/TestTransportLoopback.cpp \ -cpp/tests/opendnp3tests/src/TestTypes.cpp \ -cpp/tests/opendnp3tests/src/TestUtil.cpp \ -cpp/tests/opendnp3tests/src/TestWriteConversions.cpp \ -cpp/tests/opendnp3tests/src/Timeout.cpp \ -cpp/tests/opendnp3tests/src/TransportIntegrationStack.cpp \ -cpp/tests/opendnp3tests/src/TransportLoopbackTestObject.cpp \ -cpp/tests/opendnp3tests/src/TransportScalabilityTestObject.cpp \ -cpp/tests/opendnp3tests/src/TransportStackPair.cpp \ -cpp/tests/opendnp3tests/src/TransportTestObject.cpp diff --git a/cpp/outstation/AsyncCommand.cpp b/cpp/outstation/AsyncCommand.cpp index 590f2066f9..1353cba869 100644 --- a/cpp/outstation/AsyncCommand.cpp +++ b/cpp/outstation/AsyncCommand.cpp @@ -12,53 +12,57 @@ * limitations under the License. */ +#include + using namespace opendnp3; class AsyncCommand { public: - /* AsyncCommand() : - idx_(0), crob_(NULL), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(NULL) { - }*/ - AsyncCommand(ControlRelayOutputBlock crob, uint16_t idx) : - idx_(idx), crob_(&crob), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(NULL) { + AsyncCommand(const ControlRelayOutputBlock* crob, uint16_t idx) : + idx_(idx), crob_(crob), aoInt16_(), aoInt32_(), aoFloat32_(), aoDouble64_() { } - AsyncCommand(AnalogOutputInt16 aoInt16, uint16_t idx) : - idx_(idx), crob_(NULL), aoInt16_(&aoInt16), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(NULL) { + AsyncCommand(const AnalogOutputInt16* aoInt16, uint16_t idx) : + idx_(idx), crob_(), aoInt16_(aoInt16), aoInt32_(), aoFloat32_(), aoDouble64_() { } - AsyncCommand(AnalogOutputInt32 aoInt32, uint16_t idx) : - idx_(idx), crob_(NULL), aoInt16_(NULL), aoInt32_(&aoInt32), aoFloat32_(NULL), aoDouble64_(NULL) { + AsyncCommand(const AnalogOutputInt32* aoInt32, uint16_t idx) : + idx_(idx), crob_(), aoInt16_(), aoInt32_(aoInt32), aoFloat32_(), aoDouble64_() { } - AsyncCommand(AnalogOutputFloat32 aoFloat32, uint16_t idx) : - idx_(idx), crob_(NULL), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(&aoFloat32), aoDouble64_(NULL) { + AsyncCommand(const AnalogOutputFloat32* aoFloat32, uint16_t idx) : + idx_(idx), crob_(), aoInt16_(), aoInt32_(), aoFloat32_(aoFloat32), aoDouble64_() { } - AsyncCommand(AnalogOutputDouble64 aoDouble64, uint16_t idx) : - idx_(idx), crob_(NULL), aoInt16_(NULL), aoInt32_(NULL), aoFloat32_(NULL), aoDouble64_(&aoDouble64) { + AsyncCommand(const AnalogOutputDouble64* aoDouble64, uint16_t idx) : + idx_(idx), crob_(), aoInt16_(), aoInt32_(), aoFloat32_(), aoDouble64_(aoDouble64) { } uint16_t Idx() { return idx_; } - ControlRelayOutputBlock* CROB() { + const ControlRelayOutputBlock* CROB() { + std::cout << "crob addr:" << crob_ << std::endl; return crob_; } - AnalogOutputInt16* AOInt16() { + const AnalogOutputInt16* AOInt16() { + std::cout << "aoi16 addr:" << aoInt16_ << std::endl; return aoInt16_; } - AnalogOutputInt32* AOInt32() { + const AnalogOutputInt32* AOInt32() { + std::cout << "aoi32 addr:" << aoInt32_ << std::endl; return aoInt32_; } - AnalogOutputFloat32* AOFloat32() { + const AnalogOutputFloat32* AOFloat32() { + std::cout << "aof32 addr:" << aoFloat32_ << std::endl; return aoFloat32_; } - AnalogOutputDouble64* AODouble64() { + const AnalogOutputDouble64* AODouble64() { + std::cout << "aod64 addr:" << aoDouble64_ << std::endl; return aoDouble64_; } private: uint16_t idx_; - ControlRelayOutputBlock* crob_; - AnalogOutputInt16* aoInt16_; - AnalogOutputInt32* aoInt32_; - AnalogOutputFloat32* aoFloat32_; - AnalogOutputDouble64* aoDouble64_; + const ControlRelayOutputBlock* crob_; + const AnalogOutputInt16* aoInt16_; + const AnalogOutputInt32* aoInt32_; + const AnalogOutputFloat32* aoFloat32_; + const AnalogOutputDouble64* aoDouble64_; }; diff --git a/cpp/outstation/AsyncCommandHandler.cpp b/cpp/outstation/AsyncCommandHandler.cpp index baa9961168..cdf4612462 100644 --- a/cpp/outstation/AsyncCommandHandler.cpp +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -26,7 +26,7 @@ CommandStatus AsyncCommandHandler::Select(const ControlRelayOutputBlock& command return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) { - push(AsyncCommand(command, aIndex)); + push(new AsyncCommand(&command, aIndex)); return CommandStatus::SUCCESS; } @@ -34,7 +34,7 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputInt16& command, uint return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputInt16& command, uint16_t aIndex) { - push(AsyncCommand(command, aIndex)); + push(new AsyncCommand(&command, aIndex)); return CommandStatus::SUCCESS; } @@ -42,7 +42,7 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputInt32& command, uint return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputInt32& command, uint16_t aIndex) { - push(AsyncCommand(command, aIndex)); + push(new AsyncCommand(&command, aIndex)); return CommandStatus::SUCCESS; } @@ -50,7 +50,7 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputFloat32& command, ui return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputFloat32& command, uint16_t aIndex) { - push(AsyncCommand(command, aIndex)); + push(new AsyncCommand(&command, aIndex)); return CommandStatus::SUCCESS; } @@ -58,27 +58,35 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputDouble64& command, u return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputDouble64& command, uint16_t aIndex) { - push(AsyncCommand(command, aIndex)); + push(new AsyncCommand(&command, aIndex)); return CommandStatus::SUCCESS; } +void AsyncCommandHandler::Start() { + // Do nothing +} + +void AsyncCommandHandler::End() { + // Do nothing +} + /** * Blocks on queue until one or more AsyncCommand elements are available. * TODO Handle more than 1 subscriber */ -AsyncCommand AsyncCommandHandler::pop() { +std::shared_ptr AsyncCommandHandler::pop() { std::unique_lock lock(queue_mutex_); while (queue_.empty()) { queue_cond_.wait(lock); } - AsyncCommand* command = queue_.front(); + std::shared_ptr command = queue_.front(); queue_.pop(); - return *command; + return command; } -void AsyncCommandHandler::push(AsyncCommand command) { +void AsyncCommandHandler::push(AsyncCommand* command) { std::unique_lock lock(queue_mutex_); - queue_.push(&command); + queue_.push(std::shared_ptr(command)); lock.unlock(); queue_cond_.notify_one(); } diff --git a/cpp/outstation/AsyncCommandHandler.h b/cpp/outstation/AsyncCommandHandler.h index ee3bfd20d6..8c6484c417 100644 --- a/cpp/outstation/AsyncCommandHandler.h +++ b/cpp/outstation/AsyncCommandHandler.h @@ -47,12 +47,15 @@ class AsyncCommandHandler: public ICommandHandler { CommandStatus Select(const AnalogOutputDouble64& command, uint16_t aIndex) override final; CommandStatus Operate(const AnalogOutputDouble64& command, uint16_t aIndex) override final; - AsyncCommand pop(); + void Start() override final; + void End() override final; + + std::shared_ptr pop(); private: - void push(AsyncCommand command); + void push(AsyncCommand* command); - std::queue queue_; + std::queue> queue_; std::mutex queue_mutex_; std::condition_variable queue_cond_; }; diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index 3597b6b04e..e19b0c7439 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -39,7 +39,7 @@ class JSONTCPSession: public std::enable_shared_from_this { read(); } - void write(AsyncCommand command) { + void write(AsyncCommand* command) { StringBuffer out_json_sb = toJSONStringBuffer(command); // Get JSON string length, convert size to int64_t and write in character stream @@ -90,6 +90,10 @@ class JSONTCPSession: public std::enable_shared_from_this { if (data_pos_ == 0) { /** initialize data size*/ memcpy(&data_sz_, &buf_[buf_pos], sizeof(int64_t)); + if (data_sz_ < 0) { + /** TODO warn in log **/ + data_sz_ = 0; + } buf_pos += sizeof(int64_t); buf_rem -= sizeof(int64_t); @@ -98,11 +102,11 @@ class JSONTCPSession: public std::enable_shared_from_this { data_[data_sz_] = '\0'; /** copy data from buffer (max(data_sz_,length)) */ - size_t size = buf_rem > data_sz_ ? data_sz_: buf_rem; + size_t size = buf_rem > (size_t) data_sz_ ? data_sz_: buf_rem; memcpy(data_, &buf_[buf_pos], size); data_pos_ = size; buf_pos += size; - } else if(data_pos_ < data_sz_) { + } else if(data_pos_ < (size_t) data_sz_) { /** copy data from buffer (max(data_rem,buf_rem)) */ size_t data_rem = data_sz_ - data_pos_; size_t size = buf_rem > data_rem ? data_rem : buf_rem; @@ -110,7 +114,7 @@ class JSONTCPSession: public std::enable_shared_from_this { buf_pos += size; } /** if data_sz has been transferred, process JSON object */ - if (data_pos_ == data_sz_) { + if (data_pos_ == (size_t) data_sz_) { /** apply update from JSON chars, deallocate memory and reset pointers */ applyUpdate(data_); delete[] data_; @@ -132,48 +136,48 @@ class JSONTCPSession: public std::enable_shared_from_this { /** * Takes input AsyncCommand and serializes JSON object to resulting StringBuffer */ - StringBuffer toJSONStringBuffer(AsyncCommand command) { + StringBuffer toJSONStringBuffer(AsyncCommand* command) { StringBuffer sb; Writer writer(sb); writer.StartObject(); writer.String("index"); - writer.Int(command.Idx()); - if (command.AOInt16() != NULL) { + writer.Int(command->Idx()); + if (command->AOInt16() != NULL) { writer.String("type"); writer.String(AI16); writer.String("value"); - writer.Int(command.AOInt16()->value); + writer.Int(command->AOInt16()->value); writer.String("status"); - writer.Int(static_cast(command.AOInt16()->status)); - } else if (command.AOInt32() != NULL) { + writer.Int(static_cast(command->AOInt16()->status)); + } else if (command->AOInt32() != NULL) { writer.String("type"); writer.String(AI32); writer.String("value"); - writer.Int(command.AOInt32()->value); + writer.Int(command->AOInt32()->value); writer.String("status"); - writer.Int(static_cast(command.AOInt32()->status)); - } else if (command.AOFloat32() != NULL) { + writer.Int(static_cast(command->AOInt32()->status)); + } else if (command->AOFloat32() != NULL) { writer.String("type"); writer.String(AF32); writer.String("value"); - writer.Double(command.AOFloat32()->value); + writer.Double(command->AOFloat32()->value); writer.String("status"); - writer.Int(static_cast(command.AOFloat32()->status)); - } else if (command.AODouble64() != NULL) { + writer.Int(static_cast(command->AOFloat32()->status)); + } else if (command->AODouble64() != NULL) { writer.String("type"); writer.String(AD64); writer.String("value"); - writer.Double(command.AODouble64()->value); + writer.Double(command->AODouble64()->value); writer.String("status"); - writer.Int(static_cast(command.AODouble64()->status)); - } else if (command.CROB() != NULL) { + writer.Int(static_cast(command->AODouble64()->status)); + } else if (command->CROB() != NULL) { // TODO handle all CROB attributes writer.String("type"); writer.String(CROB); writer.String("value"); - writer.Int(static_cast(command.CROB()->functionCode)); + writer.Int(static_cast(command->CROB()->functionCode)); writer.String("status"); - writer.Int(static_cast(command.CROB()->status)); + writer.Int(static_cast(command->CROB()->status)); } else { std::cerr << "JSONTCPSession: command not recognized" << std::endl; } diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index bd44eef445..e16c9aa366 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -16,14 +16,15 @@ #include #include #include -#include -#include #include #include #include #include +#include "AsyncCommandHandler.cpp" +#include "OutstationJSONTCPServer.cpp" + using namespace opendnp3; using namespace openpal; @@ -48,7 +49,7 @@ class OutstationApp { DNP3Manager manager(1); manager.AddLogSubscriber(&ConsoleLogger::Instance()); - IChannel* pChannel = manager.AddTCPServer("server", levels::NORMAL, TimeDuration::Seconds(5), TimeDuration::Seconds(5), "0.0.0.0", 20000); + IChannel* pChannel = manager.AddTCPServer("server", levels::NORMAL, ChannelRetry::Default(), "0.0.0.0", 20000); pChannel->AddStateListener([](ChannelState state) { std::cout << "channel state: " << ChannelStateToString(state) << std::endl; diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp index ffbcbb018c..3d5346ee8f 100644 --- a/cpp/outstation/OutstationJSONTCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -29,7 +29,7 @@ using namespace asiodnp3; class OutstationJSONTCPServer { public: OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, IOutstation* pOutstation, AsyncCommandHandler& handler) : - handler_(handler), pOutstation_ { pOutstation }, acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { + pOutstation_ { pOutstation }, handler_(handler), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { start(); } @@ -43,11 +43,11 @@ class OutstationJSONTCPServer { void subscribe() { while (true) { - AsyncCommand command = handler_.pop(); + std::shared_ptr command = handler_.pop(); std::set>::iterator it; for (it = sessions_.begin(); it != sessions_.end();) { if (it->get()->is_active()) { - it->get()->write(command); + it->get()->write(command.get()); it++; } else { sessions_.erase(it++); diff --git a/cpp/tests/outstation/src/OutstationAppTests.cpp b/cpp/tests/outstation/src/OutstationAppTests.cpp new file mode 100644 index 0000000000..5672be8ae9 --- /dev/null +++ b/cpp/tests/outstation/src/OutstationAppTests.cpp @@ -0,0 +1,37 @@ +/** + * Licensed 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. + */ + +#define CATCH_CONFIG_MAIN +#include +#include + +#include "opendnp3/app/ControlRelayOutputBlock.cpp" +#include "opendnp3/gen/ControlCode.cpp" +#include "opendnp3/app/AnalogOutput.h" + +#include "../../../outstation/AsyncCommand.cpp" + +#define SUITE(name) "TestOutstationApp - " name + +using namespace std; + +TEST_CASE(SUITE("AsyncCommand")) +{ + ControlRelayOutputBlock* crob = new ControlRelayOutputBlock(ControlCode::LATCH_ON, (uint8_t)1, (uint32_t) 2, (uint32_t) 3, CommandStatus::SUCCESS); + AsyncCommand* ac = new AsyncCommand(crob, 123); + REQUIRE(ac->AOInt16() == NULL); +} + + + diff --git a/thirdparty/asio b/thirdparty/asio new file mode 160000 index 0000000000..c466dc46d5 --- /dev/null +++ b/thirdparty/asio @@ -0,0 +1 @@ +Subproject commit c466dc46d55755d38ee1831e95207d6b329c4976 From e4281267fc1ccb7403b72f4a26e24481bcfee258 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 6 Oct 2015 09:50:57 -0500 Subject: [PATCH 21/32] BAT-45 - add gitignore for outstation app - fix master demo merge issue - clean up debugging code in asyncommand/handler --- .gitignore | 1 + .gitmodules | 2 +- cpp/examples/master/main.cpp | 67 +++++++++++++------------- cpp/outstation/AsyncCommand.cpp | 7 --- cpp/outstation/AsyncCommandHandler.cpp | 2 - thirdparty/rapidjson | 1 + 6 files changed, 36 insertions(+), 44 deletions(-) create mode 160000 thirdparty/rapidjson diff --git a/.gitignore b/.gitignore index 64f3ec4527..c9f4418629 100755 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,7 @@ testopendnp3 testasiodnp3 testosslcrypto testsecauth +testoutstation #demos master-demo diff --git a/.gitmodules b/.gitmodules index 72f81b2512..c586617c38 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,5 +4,5 @@ branch = develop [submodule "thirdparty/rapidjson"] path = thirdparty/rapidjson - url = ../rapidjson/ + url = ../rapidjson branch = develop diff --git a/cpp/examples/master/main.cpp b/cpp/examples/master/main.cpp index 7f51945193..e826ef198e 100644 --- a/cpp/examples/master/main.cpp +++ b/cpp/examples/master/main.cpp @@ -46,18 +46,15 @@ int main(int argc, char* argv[]) { // send log messages to the console manager.AddLogSubscriber(&ConsoleLogger::Instance()); - // Connect via a TCPClient socket to a outstation - auto pChannel = manager.AddTCPClient("tcpclient", FILTERS, - TimeDuration::Seconds(2), TimeDuration::Seconds(5), "127.0.0.1", - "0.0.0.0", 20000); + // Connect via a TCPClient socket to a outstation + auto pChannel = manager.AddTCPClient("tcpclient", FILTERS, ChannelRetry::Default(), "127.0.0.1", "0.0.0.0", 20000); // Optionally, you can bind listeners to the channel to get state change notifications // This listener just prints the changes to the console - pChannel->AddStateListener( - [](ChannelState state) - { - std::cout << "channel state: " << ChannelStateToString(state) << std::endl; - }); + pChannel->AddStateListener([](ChannelState state) + { + std::cout << "channel state: " << ChannelStateToString(state) << std::endl; + }); // The master config object for a master. The default are // useable, but understanding the options are important. @@ -76,24 +73,21 @@ int main(int argc, char* argv[]) { // 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 pMaster = pChannel->AddMaster("master", // id for logging - PrintingSOEHandler::Instance(), // callback for data processing + auto pMaster = pChannel->AddMaster("master", // id for logging + PrintingSOEHandler::Instance(), // callback for data processing asiodnp3::DefaultMasterApplication::Instance(), // master application instance - stackConfig // stack configuration + stackConfig // stack configuration ); // do an integrity poll (Class 3/2/1/0) once per minute - //auto integrityScan = pMaster->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1)); + auto integrityScan = pMaster->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1)); // do a Class 1 exception poll every 5 seconds - auto exceptionScan = pMaster->AddClassScan(ClassField(ClassField::CLASS_1), - TimeDuration::Seconds(2)); + auto exceptionScan = pMaster->AddClassScan(ClassField(ClassField::CLASS_1), TimeDuration::Seconds(2)); // Enable the master. This will start communications. pMaster->Enable(); - auto pCommandProcessor = pMaster->GetCommandProcessor(); - do { std::cout << "Enter a command" << std::endl; std::cout << "x - exits program" << std::endl; @@ -103,7 +97,6 @@ int main(int argc, char* argv[]) { std::cout << "d - diable unsolcited" << std::endl; std::cout << "r - cold restart" << std::endl; std::cout << "c - send crob" << std::endl; - std::cout << "o - send analog output int16" << std::endl; char cmd; std::cin >> cmd; @@ -111,37 +104,43 @@ int main(int argc, char* argv[]) { case ('a'): pMaster->ScanRange(GroupVariationID(1, 2), 0, 3); break; + case ('d'): + pMaster->PerformFunction("disable unsol", FunctionCode::DISABLE_UNSOLICITED, { Header::AllObjects(60, 2), Header::AllObjects(60, 3), + Header::AllObjects(60, 4) }); + break; + case ('r'): { + auto print = [](const RestartOperationResult& result) + { + if(result.summary == TaskCompletion::SUCCESS) + { + std::cout << "Success, Time: " << result.restartTime.GetMilliseconds() << std::endl; + } + else + { + std::cout << "Failure: " << TaskCompletionToString(result.summary) << std::endl; + } + }; + pMaster->Restart(RestartType::COLD, print); + break; + } case ('x'): // C++ destructor on DNP3Manager cleans everything up for you return 0; case ('i'): - //integrityScan.Demand(); + integrityScan.Demand(); break; case ('e'): exceptionScan.Demand(); break; case ('c'): { - // This is an example of synchronously doing a control operation ControlRelayOutputBlock crob(ControlCode::LATCH_ON); - BlockingCommandCallback handler; - pCommandProcessor->SelectAndOperate(crob, 0, handler); - auto response = handler.WaitForResult(); - std::cout << "Result: " - << TaskCompletionToString(response.GetResult()) - << " Status: " - << CommandStatusToString(response.GetStatus()) << std::endl; + pMaster->SelectAndOperate(crob, 0, PrintingCommandCallback::Get()); break; } case ('o'): { // This is an example of synchronously doing a control operation AnalogOutputInt16 analogOutInt16(4242); - BlockingCommandCallback handler; - pCommandProcessor->SelectAndOperate(analogOutInt16, 0, handler); - auto response = handler.WaitForResult(); - std::cout << "Result: " - << TaskCompletionToString(response.GetResult()) - << " Status: " - << CommandStatusToString(response.GetStatus()) << std::endl; + pMaster->SelectAndOperate(analogOutInt16, 0, PrintingCommandCallback::Get()); break; } default: diff --git a/cpp/outstation/AsyncCommand.cpp b/cpp/outstation/AsyncCommand.cpp index 1353cba869..eced71dece 100644 --- a/cpp/outstation/AsyncCommand.cpp +++ b/cpp/outstation/AsyncCommand.cpp @@ -12,8 +12,6 @@ * limitations under the License. */ -#include - using namespace opendnp3; class AsyncCommand { @@ -38,23 +36,18 @@ class AsyncCommand { return idx_; } const ControlRelayOutputBlock* CROB() { - std::cout << "crob addr:" << crob_ << std::endl; return crob_; } const AnalogOutputInt16* AOInt16() { - std::cout << "aoi16 addr:" << aoInt16_ << std::endl; return aoInt16_; } const AnalogOutputInt32* AOInt32() { - std::cout << "aoi32 addr:" << aoInt32_ << std::endl; return aoInt32_; } const AnalogOutputFloat32* AOFloat32() { - std::cout << "aof32 addr:" << aoFloat32_ << std::endl; return aoFloat32_; } const AnalogOutputDouble64* AODouble64() { - std::cout << "aod64 addr:" << aoDouble64_ << std::endl; return aoDouble64_; } diff --git a/cpp/outstation/AsyncCommandHandler.cpp b/cpp/outstation/AsyncCommandHandler.cpp index cdf4612462..f54b2fe87c 100644 --- a/cpp/outstation/AsyncCommandHandler.cpp +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -12,8 +12,6 @@ * limitations under the License. */ -#include - #include "AsyncCommandHandler.h" using namespace opendnp3; diff --git a/thirdparty/rapidjson b/thirdparty/rapidjson new file mode 160000 index 0000000000..3d5848a7cd --- /dev/null +++ b/thirdparty/rapidjson @@ -0,0 +1 @@ +Subproject commit 3d5848a7cd3367c5cb451c6493165b7745948308 From 9e4f757f965e0d740b2a6c0f7696de0f2a141d0d Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 6 Oct 2015 10:56:03 -0500 Subject: [PATCH 22/32] BAT-45 - remove old build hack - update README.md with build instructions --- README.md | 17 +++++++++-------- thirdparty/build.sh | 7 ------- 2 files changed, 9 insertions(+), 15 deletions(-) delete mode 100755 thirdparty/build.sh diff --git a/README.md b/README.md index 1c4bb08aa7..0105b63cb7 100755 --- a/README.md +++ b/README.md @@ -23,17 +23,18 @@ Langauge bindings are available. Consult the documentation. Ubuntu 14.04 Dependencies ========================= -```sudo apt-get install autoconf libtool cmake libasio-dev libboost-all-dev +```sudo apt-get install cmake libasio-dev libboost-all-dev ``` Build Steps =========== ```bash -cd thirdparty -./build.sh -cd ../ - -autoreconf -f -i -./configure -make -j6 +git submodule update --init +cmake -DDEMO=on -DTEST=on -DOUTSTATION=on . +make -j5 + +# from simultaneous shells +./outstation +./master-demo (press 'o' to send analog output 16) +nc localhost 3384 ``` diff --git a/thirdparty/build.sh b/thirdparty/build.sh deleted file mode 100755 index 790184218a..0000000000 --- a/thirdparty/build.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh -cd rapidjson -git submodule update --init -mkdir build -cd build -cmake .. -make -j6 From 9340f519b200a80698a1df40fecce7af01d052b9 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 6 Oct 2015 11:18:23 -0500 Subject: [PATCH 23/32] BAT-45 - add yaml-cpp submodule for config support --- .gitmodules | 4 ++++ thirdparty/yaml-cpp | 1 + 2 files changed, 5 insertions(+) create mode 160000 thirdparty/yaml-cpp diff --git a/.gitmodules b/.gitmodules index c586617c38..8c62de9316 100644 --- a/.gitmodules +++ b/.gitmodules @@ -6,3 +6,7 @@ path = thirdparty/rapidjson url = ../rapidjson branch = develop +[submodule "thirdparty/yaml-cpp"] + path = thirdparty/yaml-cpp + url = ../yaml-cpp + branch = develop diff --git a/thirdparty/yaml-cpp b/thirdparty/yaml-cpp new file mode 160000 index 0000000000..998d7bf31e --- /dev/null +++ b/thirdparty/yaml-cpp @@ -0,0 +1 @@ +Subproject commit 998d7bf31e1bab4e45221c28fad8e00ddcf8855e From 2e5fe6d0216183a46296bba1d06e8e1c14a58a4f Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 6 Oct 2015 14:32:57 -0500 Subject: [PATCH 24/32] BAT-45 - astyle master main.cpp - first crack at yaml config for outstation app --- cpp/examples/master/main.cpp | 74 +++++++++++++++++++---------------- cpp/tests/outstation/demo.yml | 33 ++++++++++++++++ 2 files changed, 74 insertions(+), 33 deletions(-) create mode 100644 cpp/tests/outstation/demo.yml diff --git a/cpp/examples/master/main.cpp b/cpp/examples/master/main.cpp index e826ef198e..1e3e795755 100644 --- a/cpp/examples/master/main.cpp +++ b/cpp/examples/master/main.cpp @@ -34,7 +34,8 @@ using namespace asiopal; using namespace asiodnp3; using namespace opendnp3; -int main(int argc, char* argv[]) { +int main(int argc, char* argv[]) +{ // Specify what log levels to use. NORMAL is warning and above // You can add all the comms logging by uncommenting below @@ -74,10 +75,10 @@ int main(int argc, char* argv[]) { // name, log level, command acceptor, and config info. This // returns a thread-safe interface used for sending commands. auto pMaster = pChannel->AddMaster("master", // id for logging - PrintingSOEHandler::Instance(), // callback for data processing - asiodnp3::DefaultMasterApplication::Instance(), // master application instance - stackConfig // stack configuration - ); + PrintingSOEHandler::Instance(), // callback for data processing + asiodnp3::DefaultMasterApplication::Instance(), // master application instance + stackConfig // stack configuration + ); // do an integrity poll (Class 3/2/1/0) once per minute auto integrityScan = pMaster->AddClassScan(ClassField::AllClasses(), TimeDuration::Minutes(1)); @@ -88,7 +89,8 @@ int main(int argc, char* argv[]) { // Enable the master. This will start communications. pMaster->Enable(); - do { + do + { std::cout << "Enter a command" << std::endl; std::cout << "x - exits program" << std::endl; std::cout << "a - performs and ad-hoc range scan" << std::endl; @@ -100,29 +102,32 @@ int main(int argc, char* argv[]) { char cmd; std::cin >> cmd; - switch (cmd) { + switch (cmd) + { case ('a'): pMaster->ScanRange(GroupVariationID(1, 2), 0, 3); break; case ('d'): pMaster->PerformFunction("disable unsol", FunctionCode::DISABLE_UNSOLICITED, { Header::AllObjects(60, 2), Header::AllObjects(60, 3), - Header::AllObjects(60, 4) }); + Header::AllObjects(60, 4) + }); break; - case ('r'): { - auto print = [](const RestartOperationResult& result) + case ('r'): { - if(result.summary == TaskCompletion::SUCCESS) - { - std::cout << "Success, Time: " << result.restartTime.GetMilliseconds() << std::endl; - } - else + auto print = [](const RestartOperationResult & result) { - std::cout << "Failure: " << TaskCompletionToString(result.summary) << std::endl; - } - }; - pMaster->Restart(RestartType::COLD, print); - break; - } + if(result.summary == TaskCompletion::SUCCESS) + { + std::cout << "Success, Time: " << result.restartTime.GetMilliseconds() << std::endl; + } + else + { + std::cout << "Failure: " << TaskCompletionToString(result.summary) << std::endl; + } + }; + pMaster->Restart(RestartType::COLD, print); + break; + } case ('x'): // C++ destructor on DNP3Manager cleans everything up for you return 0; @@ -132,22 +137,25 @@ int main(int argc, char* argv[]) { case ('e'): exceptionScan.Demand(); break; - case ('c'): { - ControlRelayOutputBlock crob(ControlCode::LATCH_ON); - pMaster->SelectAndOperate(crob, 0, PrintingCommandCallback::Get()); - break; - } - case ('o'): { - // This is an example of synchronously doing a control operation - AnalogOutputInt16 analogOutInt16(4242); - pMaster->SelectAndOperate(analogOutInt16, 0, PrintingCommandCallback::Get()); - break; - } + case ('c'): + { + ControlRelayOutputBlock crob(ControlCode::LATCH_ON); + pMaster->SelectAndOperate(crob, 0, PrintingCommandCallback::Get()); + break; + } + case ('o'): + { + // This is an example of synchronously doing a control operation + AnalogOutputInt16 analogOutInt16(4242); + pMaster->SelectAndOperate(analogOutInt16, 0, PrintingCommandCallback::Get()); + break; + } default: std::cout << "Unknown action: " << cmd << std::endl; break; } - } while (true); + } + while (true); return 0; } diff --git a/cpp/tests/outstation/demo.yml b/cpp/tests/outstation/demo.yml new file mode 100644 index 0000000000..7d46253c5c --- /dev/null +++ b/cpp/tests/outstation/demo.yml @@ -0,0 +1,33 @@ +master: + id: server + levels: normal + channelRetry: default + host: 0.0.0.0 + port: 20000 +jsonServer: + port: 3384 +outstations: + - id: outdemo + measDB: + analogs: + - vidx: 1 + variation: StaticAnalogVariation::Group30Var5 + metadata: + - clazz: PointClass::Class2 + - variation: EventAnalogVariation::Group32Var7 + - vidx: 2 + - vidx: 3 + binaries: + - vidx: 1 + doubleBinaries: + - vidx: 1 + counters: + - vidx: 1 + frozenCounters: + - vidx: 1 + binaryOutputStatii: + - vidx: 1 + analogOutputStatii: + - vidx: 1 + timeAndIntervals: + - vidx: 1 From 348b9435512431f61bad79476f9058d372364e0f Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 7 Oct 2015 10:12:27 -0500 Subject: [PATCH 25/32] BAT-45 - include yaml-cpp in cmake build and link test app --- CMakeLists.txt | 21 ++++++++++--------- .../outstation/src/OutstationAppTests.cpp | 7 +++++++ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 47cb83b78c..e46f56af01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,12 @@ if(SECAUTH OR DNP3_TLS) endif() -if(OUTSTATION) +if(OUTSTATION OR TEST) + include_directories(./thirdparty/rapidjson/include) + include_directories(./thirdparty/yaml-cpp/include) + + add_subdirectory(./thirdparty/yaml-cpp) + set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) @@ -53,10 +58,8 @@ if(OUTSTATION) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) - #add_executable(progname file1.cxx file2.cxx) - #target_link_libraries(progname ${Boost_LIBRARIES}) -endif() - include_directories(./thirdparty/rapidjson/include) + endif() + endif() if(WIN32) @@ -195,11 +198,8 @@ if(OUTSTATION) # ----- outstation executable ----- add_executable(outstation ./cpp/outstation/OutstationApp.cpp) - target_link_libraries (outstation LINK_PUBLIC asiodnp3 ${PTHREAD} ${Boost_LIBRARIES}) -# target_link_libraries (outstation LINK_PUBLIC asiodnp3 ${PTHREAD}) -# target_link_libraries(outstation ${Boost_LIBRARIES}) - -# set_target_properties(outstation PROPERTIES FOLDER demos) + target_link_libraries (outstation LINK_PUBLIC yaml-cpp asiodnp3 ${PTHREAD} ${Boost_LIBRARIES}) + #set_target_properties(outstation PROPERTIES FOLDER demos) endif() @@ -302,6 +302,7 @@ if(TEST) # ----- outstation app tests ----- file(GLOB_RECURSE outstation_TESTSRC ./cpp/tests/outstation/src/*.cpp ./cpp/tests/outstation/src/*.h) add_executable (testoutstation ${outstation_TESTSRC}) + target_link_libraries (testoutstation yaml-cpp) endif() diff --git a/cpp/tests/outstation/src/OutstationAppTests.cpp b/cpp/tests/outstation/src/OutstationAppTests.cpp index 5672be8ae9..e00d113ee7 100644 --- a/cpp/tests/outstation/src/OutstationAppTests.cpp +++ b/cpp/tests/outstation/src/OutstationAppTests.cpp @@ -16,6 +16,8 @@ #include #include +#include + #include "opendnp3/app/ControlRelayOutputBlock.cpp" #include "opendnp3/gen/ControlCode.cpp" #include "opendnp3/app/AnalogOutput.h" @@ -33,5 +35,10 @@ TEST_CASE(SUITE("AsyncCommand")) REQUIRE(ac->AOInt16() == NULL); } +TEST_CASE(SUITE("YAMLConfig")) +{ + YAML::Node config = YAML::LoadFile("cpp/tests/outstation/demo.yml"); + std::cout << config << std::endl; +} From 99157a68993b5f79203162cf1cdf6401bd35a07d Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 7 Oct 2015 13:47:13 -0500 Subject: [PATCH 26/32] BAT-45 - add basic yaml test case --- .gitignore | 1 + cpp/tests/outstation/demo.yml | 24 +++++++++---------- .../outstation/src/OutstationAppTests.cpp | 2 ++ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index c9f4418629..fb42c96d45 100755 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ libosslcrypto.* libtestlib.* libclangfuzzer.* libdnp3mocks.* +yaml-cpp.* #test suites testopenpal diff --git a/cpp/tests/outstation/demo.yml b/cpp/tests/outstation/demo.yml index 7d46253c5c..a3dd5b7fe4 100644 --- a/cpp/tests/outstation/demo.yml +++ b/cpp/tests/outstation/demo.yml @@ -1,5 +1,5 @@ master: - id: server + id: demomaster levels: normal channelRetry: default host: 0.0.0.0 @@ -7,27 +7,27 @@ master: jsonServer: port: 3384 outstations: - - id: outdemo + - id: demooutstation measDB: analogs: - - vidx: 1 + - idx: 0 variation: StaticAnalogVariation::Group30Var5 metadata: - clazz: PointClass::Class2 - variation: EventAnalogVariation::Group32Var7 - - vidx: 2 - - vidx: 3 + - idx: 1 + - idx: 2 binaries: - - vidx: 1 + - idx: 0 doubleBinaries: - - vidx: 1 + - idx: 0 counters: - - vidx: 1 + - idx: 0 frozenCounters: - - vidx: 1 + - idx: 0 binaryOutputStatii: - - vidx: 1 + - idx: 0 analogOutputStatii: - - vidx: 1 + - idx: 0 timeAndIntervals: - - vidx: 1 + - idx: 0 diff --git a/cpp/tests/outstation/src/OutstationAppTests.cpp b/cpp/tests/outstation/src/OutstationAppTests.cpp index e00d113ee7..1c4bbbfc51 100644 --- a/cpp/tests/outstation/src/OutstationAppTests.cpp +++ b/cpp/tests/outstation/src/OutstationAppTests.cpp @@ -38,6 +38,8 @@ TEST_CASE(SUITE("AsyncCommand")) TEST_CASE(SUITE("YAMLConfig")) { YAML::Node config = YAML::LoadFile("cpp/tests/outstation/demo.yml"); + REQUIRE(config["master"]); + REQUIRE(config["outstations"]); std::cout << config << std::endl; } From 87920eb5a4fc427aa206bd1089306cf83958df18 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Thu, 8 Oct 2015 15:20:11 -0500 Subject: [PATCH 27/32] BAT-45 - add program_options for outstation app usage - first pass at yaml outstation config support, currently only used to determine number of analogs,counters, etc. - added local/remote outstation address yaml conf options --- CMakeLists.txt | 2 +- cpp/outstation/OutstationApp.cpp | 106 +++++++++++++++++++++++++------ cpp/tests/outstation/demo.yml | 4 +- 3 files changed, 91 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e46f56af01..398334d0b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,7 @@ if(OUTSTATION OR TEST) set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) - find_package(Boost 1.54.0 COMPONENTS system thread) + find_package(Boost 1.54.0 COMPONENTS system thread program_options) if(Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index e16c9aa366..a42be5d383 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -12,6 +12,9 @@ * limitations under the License. */ +#include +#include + #include #include #include @@ -19,12 +22,15 @@ #include #include -#include -#include +#include + +#include #include "AsyncCommandHandler.cpp" #include "OutstationJSONTCPServer.cpp" +namespace po = boost::program_options; + using namespace opendnp3; using namespace openpal; @@ -45,53 +51,115 @@ void signal_handler(int signal_number) { */ class OutstationApp { public: - void start() { + /** + * Validates that config contains needed elements for basic configuration. + * Does not validate correctness of config elements. + */ + static bool validConfig(YAML::Node& config) { + bool valid = config["master"] && config["master"]["id"] && config["master"]["host"] && config["master"]["port"] && config["outstations"] + && config["outstations"][0] && config["outstations"][0]["id"] && config["outstations"][0]["localAddr"] + && config["outstations"][0]["remoteAddr"]; + if (!valid) + std::cerr << "Invalid YAML config, check master (id, host, port) and ou tstations (id)" << std::endl; + return valid; + } + + void start(YAML::Node& config) { DNP3Manager manager(1); manager.AddLogSubscriber(&ConsoleLogger::Instance()); - IChannel* pChannel = manager.AddTCPServer("server", levels::NORMAL, ChannelRetry::Default(), "0.0.0.0", 20000); + IChannel* pChannel = configureManagerMaster(config, manager); pChannel->AddStateListener([](ChannelState state) { std::cout << "channel state: " << ChannelStateToString(state) << std::endl; }); - OutstationStackConfig stackConfig; - stackConfig.dbTemplate = DatabaseTemplate::AllTypes(5); - stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(5); - stackConfig.link.LocalAddr = 10; - stackConfig.link.RemoteAddr = 1; - AsyncCommandHandler handler; - IOutstation* pOutstation = pChannel->AddOutstation("outstation", handler, DefaultOutstationApplication::Instance(), stackConfig); + /** TODO for each outstation, configure **/ + for (std::size_t i = 0; i < config["outstations"].size(); i++) { + YAML::Node outstationConfig = config["outstations"][i]; + IOutstation* pOutstation = configureOutstation(outstationConfig, pChannel, handler); - // Configure and start outstation - ConfigureDatabase(pOutstation->GetConfigView()); - pOutstation->Enable(); + /** TODO Enable all at end of loop, keep separate list for OJTS, add multi-outstation handling **/ + pOutstation->Enable(); + OutstationJSONTCPServer s(io_service_, 3384, pOutstation, handler); + io_service_.run(); + } - OutstationJSONTCPServer s(io_service_, 3384, pOutstation, handler); - io_service_.run(); } void stop() { - // TODO shutdown correctly + // TODO shutdown correctly, consider DNP3Manager.shutdown() io_service_.stop(); } private: - void ConfigureDatabase(DatabaseConfigView view) { + IChannel* configureManagerMaster(YAML::Node config, DNP3Manager& manager) { + return manager.AddTCPServer("server", levels::NORMAL, ChannelRetry::Default(), "0.0.0.0", 20000); + } + + IOutstation* configureOutstation(YAML::Node& outConf, IChannel* pChannel, AsyncCommandHandler& handler) { + OutstationStackConfig stackConfig; + stackConfig.link.LocalAddr = outConf["localAddr"].as(); + stackConfig.link.RemoteAddr = outConf["remoteAddr"].as(); + + if (outConf["measDB"]) { + int binaries = outConf["measDB"]["binaries"].size(); + int doubleBinaries = outConf["measDB"]["doubleBinaries"].size(); + int analogs = outConf["measDB"]["analogs"].size(); + int counters = outConf["measDB"]["counters"].size(); + int frozenCounters = outConf["measDB"]["frozenCounters"].size(); + int binaryOutputStatii = outConf["measDB"]["binaryOutputStatii"].size(); + int analogOutputStatii = outConf["measDB"]["analogOutputStatii"].size(); + int timeAndIntervals = outConf["measDB"]["timeAndIntervals"].size(); + + stackConfig.dbTemplate = DatabaseTemplate(binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, + timeAndIntervals); + stackConfig.outstation.eventBufferConfig = EventBufferConfig(binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, + analogOutputStatii, timeAndIntervals); + } else { + /** using default demo configuration **/ + stackConfig.dbTemplate = DatabaseTemplate::AllTypes(5); + stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(5); + } + + IOutstation* pOutstation = pChannel->AddOutstation(((std::string) outConf["id"].as()).c_str(), handler, + DefaultOutstationApplication::Instance(), stackConfig); + + DatabaseConfigView view = pOutstation->GetConfigView(); // TODO read from config file view.analogs[0].variation = StaticAnalogVariation::Group30Var5; view.analogs[0].metadata.clazz = PointClass::Class2; view.analogs[0].metadata.variation = EventAnalogVariation::Group32Var7; + + return pOutstation; } boost::asio::io_service io_service_; }; int main(int argc, char* argv[]) { + std::string confFile;/** "cpp/test/outstation/demo.yml" **/ + std::signal(SIGINT, signal_handler); + po::options_description desc("Options"); + desc.add_options()("conf", po::value(&confFile)->required(), "yaml configuration file")("help", "show help"); + + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + if (vm.count("help")) { + std::cerr << desc << std::endl; + return 1; + } + + YAML::Node config = YAML::LoadFile(confFile); + if (!OutstationApp::validConfig(config)) + return 1; + OutstationApp app; - std::thread t([&app] {app.start();}); + std::thread t([&app, &config] {app.start(config);}); t.detach(); std::unique_lock lock(g_signal_mutex); diff --git a/cpp/tests/outstation/demo.yml b/cpp/tests/outstation/demo.yml index a3dd5b7fe4..03afa7b33f 100644 --- a/cpp/tests/outstation/demo.yml +++ b/cpp/tests/outstation/demo.yml @@ -7,7 +7,9 @@ master: jsonServer: port: 3384 outstations: - - id: demooutstation + - id: demostation + localAddr: 1 + remoteAddr: 1 measDB: analogs: - idx: 0 From 2a0bc102a1f48ac473c0b74daa1e2a7fd12fef97 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Mon, 12 Oct 2015 14:18:15 -0500 Subject: [PATCH 28/32] BAT-45 - add OutstationAppConfig class for managing yaml configuration (still needs some work) - prepare for multiple outstation support --- cpp/outstation/JSONTCPSession.cpp | 53 +++++----- cpp/outstation/OutstationApp.cpp | 86 ++++------------ cpp/outstation/OutstationAppConfig.cpp | 113 +++++++++++++++++++++ cpp/outstation/OutstationJSONTCPServer.cpp | 8 +- cpp/tests/outstation/demo.yml | 2 +- cpp/tests/outstation/jsontcp-test.sh | 5 +- 6 files changed, 170 insertions(+), 97 deletions(-) create mode 100644 cpp/outstation/OutstationAppConfig.cpp diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index e19b0c7439..ebd4853055 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -31,8 +31,8 @@ using namespace rapidjson; */ class JSONTCPSession: public std::enable_shared_from_this { public: - JSONTCPSession(tcp::socket socket, IOutstation* pOutstation) : - socket_(std::move(socket)), pOutstation_ { pOutstation } { + JSONTCPSession(tcp::socket socket, std::map& outstations) : + socket_(std::move(socket)), outstations_ { outstations } { } void start() { @@ -140,6 +140,7 @@ class JSONTCPSession: public std::enable_shared_from_this { StringBuffer sb; Writer writer(sb); writer.StartObject(); + /** TODO add outstation id */ writer.String("index"); writer.Int(command->Idx()); if (command->AOInt16() != NULL) { @@ -192,30 +193,36 @@ class JSONTCPSession: public std::enable_shared_from_this { Document d; d.ParseInsitu(pchar_json_data); if (d.IsObject()) { - MeasUpdate update(pOutstation_); - if (d.HasMember("type") && d["type"].IsString() && d.HasMember("index") && d["index"].IsInt() && d.HasMember("value")) { - const char* type = d["type"].GetString(); - if (!strcmp(AD64, type)) { - if (d["value"].IsDouble()) { - update.Update(Analog(d["value"].GetDouble()), d["index"].GetInt()); - } - } else if (!strcmp(BIN, type)) { - if (d["value"].IsBool()) { - update.Update(Binary(d["value"].GetBool()), d["index"].GetInt()); - } - } else if (!strcmp(CI32, type)) { - if (d["value"].IsInt()) { - update.Update(Counter(d["value"].GetInt()), d["index"].GetInt()); - } - } else if (!strcmp(FCI32, type)) { - if (d["value"].IsInt()) { - update.Update(FrozenCounter(d["value"].GetInt()), d["index"].GetInt()); + if (d.HasMember("id") && d["id"].IsString() && d.HasMember("type") && d["type"].IsString() && d.HasMember("index") && d["index"].IsInt() + && d.HasMember("value")) { + if (outstations_.count(d["id"].GetString())) { + IOutstation* pOutstation = outstations_[d["id"].GetString()]; + MeasUpdate update(pOutstation); + const char* type = d["type"].GetString(); + if (!strcmp(AD64, type)) { + if (d["value"].IsDouble()) { + update.Update(Analog(d["value"].GetDouble()), d["index"].GetInt()); + } + } else if (!strcmp(BIN, type)) { + if (d["value"].IsBool()) { + update.Update(Binary(d["value"].GetBool()), d["index"].GetInt()); + } + } else if (!strcmp(CI32, type)) { + if (d["value"].IsInt()) { + update.Update(Counter(d["value"].GetInt()), d["index"].GetInt()); + } + } else if (!strcmp(FCI32, type)) { + if (d["value"].IsInt()) { + update.Update(FrozenCounter(d["value"].GetInt()), d["index"].GetInt()); + } + } else { + std::cerr << "JSONTCPSession: type attribute[" << type << "]not recognized" << std::endl; } } else { - std::cerr << "JSONTCPSession: type attribute[" << type << "]not recognized" << std::endl; + std::cerr << "JSONTCPSession: object id not found in outstations map" << std::endl; } } else { - std::cerr << "JSONTCPSession: object missing type attribute" << std::endl; + std::cerr << "JSONTCPSession: object missing id or type attribute" << std::endl; } } else { std::cerr << "JSONTCPSession: not an object" << std::endl; @@ -233,6 +240,6 @@ class JSONTCPSession: public std::enable_shared_from_this { int64_t data_sz_ = 0; tcp::socket socket_; - IOutstation* pOutstation_; + std::map outstations_; } ; diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index a42be5d383..32d237aeb1 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -27,6 +27,7 @@ #include #include "AsyncCommandHandler.cpp" +#include "OutstationAppConfig.cpp" #include "OutstationJSONTCPServer.cpp" namespace po = boost::program_options; @@ -51,41 +52,31 @@ void signal_handler(int signal_number) { */ class OutstationApp { public: - /** - * Validates that config contains needed elements for basic configuration. - * Does not validate correctness of config elements. - */ - static bool validConfig(YAML::Node& config) { - bool valid = config["master"] && config["master"]["id"] && config["master"]["host"] && config["master"]["port"] && config["outstations"] - && config["outstations"][0] && config["outstations"][0]["id"] && config["outstations"][0]["localAddr"] - && config["outstations"][0]["remoteAddr"]; - if (!valid) - std::cerr << "Invalid YAML config, check master (id, host, port) and ou tstations (id)" << std::endl; - return valid; - } - - void start(YAML::Node& config) { + void start(OutstationAppConfig& config) { DNP3Manager manager(1); manager.AddLogSubscriber(&ConsoleLogger::Instance()); - IChannel* pChannel = configureManagerMaster(config, manager); + IChannel* pChannel = manager.AddTCPServer(config.getMasterId(), levels::NORMAL, ChannelRetry::Default(), config.getMasterHost(), + config.getMasterPort()); pChannel->AddStateListener([](ChannelState state) { std::cout << "channel state: " << ChannelStateToString(state) << std::endl; }); + std::map outstations; AsyncCommandHandler handler; - /** TODO for each outstation, configure **/ - for (std::size_t i = 0; i < config["outstations"].size(); i++) { - YAML::Node outstationConfig = config["outstations"][i]; - IOutstation* pOutstation = configureOutstation(outstationConfig, pChannel, handler); - - /** TODO Enable all at end of loop, keep separate list for OJTS, add multi-outstation handling **/ - pOutstation->Enable(); - OutstationJSONTCPServer s(io_service_, 3384, pOutstation, handler); - io_service_.run(); + for (int i = 0; i < config.getOutstationsCount(); i++) { + outstations.insert(std::pair(config.getOutstationId(i), config.configureOutstation(i, pChannel, handler))); + } + + /** configurations were successful, start all outstations */ + for (std::pair outstation : outstations) { + outstation.second->Enable(); } + OutstationJSONTCPServer s(io_service_, 3384, outstations, handler); + io_service_.run(); + } void stop() { @@ -93,47 +84,6 @@ class OutstationApp { io_service_.stop(); } private: - IChannel* configureManagerMaster(YAML::Node config, DNP3Manager& manager) { - return manager.AddTCPServer("server", levels::NORMAL, ChannelRetry::Default(), "0.0.0.0", 20000); - } - - IOutstation* configureOutstation(YAML::Node& outConf, IChannel* pChannel, AsyncCommandHandler& handler) { - OutstationStackConfig stackConfig; - stackConfig.link.LocalAddr = outConf["localAddr"].as(); - stackConfig.link.RemoteAddr = outConf["remoteAddr"].as(); - - if (outConf["measDB"]) { - int binaries = outConf["measDB"]["binaries"].size(); - int doubleBinaries = outConf["measDB"]["doubleBinaries"].size(); - int analogs = outConf["measDB"]["analogs"].size(); - int counters = outConf["measDB"]["counters"].size(); - int frozenCounters = outConf["measDB"]["frozenCounters"].size(); - int binaryOutputStatii = outConf["measDB"]["binaryOutputStatii"].size(); - int analogOutputStatii = outConf["measDB"]["analogOutputStatii"].size(); - int timeAndIntervals = outConf["measDB"]["timeAndIntervals"].size(); - - stackConfig.dbTemplate = DatabaseTemplate(binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, - timeAndIntervals); - stackConfig.outstation.eventBufferConfig = EventBufferConfig(binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, - analogOutputStatii, timeAndIntervals); - } else { - /** using default demo configuration **/ - stackConfig.dbTemplate = DatabaseTemplate::AllTypes(5); - stackConfig.outstation.eventBufferConfig = EventBufferConfig::AllTypes(5); - } - - IOutstation* pOutstation = pChannel->AddOutstation(((std::string) outConf["id"].as()).c_str(), handler, - DefaultOutstationApplication::Instance(), stackConfig); - - DatabaseConfigView view = pOutstation->GetConfigView(); - // TODO read from config file - view.analogs[0].variation = StaticAnalogVariation::Group30Var5; - view.analogs[0].metadata.clazz = PointClass::Class2; - view.analogs[0].metadata.variation = EventAnalogVariation::Group32Var7; - - return pOutstation; - } - boost::asio::io_service io_service_; }; @@ -154,12 +104,12 @@ int main(int argc, char* argv[]) { return 1; } - YAML::Node config = YAML::LoadFile(confFile); - if (!OutstationApp::validConfig(config)) + OutstationAppConfig appConfig(confFile); + if (!appConfig.isValid()) return 1; OutstationApp app; - std::thread t([&app, &config] {app.start(config);}); + std::thread t([&app, &appConfig] {app.start(appConfig);}); t.detach(); std::unique_lock lock(g_signal_mutex); diff --git a/cpp/outstation/OutstationAppConfig.cpp b/cpp/outstation/OutstationAppConfig.cpp new file mode 100644 index 0000000000..40c45de506 --- /dev/null +++ b/cpp/outstation/OutstationAppConfig.cpp @@ -0,0 +1,113 @@ +#include + +using namespace asiodnp3; + +class OutstationAppConfig { + +public: + OutstationAppConfig(std::string confFile) { + configNode_ = YAML::LoadFile(confFile); + } + + /** + * Validates that config contains needed elements for basic configuration. + * Does not validate correctness of config elements. + */ + bool isValid() { + bool valid = configNode_["master"] && configNode_["master"]["id"] && configNode_["master"]["host"] && configNode_["master"]["port"] + && configNode_["outstations"] && configNode_["outstations"][0] && configNode_["outstations"][0]["id"] + && configNode_["outstations"][0]["localAddr"] && configNode_["outstations"][0]["remoteAddr"]; + if (!valid) + std::cerr << "Invalid YAML config, check master (id, host, port) and outstations (id)" << std::endl; + return valid; + } + + const char* getMasterId() { + return ((std::string) configNode_["master"]["id"].as()).c_str(); + } + + const char* getMasterHost() { + return ((std::string) configNode_["master"]["host"].as()).c_str(); + } + + int getMasterPort() { + return configNode_["master"]["port"].as(); + } + + int getOutstationsCount() { + return configNode_["outstations"].size(); + } + + std::string getOutstationId(int idx) { + return configNode_["outstations"][idx]["id"].as(); + } + + IOutstation* configureOutstation(int idx, IChannel* pChannel, AsyncCommandHandler& handler) { + /** TODO consider decoupling dnp3 from configuration class **/ + YAML::Node outstationNode = configNode_["outstations"][idx]; + int binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, timeAndIntervals; + if (configNode_["measDB"]) { + binaries = configNode_["measDB"]["binaries"].size(); + doubleBinaries = configNode_["measDB"]["doubleBinaries"].size(); + analogs = configNode_["measDB"]["analogs"].size(); + counters = configNode_["measDB"]["counters"].size(); + frozenCounters = configNode_["measDB"]["frozenCounters"].size(); + binaryOutputStatii = configNode_["measDB"]["binaryOutputStatii"].size(); + analogOutputStatii = configNode_["measDB"]["analogOutputStatii"].size(); + timeAndIntervals = configNode_["measDB"]["timeAndIntervals"].size(); + } else { + /** using default demo configuration DatabaseTemplate::AllTypes(10), EventBufferConfig::AllTypes(10); **/ + binaries = doubleBinaries = analogs = counters = frozenCounters = binaryOutputStatii = analogOutputStatii = timeAndIntervals = 10; + } + IOutstation* pOutstation = pChannel->AddOutstation(((std::string) outstationNode["id"].as()).c_str(), handler, + DefaultOutstationApplication::Instance(), + createStackConfig(outstationNode, binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, + timeAndIntervals)); + configureDatabaseConfigView(pOutstation->GetConfigView(), binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, + analogOutputStatii, timeAndIntervals); + return pOutstation; + } + +private: + OutstationStackConfig createStackConfig(YAML::Node& outstationNode, int binaries, int doubleBinaries, int analogs, int counters, int frozenCounters, + int binaryOutputStatii, int analogOutputStatii, int timeAndIntervals) { + OutstationStackConfig stackConfig; + stackConfig.link.LocalAddr = outstationNode["localAddr"].as(); + stackConfig.link.RemoteAddr = outstationNode["remoteAddr"].as(); + stackConfig.dbTemplate = DatabaseTemplate(binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, + timeAndIntervals); + stackConfig.outstation.eventBufferConfig = EventBufferConfig(binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, + analogOutputStatii, timeAndIntervals); + return stackConfig; + } + + void configureDatabaseConfigView(DatabaseConfigView view, int binaries, int doubleBinaries, int analogs, int counters, int frozenCounters, + int binaryOutputStatii, int analogOutputStatii, int timeAndIntervals) { + for (int i = 0; i < binaries; i++) { + + } + for (int i = 0; i < doubleBinaries; i++) { + + } + for (int i = 0; i < analogs; i++) { + + } + for (int i = 0; i < counters; i++) { + + } + for (int i = 0; i < frozenCounters; i++) { + + } + for (int i = 0; i < binaryOutputStatii; i++) { + + } + for (int i = 0; i < analogOutputStatii; i++) { + + } + for (int i = 0; i < timeAndIntervals; i++) { + + } + } + + YAML::Node configNode_; +}; diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp index 3d5346ee8f..c258d502cb 100644 --- a/cpp/outstation/OutstationJSONTCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -28,8 +28,8 @@ using namespace asiodnp3; */ class OutstationJSONTCPServer { public: - OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, IOutstation* pOutstation, AsyncCommandHandler& handler) : - pOutstation_ { pOutstation }, handler_(handler), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { + OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, std::map& outstations, AsyncCommandHandler& handler) : + outstations_ { outstations }, handler_(handler), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { start(); } @@ -61,7 +61,7 @@ class OutstationJSONTCPServer { { if (!error) { - std::shared_ptr pSession = std::make_shared(std::move(socket_), pOutstation_); + std::shared_ptr pSession = std::make_shared(std::move(socket_), outstations_); sessions_.insert(pSession); pSession->start(); } else { @@ -71,7 +71,7 @@ class OutstationJSONTCPServer { }); } - IOutstation* pOutstation_; + std::map outstations_; AsyncCommandHandler& handler_; std::set> sessions_; diff --git a/cpp/tests/outstation/demo.yml b/cpp/tests/outstation/demo.yml index 03afa7b33f..911d60fdca 100644 --- a/cpp/tests/outstation/demo.yml +++ b/cpp/tests/outstation/demo.yml @@ -8,7 +8,7 @@ jsonServer: port: 3384 outstations: - id: demostation - localAddr: 1 + localAddr: 10 remoteAddr: 1 measDB: analogs: diff --git a/cpp/tests/outstation/jsontcp-test.sh b/cpp/tests/outstation/jsontcp-test.sh index 8d049e7ae2..2ac56e1cc2 100755 --- a/cpp/tests/outstation/jsontcp-test.sh +++ b/cpp/tests/outstation/jsontcp-test.sh @@ -1,2 +1,5 @@ #!/bin/sh -cat jsontcp-sample.bin | nc localhost 3384 +SCRIPT=$(readlink -f $0) +SCRIPTPATH=`dirname $SCRIPT` + +cat $SCRIPTPATH/jsontcp-sample.bin | nc localhost 3384 From e30f10a923698a5de44015d7efa64f0325c24894 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Mon, 12 Oct 2015 14:46:38 -0500 Subject: [PATCH 29/32] BAT-45 - fix up sample with demostation id --- cpp/tests/outstation/jsontcp-sample.bin | Bin 263 -> 339 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/cpp/tests/outstation/jsontcp-sample.bin b/cpp/tests/outstation/jsontcp-sample.bin index 1f64547417552929cea6101cde5599b4468cd373..fa517f0e09bbcd49d3879c3548d0073c25677bb2 100644 GIT binary patch literal 339 zcmeZgfPiYH%oHUnrIgg%{Nj?tlFa-(B^{-b%7Rp&pkrQQPJX&eerZxps+kE;JTosP zwL;0tKu4)8F{c!$#KcI?#7IX8q_eabC{gQ1gt1PUd5J}p*o-SFDosT;&4&onobyZb lN>YnF^Gb}3u$gIUVq%DFW)Kl(x)tSDrREVd+1L`r?*N!XWU~MO delta 57 ncmcc2)Xv0d!vF!*6J@M9jp3YPBTh>==b#0rJ&ZG1l+g|V1BD3X From 40c8bc17c481aeb37d2ca078d78bc0398d99202d Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Tue, 13 Oct 2015 11:26:46 -0500 Subject: [PATCH 30/32] BAT-45 - add AsyncCommandQueue that is used by all instances of AsyncCommandHandlers for sending commands to the OutstationJSONTCPServer (publish/subscribe), attempted boost:signals2 but ran into issues (turns out they were not related to signals2 - so it may be worth revisiting) - add AsyncCommand "id" support for handling multiple outstations --- cpp/outstation/AsyncCommand.cpp | 25 +++++++----- cpp/outstation/AsyncCommandHandler.cpp | 35 ++++------------- cpp/outstation/AsyncCommandHandler.h | 38 ++++++++----------- cpp/outstation/AsyncCommandQueue.cpp | 33 ++++++++++++++++ cpp/outstation/JSONTCPSession.cpp | 3 +- cpp/outstation/OutstationApp.cpp | 8 ++-- cpp/outstation/OutstationAppConfig.cpp | 7 ++-- cpp/outstation/OutstationJSONTCPServer.cpp | 8 ++-- .../outstation/src/OutstationAppTests.cpp | 2 +- 9 files changed, 87 insertions(+), 72 deletions(-) create mode 100644 cpp/outstation/AsyncCommandQueue.cpp diff --git a/cpp/outstation/AsyncCommand.cpp b/cpp/outstation/AsyncCommand.cpp index eced71dece..6dd3c39623 100644 --- a/cpp/outstation/AsyncCommand.cpp +++ b/cpp/outstation/AsyncCommand.cpp @@ -16,20 +16,24 @@ using namespace opendnp3; class AsyncCommand { public: - AsyncCommand(const ControlRelayOutputBlock* crob, uint16_t idx) : - idx_(idx), crob_(crob), aoInt16_(), aoInt32_(), aoFloat32_(), aoDouble64_() { + AsyncCommand(const ControlRelayOutputBlock* crob, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(crob), aoInt16_(), aoInt32_(), aoFloat32_(), aoDouble64_() { } - AsyncCommand(const AnalogOutputInt16* aoInt16, uint16_t idx) : - idx_(idx), crob_(), aoInt16_(aoInt16), aoInt32_(), aoFloat32_(), aoDouble64_() { + AsyncCommand(const AnalogOutputInt16* aoInt16, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(), aoInt16_(aoInt16), aoInt32_(), aoFloat32_(), aoDouble64_() { } - AsyncCommand(const AnalogOutputInt32* aoInt32, uint16_t idx) : - idx_(idx), crob_(), aoInt16_(), aoInt32_(aoInt32), aoFloat32_(), aoDouble64_() { + AsyncCommand(const AnalogOutputInt32* aoInt32, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(), aoInt16_(), aoInt32_(aoInt32), aoFloat32_(), aoDouble64_() { } - AsyncCommand(const AnalogOutputFloat32* aoFloat32, uint16_t idx) : - idx_(idx), crob_(), aoInt16_(), aoInt32_(), aoFloat32_(aoFloat32), aoDouble64_() { + AsyncCommand(const AnalogOutputFloat32* aoFloat32, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(), aoInt16_(), aoInt32_(), aoFloat32_(aoFloat32), aoDouble64_() { } - AsyncCommand(const AnalogOutputDouble64* aoDouble64, uint16_t idx) : - idx_(idx), crob_(), aoInt16_(), aoInt32_(), aoFloat32_(), aoDouble64_(aoDouble64) { + AsyncCommand(const AnalogOutputDouble64* aoDouble64, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(), aoInt16_(), aoInt32_(), aoFloat32_(), aoDouble64_(aoDouble64) { + } + + const char* Id() { + return id_; } uint16_t Idx() { @@ -52,6 +56,7 @@ class AsyncCommand { } private: + const char* id_; uint16_t idx_; const ControlRelayOutputBlock* crob_; const AnalogOutputInt16* aoInt16_; diff --git a/cpp/outstation/AsyncCommandHandler.cpp b/cpp/outstation/AsyncCommandHandler.cpp index f54b2fe87c..e928d076b4 100644 --- a/cpp/outstation/AsyncCommandHandler.cpp +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -14,17 +14,16 @@ #include "AsyncCommandHandler.h" -using namespace opendnp3; - // TODO validate requests before returning CommandStatus -AsyncCommandHandler::~AsyncCommandHandler() { +AsyncCommandHandler::AsyncCommandHandler(const char* id, AsyncCommandQueue& queue) : + id_(id), queue_(queue) { } CommandStatus AsyncCommandHandler::Select(const ControlRelayOutputBlock& command, uint16_t aIndex) { return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) { - push(new AsyncCommand(&command, aIndex)); + queue_.push(new AsyncCommand(&command, id_, aIndex)); return CommandStatus::SUCCESS; } @@ -32,7 +31,7 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputInt16& command, uint return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputInt16& command, uint16_t aIndex) { - push(new AsyncCommand(&command, aIndex)); + queue_.push(new AsyncCommand(&command, id_, aIndex)); return CommandStatus::SUCCESS; } @@ -40,7 +39,7 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputInt32& command, uint return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputInt32& command, uint16_t aIndex) { - push(new AsyncCommand(&command, aIndex)); + queue_.push(new AsyncCommand(&command, id_, aIndex)); return CommandStatus::SUCCESS; } @@ -48,7 +47,7 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputFloat32& command, ui return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputFloat32& command, uint16_t aIndex) { - push(new AsyncCommand(&command, aIndex)); + queue_.push(new AsyncCommand(&command, id_, aIndex)); return CommandStatus::SUCCESS; } @@ -56,7 +55,7 @@ CommandStatus AsyncCommandHandler::Select(const AnalogOutputDouble64& command, u return CommandStatus::SUCCESS; } CommandStatus AsyncCommandHandler::Operate(const AnalogOutputDouble64& command, uint16_t aIndex) { - push(new AsyncCommand(&command, aIndex)); + queue_.push(new AsyncCommand(&command, id_, aIndex)); return CommandStatus::SUCCESS; } @@ -68,23 +67,3 @@ void AsyncCommandHandler::End() { // Do nothing } -/** - * Blocks on queue until one or more AsyncCommand elements are available. - * TODO Handle more than 1 subscriber - */ -std::shared_ptr AsyncCommandHandler::pop() { - std::unique_lock lock(queue_mutex_); - while (queue_.empty()) { - queue_cond_.wait(lock); - } - std::shared_ptr command = queue_.front(); - queue_.pop(); - return command; -} - -void AsyncCommandHandler::push(AsyncCommand* command) { - std::unique_lock lock(queue_mutex_); - queue_.push(std::shared_ptr(command)); - lock.unlock(); - queue_cond_.notify_one(); -} diff --git a/cpp/outstation/AsyncCommandHandler.h b/cpp/outstation/AsyncCommandHandler.h index 8c6484c417..cf5cad714a 100644 --- a/cpp/outstation/AsyncCommandHandler.h +++ b/cpp/outstation/AsyncCommandHandler.h @@ -20,8 +20,6 @@ #include #include -#include "AsyncCommand.cpp" - using namespace opendnp3; /** @@ -30,34 +28,30 @@ using namespace opendnp3; */ class AsyncCommandHandler: public ICommandHandler { public: - ~AsyncCommandHandler() override final; - - CommandStatus Select(const ControlRelayOutputBlock& command, uint16_t aIndex) override final; - CommandStatus Operate(const ControlRelayOutputBlock& command, uint16_t aIndex) override final; + AsyncCommandHandler(const char* id, AsyncCommandQueue& commandQueue); - CommandStatus Select(const AnalogOutputInt16& command, uint16_t aIndex) override final; - CommandStatus Operate(const AnalogOutputInt16& command, uint16_t aIndex) override final; + CommandStatus Select(const ControlRelayOutputBlock& command, uint16_t index) override final; + CommandStatus Operate(const ControlRelayOutputBlock& command, uint16_t index) override final; - CommandStatus Select(const AnalogOutputInt32& command, uint16_t aIndex) override final; - CommandStatus Operate(const AnalogOutputInt32& command, uint16_t aIndex) override final; + CommandStatus Select(const AnalogOutputInt16& command, uint16_t index) override final; + CommandStatus Operate(const AnalogOutputInt16& command, uint16_t index) override final; - CommandStatus Select(const AnalogOutputFloat32& command, uint16_t aIndex) override final; - CommandStatus Operate(const AnalogOutputFloat32& command, uint16_t aIndex) override final; + CommandStatus Select(const AnalogOutputInt32& command, uint16_t index) override final; + CommandStatus Operate(const AnalogOutputInt32& command, uint16_t index) override final; - CommandStatus Select(const AnalogOutputDouble64& command, uint16_t aIndex) override final; - CommandStatus Operate(const AnalogOutputDouble64& command, uint16_t aIndex) override final; + CommandStatus Select(const AnalogOutputFloat32& command, uint16_t index) override final; + CommandStatus Operate(const AnalogOutputFloat32& command, uint16_t index) override final; - void Start() override final; - void End() override final; + CommandStatus Select(const AnalogOutputDouble64& command, uint16_t index) override final; + CommandStatus Operate(const AnalogOutputDouble64& command, uint16_t index) override final; - std::shared_ptr pop(); +protected: + virtual void Start() override; + virtual void End() override; private: - void push(AsyncCommand* command); - - std::queue> queue_; - std::mutex queue_mutex_; - std::condition_variable queue_cond_; + const char* id_; + AsyncCommandQueue& queue_; }; #endif diff --git a/cpp/outstation/AsyncCommandQueue.cpp b/cpp/outstation/AsyncCommandQueue.cpp new file mode 100644 index 0000000000..a0e1f45cce --- /dev/null +++ b/cpp/outstation/AsyncCommandQueue.cpp @@ -0,0 +1,33 @@ +#include +#include + +#include "AsyncCommand.cpp" + +class AsyncCommandQueue { +public: + /** + * Blocks on queue until one or more AsyncCommand elements are available. + * TODO Handle more than 1 subscriber + */ + std::shared_ptr pop() { + std::unique_lock lock(queue_mutex_); + while (queue_.empty()) { + queue_cond_.wait(lock); + } + std::shared_ptr command = queue_.front(); + queue_.pop(); + return command; + } + + void push(AsyncCommand* command) { + std::unique_lock lock(queue_mutex_); + queue_.push(std::shared_ptr(command)); + lock.unlock(); + queue_cond_.notify_all(); + } + +private: + std::queue> queue_; + std::mutex queue_mutex_; + std::condition_variable queue_cond_; +}; diff --git a/cpp/outstation/JSONTCPSession.cpp b/cpp/outstation/JSONTCPSession.cpp index ebd4853055..4199500f87 100644 --- a/cpp/outstation/JSONTCPSession.cpp +++ b/cpp/outstation/JSONTCPSession.cpp @@ -140,7 +140,8 @@ class JSONTCPSession: public std::enable_shared_from_this { StringBuffer sb; Writer writer(sb); writer.StartObject(); - /** TODO add outstation id */ + writer.String("id"); + writer.String(command->Id()); writer.String("index"); writer.Int(command->Idx()); if (command->AOInt16() != NULL) { diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index 32d237aeb1..499aebb099 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -23,9 +23,11 @@ #include #include +#include #include +#include "AsyncCommandQueue.cpp" #include "AsyncCommandHandler.cpp" #include "OutstationAppConfig.cpp" #include "OutstationJSONTCPServer.cpp" @@ -63,10 +65,10 @@ class OutstationApp { std::cout << "channel state: " << ChannelStateToString(state) << std::endl; }); + AsyncCommandQueue commandQueue; std::map outstations; - AsyncCommandHandler handler; for (int i = 0; i < config.getOutstationsCount(); i++) { - outstations.insert(std::pair(config.getOutstationId(i), config.configureOutstation(i, pChannel, handler))); + outstations.insert(std::pair(config.getOutstationId(i), config.configureOutstation(i, pChannel, commandQueue))); } /** configurations were successful, start all outstations */ @@ -74,7 +76,7 @@ class OutstationApp { outstation.second->Enable(); } - OutstationJSONTCPServer s(io_service_, 3384, outstations, handler); + OutstationJSONTCPServer s(io_service_, 3384, outstations, commandQueue); io_service_.run(); } diff --git a/cpp/outstation/OutstationAppConfig.cpp b/cpp/outstation/OutstationAppConfig.cpp index 40c45de506..7254365188 100644 --- a/cpp/outstation/OutstationAppConfig.cpp +++ b/cpp/outstation/OutstationAppConfig.cpp @@ -42,7 +42,7 @@ class OutstationAppConfig { return configNode_["outstations"][idx]["id"].as(); } - IOutstation* configureOutstation(int idx, IChannel* pChannel, AsyncCommandHandler& handler) { + IOutstation* configureOutstation(int idx, IChannel* pChannel, AsyncCommandQueue& queue) { /** TODO consider decoupling dnp3 from configuration class **/ YAML::Node outstationNode = configNode_["outstations"][idx]; int binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, timeAndIntervals; @@ -59,8 +59,9 @@ class OutstationAppConfig { /** using default demo configuration DatabaseTemplate::AllTypes(10), EventBufferConfig::AllTypes(10); **/ binaries = doubleBinaries = analogs = counters = frozenCounters = binaryOutputStatii = analogOutputStatii = timeAndIntervals = 10; } - IOutstation* pOutstation = pChannel->AddOutstation(((std::string) outstationNode["id"].as()).c_str(), handler, - DefaultOutstationApplication::Instance(), + const char* outstationId = outstationNode["id"].as().c_str(); + AsyncCommandHandler* handler = new AsyncCommandHandler(outstationId, queue); + IOutstation* pOutstation = pChannel->AddOutstation(outstationId, *handler, DefaultOutstationApplication::Instance(), createStackConfig(outstationNode, binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, timeAndIntervals)); configureDatabaseConfigView(pOutstation->GetConfigView(), binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp index c258d502cb..d81501a9c8 100644 --- a/cpp/outstation/OutstationJSONTCPServer.cpp +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -28,8 +28,8 @@ using namespace asiodnp3; */ class OutstationJSONTCPServer { public: - OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, std::map& outstations, AsyncCommandHandler& handler) : - outstations_ { outstations }, handler_(handler), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { + OutstationJSONTCPServer(boost::asio::io_service& io_service, short port, std::map& outstations, AsyncCommandQueue& queue) : + outstations_ { outstations }, queue_(queue), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { start(); } @@ -43,7 +43,7 @@ class OutstationJSONTCPServer { void subscribe() { while (true) { - std::shared_ptr command = handler_.pop(); + std::shared_ptr command = queue_.pop(); std::set>::iterator it; for (it = sessions_.begin(); it != sessions_.end();) { if (it->get()->is_active()) { @@ -72,8 +72,8 @@ class OutstationJSONTCPServer { } std::map outstations_; - AsyncCommandHandler& handler_; std::set> sessions_; + AsyncCommandQueue& queue_; tcp::acceptor acceptor_; tcp::socket socket_; diff --git a/cpp/tests/outstation/src/OutstationAppTests.cpp b/cpp/tests/outstation/src/OutstationAppTests.cpp index 1c4bbbfc51..b1594cc9a7 100644 --- a/cpp/tests/outstation/src/OutstationAppTests.cpp +++ b/cpp/tests/outstation/src/OutstationAppTests.cpp @@ -31,7 +31,7 @@ using namespace std; TEST_CASE(SUITE("AsyncCommand")) { ControlRelayOutputBlock* crob = new ControlRelayOutputBlock(ControlCode::LATCH_ON, (uint8_t)1, (uint32_t) 2, (uint32_t) 3, CommandStatus::SUCCESS); - AsyncCommand* ac = new AsyncCommand(crob, 123); + AsyncCommand* ac = new AsyncCommand(crob, (char *) "testout", 123); REQUIRE(ac->AOInt16() == NULL); } From b858a2c440b74f8248ffbf1a81e7ca3bec9468fe Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 14 Oct 2015 13:08:32 -0500 Subject: [PATCH 31/32] BAT-45 - add all known enum mappings the hard way --- cpp/outstation/OutstationAppConfig.cpp | 329 +++++++++++++++++++++++-- 1 file changed, 312 insertions(+), 17 deletions(-) diff --git a/cpp/outstation/OutstationAppConfig.cpp b/cpp/outstation/OutstationAppConfig.cpp index 7254365188..7905c3816c 100644 --- a/cpp/outstation/OutstationAppConfig.cpp +++ b/cpp/outstation/OutstationAppConfig.cpp @@ -1,7 +1,15 @@ #include +#include "opendnp3/app/MeasurementTypes.h" + using namespace asiodnp3; +/** + * Class responsible for parsing OutstationApp YAML Configuration + * and configuring appropriate OpenDNP3 Outstation objects. + * + * TODO implement a better String<->Enum mapping technique for variations/metadata/eventVariations + */ class OutstationAppConfig { public: @@ -64,8 +72,7 @@ class OutstationAppConfig { IOutstation* pOutstation = pChannel->AddOutstation(outstationId, *handler, DefaultOutstationApplication::Instance(), createStackConfig(outstationNode, binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, analogOutputStatii, timeAndIntervals)); - configureDatabaseConfigView(pOutstation->GetConfigView(), binaries, doubleBinaries, analogs, counters, frozenCounters, binaryOutputStatii, - analogOutputStatii, timeAndIntervals); + configureDatabaseConfigView(outstationNode, pOutstation->GetConfigView()); return pOutstation; } @@ -82,33 +89,321 @@ class OutstationAppConfig { return stackConfig; } - void configureDatabaseConfigView(DatabaseConfigView view, int binaries, int doubleBinaries, int analogs, int counters, int frozenCounters, - int binaryOutputStatii, int analogOutputStatii, int timeAndIntervals) { - for (int i = 0; i < binaries; i++) { - + void configureDatabaseConfigView(YAML::Node& outstationNode, DatabaseConfigView view) { + if (outstationNode["measDB"]) { + configureBinaries(outstationNode, view.binaries); + configureDoubleBinaries(outstationNode, view.doubleBinaries); + configureAnalogs(outstationNode, view.analogs); + configureCounters(outstationNode, view.counters); + configureFrozenCounters(outstationNode, view.frozenCounters); + configureBinaryOutputStatii(outstationNode, view.binaryOutputStatii); + configureAnalogOutputStatii(outstationNode, view.analogOutputStatii); + configureTimeAndIntervals(outstationNode, view.timeAndIntervals); } - for (int i = 0; i < doubleBinaries; i++) { + } + void configureBinaries(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& binaries) { + const char* type = "binaries"; + for (int i = 0; i < binaries.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticBinaryVariation::Group1Var1") { + binaries[i].variation = StaticBinaryVariation::Group1Var1; + } else if (variation == "StaticBinaryVariation::Group1Var2") { + binaries[i].variation = StaticBinaryVariation::Group1Var2; + } + } + if (hasMetadata(outstationNode, type, i)) { + if (hasMetadataClazz(outstationNode, type, i)) { + binaries[i].metadata.clazz = getPointClass(getMetadataClazz(outstationNode, type, i)); + } + if (hasMetadataVariation(outstationNode, type, i)) { + std::string metadataVariation = getMetadataVariation(outstationNode, type, i); + if (metadataVariation == "EventBinaryVariation::Group2Var1") { + binaries[i].metadata.variation = EventBinaryVariation::Group2Var1; + } else if (metadataVariation == "EventBinaryVariation::Group2Var2") { + binaries[i].metadata.variation = EventBinaryVariation::Group2Var2; + } else if (metadataVariation == "EventBinaryVariation::Group2Var3") { + binaries[i].metadata.variation = EventBinaryVariation::Group2Var3; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataVariation[" << metadataVariation << "]" << std::endl; + } + } + } } - for (int i = 0; i < analogs; i++) { + } + void configureDoubleBinaries(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& doubleBinaries) { + const char* type = "doubleBinaries"; + for (int i = 0; i < doubleBinaries.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticDoubleBinaryVariation::Group3Var2") { + doubleBinaries[i].variation = StaticDoubleBinaryVariation::Group3Var2; + } + } + if (hasMetadata(outstationNode, type, i)) { + if (hasMetadataClazz(outstationNode, type, i)) { + doubleBinaries[i].metadata.clazz = getPointClass(getMetadataClazz(outstationNode, type, i)); + } + if (hasMetadataVariation(outstationNode, type, i)) { + std::string metadataVariation = getMetadataVariation(outstationNode, type, i); + if (metadataVariation == "EventDoubleBinaryVariation::Group4Var1") { + doubleBinaries[i].metadata.variation = EventDoubleBinaryVariation::Group4Var1; + } else if (metadataVariation == "EventDoubleBinaryVariation::Group4Var2") { + doubleBinaries[i].metadata.variation = EventDoubleBinaryVariation::Group4Var2; + } else if (metadataVariation == "EventDoubleBinaryVariation::Group4Var3") { + doubleBinaries[i].metadata.variation = EventDoubleBinaryVariation::Group4Var3; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataVariation[" << metadataVariation << "]" << std::endl; + } + } + } } - for (int i = 0; i < counters; i++) { + } + void configureAnalogs(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& analogs) { + const char* type = "analogs"; + for (int i = 0; i < analogs.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticAnalogVariation::Group30Var1") { + analogs[i].variation = StaticAnalogVariation::Group30Var1; + } else if (variation == "StaticAnalogVariation::Group30Var2") { + analogs[i].variation = StaticAnalogVariation::Group30Var2; + } else if (variation == "StaticAnalogVariation::Group30Var3") { + analogs[i].variation = StaticAnalogVariation::Group30Var3; + } else if (variation == "StaticAnalogVariation::Group30Var4") { + analogs[i].variation = StaticAnalogVariation::Group30Var4; + } else if (variation == "StaticAnalogVariation::Group30Var5") { + analogs[i].variation = StaticAnalogVariation::Group30Var5; + } else if (variation == "StaticAnalogVariation::Group30Var6") { + analogs[i].variation = StaticAnalogVariation::Group30Var6; + } + } + if (hasMetadata(outstationNode, type, i)) { + if (hasMetadataClazz(outstationNode, type, i)) { + analogs[i].metadata.clazz = getPointClass(getMetadataClazz(outstationNode, type, i)); + } + if (hasMetadataVariation(outstationNode, type, i)) { + std::string metadataVariation = getMetadataVariation(outstationNode, type, i); + if (metadataVariation == "EventAnalogVariation::Group32Var1") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var1; + } else if (metadataVariation == "EventAnalogVariation::Group32Var2") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var2; + } else if (metadataVariation == "EventAnalogVariation::Group32Var3") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var3; + } else if (metadataVariation == "EventAnalogVariation::Group32Var4") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var4; + } else if (metadataVariation == "EventAnalogVariation::Group32Var5") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var5; + } else if (metadataVariation == "EventAnalogVariation::Group32Var6") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var6; + } else if (metadataVariation == "EventAnalogVariation::Group32Var7") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var7; + } else if (metadataVariation == "EventAnalogVariation::Group32Var8") { + analogs[i].metadata.variation = EventAnalogVariation::Group32Var8; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataVariation[" << metadataVariation << "]" << std::endl; + } + } + } } - for (int i = 0; i < frozenCounters; i++) { - + } + void configureCounters(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& counters) { + const char* type = "counters"; + for (int i = 0; i < counters.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticCounterVariation::Group20Var1") { + counters[i].variation = StaticCounterVariation::Group20Var1; + } else if (variation == "StaticCounterVariation::Group20Var2") { + counters[i].variation = StaticCounterVariation::Group20Var2; + } else if (variation == "StaticCounterVariation::Group20Var5") { + counters[i].variation = StaticCounterVariation::Group20Var5; + } else if (variation == "StaticCounterVariation::Group20Var6") { + counters[i].variation = StaticCounterVariation::Group20Var6; + } + } + if (hasMetadata(outstationNode, type, i)) { + if (hasMetadataClazz(outstationNode, type, i)) { + counters[i].metadata.clazz = getPointClass(getMetadataClazz(outstationNode, type, i)); + } + if (hasMetadataVariation(outstationNode, type, i)) { + std::string metadataVariation = getMetadataVariation(outstationNode, type, i); + if (metadataVariation == "EventCounterVariation::Group22Var1") { + counters[i].metadata.variation = EventCounterVariation::Group22Var1; + } else if (metadataVariation == "EventCounterVariation::Group22Var2") { + counters[i].metadata.variation = EventCounterVariation::Group22Var2; + } else if (metadataVariation == "EventCounterVariation::Group22Var5") { + counters[i].metadata.variation = EventCounterVariation::Group22Var5; + } else if (metadataVariation == "EventCounterVariation::Group22Var6") { + counters[i].metadata.variation = EventCounterVariation::Group22Var6; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataVariation[" << metadataVariation << "]" << std::endl; + } + } + } } - for (int i = 0; i < binaryOutputStatii; i++) { - + } + void configureFrozenCounters(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& frozenCounters) { + const char* type = "frozenCounters"; + for (int i = 0; i < frozenCounters.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticFrozenCounterVariation::Group21Var1") { + frozenCounters[i].variation = StaticFrozenCounterVariation::Group21Var1; + } else if (variation == "StaticFrozenCounterVariation::Group21Var2") { + frozenCounters[i].variation = StaticFrozenCounterVariation::Group21Var2; + } else if (variation == "StaticFrozenCounterVariation::Group21Var5") { + frozenCounters[i].variation = StaticFrozenCounterVariation::Group21Var5; + } else if (variation == "StaticFrozenCounterVariation::Group21Var6") { + frozenCounters[i].variation = StaticFrozenCounterVariation::Group21Var6; + } else if (variation == "StaticFrozenCounterVariation::Group21Var9") { + frozenCounters[i].variation = StaticFrozenCounterVariation::Group21Var9; + } else if (variation == "StaticFrozenCounterVariation::Group21Var10") { + frozenCounters[i].variation = StaticFrozenCounterVariation::Group21Var10; + } + } + if (hasMetadata(outstationNode, type, i)) { + if (hasMetadataClazz(outstationNode, type, i)) { + frozenCounters[i].metadata.clazz = getPointClass(getMetadataClazz(outstationNode, type, i)); + } + if (hasMetadataVariation(outstationNode, type, i)) { + std::string metadataVariation = getMetadataVariation(outstationNode, type, i); + if (metadataVariation == "EventFrozenCounterVariation::Group23Var1") { + frozenCounters[i].metadata.variation = EventFrozenCounterVariation::Group23Var1; + } else if (metadataVariation == "EventFrozenCounterVariation::Group23Var2") { + frozenCounters[i].metadata.variation = EventFrozenCounterVariation::Group23Var2; + } else if (metadataVariation == "EventFrozenCounterVariation::Group23Var5") { + frozenCounters[i].metadata.variation = EventFrozenCounterVariation::Group23Var5; + } else if (metadataVariation == "EventFrozenCounterVariation::Group23Var6") { + frozenCounters[i].metadata.variation = EventFrozenCounterVariation::Group23Var6; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataVariation[" << metadataVariation << "]" << std::endl; + } + } + } } - for (int i = 0; i < analogOutputStatii; i++) { - + } + void configureBinaryOutputStatii(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& binaryOutputStatii) { + const char* type = "binaryOutputStatii"; + for (int i = 0; i < binaryOutputStatii.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticBinaryOutputStatusVariation::Group10Var2") { + binaryOutputStatii[i].variation = StaticBinaryOutputStatusVariation::Group10Var2; + } + } + if (hasMetadata(outstationNode, type, i)) { + if (hasMetadataClazz(outstationNode, type, i)) { + binaryOutputStatii[i].metadata.clazz = getPointClass(getMetadataClazz(outstationNode, type, i)); + } + if (hasMetadataVariation(outstationNode, type, i)) { + std::string metadataVariation = getMetadataVariation(outstationNode, type, i); + if (metadataVariation == "EventBinaryOutputStatusVariation::Group11Var1") { + binaryOutputStatii[i].metadata.variation = EventBinaryOutputStatusVariation::Group11Var1; + } else if (metadataVariation == "EventBinaryOutputStatusVariation::Group11Var2") { + binaryOutputStatii[i].metadata.variation = EventBinaryOutputStatusVariation::Group11Var2; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataVariation[" << metadataVariation << "]" << std::endl; + } + } + } } - for (int i = 0; i < timeAndIntervals; i++) { + } + void configureAnalogOutputStatii(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& analogOutputStatii) { + const char* type = "analogOutputStatii"; + for (int i = 0; i < analogOutputStatii.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticAnalogOutputStatusVariation::Group40Var1") { + analogOutputStatii[i].variation = StaticAnalogOutputStatusVariation::Group40Var1; + } else if (variation == "StaticAnalogOutputStatusVariation::Group40Var2") { + analogOutputStatii[i].variation = StaticAnalogOutputStatusVariation::Group40Var2; + } else if (variation == "StaticAnalogOutputStatusVariation::Group40Var3") { + analogOutputStatii[i].variation = StaticAnalogOutputStatusVariation::Group40Var3; + } else if (variation == "StaticAnalogOutputStatusVariation::Group40Var4") { + analogOutputStatii[i].variation = StaticAnalogOutputStatusVariation::Group40Var4; + } + } + if (hasMetadata(outstationNode, type, i)) { + if (hasMetadataClazz(outstationNode, type, i)) { + analogOutputStatii[i].metadata.clazz = getPointClass(getMetadataClazz(outstationNode, type, i)); + } + if (hasMetadataVariation(outstationNode, type, i)) { + std::string metadataVariation = getMetadataVariation(outstationNode, type, i); + if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var1") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var1; + } else if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var2") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var2; + } else if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var3") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var3; + } else if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var4") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var4; + } else if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var5") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var5; + } else if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var6") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var6; + } else if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var7") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var7; + } else if (metadataVariation == "EventAnalogOutputStatusVariation::Group42Var8") { + analogOutputStatii[i].metadata.variation = EventAnalogOutputStatusVariation::Group42Var8; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataVariation[" << metadataVariation << "]" << std::endl; + } + } + } + } + } + void configureTimeAndIntervals(YAML::Node& outstationNode, openpal::ArrayView, uint16_t>& timeAndIntervals) { + const char* type = "timeAndIntervals"; + for (int i = 0; i < timeAndIntervals.Size(); i++) { + if (hasVariation(outstationNode, type, i)) { + std::string variation = getVariation(outstationNode, type, i); + if (variation == "StaticTimeAndIntervalVariation::Group50Var4") { + timeAndIntervals[i].variation = StaticTimeAndIntervalVariation::Group50Var4; + } + } + } + } + bool hasVariation(YAML::Node& outstationNode, const char* type, int idx) { + return outstationNode["measDB"][type][idx]["variation"]; + } + std::string getVariation(YAML::Node& outstationNode, const char* type, int idx) { + return outstationNode["measDB"][type][idx]["variation"].as(); + } + bool hasMetadata(YAML::Node& outstationNode, const char* type, int idx) { + return outstationNode["measDB"][type][idx]["metadata"]; + } + bool hasMetadataClazz(YAML::Node& outstationNode, const char* type, int idx) { + return outstationNode["measDB"][type][idx]["metadata"]["clazz"]; + } + std::string getMetadataClazz(YAML::Node& outstationNode, const char* type, int idx) { + return outstationNode["measDB"][type][idx]["metadata"]["clazz"].as(); + } + bool hasMetadataVariation(YAML::Node& outstationNode, const char* type, int idx) { + return outstationNode["measDB"][type][idx]["metadata"]["variation"]; + } + std::string getMetadataVariation(YAML::Node& outstationNode, const char* type, int idx) { + return outstationNode["measDB"][type][idx]["metadata"]["variation"].as(); + } + PointClass getPointClass(std::string metadataClazz) { + if (metadataClazz == "PointClass::Class0") { + return PointClass::Class0; + } else if (metadataClazz == "PointClass::Class1") { + return PointClass::Class1; + } else if (metadataClazz == "PointClass::Class2") { + return PointClass::Class2; + } else if (metadataClazz == "PointClass::Class3") { + return PointClass::Class3; + } else { + std::cerr << "OutstationAppConfig: unable to map metadataClazz[" << metadataClazz << "] defaulting to PointClass::Class1" << std::endl; + return PointClass::Class1; } } YAML::Node configNode_; -}; +} +; From 381aab5f8e926e0f718f23a08b147a4c6f40baa1 Mon Sep 17 00:00:00 2001 From: Ben Grindy Date: Wed, 14 Oct 2015 16:26:51 -0500 Subject: [PATCH 32/32] BAT-67 - shutdown DNP3Manager cleanly --- cpp/outstation/OutstationApp.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp index 499aebb099..2a491e0d6d 100644 --- a/cpp/outstation/OutstationApp.cpp +++ b/cpp/outstation/OutstationApp.cpp @@ -54,11 +54,14 @@ void signal_handler(int signal_number) { */ class OutstationApp { public: + OutstationApp() : + manager_(1) { + } + void start(OutstationAppConfig& config) { - DNP3Manager manager(1); - manager.AddLogSubscriber(&ConsoleLogger::Instance()); + manager_.AddLogSubscriber(&ConsoleLogger::Instance()); - IChannel* pChannel = manager.AddTCPServer(config.getMasterId(), levels::NORMAL, ChannelRetry::Default(), config.getMasterHost(), + IChannel* pChannel = manager_.AddTCPServer(config.getMasterId(), levels::NORMAL, ChannelRetry::Default(), config.getMasterHost(), config.getMasterPort()); pChannel->AddStateListener([](ChannelState state) { @@ -82,10 +85,11 @@ class OutstationApp { } void stop() { - // TODO shutdown correctly, consider DNP3Manager.shutdown() io_service_.stop(); + manager_.Shutdown(); } private: + DNP3Manager manager_; boost::asio::io_service io_service_; };