OPCUA C++ Monitored item doest'n callback the function - c++

I'm quite new in the opcua's world and I'm trying to monitor a server variable with a client in C++.
I'm on the 1.2.2 version of opcua
I have a boolean variable in the server at the node (1,6070) and when I run the following code I receive the LOG :
[2021-08-03 15:27:47.442 (UTC+0200)] info/session Connection 5 | SecureChannel 2 | Session ns=1;g=913a21de-f467-5bc9-ed9e-29b27b470490 | Subscription 2 | Created the Subscription with a publishing interval of 500.00 ms
But I've never reach the function 'handler_events_datachange' in which I only put an output for now. (I'm sure that the value in the node 6070 changed btw)
Thanks for helping!
int main(void) {
signal(SIGINT, stopHandler); /* catches ctrl-c */
UA_Client *client = UA_Client_new();
UA_ClientConfig *cc = UA_Client_getConfig(client);
UA_ClientConfig_setDefault(cc);
UA_Client_connect(client, "opc.tcp://localhost");
UA_MonitoredItemCreateResult result;
UA_CreateSubscriptionResponse response; // warning memory leak
UA_CreateSubscriptionRequest request = UA_CreateSubscriptionRequest_default();
UA_Client_Subscriptions_create(client, request, NULL, NULL, NULL);
UA_Int32 subId = response.subscriptionId;
if(response.responseHeader.serviceResult == UA_STATUSCODE_GOOD)
{
UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND , "subscription succed");
} else {
UA_LOG_ERROR(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND , "subscription UNsucced");
}
UA_MonitoredItemCreateRequest monRequest = UA_MonitoredItemCreateRequest_default(UA_NODEID_NUMERIC(1, 6070));
result = UA_Client_MonitoredItems_createDataChange(client, subId, UA_TIMESTAMPSTORETURN_BOTH, monRequest, NULL, handler_events_datachange, NULL);
while(running) {
}
}

I finally found the error !
The problem comme from the fact no means of handling asynchronous
events automatically is provided. However, some synchronous function
calls will trigger handling, but to ensure this happens a client
should periodically call UA_Client_run_iterate explicitly.
So the solution is to add UA_Client_run_iterate(client,100) in the while().
I didn't fully understand what the timeout was about but I'll complete this answer if I can

Related

Cannot set Scanner Capability because L_TwainStartCapsNeg returns error -84

I'm trying to use the Leadtools API version 21 for automatically scanning some documents and here is a sudo code of what I have done (it runs in a secondary thread and the unlock has been done in the main thread):
void CheckRetCode(int rc)
{
if (SUCCESS != rc)
{
L_TCHAR errMsg[1024];
memset(errMsg, 0, sizeof(errMsg));
L_GetFriendlyErrorMessage(rc, errMsg, 1024, L_FALSE);
throw TLeadException(errMsg, rc);
}
}
void OnThreadExecute(void)
{
HTWAINSESSION hSession = nullptr;
APPLICATIONDATA appData;
L_INT nRet;
L_TCHAR pszTwnSourceName[1024];
LTWAINSOURCE sInfo;
memset(&appData, 0, sizeof(APPLICATIONDATA));
appData.uStructSize = sizeof(APPLICATIONDATA);
appData.hWnd = hWnd;// hWnd is valid handle of my main window
appData.uLanguage = TWLG_ENGLISH_USA;
appData.uCountry = TWCY_USA;
wcscpy(appData.szManufacturerName, L"MyCompanyName");
wcscpy(appData.szAppProductFamily, L"MyProductName");
wcscpy(appData.szAppName, appData.szAppProductFamily);
wcscpy(appData.szVersionInfo, L"Version 0.1.0.1");
nRet = L_TwainInitSession2(&hSession, &appData, LTWAIN_INIT_MULTI_THREADED);
CheckRetCode(nRet);// the exception gets catched elsewhere but no error reported here
memset(pszTwnSourceName, 0, sizeof(pszTwnSourceName));
wcscpy(pszTwnSourceName, L"EPSON Artisan837/PX830"); // the name of the scanner is verifyed
sInfo.uStructSize = sizeof(LTWAINSOURCE);
sInfo.pszTwainSourceName = pszTwnSourceName;
CheckRetCode(L_TwainSelectSource(hSession, &sInfo)); // No error reported here
CheckRetCode(L_TwainStartCapsNeg(hSession)); // in here I get the return value -84 which is reported as "TWAIN DS or DSM reported error, app shouldn't (no need for your app to report the error)."
// the rest of the code but we cannot get there since above code reports error
}
Can anyone tell me what I'm doing wrong? Is there a step that I'm missing here?
EditThe function L_TwainSelectSource() make no effort to make sure the supplied source is valid and does not even return an error. As result, if you set the selected source to a garbage name, it will act as if it accepted it. From that point on if you try to Get/Set anything or try to acquire an image, every function returns -84.
Thank you
Sam
To test your code, I put the main window’s handle in a global variable:
globalhWnd = hWnd;
And modified your function to use that handle like this:
void OnThreadExecute(void *)
{
...
appData.hWnd = globalhWnd; // hWnd is valid handle of my main window
...
}
Then created a thread for it from the main program like this:
globalhWnd = hWnd;
_beginthread(OnThreadExecute, 0, 0);
I tried this with 5 different Twain sources: 2 virtual and 3 physical scanners (one of them an old Epson). All 5 drivers returned SUCCESS when calling L_TwainStartCapsNeg() from within the thread.
Two possibilities come to mind:
The problem might be caused by something else in your code other than the thread function.
Or the problem could be specific to your Twain driver.
To rule out the first possibility, I suggest creating a small test project that only creates a similar thread and does nothing else and trying it with different scanners. If it causes the same problem with all scanners, send that test project (not your full application) to support#leadtools.com and our support engineers with test it for you.
If the problem only happens with a specific Twain driver, try contacting the scanner’s vendor to see if they have an updated driver.

