📜 ⬆️ ⬇️

libuniset2 is a library for creating ACS. It is better to see once ... Part 3 (Creating a management process)

In the previous parts ( part 1 and part 2 ) I described the creation of the project and gave an example of creating an imitator ... Now we are implementing the control algorithm itself ...

Creating a management process


Creating a control process is no different from how we created the simulator . Therefore, I will describe the creation itself quickly, but I will show some other uniset-codegen features that I didn’t pay attention to when creating the simulator.
So the steps to create a management process

Create a process description file


controller.src.xml
<?xml version="1.0" encoding="utf-8"?> <!-- name -   msgcount -       sleep_msec -       type ==== in -  (  ) out -  () --> <Controller> <settings> <set name="class-name" val="Controller"/> <set name="msg-count" val="30"/> <set name="sleep-msec" val="150"/> </settings> <variables> <item name="HiLevel" type="long" const="1" default="95" max="100" comment="  "/> <item name="LowLevel" type="long" const="1" default="5" min=0" comment="  "/> </variables> <smap> <item name="OnControl_s" vartype="in" iotype="DI" comment="  "/> <item name="Level_s" vartype="in" iotype="AI" comment="   "/> <item name="cmdLoad_c" vartype="out" iotype="DO" comment="  ''"/> <item name="cmdUnload_c" vartype="out" iotype="DO" comment="  ''"/> </smap> <msgmap> </msgmap> </Controller> 



Here again, everything is simple. For our management process, there is a command to enable OnControl_s (input), there is a current level in the tank Level_s (input), and there are two outputs: to manage cmdLoad_c - content, cmdUnload_c - emptying.

From interesting ( new ) here I pay attention to the variables section. It describes two constants
determining the minimum and maximum thresholds at which the process changes teams. The presence of constants is not interesting in itself, but one of the features provided by the uniset2-codegen utility is shown here , which allows the “src.xml file” to define part of the “class fields”.
Why do you need it?
It is clear that any normal management process should be able to be configured, i.e. need the ability to change some process parameters without recompilation. Any thresholds, timeouts, etc. - the best candidates for this. There are three levels to configure these parameters ( in order of increasing priority ):

So, when declaring a parameter in the src.xml file, the code that implements this logic will be automatically generated for it. First, the variable is initialized with the default value specified in the field default = ".." . If you need to redefine it, then in the configuration section for this process (in the configure.xml project file) you can override this value
Like this
 <settings> ... <Controller name="Controller1" HiLevel="90"/> </settings> 


And if you need to override the parameter directly in the startup script (for example, for testing), then you can set it with the command line argument --ObjectName-variable val . In our case it will be like this:
 --Controller1-HiLoad 93 

Uniset-codegen assumes all this routine for creating a sequence and priority of initialization.
')
In addition, if you look closely at controller.src.xml, you can see two more parameters min and max . If such fields (or one of them) are set for a variable, then a code will be generated that checks (in the object constructor) that the specified value falls within the specified range: > = min and <= max . If not included, an exception will be thrown ( although this policy can be configured by prohibiting the exception and only warning in the logs will remain ).

All features of the uniset-codegen utility are described in its documentation ...

Generating the skeleton for the process


 uniset2-codegen -n Controller --ask --no-main controller.src.xml 

Or rather we insert this command into the Makefile.am
Makefile.am
 bin_PROGRAMS = controller BUILT_SOURCES = Controller_SK.h Controller_SK.h controller_LDADD = $(top_builddir)/lib/libUniSetExample.la #controller_CPPFLAGS = controller_SOURCES = Controller_SK.cc Controller.cc controller-main.cc Controller_SK.h Controller_SK.cc: controller.src.xml @UNISET_CODEGEN@ -n Controller --topdir $(top_builddir)/ --ask --no-main controller.src.xml clean-local: rm -rf *_SK.cc *_SK.h *.log 



Configuring the management process (bindings)


Configuring is two steps:

Set id

To do this, simply write the following line in the objects section ( id - any, but the main thing is unique)
 <objects name="Objects" section="Objects"> ... <item id="20002" name="Controller1"/> </objects> 

Create a setup section and bind sensors

As you remember , configuration can be done either manually or using the uniset-linkeditor utility (by running the edit_controller.sh script in the src / Algorithms / Controller directory), as a result the configuration section should appear in the configure.xml project file:
 <settings> <Controller name="Controller1" HiLevel="90" OnControl_s="OnControl_S" Level_s="Level_AS" cmdLoad_c="CmdLoad_C" cmdUnload_c="CmdUnload_C"/> </settings> 


