Set AlarmManager notifications to be triggered with variable intervals - c#

I am developing an Android app using Xamarin. I am trying to figure out how to have it send notifications using AlarmManager with a variable interval. I have figured out how to trigger a notification and have it sent on a fixed interval (say every five minutes). But in some cases I would like to have the interval more dynamic. For instance, let's say I would like to get notified every day a sunset. Since the interval between sunsets are varying, how do I schedule a notification that has an interval that will change?
Here is my code that works for a fix interval:
public void SendNotification( int id
, string title
, string message
, DateTime notifyTime
, long interval)
{
if ( ! _channelInitialized)
{
CreateNotificationChannel();
}
var intent = new Intent(AndroidApp.Context
, typeof(AlarmHandler));
intent.PutExtra(TitleKey
, title);
intent.PutExtra(MessageKey
, message);
var pendingIntent = PendingIntent.GetBroadcast(AndroidApp.Context
, id
, intent
, PendingIntentFlags.UpdateCurrent);
var triggerTime = GetNotifyTime(notifyTime);
var alarmManager = AndroidApp.Context
.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager?.SetRepeating(AlarmType.RtcWakeup
, triggerTime
, interval
, pendingIntent);
}

It all depends on how exact your alarm should go off and if you know the intervals beforehand or if they are dynamically gotten.
So, if you know the intervals beforehand you can just set multiple alarms (by using different ids on the PendingIntent, docs) to go off on exactly the specified intervals using
setExact(Android.App.AlarmType type, long triggerAtMillis, Android.App.PendingIntent? operation)
setExactAndAllowWhileIdle(Android.App.AlarmType type, long triggerAtMillis, Android.App.PendingIntent? operation)
setAlarmClock(AlarmManager.AlarmClockInfo info, PendingIntent operation)
e.g. idk your specific scenario but here you have a generic example with two alarms:
var baseline = Java.Lang.JavaSystem.CurrentTimeMillis(); // Time from 1970
var time1 = baseline + TimeSpan.FromSeconds(5332).TotalMilliseconds;
var time2 = baseline + TimeSpan.FromSeconds(8900).TotalMilliseconds;
var id1 = DateTime.Now().TotalMilliseconds
var pendingIntentWithId1 = PendingIntent.GetBroadcast(AndroidApp.Context
, id1
, intent
, PendingIntentFlags.UpdateCurrent);
var pendingIntentWithId2 = PendingIntent.GetBroadcast(AndroidApp.Context
, id1 + 1
, intent
, PendingIntentFlags.UpdateCurrent);
var alarmManager = AndroidApp.Context
.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager?.SetExact(AlarmType.RtcWakeup,
time1,
pendingIntentWithId1);
alarmManager?.SetExact(AlarmType.RtcWakeup,
time2,
pendingIntentWithId2);
If the intervals/time to go off need to be dynamically updated you need to create a Service that runs on the background and updates the alarms on the AlarmManager, or every time you receive on the BroadcastReceiver you set the next alarm time to go off.
Be careful on setting exact alarms because they are resource consuming and if possible you should set inexact alarms.
Also if you're setting exact alarms remember to have the proper permission set if you are targeting Android 12+:
SCHEDULE_EXACT_ALARM (this is target Android 12+)
USE_EXACT_ALARM (this is target Android 13+)
Most of the times SCHEDULE_EXACT_ALARM is enough, the later one can be used in the next cases:
Your app is an alarm clock app or a timer app. Your app is a calendar
app that shows notifications for upcoming events

Related

Is it possible to get different message from same intent

I am trying to get different message from same intent. Let us consider that i have a intent Greeting , so when user say 'Hi' to Bot then bot call the Greeting intent then bot show the message "hello and welcome" to the user.
If you say Hi again to the bot then i want the different message from the bot like "hello and welcome again".
here is my intent code
new OnIntent("Greeting")
{
Actions = new List<Dialog>()
{
new CodeAction(async (dialogContext, options) =>
{
var now = DateTime.Now.TimeOfDay;
var time = now < new TimeSpan(12, 0, 0)
? "morning"
: now > new TimeSpan(19, 0, 0)
? "evening"
: "afternoon";
dialogContext.State.SetValue("dialog.greetingTime", time);
dialogContext.State.SetValue("user.name",CustmerName);
return await dialogContext.EndDialogAsync(options);
}),
new SendActivity("${HelpRootDialog()}")
}
},
You can make use of conversation state. Add a boolean field called isUserGreeted, when the intent is hit for the first time, set it as true.
When the intent is hit again, check the conversation state to see if the user was already greeted, if yes you can send the user the second hello message.
If you want to reset the greeted flag based on a date, you can also store the date information to see if the user was greeted today.

Windows Forms Timer not executed on time

