From 7ec2bb9a762251dff5ce625c76b3b1caee3e2eb2 Mon Sep 17 00:00:00 2001 From: Thomas Boyer-Chammard <49786685+thomas-bc@users.noreply.github.com> Date: Wed, 23 Jul 2025 10:41:24 -0700 Subject: [PATCH] Rename ActiveLogger to EventManager (#3920) * Rename ActiveLogger to EventManager * Spelling and link fixes * Formatting and CMake API * Remove UT_AUTO_HELPERS --- .github/actions/spelling/expect.txt | 4 - .github/workflows/format-check.yml | 1 + Ref/docs/TestCases.txt | 2 +- Ref/docs/sdd.md | 4 +- Ref/test/int/ref_integration_test.py | 4 +- Svc/ActiveLogger/ActiveLogger.hpp | 17 - Svc/ActiveLogger/ActiveLoggerImpl.cpp | 205 ------ Svc/ActiveLogger/ActiveLoggerImpl.hpp | 67 -- Svc/ActiveLogger/CMakeLists.txt | 26 - Svc/ActiveLogger/changed-symbols.txt | 86 --- Svc/ActiveLogger/docs/.gitignore | 1 - Svc/ActiveLogger/docs/Checklist_Code.xlsx | Bin 29740 -> 0 bytes Svc/ActiveLogger/docs/Checklist_Design.xlsx | Bin 24983 -> 0 bytes Svc/ActiveLogger/docs/Checklist_Unit_Test.xls | Bin 51200 -> 0 bytes .../test/ut/ActiveLoggerTestMain.cpp | 156 ----- .../test/ut/ActiveLoggerTester.cpp | 625 ------------------ .../test/ut/ActiveLoggerTester.hpp | 80 --- Svc/CMakeLists.txt | 2 +- Svc/EventManager/CMakeLists.txt | 29 + Svc/EventManager/EventManager.cpp | 196 ++++++ .../EventManager.fpp} | 2 +- Svc/EventManager/EventManager.hpp | 70 ++ .../docs/img/EventManagerBDD.jpg} | Bin .../docs/img/ReceiveEvents.jpg | Bin .../docs/img/WriteEventBuffer.jpg | Bin .../docs/sdd.md | 48 +- .../test/ut/.gitignore | 0 .../test/ut/EventManagerTestMain.cpp | 143 ++++ .../test/ut/EventManagerTester.cpp | 534 +++++++++++++++ .../test/ut/EventManagerTester.hpp | 76 +++ .../test/ut/Readme.txt | 2 +- .../test/ut/RateGroupDriverImplTester.hpp | 2 +- Svc/Subtopologies/CdhCore/CdhCore.fpp | 2 +- cmake/test/src/settings.py | 2 +- cmake/test/src/test_unittests.py | 2 +- cmake/utilities.cmake | 4 +- default/config/CMakeLists.txt | 2 +- ...eLoggerImplCfg.hpp => EventManagerCfg.hpp} | 8 +- .../overview/04-cmd-evt-chn-prm.md | 4 +- docs/user-manual/overview/source-tree.md | 4 +- 40 files changed, 1092 insertions(+), 1318 deletions(-) delete mode 100644 Svc/ActiveLogger/ActiveLogger.hpp delete mode 100644 Svc/ActiveLogger/ActiveLoggerImpl.cpp delete mode 100644 Svc/ActiveLogger/ActiveLoggerImpl.hpp delete mode 100644 Svc/ActiveLogger/CMakeLists.txt delete mode 100644 Svc/ActiveLogger/changed-symbols.txt delete mode 100644 Svc/ActiveLogger/docs/.gitignore delete mode 100644 Svc/ActiveLogger/docs/Checklist_Code.xlsx delete mode 100644 Svc/ActiveLogger/docs/Checklist_Design.xlsx delete mode 100644 Svc/ActiveLogger/docs/Checklist_Unit_Test.xls delete mode 100644 Svc/ActiveLogger/test/ut/ActiveLoggerTestMain.cpp delete mode 100644 Svc/ActiveLogger/test/ut/ActiveLoggerTester.cpp delete mode 100644 Svc/ActiveLogger/test/ut/ActiveLoggerTester.hpp create mode 100644 Svc/EventManager/CMakeLists.txt create mode 100644 Svc/EventManager/EventManager.cpp rename Svc/{ActiveLogger/ActiveLogger.fpp => EventManager/EventManager.fpp} (99%) create mode 100644 Svc/EventManager/EventManager.hpp rename Svc/{ActiveLogger/docs/img/ActiveLoggerBDD.jpg => EventManager/docs/img/EventManagerBDD.jpg} (100%) rename Svc/{ActiveLogger => EventManager}/docs/img/ReceiveEvents.jpg (100%) rename Svc/{ActiveLogger => EventManager}/docs/img/WriteEventBuffer.jpg (100%) rename Svc/{ActiveLogger => EventManager}/docs/sdd.md (71%) rename Svc/{ActiveLogger => EventManager}/test/ut/.gitignore (100%) create mode 100644 Svc/EventManager/test/ut/EventManagerTestMain.cpp create mode 100644 Svc/EventManager/test/ut/EventManagerTester.cpp create mode 100644 Svc/EventManager/test/ut/EventManagerTester.hpp rename Svc/{ActiveLogger => EventManager}/test/ut/Readme.txt (80%) rename default/config/{ActiveLoggerImplCfg.hpp => EventManagerCfg.hpp} (79%) diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index 5f5ca0d62e..4555592917 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -1,9 +1,5 @@ Aadil AClass -ACTIVELOGGER -ACTIVELOGGERIMPL -ACTIVELOGGERIMPLCFG -ACTIVELOGGERIMPLTESTER ACTIVERATEGROUP ACTIVERATEGROUPCFG ACTIVERATEGROUPTESTER diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 4b03a1ff25..dfaa8dd131 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -34,6 +34,7 @@ jobs: Fw/Cmd Fw/Com Svc/Ccsds + Svc/EventManager run: | fprime-util format --check --dirs $CHECKED_DIRS shell: bash diff --git a/Ref/docs/TestCases.txt b/Ref/docs/TestCases.txt index 171ab7c3c2..3cf6c43a8e 100644 --- a/Ref/docs/TestCases.txt +++ b/Ref/docs/TestCases.txt @@ -27,7 +27,7 @@ The following test case IDs are assigned: Svc: -100 - ActiveLogger +100 - EventManager 101 - ActiveRateGroup 102 - CmdDispatcher 103 - CmdSequencer diff --git a/Ref/docs/sdd.md b/Ref/docs/sdd.md index 9cbf177d43..e10850726b 100644 --- a/Ref/docs/sdd.md +++ b/Ref/docs/sdd.md @@ -23,7 +23,7 @@ It interconnects those application components with reusable service components: |Component|Description|Link |---|---|---| -|ActiveLogger|Logs events for downlink| [SDD](../../Svc/ActiveLogger/docs/sdd.md)| +|EventManager|Logs events for downlink| [SDD](../../Svc/EventManager/docs/sdd.md)| |ActiveRateGroup|Executes a rate group by calling components| [SDD](../../Svc/ActiveRateGroup/docs/sdd.md)| |BufferManager|Manages a pool of buffers| [SDD](../../Svc/BufferManager/docs/sdd.md)| |CmdSequencer|Loads a set of commands from a binary file and executes them| [SDD](../../Svc/CmdSequencer/docs/sdd.md)| @@ -84,7 +84,7 @@ The connections for the reference deployment telemetry are as follows: ### 2.3 Logging -The logging view consists of connections for components to send events for logging to the `ActiveLogger` component. +The logging view consists of connections for components to send events for logging to the `EventManager` component. #### 2.3.1 C&DH Logging diff --git a/Ref/test/int/ref_integration_test.py b/Ref/test/int/ref_integration_test.py index ebf2fc63d0..9080231615 100644 --- a/Ref/test/int/ref_integration_test.py +++ b/Ref/test/int/ref_integration_test.py @@ -13,7 +13,7 @@ from fprime_gds.common.utils.event_severity import EventSeverity """ -This enum is includes the values of EventSeverity that can be filtered by the ActiveLogger Component +This enum is includes the values of EventSeverity that can be filtered by the EventManager Component """ FilterSeverity = Enum( "FilterSeverity", @@ -39,7 +39,7 @@ def test_is_streaming(fprime_test_api): def set_event_filter(fprime_test_api, severity, enabled): """Send command to set event filter - This helper will send a command that updates the given severity filter on the ActiveLogger + This helper will send a command that updates the given severity filter on the EventManager Component in the Ref App. Args: diff --git a/Svc/ActiveLogger/ActiveLogger.hpp b/Svc/ActiveLogger/ActiveLogger.hpp deleted file mode 100644 index e7ecdde934..0000000000 --- a/Svc/ActiveLogger/ActiveLogger.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// ====================================================================== -// ActiveLogger.hpp -// Standardization header for ActiveLogger -// ====================================================================== - -#ifndef Svc_ActiveLogger_HPP -#define Svc_ActiveLogger_HPP - -#include "Svc/ActiveLogger/ActiveLoggerImpl.hpp" - -namespace Svc { - - typedef ActiveLoggerImpl ActiveLogger; - -} - -#endif diff --git a/Svc/ActiveLogger/ActiveLoggerImpl.cpp b/Svc/ActiveLogger/ActiveLoggerImpl.cpp deleted file mode 100644 index 7e930df09b..0000000000 --- a/Svc/ActiveLogger/ActiveLoggerImpl.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/* - * TestCommand1Impl.cpp - * - * Created on: Mar 28, 2014 - * Author: tcanham - */ - -#include - -#include -#include -#include - -namespace Svc { - static_assert(std::numeric_limits::max() >= TELEM_ID_FILTER_SIZE, "TELEM_ID_FILTER_SIZE must fit within range of FwSizeType"); - typedef ActiveLogger_Enabled Enabled; - typedef ActiveLogger_FilterSeverity FilterSeverity; - - ActiveLoggerImpl::ActiveLoggerImpl(const char* name) : - ActiveLoggerComponentBase(name) - { - // set filter defaults - this->m_filterState[FilterSeverity::WARNING_HI].enabled = - FILTER_WARNING_HI_DEFAULT?Enabled::ENABLED:Enabled::DISABLED; - this->m_filterState[FilterSeverity::WARNING_LO].enabled = - FILTER_WARNING_LO_DEFAULT?Enabled::ENABLED:Enabled::DISABLED; - this->m_filterState[FilterSeverity::COMMAND].enabled = - FILTER_COMMAND_DEFAULT?Enabled::ENABLED:Enabled::DISABLED; - this->m_filterState[FilterSeverity::ACTIVITY_HI].enabled = - FILTER_ACTIVITY_HI_DEFAULT?Enabled::ENABLED:Enabled::DISABLED; - this->m_filterState[FilterSeverity::ACTIVITY_LO].enabled = - FILTER_ACTIVITY_LO_DEFAULT?Enabled::ENABLED:Enabled::DISABLED; - this->m_filterState[FilterSeverity::DIAGNOSTIC].enabled = - FILTER_DIAGNOSTIC_DEFAULT?Enabled::ENABLED:Enabled::DISABLED; - - memset(m_filteredIDs,0,sizeof(m_filteredIDs)); - - } - - ActiveLoggerImpl::~ActiveLoggerImpl() { - } - - void ActiveLoggerImpl::LogRecv_handler(FwIndexType portNum, FwEventIdType id, Fw::Time &timeTag, const Fw::LogSeverity& severity, Fw::LogBuffer &args) { - - // make sure ID is not zero. Zero is reserved for ID filter. - FW_ASSERT(id != 0); - - switch (severity.e) { - case Fw::LogSeverity::FATAL: // always pass FATAL - break; - case Fw::LogSeverity::WARNING_HI: - if (this->m_filterState[FilterSeverity::WARNING_HI].enabled == Enabled::DISABLED) { - return; - } - break; - case Fw::LogSeverity::WARNING_LO: - if (this->m_filterState[FilterSeverity::WARNING_LO].enabled == Enabled::DISABLED) { - return; - } - break; - case Fw::LogSeverity::COMMAND: - if (this->m_filterState[FilterSeverity::COMMAND].enabled == Enabled::DISABLED) { - return; - } - break; - case Fw::LogSeverity::ACTIVITY_HI: - if (this->m_filterState[FilterSeverity::ACTIVITY_HI].enabled == Enabled::DISABLED) { - return; - } - break; - case Fw::LogSeverity::ACTIVITY_LO: - if (this->m_filterState[FilterSeverity::ACTIVITY_LO].enabled == Enabled::DISABLED) { - return; - } - break; - case Fw::LogSeverity::DIAGNOSTIC: - if (this->m_filterState[FilterSeverity::DIAGNOSTIC].enabled == Enabled::DISABLED) { - return; - } - break; - default: - FW_ASSERT(0,static_cast(severity.e)); - return; - } - - // check ID filters - for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { - if ( - (m_filteredIDs[entry] == id) && - (severity != Fw::LogSeverity::FATAL) - ) { - return; - } - } - - // send event to the logger thread - this->loqQueue_internalInterfaceInvoke(id,timeTag,severity,args); - - // if connected, announce the FATAL - if (Fw::LogSeverity::FATAL == severity.e) { - if (this->isConnected_FatalAnnounce_OutputPort(0)) { - this->FatalAnnounce_out(0,id); - } - } - } - - void ActiveLoggerImpl::loqQueue_internalInterfaceHandler(FwEventIdType id, const Fw::Time &timeTag, const Fw::LogSeverity& severity, const Fw::LogBuffer &args) { - - // Serialize event - this->m_logPacket.setId(id); - this->m_logPacket.setTimeTag(timeTag); - this->m_logPacket.setLogBuffer(args); - this->m_comBuffer.resetSer(); - Fw::SerializeStatus stat = this->m_logPacket.serialize(this->m_comBuffer); - FW_ASSERT(Fw::FW_SERIALIZE_OK == stat,static_cast(stat)); - - if (this->isConnected_PktSend_OutputPort(0)) { - this->PktSend_out(0, this->m_comBuffer,0); - } - } - - void ActiveLoggerImpl::SET_EVENT_FILTER_cmdHandler(FwOpcodeType opCode, U32 cmdSeq, FilterSeverity filterLevel, Enabled filterEnable) { - this->m_filterState[filterLevel.e].enabled = filterEnable; - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK); - } - - void ActiveLoggerImpl::SET_ID_FILTER_cmdHandler( - FwOpcodeType opCode, //!< The opcode - U32 cmdSeq, //!< The command sequence number - FwEventIdType ID, - Enabled idEnabled //!< ID filter state - ) { - - if (Enabled::ENABLED == idEnabled.e) { // add ID - // search list for existing entry - for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { - if (this->m_filteredIDs[entry] == ID) { - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK); - this->log_ACTIVITY_HI_ID_FILTER_ENABLED(ID); - return; - } - } - // if not already a match, search for an open slot - for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { - if (this->m_filteredIDs[entry] == 0) { - this->m_filteredIDs[entry] = ID; - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK); - this->log_ACTIVITY_HI_ID_FILTER_ENABLED(ID); - return; - } - } - // if an empty slot was not found, send an error event - this->log_WARNING_LO_ID_FILTER_LIST_FULL(ID); - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); - } else { // remove ID - // search list for existing entry - for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { - if (this->m_filteredIDs[entry] == ID) { - this->m_filteredIDs[entry] = 0; // zero entry - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK); - this->log_ACTIVITY_HI_ID_FILTER_REMOVED(ID); - return; - } - } - // if it gets here, wasn't found - this->log_WARNING_LO_ID_FILTER_NOT_FOUND(ID); - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::EXECUTION_ERROR); - } - - } - - void ActiveLoggerImpl::DUMP_FILTER_STATE_cmdHandler( - FwOpcodeType opCode, //!< The opcode - U32 cmdSeq //!< The command sequence number - ) { - - // first, iterate through severity filters - for (FwEnumStoreType filter = 0; filter < FilterSeverity::NUM_CONSTANTS; filter++) { - FilterSeverity filterState(static_cast(filter)); - this->log_ACTIVITY_LO_SEVERITY_FILTER_STATE( - filterState, - Enabled::ENABLED == this->m_filterState[filter].enabled.e - ); - } - - // iterate through ID filter - for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { - if (this->m_filteredIDs[entry] != 0) { - this->log_ACTIVITY_HI_ID_FILTER_ENABLED(this->m_filteredIDs[entry]); - } - } - - this->cmdResponse_out(opCode,cmdSeq,Fw::CmdResponse::OK); - } - - void ActiveLoggerImpl::pingIn_handler( - const FwIndexType portNum, - U32 key - ) - { - // return key - this->pingOut_out(0,key); - } - -} // namespace Svc diff --git a/Svc/ActiveLogger/ActiveLoggerImpl.hpp b/Svc/ActiveLogger/ActiveLoggerImpl.hpp deleted file mode 100644 index d30cba6a6f..0000000000 --- a/Svc/ActiveLogger/ActiveLoggerImpl.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - * ActiveLoggerImpl.hpp - * - * Created on: Mar 28, 2014 - * Author: tcanham - */ - -#ifndef ACTIVELOGGERIMPL_HPP_ -#define ACTIVELOGGERIMPL_HPP_ - -#include -#include -#include - -namespace Svc { - - class ActiveLoggerImpl final : public ActiveLoggerComponentBase { - public: - ActiveLoggerImpl(const char* compName); //!< constructor - virtual ~ActiveLoggerImpl(); //!< destructor - protected: - private: - void LogRecv_handler(FwIndexType portNum, FwEventIdType id, Fw::Time &timeTag, const Fw::LogSeverity& severity, Fw::LogBuffer &args); - void loqQueue_internalInterfaceHandler(FwEventIdType id, const Fw::Time &timeTag, const Fw::LogSeverity& severity, const Fw::LogBuffer &args); - - void SET_EVENT_FILTER_cmdHandler( - FwOpcodeType opCode, - U32 cmdSeq, - ActiveLogger_FilterSeverity filterLevel, - ActiveLogger_Enabled filterEnabled); - - void SET_ID_FILTER_cmdHandler( - FwOpcodeType opCode, //!< The opcode - U32 cmdSeq, //!< The command sequence number - FwEventIdType ID, - ActiveLogger_Enabled idFilterEnabled //!< ID filter state - ); - - void DUMP_FILTER_STATE_cmdHandler( - FwOpcodeType opCode, //!< The opcode - U32 cmdSeq //!< The command sequence number - ); - - //! Handler implementation for pingIn - //! - void pingIn_handler( - const FwIndexType portNum, /*!< The port number*/ - U32 key /*!< Value to return to pinger*/ - ); - - // Filter state - struct t_filterState { - ActiveLogger_Enabled enabled; //~ zSL#!{s&++Q3K#?h01N;E004jxKzCOYtpyMO;Pa=63;+S7C1hvoY+~!Ir|e;G;-o|C zZevYQ2m(Zr4*>Mj|Nn0P2TR~@>bm7VJwoU;v=4aEIuiAzpoV0&?t}d*jrB92vISDs zNw8@mncW|w63CL`+f0mBHndij@8`IKS$D@S*H(3bezF%y?NlQL|K&brU)=~ky11ge zLVm?93T$p=LtqNqvSYKPG#QXz>}y7(mWA%bq-R*!{fxplAEs!DN`q|{v_*5!EZ`n2 zczRN}+yP~XEm($}9bp-onpAj8AMWQQ0U`<;Hsr0bkNTa4v#Y4A1`ve z;m|O~J-I!k8l^_S@vLa}C)4y%t_fBty&t86DU9s_acj7|&h|E&do!f<^OwCzo`F0O&g2cix9kAky1q<1G7!%yHxw7KHJ;D5tAH6 zV=lz|1Bk3E&#NApVyF%aRvvjHtM>|nX-U8RF!sd2M&v@*1_v-)R> z{C@}n{Fx_z`u?B2btg|-4$#94UkCjTn)Pu=wAI0M@m#@p=yZYCwI8DltMq@e4 zm?#gn#yX^_G>yQ4+Ld?@NRkdFnO5LxEu@gB=fZX0h)TYNaMJ_Q-+@Z{0=vDVn7Y(Z z_pvWunRkR|O!8;Kg8`1IplROVJ@o%dr7YlK+?jv@08qgJ0N{Ug6jk|DGBTBQItNJz(;P4y!DN#YVtz8uSfZE_oF3OkHJ3>_*g` zCqhY2WAz^Y)NCx9$Eh4ZWp|?2{%vz4(b=)XA@32;cgI8?0-5^=aXXQGeuQ&XQn%{A zGcc{5(>;Fag}u#_BdO5rF{Mh}0=s(*C~9N`HS-+qM(HhT*_~);n1T7J)?%8wwgjBN zb@+RgqRsGtx^|TVz~39J)%d-Osx4$R12p` z%$QsT%A@L48>uO!$}XuaV{G7JMM2%ZiMssUtgbk6y;@RBWg4ldqy{R19t$8J6Ks;x3f#=9*OVw z%O%}#)tU*!wCNYosdhJ^7XKLrEQ~7)NN5*_yv~((=-OMBK*QTxr~kuEN1~&8dQ_tj z#@3XT=rwJJOAz?%4&W169yfh_VrWvI4|hAh8;X~1ytQ`2963up&IMk(Eu7$3(`4p* zG8cKQbp+^{pb}B7*B5}^eWP&?O@YXl{`VyXz6d4E7&i{U_sV$6?zm0rjAt(PP5Eya zJFy8wS+T0wi%+`UEqn5OV&0-J6ijF6isc1_QYepou!f zGwP-+InS47WFwAD-g(1vb`Z2ek%HrwF>>wX9g;wjLe1GF40A$`?3XLxjzPGqxlY?q z!DGy~=V_3UssnDkePi6Xv@uJ@SW^bP=~Am~X{YEi$gk^$9DLnnC(;5!F8QTTy{*Sd zRlK>9g2y3`dbHxe(3KK4Z4g1|eEngkx#(eG(Ph>xziTR8HOeuFz}F7# zkx2@bN_9QWLVfV1UWo-!_1g3^M5oEj2J~+5aEW(k-Nf6G#YRo=PVBZB|c;#S>R46>=qR7{X zRMuMB-A6|xCFmp0`_7}XY&BU7oL^ZiB6E4PnwLvhYOSSru>KVu+GX<=Zk0+NVE+3U zu9hwiCQF@Gi%Y_#z>WCFCT-M@l9MA`cWOt@w#}L~o<$2oP%H%R4Xj~$Rn<$ZX*}J4 z8s0u|dR8{A>s5GL{=a-(;I@Cysn@)^1508~!XM476>Qp4`aoeiw&DiY0eaUc_M1_J zhgvwFl7@1tMH$izJa?P`@dIP3GK*Pi#}md9My|7;Lq`%T?_^71l>bueOhJ9A^0PWp zlA()RhIBnzEl{6=wUCLA2BlGEdP67{g*#Zhzt-dNi`3=ubNskjy&cGl#pmJleJM&g zl-U^e*pu~sy)^Ul{bYFCKV_rC_d^_(LjOL8&-=K$KQ+!x@Zz8Abd3$)i z#slBU0(#bQk24i6)H;B;yoGr(h1s`~ecSV9@}w-?g2@iN@IQ%_Kdc?JJxB#h*};d_ z5OppNnkx&jU(Q6J7<#mUR}dfo&73aa3UanB=ff;YGV)l62(kJ-Ym0> z_6gVnfG~2FR*mol?wLv051enuyP)e@aj)aunW$X|S*}FG|Ad?v@#fH_|I-_%iI6PR z#Q%l|Z`%BeQ_&1X{S%~Oc{`z#Cq*{#jSR=XU}23{;4sSmTM%fwzsNg{f9h}23D=kJ z$o8yX?jQ8SyP>A1=<XoN@uiZ9E9u5Z75J)^A$@j@J1Oz zhAji&uI@vZDrJV!$C>HA?u|2%X!c7(sr+C}Jm3TP84@#5#iJh$(8E3wFB3{8+k0r5|J z?oseLs#uy=+SD(9$vUQ0(Wu+*{cgLRD_+g@kT|}Ka5nD9%KjxLHd(HqS1U#_EW(5( zm<5a91k_FIQW2p6%}b(QPEm2ZDXD;W&%;a zp$&!cnRc$f6t(6b^T0M^XaXC!Yc~&canh9v6Goz1+mwUoL~RH_nsS#wWXOk$Q^1QI z!2@%(TmbcsDG&Kf7>f9S-o7@!#2V-ANSieETb_xXD^??6FVWT|QkHSGUj|Rkg?_ub zRg`B%OWv)i`fL?m1dfjIx!_s zTA1T(yuWgjJS_N;8NBwaiARt!xYHmh5e(+a_~UhZ0_KdobM&=Lm6tVRS_|*wtT$EN z^$XJ~AyepWN?`^f*Qc~JbFR+{?#T(SCAHzmwOgC-UO}su1$l#(Iy@phGJQ3T^o{@W zmS;y5kC9Ho8ZcTr#(7I1iwGCZYv=7!p<5YGzavdVkgY|98lsm=d$$X%YPZ8Bh*0LkN?bCEI%f8I6M81*yG;@)BlV- zx)Y|M`src9t^=>-9osg^!ozdW5xXOK`7avz+FMF03hnF!KtN}+%Ho5)|G+nD>xAiQ zQ`jF@hiaP=jAE&|w9eYE*pWFQ;LNJ7a7kg=4%SnR6dlHTMxTbSPc@07Okk~Hd#yvl) zZ@4^S91kf$XlNlAB|frma)oqbKCc(b_dP|A$HDjgXdd3#IVJS3xo?{fMCb zC#L%U6$Rt}6h(FXge5QojL37ySI{i)k~CUDvU?+Bh_%ogVE+>L$xFat>Dcyi!Hg@x zjEq|%EhndWSJ#V14#xyaP=G31KKsf^DLsZe&+qD7eu;t|^Vk4bZ4Lcp9$xD>Zlww- zMp+5v5)cOZ)WlrGq1X!vXK5VExsi*C1y=a<(Vp4fcH2zYqivWIG-(%mu)Dq*ft=b3 z-LEX%ci+bDxTj4g3OCvOm@hO56GgmsjF~sDm9sM_cfF!dxtONB=M3JnICP>bE3%iQ zw$3Fsa_x_<_n)BiKm8jNmp`81hyVZ(8UO$o|DhrO>c+nUY1WpCTW3e=p{xHExG|s$ zYOV>(TfY=&tLXy~Z|oys>dOU-q;bP;psB05k~MAk_Nf|86h~V{;z8E=AmB?GHEejf zRaxfq-MkJg`*7Yu3(L}VXE_0VHBhN;3wzJns>Ap5WH|*rX5^}F(s3BI^W463x*mDm z{&xM;&hE*^zw3tK@89*F_oA5CuioaX!@Kiw?V*Z&1$1N`vdBxhd3N6B!wcv43hh)q zoQ-Tx=4k1rZy0aeu9Y3?SV>l^GTcrRX|m$Dl#B7aQsYJ4G%KTv8&M;8fk=7p-n5yp zEA0HO|0nZz4x&DNi60t;Un&67_HU!T8M_wXLC=eM7fE7lWn zLtf$l)TfWf%b!>+TY^gYpCD!4PV-%`W)|$?2*d<`Z0?Vnp13HQrt$A|dZ_MM#-Y9_ zB0s}aS|_#(YAy(D094h>_PDl&bmMErFO|OV?Ad-#i)f&Efj|J$5n4D=eT~8k`cwR-AX$PY z&5{$BP<+q;gX6PW)SsndIputjcz~EW;oLTk>vB}M;I&yk9FVbdWPubL(?@~z zQaPd5mk$))%0rpYW+J+eF7kVPwhV-r8WfD43pRA8^we9m*kzQC$Hpz zq7zH3s36{#_D{hnOp$KACwCBDIY^<26=poOaWC6gy0F?9JzRr7?DWtB&PhlvbeP(% zJ7;Ee7Jjhyt>8;3c$mZh^n2q}|9GlK#$=t`kQ2Zr9+h7xD{vXg6o2Z>7D$U4ueF-q z71TnhQpy;@g?R{(tpb=i#2LT|6A9mB*{;hsWTvx7I#<8==e`m&wTVSZv8N=yq&7PC z!`~>}81G2j5oA@%etznT%psOx3Pk;1BE!0*ijl&Y@+3M5`;^p32K6D9 zCgfK{24+S!=M^L>B*}e|%$W5=h@y0g?D}B!@%icxeAeHja^fu@LQMqEa+a;ORw#fi zGwTdEO`7cV`w0;m;?b&*x$%~eqNKegUnckbnAJ3bwhh6U5xVKZ3jJ7Cgn*i;?t+!1 z7%CW9D)wE=~vA`G%<8ngeVUP^Mk^q1nN`Ac%jK{lqGV&zRNoxYr)ro*(JREVWh*cJeA7 z39Q@sGK#En8(XrE(aVzb0n5+O=0jk0CR@MVg@fo2d5FM;910-c!6qS!%AznkF$V-h zMluhorx0F{5StrYle62I0-@2)Q-O%HktK6HfMFKa0Exj&C1OUboscP{1fGI)W^@Sv zJM2#t*uf4pbA85k=q`MI2y7y>>MR$|K*Q(bky&Xcf%HN(jHd}Q1*t^A03HVTOYK_x zzHd!T`BLZ}vGEBKCUH$2hG4(v08p49MQ#BcnfYme1mwOVm=V6rczgSRR3-h~I^}^d zqy-h3-%PsEA&A>^}l5XHoTnu!w`~=g#3q)?kZEW^qNq;ImxEAx8q0)0ECEUh!LObSIZ-|s@F4ttNahrb*I}bY zf>N5-q^XID@M4b`$^ChRS&o1gERZ3r;BY=x>?ClClI7K56c=%>5Niy5=QnJpa|Hrz zcO+i1 zXSXxgSolv@R$32)+*_aqWO8){nKY)Akc<@g!%3J5FUWIt#mf~_Xt4oSz43M@95Bbd zgE&&;3#DR66skS~R*9_#+mAvLMwp)h^9IM0a4^ph5=N7dS26dJ!zah!NzUriAmxv~6l)-6eS2wV$b4HauUaYJ=5|lT%4x4WW(oce20ZT#} zW4mt<1|T4;*H><9QCo}MCt`&Zc4|Cu_TH+Bx9?YnQ{bE>BFD#&`z;B2}x}^l< zF;+*+LO>`U3imE2{ux(^&>2J)9gB;%0!_B!yijO|y`V@s+V7WNT8SiKx}3(z4Z{?A z3((|i5Ag-)ya6QAQM%q}6cUL-bht>9>cp9JXCe1?p5n_jVmnw-XY z4a2;8%Ft$q5Ah64hsnwGG)H?v693*;%f7dz!ouXEK$l%LFVv~~GKV6dxvy-!~x|o})8S zE~XN9CuqOj{S`g4gy_%_wx!fA?PZYM_ya^2nwI0^c%hW#Y${1?R;T{aWFyNYXK}p{ zYmp=5OJ#uVAcFF(e(xAF0SqJjY&umheZ_u^b>Hzl=}TvEHRPPyDLdqr2%Os%??9}z z-GdzZ$uKf+e#!uxr)@&15RO)8BXmeyeS~c_I^w{mE(}kVg4A3NwevJ;w_w+#K zbs(4C=Rf<$0@{Ps1D;P-_eCBCtiOqlibU=0sf^o)+Jgka=?{7@3CIuFztBsjiw@lO z76&_=mJS>$b-xy3j>|LDrcbj#SW*lyqHr2n`2SsOARX(24d@K3-%3XsE{TR?>kn%# zsj?JH1Qe3Kx+;IH|LHNZvFOhe;B zrW8hfCta0d**V+ncDmgdjG%WW83N;*q)_i0m3+^YhNRSg9)T(2FNS@I9gMC(Nm$5h znsS2)YO>#BzX@O@iwR-ujM5{%|090j(n|L>M&qWVXnDx}t_x$(Em`@IDkR?heCrKh z)zXnP9EjqqTuH}_Ty}#@BvFiNjT$($qn*MKT|M52d~0rmg4ns6T2TV?)6S}j;?TD_ z8#Iq~T|SOLt31Ce-zGF^3t@RP zPO+JczISD!z-yHYKBX>T2FgJT=KBA`oIjlqA^Uo%KZ;x+Ub@E~ej4-k{k$t|rLE(g zP6kvlU8~kWfOi~0_aZ5~?@{u>PPLRx987XUR*G9a;&c{D<(;r7S>xT(qRseHn-$qo zIDtTDSalNf^!S@KCsDjjq7myT+tzsxvN?g_66SA)`(_%$Vq)iT1KBzen3~r05(B&v z5f~b!b;It@S{epqQPQj-h~}yivdX?9%$Ybe21l=X+lq)nabCj{ z{%jpe5ee_X8F9NPg`;EQBhvdUiu-#+p`u(JEE8W|LtVx{5m+=o9j*d@P7M8=Xxxiv zG{k41)3{aEXb8gy7%mF;-q-g`K!t9uVv?q7Gz@T!>==zK`|0pNM2%){WqN?5B5>caiwV0c3=#3!VEWn8l7nD%w1n6G(?d=mNrP{lM z(J`Ia^ND+0ogpA>R4047E6IL0w%5~*rA23x0jpl#&-=t03{Le`x(~YRB6#cj>X2Iz zF4mK%z5y83bU23I!lZ5CPDc+j9SqcFgE$IHM)*}9`2@yPvFgYSg;oUVEf+Lo0s%LQYG zU=Gw1b_4rO{DLoXlhe!g5x47y1ZFP}-ZOF&GcE9ia2Iyt2l84k3{bcIao0XkVNFe+ zv}^_e`|J{AGF&7%GT+KBpx>K?$B6;q{_J^w`#>T10E*y$o16kFTr&QBpMH6B#Uh;R zwPA9<^@lK=_~LU`55k;Gcm9s^r0~q;q_tb`xwieohCa8lt>Dj{pVs%%D`z;`vH8Rj zStwaBrEin%%}y~D&0C{v7EoMW<#IvLMI+@+af2sz=g?H9A*{fM?}V$?%dmZJic~cJ zrzzTF_~w(gpz_FOt$WY9Nf;dw*y3w}yRQ#BZ>rt>ea4;&SbBP{?9iBavH&(Zv9*yH zTkTJVTc*lZ%f)KTrpi{&<+&4ENe=pW=GNCwZ9#`FnA*T}sI$U6mgT#hhT&)*97634 z#y~94-2X>czoY6w>l5X@dpCG@?Re9o(bTJOX3fn)XRPn zI6)#_6arDn1f)v!jFl?ON$-~jF3%m&MS2~9b}1P7H&(7qyFPVSgmL5n1H#g%E-nyVA{OETwc5&CNRNZz2ZrisbcK`C`Rr|7)JUKbBa5Y@P*2J z5qthBcGvKEt%tyia>ML2OXqo@mS1t!ejmr!w;d>XfAsSg_&>byQLtanb6)-+ZHgh#^IiQgF#6uBwbs>kMrY6 z{lJ8|Vrg+&Nwj@nr5D57#|dxpgQ#(gc?b+i)P3o5%cIUn(`tfoL{#csDJo+D=I-&B zDylJY!1g-C z18~wS3C0%Dbf2q_7+p~vG1JIqTrP}aWE)n5x_`sgNrv(0DN>m%Sz{q!>a54^xQEq> z5Q{I`NLifnowcUsUlYEEOHTv}gG-FF&Pzt~JPMmIOkQF zImVo+Bsy|^VIpW&2#R?aw1#*s9zK{`8FYtuhTEW2_tklEdPZdw!yau608oO)l$&Cx zLNih{2(NpuF0Dy{PSo0ruU6XpcO3yblPYx29uebG>p#Yc5NHnTH-QLSb?pTR=5z`vw$ z!No!NOhM>~A{9_rW373vk#2MO#iR~2tjL%w(}@<$ zRlFIME>$pZ4|8^$v>i1GNf?y5mO?Z&VGD^ic3aD9Op6f7an@bNc{QS;C5L|VDHFV~ zWk~r-rl6&yU{~Zpw9sl=TLeuU&v_r_1h%g^S1ly~u*ep4VF6z| zOkH>13U?s=9hd^ji`wj5R%q^X+hw)<5Z7c++aqdlr?p&$&0|vMBHhuD@00y=@RbPA zYrvnFYtjo(rRh9;N`}cLFc52jA`i_5+xLNnm!GnT|K#=nD3jjjhuh)*J-7d^>?W(~ z*sal{`229Ie`sJA53&~A7$2d~pmN5#;BwyNfMdwgz_gCC()``yWs{Fy7>V>Eg5*%hk4M-nnl-x&DrjQAuc~3%%3(-N`SC>G4 zrL5asN+&6Au-`e9m&kkyMqkEg^Mn_aEoCTo`)x_f@ zf}IWvPo;PY&ttQ53)*%c!3arGbmzVVFS8hq$iwip9(V5qXt`zU=CZqtfxm&$oi?`a z90gHWvp|7u3S?^wOI$Oq&}>yKiqWE_hIfOTi13N#9QevfjZqpS73Pul3ReF z(Hun9Zh9Tg?o(XpYCjnXv$+Pno)5&+#HdWB29#YFXqVSklz5abM)DID7SRkf#)i@J z%7U`zUt+|3Lh{e?zvSLE=Y_~2Ac67uZ|pyP4~v++pMK|T=J>C}$%V(}`P1h8#fgD; z*xzp-<7hXBX}?($Baw*RFFoEVyd|{t#6fpCKVZ+lCA`{Uw}nwVik{;?AAorN3i8&S zwW+#F#t6#I9>A=Ti)Iv`RpOSYQrbBZ#_ z8?CfuX`15iB`!Js%92re^&v~hGjO#z)mO@o$cxZ~uoWJs#-Ic(YPk^Cg(@{l{vD0^ z=(g$t`+N4wo1PUY1aD(avUFN?b6ki)e}Tc-Hy%qBW~gjfP|+mZ+baeLo6(?O39MU6 z4oTHfhs4~>zd~6r*R`IzU8lLMK?Y&Xgj0MURwv1o6z&VY8GSy_a;Mso9ME`)O?Rb$ zJcgH_meilgq-aF@=w^<})1LOH*gf!FXm4kw=E{>wf4}%}M${%!y#2OoA?N|${5Gev zrhtCUbXa}|?|DAfXu_Ma{gWhD>7Ok)(<6R`jT?>M!x(vn+mQ>-EFkM1 z(|;m60mD%2@Plil3IG7?e_y9?a`vz`ar(CcLt7zkjUB<4@AOM3*$t~7CbvPF@?h>=x%K`T?;swxT zOP+0;*(JtZ$yCXXcj4O`gUz!O&3>YZCd|XQyiOwj5BBHV1i_9~n71B6KMTPwsE|+J z>)jH4;AvpH>P;w31o)r7>DQV#wx1n{zkCc=0a+b!ix#b2VV4VY@f6!#%BY03C;YT& zu$7KzX=KS43cP8|PcylGe{w&k+sWYQKMq-I&8N4vOC-3RE|>vk-Lmmd={I5KBr>P@ z6xjik%<2zw#$vR5ZhlT%w{~+C7qJl@Z_p7Gr@6 zUQjxs*DK1E(GBNW@H(RPg_t>@lXOOb`8i<#5n`VKgm&>A(dhRjLZNgdbg(xi~@G$j1A8iwFa% zFIcf}ec=dBL(h!?+skLa=VjDBTX-J!h?IWcn-Wqg*`0R(w9&R?T2-_~pbyuQ5cL|S z<28>)X5IMRPCZh<#P(0enez7P(riSDG7dWoP2GJ@~AsU2yXs48Q zr(=Ni%0vyS*-1$QrY}KZ-A(I`P<_D-BfS@1ckWM)j}@gJTBW+ypvNCUQQ^;=+x%Ke zPv(rxub78TP7{uqCOY}dOlBv5;;03gkh|;+MpbiYLq3JkSoDUN5ucrfM6`SLW4j`y zs{?ajZOK0^pg^cy%}QR|^qvzgsfU53MCz!H8Q{9LOd#@16J7C@4V4HiBLn#`uULOY z5r3Us91Lk6ep8Mr$CpH7`=nfXz2PB7W`N5x`-Pm|B(qPsWw(8?tpAO$)3?26obOKI za(y)$)fC*qWE#08J%B8nuz$ne>SZ&DkrqESawRNEF{MeHdOIJ_LGb&9^t|V^M_H=S z>gHy+*Yn%&dZyzTNje(Xudt6T&w=sK&_JLVq8R2;ZZJM!kV}{k(7R0w%Hz@`wJH$1 zqu1daK}cJr*IC3JJqT}`oPWdxoO&ssRiF10P{!eN_gba*)VgM$3ceP$m)Ksf^XWos z0{o6>RLG7&*Yu8dzLVb9Aza+^g|Y1=3HUl8)`W1Wy}DNk3;XPjjRHu zuF)YQsHG8=$L=bT6_06k#InbhIqIRm(?h~2Z6YUvTf`uZ(W@&#nY)|X{;Z&X8gD@uV^s3bCsVg+$>cJFg$FJP{LKx zTN8=2RZ#SZv=tyKhwwfqHgj8C6?UWF<&|stEucn|2EU=QOU(paoHErjUYN-9se}$MO2^Oo2BsWy6yuB7wbB~o7cY+6Pf3nq_q1`% z?Yb@uN>!Z~xSelLO>{ej#to^>rnENaq!G~s>dN?U#gS7%Jj4Q*fjej+?Ux5ds&^z@ z;?oj%vRK3`Ksbi4K0w<)c$MWCo8FuYHjs3@$;UGibG|9uS;DGLpa)s$S6)(9{Mwq{ zZJVT|2j)lYFJd_0yvSOgAZ3x?;2!aemi@JORTp*_259u@RnZn&A@QowkALlN`XJ^g zJIz=Za$rOSP5#*f<00QoL9!Y0?YewoMVRzmwWYM)&lPl!caUe?pCNd$jn-z2yM-hg zPbP>^L@l`i(F5hyDN#BHRokT8bH2y@Du=#_Y}A02GKK zuhDgwpg4rdc=i$AuQ zbbu(`fq&M<%Y;7j;@s@spYca(MC&}3t(xt`%Vw`PHrY5Y;oY8}_kEidKW^*_1CJ0N znM88r*!3-2?SR26t=|&r5%0I#rb9jV$c*(so_k#rpM9&=y|)>%e&WV?>*g-iy4mwB zXRQ6$)z0hIYhKsk9FXfA$L=hd0pl4hnO6NyHc%zA zU%g9+(Q1F6@!21x#1BL4YoiJuI*{2d5mD(`QWsI_K5Vzc5Y?jKsdSK;LA4=3?;8h` ziySt`^e-Lay5=ntPlU=SQ`_2 z&JfoJNbh>?-a$RD%TDgUNq6WC-`eCXp zK4?igF#3*mTeu&daR<+-wl};O9tOn~IKtH@iv1`!K*|_Cu`0tTdl3RvAcArrmF_uW+Sd8A9{}`lQj&69pcv0KsL$Ec~sRw4IOMT&o z2DHI{b!qo{jm1ltRLj2m)@Ejlh))XS(p3u<;a7y$?1_Rtdb{PNe7PMWdBhKB%z-N` zQ@W>l^z2wM*g;KQR2b+6e)ao_wk%#JH5nw^*Qe-;!l`@@favx2ECiKh6O18zdT$vd z5G66=E;mr(oTc7@s05#QwuVmX9K2#PrNRyjniv{T8O2g^5RMi~iMoVdy%Qc~8enw! zCc?u8%$!vkUw3H8a&ml)975!m104>2swEQ}t$eKmTFB7?b>&u7TehcQ+Knep_8rSy zl4Y1;xYcTbz0@Z)nve}Z*$cYwAVpVnm9&pC2NXa?0C?DAf4Lw)G3Lh_0c03IV8K=4$Cs$?66xr(@+eLOoXE+>x-z(Pjo}4K zE$b+6cc3|Q2}^6QHsX`igI=;aA)bpiBqfXiNIa=$j3K{&Ceu|#(P2V338K9wXWPsJ>6=GDQssrg+e{=F(-5A^qJ&x|XMUuG`{J ze1n<*6T1rHNPHFq5E1YfA>6p@@||c}okqo>6d_6EVnxn=4nS4xLdYAKwQK#VDcgKB zLiSce?I~QhOAugj6M*xJ$@TleZr8eg0?6zdX4aCId?zS?q7Y00|8)jmm)vl1FVrwN zbB~3f2TdU106Ts?XNAy;j($OX5Mt8B9&uyEzqNLAxeg%<)H(_S6tJ0R)Zp3XQGVnJ z`FjZjcI;fhCIx8vfS^pOno)qr>UHS(7z5tl)N`MZj*)?hgPq~$9fixcij;h3jkIHQ zR|!*8nW^6bVmI*yORa;1MYhyKUH+_`zr6Y@N?GbJ6}AS5aRB&P7@;o}+$%7>%C*OY zc2<)Qw0kSOAVwyQREY_vd4A<06X(M9Rh z6x`c6<-bKiD#WpgqV@z+PQ-uhjU{PGN5}M!DLTT>v*I2mRYUCeW0cb(3)zj)D=6C= z+<|X&aR~NI!Rmv0&v`tEVp=&m<);S}(eq=rUC`o&r(N6i2e?1JvBCgYWpgkS;K*D1 z1FaSAp8f?bPABGm7Nm`r3Y;Y(kNm00%aYV25b>Q@6SkLDPJQ`>x4fi9>2w{BMS-wD zIf>akP+DQ2D)5K&2HoieoOzC@p;!2O!iUwr7Z%G%S}8dlz8#*K7#?WWp$QyD)#8PJ zix?FN3Z6BWl{*fwn-l{@CYmN6HFO6zCI*8(#hz`$rqgNUlA#}o%b*OV*M>V<8x=R| z!kXNOG4~JqJS!7Yj<*o>;IYVgo1`52T1d;}ZSA6l53G6;QYwSJ6_HLbx0!^4?CGBW58fSc-jqeH! zRn*K;ebvq(svZ=-e2M0JG>5O{V!!8h=4X=YRxQ2S2eNg%K49y9H%m3+^x`4BKfsEaH;sXoO!4@(`BI^?@<43LB-L-Z-+0jyOgSIDPH zs1UK15R{?>3yE8-#s+_Yl{$L{X^^IQb{!?OYc(7JswogG)q;r{Qr=+ao;WE9S;hB) zxRO~QV@!2=Bsr37T4K)5zl+H{?u9=bWAXLZ0SSSi)b2~L@=WIRAu)UTDvNl{{s9t^ zlFJDo#q_i%l!7(hQIk6n9IhHVrAY_F5;CzDF61|+hg6=7P?ue0eWeINO?4h$y#nZV zjbq+(S22{l)?TCWeHp=z1I4@h48t;x=}@l%|A-A54P^IBda8-~$}7{~>ZlI*<8*u# zMxO6pn8E^ue*Z-CNt{m(_8Ua0r2KCN1-eZm>%jMsL$I!)yVa8+)cv5J44L)Ayewh$ z8HYm_iGBpAetCvkNs6hACy;}GCD_n$rK#z`BX9{pOS5$ptufV+*#5pN7Bu$L^alK~ z6c)^&d^~C94MhrWOqO#2p?WYRORFO4(LAsK7YSY-*D=GSjT%=adipE@fF>8@2bnL%05J|Iyf$N)P4P8>c5uyf9m{id81%Q6cBa3WiO8scTq=#z)p zp(}CqakRHT~)SSeV;k^DrAX(2A z{lszmxoC+B7iLD0{`Ac^+TSlAIf{@Ikv(rL_`fq5f$pnL2FBrH41$_Apj+cnE_Z$2 z0;zIwdCr3@<^2-Z2H)0+9cV8l zQ+>D~!sa}sYZ5N>CX(#XUfVvw?ZkDwo?s_A&Dzqg^}f74T;A_5<(BE*;dL90WtC7T z_b=0`ulwFKz_twaKG`fDyecW<%$ZOnAy6fvb&CA+C79~2>mF$r<{s#_#>^Mr0LHO= zH!Pai5xyNc9!$^S=-qkGK6LH2#%>tA-u`RP+JZ1F}cTj zk5f(OYGW<@-;Wt+e~vG5)@}FMkwUM*)7=mj(0!7ug(VB&Y{1hmhc+&0>%lJ$`5The z=Nrw{BlmO5@~9LWMdTXQ(@A8+nJ_P8rcb|9Tl{Ci}Ltx;s90olm~oZJa#yTPfwR$)xoTp4PZ^Tv2^5 zt?Id7W?)a9w9pQd*^D)0ptU>KX;DiBabZzCj}L}7M28>e#ZwdOn7DoKfn4*%{c?=l znWiI-F~_iG=UWhZ*J^^JG*ECR_YDKQFoMsEKJPUEeYf1G9oW<%Ps2ax%<*PtneXc( zj{*Oxf%HAJ;fvnpS~;)9%?jdf1b_o|L+Qq;?6i4P$fo*kg%kVeg}r_^W!iJ79RTLe znl^I)I&RX)9pfj;3=P=0NpkeOBjCM$or$Hno;b7E**stI?n_!z2}^7JbDeqIU`(6#v+b0fpn@-yjjrxgnZdoM`ObMR?f&bYp3X_`^W@<|ZQOliJMJ$#y*DJf)+7w*dL##YAf%52Hn2ccx6dItCU{Ve67FcW7q3smSOLH-Cp_uRb6*~D*ai#3*xT# z-vX^}haT18-z5(Sqv(NlbSKy0$sRxKtRKnSN%jq;{&;a(gpE-*@DZ^r*|NHBla+yK=zxY)K5~y7ny7;0VD~iAO|wG5VUUr zIojc@NecwHl?2rG8VCR#GnNsuKL3W*KM5Wn76t(3JIOp`+YP{U2x*783xKpEB;8H79&q>LkUCB&aVL6R0J@YhSpt)d_ao*wwE zh$)~d*j@nt$q=}A&cHhs08_&y7YH&igSr~fA0c|;C=BWOkZ}KiK0)E)T!S!7Fxy=^ zBJF&b*qw;*^tLgGrvwDnUi;jv*nW>@jFH(7A|fc5sit84Ts^l}83!Ej9im=_!9A3c z`q1$DQM&|8HN*^@Rhj*E7%`7|c3N3s`&`T^?tv>tm^rn|a11fm7a==xbx}JmszQ3a zfH6U^BxSfWvcM*7LiT5nE-8BB>2hI=N8n(igy&8fK?Fk7Y5-{>LrxH-Kmj+XUJ|s? zDS!!*b(}zG`1&q@BuZGo1dIiGxHG62gbxKq89k7o41-*KlpdzO)kOV-7WOX81n?Cb z{{T&#egj1ephi(LK~BO{L3?YQ9p{X20+ z9yfzEhWHdvWIzOgJq$WzR?eF968rMSgDGtTmwMC1&6nBag z_u>?JINj&`x;J~D=M|jf%9Z4g^|^9S*33OKYt46M3?tQn2w{BLnhLwC+k)`JM`)&V zrOZ)tQlN#kuZs8JHZu7_(JkPZIuKEz1X7nE8jEc0Y8 z5R#XU_AnhY4pJ{ajf1v<7i9f_f!_yHm!e70H`OIoh_d&c^?2k=llBTGb2taU1x1TM zIMaz5ScG4~MV)~zkQzSk;hcgMA)?I~P*dC_STx%zv{Orr!-XDT6tYcNxWEbH$_>v= z9A+AlcvAO;aq~DsH&TOZ9hpBAuaA9r(6p~L2(Z2~#)^Hv6y6o?(Dajper7{2yi2h? ztrx0IzRELw%slFO%|^inWenGcPYv6!J4w}7(1YE>7qW$lR=8UH^NT327+KC>b?=Cz z$+C3}V!;uub}1EtU0ymg+og2oFq~mjGt{(X`Hr(nq_n5lJZh2F;nN;R=E`O`9teGkbR^U@XMIkwiA5q}>=Pz52kr#ZoABzd5E^jMH7@p|{UwTd(G#IUXD?NYE16viQd*SKb*R|ue19F)RB__CS;TA7O_1K3h=o?C}Y zxu@b?bIoh0@%_YSM-@zL!yV=eF+CY=8aw@<(hWO`5#D1`fXd-uSAEYu$U2C_(PpUR zgy(HLW*)@Cb;355ToGB_o9k||JSPjfbR)1CJ=Ij6Zviov83VeNK;=r#Iz>Y?H7QtQ zQ33?DY%a4H5yWH3t9l!4eEskr)i8=f-xP8oYj>N3OIobX3?#}y;32HZA??_pDYvri z@OIT66D2}5a@wNV)u=FWoY-L1?toCbNZkFXSCc`hMj0APlvLHzupi<1ymd>Q>}ouj zISfue7Di4Gs^4lB709=8?XY*Ec@;!;$VO$#;r0bw4c^fE zCNX85#D&OXjMG;)tCS*VA;^@0WwW!qQ3-8kQz}4lh|J&La60V{CASVRsfhtJ>M&%a zj~7b~nbF$BNw&9!nruAq^$#B!e}$WYMofIT@4^Yx`qPaF;m9e zy}O8AX=O<_?5#2CkE!if#EqJx-yl}2y4jF!rm8)KIJw$WW<`>oXJ9a`%ct#*+?zLV zQTwOQ+ndVSx{5^366P(dOP44zD!M6m$~Y-d%g$=O(WJUjvebgAzwox&yOe>_`0BFMoTs zq#cvKF`3uCf;Vc_{+YjQEM)-7aNi`}Huw?EX7hc)<8oEjYCSoOVIVTG!V1}*)a4DU zQ8Xrbq08Xv0xgL<-8X(BAm zv91K8ggx!+-PL|ZWA%M=Hr;lsUjNQXna_udPI2=X&DidW0a|kw6iMT4$gIo#;@TkI zH#!8vDboW?Wg*PNMz6*a_=6HG`GVmbh+ut(fy2J1{V`KNcCLj`sG?!gZiO^d0n`z*q)Tw?-x{w|1_!^)8kjb9WKIrTjaetI%)dm92ev<2>WPLHqy!++Nd+{bbJcw z9X~b_cME#G&OhGv*5;tA;x2h};f?;;FmYkd@yC>*(JcETkyUoDxGIbBMyKnp$s}cx z6QPO~0Ya7a@%J~D{0rV`8;*fr)^R~59Lr}vbv=@9MzpD!ERHj~*$su)B4-o~r|(K0 z=o|11)0YHCweB{U4O0e&E?48S=877fw?|7|UeWL#1QkVFwy)QzZX1S9QmeqhaedzJ zu@|FTg|Qr>9=3R_XKgRfQ8#?|n95eAALtJAWS@T0=AXoua4VITVg~QuWMr3gtIjiJ z+NfV6i?~A%+A&wB)El*MuNi9e9FtfI92&t1PEQpuG`q}lJ9&CMZt6z0vyWz}#eUzn zF0z)Bde+NfdpdBkvUjR>8ODNhnT0gV&DYo`*ts29#V^U16Q(<4=&_kUZLs3Yst~ zYfZzkM%J~kb65Wyx~y5?&5E)2C|k$*x-+S@dD!gk*3#sWRy!=A*6WL)z&guO__3P4 zFv1KDB+x)J>YK3N=)uZ%x9!O!OVtL~H=Pi~6W8>2wt8qht`4@7Kvbejz#9le5e29s_G&#KRF3$QA~-%kw7CLjIYTN`7Yr9329j&>9Fg&D0<` zg(!0*ID!gYqDY+W+jUeK78Ec4EM%_7PX~`{N1k5{AFjZW9-k9SHmWA*mPnO9r!F;K z+YlG{$g4_>1hX_IjKJ!6Cw`W*O%C-A8@D-sFnia3FWSJX6xaf*uHQm0Q*J)lv>TMX zh1SJ4AWW~AMzMxz9zS635+90=_esh+@HBk|FY|lTm!>Q!D;be?;V*H$fH3SLmMnfa z9;r(0#P+W3k1Kfy6|o&R)Dlr(s2@Y}{j)xnL~>U}0MouAWiV&n_}mBjiH`Nw6-gb% zc8vgJ*G|_L3omk3ecztm+jFv1HZYHPOD<`dK^ z=CIvcpgHFtSt8c+X&<9O3gif})d(FKwVlW^4oSF$s#z2qMWg9J7G1SAA1SGd@kdoL z&=MMfo@BAtOt+GuoC>_;l`gC|b(o)d_{MC?guCQ~&G&;Z0H$RPM$u9hIN7v~KrSAQ z<`zpKVNBT`TM?-O{w^B{Z*sqjYQW8|LHyXM_ruW=?7L;fs?lX07+~9sd0GfE&jHfb zNU)1)7!XPM`a=Z{N^x4gq3BzL!#-BlWjrT(eqT{HL{QqS0+bW$8AP_MVE_H$?%LL_ z4{t}GQq*cOtW2ngpp4NB*bhvzQf)Y{)(_f67_|gNG15AxJiG>Bx`B05azgw~mF7nT zrY6_E7FP`+))T5Qhdrc#+%?wtw~%9mI$*l9va1$s=ljN>o1FzoKa)@G`Yxga^7I|Hifi{znH z%%pB@r}XZj?UmZKavok%4s zu*Gp0A-}JNMTrRz!-`PyYda~2`!06kgea*a(CD(xYeEBzU;&=&gY2KA(2n`@1O_n^+8!c8X z15FXKgO<77)F3-{wH{ltjo823)S~z3Eqn(&7~z@DM;Lt6#q^Pdn*xRR!??Wlj2s?; z*4)La(U1qLDMSGsp*mR>+~=9!p8uTU23A}X zZxc4;;61QH#b;%Wk!y?vL2`JY3H zXk6pY2iln{sl()Aw_i#r_Nu~`{pEpkr_V6Ci5$h>E`{C3PF zs<6HxsiU4|ZQU*Ww00Bu9OUl<%K8D|{UE7)CMW0~zCK)D;d>^(ERqs0Oz4#yRCrQY zfa4%qKvTl8D4s8kN)pGr)pu`C&mRwP-#uABogN{AEUzn@6m&5zbGk{fc5^F?*^ztj zZ`ySb~+kh{o}0<}8?Xx(X9X9~=1215x#g-YFzrX?OE?OXh}&ZPd>jr>pG zVQe#d3NU0xY*st>&5T{Mx^_aE7?3gO^TT2Mm zNsP97-k!WTZ55|$6ri;cI5M>a0?Fb|aC51_%sl5F`l1z20u1dXRVP}#U~r$EMJ7Hx zf;e3zIMw|8SfD}7r_fLg1h{KMfJh?)u(qznkaQ!Q^gTVUtcmgkSo~Ij?CpnGF8p_d ze((Sy)nsoNK;N#0nZ#K?E|XS1n}{S8Dd(w_@u#o|*_krSNh>0uAs;^w4>ZvbSpD(o zUSk8v(XGC!WCrR2z7Sq&=DP}daB(0@RNst@)`3TTPKS1sbaN_lAiiA)^JC00&6bmD zE}VHOUO-XyyG4i@3x*`yP9F(OmZeKzMg#Hyz`iZ_0ICpCTvxC=tufILB|a!qOKAP3 zV+KDxY1zc`?d%Vt2YE*&9k{7uw&@-fF5H>8A)T2IM&L$f(5Hu&YJlL?s#qKv;wGCz znq2N>czFPZD@NhyyiNXl2T$KN=~lg0>Tv_p!^?3hk*C4UWX|`o7TIoO^InIB0R4@- z=Of+tV7nbPkGD9E6 z;^>C3D1(Bb4EN;;4wUan)?CKWkaYICo9 zQ6g4R>eUP}6-zL+72QDIV^#xk%X&pM_#Zk9WRfAyFd9^Pru50qmL3n|U=A#z`1Yssj| z(_W%9+bOMYDLTS*gJ{O$@gaUqSv6IolH%dy#tQa5k4w+^Bd{SmmOjb~qVU@yK`Tbh zTlm-pNyU8|krtZTykJL&tVSqb-eO7dejRcn#`->s0&9VyM0QnqI{OtY+mS8_(tmy}e}V2m#+>r~k(b-kCA>4_s^j?6UCx!19pcguAFb)U_p&hxk*)yk z4hLj)?;D7ot~KZ-H%;N03``>FH+BYx0&}8~b=;?((t2-(*x1lhYq0G}UZ*dW_$p$8 zbr%JXO&w#rZg4P)GhTb;$YBhQn>dPnB+9~-Rz)@L--v2ayLXQ|;~WJo2}xOL70cLx zaGhW&T~uj1Fi=F0?c<5tRmJ-K=VG0Nqs`|VhTfHJGuZl%YTF-r&_|oyO=3$_^lcfV zU3kReHh7IP=^%M|EZRJ1xYY(7)+?)z1n*+LPg=AEg1fDx)Hn#6li;|NoM`y)TMV1d zgb|rQ`fD#pA+aX|V6r%n&^=9%B>SFF;ZaFa*3Z6kdlJR??aH`StH~nDPX!4G9(sgJ zWKS7>nD4UY*(ahH54YRr$cigeQY`)+2A*n%ZFD$oe(`(UlTaiS8P53u#(=pb2Y@eWS_{g5YP5qLqY{_ znJ8;X$}q|*D;=xoSd$F!^EW;}SW(g=QN~d6XBw9FKzJPhI&BI2<@)BN`gzMD&tpo| znQ#qTd_Gg>D(#|g@J-(Fu>}N&x1%;W*Tljn3ge+P-N)X}FV{5e8VXRD-S38?mXVj( z>ExJllc&ZBIzzIDaI)Yq`&Nl7q?@3JXivaMY9Cj4JC6n(K2hwt+sAGny#J6we6u0) zbjRap)=_TlB9`-0u6cdtq#JHn^s+M1z}Fe9PL#_sqF(xOS5mQVY`m1FyX`1gs})QT zGse|f+P5ONtmhQ3Bo=atv8@ao@cepz0_5%Als6Q4Fih)*hkOh4ET;+Mi|;FiDZt)vu zpBv%B>}qJCwHb0^$sCatl4+lEx7X2E7qKVx7~+d<0-v&c*2|A$Z-GZ%@6(IMHv}eK zXr-jTXxG)2TG-+AXv^VAGBzW{Cwyl#+KK8-e$ylyQ6?jQ_pq6QF)BcE*P$dF=!UT+ zy)hQ)=u~-EA(8&g2GKW>D?qk8`J9sw>=9$jOw?1Vx?CE`4rB8=@T@dZYuV|i!uN!f zF<%9rn!ee2)mABBrH;?_*E@WPm;}EJ6w z5Crkz&{gMSs|jn;K*TO61qd7gOk}C&1>&#onB0LDoO=8Z8Ua#|Y%wBl<|*c}Ci84a z&4e~v`<|$jG9501LtWM5jOdt4OIcr&WvZ11(neC25gp*_ z>uZ3w`MCJvI)fnvOo@Iw96CGv%w6;&pCEfF`e)WS|FWoj$Vl{w$*%Y zIz4p{d;n7OX}>3kz_H78d<>${zo)z+>e`-dYu;rR_`c(a!mJ0RTS8;inCm<4dfVWZ zOu5dN%#v=B$bd>G zAI}2y|1i$8-vShTp1VW!vxWltpSy#pqqF({`on*E0~nZ3yuLy=DwVd2dJe@as$Brc~1mfS38LCJ|oydVCo7KXwH zpNKnxrGkUJrzkx^c|e;DHeS_B90@8%63q-+vDrV2N0tiK?0P$mIGfTR25J6TauDzK z2aHqSljm0k0p+~z+Q>}qH|`7amA#!0>z77)j5vGbC#F63QG!Y*$3IKM8_Oyv*J@Sb z6=2EhD>H0>h*n>fB90+j123T$;K4U=%YX1fS;FM*Yg&<Dui3Np!E%#m14rd5=gU*gkLS4G-y?$C z@UU=a&wtGPv&;S`B4})9Dq&@8?eH&;HH%+Th9tw1`lfV;`n~lN64SIoh&6tmVK#{< z;83y49HM9b3vTpl*DtZvq(93+*&?mb;|BW7pop}dLW=ENrA@+DX&!Sj%CWno-ty zZa;VAX$8)Ckz}rfnmd!04#oC-F)Y5zTt5a`%n;3BB_UKruS6^TeRTCpzde4TSek#t zmv>4sqU}@($bpi855e%kx}iN)WtD&yM)>s+J(e-s9efZX9-6vaH_Wi@JWGVH^&f9= zaK__EOwT=f_4)M=0qI|oCuXrL%3Y#p!6!;Sp`)wAI8hNnNr=7}7#qR6kWoh;9BM^I z$4D=Zh>B_ZhWT2~GeH*o0$xSJY;n|dV&klz=XsV=~+G+k>gUW^=*UIGX!5?A_F-S26o5o8$fjvoIqrb9+*YnQWip6|{hD1t! zQyxio$X8izs<-+-L7 z6>%|lBNyr4L_WdeKAhR@TAW92T~izW`4j&&pEbb^O(s96mT72$YIINg%SE2W7vJ80 z9x%((XqhOUn|9@&oA*~t+ALP-6~w

rg+27P{||%OV*kXNLp@Iic}+lpiuK zbsh1j#1=K;Qfemq7-!b&Iz{H7pQsIqd`&Rl$G5T;E2bt`Mj!7ZerFPcLohx2=Ra4c z`#tRb@#!CG)u|}_74X-BSAPZ^KX;QqmA`sv_}8*Oe>Nn0mM8h|MSxy%Ue%+% z#rbpPmzTyb3t9X&o<{m*{IaaYOTbGl@!x>>=Rx$}b!Yy6%Hl79FBQ3e12dmTmH!I- zt>JrrRmEGiz3li~2MZ~qS-OEmic diff --git a/Svc/ActiveLogger/docs/Checklist_Design.xlsx b/Svc/ActiveLogger/docs/Checklist_Design.xlsx deleted file mode 100644 index e41db23076f23d532645a64d34a219f9f7a08bdb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24983 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 diff --git a/Svc/ActiveLogger/docs/Checklist_Unit_Test.xls b/Svc/ActiveLogger/docs/Checklist_Unit_Test.xls deleted file mode 100644 index 2076b23a47591c0d5124e8643cac5e1b5107d56b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51200 zcmeHw30zcF`~R6?aX{P<756Iwf&!x8QktwPD4T%W>pCz4i~@r*14?O9YT5FdshL}4 zrDcm|nq`Yi*`Ah~Wu~Q;ty*bizP9oIKIblT=gx)bpYQ+o`Tss%xHI=Y=iKLc&a<86 zo^$5ldu>iV_;jb!OmhrlKJ3r>0OqfP8*p7D-?wH+udkp*k?E=vnm$KSem)A%v=#86xu56uCiKTv=Eh$c+JRY z;lFXDavsbE;$bfg?>d7C+!>fJ;OlUDoc%XbL)m&z zbJO)TWH2je)_^Ld8Ibl82?U2ajrdj4kpB1hT@P$-n%Za2{{?1W7S2dcBtY%6Z$eu^ zjifRb$Wuw|+Gj%zjhCny8^cHz`g9pmx%Rv;ci}R2r}`TTq1Q5IXBGJF$0*$(;U>QH z($(1Y($&1_rTeLP*QPgad)?RxXv?IOt(hT>Tcr(gnJly)6LSRoWsa6|j&}Mu3@nk$ z7cyutN5Eg?FtQFZ6{b&x!H*?9=~9V-6%1>Xitk_MI01b&j)P=6tWiEFN8(@R82T4E z3@k}5i$PBwWjbZ4kTgrSNxC&a`#c6Ts27!2>Bv-2#~9!+843_%N@4$2Ca|$AlXdg$ z!u+5)78V}XGp1+H_-WBYCP&FPlcT%&_CTwki%0sTe5+-OXQnXLjCFNqii`KegL^{N z3&jc3=F(u3vJPUsS%1c+5i7j~nKS8jauf$ljvgWb!eYa~_q3?wX`m9vqVb1E)z9@7 z)DQI*Jf28mG3;7TVqytA>2cOnB$JZdLi612;fKOqMqBMp{h}2IC zv5?^u)aU~131CnB#j`k^@oXAIG=xoJ`LF~F&l82a#-EvtXS(^C*e2A?EIuy2n{POK zjCE&hQh01){54Z7I3~wT7AkN|7R2jX9H&?aJc_Z4^#cDEKlTa)r%IQ@f0mOPha*Vq z=5Gj2SoQG#Z-yJ7Z}%a}6CN(g;Tb8#_cktnZ}?LUz}GYYZwSxY2J$zAe`N#t?`r`5 zL<8`K@I2W-evu!vcs25Qn&SrLx%YT(U~ zo9EZi1!lk-k`M4}_%}3~pYRBb;a}!ckw22eL(RWH-5wn{fOFck;rjB8;pL)CT<`G` z+(Qm1wQhdEHTDq5NdAD&)#8`nA)fpw{LAw6uuqlzJ>VpN!9xzBoTDHgPyR*x$crTL zP}@JrK>|y1Q1m$0(|$z!MAs9p;Lp&*FEDhH#lu6+QC@sfChiv+I~3u_uYrqv_J*B% zmRHF?xOE%m<&Q+Shp2Z+o=SdM4jyu*{DOy^mHbtncuYtzkdS3N(c+J9(?cB}6g=@g zbl@nL#;%n78hA@iS7j%@9k@M9kTkBH zUQI*NxOSoxBt~4VWB&DC^$sP}zwvIpTRV4aDz9kdXvQlq%KPg)rjO*uAAeMkG(&|( zndi@+UoVf^nN%=AIs#NMWameq4s4!D%M@A|5>r&8(xF?jjT<+5F_n~*Kq3l}xb`ZJwi8s_b#*MVZg24}Ogk-^f!<{LH$rBhn@oVU62+sWD*;&u5~c7lF_lWUX74ZTiX7tkIuZLPo|@m zjE<%CX@rd0()MTV-}|vXnNC_VI+oVA5i)8^+n@E^r(f%n>8vHAV`&MEkWpLO{;c0W zct)R07a}vTaZ3|GW?&O6t$)Hj+mDK5D4^j`Vp>-%868UtZ-k86()uTSymEy;nQmG# zI+hmI2pP4d^-p;9=vsX;-L+(NEG?!HGHOffpYZhhH}uH}S~5D87T*XNwWak>Sa#rq zKA9d`GCG!)*a#W5rS(tvbpV zr)d_mYE0)fqv>VCgu6-*pqF1FQtGkE1ZYHv6Y$47NTNvHMguad?@S(d0UmeDjjixz zIuMMNS&L^1Y1Z=cWv2NWzB<$gg0C_(28-EDG!4^mRgOP3S2Wtg8-?lsDsx4hLVtxT z@lNJSypy>S@0R1WOrJWR`ei1;lNCQ75J}^6aA_7U9h5hw&L4TBGP1 zaJ%lA^F(xH(9OV^7*3ZPTe1W-azs!q@-{li#VFbU^7^Dl_fSuOkSD5ek;00uE?!ZH%c(S5tsP7*Jp4OO`f{2)9tDI@INRel>hM7=A#4O1~ zMbmZ19v{UIlqe|2JsR5chp>a#HQ=A+SgTzQGXk5YN-%Umm5>7qq>?&dhZNGpz7&t- z7~7M}yAA_@imS0Y29bw(@`_am{y;|lN}-8RQE&>t>|hvfj@@NlW-9W0#25&dqDNfd zfkq1D5Kkk8a_pp0K6gYH@8sAi-pR33yjyN;j@h|*)skE7;tHOvA=(b8b9s@yRjjUC zC=`3Bm;i20#5{p7TR3pkXAp> zr+ivL8-*|i7lcdl@^%Qra$_JS=o1RNixjRIvmmqmKU(7;CFIml2u3X8GfhDX40AV%(=W2cemVjpgnB2N0wbO_eXMwTl1{&C11Z~#m z6WpKWqbka_E`nUY97hne4ye(SWplbHTigm7;-zdgS@zOJsIqUnmmp{mIBYJf!)9J% z3T10lY9b_b5a?knk9$~?b|>@2^9HuX;7eD&9YBSc^5LvfN+|2hdVmPxAad}d1r6Ne zhW;p7kdo;^9R5QL_>9Dv07JRN~=)|`+_N5S}M#b%ea6m1ZYp+#~c8SsEe z@0rn6TV>^$e8993*aXVtugZjF2h(!wQXWct*J+24Vcfzv zs%x1p*$!PNu;%B3@x2F1HL;rvDbVg-Loz;Psu*=JAp)2%>BK}-e`hO$=HZDfoRE7# zb;wkKBW-_qh=HfD8R<+ehJPBoHr(kwE?KZL?i%@1QuM%vPC3Z&z@6HI; ziXyAUf)rb&U>1rcZt00)8-HT9%~dA2%FHeWXPhv;%vvc_QPC-!*4nFuVsoWnMNX>& zh>FXrEmmOTIfy$~ncZoXkvj#uLrNmt_G-w|BRx(iEVBwOYXw@~E~|{s=_crfF>W8m z6oyjaGwibkmtAmJE9^B2Mfa1jlqj+_quNP?k%_`dIl>7IObk#VF{w05V9^VGgp>c!B2U)eYVxonX!II&9*t6uz|h> z<`S!`R-k|gGbj(E=VPsvrM5~d^c4#mRiU8nPNmh-6R#c`Mj;ebS5?^^E=+pLLFr;Fw5)oyRu7iHrtbnTVXjC6>sjBm#&GV`Nj#sfMjOxVKhR zm>sok$#v|-R;N?Qtf+E8;UrGHtQ>EJ{2>arY6VmU74T2lEg&iQO` zJCY!~@zAV?Wl*N7a;wXl>4fHOv@ zw7Uew%AGFqB{ZnSVuKv0=ZQaS1w}PSq;r6!QxXS zlMo+9W@mM|%So86W`y!`YZ7W_%0$m=GF4Yvpl26MhG$H2XS7v1U5;vAGN)k0P=T!& z0wdev#)MkHRffPyIH?^|VHHYHqa^hgG2j7I1&F;eHx{e2*kMCJ7RWHH6v%lgip@@I zQk5(q2__52SzQ9tvQZ@id$3kIt9dC9u!(GmBzV+O5Hqj|fyH)*19Z!4W%g_k1Y$X= z5>U2Q*4P~ON^+$n)Sww=#8L7kjD5g(toCXizwJd%J2`~N3!JamY5`rRP-I2bU@f;* z*f3~fwIsoT^GG`>PA9B`u);0`RGTPZ9x5lrqF@}T^`*Gz@c>#KoC`?DR*G7Rq^pit zoTSBGTus=;nv6JC~bs%R+6p$QGL`4^u+ev9j2x#P9Xcs`2iX=kSP%cZcPJv0H zp@s*j*v@Q~s?40K6!S_Pc0>da*NTtxgwhi)B1tYtVg`DH>J^u%f=UbW@|{v?J)S}Q zu{+?Mq--)GJEukZNka2Tu^w*5qFeQqtz9Gs{^6or_Qd5#_z$@|q7<6DNf_=WCnsTw zo4(3zB|I2gqbF)Nxi=4zK6jU}+? zP(qyGbkza@v6KW#yhno4BPeVyxO;-Em%Jxv0aWic{zp*AL-TVYUJ> z0(Ai@FIZ&-JRMio8b>d&zILJMTw!%VmRv@tH+VaPj6~tvOm}#vBaF9|m*CD^ThH>*hY80_HgLzzVlvwM=;DM7#q1>XN`A!m$39pEjwx1w=pr4* z_da|thrix{s{0!_-64d96VTG4#Z~xXu+a4YJ+oi0-g{hl__T@3h5-#08 zhA*p1v$kEgWT~V3(hY}y9c+AOKuu)bA9XDcRF63kdiJ)v2Y>!n+R-n3p55AY z!gb$&ad}nvXG@=XA!%UFlcUm13%2iFcI4cq%Ol=upBne}^Sv&{ZRm5`g9j$QQh2%J z)~}~p2OJu(=~Lmy=643~+ZuG+wD(GdjiKLq1qLy2ZqpbEnzpuf>#~exu!&-^>Yp z>zRMIJ(>BYbIiP+A1qz8dB-QhhdcUw(Bp@gPo_Q}KI6JOMvnjb{Y%lG4L{o_GSE0! zpZWM!i)Xfh7k!E@7PIg%&$ThKHx8OkTQkR&w&jai8T6%U*A~{m4^$*RKBLV$RUY zb-~HW&az$G_gwmN*U8@!=Ku2Tj?Y&;m=+fPMP|aLsat#(hxh1y*m5H1`(IwXcc3x6 zUCu45E-Xx35PkQ{J2uZg`TXsJ#-l`#m3wvd)WmJux8f=Hk2?N)sRIkuq;{|Bv3jXLjh&$Wh)HU<;)w}$wYU7fDR;@(y3mz@Qxg~5e!2O$XSZxQbs%f;V}9Er zp5Jo$oiqJ!I{otFs|)UI_S%LIDmTxvK6=p3vJQNH?R_~f7R3JCk+n=af8^u*4=0|j zES&WGyTNb%x$fvETaNS@&|%u)Jp)Uf4}UP@gU+jC$312^_P~y7ejj1o_3W;1PqldS zv$M|s{A=cmpG=*7-rsToH{aj$`t=81>zwy$nIrzz4-MbdBz)&*dG%O}zH@y}b-pKP zSX#=!kYgJk4ZV;a`}U5|q*D(yXDuuf4*5Q28#Q9^!kEg>=Lk_Bwmp@7^Jx3Iyr{2k z`FV22EjK^%c*mC>DB84m_3rWS?U{G+_vq^{{{Gfm`$~JYnm@Sq=daJ4zBKOMo^yP9 zeZK9~J5MED{^OlihwQm~uD|@Bgrun}FC@*sbSO9J#&4UY`W=Y5{=((u)9$~n_QGp# z?78u$k9Kknr6+iIyuq6{$pPw{g_^_Tg6tr16tLtOJ;o?^Ye;ah$ieamV7=MrT`y=v} z8^37rTin{${)>*C9|fA#ufCA&m_fNdzk&M!A zgMOblcF{X``1yUlVpi`BAHUpxTffAow&gvY{+{K#X`7F_woSRy=l+sy>)OriyfmzS zRlC+R20dd*412J5vlkN|dVc#|6MLO$J3PH_r*01#)-IkGbh=s2j-<5Sr(by`D4=U;klGa{g2JQW!sErf1YqpOo65U z3-l}yC+OLKVrwKOCiTT-e&!5Ue2~V zvR4ebui%ajnKiF(-M6Fbq>KUSzqoFD!-Zm}c$JlKtGyC0F zw&>KF9}b=hESVEKWBfa*b1s&jPJLy6Q0$AX2X>e-%9wEEAb%bDk9RU%`^`PG*1TM? z^W2i;{PsDw9UEfVI{oG)_CepZE4_PkRCZ)|epFn)RgRXuMlD}=@b<#Xbz25MeAl=$ zVGlo&^5%;NGcHxnTYq7#VeV_;yNX)h@B!<%GH*@&_CE%9x~;Tt_x4l%9FyWaw#YB) zq1X4H>bdsuEZ?^$z3{@TCzYZbjImg{&``S=*;I#4D>-kpCjxCl(fxEhlY9G`# zyxb3VM2pm`C8H9*7L&FWfg49>^h(s+G-48Yqx~=}Nw0$Z*c6l= zo0{NQiw?_LRp$f=>`uV$RKX}8=rD2n%hn#-Up6;loJl$`V9%~AWbCc641@QyRb&u8 zEqEbC*KPQVr`G85{QlU5!`Vf7Nz*vIH;4D(pEq+z9q$&2NF(q99jpG>O-Om!F9cK3 zFz_Fl6`3BH&tD@&=FxCaWKLunyUgXIj!ibtSANsRi zFfs&fKD5-mVCchp!N?M{&yp!=VbtMP!~Wnlgzi3GFfvAW#CkftFywc{dpegtr4iS| z`8#5|BYubTmzQ)$PYmZd=#F?y=bv;(+_|44zTD3dN5eU;qj;5D2(dqdx6#_abC^&7 zi#?x#+_2X3q+GH9Qd}$M!(XYkV!r32I6UBdYkUuWd+n1`H+-8<3Vin{I|S=%Z{xWQdN0Cm3T4FyR@F*6%|mbvbl<~ryy5~0U$iMXF8DEr$giQ5Krp)T zW5Fa4JTJoN#*el1$mhdhp83cJwfV??wJ`EWEb8>YM_%d2YCPzYP09S#8PYg^({|?@$MI35)WXPtv@nXVS{V7R7Df|*WV$kcG#$d7Pb>!w)M;T< z7s@c1XR3!}n2e8xL)@?uUJe*KV4^PNe0W=-$|ut8r~|_&s*+Epi}#6MFmhro4UEnx z`DD6yS*e1Fa_-Jye{!AI8O8||ZRwbeslSZvMzEsrGl&1x!jdVSGLK-%_!RF9m_0iJ zce`Qzw3IXhf0zcN8XeiFs+d;%NMdd1<0cf z7u*wH9O0Z1?B)7!*5Bc{QT?!`MF>CRxJ>cbZk<}mpUPl*`t#YjEyD@>M6 z2jacm49tffCFNCOcAbcbD(eE;mx!Yc1oPq@p(UZ(IhSue zj*K=GXd^_lpr)X0#nBFt&{9j;jTS8dk2@-F26B=OkOdR!F1*YX^~*44M`VuLhHfma z6)aTd)|@$#Qyb*2!Irh6vf2QHTUkVbVjfWtIE7%Oo6-6fDbPmpZoLG9f_O(1fdAzl&8{w8-;8rFHO2cHk6a4AWX&)%E`L#NTr!bBg3Eyh)~CH!|2Gd440mx z-x+X=x}yj49k~Vx1H}=-5XUie;uumS44pjq=;XmiCyqhpgMu7kiRV~41B;o(NLV_1 z@X^_WkIozmm6EE%8a|PfgxCcbJcUSAqb$UJ$qajnb0LDTBE+to`BaH)R}ZpX6|zLE zE67$rj5Oi1JE2lg_Xld!uB0DOR8V*0sPRgYYowb8>TU{Za=C6mJ(i=TN?PVP0cZsg zEvPAIy92Em%iBe?-96BD_dttXXMy%8dRJu#(d9+~;+6kG5Q_vX{Th$Y@%(!dK1IlQ zKnPBd5ga-R_tZ88J6`RI9vtS`h7D8YliM>?t;lUA8Aj2|kBu_mizQ2KT?ARLe33dZ zie-M9J~ag*EsUaw7Dm3Rg^}rLVI*ZOEKUbT?Er1Qc<9Nf;jf<#tiKK{K?jzo0~??N z8>j;tqywWL3eoa+jSlQu9oP^Z*iaqVbvm$NIbY-#wKo*`ujxDIj(zucXizv`u zhV#--s}yk5VICyIK+?$rE?GXI9GFaO_T*0|N>BGxp6==KbWi>?8y9O@@0h zO#UJ>JSdsZt0#s*RFP8z9?Iuc3Naj^kc{vk83B@oj6_fFCZik-lsNtXN>EZzMsk!? zl@O1S9-@!*KpV-?65%L3lgwonjjI%X)myphqg-MCekot90*h0w`YNg9VImgtEAfiF zNu)tOB*MsZL>T#t2qP~MugEvVEAj~Oiu^#lBI_5QC7TyvWa;7+*|&H_RxRcuTTVgL zO=WR@45N+|Nei%|hd#$cR)6P@VlJ>ky1#NQAa3$F44PiBR1J(IPS1*5(G%ho<(mQi z0@=TSGm7(bj=hVTKVeAf+MqIEE$R-c~%NFv`$5_BW_ZV8E-J4{jH79#~ zs4t1W0$<|bgR5RXeyHUz+1#MVdq}9S2fj)X7~h-#l%fzNrSlj z;o3j}lbUE=y6SLGQ7^Zb+KROk)%hc_%2aeg(4UMhL=DBn@@V9y+2)!fRv0Fzr=8QJ z?n*o6PGh<|%~&onJ8jOup}Z`uU;(ssOKa8=lbQCQjI`?S9J|p=cg?+YxAzL@&WPXe zh(Lqfmst$`*e{^3)m?KP-K|ew^T=n3C$e7%$jH5Q$;HL8^)PU50uDXhjaPJsaZh;) z{k4Ian7&$BD@_eGW1=uk5*Ozxlh~}{oIp&1byzV+otAiFc>|Vtlwh(jtpUNC8_bw> zR?}ykEYl41dIKQSUktQ@Zl53hRFR%cgS=$A^}s*(2P1607k*L&8;%+EWg4U-)3@V0 z-dG&)Sk8mLpPJ?0<(?m!BGWe&nZDJaOp7u7QmC$ipdhW;v=D(LtX)rIc25}Qx`+{q zFm=?4#W3X-Ol}m)v7iL92MTAc6H6DcZ~zN8Fk>5YEla9tW+)Uy3rjFRQmY_5M5`TW zVzU$L188z*CE>(!7HP&Obm*S1tX1M+-yr^bV+|=W)k}%O6;PrNyTivHmA)^VXK2C3 z>NrNKjuQVq`U;vHoc+f)py&4t2OANS@h&1#dK;9R?;Y%#aONUW>vb z1)nvHd9&R9r5W3pu}fBs3H-CmYNhZIjKSDogpW4tBr2amMWAzdNS=dxxjM~|1iBp6 z=|I5i*eM9qU6GppW<^TDnAV#6u)9#*mg$HxLr0Wt53s^N4~@!RJI48Ki;VX$*g~rg z>1a3@rz@fyt`=nqBC#|pzq$(Z#^vP`;-q6qQo0KpM5lF&ZZ$-g4i=2C4Lw`T^s>de zD`Ja}pf0@zPFo5U>N#zujsVxbd1L#SpT=fy`tpZXzDK^>+Z0=zq6l!RhXCbe0piL6 z1Uw~zDl$Y2{T|>zhYxh;*HMZbU8`Hh*oD(TYql zUf}lcG+JSyBS!?njNvKivgKV1OCzU!3E$BSYk{y9PtWr5yezK@^7*H2V2Xn6v3%H< zg`vHB2)fZqDNjdAyLUu){PC-wve*Cg{RGR)DHobzd0C2-vei<`HqV5Vs2=9Vg|)7< z$cfC0*Q~Uzgqlh`2vWRLuT0~MIJ~0YV{qXt6oi(S>j`pU6hh^zBNZFXf~e^AODouD zV@;kyFB^Sx;iW5LqoD{k*j=gtffecqtg_|eCCl$g&0KKb`t7$}|J>V6vC%XP1<)~+ zDpOft=rr-AE!3b!5L8L542#Watx*+UK*|N^UU`cC5loHn1>+rDbI7kPrbIT!AW0j* zXv;86HoGZE*ViX{$#K(yDmy|j19P=5R|;uaxdp@15e?ASC8$UP%U@yi;&M#%nWP2YwAPVU`wFxkOja%S(V~t3`de7+ zM&XICjYY4D9zySm79-}Tr;U}-@iK_ZCQ0RkA=nU>&!bB_47diPk=d55gX^G9!kheH zHWBwWpJ4VK?kW9yr2*+Z4l&8g6z1G=B}{>u+zyyR9ZcbpjwwviF@+b#$A5OOZ}YVK z+CBeupHqwbHN_PC454Ica4CQM?y|g;zds*|aBGJ&&NbA~GE*-2GFppF-D%#PP_I0V zAx+n+nk^Qav$z^dj%mjWhc!;f#M(w zxO?;O^03s zX^NMe%C1P|(+_hosPcIS?=kfHI8$`wG}1pOeR|Xj8TbCM;EQ{^5Bsz!a_VLfsA%w% z?ru-HHZWaWQOz4Le3`Dm*S(4Z73zh(Qn0d!JBz@?5wB2@V{NzCXd>g45|tK_h>$y1 z2{x#y6>-}{xOTlr8EWQe&28KM%rZl zOwGLkdZM20CF%)RNYq`}W{5frqFx1W(KcbG>xi0F?{@h7`{SJF27J3d$G`22riglt zBI>njQS&_o$SM_o6gzy>5OMFrQOS3zAz!F6yUM89z()?bIa8M~NuYg-c(+E3dNe#M zx&&2R4l7^0&Ij3O-v+)_5FDC1lH9Sxv0rQe;iZ7 zAeV)+V>)Uv>!?MDe|fmDy?^dam-pNn_-ePUO;OA9idtS!tA+21Det=AETa~hJQ!Jo zJqk4YFCc`CKrpn*Ix5JX;R~Ap5hNTs{Nr55lYo-!L~qTi`4!l zJI2m9w6O)f^MK5F531S;rx;JY8wlBqoyag|LN5TtMg-qIgkDUc4WUATE)xQmv|)^P zU~q=9IhGC?$gFg2+TtboomV6h1!FeHR!DvWG_Xxa@)jM*XSGUY*2^QaAIpz9YQOKu zeND0HI~2*^sh0e3_x4G0TPQyzeb9h-p~74%H+bk>9N!8Cb+l5EV7$P)t;f4Of?j1re18Zdg)=+6^Jc9=+=tw z^A_P1fnKxOs-uT?vG&*VW|XHr_tD;v>tAd0YE$&^|LlF9-Ahn|uYjQFB~&Z^4rLNX zwDlT~b{#?eI@z}8)#Mqu>sl;+?5obNf728}J*XJ$Lu!M?D2ei_PTmTM-WpfX{)ljI zIT-W$kcSVx6Bysf%*l5U6VO0+*sEv@C<-flm!Y^oWltY zPM&&DD`a7(G$oMBP5ez5FL@B-Bo}!&`4?p7dp;l6%cQwBvnB1M;pHHWILyadA76vG zAt?4g#*=$|=>3>Hj|OW7a2nGAIa>gd>C(_hznoFfy6q>P(McDd@G_RVm>H3K@Gn#&`QWq`FZ>A~tx`_f$EYeTx z$rFq95<_6zgtocb^vqOCAdr#fTM{S&e?C6qy3j9R*(TNlf8>1O_@i&}@Wp@_jgR|c zIE*HM`ogGb&bu$l(VTya6n!zWEUzxzY8U}3MmPpd%JhM2(J!ddRfGfy0Izf(grO+< zrk98GO*=KycULCx@23q|*noK`m;hTpWWBKtvp@Yh_|RUTPa9xTn0BX1@$X>7Cc)m1 zBsa$+*d%l-kokb|F#Hh_gL38l%s(J=gQ*X@0XyHyr3Z^#1w`6M8hRs`bm(WNSmxLC z>{J~8#j3Yt~^ahYcg7sl1*s0VG&1E?#9zLMJ|ybv%6K7!wP<&`eh!v{{`u0Z!> zuIlX+V5MHmrb*UfT$Gnh<5xq-U|F3(T$(~RzM5&$?UJ@9J4d@_e?p?DJ#;e}blA&xd=6&y%lCRKsvHs|XxHl|_S8rGV>u+eo0^+7_@5%WK2d@pnY=2OYHY~t1 zWc^fwJ&SL~lKH3j;a!7CbyWa=h{7gT&HBj|6D{7N0@6X>~f zEh@qT7|^_9&40t4l=}A`J|Wbl;N; zsncmE0v6UvaXHA-%caC1NuRPysdfb5FfuajswUz<3nS;$!a&~xCc9oKEC|eCZ35X; zk>Ym6a2Ofx!?Go~FMh2H?Lf)Zs1rbo2I*-sPRLGXnY_gA0n9UxP3@qVka4A z;b5$~G@X>74SxY`78(Kl$T}E4y$@oE;1q?V>wd6B@rN$z>j$s*uR}XmKq``Fo8D!! zRmBpSLk9k+j6%#?Ur#0|AHcN1Sm$R*!P^HLdOGDu*lE1en7a56o+Z@hPUF|{#jR>^ z|7QP8AJ5Xh7MNB)Lz2TslmLBxO^~qTiU}hg%&Yn1JNEGW`Ei*$G_P z`1C&Xl#Mo-qU3JXhd(pbzhIH?J<7bi7Y z_T!{b{t-^1cm@x5!#6cNvu*f^8f&>QDo!YH;e~dkQ%s|=9ZW|pGgrh2g*MFiEvpqq z3sDJ)*NnuB2zMSl@31@Z+d4wFxfpM@?AVWe_?%)Z@sy0SuJ7(~Lq2}Xz^{WIu_d(G zCte%>eR9?Lg9b8#tH)IdT$R9830#%HRS8^`z*PxcmB3XAT$R9830#%HRSEoWl|V!N zf9jtbKREEgy134*m*0#2f6V1A>45oZU>h(HFdZjexbZOo8VjHo3=?qD{0s|D8VjI) zcx#+4oHPbN3ui=eA5f1W%wpptid;p(L920lFv4L(`fx}>7HcX4yW?l zeFYeTp;_wqIaW@9hVoUg%ydqU*utV}hlO_isO6MN%L*3pCu2D$1kX*&6!Ni7$6@0O zEjed2`a|#(_mtO6{QflOmMjP>!mt)119Sc`(>@oISZ9;Ucp==$BkWiUu1==1(O6VA ze#O;BllTkx&uUvOUe<+Gid-`!y?E6%E;*d|PDx8EjPE~rR7Of+L19Xvq7Bk0Jx7Op z9SejFU5h!deqNQpRS8^`z*PxcmB3XAT$R9830#%HRS8^`!2g5<(EZ~*BI-j@Plo!N z)NiCdBlQ=luSk6<>gZAboI0u06{J2s^^9mO2KBS3PftBL>O)e$p1NkCI5Cx-PZFd_ ziu5j@<_=Tus5?#pXAhiVID6s@#~Fb$5@#=*Q8=S<_Qu%^IA ziE|Xr(KxekX5-AknTs*0*dJ@jbIH%y8igOyy z={PC<51jPD)fnural<`pqSGSnybbp&$P`ml|2(uDB(O2#D$rPc0d|F rqCnq(Wb&V;(jP -#include -#include -#include - -#include - -#if FW_OBJECT_REGISTRATION == 1 -static Fw::SimpleObjRegistry simpleReg; -#endif - -void connectPorts(Svc::ActiveLoggerImpl& impl, Svc::ActiveLoggerTester& tester) { - - tester.connect_to_CmdDisp(0,impl.get_CmdDisp_InputPort(0)); - impl.set_CmdStatus_OutputPort(0,tester.get_from_CmdStatus(0)); - impl.set_FatalAnnounce_OutputPort(0,tester.get_from_FatalAnnounce(0)); - - tester.connect_to_LogRecv(0,impl.get_LogRecv_InputPort(0)); - - impl.set_Log_OutputPort(0,tester.get_from_Log(0)); - impl.set_LogText_OutputPort(0,tester.get_from_LogText(0)); - - impl.set_PktSend_OutputPort(0,tester.get_from_PktSend(0)); - -#if FW_PORT_TRACING - // Fw::PortBase::setTrace(true); -#endif - - // simpleReg.dump(); -} - -TEST(ActiveLoggerTest,NominalEventSend) { - - TEST_CASE(100.1.1,"Nominal Event Logging"); - - Svc::ActiveLoggerImpl impl("ActiveLoggerImpl"); - - impl.init(10,0); - - Svc::ActiveLoggerTester tester(impl); - - tester.init(); - - // connect ports - connectPorts(impl,tester); - - tester.runEventNominal(); - -} - -TEST(ActiveLoggerTest,FilteredEventSend) { - - TEST_CASE(100.1.2,"Nominal Event Filtering"); - - Svc::ActiveLoggerImpl impl("ActiveLoggerImpl"); - - impl.init(10,0); - - Svc::ActiveLoggerTester tester(impl); - - tester.init(); - - // connect ports - connectPorts(impl,tester); - - tester.runFilterEventNominal(); - -} - -TEST(ActiveLoggerTest,FilterIdTest) { - - TEST_CASE(100.1.3,"Filter events by ID"); - - Svc::ActiveLoggerImpl impl("ActiveLoggerImpl"); - - impl.init(10,0); - - Svc::ActiveLoggerTester tester(impl); - - tester.init(); - - // connect ports - connectPorts(impl,tester); - - tester.runFilterIdNominal(); - -} - -TEST(ActiveLoggerTest,FilterDumpTest) { - - TEST_CASE(100.1.3,"Dump filter values"); - - Svc::ActiveLoggerImpl impl("ActiveLoggerImpl"); - - impl.init(10,0); - - Svc::ActiveLoggerTester tester(impl); - - tester.init(); - - // connect ports - connectPorts(impl,tester); - - tester.runFilterDump(); - -} - -TEST(ActiveLoggerTest,InvalidCommands) { - - TEST_CASE(100.2.1,"Off-Nominal Invalid Commands"); - - Svc::ActiveLoggerImpl impl("ActiveLoggerImpl"); - - impl.init(10,0); - - Svc::ActiveLoggerTester tester(impl); - - tester.init(); - - // connect ports - connectPorts(impl,tester); - - tester.runFilterInvalidCommands(); - -} - -TEST(ActiveLoggerTest,FatalTesting) { - - TEST_CASE(100.2.2,"Off-Nominal FATAL processing"); - - Svc::ActiveLoggerImpl impl("ActiveLoggerImpl"); - - impl.init(10,0); - - Svc::ActiveLoggerTester tester(impl); - - tester.init(); - - // connect ports - connectPorts(impl,tester); - - tester.runEventFatal(); - -} - -int main(int argc, char* argv[]) { - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} diff --git a/Svc/ActiveLogger/test/ut/ActiveLoggerTester.cpp b/Svc/ActiveLogger/test/ut/ActiveLoggerTester.cpp deleted file mode 100644 index 4a556c1939..0000000000 --- a/Svc/ActiveLogger/test/ut/ActiveLoggerTester.cpp +++ /dev/null @@ -1,625 +0,0 @@ -/* - * ActiveLoggerTester.cpp - * - * Created on: Mar 18, 2015 - * Author: tcanham - */ - -#include -#include -#include -#include -#include -#include - -#include - -namespace Svc { - - typedef ActiveLogger_Enabled Enabled; - typedef ActiveLogger_FilterSeverity FilterSeverity; - - ActiveLoggerTester::ActiveLoggerTester(Svc::ActiveLoggerImpl& inst) : - Svc::ActiveLoggerGTestBase("testerbase",100), - m_impl(inst), - m_receivedPacket(false), - m_receivedFatalEvent(false) { - } - - ActiveLoggerTester::~ActiveLoggerTester() { - } - - void ActiveLoggerTester::from_PktSend_handler( - const FwIndexType portNum, //!< The port number - Fw::ComBuffer &data, //!< Buffer containing packet data - U32 context //!< context; not used - ) { - this->m_sentPacket = data; - this->m_receivedPacket = true; - } - - void ActiveLoggerTester::from_FatalAnnounce_handler( - const FwIndexType portNum, //!< The port number - FwEventIdType Id //!< The ID of the FATAL event - ) { - this->m_receivedFatalEvent = true; - this->m_fatalID = Id; - } - - void ActiveLoggerTester::runEventNominal() { - REQUIREMENT("AL-001"); - - this->writeEvent(29,Fw::LogSeverity::WARNING_HI,10); - } - - void ActiveLoggerTester::runWithFilters(Fw::LogSeverity filter) { - - REQUIREMENT("AL-002"); - - Fw::LogBuffer buff; - U32 val = 10; - FwEventIdType id = 29; - - Fw::SerializeStatus stat = buff.serialize(val); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - Fw::Time timeTag(TimeBase::TB_NONE,0,0); - U32 cmdSeq = 21; - - // enable report filter - this->clearHistory(); - FilterSeverity reportFilterLevel = FilterSeverity::WARNING_HI; - - switch (filter.e) { - case Fw::LogSeverity::WARNING_HI: - reportFilterLevel = FilterSeverity::WARNING_HI; - break; - case Fw::LogSeverity::WARNING_LO: - reportFilterLevel = FilterSeverity::WARNING_LO; - break; - case Fw::LogSeverity::COMMAND: - reportFilterLevel = FilterSeverity::COMMAND; - break; - case Fw::LogSeverity::ACTIVITY_HI: - reportFilterLevel = FilterSeverity::ACTIVITY_HI; - break; - case Fw::LogSeverity::ACTIVITY_LO: - reportFilterLevel = FilterSeverity::ACTIVITY_LO; - break; - case Fw::LogSeverity::DIAGNOSTIC: - reportFilterLevel = FilterSeverity::DIAGNOSTIC; - break; - default: - ASSERT_TRUE(false); - break; - } - - this->clearHistory(); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,reportFilterLevel,Enabled::ENABLED); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_EVENT_FILTER, - cmdSeq, - Fw::CmdResponse::OK - ); - - this->m_receivedPacket = false; - - this->invoke_to_LogRecv(0,id,timeTag,filter,buff); - - // should not have received packet - ASSERT_FALSE(this->m_receivedPacket); - // dispatch message - this->m_impl.doDispatch(); - // should have received packet - ASSERT_TRUE(this->m_receivedPacket); - // verify contents - // first piece should be log packet descriptor - FwPacketDescriptorType desc; - stat = this->m_sentPacket.deserialize(desc); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(desc,static_cast(Fw::ComPacketType::FW_PACKET_LOG)); - // next piece should be event ID - FwEventIdType sentId; - stat = this->m_sentPacket.deserialize(sentId); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(sentId,id); - // next piece is time tag - Fw::Time recTimeTag(TimeBase::TB_NONE,0,0); - stat = this->m_sentPacket.deserialize(recTimeTag); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_TRUE(timeTag == recTimeTag); - // next piece is event argument - U32 readVal; - stat = this->m_sentPacket.deserialize(readVal); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(readVal, val); - // packet should be empty - ASSERT_EQ(this->m_sentPacket.getBuffLeft(),0u); - - // Disable severity filter - this->clearHistory(); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,reportFilterLevel,Enabled::DISABLED); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_EVENT_FILTER, - cmdSeq, - Fw::CmdResponse::OK - ); - - this->m_receivedPacket = false; - - this->invoke_to_LogRecv(0,id,timeTag,filter,buff); - - // should not have received packet - all we can check since no message is dispatched. - ASSERT_FALSE(this->m_receivedPacket); - } - - void ActiveLoggerTester::runFilterInvalidCommands() { - - U32 cmdSeq = 21; - this->clearHistory(); - FilterSeverity reportFilterLevel = FilterSeverity::WARNING_HI; - Enabled filterEnabled(static_cast(10)); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,reportFilterLevel,filterEnabled); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_EVENT_FILTER, - cmdSeq, - Fw::CmdResponse::FORMAT_ERROR - ); - this->clearHistory(); - reportFilterLevel = FilterSeverity::WARNING_HI; - filterEnabled.e = static_cast(-2); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,reportFilterLevel,filterEnabled); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_EVENT_FILTER, - cmdSeq, - Fw::CmdResponse::FORMAT_ERROR - ); - FilterSeverity eventLevel; - this->clearHistory(); - Enabled reportEnable = Enabled::ENABLED; - eventLevel.e = static_cast(-1); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,eventLevel,reportEnable); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_EVENT_FILTER, - cmdSeq, - Fw::CmdResponse::FORMAT_ERROR - ); - - this->clearHistory(); - - reportEnable = Enabled::ENABLED; - eventLevel.e = static_cast(100); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,eventLevel,reportEnable); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_EVENT_FILTER, - cmdSeq, - Fw::CmdResponse::FORMAT_ERROR - ); - - } - - void ActiveLoggerTester::runFilterEventNominal() { - - for (Fw::LogSeverity::t sev = Fw::LogSeverity::WARNING_HI; sev <= Fw::LogSeverity::DIAGNOSTIC; sev = static_cast(sev + 1)) { - this->runWithFilters(sev); - } - - } - - void ActiveLoggerTester::runFilterIdNominal() { - - U32 cmdSeq = 21; - - // for a set of IDs, fill filter - - REQUIREMENT("AL-003"); - - for (FwSizeType filterID = 1; filterID <= TELEM_ID_FILTER_SIZE; filterID++) { - this->clearHistory(); - this->clearEvents(); - this->sendCmd_SET_ID_FILTER(0,cmdSeq,filterID,Enabled::ENABLED); - // dispatch message - this->m_impl.doDispatch(); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_ID_FILTER, - cmdSeq, - Fw::CmdResponse::OK - ); - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_ID_FILTER_ENABLED_SIZE(1); - ASSERT_EVENTS_ID_FILTER_ENABLED(0,filterID); - // send it again, to verify it will accept a second add - this->clearHistory(); - this->clearEvents(); - this->sendCmd_SET_ID_FILTER(0,cmdSeq,filterID,Enabled::ENABLED); - // dispatch message - this->m_impl.doDispatch(); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_ID_FILTER, - cmdSeq, - Fw::CmdResponse::OK - ); - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_ID_FILTER_ENABLED_SIZE(1); - ASSERT_EVENTS_ID_FILTER_ENABLED(0,filterID); - - } - - // Try to send the IDs that are filtered - for (FwSizeType filterID = 1; filterID <= TELEM_ID_FILTER_SIZE; filterID++) { - this->clearHistory(); - this->clearEvents(); - - Fw::LogBuffer buff; - U32 val = 10; - FwEventIdType id = filterID; - - Fw::SerializeStatus stat = buff.serialize(val); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - Fw::Time timeTag(TimeBase::TB_NONE,0,0); - - this->m_receivedPacket = false; - - this->invoke_to_LogRecv(0,id,timeTag,Fw::LogSeverity::ACTIVITY_HI,buff); - - // should not get a packet - ASSERT_FALSE(this->m_receivedPacket); - - } - - // send one of the IDs as a FATAL, it should not be filtered event thought the ID is in the filter - this->clearHistory(); - this->clearEvents(); - - Fw::LogBuffer buff; - U32 val = 10; - FwEventIdType id = 1; - - Fw::SerializeStatus stat = buff.serialize(val); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - Fw::Time timeTag(TimeBase::TB_NONE,0,0); - - this->m_receivedPacket = false; - - this->invoke_to_LogRecv(0,id,timeTag,Fw::LogSeverity::FATAL,buff); - this->m_impl.doDispatch(); - - // should get a packet anyway - ASSERT_TRUE(this->m_receivedPacket); - - // Try to add to the full filter. It should be rejected - this->clearHistory(); - this->clearEvents(); - this->sendCmd_SET_ID_FILTER(0,cmdSeq,TELEM_ID_FILTER_SIZE+1,Enabled::ENABLED); - // dispatch message - this->m_impl.doDispatch(); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_ID_FILTER, - cmdSeq, - Fw::CmdResponse::EXECUTION_ERROR - ); - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_ID_FILTER_LIST_FULL_SIZE(1); - ASSERT_EVENTS_ID_FILTER_LIST_FULL(0,TELEM_ID_FILTER_SIZE+1); - - // Now clear them - - for (FwSizeType filterID = 1; filterID <= TELEM_ID_FILTER_SIZE; filterID++) { - this->clearHistory(); - this->clearEvents(); - this->sendCmd_SET_ID_FILTER(0,cmdSeq,filterID,Enabled::DISABLED); - // dispatch message - this->m_impl.doDispatch(); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_ID_FILTER, - cmdSeq, - Fw::CmdResponse::OK - ); - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_ID_FILTER_REMOVED_SIZE(1); - ASSERT_EVENTS_ID_FILTER_REMOVED(0,filterID); - } - - // Try to clear one that doesn't exist - - this->clearHistory(); - this->clearEvents(); - this->sendCmd_SET_ID_FILTER(0,cmdSeq,10,Enabled::DISABLED); - // dispatch message - this->m_impl.doDispatch(); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_ID_FILTER, - cmdSeq, - Fw::CmdResponse::EXECUTION_ERROR - ); - ASSERT_EVENTS_SIZE(1); - ASSERT_EVENTS_ID_FILTER_NOT_FOUND_SIZE(1); - ASSERT_EVENTS_ID_FILTER_NOT_FOUND(0,10); - - // Send an invalid argument - this->clearHistory(); - this->clearEvents(); - Enabled idEnabled(static_cast(10)); - this->sendCmd_SET_ID_FILTER(0,cmdSeq,10,idEnabled); - // dispatch message - this->m_impl.doDispatch(); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_ID_FILTER, - cmdSeq, - Fw::CmdResponse::FORMAT_ERROR - ); - ASSERT_EVENTS_SIZE(0); - - } - - void ActiveLoggerTester::runFilterDump() { - U32 cmdSeq = 21; - // set random set of filters - - this->sendCmd_SET_EVENT_FILTER(0,0,FilterSeverity::WARNING_HI,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,0,FilterSeverity::WARNING_LO,Enabled::DISABLED); - this->sendCmd_SET_EVENT_FILTER(0,0,FilterSeverity::COMMAND,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,0,FilterSeverity::ACTIVITY_HI,Enabled::DISABLED); - this->sendCmd_SET_EVENT_FILTER(0,0,FilterSeverity::ACTIVITY_LO,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,0,FilterSeverity::DIAGNOSTIC,Enabled::ENABLED); - - this->sendCmd_SET_ID_FILTER(0,cmdSeq,4,Enabled::ENABLED); - // dispatch message - this->m_impl.doDispatch(); - - this->sendCmd_SET_ID_FILTER(0,cmdSeq,13,Enabled::ENABLED); - // dispatch message - this->m_impl.doDispatch(); - - this->sendCmd_SET_ID_FILTER(0,cmdSeq,4000,Enabled::ENABLED); - // dispatch message - this->m_impl.doDispatch(); - - // send command to dump the filters - - this->clearHistory(); - this->clearEvents(); - this->sendCmd_DUMP_FILTER_STATE(0,cmdSeq); - // dispatch message - this->m_impl.doDispatch(); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_DUMP_FILTER_STATE, - cmdSeq, - Fw::CmdResponse::OK - ); - ASSERT_EVENTS_SIZE(6+3); - ASSERT_EVENTS_SEVERITY_FILTER_STATE_SIZE(6); - ASSERT_EVENTS_SEVERITY_FILTER_STATE(0,FilterSeverity::WARNING_HI,true); - ASSERT_EVENTS_SEVERITY_FILTER_STATE(1,FilterSeverity::WARNING_LO,false); - ASSERT_EVENTS_SEVERITY_FILTER_STATE(2,FilterSeverity::COMMAND,true); - ASSERT_EVENTS_SEVERITY_FILTER_STATE(3,FilterSeverity::ACTIVITY_HI,false); - ASSERT_EVENTS_SEVERITY_FILTER_STATE(4,FilterSeverity::ACTIVITY_LO,true); - ASSERT_EVENTS_SEVERITY_FILTER_STATE(5,FilterSeverity::DIAGNOSTIC,true); - } - - void ActiveLoggerTester::runEventFatal() { - Fw::LogBuffer buff; - U32 val = 10; - FwEventIdType id = 29; - U32 cmdSeq = 21; - REQUIREMENT("AL-004"); - - Fw::SerializeStatus stat = buff.serialize(val); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - Fw::Time timeTag(TimeBase::TB_NONE,0,0); - - this->m_receivedPacket = false; - - this->invoke_to_LogRecv(0,id,timeTag,Fw::LogSeverity::FATAL,buff); - - // should not have received packet - ASSERT_FALSE(this->m_receivedPacket); - // should have seen event port - ASSERT_TRUE(this->m_receivedFatalEvent); - ASSERT_EQ(this->m_fatalID,id); - // dispatch message - this->m_impl.doDispatch(); - // should have received packet - ASSERT_TRUE(this->m_receivedPacket); - // verify contents - // first piece should be log packet descriptor - FwPacketDescriptorType desc; - stat = this->m_sentPacket.deserialize(desc); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(desc,static_cast(Fw::ComPacketType::FW_PACKET_LOG)); - // next piece should be event ID - FwEventIdType sentId; - stat = this->m_sentPacket.deserialize(sentId); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(sentId,id); - // next piece is time tag - Fw::Time recTimeTag(TimeBase::TB_NONE,0,0); - stat = this->m_sentPacket.deserialize(recTimeTag); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_TRUE(timeTag == recTimeTag); - // next piece is event argument - U32 readVal; - stat = this->m_sentPacket.deserialize(readVal); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(readVal, val); - // packet should be empty - ASSERT_EQ(this->m_sentPacket.getBuffLeft(),0u); - // Turn on all filters and make sure FATAL still gets through - - this->clearHistory(); - this->clearEvents(); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::WARNING_HI,Enabled::DISABLED); - ASSERT_CMD_RESPONSE_SIZE(1); - ASSERT_CMD_RESPONSE( - 0, - ActiveLoggerImpl::OPCODE_SET_EVENT_FILTER, - cmdSeq, - Fw::CmdResponse::OK - ); - - - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::WARNING_LO,Enabled::DISABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::COMMAND,Enabled::DISABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::ACTIVITY_HI,Enabled::DISABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::ACTIVITY_LO,Enabled::DISABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::DIAGNOSTIC,Enabled::DISABLED); - - this->m_receivedPacket = false; - - this->invoke_to_LogRecv(0,id,timeTag,Fw::LogSeverity::FATAL,buff); - - // should not have received packet - ASSERT_FALSE(this->m_receivedPacket); - // dispatch message - this->m_impl.doDispatch(); - // should have received packet - ASSERT_TRUE(this->m_receivedPacket); - // verify contents - // first piece should be log packet descriptor - stat = this->m_sentPacket.deserialize(desc); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(desc,static_cast(Fw::ComPacketType::FW_PACKET_LOG)); - // next piece should be event ID - stat = this->m_sentPacket.deserialize(sentId); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(sentId,id); - // next piece is time tag - stat = this->m_sentPacket.deserialize(recTimeTag); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_TRUE(timeTag == recTimeTag); - // next piece is event argument - stat = this->m_sentPacket.deserialize(readVal); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(readVal, val); - // packet should be empty - ASSERT_EQ(this->m_sentPacket.getBuffLeft(),0u); - - // turn off filters - - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::WARNING_HI,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::WARNING_LO,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::COMMAND,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::ACTIVITY_HI,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::ACTIVITY_LO,Enabled::ENABLED); - this->sendCmd_SET_EVENT_FILTER(0,cmdSeq,FilterSeverity::DIAGNOSTIC,Enabled::ENABLED); - - } - - void ActiveLoggerTester::writeEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value) { - Fw::LogBuffer buff; - - Fw::SerializeStatus stat = buff.serialize(value); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - Fw::Time timeTag(TimeBase::TB_NONE,1,2); - - this->m_receivedPacket = false; - - this->invoke_to_LogRecv(0,id,timeTag,severity,buff); - - // should not have received packet - ASSERT_FALSE(this->m_receivedPacket); - // dispatch message - this->m_impl.doDispatch(); - // should have received packet - ASSERT_TRUE(this->m_receivedPacket); - // verify contents - // first piece should be log packet descriptor - FwPacketDescriptorType desc; - stat = this->m_sentPacket.deserialize(desc); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(desc,static_cast(Fw::ComPacketType::FW_PACKET_LOG)); - // next piece should be event ID - FwEventIdType sentId; - stat = this->m_sentPacket.deserialize(sentId); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(sentId,id); - // next piece is time tag - Fw::Time recTimeTag(TimeBase::TB_NONE,1,2); - stat = this->m_sentPacket.deserialize(recTimeTag); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_TRUE(timeTag == recTimeTag); - // next piece is event argument - U32 readVal; - stat = this->m_sentPacket.deserialize(readVal); - ASSERT_EQ(Fw::FW_SERIALIZE_OK,stat); - ASSERT_EQ(readVal, value); - // packet should be empty - ASSERT_EQ(this->m_sentPacket.getBuffLeft(),0u); - - } - - void ActiveLoggerTester::readEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value, Os::File& file) { - static const BYTE delimiter = 0xA5; - - // first read should be delimiter - BYTE de; - FwSizeType readSize = static_cast(sizeof(de)); - - ASSERT_EQ(file.read(&de,readSize,Os::File::WaitType::WAIT),Os::File::OP_OK); - ASSERT_EQ(delimiter,de); - // next is LogPacket - Fw::ComBuffer comBuff; - // size is specific to this test - readSize = sizeof(FwPacketDescriptorType) + sizeof(FwEventIdType) + Fw::Time::SERIALIZED_SIZE + sizeof(U32); - ASSERT_EQ(file.read(comBuff.getBuffAddr(),readSize,Os::File::WaitType::WAIT),Os::File::OP_OK); - comBuff.setBuffLen(readSize); - - // deserialize LogPacket - Fw::LogPacket packet; - Fw::Time time(TimeBase::TB_NONE,1,2); - Fw::LogBuffer logBuff; - ASSERT_EQ(comBuff.deserialize(packet),Fw::FW_SERIALIZE_OK); - - // read back values - ASSERT_EQ(id,packet.getId()); - ASSERT_EQ(time,packet.getTimeTag()); - logBuff = packet.getLogBuffer(); - U32 readValue; - ASSERT_EQ(logBuff.deserialize(readValue),Fw::FW_SERIALIZE_OK); - ASSERT_EQ(value,readValue); - - } - - void ActiveLoggerTester::textLogIn(const FwEventIdType id, //!< The event ID - const Fw::Time& timeTag, //!< The time - const Fw::LogSeverity severity, //!< The severity - const Fw::TextLogString& text //!< The event string - ) { - TextLogEntry e = { id, timeTag, severity, text }; - - printTextLogHistoryEntry(e, stdout); - } - void ActiveLoggerTester :: - from_pingOut_handler( - const FwIndexType portNum, - U32 key - ) - { - this->pushFromPortEntry_pingOut(key); - } -} /* namespace SvcTest */ diff --git a/Svc/ActiveLogger/test/ut/ActiveLoggerTester.hpp b/Svc/ActiveLogger/test/ut/ActiveLoggerTester.hpp deleted file mode 100644 index fe1669e34d..0000000000 --- a/Svc/ActiveLogger/test/ut/ActiveLoggerTester.hpp +++ /dev/null @@ -1,80 +0,0 @@ -/* - * ActiveLoggerTester.hpp - * - * Created on: Mar 18, 2015 - * Author: tcanham - */ - -#ifndef ACTIVELOGGER_TEST_UT_ACTIVELOGGER_TESTER_HPP_ -#define ACTIVELOGGER_TEST_UT_ACTIVELOGGER_TESTER_HPP_ - -#include -#include -#include - -namespace Svc { - - class ActiveLoggerTester: public Svc::ActiveLoggerGTestBase { - public: - explicit ActiveLoggerTester(Svc::ActiveLoggerImpl& inst); - virtual ~ActiveLoggerTester(); - - void runEventNominal(); - void runFilterEventNominal(); - void runFilterIdNominal(); - void runFilterDump(); - void runFilterInvalidCommands(); - void runEventFatal(); - void runFileDump(); - void runFileDumpErrors(); - - private: - - void from_PktSend_handler( - const FwIndexType portNum, //!< The port number - Fw::ComBuffer &data, //!< Buffer containing packet data - U32 context //!< context (not used) - ) override; - void from_FatalAnnounce_handler( - const FwIndexType portNum, //!< The port number - FwEventIdType Id //!< The ID of the FATAL event - ) override; - - Svc::ActiveLoggerImpl& m_impl; - - bool m_receivedPacket; - Fw::ComBuffer m_sentPacket; - - bool m_receivedFatalEvent; - FwEventIdType m_fatalID; - - void runWithFilters(Fw::LogSeverity filter); - - void writeEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value); - void readEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value, Os::File& file); - - // enumeration to tell what kind of error to inject - typedef enum { - FILE_WRITE_WRITE_ERROR, // return a bad read status - FILE_WRITE_SIZE_ERROR, // return a bad size - } FileWriteTestType; - FileWriteTestType m_writeTestType; - FwSizeType m_writeSize; - - void textLogIn(const FwEventIdType id, //!< The event ID - const Fw::Time& timeTag, //!< The time - const Fw::LogSeverity severity, //!< The severity - const Fw::TextLogString& text //!< The event string - ) override; - - //! Handler for from_pingOut - //! - void from_pingOut_handler( - const FwIndexType portNum, /*!< The port number*/ - U32 key /*!< Value to return to pinger*/ - ) override; - }; - -} /* namespace Svc */ - -#endif /* ACTIVELOGGER_TEST_UT_ACTIVELOGGER_TESTER_HPP_ */ diff --git a/Svc/CMakeLists.txt b/Svc/CMakeLists.txt index 7841eb1ff1..bc80aec4fb 100644 --- a/Svc/CMakeLists.txt +++ b/Svc/CMakeLists.txt @@ -12,7 +12,6 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/WatchDog/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/Ports/") # Components -add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ActiveLogger/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ActiveRateGroup/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/AssertFatalAdapter/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/BufferAccumulator/") @@ -31,6 +30,7 @@ add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DpCatalog/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DpManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DpPorts/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/DpWriter/") +add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/EventManager/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FatalHandler/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileDownlinkPorts/") add_fprime_subdirectory("${CMAKE_CURRENT_LIST_DIR}/FileDownlink/") diff --git a/Svc/EventManager/CMakeLists.txt b/Svc/EventManager/CMakeLists.txt new file mode 100644 index 0000000000..dcf6ca8e52 --- /dev/null +++ b/Svc/EventManager/CMakeLists.txt @@ -0,0 +1,29 @@ +#### +# F prime CMakeLists.txt: +# +# SOURCE_FILES: combined list of source and autocoding files +# MOD_DEPS: (optional) module dependencies +# +# Note: using PROJECT_NAME as EXECUTABLE_NAME +#### + + +register_fprime_library( + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/EventManager.cpp" + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/EventManager.fpp" +) + +### UTs ### +register_fprime_ut( + SOURCES + "${CMAKE_CURRENT_LIST_DIR}/test/ut/EventManagerTestMain.cpp" + "${CMAKE_CURRENT_LIST_DIR}/test/ut/EventManagerTester.cpp" + AUTOCODER_INPUTS + "${CMAKE_CURRENT_LIST_DIR}/EventManager.fpp" +) +set (UT_TARGET_NAME "${FPRIME_CURRENT_MODULE}_ut_exe") +if (TARGET "${UT_TARGET_NAME}") + target_compile_options("${UT_TARGET_NAME}" PRIVATE -Wno-conversion) +endif() diff --git a/Svc/EventManager/EventManager.cpp b/Svc/EventManager/EventManager.cpp new file mode 100644 index 0000000000..a17760504f --- /dev/null +++ b/Svc/EventManager/EventManager.cpp @@ -0,0 +1,196 @@ +/* + * TestCommand1Impl.cpp + * + * Created on: Mar 28, 2014 + * Author: tcanham + */ + +#include + +#include +#include +#include + +namespace Svc { +static_assert(std::numeric_limits::max() >= TELEM_ID_FILTER_SIZE, + "TELEM_ID_FILTER_SIZE must fit within range of FwSizeType"); +typedef EventManager_Enabled Enabled; +typedef EventManager_FilterSeverity FilterSeverity; + +EventManager::EventManager(const char* name) : EventManagerComponentBase(name) { + // set filter defaults + this->m_filterState[FilterSeverity::WARNING_HI].enabled = + FILTER_WARNING_HI_DEFAULT ? Enabled::ENABLED : Enabled::DISABLED; + this->m_filterState[FilterSeverity::WARNING_LO].enabled = + FILTER_WARNING_LO_DEFAULT ? Enabled::ENABLED : Enabled::DISABLED; + this->m_filterState[FilterSeverity::COMMAND].enabled = + FILTER_COMMAND_DEFAULT ? Enabled::ENABLED : Enabled::DISABLED; + this->m_filterState[FilterSeverity::ACTIVITY_HI].enabled = + FILTER_ACTIVITY_HI_DEFAULT ? Enabled::ENABLED : Enabled::DISABLED; + this->m_filterState[FilterSeverity::ACTIVITY_LO].enabled = + FILTER_ACTIVITY_LO_DEFAULT ? Enabled::ENABLED : Enabled::DISABLED; + this->m_filterState[FilterSeverity::DIAGNOSTIC].enabled = + FILTER_DIAGNOSTIC_DEFAULT ? Enabled::ENABLED : Enabled::DISABLED; + + memset(m_filteredIDs, 0, sizeof(m_filteredIDs)); +} + +EventManager::~EventManager() {} + +void EventManager::LogRecv_handler(FwIndexType portNum, + FwEventIdType id, + Fw::Time& timeTag, + const Fw::LogSeverity& severity, + Fw::LogBuffer& args) { + // make sure ID is not zero. Zero is reserved for ID filter. + FW_ASSERT(id != 0); + + switch (severity.e) { + case Fw::LogSeverity::FATAL: // always pass FATAL + break; + case Fw::LogSeverity::WARNING_HI: + if (this->m_filterState[FilterSeverity::WARNING_HI].enabled == Enabled::DISABLED) { + return; + } + break; + case Fw::LogSeverity::WARNING_LO: + if (this->m_filterState[FilterSeverity::WARNING_LO].enabled == Enabled::DISABLED) { + return; + } + break; + case Fw::LogSeverity::COMMAND: + if (this->m_filterState[FilterSeverity::COMMAND].enabled == Enabled::DISABLED) { + return; + } + break; + case Fw::LogSeverity::ACTIVITY_HI: + if (this->m_filterState[FilterSeverity::ACTIVITY_HI].enabled == Enabled::DISABLED) { + return; + } + break; + case Fw::LogSeverity::ACTIVITY_LO: + if (this->m_filterState[FilterSeverity::ACTIVITY_LO].enabled == Enabled::DISABLED) { + return; + } + break; + case Fw::LogSeverity::DIAGNOSTIC: + if (this->m_filterState[FilterSeverity::DIAGNOSTIC].enabled == Enabled::DISABLED) { + return; + } + break; + default: + FW_ASSERT(0, static_cast(severity.e)); + return; + } + + // check ID filters + for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { + if ((m_filteredIDs[entry] == id) && (severity != Fw::LogSeverity::FATAL)) { + return; + } + } + + // send event to the logger thread + this->loqQueue_internalInterfaceInvoke(id, timeTag, severity, args); + + // if connected, announce the FATAL + if (Fw::LogSeverity::FATAL == severity.e) { + if (this->isConnected_FatalAnnounce_OutputPort(0)) { + this->FatalAnnounce_out(0, id); + } + } +} + +void EventManager::loqQueue_internalInterfaceHandler(FwEventIdType id, + const Fw::Time& timeTag, + const Fw::LogSeverity& severity, + const Fw::LogBuffer& args) { + // Serialize event + this->m_logPacket.setId(id); + this->m_logPacket.setTimeTag(timeTag); + this->m_logPacket.setLogBuffer(args); + this->m_comBuffer.resetSer(); + Fw::SerializeStatus stat = this->m_logPacket.serialize(this->m_comBuffer); + FW_ASSERT(Fw::FW_SERIALIZE_OK == stat, static_cast(stat)); + + if (this->isConnected_PktSend_OutputPort(0)) { + this->PktSend_out(0, this->m_comBuffer, 0); + } +} + +void EventManager::SET_EVENT_FILTER_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + FilterSeverity filterLevel, + Enabled filterEnable) { + this->m_filterState[filterLevel.e].enabled = filterEnable; + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +void EventManager::SET_ID_FILTER_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + FwEventIdType ID, + Enabled idEnabled //!< ID filter state +) { + if (Enabled::ENABLED == idEnabled.e) { // add ID + // search list for existing entry + for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { + if (this->m_filteredIDs[entry] == ID) { + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); + this->log_ACTIVITY_HI_ID_FILTER_ENABLED(ID); + return; + } + } + // if not already a match, search for an open slot + for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { + if (this->m_filteredIDs[entry] == 0) { + this->m_filteredIDs[entry] = ID; + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); + this->log_ACTIVITY_HI_ID_FILTER_ENABLED(ID); + return; + } + } + // if an empty slot was not found, send an error event + this->log_WARNING_LO_ID_FILTER_LIST_FULL(ID); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + } else { // remove ID + // search list for existing entry + for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { + if (this->m_filteredIDs[entry] == ID) { + this->m_filteredIDs[entry] = 0; // zero entry + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); + this->log_ACTIVITY_HI_ID_FILTER_REMOVED(ID); + return; + } + } + // if it gets here, wasn't found + this->log_WARNING_LO_ID_FILTER_NOT_FOUND(ID); + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + } +} + +void EventManager::DUMP_FILTER_STATE_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number +) { + // first, iterate through severity filters + for (FwEnumStoreType filter = 0; filter < FilterSeverity::NUM_CONSTANTS; filter++) { + FilterSeverity filterState(static_cast(filter)); + this->log_ACTIVITY_LO_SEVERITY_FILTER_STATE(filterState, + Enabled::ENABLED == this->m_filterState[filter].enabled.e); + } + + // iterate through ID filter + for (FwSizeType entry = 0; entry < TELEM_ID_FILTER_SIZE; entry++) { + if (this->m_filteredIDs[entry] != 0) { + this->log_ACTIVITY_HI_ID_FILTER_ENABLED(this->m_filteredIDs[entry]); + } + } + + this->cmdResponse_out(opCode, cmdSeq, Fw::CmdResponse::OK); +} + +void EventManager::pingIn_handler(const FwIndexType portNum, U32 key) { + // return key + this->pingOut_out(0, key); +} + +} // namespace Svc diff --git a/Svc/ActiveLogger/ActiveLogger.fpp b/Svc/EventManager/EventManager.fpp similarity index 99% rename from Svc/ActiveLogger/ActiveLogger.fpp rename to Svc/EventManager/EventManager.fpp index 8ea4ff5cb2..4644b01a1c 100644 --- a/Svc/ActiveLogger/ActiveLogger.fpp +++ b/Svc/EventManager/EventManager.fpp @@ -1,7 +1,7 @@ module Svc { @ A component for logging events - active component ActiveLogger { + active component EventManager { # ---------------------------------------------------------------------- # Types diff --git a/Svc/EventManager/EventManager.hpp b/Svc/EventManager/EventManager.hpp new file mode 100644 index 0000000000..3cf9f049be --- /dev/null +++ b/Svc/EventManager/EventManager.hpp @@ -0,0 +1,70 @@ +/* + * EventManager.hpp + * + * Created on: Mar 28, 2014 + * Author: tcanham + */ + +#ifndef Svc_EventManager_HPP_ +#define Svc_EventManager_HPP_ + +#include +#include +#include + +namespace Svc { + +class EventManager final : public EventManagerComponentBase { + public: + EventManager(const char* compName); //!< constructor + virtual ~EventManager(); //!< destructor + + private: + void LogRecv_handler(FwIndexType portNum, + FwEventIdType id, + Fw::Time& timeTag, + const Fw::LogSeverity& severity, + Fw::LogBuffer& args); + + void loqQueue_internalInterfaceHandler(FwEventIdType id, + const Fw::Time& timeTag, + const Fw::LogSeverity& severity, + const Fw::LogBuffer& args); + + void SET_EVENT_FILTER_cmdHandler(FwOpcodeType opCode, + U32 cmdSeq, + EventManager_FilterSeverity filterLevel, + EventManager_Enabled filterEnabled); + + void SET_ID_FILTER_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq, //!< The command sequence number + FwEventIdType ID, + EventManager_Enabled idFilterEnabled //!< ID filter state + ); + + void DUMP_FILTER_STATE_cmdHandler(FwOpcodeType opCode, //!< The opcode + U32 cmdSeq //!< The command sequence number + ); + + //! Handler implementation for pingIn + //! + void pingIn_handler(const FwIndexType portNum, /*!< The port number*/ + U32 key /*!< Value to return to pinger*/ + ); + + // Filter state + struct t_filterState { + EventManager_Enabled enabled; // +#include +#include +#include + +#include + +#if FW_OBJECT_REGISTRATION == 1 +static Fw::SimpleObjRegistry simpleReg; +#endif + +void connectPorts(Svc::EventManager& impl, Svc::EventManagerTester& tester) { + tester.connect_to_CmdDisp(0, impl.get_CmdDisp_InputPort(0)); + impl.set_CmdStatus_OutputPort(0, tester.get_from_CmdStatus(0)); + impl.set_FatalAnnounce_OutputPort(0, tester.get_from_FatalAnnounce(0)); + + tester.connect_to_LogRecv(0, impl.get_LogRecv_InputPort(0)); + + impl.set_Log_OutputPort(0, tester.get_from_Log(0)); + impl.set_LogText_OutputPort(0, tester.get_from_LogText(0)); + + impl.set_PktSend_OutputPort(0, tester.get_from_PktSend(0)); + +#if FW_PORT_TRACING + // Fw::PortBase::setTrace(true); +#endif + + // simpleReg.dump(); +} + +TEST(EventManagerTest, NominalEventSend) { + TEST_CASE(100.1.1, "Nominal Event Logging"); + + Svc::EventManager impl("EventManager"); + + impl.init(10, 0); + + Svc::EventManagerTester tester(impl); + + tester.init(); + + // connect ports + connectPorts(impl, tester); + + tester.runEventNominal(); +} + +TEST(EventManagerTest, FilteredEventSend) { + TEST_CASE(100.1.2, "Nominal Event Filtering"); + + Svc::EventManager impl("EventManager"); + + impl.init(10, 0); + + Svc::EventManagerTester tester(impl); + + tester.init(); + + // connect ports + connectPorts(impl, tester); + + tester.runFilterEventNominal(); +} + +TEST(EventManagerTest, FilterIdTest) { + TEST_CASE(100.1.3, "Filter events by ID"); + + Svc::EventManager impl("EventManager"); + + impl.init(10, 0); + + Svc::EventManagerTester tester(impl); + + tester.init(); + + // connect ports + connectPorts(impl, tester); + + tester.runFilterIdNominal(); +} + +TEST(EventManagerTest, FilterDumpTest) { + TEST_CASE(100.1.3, "Dump filter values"); + + Svc::EventManager impl("EventManager"); + + impl.init(10, 0); + + Svc::EventManagerTester tester(impl); + + tester.init(); + + // connect ports + connectPorts(impl, tester); + + tester.runFilterDump(); +} + +TEST(EventManagerTest, InvalidCommands) { + TEST_CASE(100.2.1, "Off-Nominal Invalid Commands"); + + Svc::EventManager impl("EventManager"); + + impl.init(10, 0); + + Svc::EventManagerTester tester(impl); + + tester.init(); + + // connect ports + connectPorts(impl, tester); + + tester.runFilterInvalidCommands(); +} + +TEST(EventManagerTest, FatalTesting) { + TEST_CASE(100.2.2, "Off-Nominal FATAL processing"); + + Svc::EventManager impl("EventManager"); + + impl.init(10, 0); + + Svc::EventManagerTester tester(impl); + + tester.init(); + + // connect ports + connectPorts(impl, tester); + + tester.runEventFatal(); +} + +int main(int argc, char* argv[]) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Svc/EventManager/test/ut/EventManagerTester.cpp b/Svc/EventManager/test/ut/EventManagerTester.cpp new file mode 100644 index 0000000000..312e57184c --- /dev/null +++ b/Svc/EventManager/test/ut/EventManagerTester.cpp @@ -0,0 +1,534 @@ +/* + * EventManagerTester.cpp + * + * Created on: Mar 18, 2015 + * Author: tcanham + */ + +#include +#include +#include +#include +#include +#include + +#include + +namespace Svc { + +typedef EventManager_Enabled Enabled; +typedef EventManager_FilterSeverity FilterSeverity; + +EventManagerTester::EventManagerTester(Svc::EventManager& inst) + : Svc::EventManagerGTestBase("testerbase", 100), + m_impl(inst), + m_receivedPacket(false), + m_receivedFatalEvent(false) {} + +EventManagerTester::~EventManagerTester() {} + +void EventManagerTester::from_PktSend_handler(const FwIndexType portNum, //!< The port number + Fw::ComBuffer& data, //!< Buffer containing packet data + U32 context //!< context; not used +) { + this->m_sentPacket = data; + this->m_receivedPacket = true; +} + +void EventManagerTester::from_FatalAnnounce_handler(const FwIndexType portNum, //!< The port number + FwEventIdType Id //!< The ID of the FATAL event +) { + this->m_receivedFatalEvent = true; + this->m_fatalID = Id; +} + +void EventManagerTester::runEventNominal() { + REQUIREMENT("AL-001"); + + this->writeEvent(29, Fw::LogSeverity::WARNING_HI, 10); +} + +void EventManagerTester::runWithFilters(Fw::LogSeverity filter) { + REQUIREMENT("AL-002"); + + Fw::LogBuffer buff; + U32 val = 10; + FwEventIdType id = 29; + + Fw::SerializeStatus stat = buff.serialize(val); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + Fw::Time timeTag(TimeBase::TB_NONE, 0, 0); + U32 cmdSeq = 21; + + // enable report filter + this->clearHistory(); + FilterSeverity reportFilterLevel = FilterSeverity::WARNING_HI; + + switch (filter.e) { + case Fw::LogSeverity::WARNING_HI: + reportFilterLevel = FilterSeverity::WARNING_HI; + break; + case Fw::LogSeverity::WARNING_LO: + reportFilterLevel = FilterSeverity::WARNING_LO; + break; + case Fw::LogSeverity::COMMAND: + reportFilterLevel = FilterSeverity::COMMAND; + break; + case Fw::LogSeverity::ACTIVITY_HI: + reportFilterLevel = FilterSeverity::ACTIVITY_HI; + break; + case Fw::LogSeverity::ACTIVITY_LO: + reportFilterLevel = FilterSeverity::ACTIVITY_LO; + break; + case Fw::LogSeverity::DIAGNOSTIC: + reportFilterLevel = FilterSeverity::DIAGNOSTIC; + break; + default: + ASSERT_TRUE(false); + break; + } + + this->clearHistory(); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, reportFilterLevel, Enabled::ENABLED); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_EVENT_FILTER, cmdSeq, Fw::CmdResponse::OK); + + this->m_receivedPacket = false; + + this->invoke_to_LogRecv(0, id, timeTag, filter, buff); + + // should not have received packet + ASSERT_FALSE(this->m_receivedPacket); + // dispatch message + this->m_impl.doDispatch(); + // should have received packet + ASSERT_TRUE(this->m_receivedPacket); + // verify contents + // first piece should be log packet descriptor + FwPacketDescriptorType desc; + stat = this->m_sentPacket.deserialize(desc); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(desc, static_cast(Fw::ComPacketType::FW_PACKET_LOG)); + // next piece should be event ID + FwEventIdType sentId; + stat = this->m_sentPacket.deserialize(sentId); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(sentId, id); + // next piece is time tag + Fw::Time recTimeTag(TimeBase::TB_NONE, 0, 0); + stat = this->m_sentPacket.deserialize(recTimeTag); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_TRUE(timeTag == recTimeTag); + // next piece is event argument + U32 readVal; + stat = this->m_sentPacket.deserialize(readVal); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(readVal, val); + // packet should be empty + ASSERT_EQ(this->m_sentPacket.getBuffLeft(), 0u); + + // Disable severity filter + this->clearHistory(); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, reportFilterLevel, Enabled::DISABLED); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_EVENT_FILTER, cmdSeq, Fw::CmdResponse::OK); + + this->m_receivedPacket = false; + + this->invoke_to_LogRecv(0, id, timeTag, filter, buff); + + // should not have received packet - all we can check since no message is dispatched. + ASSERT_FALSE(this->m_receivedPacket); +} + +void EventManagerTester::runFilterInvalidCommands() { + U32 cmdSeq = 21; + this->clearHistory(); + FilterSeverity reportFilterLevel = FilterSeverity::WARNING_HI; + Enabled filterEnabled(static_cast(10)); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, reportFilterLevel, filterEnabled); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_EVENT_FILTER, cmdSeq, Fw::CmdResponse::FORMAT_ERROR); + this->clearHistory(); + reportFilterLevel = FilterSeverity::WARNING_HI; + filterEnabled.e = static_cast(-2); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, reportFilterLevel, filterEnabled); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_EVENT_FILTER, cmdSeq, Fw::CmdResponse::FORMAT_ERROR); + FilterSeverity eventLevel; + this->clearHistory(); + Enabled reportEnable = Enabled::ENABLED; + eventLevel.e = static_cast(-1); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, eventLevel, reportEnable); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_EVENT_FILTER, cmdSeq, Fw::CmdResponse::FORMAT_ERROR); + + this->clearHistory(); + + reportEnable = Enabled::ENABLED; + eventLevel.e = static_cast(100); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, eventLevel, reportEnable); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_EVENT_FILTER, cmdSeq, Fw::CmdResponse::FORMAT_ERROR); +} + +void EventManagerTester::runFilterEventNominal() { + for (Fw::LogSeverity::t sev = Fw::LogSeverity::WARNING_HI; sev <= Fw::LogSeverity::DIAGNOSTIC; + sev = static_cast(sev + 1)) { + this->runWithFilters(sev); + } +} + +void EventManagerTester::runFilterIdNominal() { + U32 cmdSeq = 21; + + // for a set of IDs, fill filter + + REQUIREMENT("AL-003"); + + for (FwSizeType filterID = 1; filterID <= TELEM_ID_FILTER_SIZE; filterID++) { + this->clearHistory(); + this->clearEvents(); + this->sendCmd_SET_ID_FILTER(0, cmdSeq, filterID, Enabled::ENABLED); + // dispatch message + this->m_impl.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_ID_FILTER, cmdSeq, Fw::CmdResponse::OK); + ASSERT_EVENTS_SIZE(1); + ASSERT_EVENTS_ID_FILTER_ENABLED_SIZE(1); + ASSERT_EVENTS_ID_FILTER_ENABLED(0, filterID); + // send it again, to verify it will accept a second add + this->clearHistory(); + this->clearEvents(); + this->sendCmd_SET_ID_FILTER(0, cmdSeq, filterID, Enabled::ENABLED); + // dispatch message + this->m_impl.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_ID_FILTER, cmdSeq, Fw::CmdResponse::OK); + ASSERT_EVENTS_SIZE(1); + ASSERT_EVENTS_ID_FILTER_ENABLED_SIZE(1); + ASSERT_EVENTS_ID_FILTER_ENABLED(0, filterID); + } + + // Try to send the IDs that are filtered + for (FwSizeType filterID = 1; filterID <= TELEM_ID_FILTER_SIZE; filterID++) { + this->clearHistory(); + this->clearEvents(); + + Fw::LogBuffer buff; + U32 val = 10; + FwEventIdType id = filterID; + + Fw::SerializeStatus stat = buff.serialize(val); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + Fw::Time timeTag(TimeBase::TB_NONE, 0, 0); + + this->m_receivedPacket = false; + + this->invoke_to_LogRecv(0, id, timeTag, Fw::LogSeverity::ACTIVITY_HI, buff); + + // should not get a packet + ASSERT_FALSE(this->m_receivedPacket); + } + + // send one of the IDs as a FATAL, it should not be filtered event thought the ID is in the filter + this->clearHistory(); + this->clearEvents(); + + Fw::LogBuffer buff; + U32 val = 10; + FwEventIdType id = 1; + + Fw::SerializeStatus stat = buff.serialize(val); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + Fw::Time timeTag(TimeBase::TB_NONE, 0, 0); + + this->m_receivedPacket = false; + + this->invoke_to_LogRecv(0, id, timeTag, Fw::LogSeverity::FATAL, buff); + this->m_impl.doDispatch(); + + // should get a packet anyway + ASSERT_TRUE(this->m_receivedPacket); + + // Try to add to the full filter. It should be rejected + this->clearHistory(); + this->clearEvents(); + this->sendCmd_SET_ID_FILTER(0, cmdSeq, TELEM_ID_FILTER_SIZE + 1, Enabled::ENABLED); + // dispatch message + this->m_impl.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_ID_FILTER, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + ASSERT_EVENTS_SIZE(1); + ASSERT_EVENTS_ID_FILTER_LIST_FULL_SIZE(1); + ASSERT_EVENTS_ID_FILTER_LIST_FULL(0, TELEM_ID_FILTER_SIZE + 1); + + // Now clear them + + for (FwSizeType filterID = 1; filterID <= TELEM_ID_FILTER_SIZE; filterID++) { + this->clearHistory(); + this->clearEvents(); + this->sendCmd_SET_ID_FILTER(0, cmdSeq, filterID, Enabled::DISABLED); + // dispatch message + this->m_impl.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_ID_FILTER, cmdSeq, Fw::CmdResponse::OK); + ASSERT_EVENTS_SIZE(1); + ASSERT_EVENTS_ID_FILTER_REMOVED_SIZE(1); + ASSERT_EVENTS_ID_FILTER_REMOVED(0, filterID); + } + + // Try to clear one that doesn't exist + + this->clearHistory(); + this->clearEvents(); + this->sendCmd_SET_ID_FILTER(0, cmdSeq, 10, Enabled::DISABLED); + // dispatch message + this->m_impl.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_ID_FILTER, cmdSeq, Fw::CmdResponse::EXECUTION_ERROR); + ASSERT_EVENTS_SIZE(1); + ASSERT_EVENTS_ID_FILTER_NOT_FOUND_SIZE(1); + ASSERT_EVENTS_ID_FILTER_NOT_FOUND(0, 10); + + // Send an invalid argument + this->clearHistory(); + this->clearEvents(); + Enabled idEnabled(static_cast(10)); + this->sendCmd_SET_ID_FILTER(0, cmdSeq, 10, idEnabled); + // dispatch message + this->m_impl.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_ID_FILTER, cmdSeq, Fw::CmdResponse::FORMAT_ERROR); + ASSERT_EVENTS_SIZE(0); +} + +void EventManagerTester::runFilterDump() { + U32 cmdSeq = 21; + // set random set of filters + + this->sendCmd_SET_EVENT_FILTER(0, 0, FilterSeverity::WARNING_HI, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, 0, FilterSeverity::WARNING_LO, Enabled::DISABLED); + this->sendCmd_SET_EVENT_FILTER(0, 0, FilterSeverity::COMMAND, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, 0, FilterSeverity::ACTIVITY_HI, Enabled::DISABLED); + this->sendCmd_SET_EVENT_FILTER(0, 0, FilterSeverity::ACTIVITY_LO, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, 0, FilterSeverity::DIAGNOSTIC, Enabled::ENABLED); + + this->sendCmd_SET_ID_FILTER(0, cmdSeq, 4, Enabled::ENABLED); + // dispatch message + this->m_impl.doDispatch(); + + this->sendCmd_SET_ID_FILTER(0, cmdSeq, 13, Enabled::ENABLED); + // dispatch message + this->m_impl.doDispatch(); + + this->sendCmd_SET_ID_FILTER(0, cmdSeq, 4000, Enabled::ENABLED); + // dispatch message + this->m_impl.doDispatch(); + + // send command to dump the filters + + this->clearHistory(); + this->clearEvents(); + this->sendCmd_DUMP_FILTER_STATE(0, cmdSeq); + // dispatch message + this->m_impl.doDispatch(); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_DUMP_FILTER_STATE, cmdSeq, Fw::CmdResponse::OK); + ASSERT_EVENTS_SIZE(6 + 3); + ASSERT_EVENTS_SEVERITY_FILTER_STATE_SIZE(6); + ASSERT_EVENTS_SEVERITY_FILTER_STATE(0, FilterSeverity::WARNING_HI, true); + ASSERT_EVENTS_SEVERITY_FILTER_STATE(1, FilterSeverity::WARNING_LO, false); + ASSERT_EVENTS_SEVERITY_FILTER_STATE(2, FilterSeverity::COMMAND, true); + ASSERT_EVENTS_SEVERITY_FILTER_STATE(3, FilterSeverity::ACTIVITY_HI, false); + ASSERT_EVENTS_SEVERITY_FILTER_STATE(4, FilterSeverity::ACTIVITY_LO, true); + ASSERT_EVENTS_SEVERITY_FILTER_STATE(5, FilterSeverity::DIAGNOSTIC, true); +} + +void EventManagerTester::runEventFatal() { + Fw::LogBuffer buff; + U32 val = 10; + FwEventIdType id = 29; + U32 cmdSeq = 21; + REQUIREMENT("AL-004"); + + Fw::SerializeStatus stat = buff.serialize(val); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + Fw::Time timeTag(TimeBase::TB_NONE, 0, 0); + + this->m_receivedPacket = false; + + this->invoke_to_LogRecv(0, id, timeTag, Fw::LogSeverity::FATAL, buff); + + // should not have received packet + ASSERT_FALSE(this->m_receivedPacket); + // should have seen event port + ASSERT_TRUE(this->m_receivedFatalEvent); + ASSERT_EQ(this->m_fatalID, id); + // dispatch message + this->m_impl.doDispatch(); + // should have received packet + ASSERT_TRUE(this->m_receivedPacket); + // verify contents + // first piece should be log packet descriptor + FwPacketDescriptorType desc; + stat = this->m_sentPacket.deserialize(desc); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(desc, static_cast(Fw::ComPacketType::FW_PACKET_LOG)); + // next piece should be event ID + FwEventIdType sentId; + stat = this->m_sentPacket.deserialize(sentId); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(sentId, id); + // next piece is time tag + Fw::Time recTimeTag(TimeBase::TB_NONE, 0, 0); + stat = this->m_sentPacket.deserialize(recTimeTag); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_TRUE(timeTag == recTimeTag); + // next piece is event argument + U32 readVal; + stat = this->m_sentPacket.deserialize(readVal); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(readVal, val); + // packet should be empty + ASSERT_EQ(this->m_sentPacket.getBuffLeft(), 0u); + // Turn on all filters and make sure FATAL still gets through + + this->clearHistory(); + this->clearEvents(); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::WARNING_HI, Enabled::DISABLED); + ASSERT_CMD_RESPONSE_SIZE(1); + ASSERT_CMD_RESPONSE(0, EventManager::OPCODE_SET_EVENT_FILTER, cmdSeq, Fw::CmdResponse::OK); + + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::WARNING_LO, Enabled::DISABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::COMMAND, Enabled::DISABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::ACTIVITY_HI, Enabled::DISABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::ACTIVITY_LO, Enabled::DISABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::DIAGNOSTIC, Enabled::DISABLED); + + this->m_receivedPacket = false; + + this->invoke_to_LogRecv(0, id, timeTag, Fw::LogSeverity::FATAL, buff); + + // should not have received packet + ASSERT_FALSE(this->m_receivedPacket); + // dispatch message + this->m_impl.doDispatch(); + // should have received packet + ASSERT_TRUE(this->m_receivedPacket); + // verify contents + // first piece should be log packet descriptor + stat = this->m_sentPacket.deserialize(desc); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(desc, static_cast(Fw::ComPacketType::FW_PACKET_LOG)); + // next piece should be event ID + stat = this->m_sentPacket.deserialize(sentId); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(sentId, id); + // next piece is time tag + stat = this->m_sentPacket.deserialize(recTimeTag); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_TRUE(timeTag == recTimeTag); + // next piece is event argument + stat = this->m_sentPacket.deserialize(readVal); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(readVal, val); + // packet should be empty + ASSERT_EQ(this->m_sentPacket.getBuffLeft(), 0u); + + // turn off filters + + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::WARNING_HI, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::WARNING_LO, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::COMMAND, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::ACTIVITY_HI, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::ACTIVITY_LO, Enabled::ENABLED); + this->sendCmd_SET_EVENT_FILTER(0, cmdSeq, FilterSeverity::DIAGNOSTIC, Enabled::ENABLED); +} + +void EventManagerTester::writeEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value) { + Fw::LogBuffer buff; + + Fw::SerializeStatus stat = buff.serialize(value); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + Fw::Time timeTag(TimeBase::TB_NONE, 1, 2); + + this->m_receivedPacket = false; + + this->invoke_to_LogRecv(0, id, timeTag, severity, buff); + + // should not have received packet + ASSERT_FALSE(this->m_receivedPacket); + // dispatch message + this->m_impl.doDispatch(); + // should have received packet + ASSERT_TRUE(this->m_receivedPacket); + // verify contents + // first piece should be log packet descriptor + FwPacketDescriptorType desc; + stat = this->m_sentPacket.deserialize(desc); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(desc, static_cast(Fw::ComPacketType::FW_PACKET_LOG)); + // next piece should be event ID + FwEventIdType sentId; + stat = this->m_sentPacket.deserialize(sentId); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(sentId, id); + // next piece is time tag + Fw::Time recTimeTag(TimeBase::TB_NONE, 1, 2); + stat = this->m_sentPacket.deserialize(recTimeTag); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_TRUE(timeTag == recTimeTag); + // next piece is event argument + U32 readVal; + stat = this->m_sentPacket.deserialize(readVal); + ASSERT_EQ(Fw::FW_SERIALIZE_OK, stat); + ASSERT_EQ(readVal, value); + // packet should be empty + ASSERT_EQ(this->m_sentPacket.getBuffLeft(), 0u); +} + +void EventManagerTester::readEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value, Os::File& file) { + static const BYTE delimiter = 0xA5; + + // first read should be delimiter + BYTE de; + FwSizeType readSize = static_cast(sizeof(de)); + + ASSERT_EQ(file.read(&de, readSize, Os::File::WaitType::WAIT), Os::File::OP_OK); + ASSERT_EQ(delimiter, de); + // next is LogPacket + Fw::ComBuffer comBuff; + // size is specific to this test + readSize = sizeof(FwPacketDescriptorType) + sizeof(FwEventIdType) + Fw::Time::SERIALIZED_SIZE + sizeof(U32); + ASSERT_EQ(file.read(comBuff.getBuffAddr(), readSize, Os::File::WaitType::WAIT), Os::File::OP_OK); + comBuff.setBuffLen(readSize); + + // deserialize LogPacket + Fw::LogPacket packet; + Fw::Time time(TimeBase::TB_NONE, 1, 2); + Fw::LogBuffer logBuff; + ASSERT_EQ(comBuff.deserialize(packet), Fw::FW_SERIALIZE_OK); + + // read back values + ASSERT_EQ(id, packet.getId()); + ASSERT_EQ(time, packet.getTimeTag()); + logBuff = packet.getLogBuffer(); + U32 readValue; + ASSERT_EQ(logBuff.deserialize(readValue), Fw::FW_SERIALIZE_OK); + ASSERT_EQ(value, readValue); +} + +void EventManagerTester::textLogIn(const FwEventIdType id, //!< The event ID + const Fw::Time& timeTag, //!< The time + const Fw::LogSeverity severity, //!< The severity + const Fw::TextLogString& text //!< The event string +) { + TextLogEntry e = {id, timeTag, severity, text}; + + printTextLogHistoryEntry(e, stdout); +} +void EventManagerTester ::from_pingOut_handler(const FwIndexType portNum, U32 key) { + this->pushFromPortEntry_pingOut(key); +} +} // namespace Svc diff --git a/Svc/EventManager/test/ut/EventManagerTester.hpp b/Svc/EventManager/test/ut/EventManagerTester.hpp new file mode 100644 index 0000000000..b34bff2175 --- /dev/null +++ b/Svc/EventManager/test/ut/EventManagerTester.hpp @@ -0,0 +1,76 @@ +/* + * EventManagerTester.hpp + * + * Created on: Mar 18, 2015 + * Author: tcanham + */ + +#ifndef EventManager_TEST_UT_EventManager_TESTER_HPP_ +#define EventManager_TEST_UT_EventManager_TESTER_HPP_ + +#include +#include +#include + +namespace Svc { + +class EventManagerTester : public Svc::EventManagerGTestBase { + public: + explicit EventManagerTester(Svc::EventManager& inst); + virtual ~EventManagerTester(); + + void runEventNominal(); + void runFilterEventNominal(); + void runFilterIdNominal(); + void runFilterDump(); + void runFilterInvalidCommands(); + void runEventFatal(); + void runFileDump(); + void runFileDumpErrors(); + + private: + void from_PktSend_handler(const FwIndexType portNum, //!< The port number + Fw::ComBuffer& data, //!< Buffer containing packet data + U32 context //!< context (not used) + ) override; + void from_FatalAnnounce_handler(const FwIndexType portNum, //!< The port number + FwEventIdType Id //!< The ID of the FATAL event + ) override; + + Svc::EventManager& m_impl; + + bool m_receivedPacket; + Fw::ComBuffer m_sentPacket; + + bool m_receivedFatalEvent; + FwEventIdType m_fatalID; + + void runWithFilters(Fw::LogSeverity filter); + + void writeEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value); + void readEvent(FwEventIdType id, Fw::LogSeverity severity, U32 value, Os::File& file); + + // enumeration to tell what kind of error to inject + typedef enum { + FILE_WRITE_WRITE_ERROR, // return a bad read status + FILE_WRITE_SIZE_ERROR, // return a bad size + } FileWriteTestType; + FileWriteTestType m_writeTestType; + FwSizeType m_writeSize; + + void textLogIn(const FwEventIdType id, //!< The event ID + const Fw::Time& timeTag, //!< The time + const Fw::LogSeverity severity, //!< The severity + const Fw::TextLogString& text //!< The event string + ) override; + + //! Handler for from_pingOut + //! + void from_pingOut_handler(const FwIndexType portNum, /*!< The port number*/ + U32 key /*!< Value to return to pinger*/ + ) override; +}; + +} /* namespace Svc */ + +#endif /* EventManager_TEST_UT_EventManager_TESTER_HPP_ */ diff --git a/Svc/ActiveLogger/test/ut/Readme.txt b/Svc/EventManager/test/ut/Readme.txt similarity index 80% rename from Svc/ActiveLogger/test/ut/Readme.txt rename to Svc/EventManager/test/ut/Readme.txt index 2d4c541527..625e80811c 100644 --- a/Svc/ActiveLogger/test/ut/Readme.txt +++ b/Svc/EventManager/test/ut/Readme.txt @@ -1,6 +1,6 @@ This test can be run by executing the following: -From Svc/ActiveLogger: +From Svc/EventManager: "make ut run_ut" diff --git a/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp b/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp index 2efadc66af..d76f9d0a75 100644 --- a/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp +++ b/Svc/RateGroupDriver/test/ut/RateGroupDriverImplTester.hpp @@ -34,4 +34,4 @@ namespace Svc { } /* namespace Svc */ -#endif /* RATEGROUPDRIVER_TEST_UT_ACTIVELOGGERIMPLTESTER_HPP_ */ +#endif diff --git a/Svc/Subtopologies/CdhCore/CdhCore.fpp b/Svc/Subtopologies/CdhCore/CdhCore.fpp index 9dec9db172..54a0fdd94d 100644 --- a/Svc/Subtopologies/CdhCore/CdhCore.fpp +++ b/Svc/Subtopologies/CdhCore/CdhCore.fpp @@ -7,7 +7,7 @@ module CdhCore { stack size CdhCoreConfig.StackSizes.cmdDisp \ priority CdhCoreConfig.Priorities.cmdDisp - instance events: Svc.ActiveLogger base id CdhCoreConfig.BASE_ID + 0x0200 \ + instance events: Svc.EventManager base id CdhCoreConfig.BASE_ID + 0x0200 \ queue size CdhCoreConfig.QueueSizes.events \ stack size CdhCoreConfig.StackSizes.events \ priority CdhCoreConfig.Priorities.events diff --git a/cmake/test/src/settings.py b/cmake/test/src/settings.py index 567a31a4c3..b59246e461 100644 --- a/cmake/test/src/settings.py +++ b/cmake/test/src/settings.py @@ -38,13 +38,13 @@ STANDARD_MODULES = [ ] REF_MODULES = [ - "Svc_ActiveLogger", "Svc_ActiveRateGroup", "Svc_AssertFatalAdapter", "Svc_BufferManager", "Svc_CmdDispatcher", "Svc_CmdSequencer", "Svc_Cycle", + "Svc_EventManager", "Svc_Fatal", "Svc_FatalHandler", "Svc_FileDownlink", diff --git a/cmake/test/src/test_unittests.py b/cmake/test/src/test_unittests.py index ef9b59aa92..0e795bc7e4 100644 --- a/cmake/test/src/test_unittests.py +++ b/cmake/test/src/test_unittests.py @@ -33,7 +33,7 @@ UNIT_TESTS = [ "Fw_Types_ut_exe", "Os_ut_exe", "Ref_SignalGen_ut_exe", - "Svc_ActiveLogger_ut_exe", + "Svc_EventManager_ut_exe", "Svc_ActiveRateGroup_ut_exe", "Svc_ActiveTextLogger_ut_exe", "Svc_AssertFatalAdapter_ut_exe", diff --git a/cmake/utilities.cmake b/cmake/utilities.cmake index 5d42caf826..feb06a422c 100644 --- a/cmake/utilities.cmake +++ b/cmake/utilities.cmake @@ -481,8 +481,8 @@ endfunction() # 1. If passed a path, the module name is the '_'ed variant of the relative path from BUILD_ROOT # 2. If passes something which does not exist on the file system, it is just '_'ed # -# i.e. ${BUILD_ROOT}/Svc/ActiveLogger becomes Svc_ActiveLogger -# Svc/ActiveLogger also becomes Svc_ActiveLogger +# i.e. ${BUILD_ROOT}/Svc/EventManager becomes Svc_EventManager +# Svc/EventManager also becomes Svc_EventManager # # - **DIRECTORY_PATH:** (optional) path to infer MODULE_NAME from. Default: CMAKE_CURRENT_LIST_DIR # - **Return: MODULE_NAME** (set in parent scope) diff --git a/default/config/CMakeLists.txt b/default/config/CMakeLists.txt index b17083285c..2673af2f3a 100644 --- a/default/config/CMakeLists.txt +++ b/default/config/CMakeLists.txt @@ -13,7 +13,7 @@ register_fprime_config( "${CMAKE_CURRENT_LIST_DIR}/PolyDbCfg.fpp" "${CMAKE_CURRENT_LIST_DIR}/VersionCfg.fpp" HEADERS - "${CMAKE_CURRENT_LIST_DIR}/ActiveLoggerImplCfg.hpp" + "${CMAKE_CURRENT_LIST_DIR}/EventManagerCfg.hpp" "${CMAKE_CURRENT_LIST_DIR}/ActiveRateGroupCfg.hpp" "${CMAKE_CURRENT_LIST_DIR}/BufferManagerComponentImplCfg.hpp" "${CMAKE_CURRENT_LIST_DIR}/CommandDispatcherImplCfg.hpp" diff --git a/default/config/ActiveLoggerImplCfg.hpp b/default/config/EventManagerCfg.hpp similarity index 79% rename from default/config/ActiveLoggerImplCfg.hpp rename to default/config/EventManagerCfg.hpp index e24f973b51..69bb77944d 100644 --- a/default/config/ActiveLoggerImplCfg.hpp +++ b/default/config/EventManagerCfg.hpp @@ -1,12 +1,12 @@ /* - * ActiveLoggerImplCfg.hpp + * EventManagerCfg.hpp * * Created on: Apr 16, 2015 * Author: tcanham */ -#ifndef ACTIVELOGGER_ACTIVELOGGERIMPLCFG_HPP_ -#define ACTIVELOGGER_ACTIVELOGGERIMPLCFG_HPP_ +#ifndef Config_EventManagerCfg_HPP_ +#define Config_EventManagerCfg_HPP_ // set default filters @@ -24,4 +24,4 @@ enum { TELEM_ID_FILTER_SIZE = 25, //!< Size of telemetry ID filter }; -#endif /* ACTIVELOGGER_ACTIVELOGGERIMPLCFG_HPP_ */ +#endif /* Config_EventManagerCfg_HPP_ */ diff --git a/docs/user-manual/overview/04-cmd-evt-chn-prm.md b/docs/user-manual/overview/04-cmd-evt-chn-prm.md index 2751d826ec..d6387c55c8 100644 --- a/docs/user-manual/overview/04-cmd-evt-chn-prm.md +++ b/docs/user-manual/overview/04-cmd-evt-chn-prm.md @@ -75,7 +75,7 @@ while a successful response moves to the next command in the sequence. Events represent a log of activities taken by the embedded system. Events can be thought of in the same way as a program execution log in that they enable the ability to trace the execution of the system. Events are sent out of the system via -the `Svc::ActiveLogger` component and components defining events should hook up the log port to it. If console logging is +the `Svc::EventManager` component and components defining events should hook up the log port to it. If console logging is desired, the text log port can be hooked up to the `Svc::PassiveConsoleTextLogger` component. Events are defined per component and are typically used to capture what the component is doing. Events can occur sporadically; however, they should all be captured for downlink. Events are defined by the following properties: @@ -108,7 +108,7 @@ events: ### Event Logging -Events first acquire a time tag to represent when they occurred and then are typically sent to the `Svc::ActiveLogger` +Events first acquire a time tag to represent when they occurred and then are typically sent to the `Svc::EventManager` component on their way to be sent down to the ground. This logger component both processes the event and also recognizes and begins responses for FATAL severity events. diff --git a/docs/user-manual/overview/source-tree.md b/docs/user-manual/overview/source-tree.md index ab6e353a41..87d8104d07 100644 --- a/docs/user-manual/overview/source-tree.md +++ b/docs/user-manual/overview/source-tree.md @@ -157,7 +157,7 @@ methods. For these directories, each file will not be described, but a higher-level description of what each directory contains will be given instead. The descriptions are as follows. -### ActiveLogger +### EventManager This directory contains a component FPP description and implementation for an active component that accepts serialized log events. The input @@ -214,7 +214,7 @@ components. The port passes a time stamp indicating when the cycle started. ### Fatal The directory specifies a port used to pass a notification that a FATAL -event has occurred. It is currently produced by the ActiveLogger +event has occurred. It is currently produced by the EventManager component when it receives a FATAL event from a component. ### GndIf