We developed a Java application which uses the TwinCat ADS library (DLL) to read, write and handle events from the Beckhoff PLC (CX5120).
We successfully run this on several machines but unfortunately we’re currently having a problem case where the event handling suddenly stops.
This is the exact scenario we went through:
Read, write and events are handled correctly.
Suddenly we don’t get any events at all anymore, reading and writing are still working correctly though.
Replaced the PLC for another one, started working successfully again. We assumed it was a licensing problem then.
After a week of unattended running, the same problem started again, PLC/ADS library seems not to be triggering events anymore and we can’t seem to get it working again in any way. Reading/writing still working as it should.
Tested using another PC with the Java application, same problem. So something in the PLC seems to freeze up / stop working.
Here's how we have setup the event handling:
// Implementation of the CallbackListenerAdsState interface
public class ADSEventController implements CallbackListenerAdsState {
......
// Register itself as listener for the ADS events (in constructor)
callObject = new AdsCallbackObject();
callObject.addListenerCallbackAdsState(this);
....
// Event handling
public void onEvent(AmsAddr addr, AdsNotificationHeader notification, long user) {
log.info("Got ADS event for handle[{}] and with raw data[{}]", user, notification.getData());
......
// Registering notification handles for PLC variables
// If we already assigned a notification, delete it first (while reconnecting)
JNILong notification = new JNILong();
if(var.getNotification() != null) {
notification = var.getNotification();
AdsCallDllFunction.adsSyncDelDeviceNotificationReq(addr,notification);
}
// Specify attributes of the notificationRequest
AdsNotificationAttrib attr = new AdsNotificationAttrib();
attr.setCbLength(var.getSize());
attr.setNTransMode(AdsConstants.ADSTRANS_SERVERONCHA);
attr.setDwChangeFilter(1000); // 0.01 sec
attr.setNMaxDelay(2000); // 0.02 sec
// Create notificationHandle
long err = AdsCallDllFunction.adsSyncAddDeviceNotificationReq(
addr,
AdsCallDllFunction.ADSIGRP_SYM_VALBYHND, // IndexGroup
var.getHandle(), // IndexOffset
attr, // The defined AdsNotificationAttrib object
var.getHandle(), // Choose arbitrary number
notification);
var.setNotification(notification);
if (err != 0) {
log.error("Error: Add notification: 0x{} for var[{}]", Long.toHexString(err), var.getId());
}
We managed to find the cause.
When we register a variable we get a handle (long) from the PLC, which, in our case unexpectedly started to be negative values after a while.
We also used this long value as user reference for notifications, however, we found the user reference is an unsigned long in the ADS library.
So if we set a negative value of e.g. -1258290964 as ‘arbitrary number’ in the adsSyncAddDeviceNotificationReq call, the CallbackListenerAdsState onEvent method’s parameter ‘user’ (Long) got the unsigned long representation of our signed long user reference, which is 3036676332.
In our Java application we used this user reference to match an event to a specific plc variable by this handle. Since, in our example, we expected -1258290964 but got 3036676332, we never handled any events.
Related
I am developing an application for a DigitalPersona U.are.U 4500 fingerprint reader and using the U.are.U 2.2.3 SDK Java API.
The sample Java application that ships with the SDK works flawlessly.
However, when I try to do the same thing in my own sample application, the call to the Reader.Capture() method never returns, even though I can see the reader flashing when recording my fingerprint.
Below is a variation on the sample code I have tried with.
Other things I have tried:
Running the capture code in an instance of the class (i.e. not in a static context)
Running the capture operation in its own thread as well, but the results are the same.
Using the CaptureThread class from the demo application
The only difference I can see between my sample and the SDK sample app is that the latter is a graphical application. But why would that make a difference?
Unplugging the device causes the call to fail with an exception. That is about the only way I can get it to return.
import com.digitalpersona.uareu.*;
public class Main{
static Reader r;
public static void main(String[] args) {
try {
// Pick first available reader
ReaderCollection rc = UareUGlobal.GetReaderCollection();
rc.GetReaders();
r = rc.get(0);
if (r==null)
return;
// Open Reader
r.Open(Reader.Priority.COOPERATIVE);
System.out.println(r.GetStatus().status); // Outputs READY
// The following call just hangs and never returns...
Reader.CaptureResult
cr = r.Capture(Fid.Format.ISO_19794_4_2005, Reader.ImageProcessing.IMG_PROC_DEFAULT, 500, -1);
System.out.println(cr.quality.name()); // Just to test
} catch (UareUException e) {
e.printStackTrace();
}
}
}
The last two parameters, the two ints, passed to the Capture method are the resolution and the timeout respectively; passing -1 for the timeout blocks indefinitely. This is taken from the sample application as well.
I finally managed to get an example working.
Strange as it may seem, it only works in the context of a Java GUI application.
So, simply extending a JFrame and starting the reader capture on a separate thread seems to be sufficient.
This requirement is not specified anywhere in the SDK documentation that I can see.
UPDATE
It seems the problem is worse than I initially thought. Not only must the API be called in the context of a Java GUI application, but the GUI must also be in focus, otherwise the capture call simply does not return.
I have verified this with the example SDK applications. The Capture() method does not return if the apps are not in focus. This also applies to the C# examples, where the windows must be in focus, which suggests that this is built into the DLLs that ship with the solution.
This is terrible for our scenario, where we want to develop a local service that a browser can communicate with because, while the browser is in focus, obviously the Java application is not.
I faced the similar issue and it can be fixed by opening a reader in exclusive mode as below,
m_reader.Open(Reader.Priority.EXCLUSIVE);
Refer to below lines from documents,
public static final Reader.Priority COOPERATIVE
Client uses this priority to open reader in cooperative mode. Multiple clients with this priority are allowed. Client receives captured images if it has window with focus.
public static final Reader.Priority EXCLUSIVE
Client uses this priority to open reader exclusively. Only one client with this priority is allowed.
So I am making an app that has to check for some information, but I need to be able to do that while the app is killed/not actively running. So basically like Youtube's notifications or something. I am a beginner and watched some tutorials on FCM, which can send a notification while the app is killed, which is fine. However, I need to be able to periodically make API calls, check if a certain condition is true and send the notification if so (all of that while app is not running). I tried googling that and found nothing that can help me. So... any ideas? (No code included since I don't think it's relevant.)
If this condition only affects the display of notifications, you can simply check the condition when you receive a notification through FCM. Here's an example :
class FCMService : FirebaseMessagingService()
{
override fun onMessageReceived(message: RemoteMessage)
{
val myCondition = getConditionFromAPI()
if (myCondition) {
showNotification(message)
}
}
}
Use AlarmManager to request periodic runs of your code. It may be set up to broadcast a message time to time to your receiver (declared in AndroidManifest.xml or in your code).
It will work while device is on. To continue after reboot, you have to set up a receiver for Intent.ACTION_BOOT_COMPLETED and register it in your code (not in AndroidManifest.xml, as it no longer works in latest versions of Android).
I'm working on a phone-conference app on Android 7. I found this problem.
When app crash I loose ongoing call control resulting in app closed and voice channel open.
Reopening app result in two ongoing calls.
There are ways to close the first voice call?
I try closing the call at app restart but obviously Android OS don't let me touch it.
the best (still not working) result I achieved is error class extension. that event is fired at crash start.
here is my class CrashKillCall that implements Thread.UncaughtExceptionHandler
public void uncaughtException(Thread t, Throwable e) {
//"the last song kill the audience" by Crash & the boys
Log.e(TAG, "--------------------------------------");
Log.e(TAG,t.getName());
Log.e(TAG,e.getCause().getMessage());
Log.e(TAG, "--------------------------------------");
crashCall.disconnect();
Log.e(TAG,"work?");
}
public static void setCall(Call call){
crashCall=call;
}
the desired result is some way to, or to let system know that i want to, terminate the ongoing or all calls.
thank you for your help.
ended out that i was pointing at wrong Call object. code work, you just need declare an istance of that class as default exception listener and register the right Call
I haven't my own server and client doesn't want a server. Can i do it with direbase notifications? And how i will calculate how amny days spent from last user session?
--------------------------------NEW ANSWER------------------------------------
You should be able to do this using the Firebase Console. When creating a Cloud Message you can select in the "Target" section "Last App Engagement" and select that you want the message to go to everyone that hasn't engaged with the app for say 1 day.
After that you set in Scheduling that you want it to be a recurring campaign and you set it to happen "Daily" at say 12pm and set that you want each user to get this only once. Now you have a campaign that every day at 12pm checks who wasn't in the App for 1d and sends him the campaign, but only sends it once to not spam the same guy every single day.
--------------------------------OLD ANSWER -----------------------------------
You could create for example a DailyJob within your app and that DailyJob could either on it's own check how many days since last session was active and create a local notification or you could use that DailyJob to send to say Firebase a custom attribute "days_since_last_login" and setup different campaigns for 2 days offline, 7 days offline, 31 days offline and so on in Firebase.
To easily create a DailyJob you can use Evernote Android Job library.
You can use firebase to make push notification.
In this case, you don't have any server to make push notification and track the last user session, I think you can follow this:
Use local notification (not push notification) to notify user.
Store user session to Shared Preferences.
Create a background service to trigger local notification base on the last session stored.
But you should have a clear view of notification on Android to understand how it works first.
This is simple: don't use Firebase or the internet at all.
This is what you want to do: every time the user logs into your app, Create an Alarm for 48 hours (2 days) from when onResume and cancel any previous alarms. Or cancel previous alarms during onResume and create a new one in onPause.
Then, setup the alarm receiver to send a local notification. AlarmManager with Notification Android
Every time the user opens your app, it pushes the local notification back. When they go 2 days without using it, they will get a notification at around the same time that they used it previously.
"Can I do it with firebase notifications?"
Yes, you can with cloud function code.
Just trigger an event after a new notification added to the database.
My below code trigger an event after a new notification added to DB and looks for a post older than 24hrs. You can use it and change it for 48hrs
The code work below:
Trigger event after data is added to DB.
Check the database for old notifications older than 24hrs.
Retrieve them and delete (by changing their value to null);
//DELETE OLD NOTIFICATION LESS THAN 24HRS /////////////////
exports.deleteOldItems = functions.database.ref('/notification/{user_id}/{notification_id}').onWrite((change) => {
const ref = change.after.ref.parent; // reference to the parent
const yesterday = Date.now() - 86400000;
const oldItemsQuery = ref.orderByChild('time').endAt(yesterday);
return oldItemsQuery.once('value').then((snapshot) => {
// create a map with all children that need to be removed
const updates = {};
snapshot.forEach(child => {
updates[child.key] = null;
});
return ref.update(updates);
// execute all updates in one go and return the result to end the function
});
});
////////////////////DELETE FUNCTION ENDED HERE//////////////////////
The most important part is how to calculate for 48hrs (which is 2 days)
PS: this is still 24hrs. I will update the answer when finding a way to convert for 48hrs.
const yesterday = Date.now() - 86400000;
EDIT:172800000 is for 2 days ago (24hrs)
Now, you subtract the current date from 2 days ago and compare it with the notification date.
const yesterday = Date.now() - 172800000;
I am most concerned about Performance issue and don't want users to wait for progress.
I have a chatActivity, where i show a ListView.
Here i send a chatMessage
Chats chat = new Chats(chatBox.getText().toString(),Chats.TYPE_MINE, dt.format(now));
chat.personId = chatee.getMyId();
chat.isDelievered = Chats.DELIEVERED_NONE;
chats.add(chat);
mAdapter.notifyDataSetChanged();
Notice that Chat delievery is set to NONE Right now. So basically the message is being added to the Chat List even its not delivered yet.
Now on back thread here is what's happening
It takes few seconds to send message where i do this
boolean bool = sendMessage(m);
if (bool)
chatee.isDelievered = Chats.DELIEVERED_DONE; (MESSAGE SENT)
if (chatee.isDelievered == Chats.DELIEVERED_DONE)
{
app.mDbHelper.saveMessage(chatee); // SAVING TO DATABASE
Intent i = new Intent(Constants.REFRESH_NOTIF).putExtra("refresh",Constants.REFRESH_NOTIF);
context.sendBroadcast(i);
}
It will send a broadcast to the activity.
Now here is the problem.
Broadcast call this function
public void callUIMethodForRefresh(Intent intent)
{
String ref = intent.getStringExtra("refresh");
if (ref == null)
{
}
else if (ref.equals(Constants.REFRESH_NOTIF))
{
}
}
Here i am confused of how can I reset that previous Chat object added to my List.
Points to be noted , i can be sending messages at a very fast speed and the refresh could be called for an old message whereas a new message is already typed.
ONE way is i make a For loop and check for all the "ChatList" array for the message sent and then replace its delivery notice, but again this is very low performance incase i have 1000+ objects in the list.
Is there any way, i can attach the sqlite database with my listView adapter that automatically detects the changes and reset the listView etc and etc?
What could be the best strategies here to avoid performance issues.
I would suggest looking into ContentProviders and Loaders (specifically a CusorLoader). Combining these with a CursorAdapter, you can use the ContentProvider which inserts/deletes/updates your sqlite database and notifies your loader to reload it's dataset and update the CursorAdapter/ListView.