gRPC: What are the best practices for long-running streaming?

We've implemented a Java gRPC service that runs in the cloud, with an unidirectional (client to server) streaming RPC which looks like:
rpc PushUpdates(stream Update) returns (Ack);
A C++ client (a mobile device) calls this rpc as soon as it boots up, to continuously send an update every 30 or so seconds, perpetually as long as the device is up and running.
ChannelArguments chan_args;
// this will be secure channel eventually
auto channel_p = CreateCustomChannel(remote_addr, InsecureChannelCredentials(), chan_args);
auto stub_p = DialTcc::NewStub(channel_p);
// ...
Ack ack;
auto strm_ctxt_p = make_unique<ClientContext>();
auto strm_p = stub_p->PushUpdates(strm_ctxt_p.get(), &ack);
// ...
While(true) {
// wait until we are ready to send a new update
Update updt;
// populate updt;
if(!strm_p->Write(updt)) {
// stream is not kosher, create a new one and restart
break;
}
}
Now different kinds of network interruptions happen while this is happening:
the gRPC service running in the cloud may go down (for maintenance) or may simply become unreachable.
the device's own ip address keeps changing as it is a mobile device.
We've seen that on such events, neither the channel, nor the Write() API is able to detect network disconnection reliably. At times the client keep calling Write() (which doesn't return false) but the server doesn't receive any data (wireshark doesn't show any activity at the outgoing port of the client device).
What are the best practices to recover in such cases, so that the server starts receiving the updates within X seconds from the time when such an event occurs? It is understandable that there would loss of X seconds worth data whenever such an event happens, but we want to recover reliably within X seconds.
gRPC version: 1.30.2, Client: C++-14/Linux, Sever: Java/Linux
Here's how we've hacked this. I want to check if this can be made any better or anyone from gRPC can guide me about a better solution.
The protobuf for our service looks like this. It has an RPC for pinging the service, which is used frequently to test connectivity.
// Message used in IsAlive RPC
message Empty {}
// Acknowledgement sent by the service for updates received
message UpdateAck {}
// Messages streamed to the service by the client
message Update {
...
...
}
service GrpcService {
// for checking if we're able to connect
rpc Ping(Empty) returns (Empty);
// streaming RPC for pushing updates by client
rpc PushUpdate(stream Update) returns (UpdateAck);
}
Here is how the c++ client looks, which does the following:
Connect():
Create the stub for calling the RPCs, if the stub is nullptr.
Call Ping() in regular intervals until it is successful.
On success call PushUpdate(...) RPC to create a new stream.
On failure reset the stream to nullptr.
Stream(): Do the following a while(true) loop:
Get the update to be pushed.
Call Write(...) on the stream with the update to be pushed.
If Write(...) fails for any reason break and the control goes back to Connect().
Once in every 30 minutes (or some regular interval), reset everything (stub, channel, stream) to nullptr to start afresh. This is required because at times Write(...) does not fail even if there is no connection between the client and the service. Write(...) calls are successful but the outgoing port on the client does not show any activity on wireshark!
Here is the code:
constexpr GRPC_TIMEOUT_S = 10;
constexpr RESTART_INTERVAL_M = 15;
constexpr GRPC_KEEPALIVE_TIME_MS = 10000;
string root_ca, tls_key, tls_cert; // for SSL
string remote_addr = "https://remote.com:5445";
...
...
void ResetStreaming() {
if (stub_p) {
if (strm_p) { // graceful restart/stop, this pair of API are called together, in this order
if (!strm_p->WritesDone()) {
// Log a message
}
strm_p->Finish(); // Log if return value of this is NOT grpc::OK
}
strm_p = nullptr;
strm_ctxt_p = nullptr;
stub_p = nullptr;
channel_p = nullptr;
}
}
void CreateStub() {
if (!stub_p) {
ChannelArguments chan_args;
chan_args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, GRPC_KEEPALIVE_TIME_MS);
channel_p = CreateCustomChannel(
remote_addr,
SslCredentials(SslCredentialsOptions{root_ca, tls_key, tls_cert}),
chan_args);
stub_p = GrpcService::NewStub(m_channel_p);
}
}
void Stream() {
const auto restart_time = steady_clock::now() + minutes(RESTART_INTERVAL_M);
while (!stop) {
// restart every RESTART_INTERVAL_M (15m) even if ALL IS WELL!!
if (steady_clock::now() > restart_time) {
break;
}
Update updt = GetUpdate(); // get the update to be sent
if (!stop) {
if (channel_p->GetState(true) == GRPC_CHANNEL_SHUTDOWN ||
!strm_p->Write(updt)) {
// could not write!!
return; // we will Connect() again
}
}
}
// stopped due to stop = true or interval to create new stream has expired
ResetStreaming(); // channel, stub, stream are recreated once in every 15m
}
bool PingRemote() {
ClientContext ctxt;
ctxt.set_deadline(system_clock::now() + seconds(GRPC_TIMEOUT_S));
Empty req, resp;
CreateStub();
if (stub_p->Ping(&ctxt, req, &resp).ok()) {
static UpdateAck ack;
strm_ctxt_p = make_unique<ClientContext>(); // need new context
strm_p = stub_p->PushUpdate(strm_ctxt_p.get(), &ack);
return true;
}
if (strm_p) {
strm_p = nullptr;
strm_ctxt_p = nullptr;
}
return false;
}
void Connect() {
while (!stop) {
if (PingRemote() || stop) {
break;
}
sleep_for(seconds(5)); // wait before retrying
}
}
// set to true from another thread when we want to stop
atomic<bool> stop = false;
void StreamUntilStopped() {
if (stop) {
return;
}
strm_thread_p = make_unique<thread>([&] {
while (!stop) {
Connect();
Stream();
}
});
}
// called by the thread that sets stop = true
void Finish() {
strm_thread_p->join();
}
With this we are seeing that the streaming recovers within 15 minutes (or RESTART_INTERVAL_M) whenever there is a disruption for any reason. This code runs in a fast path, so I am curious to know if this can be made any better.

