RedHawk Property query/Config from Component A to Component B - c++

I'm very new to RedHawk and I have the following scenario:
I have three component A B and C, B and C both have a property skill, which is one keyword describing what B or C's capability. The flow is: A starts and queries B.skill and C.skill so A knows what B and C can do. Then when A encounters a task that fits in B's or C's skill set, A will start up that specific component to do the task.
My question is: how does component A access a property of B? I looked up online and found some simple redhawk query introduction (https://redhawksdr.github.io/Documentation/mainch4.html section 4.6.2), but I hope if someone can show me a code snippet that demos how A accesses B's property. Also, I can't find any detailed documentation of the query api. It would be great if someone can direct me to it.
Thank you.

This example could probably get cleaned up a bit but in my example snippet, CompA has two output ports, both of type resource with the names compB_connection and compC_connection. We can then connect to compB and compC's resource port (also called the lollipop port) which is a direct connection to the component itself since it inherits from the resource API. This gives us access to methods on the component like start, stop, configure, query etc. For a full list see the idl files.
CompB and CompC both have a property with the id of "skill". We can use the query API to query the values of those properties.
std::string CompA_i::any_to_string(CORBA::Any value) {
std::ostringstream result;
const char* tmp;
value >>= tmp;
result << tmp;
return result.str();
}
int CompA_i::serviceFunction() {
CF::Properties compB_props, compC_props;
compB_props.length(1);
compC_props.length(1);
compB_props[0].id = "skill";
compC_props[0].id = "skill";
compB_connection->query(compB_props);
compC_connection->query(compC_props);
std::cout << "CompB Skills: " << any_to_string(compB_props[0].value) << std::endl;
std::cout << "CompC Skills: " << any_to_string(compC_props[0].value) << std::endl;
return FINISH;
}
Now when we connect CompA up to CompB and CompC and Start the waveform, or sandbox we get the following output:
CompB Skills: nunchuck skills
CompC Skills: bow hunting skills
The any_to_string method was found in prop_helpers.cpp in the core framework code; there is probably a helper function in a header file somewhere that would be a better fix.

Related

have redhawk component access another component's properties

Say I have two Redhawk components. Component 1 has a property called "taps". Component 2 also needs to know the value of "taps". It would make things a lot easier if Component 2 could look up the value of "taps" for Component 1, rather than having its own value of "taps" given to it. How can Component 2 access the properties of Component 1? I'm implementing these components in c++.
I've tried a resource port using the following code.
int NeedsProperties_i::serviceFunction(){
CF::Properties otherProperties;
otherProperties.length(1);
otherProperties[0].id = "name";
resourceOut->query(otherProperties);
std::cout << "name: " << any_to_string(otherProperties[0].value) <<
std::endl;
sleep(1);
return NORMAL;
}
std::string NeedsProperties_i::any_to_string(CORBA::Any value) {
std::ostringstream result;
const char* tmp;
value >>= tmp;
result << tmp;
return result.str();
}
NeedsProperties is Component 2 in this case and is trying to get the property "name" from another component. NeedsProperties has the output resource port "resourceOut" connected to the component. The code just prints empty strings regardless if the resource port is connected or not. Whats going on here? Also, is the resource port even a good way to implement this, or is there a better implementation?
Using REDHAWK 2.1.3, I was able to successfully query Component 1(with a property that had the ID 'name') from Component 2 using your example code.
Two things to note are that you will get an exception from query for a property that doesn't exist - does Component1 have a property with the id 'name'? If you try to unmarshall the wrong type out of the property, you will get no value. Using the ossie::any_to_string() helper is the preferred way to unmarshall simple CORBA objects.
An alternate implementation would use PropertyChangeListeners. When a Property gets a new value, there is an event generated and published for anyone to grab. In Component2, you would need to inherit from PropertyChangeListener, call registerPropertyChangeListener() with the id of the property on Component1 and handle the change event. You can see redhawk/src/testing/tests/test_08_PropertyChange*.py for python code that handles PropertyChangeEvents.
try this:
int NeedsProperties_i::serviceFunction(){
CF::Properties otherProperties;
otherProperties.length(1);
otherProperties[0].id = CORBA::string_dup("name");
if(resourceOut->isActive()){
resourceOut->query(otherProperties);
std::cout << "name: " << any_to_string(otherProperties[0].value) <<
std::endl;
sleep(1);
return NORMAL;
}else{
sleep(1);
return NOOP;
}
}
The issue with my code above was that I had connected the output resource port of "NeedsProperties" to my own input resource port on the other component that I created. You are supposed to connect the output port to the "lollipop" on the other component. It is that little dot protruding from the name.
Thanks for the answers though.

How to use the cursor in the new C++ Mongo Driver

I am using the new C++ driver to access MongoDB from my C++ program.
Through the tutorial I am able to fetch an entire collection from the DB.
I am also able to specify filters so I only get a few.
But once I get the collection data into the program, there is only a single example available for inspecting the data:
for (auto&& doc : cursor) {
std::cout << bsoncxx::to_json(doc) << std::endl;
}
I would like to know how to get the count of the collection
I would also like to know how to get number "i" in the return data, i.e.,:
cursor[i] or similar... which of course doesn't work.
Thanks for pointing out this oversight in our examples. If you would, please file a bug in the Documentation component at https://jira.mongodb.org/browse/CXX requesting that our examples include more detail on how to access data on the client.
You have two questions here, really:
How can I get a count? The unhelpful answer is that you could probably write std::distance(cursor.begin(), cursor.end()), but you probably don't want to do that, as it would require pulling all of the data back from the server. Instead, you likely want to invoke mongocxx::collection::count.
How can I get the Nth element out of a cursor? First, are you sure this is what you want? The obvious way would be to do auto view = *std::next(cursor.begin(), N-1), but again, this probably isn't what you want for the reasons above, and also because the order is not necessarily specified. Instead, have a look at mongocxx::options::find::sort, mongocxx::options::find::limit, and mongocxx:options::find::skip, which should give you finer control over what data is returned via the cursor, and in what order.
Many thanks, acm!
I filed the bug and I figured out how to do it. To help others, let me post the two code examples here:
auto db = conn["db-name"];
int count = db["collection-name"].count( {} );
And
mongocxx::options::find opts;
opts.limit( 1 );
auto cursor = db["db-name"].find({ }, opts);
bsoncxx::document::view doc = *cursor.begin();
std::cout << bsoncxx::to_json(doc) << std::endl;

MongoDB finding using an _id

I am using the latest version of the new C++ Mongodb driver/library (not the legacy, 26compat or C version) along with the Qt framework (latest 64b on Linux). Within the same program I am successfully reading and writing to the database and everything is working well.
I realize this version is unstable, but I don't want the boost dependencies and it is a new project, that only I am working on.
I'm not a professional programmer, so please forgive any knowledge gaps.
In my database I have a supporting collection that just remembers the last project a user was working on and I want to do is use a value stored within that document as a string with a field name, to load that project when the program is started.
I am wanting to use the key stored within the m_Current_Project_key variable to load the project data from the project collection.
In the code below the first line after the find statement, carries out the search using different hard coded field name and data in the same collection, just to prove the code works more generally.
The problem I am having is getting the program to search for a specific "_id" that I can see is correctly in the collection and document from the mongo command line.
The comments on the end of the lines of code below show the output achieved for different things I have tried.
This sits within a method that reads a different collection from the same database and get a value from within it, that it puts in the m_Current_Project_key variable which is a QString.
qDebug() << m_Current_Project_key; // "553b976484e4b5167e39b6f1"
qDebug() << Utility::format_key(m_Current_Project_key); // "ObjectId("553b976484e4b5167e39b6f1")" - this utility function just modifies the value passed to it to look like the output
QString test = Utility::format_key(m_Current_Project_key);
test.remove('\"');
qDebug() << test; // "ObjectId(553b976484e4b5167e39b6f1)"
char const *c = m_Current_Project_key.toStdString().c_str();
qDebug() << c; // 553b976484e4b5167e39b6f1
bsoncxx::oid hhh(c, 12);
qDebug() << hhh.get_time_t(); // 892679010
auto cursor = db["project"].find(document{}
// << "title" << "Testing Project"
<< "_id"
<< c
// << hhh
// << m_Current_Project_key.toStdString()
// << m_Current_Project_key.toStdString().c_str()
// << Utility::format_key(m_Current_Project_key).toStdString()
// << test.toStdString()
<< finalize);
The cursor only points to a value when I use the title line above, without the next two lines - the value I get is the document I want, but in the real situation the only thing the program would know, at this point would be the "_id". The project name might not be unique.
I have tried casting the std::string to an OID, but that wasn't recognised as a type.
I've done a lot of Googling and a lot of trying things out and I can't believe there isn't a straight forward way to find a document based on it's "_id". In the examples the only finding examples use values other than the "_id".
db.project.find({ "_id" : ObjectId("553b976484e4b5167e39b6f1")}, { title : 1 })
Does what I want on the Mongo command line.
I would appreciate any assistance I could get with this, I have spent a lot of time trying.
Thanks.
The issue here is that you are using the wrong bsoncxx::oid constructor. When creating an oid from a std::string of the hex representation of the ObjectId (e.g. "553b976484e4b5167e39b6f1") you should use the single-argument constructor that takes a stdx::string_view.
The correct code looks like this:
using bsoncxx::stdx::string_view;
auto cursor = db["project"].find(document{}
<< "_id"
<< bsoncxx::oid{stdx::string_view{m_Current_Project_key.toStdString()}}
<< finalize
);

How to create a test environment while using an external C++ API?

I'm trying to create a test environment for using an external C++ API so that I can test things offline without having to be connected to the actual service. In essence, I want to create my own fake service that will be used for testing purposes. However, I want to be able to change between these two environments easily without having to change a ton of code. Basically, I want to be able to use the external C++ API classes in a test environment somehow. One problem I'm running into is that since the classes are part of an external API, I can't change them. I can only wrap them in other classes I create. How can I deal with that while being able to create an environment that doesn't require me changing a ton of code every time I want to switch back and forth? I have some sample code below (the relevant pieces where the API is being used). How can I put these classes below in a test harness? Thanks!
...
SessionOptions sessionOptions;
sessionOptions.setServerHost(d_host.c_str());
sessionOptions.setServerPort(d_port);
Session session(sessionOptions);
if (! session.start())
{
std::cerr <<"Failed to start session." << std::endl;
return;
}
if (! session.openService("//blp/mktdata"))
{
std::cerr <<"Failed to open //blp/mktdata" << std::endl;
return;
}
...
SubscriptionList subscriptions;
std::set<std::string>::const_iterator cItorSubscriptionStrings(m_SubscriptionStrings.begin());
for ( ; cItorSubscriptionStrings != m_SubscriptionStrings.end(); ++cItorSubscriptionStrings)
{
subscriptions.add((*cItorSubscriptionStrings).c_str(),
"LAST_PRICE,BID,ASK,TIME",
"",
CorrelationId((char*)(*cItorSubscriptionStrings).c_str()));
}
session.subscribe(subscriptions);
while (true)
{
Event event = session.nextEvent();
MessageIterator msgIter(event);
...
while (msgIter.next())
{
Message msg = msgIter.message();
if (event.eventType() == Event::SUBSCRIPTION_DATA)
{
if ((msg.hasElement("LAST_PRICE")) || ((msg.hasElement("BID")) && msg.hasElement("ASK")))
{
double mid = 0;
if ((msg.hasElement("BID")) && (msg.hasElement("ASK")))
{
mid = (msg.getElementAsFloat64("BID") + msg.getElementAsFloat64("ASK")) / 2;
}
else
{
mid = msg.getElementAsFloat64("LAST_PRICE");
}
...
}
}
}
...
}
One thing you can do is to use the interface (i.e. the header files) and provide an implementation of your own, at least for those functions you care about. To switch between both versions essentially amounts to linking with different libraries: yours for testing, theirs for the real implementation.
There are a few issues with that which can be addressed e.g. by only retaining the public interface and changing the private interface (on this case compilation needs to be directed at the different declarations, e.g. using different search pathes for the headers):
often the stubbed version wants to store different data
some object may need to construct private subobjects in specific ways
inline function may call other functions you don't really want to implement
You could try introducing a simplicator (http://www.natpryce.com/articles/000785.html). If the given API isn't amenable to testing, introduce a new thin interface on top of it that is.

Validation Framework in C++

Both Java and .Net seem to have a wealth of object validation frameworks (e.g. Commons Validator, XWork, etc.), but I've been unable to find anything similar for C++. Has anyone come across something like this, or do people typically roll their own?
In 2020 there is cpp-validator library.
This is a C++14/C++17 header-only library that can be used to validate:
plain variables;
properties of objects, where a property can be accessed either as object's variable or object's getter method;
contents and properties of containers;
nested containers and objects.
Basic usage of the library includes two steps:
first, define a validator using almost declarative syntax;
then, apply the validator to data that must be validated and check the results.
See example below.
// define validator
auto string_validator=validator(
value(gte,"sample string"),
size(lt,15)
);
// validate variable
std::string var="sample";
error_report err;
validate(var,string_validator,err);
if (err)
{
std::cerr << err.message() << std::endl;
/* prints:
must be greater than or equal to "sample string"
*/
}
Some GUI frameworks have validators.
Check out wxWidgets Validators