diff --git a/cpp/lib/include/opendnp3/outstation/IOutstationApplication.h b/cpp/lib/include/opendnp3/outstation/IOutstationApplication.h index dfd2471ec4..77f8e65219 100644 --- a/cpp/lib/include/opendnp3/outstation/IOutstationApplication.h +++ b/cpp/lib/include/opendnp3/outstation/IOutstationApplication.h @@ -127,6 +127,16 @@ class IOutstationApplication : public ILinkListener, public IDnpTimeSource return 65535; } + /// This method notifies that application code that an expected CONFIRM has been + /// received, and events may have cleared from the event buffer. It is informational + /// only. + /// + /// @param is_unsolicited true, if the confirm is for an unsolicited response, false for a solicited response + /// @param num_class1 number of Class 1 events remaining in the event buffer after processing the confirm + /// @param num_class2 number of Class 2 events remaining in the event buffer after processing the confirm + /// @param num_class3 number of Class 3 events remaining in the event buffer after processing the confirm + virtual void OnConfirmProcessed(bool is_unsolicited, uint32_t num_class1, uint32_t num_class2, uint32_t num_class3) {} + virtual ~IOutstationApplication() = default; }; diff --git a/cpp/lib/src/outstation/OutstationStates.cpp b/cpp/lib/src/outstation/OutstationStates.cpp index 09af1e9077..e2e4612e0b 100644 --- a/cpp/lib/src/outstation/OutstationStates.cpp +++ b/cpp/lib/src/outstation/OutstationStates.cpp @@ -99,6 +99,14 @@ OutstationState& StateSolicitedConfirmWait::OnConfirm(OContext& ctx, const Parse ctx.eventBuffer.ClearWritten(); ctx.lastBroadcastMessageReceived.clear(); + // information the application about the confirm + ctx.application->OnConfirmProcessed( + false, + ctx.eventBuffer.NumEvents(EventClass::EC1), + ctx.eventBuffer.NumEvents(EventClass::EC2), + ctx.eventBuffer.NumEvents(EventClass::EC3) + ); + if (ctx.rspContext.HasSelection()) { return ctx.ContinueMultiFragResponse(request.addresses, AppSeqNum(request.header.control.SEQ).Next()); @@ -170,6 +178,14 @@ OutstationState& StateUnsolicitedConfirmWait::OnConfirm(OContext& ctx, const Par ctx.confirmTimer.cancel(); ctx.lastBroadcastMessageReceived.clear(); + // information the application about the confirm + ctx.application->OnConfirmProcessed( + true, + ctx.eventBuffer.NumEvents(EventClass::EC1), + ctx.eventBuffer.NumEvents(EventClass::EC2), + ctx.eventBuffer.NumEvents(EventClass::EC3) + ); + if (ctx.unsol.completedNull) { ctx.eventBuffer.ClearWritten(); diff --git a/cpp/lib/src/outstation/event/EventBuffer.cpp b/cpp/lib/src/outstation/event/EventBuffer.cpp index 31854f6dde..e9b9fc5dc6 100644 --- a/cpp/lib/src/outstation/event/EventBuffer.cpp +++ b/cpp/lib/src/outstation/event/EventBuffer.cpp @@ -225,4 +225,9 @@ void EventBuffer::ClearWritten() this->storage.ClearWritten(); } +uint32_t EventBuffer::NumEvents(EventClass ec) const +{ + return this->storage.NumUnwritten(ec); +} + } // namespace opendnp3 diff --git a/cpp/lib/src/outstation/event/EventBuffer.h b/cpp/lib/src/outstation/event/EventBuffer.h index 23e48577a6..eaa9b38271 100644 --- a/cpp/lib/src/outstation/event/EventBuffer.h +++ b/cpp/lib/src/outstation/event/EventBuffer.h @@ -82,6 +82,9 @@ class EventBuffer final : public IEventReceiver, public IEventSelector, public I void SelectAllByClass(const ClassField& clazz); + uint32_t NumEvents(EventClass ec) const; + + private: bool overflow = false; EventStorage storage; diff --git a/cpp/tests/dnp3mocks/include/dnp3mocks/MockOutstationApplication.h b/cpp/tests/dnp3mocks/include/dnp3mocks/MockOutstationApplication.h index c660f8ee32..a1e25e2e37 100644 --- a/cpp/tests/dnp3mocks/include/dnp3mocks/MockOutstationApplication.h +++ b/cpp/tests/dnp3mocks/include/dnp3mocks/MockOutstationApplication.h @@ -25,6 +25,14 @@ #include #include +struct ConfirmResult +{ + bool is_unsolicited; + uint32_t num_class1; + uint32_t num_class2; + uint32_t num_class3; +}; + class MockOutstationApplication : public opendnp3::IOutstationApplication { public: @@ -119,6 +127,16 @@ class MockOutstationApplication : public opendnp3::IOutstationApplication return warmRestartTimeDelay; } + void OnConfirmProcessed(bool is_unsolicited, uint32_t num_class1, uint32_t num_class2, uint32_t num_class3) final + { + ConfirmResult confirm{}; + confirm.is_unsolicited = is_unsolicited; + confirm.num_class1 = num_class1; + confirm.num_class2 = num_class2; + confirm.num_class3 = num_class3; + this->confirms.push_back(confirm); + } + void SetTime(opendnp3::DNPTime time) { this->currentTime = time; @@ -143,6 +161,7 @@ class MockOutstationApplication : public opendnp3::IOutstationApplication std::deque timestamps; std::deque> classAssignments; std::deque> timeAndIntervals; + std::deque confirms; }; #endif diff --git a/cpp/tests/unit/TestOutstationEventResponses.cpp b/cpp/tests/unit/TestOutstationEventResponses.cpp index fe5d6e5105..809546f4d9 100644 --- a/cpp/tests/unit/TestOutstationEventResponses.cpp +++ b/cpp/tests/unit/TestOutstationEventResponses.cpp @@ -137,6 +137,13 @@ TEST_CASE(SUITE("EventBufferOverflowAndClear")) t.OnTxReady(); t.SendToOutstation(hex::SolicitedConfirm(1)); + // Check that the confirm is reported to the IOutstationApplication + REQUIRE(t.application->confirms.size() == 1); + REQUIRE(t.application->confirms[0].is_unsolicited == false); + REQUIRE(t.application->confirms[0].num_class1 == 1); + REQUIRE(t.application->confirms[0].num_class2 == 0); + REQUIRE(t.application->confirms[0].num_class3 == 0); + t.SendToOutstation("C0 01"); REQUIRE("C0 81 82 00" == t.lower->PopWriteAsHex()); } diff --git a/cpp/tests/unit/TestOutstationUnsolicitedResponses.cpp b/cpp/tests/unit/TestOutstationUnsolicitedResponses.cpp index 92ef126c3a..21346c47ab 100644 --- a/cpp/tests/unit/TestOutstationUnsolicitedResponses.cpp +++ b/cpp/tests/unit/TestOutstationUnsolicitedResponses.cpp @@ -277,6 +277,13 @@ TEST_CASE(SUITE("UnsolData")) t.OnTxReady(); t.SendToOutstation(hex::UnsolConfirm(0)); + // Check that the confirm is reported to the IOutstationApplication + REQUIRE(t.application->confirms.size() == 1); + REQUIRE(t.application->confirms[0].is_unsolicited == true); + REQUIRE(t.application->confirms[0].num_class1 == 0); + REQUIRE(t.application->confirms[0].num_class2 == 0); + REQUIRE(t.application->confirms[0].num_class3 == 0); + // do a transaction before the layer comes online to prove that the null transaction // is occuring before unsol data is sent t.Transaction([](IUpdateHandler& db) { db.Update(Binary(false, Flags(0x01)), 2); }); @@ -287,6 +294,13 @@ TEST_CASE(SUITE("UnsolData")) t.OnTxReady(); t.SendToOutstation(hex::UnsolConfirm(1)); REQUIRE(t.lower->PopWriteAsHex().empty()); + + // Check that the confirm is reported to the IOutstationApplication + REQUIRE(t.application->confirms.size() == 2); + REQUIRE(t.application->confirms[1].is_unsolicited == true); + REQUIRE(t.application->confirms[1].num_class1 == 0); + REQUIRE(t.application->confirms[1].num_class2 == 0); + REQUIRE(t.application->confirms[1].num_class3 == 0); } TEST_CASE(SUITE("UnsolEventBufferOverflow")) @@ -316,6 +330,13 @@ TEST_CASE(SUITE("UnsolEventBufferOverflow")) t.OnTxReady(); t.SendToOutstation(hex::UnsolConfirm(1)); + // Check that the confirm is reported to the IOutstationApplication + REQUIRE(t.application->confirms.size() == 2); + REQUIRE(t.application->confirms[1].is_unsolicited == true); + REQUIRE(t.application->confirms[1].num_class1 == 0); + REQUIRE(t.application->confirms[1].num_class2 == 0); + REQUIRE(t.application->confirms[1].num_class3 == 0); + REQUIRE(t.lower->PopWriteAsHex().empty()); } @@ -345,12 +366,26 @@ TEST_CASE(SUITE("UnsolMultiFragments")) t.OnTxReady(); t.SendToOutstation(hex::UnsolConfirm(1)); + // Check that the confirm is reported to the IOutstationApplication + REQUIRE(t.application->confirms.size() == 2); + REQUIRE(t.application->confirms[1].is_unsolicited == true); + REQUIRE(t.application->confirms[1].num_class1 == 1); + REQUIRE(t.application->confirms[1].num_class2 == 0); + REQUIRE(t.application->confirms[1].num_class3 == 0); + // should immediately try to send another unsol packet REQUIRE(t.lower->PopWriteAsHex() == "F2 82 80 00 20 01 28 01 00 03 00 01 0D 00 00 00"); t.OnTxReady(); t.SendToOutstation(hex::UnsolConfirm(2)); REQUIRE(t.lower->PopWriteAsHex().empty()); + + // Check that the confirm is reported to the IOutstationApplication + REQUIRE(t.application->confirms.size() == 3); + REQUIRE(t.application->confirms[2].is_unsolicited == true); + REQUIRE(t.application->confirms[2].num_class1 == 0); + REQUIRE(t.application->confirms[2].num_class2 == 0); + REQUIRE(t.application->confirms[2].num_class3 == 0); } void WriteDuringUnsol(bool beforeTx) diff --git a/dotnet/CLRAdapter/src/OutstationApplicationAdapter.cpp b/dotnet/CLRAdapter/src/OutstationApplicationAdapter.cpp index 396e0cb434..e5255bdbd6 100644 --- a/dotnet/CLRAdapter/src/OutstationApplicationAdapter.cpp +++ b/dotnet/CLRAdapter/src/OutstationApplicationAdapter.cpp @@ -129,6 +129,10 @@ namespace Automatak return proxy->WarmRestart(); } + void OutstationApplicationAdapter::OnConfirmProcessed(bool is_unsolicited, uint32_t num_class1, uint32_t num_class2, uint32_t num_class3) + { + proxy->OnConfirmProcessed(is_unsolicited, num_class1, num_class2, num_class3); + } } } } diff --git a/dotnet/CLRAdapter/src/OutstationApplicationAdapter.h b/dotnet/CLRAdapter/src/OutstationApplicationAdapter.h index 7a0a5c60cb..0b02cc646f 100644 --- a/dotnet/CLRAdapter/src/OutstationApplicationAdapter.h +++ b/dotnet/CLRAdapter/src/OutstationApplicationAdapter.h @@ -40,11 +40,11 @@ namespace Automatak public: OutstationApplicationAdapter(Automatak::DNP3::Interface::IOutstationApplication^ proxy); - virtual void OnStateChange(opendnp3::LinkStatus value) override; + virtual void OnStateChange(opendnp3::LinkStatus value) override final; - virtual void OnUnknownDestinationAddress(uint16_t destination) override; + virtual void OnUnknownDestinationAddress(uint16_t destination) override final; - virtual void OnUnknownSourceAddress(uint16_t source) override; + virtual void OnUnknownSourceAddress(uint16_t source) override final; virtual void OnKeepAliveInitiated() override final; @@ -70,9 +70,11 @@ namespace Automatak virtual opendnp3::RestartMode WarmRestartSupport() const override final; - virtual uint16_t ColdRestart(); + virtual uint16_t ColdRestart() override final; - virtual uint16_t WarmRestart(); + virtual uint16_t WarmRestart() override final; + + virtual void OnConfirmProcessed(bool is_unsolicited, uint32_t num_class1, uint32_t num_class2, uint32_t num_class3) override final; private: diff --git a/dotnet/CLRInterface/src/IOutstationApplication.cs b/dotnet/CLRInterface/src/IOutstationApplication.cs index a9715ef66f..86a7364405 100644 --- a/dotnet/CLRInterface/src/IOutstationApplication.cs +++ b/dotnet/CLRInterface/src/IOutstationApplication.cs @@ -84,6 +84,16 @@ ApplicationIIN ApplicationIndications /// @return number of seconds or milliseconds until restart is complete. The value /// is interpreted based on the Restart Mode returned from WarmRestartSupport() UInt16 WarmRestart(); + + /// This method notifies that application code that an expected CONFIRM has been + /// received, and events may have cleared from the event buffer. It is informational + /// only. + /// + /// true, if the confirm is for an unsolicited response, false for a solicited response + /// number of Class 1 events remaining in the event buffer after processing the confirm + /// number of Class 1 events remaining in the event buffer after processing the confirm + /// number of Class 3 events remaining in the event buffer after processing the confirm + void OnConfirmProcessed(bool is_unsolicited, uint num_class1, uint num_class2, uint num_class3); } public class DefaultOutstationApplication : IOutstationApplication @@ -177,6 +187,8 @@ ushort IOutstationApplication.WarmRestart() return UInt16.MaxValue; } + void IOutstationApplication.OnConfirmProcessed(bool is_unsolicited, uint num_class1, uint num_class2, uint num_class3) {} + DNPTime IDnpTimeSource.Now() { return DNPTime.Unset; diff --git a/java/bindings/src/main/java/com/automatak/dnp3/OutstationApplication.java b/java/bindings/src/main/java/com/automatak/dnp3/OutstationApplication.java index 6ed178933d..f563fe3154 100644 --- a/java/bindings/src/main/java/com/automatak/dnp3/OutstationApplication.java +++ b/java/bindings/src/main/java/com/automatak/dnp3/OutstationApplication.java @@ -87,6 +87,18 @@ public interface OutstationApplication extends LinkStatusListener { */ int warmRestart(); + /** + * This method notifies that application code that an expected CONFIRM has been + * received, and events may have cleared from the event buffer. It is informational + * only. + * + * @param isUnsolicited true if the confirm is for an unsolicited response, false for a solicited response + * @param numClass1 number of Class 1 events remaining in the event buffer after processing the confirm + * @param numClass2 number of Class 2 events remaining in the event buffer after processing the confirm + * @param numClass3 number of Class 3 events remaining in the event buffer after processing the confirm + */ + void onConfirmProcessed(boolean isUnsolicited, long numClass1, long numClass2, long numClass3); + /** * Return the current time and the synchronization status. * This value is used when freezing counters diff --git a/java/bindings/src/main/java/com/automatak/dnp3/mock/DefaultOutstationApplication.java b/java/bindings/src/main/java/com/automatak/dnp3/mock/DefaultOutstationApplication.java index bdcb240fd5..643c8e79c7 100644 --- a/java/bindings/src/main/java/com/automatak/dnp3/mock/DefaultOutstationApplication.java +++ b/java/bindings/src/main/java/com/automatak/dnp3/mock/DefaultOutstationApplication.java @@ -91,6 +91,11 @@ public int warmRestart() { return 65535; } + @Override + public void onConfirmProcessed(boolean isUnsolicited, long numClass1, long numClass2, long numClass3) { + // do nothing in the default implementation + } + @Override public DNPTime now() { diff --git a/java/cpp/adapters/OutstationApplicationAdapter.cpp b/java/cpp/adapters/OutstationApplicationAdapter.cpp index 0c4ec11731..04aed78665 100644 --- a/java/cpp/adapters/OutstationApplicationAdapter.cpp +++ b/java/cpp/adapters/OutstationApplicationAdapter.cpp @@ -96,6 +96,12 @@ uint16_t OutstationApplicationAdapter::WarmRestart() return static_cast(JCache::OutstationApplication.warmRestart(env, proxy)); } +void OutstationApplicationAdapter::OnConfirmProcessed(bool is_unsolicited, uint32_t num_class1, uint32_t num_class2, uint32_t num_class3) +{ + const auto env = JNI::GetEnv(); + JCache::OutstationApplication.onConfirmProcessed(env, proxy, is_unsolicited, num_class1, num_class2, num_class3); +} + DNPTime OutstationApplicationAdapter::Now() { const auto env = JNI::GetEnv(); diff --git a/java/cpp/adapters/OutstationApplicationAdapter.h b/java/cpp/adapters/OutstationApplicationAdapter.h index 39fa3f25a6..4bbf5f6207 100644 --- a/java/cpp/adapters/OutstationApplicationAdapter.h +++ b/java/cpp/adapters/OutstationApplicationAdapter.h @@ -52,6 +52,8 @@ class OutstationApplicationAdapter : public opendnp3::IOutstationApplication uint16_t WarmRestart() override; + void OnConfirmProcessed(bool is_unsolicited, uint32_t num_class1, uint32_t num_class2, uint32_t num_class3) override; + opendnp3::DNPTime Now() override; private: diff --git a/java/cpp/jni/JNICommandHandler.cpp b/java/cpp/jni/JNICommandHandler.cpp index eb65303c0d..474678f53c 100644 --- a/java/cpp/jni/JNICommandHandler.cpp +++ b/java/cpp/jni/JNICommandHandler.cpp @@ -48,19 +48,19 @@ namespace jni this->method1 = env->GetMethodID(this->clazz, "end", "()V"); if(!this->method1) return false; - this->method2 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputDouble64;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); + this->method2 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputInt16;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); if(!this->method2) return false; - this->method3 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputFloat32;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); + this->method3 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputInt32;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); if(!this->method3) return false; - this->method4 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputInt16;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); + this->method4 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputFloat32;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); if(!this->method4) return false; - this->method5 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputInt32;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); + this->method5 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/ControlRelayOutputBlock;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); if(!this->method5) return false; - this->method6 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/ControlRelayOutputBlock;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); + this->method6 = env->GetMethodID(this->clazz, "operate", "(Lcom/automatak/dnp3/AnalogOutputDouble64;ILcom/automatak/dnp3/Database;Lcom/automatak/dnp3/enums/OperateType;)Lcom/automatak/dnp3/enums/CommandStatus;"); if(!this->method6) return false; this->method7 = env->GetMethodID(this->clazz, "select", "(Lcom/automatak/dnp3/AnalogOutputInt32;I)Lcom/automatak/dnp3/enums/CommandStatus;"); @@ -96,27 +96,27 @@ namespace jni env->CallVoidMethod(instance, this->method1); } - LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputDouble64 arg0, jint arg1, JDatabase arg2, JOperateType arg3) + LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt16 arg0, jint arg1, JDatabase arg2, JOperateType arg3) { return LocalRef(env, env->CallObjectMethod(instance, this->method2, arg0, arg1, arg2, arg3)); } - LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputFloat32 arg0, jint arg1, JDatabase arg2, JOperateType arg3) + LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt32 arg0, jint arg1, JDatabase arg2, JOperateType arg3) { return LocalRef(env, env->CallObjectMethod(instance, this->method3, arg0, arg1, arg2, arg3)); } - LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt16 arg0, jint arg1, JDatabase arg2, JOperateType arg3) + LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputFloat32 arg0, jint arg1, JDatabase arg2, JOperateType arg3) { return LocalRef(env, env->CallObjectMethod(instance, this->method4, arg0, arg1, arg2, arg3)); } - LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt32 arg0, jint arg1, JDatabase arg2, JOperateType arg3) + LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JControlRelayOutputBlock arg0, jint arg1, JDatabase arg2, JOperateType arg3) { return LocalRef(env, env->CallObjectMethod(instance, this->method5, arg0, arg1, arg2, arg3)); } - LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JControlRelayOutputBlock arg0, jint arg1, JDatabase arg2, JOperateType arg3) + LocalRef CommandHandler::operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputDouble64 arg0, jint arg1, JDatabase arg2, JOperateType arg3) { return LocalRef(env, env->CallObjectMethod(instance, this->method6, arg0, arg1, arg2, arg3)); } diff --git a/java/cpp/jni/JNICommandHandler.h b/java/cpp/jni/JNICommandHandler.h index ef6efbb309..28bda3fbdd 100644 --- a/java/cpp/jni/JNICommandHandler.h +++ b/java/cpp/jni/JNICommandHandler.h @@ -54,11 +54,11 @@ namespace jni // methods void begin(JNIEnv* env, JCommandHandler instance); void end(JNIEnv* env, JCommandHandler instance); - LocalRef operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputDouble64 arg0, jint arg1, JDatabase arg2, JOperateType arg3); - LocalRef operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputFloat32 arg0, jint arg1, JDatabase arg2, JOperateType arg3); LocalRef operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt16 arg0, jint arg1, JDatabase arg2, JOperateType arg3); LocalRef operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt32 arg0, jint arg1, JDatabase arg2, JOperateType arg3); + LocalRef operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputFloat32 arg0, jint arg1, JDatabase arg2, JOperateType arg3); LocalRef operate(JNIEnv* env, JCommandHandler instance, JControlRelayOutputBlock arg0, jint arg1, JDatabase arg2, JOperateType arg3); + LocalRef operate(JNIEnv* env, JCommandHandler instance, JAnalogOutputDouble64 arg0, jint arg1, JDatabase arg2, JOperateType arg3); LocalRef select(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt32 arg0, jint arg1); LocalRef select(JNIEnv* env, JCommandHandler instance, JAnalogOutputInt16 arg0, jint arg1); LocalRef select(JNIEnv* env, JCommandHandler instance, JAnalogOutputFloat32 arg0, jint arg1); diff --git a/java/cpp/jni/JNIOutstationApplication.cpp b/java/cpp/jni/JNIOutstationApplication.cpp index 433ced141e..e3469b04a9 100644 --- a/java/cpp/jni/JNIOutstationApplication.cpp +++ b/java/cpp/jni/JNIOutstationApplication.cpp @@ -54,24 +54,27 @@ namespace jni this->method3 = env->GetMethodID(this->clazz, "now", "()Lcom/automatak/dnp3/DNPTime;"); if(!this->method3) return false; - this->method4 = env->GetMethodID(this->clazz, "recordClassAssignment", "(Lcom/automatak/dnp3/enums/AssignClassType;Lcom/automatak/dnp3/enums/PointClass;II)V"); + this->method4 = env->GetMethodID(this->clazz, "onConfirmProcessed", "(ZJJJ)V"); if(!this->method4) return false; - this->method5 = env->GetMethodID(this->clazz, "supportsAssignClass", "()Z"); + this->method5 = env->GetMethodID(this->clazz, "recordClassAssignment", "(Lcom/automatak/dnp3/enums/AssignClassType;Lcom/automatak/dnp3/enums/PointClass;II)V"); if(!this->method5) return false; - this->method6 = env->GetMethodID(this->clazz, "supportsWriteAbsoluteTime", "()Z"); + this->method6 = env->GetMethodID(this->clazz, "supportsAssignClass", "()Z"); if(!this->method6) return false; - this->method7 = env->GetMethodID(this->clazz, "warmRestart", "()I"); + this->method7 = env->GetMethodID(this->clazz, "supportsWriteAbsoluteTime", "()Z"); if(!this->method7) return false; - this->method8 = env->GetMethodID(this->clazz, "warmRestartSupport", "()Lcom/automatak/dnp3/enums/RestartMode;"); + this->method8 = env->GetMethodID(this->clazz, "warmRestart", "()I"); if(!this->method8) return false; - this->method9 = env->GetMethodID(this->clazz, "writeAbsoluteTime", "(J)Z"); + this->method9 = env->GetMethodID(this->clazz, "warmRestartSupport", "()Lcom/automatak/dnp3/enums/RestartMode;"); if(!this->method9) return false; + this->method10 = env->GetMethodID(this->clazz, "writeAbsoluteTime", "(J)Z"); + if(!this->method10) return false; + return true; } @@ -100,34 +103,39 @@ namespace jni return LocalRef(env, env->CallObjectMethod(instance, this->method3)); } - void OutstationApplication::recordClassAssignment(JNIEnv* env, JOutstationApplication instance, JAssignClassType arg0, JPointClass arg1, jint arg2, jint arg3) + void OutstationApplication::onConfirmProcessed(JNIEnv* env, JOutstationApplication instance, jboolean arg0, jlong arg1, jlong arg2, jlong arg3) { env->CallVoidMethod(instance, this->method4, arg0, arg1, arg2, arg3); } + void OutstationApplication::recordClassAssignment(JNIEnv* env, JOutstationApplication instance, JAssignClassType arg0, JPointClass arg1, jint arg2, jint arg3) + { + env->CallVoidMethod(instance, this->method5, arg0, arg1, arg2, arg3); + } + jboolean OutstationApplication::supportsAssignClass(JNIEnv* env, JOutstationApplication instance) { - return env->CallBooleanMethod(instance, this->method5); + return env->CallBooleanMethod(instance, this->method6); } jboolean OutstationApplication::supportsWriteAbsoluteTime(JNIEnv* env, JOutstationApplication instance) { - return env->CallBooleanMethod(instance, this->method6); + return env->CallBooleanMethod(instance, this->method7); } jint OutstationApplication::warmRestart(JNIEnv* env, JOutstationApplication instance) { - return env->CallIntMethod(instance, this->method7); + return env->CallIntMethod(instance, this->method8); } LocalRef OutstationApplication::warmRestartSupport(JNIEnv* env, JOutstationApplication instance) { - return LocalRef(env, env->CallObjectMethod(instance, this->method8)); + return LocalRef(env, env->CallObjectMethod(instance, this->method9)); } jboolean OutstationApplication::writeAbsoluteTime(JNIEnv* env, JOutstationApplication instance, jlong arg0) { - return env->CallBooleanMethod(instance, this->method9, arg0); + return env->CallBooleanMethod(instance, this->method10, arg0); } } } diff --git a/java/cpp/jni/JNIOutstationApplication.h b/java/cpp/jni/JNIOutstationApplication.h index e06115c3eb..17c719b3ca 100644 --- a/java/cpp/jni/JNIOutstationApplication.h +++ b/java/cpp/jni/JNIOutstationApplication.h @@ -56,6 +56,7 @@ namespace jni LocalRef coldRestartSupport(JNIEnv* env, JOutstationApplication instance); LocalRef getApplicationIIN(JNIEnv* env, JOutstationApplication instance); LocalRef now(JNIEnv* env, JOutstationApplication instance); + void onConfirmProcessed(JNIEnv* env, JOutstationApplication instance, jboolean arg0, jlong arg1, jlong arg2, jlong arg3); void recordClassAssignment(JNIEnv* env, JOutstationApplication instance, JAssignClassType arg0, JPointClass arg1, jint arg2, jint arg3); jboolean supportsAssignClass(JNIEnv* env, JOutstationApplication instance); jboolean supportsWriteAbsoluteTime(JNIEnv* env, JOutstationApplication instance); @@ -78,6 +79,7 @@ namespace jni jmethodID method7 = nullptr; jmethodID method8 = nullptr; jmethodID method9 = nullptr; + jmethodID method10 = nullptr; }; } }