I am using a System.Windows.Forms.Timer in my application which triggers a method that adds new data to four LiveCharts. The interval is set to 1000 ms. It works fine for like the first one or two minutes but then the interval gets bigger and bigger and after 10 minutes it triggers only every 15 seconds or so.
I have tried using different Timers without any luck. Might it be that the performance of the application/LiveCharts is just too bad and because the timer runs on the UI thread it has to wait for the application to be "ready"?
Thanks!
Timer setup
graphRefreshTimer = new System.Windows.Forms.Timer();
graphRefreshTimer.Interval = 1000;
graphRefreshTimer.Tick += new EventHandler(refreshUI);
graphRefreshTimer.Start();
refreshUI (basically just adding new ChartValues to the LiveCharts. Takes a maximum of 100 ms to run)
//...
B1VoltageValues.Add(new MeasureModel
{
DateTime = now,
Value = Convert.ToDouble(B1.Voltage) / 1000
});
B1CurrentValues.Add(new MeasureModel
{
DateTime = now,
Value = Convert.ToDouble(B1.Current) / 1000
});
B1ChargingCapacityValues.Add(new MeasureModel
{
DateTime = now,
Value = Convert.ToDouble(B1.getChargeCapIfTrue()) / 1000
});
B1DischargingCapacityValues.Add(new MeasureModel
{
DateTime = now,
Value = Convert.ToDouble(B1.getDischargeCapIfTrue()) / 1000
});
B1EnergyValues.Add(new MeasureModel
{
DateTime = now,
Value = Convert.ToDouble(B1.Energy) / 1000
});
//...
The solution to the problem was to use the LiveCharts.Geared package which can handle way more data that the standard one. No further freezes of the UI occured after using this package.

Service health status doesn't update in azure service fabric explorer from Health Manager?

My apologies if the question sounds vague. Here is the scenario I observe.
I've created an azure service fabric application (POC) with 2 stateless services.
Service-1 initially reports it's health as OK with a time to live of 5 minutes in the first iteration and waits for 2 minutes (randomly configured the wait for 2 minutes).
10 seconds later, Service-2 reports it's health as Error with time to live as 10 seconds in its first iteration. Even this waits for 2 minutes.
At this point, Service fabric explorer correctly shows Service-1's status as OK and Service-2's status as Error. AS EXPECTED.
Meanwhile, second iteration of Service-1 starts and reports it's status now as ERROR.
Second iteration of Service-2 also starts and reports its status as OK now.
EXPECTED: Service fabric explorer would show Service-1's status as ERROR and Service-2's status as OK.
ACTUAL : Both services are being shown as Error.
Doesn't Service fabric explorer get the health status from Health Manager every time it refreshes? If so, why am I being shown the status as Error for both services?
Code below for reference:
Service-1:
long iterations = 0;
if (iterations++%2 == 0)
{
var healthInformation = new HealthInformation("Service-1", $"{this.Context.ServiceName}-OK-{iterations}-Property",
HealthState.Ok);
healthInformation.TimeToLive = TimeSpan.FromSeconds(300);
var healthReport = new StatelessServiceInstanceHealthReport(this.Context.PartitionId,
this.Context.InstanceId, healthInformation);
fabricClient.HealthManager.ReportHealth(healthReport);
ServiceEventSource.Current.ServiceMessage(this, "Logged OK health from {0}", this.Context.ServiceName);
await Task.Delay(TimeSpan.FromSeconds(120), cancellationToken);
}
else
{
var healthInformation = new HealthInformation("Service-1", $"{this.Context.ServiceName}-Error-{iterations}-Property",
HealthState.Error);
healthInformation.TimeToLive = TimeSpan.FromSeconds(10);
var healthReport = new StatelessServiceInstanceHealthReport(this.Context.PartitionId,
this.Context.InstanceId, healthInformation);
fabricClient.HealthManager.ReportHealth(healthReport);
ServiceEventSource.Current.ServiceMessage(this, "Logged Error health from {0}", this.Context.ServiceName);
await Task.Delay(TimeSpan.FromSeconds(120), cancellationToken);
}
Service-2:
long iterations = 0;
if (iterations++ % 2 == 0)
{
var healthInformation = new HealthInformation("StatelessService2", $"{this.Context.ServiceName}-Error-{iterations}-Property",
HealthState.Error);
healthInformation.TimeToLive = TimeSpan.FromSeconds(10);
var healthReport = new StatelessServiceInstanceHealthReport(this.Context.PartitionId,
this.Context.InstanceId, healthInformation);
fabricClient.HealthManager.ReportHealth(healthReport);
ServiceEventSource.Current.ServiceMessage(this, "Logged Error from {0}" , this.Context.ServiceName);
await Task.Delay(TimeSpan.FromSeconds(120), cancellationToken);
}
else
{
var healthInformation = new HealthInformation("StatelessService2", $"{this.Context.ServiceName}-OK-{iterations}-Property",
HealthState.Ok);
healthInformation.TimeToLive = TimeSpan.FromSeconds(300);
var healthReport = new StatelessServiceInstanceHealthReport(this.Context.PartitionId,
this.Context.InstanceId, healthInformation);
fabricClient.HealthManager.ReportHealth(healthReport);
ServiceEventSource.Current.ServiceMessage(this, "Logged OK from {0}" ,this.Context.ServiceName);
await Task.Delay(TimeSpan.FromSeconds(120), cancellationToken);
}
Since the second health property is different from the first health property (since your are naming them based on the iteration), the first health property will remain until its TTL and then have its behavior as defined by the RemoveWhenExpired property (which is false by default, so it will just remain). The first property is not overwritten by the “OK” health report since that is a "different property". In SFX I bet you would actually see both of these properties. They would need to have the same name for this to work the way you’re expecting. More information is here.