Why timer_create throw error for SIGEV_THREAD in solaris 10?

I wrote a piece of by using timer_create for sets the timer to invoke a thread in which i set sigev_notify as SIGEV_THREAD, it is giving me error EINVAL(Invalid argument) but when i am setting sigev_notify as SIGEV_SIGNAL code is working fine.
my this piece of code is working in all OS even in solaris 11 but for solaris 10 giving me error.
code given below:
{
int status =0;
struct itimerspec ts;
struct sigevent se;
se.sigev_notify = SIGEV_THREAD;
se.sigev_value.sival_int = val;
se.sigev_notify_function = func;
se.sigev_notify_attributes = NULL;
status = timer_create(CLOCK_REALTIME, &se, timer_id);
ts.it_value.tv_sec = abs(delay);
ts.it_value.tv_nsec = (delay-abs(delay)) * 1e09;
ts.it_interval.tv_sec = abs(interval);
ts.it_interval.tv_nsec = (interval-abs(interval)) * 1e09;
status = timer_settime(*timer_id, 0, &ts, 0);
}
Please help me to solve this issue.
Thanks in advance...
As per this man-page Solaris 10 does not know SIGEV_THREAD, but only
The sigev_notify member specifies the notification mechanism to use when an asynchronous event occurs. The sigev_notify member may be defined with the following values:
SIGEV_NONE
No asynchronous notification is delivered when the event of interest occurs.
SIGEV_SIGNAL
A queued signal, with its value application-defined, is generated when the event of interest occurs.
SIGEV_PORT
An asynchronous notification is delivered to an event port when the event of interest occurs. The sival_ptr member points to a port_notify_t structure (see port_associate(3C)). The event port identifier as well as an application-defined cookie are part of the port_notify_t structure

ActiveMQ-CPP - message redelivery delay do not get applied

