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.
Related
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.
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;
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
);
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
I am some ATL code that uses smart COM pointers to iterate through MS Outlook contacts, and on some PC's I am getting a COM error 0x80004003 ('Invalid Pointer') for each contact. The same code works fine on other PCs. The code looks like this:
_ApplicationPtr ptr;
ptr.CreateInstance(CLSID_Application);
_NameSpacePtr ns = ptr->GetNamespace(_T("MAPI"));
MAPIFolderPtr folder = ns->GetDefaultFolder(olFolderContacts);
_ItemsPtr items = folder->Items;
const long count = items->GetCount();
for (long i = 1; i <= count; i++)
{
try
{
_ContactItemPtr contactitem = items->Item(i);
// The following line throws a 0x80004003 exception on some machines
ATLTRACE(_T("\tContact name: %s\n"), static_cast<LPCTSTR>(contactitem->FullName));
}
catch (const _com_error& e)
{
ATLTRACE(_T("%s\n"), e.ErrorMessage());
}
}
I wonder if any other applications/add-ins could be causing this? Any help would be welcome.
FullName is a property and you do the GET operation (it's probably something like this in IDL: get_FullName([out,retval] BSTR *o_sResult)). Such operation works ok with null values.
My assumption is that contactItem smart pointer points to any valid COM object. In such case the formatting operation done by ATLTRACE can cause the problem. Internally it behaves probably like standard sprintf("",args...) function.
To avoid such problems just do something like below:
ATLTRACE(_T("\tContact name: %s\n"),
_bstr_t(contactitem->FullName)?static_cast<LPCTSTR>(contactitem->FullName):"(Empty)")
Just a guess:
Maybe the "FullName" field in the address book is empty and that's why the pointer is invalid?
hard to tell, because your code doesn't indicate which COM-interfaces you're using.
Does this make any difference?
ATLTRACE(_T("\tContact name: %s\n"), static_cast<LPCTSTR>(contactitem->GetFullName()));
In my example you format NULL value to a proper text value.
If the question is about the difference between FullName(as a property) and GetFullName() (as a method) then the answer is no. Property and method should give the same result. Sometimes property can be mapped to different methods then setXXX and getXXX. It can be achieved by using some specific syntax in IDL (and in reality in TLB after compilation of IDL to TLB). If property FullName is not mapped to method GetFullName then you will achieve different result.
So please examine file *.tlh after importing some type library to your project...