How to Wait on Service Request (RQS)

**Note: Cross-posted at LabVIEW forums: http://forums.ni.com/t5/LabVIEW/C-VISA-wait-on-RQS/td-p/3122939
I'm trying to write a simple C# (.NET 4.0) program to control a Keithley 2400 SMU over VISA GPIB and I'm having trouble with getting the program to wait for the Service Request that the Keithley sends at the end of the sweep.
The sweep is a simple linear voltage sweep, controlled internally by the Keithley unit. I've got the unit set up to send a ServiceRequest signal at the end of the sweep or when compliance is reached.
I'm able to send the commands to the SMU and read the data buffer, but only if I manually enter a timeout between the sweep start command and the read data command.
One issue I'm having is that I'm quite new to C# - I'm using this project (porting parts of my LV code) to learn it.
Here is what I have so far for my C# code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using NationalInstruments.VisaNS;
private void OnServiceRequest(object sender, MessageBasedSessionEventArgs e)
{
Console.WriteLine("Service Request Received!");
}
// The following code is in a class method, but
public double[,] RunSweep()
{
// Create the session and message-based session
MessageBasedSession mbSession = null;
Session mySession = null;
string responseString = null;
// open the address
Console.WriteLine("Sending Commands to Instrument");
instrAddr = "GPIB0::25::INSTR";
mySession = ResourceManager.GetLocalManager().Open(instrAddr);
// Cast to message-based session
mbSession = (MessageBasedSession)mySession;
// Here's where things get iffy for me... Enabling the event and whatnot
mbSession.ServiceRequest += new MessageBasedSessionEventHandler(OnServiceRequest);
MessageBasedSessionEventType srq = MessageBasedSessionEventType.ServiceRequest;
mbSession.EnableEvent(srq, EventMechanism.Handler);
// Start the sweep (SMU was set up earlier)
Console.WriteLine("Starting Sweep");
mbSession.Write(":OUTP ON;:INIT");
int timeout = 10000; // milliseconds
// Thread.Sleep(10000); // using this line works fine, but it means the test always takes 10s even if compliance is hit early
// This raises error saying that the event is not enabled.
mbSession.WaitOnEvent(srq, timeout);
// Turn off the SMU.
Console.WriteLine("I hope the sweep is done, cause I'm tired of waiting");
mbSession.Write(":OUTP OFF;:TRAC:FEED:CONT NEV");
// Get the data
string data = mbSession.Query(":TRAC:DATA?");
// Close session
mbSession.Dispose();
// For now, create a dummy array, 3x3, to return. The array after is the starting value.
double[,] dummyArray = new double[3, 3] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
return dummyArray;
}
All the above is supposed to mimic this LabVIEW code:
So, any ideas on where I'm going wrong?
Thanks,
Edit:
After a little more fiddling, I've found that the Service Request function OnServiceRequest is actually fired at the right time ("Service Request Received!" is printed to the console).
It turns out that I need to enable the event as a Queue rather than a handler. This line:
mbSession.EnableEvent(srq, EventMechanism.Handler);
Should actually be:
mbSession.EnableEvent(srq, EventMechanism.Queue);
Source: The documentation under "Remarks". It was a pain to find the docs on it... NI needs to make it easier :-(.
With this change, I also don't need to create the MessageBasedSessionEventHandler.
The final, working code looks like:
rm = ResourceManager.GetLocalManager().Open("GPIB0::25::INSTR");
MessageBasedSession mbSession = (MessageBasedSession)rm;
MessageBasedSessionEventType srq = MessageBasedSessionEventType.ServiceRequest;
mbSession.EnableEvent(srq, EventMechanism.Queue); // Note QUEUE, not HANDLER
int timeout = 10000;
// Start the sweep
mbSession.Write(":OUTP ON;:INIT");
// This waits for the Service Request
mbSession.WaitOnEvent(srq, timeout);
// After the Service Request, turn off the SMUs and get the data
mbSession.Write(":OUTP OFF;:TRAC:FEED:CONT NEV");
string data = mbSession.Query(":TRAC:DATA?");
mbSession.Dispose();
What you're doing looks correct to me so it's possible that there's an issue with the NI library.
The only thing I can think of to try is waiting for "all events" instead of just "ServiceRequest." like this:
mbSession.WaitOnEvent(MessageBasedSessionEventType.AllEnabledEvents, timeout);
Note: that it doesn't look like you can "enable" all events (so don't change that part).
I also looked for some examples of how other people are doing Keithley sweeps and I found this and this(Matlab ex). As I suspected in both they don't use events to determine when the sweep is finished, but rather a "while loop that keeps polling the Keithley" (the first link actually uses threads, but it's the same idea). This makes me think that maybe that's your best bet. So you could just do this:
int timeout = 10000;
int cycleWait = 1000;
for (int i = 0; i < timeout / cycleWait; i++)
{
try
{
string data = mbSession.Query(":TRAC:DATA?");
break;
}
catch
{
Thread.Sleep(cycleWait);
}
}
(You may also have to check if data is null, but there has to be some way of knowing when the sweep is finished).