We write implementation


Here we need to implement the processing of messages from the sensors, i.e. define only one function virtual void sensorInfo (const UniSetTypes :: SensorMessage * sm) override;
Implementation
 void Controller::sensorInfo(const UniSetTypes::SensorMessage* sm) { if( sm->id == OnControl_s ) { if( sm->value ) { myinfo << myname << "(sensorInfo):   .." << endl; if( in_Level_s > LowLevel && in_Level_s < HiLevel ) { //   "" out_cmdLoad_c = true; out_cmdUnload_c = false; } else processing(); } else { myinfo << myname << "(sensorInfo):   .." << endl; //     out_cmdLoad_c = false; out_cmdUnload_c = false; } } else if( sm->id == Level_s ) { //   ,   if( in_OnControl_s ) processing(); } } // ----------------------------------------------------------------------------- void Controller::processing() { if( in_Level_s >= HiLevel ) { myinfo << myname << "(sensorInfo):   (" << HiLevel << ").  .." << endl; //  "" out_cmdLoad_c = false; out_cmdUnload_c = true; } else if( in_Level_s <= LowLevel ) { myinfo << myname << "(sensorInfo):   (" << LowLevel << ").  .." << endl; //  "" out_cmdLoad_c = true; out_cmdUnload_c = false; } } 



For completeness, here is the header file.
Controller.h
 #ifndef Controller_H_ #define Controller_H_ // ----------------------------------------------------------------------------- #include <string> #include "Controller_SK.h" // ----------------------------------------------------------------------------- /*! \page_Controller    - \ref sec_controller_Common \section sec_controller_Common         OnControl_s=1  ,    ,       HiLevel.    «»   ,     LowLevel.    .    OnControl_s=0,   . */ class Controller: public Controller_SK { public: Controller( UniSetTypes::ObjectId id, xmlNode* cnode, const std::string& prefix = "" ); virtual ~Controller(); protected: virtual void sensorInfo( const UniSetTypes::SensorMessage* sm ) override; virtual std::string getMonitInfo() override; //  ( ) void processing(); private: }; // ----------------------------------------------------------------------------- #endif // Controller_H_ 


Create main ()


Everything is by analogy with the simulator , so I’ll give the result immediately:
controller-main.cc
 #include <UniSetActivator.h> #include "UniSetExampleConfiguration.h" #include "Controller.h" // ----------------------------------------------------------------------------- using namespace UniSetTypes; using namespace std; // ----------------------------------------------------------------------------- int main( int argc, const char** argv ) { try { auto conf = uniset_init(argc, argv); auto act = UniSetActivator::Instance(); auto cn = UniSetExample::make_object<Controller>("Controller1", "Controller"); act->add(cn); SystemMessage sm(SystemMessage::StartUp); act->broadcast( sm.transport_msg() ); act->run(false); return 0; } catch( const Exception& ex ) { cerr << "(controller): " << ex << endl; } catch( const std::exception& ex ) { cerr << "(controller): " << ex.what() << endl; } catch(...) { cerr << "(controller): catch(...)" << endl; } return 1; } // ----------------------------------------------------------------------------- 



Trial run


If everything compiles, then go to the src / Algorithms / Controller directory and run ./start_fg.sh . On the screen, if all is well, we see something like this:
 [pv@pvbook Controller]$ ./start_fg.sh 04/03/2016 15:39:19( info): Controller1(waitSM): waiting SM ready 60000 msec testID=100 04/03/2016 15:39:19( info): Controller1(sensorInfo):   .. 

Important : Do not forget to start SharedMemory before this ( in the src / SharedMemory directory run ./start_fg.sh ).

Check that the process is visible to others. To do this, go to src / Services / Administrator and run ./exist . On the screen should see:
Output to console at process start
 [pv@pvbook Administrator]$ ./exist ||=======******** UNISET-EXAMPLE/Services ********=========|| !!!!!! ||=======******** UNISET-EXAMPLE/Controllers ********=========|| (22000 )SharedMemory1 <--- exist ok ||=======******** UNISET-EXAMPLE/Objects ********=========|| (20002 )Controller1 <--- exist ok 



Well, that's all with the creation of the management process ... further adjustment.

Small total


The management process (or simulator) is not difficult to create. Just a few steps:


How to organize work and what tools for uniset are for this purpose, we will see in the next part ...

Source: https://habr.com/ru/post/278565/


All Articles