diff --git a/.cproject b/.cproject new file mode 100644 index 0000000000..636e313787 --- /dev/null +++ b/.cproject @@ -0,0 +1,3211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + make + -j6 + all + true + true + false + + + make + -j6 + am--refresh + true + true + false + + + make + -j6 + check + true + true + false + + + make + -j6 + clean + true + true + false + + + make + -j6 + clean-cscope + true + true + false + + + make + -j6 + clean-libLTLIBRARIES + true + true + false + + + make + -j6 + clean-libtool + true + true + false + + + make + -j6 + cpp/examples/master/masterdemo-DemoMain.o + true + true + false + + + make + -j6 + cpp/examples/master/masterdemo-DemoMain.obj + true + true + false + + + make + -j6 + cpp/examples/outstation/outstationdemo-DemoMain.o + true + true + false + + + make + -j6 + cpp/examples/outstation/outstationdemo-DemoMain.obj + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-LinkLayerRouter.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-PhysicalLayerMonitor.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/impl/libasiodnp3_la-PhysicalLayerMonitorStates.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-BlockingCommandCallback.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ChangeSet.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ChannelSet.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-ConsoleLogger.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DefaultMasterApplication.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DestructorHook.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DNP3Channel.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-DNP3Manager.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-HeaderTypes.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MasterStackImpl.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MeasUpdate.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-MultidropTaskLock.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-OutstationStackImpl.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-PrintingSOEHandler.lo + true + true + false + + + make + -j6 + cpp/libs/asiodnp3/src/asiodnp3/libasiodnp3_la-StackActionHandler.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-ASIOExecutor.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-ASIOSerialHelpers.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-IOServiceThreadPool.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-LogFanoutHandler.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerBase.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerBaseTCP.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerSerial.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerTCPClient.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-PhysicalLayerTCPServer.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-SerialTypes.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-TimerASIO.lo + true + true + false + + + make + -j6 + cpp/libs/asiopal/src/asiopal/libasiopal_la-UTCTimeSource.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AnalogCommandEvent.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AnalogOutput.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUBuilders.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHandlerBase.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHeader.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUHeaderParser.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDULogging.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUParser.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDURequest.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUResponse.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-APDUWrapper.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-AppControlField.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-BinaryCommandEvent.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-BitReader.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ClassField.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ControlRelayOutputBlock.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-EventTriggers.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-FunctionHelpers.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-GroupVariationRecord.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-IINField.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-MeasurementTypes.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-ObjectWriter.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-OctetData.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/app/libopendnp3_la-TimeAndInterval.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-ChannelState.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-CommandStatus.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-ControlCode.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-DoubleBit.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-FunctionCode.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-GroupVariation.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-IntervalUnits.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-LinkFunction.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-PointClass.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-QualifierCode.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-TaskCompletion.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/gen/libopendnp3_la-TimeSyncMode.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-CRC.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-IOpenDelayStrategy.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkFrame.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkHeader.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkLayer.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-LinkLayerParser.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-PriLinkLayerStates.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-SecLinkLayerStates.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/link/libopendnp3_la-ShiftableBuffer.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-AssignClassTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ClearRestartTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandMarshaller.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandResponse.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-CommandTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-DisableUnsolicitedTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-EnableUnsolicitedTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-EventScanTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-IMasterState.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-IMasterTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ISOEHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-ITaskLock.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-Master.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterContext.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterParams.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterScan.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterScheduler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MasterTasks.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-MeasurementHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-PollTaskBase.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-SerialTimeSyncTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-StartupIntegrityPoll.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-UserPollTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/master/libopendnp3_la-WriteTask.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group1.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group10.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group11.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group12.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group13.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group2.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group20.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group21.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group22.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group23.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group3.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group30.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group32.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group4.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group40.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group41.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group42.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group43.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group50.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group51.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/objects/libopendnp3_la-Group52.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ApplicationIIN.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-AssignClassHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ClassBasedRequestHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-CommandActionAdapter.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-CommandResponseHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-Database.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-DatabaseBuffers.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-DatabaseConfigView.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventBuffer.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventBufferConfig.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventCount.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-EventWriter.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-IINHelpers.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-IOutstationApplication.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-Outstation.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationContext.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationParams.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationSolicitedStates.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-OutstationUnsolicitedStates.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ReadHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-ResponseContext.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SelectedRanges.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SimpleCommandHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-SOERecord.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-StaticBuffers.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-StaticLoadFunctions.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/outstation/libopendnp3_la-WriteHandler.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportLayer.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportRx.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportStack.lo + true + true + false + + + make + -j6 + cpp/libs/opendnp3/src/opendnp3/transport/libopendnp3_la-TransportTx.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/container/libopenpal_la-DynamicBuffer.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/container/libopenpal_la-ReadBufferView.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/container/libopenpal_la-WriteBufferView.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/executor/libopenpal_la-Action0.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/executor/libopenpal_la-Erasure.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/executor/libopenpal_la-MonotonicTimestamp.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/executor/libopenpal_la-TimeDuration.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/logging/libopenpal_la-LogEntry.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/logging/libopenpal_la-Logger.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/logging/libopenpal_la-LogRoot.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/logging/libopenpal_la-StringFormatting.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/serialization/libopenpal_la-ByteSerialization.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/serialization/libopenpal_la-UInt48LE.lo + true + true + false + + + make + -j6 + cpp/libs/openpal/src/openpal/util/libopenpal_la-Limits.lo + true + true + false + + + 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 + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-APDUHelpers.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-APDUHexBuilders.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-APDUHexBuilders.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-BufferHelpers.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-BufferHelpers.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-BufferTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-BufferTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-CatchTestStart.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-CatchTestStart.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-CopyableBuffer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-CopyableBuffer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-DNPHelpers.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-DNPHelpers.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-HexConversions.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-HexConversions.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerRouterTest.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerRouterTest.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerTest.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LinkLayerTest.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LogTester.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LogTester.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LoopbackPhysicalLayer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LoopbackPhysicalLayer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LowerLayerToPhysAdapter.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-LowerLayerToPhysAdapter.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MasterTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MasterTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockExecutor.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockExecutor.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockFrameSink.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockFrameSink.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockLowerLayer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockLowerLayer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayerMonitor.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockPhysicalLayerMonitor.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockTransportLayer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockTransportLayer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockUpperLayer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-MockUpperLayer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-OutstationTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-OutstationTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-PhysBaseTest.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-PhysBaseTest.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-PhysLoopback.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-PhysLoopback.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-PhysTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-PhysTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-ProtocolUtil.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-ProtocolUtil.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-RandomizedBuffer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-RandomizedBuffer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-SerialTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-SerialTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-StopWatch.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-StopWatch.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUParsing.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUParsing.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUWriting.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestAPDUWriting.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestASIO.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestASIO.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestASIOThreadPool.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestASIOThreadPool.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestCastLongLongDouble.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestCastLongLongDouble.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestCRC.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestCRC.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestDatabase.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestDatabase.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestDNP3Manager.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestDNP3Manager.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestIndexSearch.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestIndexSearch.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLazyCollection.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLazyCollection.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkFrame.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkFrame.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayerRouter.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkLayerRouter.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkReceiver.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkReceiver.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkRoute.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLinkRoute.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLog.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestLog.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMaster.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMaster.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterAssignClass.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterAssignClass.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterCommandRequests.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterCommandRequests.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterMultidrop.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterMultidrop.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterUnsolBehaviors.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestMasterUnsolBehaviors.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestObjectASIO.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestObjectASIO.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstation.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstation.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationAssignClass.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationAssignClass.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationCommandResponses.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationCommandResponses.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationEventResponses.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationEventResponses.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationUnsolicitedResponses.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestOutstationUnsolicitedResponses.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncBase.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncBase.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncSerial.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncSerial.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncTCP.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerAsyncTCP.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerLoopback.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerLoopback.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerMonitor.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestPhysicalLayerMonitor.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestSerialization.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestSerialization.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestShiftableBuffer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestShiftableBuffer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTime.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTime.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTimers.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTimers.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLayer.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLayer.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLoopback.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTransportLoopback.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTypes.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestTypes.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestUtil.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestUtil.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestWriteConversions.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TestWriteConversions.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-Timeout.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-Timeout.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportIntegrationStack.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportIntegrationStack.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportLoopbackTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportLoopbackTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportScalabilityTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportScalabilityTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportStackPair.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportStackPair.obj + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportTestObject.o + true + true + false + + + make + -j6 + cpp/tests/opendnp3tests/src/dnp3test-TransportTestObject.obj + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-CatchTestStart.o + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-CatchTestStart.obj + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-FunctionalTestSuite.o + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-FunctionalTestSuite.obj + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-LinkedListTestSuite.o + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-LinkedListTestSuite.obj + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-ManagedPointerTestSuite.o + true + true + false + + + make + -j6 + cpp/tests/openpaltests/src/openpaltest-ManagedPointerTestSuite.obj + true + true + false + + + make + -j6 + cscope + true + true + false + + + make + -j6 + cscope.files + true + true + false + + + make + -j6 + cscopelist + true + true + false + + + make + -j6 + ctags + true + true + false + + + make + -j6 + dist + true + true + false + + + make + -j6 + dist-all + true + true + false + + + make + -j6 + dist-bzip2 + true + true + false + + + make + -j6 + dist-gzip + true + true + false + + + make + -j6 + dist-lzip + true + true + false + + + make + -j6 + dist-shar + true + true + false + + + make + -j6 + dist-tarZ + true + true + false + + + make + -j6 + dist-xz + true + true + false + + + make + -j6 + dist-zip + true + true + false + + + make + -j6 + distcheck + true + true + false + + + make + -j6 + distclean + true + true + false + + + make + -j6 + distclean-compile + true + true + false + + + make + -j6 + distclean-libtool + true + true + false + + + make + -j6 + distclean-tags + true + true + false + + + make + -j6 + distcleancheck + true + true + false + + + make + -j6 + distdir + true + true + false + + + make + -j6 + distuninstallcheck + true + true + false + + + make + -j6 + dvi + true + true + false + + + make + -j6 + html + true + true + false + + + make + -j6 + info + true + true + false + + + make + -j6 + install + true + true + false + + + make + -j6 + install-data + true + true + false + + + make + -j6 + install-dvi + true + true + false + + + make + -j6 + install-exec + true + true + false + + + make + -j6 + install-html + true + true + false + + + make + -j6 + install-info + true + true + false + + + make + -j6 + install-libLTLIBRARIES + true + true + false + + + make + -j6 + install-man + true + true + false + + + make + -j6 + install-pdf + true + true + false + + + make + -j6 + install-ps + true + true + false + + + make + -j6 + install-strip + true + true + false + + + make + -j6 + installcheck + true + true + false + + + make + -j6 + installdirs + true + true + false + + + make + -j6 + libasiodnp3.la + true + true + false + + + make + -j6 + libasiopal.la + true + true + false + + + make + -j6 + libopendnp3.la + true + true + false + + + make + -j6 + libopenpal.la + true + true + false + + + make + -j6 + maintainer-clean + true + true + false + + + make + -j6 + Makefile + true + true + false + + + make + -j6 + mostlyclean + true + true + false + + + make + -j6 + mostlyclean-compile + true + true + false + + + make + -j6 + mostlyclean-libtool + true + true + false + + + make + -j6 + pdf + true + true + false + + + make + -j6 + ps + true + true + false + + + make + -j6 + tags + true + true + false + + + make + -j6 + uninstall + true + true + false + + + make + -j6 + uninstall-libLTLIBRARIES + true + true + false + + + + diff --git a/.gitignore b/.gitignore index 56d753b686..fb42c96d45 100755 --- a/.gitignore +++ b/.gitignore @@ -48,6 +48,7 @@ libosslcrypto.* libtestlib.* libclangfuzzer.* libdnp3mocks.* +yaml-cpp.* #test suites testopenpal @@ -55,6 +56,7 @@ testopendnp3 testasiodnp3 testosslcrypto testsecauth +testoutstation #demos master-demo @@ -144,3 +146,11 @@ coverage.info cpp/demos/cover.info cpp/demos/cover_html/ +# eclipse generated files +RemoteSystemsTempFiles +.metadata +.settings +.autotools + +# apps +/outstation diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..8c62de9316 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,12 @@ +[submodule "thirdparty/asio"] + path = thirdparty/asio + url = ../asio/ + branch = develop +[submodule "thirdparty/rapidjson"] + path = thirdparty/rapidjson + url = ../rapidjson + branch = develop +[submodule "thirdparty/yaml-cpp"] + path = thirdparty/yaml-cpp + url = ../yaml-cpp + branch = develop 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 + + diff --git a/CMakeLists.txt b/CMakeLists.txt index d18fe63d1a..398334d0b9 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,23 @@ if(SECAUTH OR DNP3_TLS) endif() +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) + find_package(Boost 1.54.0 COMPONENTS system thread program_options) + + if(Boost_FOUND) + include_directories(${Boost_INCLUDE_DIRS}) + endif() + +endif() + if(WIN32) set_property(GLOBAL PROPERTY USE_FOLDERS ON) #allows the creation of solution folders @@ -93,17 +112,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 +194,15 @@ 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 yaml-cpp asiodnp3 ${PTHREAD} ${Boost_LIBRARIES}) + #set_target_properties(outstation PROPERTIES FOLDER demos) + +endif() + if(DEMO) # ----- master demo executable ----- @@ -279,6 +298,11 @@ 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}) + target_link_libraries (testoutstation yaml-cpp) endif() diff --git a/README.md b/README.md index 120ac0d2f1..0105b63cb7 100755 --- a/README.md +++ b/README.md @@ -16,5 +16,25 @@ 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. + +Ubuntu 14.04 Dependencies +========================= +```sudo apt-get install cmake libasio-dev libboost-all-dev +``` + +Build Steps +=========== +```bash +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/cpp/examples/master/main.cpp b/cpp/examples/master/main.cpp index 7ca6f2cb3c..1e3e795755 100644 --- a/cpp/examples/master/main.cpp +++ b/cpp/examples/master/main.cpp @@ -74,13 +74,11 @@ 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)); @@ -104,45 +102,52 @@ int main(int argc, char* argv[]) char cmd; std::cin >> cmd; - switch(cmd) + switch (cmd) { - case('a') : + 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) } - ); + 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) + case ('r'): { - 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'): + 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'): + case ('i'): integrityScan.Demand(); break; - case('e'): + case ('e'): exceptionScan.Demand(); break; - case('c'): + case ('c'): + { + ControlRelayOutputBlock crob(ControlCode::LATCH_ON); + pMaster->SelectAndOperate(crob, 0, PrintingCommandCallback::Get()); + break; + } + case ('o'): { - ControlRelayOutputBlock crob(ControlCode::LATCH_ON); - pMaster->SelectAndOperate(crob, 0, PrintingCommandCallback::Get()); + // This is an example of synchronously doing a control operation + AnalogOutputInt16 analogOutInt16(4242); + pMaster->SelectAndOperate(analogOutInt16, 0, PrintingCommandCallback::Get()); break; } default: @@ -150,7 +155,7 @@ int main(int argc, char* argv[]) break; } } - while(true); + while (true); return 0; } diff --git a/cpp/outstation/AsyncCommand.cpp b/cpp/outstation/AsyncCommand.cpp new file mode 100644 index 0000000000..6dd3c39623 --- /dev/null +++ b/cpp/outstation/AsyncCommand.cpp @@ -0,0 +1,66 @@ +/** + * 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(const ControlRelayOutputBlock* crob, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(crob), 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, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(), aoInt16_(), aoInt32_(aoInt32), 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, const char* id, uint16_t idx) : + id_(id), idx_(idx), crob_(), aoInt16_(), aoInt32_(), aoFloat32_(), aoDouble64_(aoDouble64) { + } + + const char* Id() { + return id_; + } + + uint16_t Idx() { + return idx_; + } + const ControlRelayOutputBlock* CROB() { + return crob_; + } + const AnalogOutputInt16* AOInt16() { + return aoInt16_; + } + const AnalogOutputInt32* AOInt32() { + return aoInt32_; + } + const AnalogOutputFloat32* AOFloat32() { + return aoFloat32_; + } + const AnalogOutputDouble64* AODouble64() { + return aoDouble64_; + } + +private: + const char* id_; + uint16_t idx_; + 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 new file mode 100644 index 0000000000..e928d076b4 --- /dev/null +++ b/cpp/outstation/AsyncCommandHandler.cpp @@ -0,0 +1,69 @@ +/** + * 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" + +// TODO validate requests before returning CommandStatus +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) { + queue_.push(new AsyncCommand(&command, id_, 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) { + queue_.push(new AsyncCommand(&command, id_, 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) { + queue_.push(new AsyncCommand(&command, id_, 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) { + queue_.push(new AsyncCommand(&command, id_, 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) { + queue_.push(new AsyncCommand(&command, id_, aIndex)); + return CommandStatus::SUCCESS; +} + +void AsyncCommandHandler::Start() { + // Do nothing +} + +void AsyncCommandHandler::End() { + // Do nothing +} + diff --git a/cpp/outstation/AsyncCommandHandler.h b/cpp/outstation/AsyncCommandHandler.h new file mode 100644 index 0000000000..cf5cad714a --- /dev/null +++ b/cpp/outstation/AsyncCommandHandler.h @@ -0,0 +1,58 @@ +/** + * 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_ASYNCCOMMANDHANDLER_H +#define OPENDNP3_ASYNCCOMMANDHANDLER_H + +#include + +#include +#include +#include + +using namespace opendnp3; + +/** + * Custom command handler for asynchronously receiving AsyncCommands + * through blocking queue operations on a separate thread. + */ +class AsyncCommandHandler: public ICommandHandler { +public: + AsyncCommandHandler(const char* id, AsyncCommandQueue& commandQueue); + + CommandStatus Select(const ControlRelayOutputBlock& command, uint16_t index) override final; + CommandStatus Operate(const ControlRelayOutputBlock& command, uint16_t index) 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 AnalogOutputInt32& command, uint16_t index) override final; + CommandStatus Operate(const AnalogOutputInt32& command, uint16_t index) override final; + + CommandStatus Select(const AnalogOutputFloat32& command, uint16_t index) override final; + CommandStatus Operate(const AnalogOutputFloat32& command, uint16_t index) override final; + + CommandStatus Select(const AnalogOutputDouble64& command, uint16_t index) override final; + CommandStatus Operate(const AnalogOutputDouble64& command, uint16_t index) override final; + +protected: + virtual void Start() override; + virtual void End() override; + +private: + 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 new file mode 100644 index 0000000000..4199500f87 --- /dev/null +++ b/cpp/outstation/JSONTCPSession.cpp @@ -0,0 +1,246 @@ +/** + * 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; + +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, std::map& outstations) : + socket_(std::move(socket)), outstations_ { outstations } { + } + + void start() { + read(); + } + + void write(AsyncCommand* command) { + StringBuffer out_json_sb = toJSONStringBuffer(command); + + // Get JSON string length, convert size to int64_t and write in character stream + int64_t json_size = out_json_sb.GetSize(); + + // 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(out_data, sizeof(out_data)), [this, self](boost::system::error_code ec, std::size_t length) { + // TODO handle errors + }); + } + + bool is_active() { + return socket_.is_open(); + } + +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() { + // 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; + 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)); + if (data_sz_ < 0) { + /** TODO warn in log **/ + data_sz_ = 0; + } + 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 > (size_t) data_sz_ ? data_sz_: buf_rem; + memcpy(data_, &buf_[buf_pos], size); + data_pos_ = size; + buf_pos += size; + } 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; + memcpy(data_ + data_pos_, &buf_[buf_pos], size); + buf_pos += size; + } + /** if data_sz has been transferred, process JSON object */ + if (data_pos_ == (size_t) 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) { + /** read() returned EOF, shutdown entire socket **/ + socket_.shutdown(socket_.shutdown_both); + socket_.close(); + } 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(); + writer.String("id"); + writer.String(command->Id()); + writer.String("index"); + writer.Int(command->Idx()); + if (command->AOInt16() != NULL) { + writer.String("type"); + 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(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(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(AD64); + 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(CROB); + writer.String("value"); + writer.Int(static_cast(command->CROB()->functionCode)); + writer.String("status"); + writer.Int(static_cast(command->CROB()->status)); + } else { + std::cerr << "JSONTCPSession: command not recognized" << std::endl; + } + writer.EndObject(); + 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()) { + 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: object id not found in outstations map" << std::endl; + } + } else { + std::cerr << "JSONTCPSession: object missing id or type attribute" << std::endl; + } + } else { + std::cerr << "JSONTCPSession: not an object" << std::endl; + } + } + + enum { + buf_sz = 1024 + + }; + char buf_[buf_sz]; + + char* data_ = NULL; + size_t data_pos_ = 0; + int64_t data_sz_ = 0; + + tcp::socket socket_; + std::map outstations_; +} +; diff --git a/cpp/outstation/OutstationApp.cpp b/cpp/outstation/OutstationApp.cpp new file mode 100644 index 0000000000..2a491e0d6d --- /dev/null +++ b/cpp/outstation/OutstationApp.cpp @@ -0,0 +1,127 @@ +/** + * 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 "AsyncCommandQueue.cpp" +#include "AsyncCommandHandler.cpp" +#include "OutstationAppConfig.cpp" +#include "OutstationJSONTCPServer.cpp" + +namespace po = boost::program_options; + +using namespace opendnp3; +using namespace openpal; + +std::condition_variable g_signal_cond; +std::mutex g_signal_mutex; +volatile std::sig_atomic_t g_signal; + +void signal_handler(int signal_number) { + std::unique_lock lock(g_signal_mutex); + g_signal = 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: + OutstationApp() : + manager_(1) { + } + + void start(OutstationAppConfig& config) { + manager_.AddLogSubscriber(&ConsoleLogger::Instance()); + + 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; + }); + + AsyncCommandQueue commandQueue; + std::map outstations; + for (int i = 0; i < config.getOutstationsCount(); i++) { + outstations.insert(std::pair(config.getOutstationId(i), config.configureOutstation(i, pChannel, commandQueue))); + } + + /** configurations were successful, start all outstations */ + for (std::pair outstation : outstations) { + outstation.second->Enable(); + } + + OutstationJSONTCPServer s(io_service_, 3384, outstations, commandQueue); + io_service_.run(); + + } + + void stop() { + io_service_.stop(); + manager_.Shutdown(); + } +private: + DNP3Manager manager_; + 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; + } + + OutstationAppConfig appConfig(confFile); + if (!appConfig.isValid()) + return 1; + + OutstationApp app; + std::thread t([&app, &appConfig] {app.start(appConfig);}); + t.detach(); + + std::unique_lock lock(g_signal_mutex); + while (!g_signal) { + g_signal_cond.wait(lock); + } + app.stop(); + return 0; +} diff --git a/cpp/outstation/OutstationAppConfig.cpp b/cpp/outstation/OutstationAppConfig.cpp new file mode 100644 index 0000000000..7905c3816c --- /dev/null +++ b/cpp/outstation/OutstationAppConfig.cpp @@ -0,0 +1,409 @@ +#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: + 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, AsyncCommandQueue& queue) { + /** 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; + } + 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(outstationNode, pOutstation->GetConfigView()); + 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(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); + } + } + + 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; + } + } + } + } + } + + 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; + } + } + } + } + } + + 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; + } + } + } + } + } + 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; + } + } + } + } + } + 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; + } + } + } + } + } + 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; + } + } + } + } + } + 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_; +} +; diff --git a/cpp/outstation/OutstationJSONTCPServer.cpp b/cpp/outstation/OutstationJSONTCPServer.cpp new file mode 100644 index 0000000000..d81501a9c8 --- /dev/null +++ b/cpp/outstation/OutstationJSONTCPServer.cpp @@ -0,0 +1,80 @@ +/** + * 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 "JSONTCPSession.cpp" + +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, std::map& outstations, AsyncCommandQueue& queue) : + outstations_ { outstations }, queue_(queue), acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), socket_(io_service) { + start(); + } + +private: + void start() { + // TODO shutdown thread correctly on SIGINT + std::thread t([this] {subscribe();}); + t.detach(); + accept(); + } + + void subscribe() { + while (true) { + std::shared_ptr command = queue_.pop(); + std::set>::iterator it; + for (it = sessions_.begin(); it != sessions_.end();) { + if (it->get()->is_active()) { + it->get()->write(command.get()); + it++; + } else { + sessions_.erase(it++); + } + } + } + } + + void accept() { + acceptor_.async_accept(socket_, [this](boost::system::error_code error) + { + if (!error) + { + std::shared_ptr pSession = std::make_shared(std::move(socket_), outstations_); + sessions_.insert(pSession); + pSession->start(); + } else { + std::cerr << "Unable to create TCP Session " << error << std::endl; + } + accept(); + }); + } + + std::map outstations_; + std::set> sessions_; + AsyncCommandQueue& queue_; + + tcp::acceptor acceptor_; + tcp::socket socket_; +}; diff --git a/cpp/tests/outstation/demo.yml b/cpp/tests/outstation/demo.yml new file mode 100644 index 0000000000..911d60fdca --- /dev/null +++ b/cpp/tests/outstation/demo.yml @@ -0,0 +1,35 @@ +master: + id: demomaster + levels: normal + channelRetry: default + host: 0.0.0.0 + port: 20000 +jsonServer: + port: 3384 +outstations: + - id: demostation + localAddr: 10 + remoteAddr: 1 + measDB: + analogs: + - idx: 0 + variation: StaticAnalogVariation::Group30Var5 + metadata: + - clazz: PointClass::Class2 + - variation: EventAnalogVariation::Group32Var7 + - idx: 1 + - idx: 2 + binaries: + - idx: 0 + doubleBinaries: + - idx: 0 + counters: + - idx: 0 + frozenCounters: + - idx: 0 + binaryOutputStatii: + - idx: 0 + analogOutputStatii: + - idx: 0 + timeAndIntervals: + - idx: 0 diff --git a/cpp/tests/outstation/jsontcp-sample.bin b/cpp/tests/outstation/jsontcp-sample.bin new file mode 100644 index 0000000000..fa517f0e09 Binary files /dev/null and b/cpp/tests/outstation/jsontcp-sample.bin differ diff --git a/cpp/tests/outstation/jsontcp-test.sh b/cpp/tests/outstation/jsontcp-test.sh new file mode 100755 index 0000000000..2ac56e1cc2 --- /dev/null +++ b/cpp/tests/outstation/jsontcp-test.sh @@ -0,0 +1,5 @@ +#!/bin/sh +SCRIPT=$(readlink -f $0) +SCRIPTPATH=`dirname $SCRIPT` + +cat $SCRIPTPATH/jsontcp-sample.bin | nc localhost 3384 diff --git a/cpp/tests/outstation/src/OutstationAppTests.cpp b/cpp/tests/outstation/src/OutstationAppTests.cpp new file mode 100644 index 0000000000..b1594cc9a7 --- /dev/null +++ b/cpp/tests/outstation/src/OutstationAppTests.cpp @@ -0,0 +1,46 @@ +/** + * 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 + +#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, (char *) "testout", 123); + REQUIRE(ac->AOInt16() == NULL); +} + +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; +} + + 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 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 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