Enumerating events occuring in time using reactive extensions (rx)

public interface Event
{
Guid identifier;
Timestamp ts;
}
We're thinking of using Reactive Extensions for a rewrite of a problem at my financial firm.
The premise is that we get events identified by a Guid (stock symbol + uniqueness entropy embedded into it), a timestamp, and a Value field. These come at a high rate, and we cannot act on these objects until "at least" after X seconds (10 seconds), after which we have to act on them, and remove them from the system.
Think about it like two windows, an initial window of "10 seconds" (for example T0 to T10), where we identify all the unique events (basically, group by guid), then we look into the next "10 seconds", "secondary window" (T10-T20), to make sure we're implementing the policy of "at least" 10 seconds. From the "initial window", we remove all the events (because we've accounted for them), and then from the "secondary window", we remove the ones that occurred in the "initial window". And we keep on moving 10 second sliding windows, so now we're looking at window T20-T30, repeat and rinse.
How could I implement this in Rx, because it seems like the way to go.
If you can rely on the server clock and the timestamp in the message (that is, we're in 'real life' mode), and you're after a sliding 10 second delay as opposed to a jumping 10 second window, then you can just delay the events 10 seconds:
var events = new Subject<Event>();
var delayedEvents = events.Delay(TimeSpan.FromSeconds(10));
Checking for unique events etc is just a matter of adding them to a set of some sort:
var guidSet = new HashSet<Guid>();
delayedEvents.Do(e => guidSet.Add(e.identifier));
If you're problem is instead that you must wait 10 seconds and then process the last 10 seconds at once, then you just want to buffer for 10 seconds instead:
var bufferedEvents = events.Buffer(TimeSpan.FromSeconds(10));
bufferedEvents.Do(es => { foreach (var e in es) guidSet.Add(e.identifier); });
I haven't shown the example of a sliding 10 second window as I can't imagine that's what you want (events get processed more than once).
Now we get serious. Let's say you don't want to rely on wall time and instead want to use the time within your events to drive your logic. Assuming event is redefined as:
public class Event
{
public Guid identifier;
public DateTime ts;
}
Create the historical scheduler and feed the scheduled events from the original ones:
var scheduler = new HistoricalScheduler();
var driveSchedule = events.Subscribe(e => scheduler.AdvanceTo(e.ts));
var target = events.SelectMany(e => Observable.Timer(e.ts, scheduler).Select(_ => e));
Now you can simply use the regular Rx combinators on target instead of event, and just pass through the scheduler so they are triggered appropriately, for example:
var bufferedEvents = target.Buffer(TimeSpan.FromSeconds(10), scheduler);
Here's a simple test. Create a hundred events each 'virtually' 30 seconds apart but in real-time triggered every second:
var now = DateTime.Now;
var test = Enumerable.Range(0,99).Select(i =>
Scheduler.ThreadPool.Schedule(
TimeSpan.FromSeconds(i),
() => events.OnNext(new Event() {
identifier = Guid.NewGuid(),
ts = now.AddSeconds(i * 30)
})
)
).ToList();
Subscribe to it and ask for 60 seconds of buffered events - and actually receive 2 events every 2 'real' seconds (60 virtual seconds):
target.Select(e => String.Format("{0} {1}", e.identifier, e.ts.ToString()))
.Buffer(TimeSpan.FromSeconds(60), scheduler)
.Select(es => String.Join(" - ", es))
.DumpLive();

Categories

Resources