📜 ⬆️ ⬇️

libuniset2 is a library for creating ACS. It’s better to see once ... Part 4 (Adjustment)

In the previous parts ( Part 1 , Part 2 , Part 3 ), the creation of two processes was described: the simulator and the control process ... Now it’s time for the adjustment .

So, at the moment we have already implemented and run the following processes:

you can start adjusting ...

Adjustment. The prologue.


The time has come for a small explanatory picture to understand what is happening here ...
Exchange structure

In fact, of course, everything is a bit more complicated, but the figure is designed to help understand how we have “information flows” .
So..

')
Once all the interaction takes place through the sensors, the adjustment, in general, is “setting the sensors” and “tracking the current status of the sensors”. For these purposes, several utilities are included in libuniset2-utils :

All these utilities are actively used in debugging, but they are tools for tracking the “external” life of processes. And there are two additional mechanisms that allow you to observe the "life of processes" from the inside:

Of course, it would be possible to start the setup by running two processes at once, and watch what is happening there. But it is more correct, if possible, to carry out the adjustment of processes separately. Let's start with the simulator.

Debugging simulator




Check that objects are available. Go to src / Services / Administrator / and run ./exist
Must see the following
Screen output
[pv@pvbook Administrator]$ ./exist ||=======******** UNISET-EXAMPLE/Services ********=========|| !!!!!! ||=======******** UNISET-EXAMPLE/Controllers ********=========|| (22000 )SharedMemory1 <--- exist ok ||=======******** UNISET-EXAMPLE/Objects ********=========|| (20001 )Imitator1 <--- exist ok [pv@pvbook Administrator]$ 


Now back to the beginning and remember what the simulator should do.
The simulator should, on arrival of the cmdLoad_C = 1 command, start simulating tank filling (growth of the Level_AS sensor), and on the arrival of the cmdUnload_C = 1 command - simulate tank emptying (reduce the Level_AS sensor).

So we must

Let's look at all the current status of the sensors. Let's use the uniset2-smviewer utility .
Enter the src / Services / SMViewer directory and run ./start_fg.sh
See this ...
Screen output
 [pv@pvbook SMViewer]$ ./start_fg.sh ====================================================== SharedMemory1  ------------------------------------------------------ ( 101) | AI | Level_AS | 0 ( 100) | DI | OnControl_S | 0 ------------------------------------------------------ ====================================================== SharedMemory1  ------------------------------------------------------ ( 103) | DO | CmdUnload_C | 0 ( 102) | DO | CmdLoad_C | 0 ------------------------------------------------------ ====================================================== SharedMemory1   ------------------------------------------------------ 


As you can see, everything is by zero ... Of course, there will be A LOT of sensors in a real project, and therefore you can ( and should) use uniset2-smviewer with grep if you want to somehow filter the output ...

The second utility we need is uniset2-smonit to see how the level sensor will change. Let's run it. Go to src / Services / SMonit / and ...
Small subtlety when using smonit
Since uniset2-smonit starts and tracks the change of the specified sensors, it must have a “return address”. For a uniset system, such an address is an identifier . By default, uniset2-smonit tries to run as TestProc . Those. it is assumed that in configure.xml in the objects section an object with the name = "TestProc" is declared. But if for some reason you don’t want to have such an object in your project, you can run uniset2-smonit with the --name XXX parameter and specify any name from existing objects in the project (not currently used).

For this utility, you need to specify which sensors we want to monitor, so it has a key --sid
(for simplicity, it is entered immediately in start_fg.sh ). As a parameter --sid you can specify an identifier , and you can specify the name of the sensor. We will specify the name Level_AS.
 ./start_fg.sh Level_AS 

Command output


smonit starts and hangs waiting for changes. At the same time, the current state of the sensor is displayed at the beginning. From the output, you can see the name , the last modification time (including microseconds), the process identifier that saved this sensor in SM (in this case, Imitator1), and the current value (and in the form of float - fvalue ).

Everything seems to be ready, set the cmdLoad_C = 1 sensor and see how the Level_AS sensor ran.
For exposure just we will use admin th.
 [pv@pvbook Administrator]$ ./setValue CmdLoad_C=1 

and switching to the console, where smonit is running, look at how the sensor ran (from 0 to 100).
Smonit screen output


So the increase is working. The reduction is checked in the same way ... first you need to remember to reset the previous command, and we will do it in one fell swoop
 [pv@pvbook Administrator]$ ./setValue CmdLoad_C=0,CmdUnload_C=1 

smonit ran in the opposite direction (from 100 to 0)
Screen output



Monitoring object's internal variables (vmonit)


Now I will describe the mechanism that allows you to view the internal variables of the object. In general, everything is very simple. Knowing the identifier or name of the object, you can simply request information from it.
So, everything is up and running. Let's see what the Imitator1 object will show us.
Go to src / Services / Administrator and run the command ./oinfo Imitator1
Screen output


As you can see from the output, the oinfo command allows you to see

About user information I will say a little more ...
Each object (more precisely, the skeleton class) has a special function
 virtual std::string getMonitInfo() override; 

redefining that, you can display your information in the form of text (string). In this case, the simulator, for example, writes "The current mode of operation: fill" (or "empty") . You can write something more complicated.
An example of the implementation of the function in the simulator
 string Imitator::getMonitInfo() { ostringstream s; s << "  : " ; if( in_cmdLoad_c ) s << " .." << endl; else if( in_cmdUnload_c ) s << " .." << endl; return std::move(s.str()); } 


Adding to the information output of its variables

Of course, when you write your management process, you will most likely have, in addition to the variables declared in the xml file, some other class fields of your own. And of course, you also want to follow (display information) about the current state and your variables. There is nothing easier . Suppose we add two command counters to the simulator.
Adding to Imitator.h
  ... private: unsigned int numCmdLoad = { 0 }; unsigned int numCmdUnload = { 0 }; 