I am using ActiveMQ-CPP 3.4.5 to connect from C++ program to message broker.
The problem I encountered is connected with redelivery policy.
Messages which are returned to queue are redelivered immediately. This is not the
behaviour I expect. I would expect the messages to be returned after certain period
of time which is set through redelivery policy.
This is a code snipped showing the way I set up redelivery policy:
policy = new activemq::core::policies::DefaultRedeliveryPolicy();
policy->setInitialRedeliveryDelay(0);
policy->setRedeliveryDelay(10000);
policy->setMaximumRedeliveries((int)activemq::core::RedeliveryPolicy::NO_MAXIMUM_REDELIVERIES);
connectionFactory.setRedeliveryPolicy(policy);
As I said before I would except the messages to be redelivered after 10000 ms, but the are not.
They come back to consumer immediately.
Does anyone know what could be the reason of such behaviour?
You set the initial delay to zero so they are going to be redelivered immediately the first time a transaction is rolled back. If you want them to be delayed on the first redelivery cycle then you need to set the initial delay to 10000 as well.
When I looked into ActiveMQ-CPP sources I found the following code snippet in ActiveMQConsumer.cpp file:
if( internal->redeliveryDelay > 0 && !this->internal->unconsumedMessages->isClosed() ) {
// TODO - Can't do this until we can control object lifetime.
// Start up the delivery again a little later.
// this->internal->scheduler->executeAfterDelay(
// new StartConsumerTask(this), internal->redeliveryDelay);
start();
} else {
start();
}
So it seems that redeliveryDelay is not taken into account after rollback at all.
That is why, I suppose, my messages arrive immediately after rollback.
onMessage method:
void BaseProdListener::onMessage( const cms::Message* message ){
log4cxx::Logger::getLogger("BaseProdListener")->info("onMessage");
_message = message;
try {
const cms::TextMessage* textMessage = dynamic_cast< const cms::TextMessage* >( message );
std::string text = "";
if( textMessage != NULL ) {
text = textMessage->getText();
log4cxx::Logger::getLogger("BaseProdListener")->debug("Received message:" + text);
handleMessage(text);
}
} catch (cms::CMSException& e){
log4cxx::Logger::getLogger("BaseProdListener")->error(e.getStackTraceString());
}
}

Using a Waitable Timer from within a Windows Service

I am attempting to use a WaitableTimer in a Windows Service written in C++ to bring a Windows XP machine out of sleep/stand-by mode but I cannot seem to get it to work. I copy/pasted the code I am using in the service into a standalone app and that works fine. Is there a step I am missing to get this to work within a service?
The code I am using to set up the waitable timer is as follows (the call to UpdateWaitableTimer() happens within a thread that loops indefinitely):
void UpdateWaitableTimer(job_definition *jobdef)
{
HANDLE existingHandle;
try
{
if (jobdef->JobType == JOB_TYPE_SCAN)
{
char szTimerName[MAX_PATH];
sprintf_s(szTimerName, MAX_PATH, "Timer_%I64d", jobdef->JobID);
existingHandle = OpenWaitableTimer(TIMER_ALL_ACCESS, TRUE, szTimerName);
if (existingHandle != NULL)
{
// A timer handle already exists for this job, so cancel it
CancelWaitableTimer(existingHandle);
}
else
{
// No timer handle exists, create one
existingHandle = CreateWaitableTimer(NULL, TRUE, szTimerName);
}
if (jobdef->JobStatus != JOB_STATUS_SCHEDULED)
{
// This job was cancelled, so close the handle
CloseHandle(existingHandle);
}
else
{
time_t now = time(NULL);
time_t dt = jobdef->JobScheduleTime;
while(dt < now)
{
dt += 86400;
}
// Get a FILETIME one minute before
FILETIME utcFileTime = GetTimestamp(dt - 60);
// Convert to LARGE_INTEGER
LARGE_INTEGER dueTime;
dueTime.HighPart = utcFileTime.dwHighDateTime;
dueTime.LowPart = utcFileTime.dwLowDateTime;
SYSTEMTIME st;
FILETIME *ft = &utcFileTime;
FileTimeToSystemTime(ft, &st);
LogRelease(false, "Setting Timer for scheduled job: %02d/%02d/%d %02d:%02d:%02d", st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
if(SetWaitableTimer(existingHandle, &dueTime, 0, NULL, NULL, TRUE))
{
if(GetLastError() == ERROR_NOT_SUPPORTED)
{
LogRelease(false, "Resume from sleep/stand-by feature not supported on this operating system.");
}
}
else
{
LogError(false, "Could not create timer. Error: %d", GetLastError());
}
}
}
}
catch(...)
{
LogError(false, "An exception occured while updating waitable timer for job %I64d", jobdef->JobID);
}
LogRelease(false, "Finished Updating Waitable Timer [Job:%I64d]", jobdef->JobID);
}
If it works outside a service then your code is probably fine. There are really only two things I can think of that might cause it to behave differently:
Resuming might require the service to be able to "interact with the desktop." Try setting that option in the service manager under the log on tab, and restart your service.
Services running as LOCAL_SYSTEM might not have rights to resume from sleep. Try running the service as yourself, or create a service account specifically for the service to run from.
If you're already running the service as a specific user, then perhaps that user account has insufficient permissions.