Then, if you want to see them in the oinfo output, simply in the constructor we will make two magic calls:
Adding to Imitator.cc
 Imitator::Imitator( UniSetTypes::ObjectId id, xmlNode* cnode, const string& prefix ): Imitator_SK(id, cnode, prefix) { ... vmonit(numCmdLoad); vmonit(numCmdUnload); } 


Those. just wrapped your variables in vmonit (xxx) . There certainly was not without macro magic , but probably it doesn’t bother much ...
As a result, we will already see our variables on the screen ( started among others ).
Screen output (re-call ./oinfo)


IMPORTANT: while only standard simple types are supported: bool, int, long, etc., for everything else there is a universal function getMonitInfo ()

Remote reading of logs (built-in LogServer)


As you know, no matter how many debugging mechanisms exist, the favorite cout ( or not the favorite printf ) will still be used. Well, libuniset provides this method as well. In fact, the topic is very extensive, if you disclose all the features and details, then this is a topic for a separate article. Therefore, I will show the application and tell you some details ...
In the generated class skeleton, there is a special object for logs - log . It essentially has an interface as cout, only this is shared_ptr , so you need to use it as a pointer. for example
 log->info() << "......information.." << endl; 

or
 log->crit() << "......critical" << endl; 

The log has 15 levels, you can enable them "in parallel" (eg, info, warn, crit), it can specify the file where to write logs, you can enable and disable the output of date and time at the beginning of each line, etc. . In general, a lot of standard features. For each object, you can enable or disable logs simply by specifying a command line argument when starting.
Log management through command line arguments
 --ObjectName-log-add-levels info,warn,crit,level1,... -    (  ) --ObjectName-log-del-levels info,warn,crit,level1,... -    ( ) --ObjectName-log-set-levels info,warn,crit,level1,... -    ( ) 


But all this would not be so interesting if it were not for the existence of such a mechanism as LogServer . Each object has a built-in LogServer, which by default does not start, accordingly it does not consume resources and is not visible at all. But a simple command line argument
 --ObjectName-run-logserver 

we can activate it. By default, it runs on localhost , and uses the object identifier as the port. But you can also force the launch host and port .
Commands to override host and port
 --ObjectName-logserver-host xxx --ObjectName-logserver-port zzz 


Once the object is running LogServer, we can read its logs, and remotely . Just connect to the specified host and port. There is a special utility uniset2-log for reading logs. With the help of it, in addition to reading logs, you can also control the level of log output, write to a file, etc., i.e. exercise full control over the object logs. This is a very convenient mechanism, since allows you to enable and disable logs without restarting the program ( and you can’t often stop the process, but you really need to look at what’s going on inside).
... let's just show ...
So, everything is running, and we added a line to the simulator start_fg.sh
 --Imitator1-run-logserver 

By the way, in the output ./oinfo , if someone did not notice, information is displayed on whether the LogServer is running. But let me show you again ( go to the src / Services / Administator / directory and run the command ./oinfo Imitator1 ).
Displaying information about the object (note the LogServer)


So logserver is running on localhost and port 20001 . But by default (unless, of course, the developer forcibly turns them on) , the logs are disabled. Accordingly, we will not just connect, but also immediately turn on all (any) logs to immediately start seeing them. Let's connect (add the -v key to see the debug information about who we are connecting to)
 uniset2-log -i localhost -p 20001 -v -a any 

I added a log (level 3) to the timer function, for output to demonstrate the operation.
Additive to Imitator.cc (mylog3)
 void Imitator::timerInfo( const UniSetTypes::TimerMessage* tm ) { if( tm->id == tmStep ) { if( in_cmdLoad_c ) //  .. { mylog3 << myname << "(timerInfo): (" << tmStep << ").. " << endl; out_Level_s += stepVal; if( out_Level_s >= maxLevel ) { out_Level_s = maxLevel; askTimer(tmStep,0); //   ( ) } return; } if( in_cmdUnload_c ) //   { mylog3 << myname << "(timerInfo): (" << tmStep << ")... " << endl; out_Level_s -= stepVal; if( out_Level_s <= minLevel ) { out_Level_s = minLevel; askTimer(tmStep,0); //   ( ) } return; } } } 


Then we connect (as indicated above) and in another console ( go to src / Services / Administrator ) set the command
 ./setValue CmdLoad_C=1,CmdUnload_C=0 
... and after some time, the opposite
 ./setValue CmdLoad_C=0,CmdUnload_C=1 

But what we see in the logs ( remotely read )
Reading logs


Taking the opportunity to read logs remotely, it is important not to forget that if you turn them on , it would be nice to turn off when you log off , because in a real system logs should not work, input / output is expensive. And yet, I will point out a little opportunities that are not disclosed earlier

In a real application, the LogServer of course runs one on all objects (within a single executable file), which are lined up in the hierarchy using the LogAgregator and can be flexibly managed.

Small total


Talking about some mechanisms each time, I try to strike a balance between a bunch of details of internal functioning and simplicity of external application. Those. " Here are ready-made commands, take and use - they work out of the box ." Therefore, I did not tell a lot, and maybe something remains unclear ... I will try to answer your questions.
In general, if we do not assume that normal testing is much more than any “tests” (boundary cases, max, min task, simultaneous setting of commands, etc.), then we finished with setting up the simulator. The main thing was to demonstrate the setup tools included in libuniset2:

Approximately in the same way, we can adjust the work of our management process, but better I will show the adjustment of the control algorithm using the example of using a more advanced method of setting up - writing functional tests using uniset2-testsuite . About it in the following part ...

Well, in the end, as usual, reference:

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


All Articles