I am using RX to query the events that come out of an automation device, which has buttons connected to it. I want to be able to tell the difference when a user has just pressed and immediately released the button, or if he is holding the button down for some time. I'm having a problem with the query I use to see if he's holding the button down.
The basic idea is that once the button has been pressed, I would produce a value every half second, until the button was released again. I am also timestamping each value, so that I know how long the button has been pressed.
Here's my code, and how I think this should work:
public IObservable<DigitalInputHeldInfo> Adapt(IObservable<EventPattern<AdsNotificationEventArgs>> messageStream) {
var startObservable =
_digitalInputPressedEventAdapter.Adapt(messageStream);
var endObservable =
_digitalInputReleasedEventAdapter.Adapt(messageStream);
return from notification in startObservable.Timestamp()
from interval in
Observable.
Interval(
500.Milliseconds(),
_schedulerProvider.ThreadPool).
Timestamp().
TakeUntil(endObservable)
select new DigitalInputHeldInfo(
interval.Timestamp.Subtract(notification.Timestamp),
notification.Value);
}
From a given IObservable, I am applying a query on it so that I have an observable startObservable which produces a value everytime the button is pressed (state goes from false to true). I also have an observable endObservable, queried from the same source observable, which produces a value when the button is released again (state goes from true to false). When startObservable produces a value, I start an observable interval at every 500 milliseconds. I timestamp these values, and I take from it until endObservable produces a value. I then return an object holding the value of the startObservable and how long is has been held up until now.
I have a unit test for this where I hold the button for 2 seconds (using the TestScheduler), release it and then let the scheduler go on for a couple more seconds. I do this to make sure that no more values are produced after the button was released.
This is the point where my test fails, and the topic of my question. In my test, I am expecting 4 values to be produced (after 0.5s, 1s, 1.5s and 2s). However, there are still events being produced after the button was released, even though I am using TakeUntil(endObservable) (the digitalInputReleasedEventAdapter that produces the endObservable has its own set of tests, and I'm confident that it works the way it should).
I don't think I've constructed my query incorrectly. I suspect it might have something to do with hot vs. cold observables. But as I'm just starting out with RX, I don't quite have a full understanding of how that might relate to the issue I'm having here.
Not sure if this does exactly what you're after, but it's "yet another approach":
void Main()
{
// ButtonPush returns IObservable<bool>
var buttonPusher = ButtonPush();
var pushTracker =
from pushOn in buttonPusher.Where(p => p).Timestamp()
let tickWindow = Observable.Interval(TimeSpan.FromMilliseconds(500))
from tick in tickWindow
.TakeUntil(buttonPusher.Where(p => !p))
.Timestamp()
.Select(t => t.Timestamp)
select tick;
Console.WriteLine("Start: {0}", Observable.Return(true).Timestamp().First().Timestamp);
var s = pushTracker
.SubscribeOn(NewThreadScheduler.Default)
.Subscribe(x => Console.WriteLine("tick:{0}", x));
}
IObservable<bool> ButtonPush()
{
var push = new BehaviorSubject<bool>(false);
var rnd = new Random();
Task.Factory.StartNew(
() =>
{
// "press button" after random # of seconds
Thread.Sleep(rnd.Next(2, 5) * 1000);
push.OnNext(true);
// and stop after another random # of seconds
Thread.Sleep(rnd.Next(3, 10) * 1000);
push.OnNext(false);
});
return push;
}
It sounds like what you want is to only receive time events while the button is down. CombineLatest should help you here.
For example:
using Microsoft.Reactive.Testing;
using System;
using System.Diagnostics;
using System.Reactive.Linq;
using System.Reactive.Subjects;
namespace ButtonTest
{
class Program
{
enum State
{
KeyDown, KeyUp
}
static void Main(string[] args)
{
var buttonState = new BehaviorSubject<State>(State.KeyUp);
var testScheduler = new TestScheduler();
var events = testScheduler.CreateObserver<long>();
Observable.Interval(TimeSpan.FromTicks(100), testScheduler)
.CombineLatest(buttonState, (t,s)=> new { TimeStamp = t, ButtonState = s })
.Where(t => t.ButtonState == State.KeyDown)
.Select(t => t.TimeStamp)
.Subscribe(events);
testScheduler.AdvanceBy(100);//t=0
testScheduler.AdvanceBy(100);//t=1
buttonState.OnNext(State.KeyDown);
testScheduler.AdvanceBy(100);//t=2
testScheduler.AdvanceBy(100);//t=3
buttonState.OnNext(State.KeyUp);
testScheduler.AdvanceBy(100);//t=4
testScheduler.AdvanceBy(100);//t=5
buttonState.OnNext(State.KeyDown);
testScheduler.AdvanceBy(100);//t=6
buttonState.OnNext(State.KeyUp);
testScheduler.AdvanceBy(100);//t=7
testScheduler.AdvanceBy(100);//t=8
Debug.Assert(events.Messages.Count == 5);
Debug.Assert(events.Messages[0].Value.Value == 1);
Debug.Assert(events.Messages[1].Value.Value == 2);
Debug.Assert(events.Messages[2].Value.Value == 3);
Debug.Assert(events.Messages[3].Value.Value == 5);
Debug.Assert(events.Messages[4].Value.Value == 6);
}
}
}
Not sure if this is the bug, but your TimeStamp call isn't incorporating the test scheduler. Check all operators that take an IScheduler parameter and make sure they're passing in the test scheduler.
I have found a solution.
public IObservable<DigitalInputHeldInfo> Adapt(
IObservable<EventPattern<AdsNotificationEventArgs>> messageStream) {
var startObservable = _digitalInputPressedEventAdapter.
Adapt(messageStream).
Publish();
var endObservable = _digitalInputReleasedEventAdapter.
Adapt(messageStream).
Publish();
startObservable.Connect();
endObservable.Connect();
return from notification in startObservable.Timestamp()
from interval in Observable.Interval(500.Milliseconds(),
_schedulerProvider.ThreadPool).
Timestamp().
TakeUntil(endObservable)
select new DigitalInputHeldInfo(
interval.Timestamp.Subtract(notification.Timestamp),
notification.Value);
}
I isolated this code into a console application, and constructed the source observable (here called messageStream) from an IEnumerable<> which yielded some true and false values. I saw that this IEnumerable<> was generated several times, so there must have been several threads started. I believe the produced values were being consumed by separate instances of Observable.Interval, and that these were racing against each other to receive the messages indicating the button release. So now I'm calling Publish() and Connect() on the startObservable and endObservable, and so the Observable.Interval instances share the same subscription.
Related
I need to click an okay button which might appear after completing a field - it might take 5 seconds to appear. So i need (if) Wait for existence 5 seconds. I'm using PageFactory in a pages framework, I've seen some solutions but cant figure out how to implement them in this context.
[FindsBy(How = How.Name, Using = "OK")]
private IWebElement alertOKBtn;
public void PopulateFields //method to populate the form
{
// Populate fields
dateFromField.SendKeys(DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"));
// Click on this field
descriptionField.Click();
//OK button might appear, might take 5secs - pseudcode
if ( ***alertOKBtn exists, wait for it for 5 secs..*** )
{
alertOkBtn.Click();
}
//continue populating form
}
The PopulateFields method is called from the [Test] as:-
Pages.PTW.PopulateFields();
where Pages.PTW is a get method to PageFactory.InitElements(browser.Driver, page); return page;
Managed to resolve it - in PopulateFields i now do this:-
//wait to see if alert popup appears - give it 8 secs
string waitToSee = browser.wait(alertOKBtn, 8);
if ( waitToSee == "true" )
{
alertOKBtn.Click(); //alert popup did appear
}
Then I've added a method to my browser.class :-
public static string wait(IWebElement elem, int timeout ) //waits for existence of element up to timeout amount
{
try
{
var wait = new WebDriverWait(webDriver, TimeSpan.FromSeconds(timeout));
wait.Until(ExpectedConditions.ElementToBeClickable(elem));
return "true";
}
catch (Exception e ) //didnt appear so exception thrown return false
{
return "false";
}
So it now waits up to 8 seconds and if it doesnt appear it ignores and moves on. Thanks Bendram for the pointers.
Need to add conditional wait. That means, your code should wait till the control appears and then perform the action.
WebDriverWait class which inherits DefaultWait class serves the purpose. The below is the code snippet.
var wait = new WebDriverWait(this.driver, waitTime);
wait.Until(ExpectedConditions.ElementToBeClickable(alertOkBtn));
alertOkBtn.Click();
I have a ICollectionVIew named 'CompanyView'.
I also have a Filter for it called 'CompanyFilter'.
And a Textbox bound to a 'SearchCompanyTitle' property.
As I type in a databound textbox, 'CompanyFilter' gets fired with every letter and the 'CompanyView' gets filtered to show relevant results.
That works fine.
Unfortunately the table I'm filtering has about 9 000 rows so there tends to be a notable delay between the moment you press the key on the keyboard and it showing up on screen.
So what I decided to do instead was ensure that the filter was automatically fired when the user had finished typing. Which raises the question of how does the ViewModel know when the user has finished?
What I did is the below;
// This is the property the Textbox is bound to
private string _searchCompanyTitle = "";
public string SearchCompanyTitle
{
get { return _searchCompanyTitle; }
set
{
_searchCompanyTitle = value;
OnPropertyChanged("SearchCompanyTitle");
// After a character has been typed it will fire the below method
SearchCompany();
}
}
// This method is fired by the above property everytime a character is typed into the textbox
// What this method is meant to do is wait 1000 microseconds before it fires the filter
// However I need the timer to be reset every time a character is typed,
// Even if it hasn't reached 1000 yet
// But it doesn't do that. It continues to count before triggering the filter
private async void SearchCompany()
{
bool wait = true;
while (wait == true)
{
await Task.Delay(1000);
wait = false;
}
CompanyView.Filter = CompanyFilter;
}
// And this is the filter
private bool CompanyFilter(object item)
{
company company = item as company;
return company.title.Contains(SearchCompanyTitle);
}
So that's my problem. I need the filter to fire only when the timer hits 1000 and not before. At the same time I need the timer to go back to 0 every time the method is fired by the property. Clearly I'm not doing it right. Any ideas?
Sounds like a perfect candidate for binding Delay:
<TextBox Text="{Binding SearchCompanyTitle, Delay=1000}"/>
One solution could be to use the System.Threading.Timer class.
You can give it a callback to be called when the time set is elapsed.
Put the filter method as the callback and reset the timer's time on every key stroke.
You can find an example here.
--EDIT--
I didn't see that you were using WPF, Sinatr answer is the correct one, just use binding delay
I have a ListView in my Windows Phone 8.1 application and I can have something like 1000 or more results, so I need to implement a Load More feature each time the scroll hits bottom, or some other logic and natural way of triggering the adding of more items to the List.
I found that the ListView has support for an ISupportIncrementalLoading, and found this implementation: https://marcominerva.wordpress.com/2013/05/22/implementing-the-isupportincrementalloading-interface-in-a-window-store-app/
This was the better solution I found, since it does not specify a type, i.e., it's generic.
My problem with this solution is that when the ListView is Loaded, the LoadMoreItemsAsync runs all the times needed until it got all the results, meaning that the Load More is not triggered by the user. I'm not sure what make the LoadMoreItemsAsync trigger, but something is not right, because it assumes that happens when I open the page and loads all items on the spot, without me doing anything, or any scrolling. Here's the implementation:
IncrementalLoadingCollection.cs
public interface IIncrementalSource<T> {
Task<IEnumerable<T>> GetPagedItems(int pageIndex, int pageSize);
void SetType(int type);
}
public class IncrementalLoadingCollection<T, I> : ObservableCollection<I>, ISupportIncrementalLoading where T : IIncrementalSource<I>, new() {
private T source;
private int itemsPerPage;
private bool hasMoreItems;
private int currentPage;
public IncrementalLoadingCollection(int type, int itemsPerPage = 10) {
this.source = new T();
this.source.SetType(type);
this.itemsPerPage = itemsPerPage;
this.hasMoreItems = true;
}
public bool HasMoreItems {
get { return hasMoreItems; }
}
public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count) {
var dispatcher = Window.Current.Dispatcher;
return Task.Run<LoadMoreItemsResult>(
async () => {
uint resultCount = 0;
var result = await source.GetPagedItems(currentPage++, itemsPerPage);
if(result == null || result.Count() == 0) {
hasMoreItems = false;
}
else {
resultCount = (uint)result.Count();
await dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
() => {
foreach(I item in result)
this.Add(item);
});
}
return new LoadMoreItemsResult() { Count = resultCount };
}).AsAsyncOperation<LoadMoreItemsResult>();
}
}
Here's the PersonModelSource.cs
public class DatabaseNotificationModelSource : IIncrementalSource<DatabaseNotificationModel> {
private ObservableCollection<DatabaseNotificationModel> notifications;
private int _type = "";
public DatabaseNotificationModelSource() {
//
}
public void SetType(int type) {
_type = type;
}
public async Task<IEnumerable<DatabaseNotificationModel>> GetPagedItems(int pageIndex, int pageSize) {
if(notifications == null) {
notifications = new ObservableCollection<DatabaseNotificationModel>();
notifications = await DatabaseService.GetNotifications(_type);
}
return await Task.Run<IEnumerable<DatabaseNotificationModel>>(() => {
var result = (from p in notifications select p).Skip(pageIndex * pageSize).Take(pageSize);
return result;
});
}
}
I changed it a bit, because the call to my Database is Asynchronous and it was the only way I found to make sure I could wait for the query before filling the collection.
And in my DatabaseNotificationViewModel.cs
IncrementalNotificationsList = new IncrementalLoadingCollection<DatabaseNotificationModelSource, DatabaseNotificationModel>(type);
Everything works fine, apart from the not so normal "Load More". What's wrong in my code?
I created a very simplified example of this issue here, and raised this issue on the MSDN forums here. Honestly, I don't know why this weird behavior is happening.
What I observed
The ListView will call LoadMoreItemsAsync first with a count of 1. I assume this is to determine the size of a single item so that it can work out the number of items to request for the next call.
If the ListView is behaving nicely, the second call to LoadMoreItemsAsync should happen immediately after the first call, but with the correct number of items (count > 1), and then no more calls to LoadMoreItemsAsync will occur unless you scroll down. In your example, however, it may incorrectly call LoadMoreItemsAsync with a count of 1 again.
In the worst case, which actually occurs quite frequently in your example, is that the ListView will continue to call LoadMoreItemsAsync with a count of 1 over and over, in order, until HasMoreItems becomes false, in which case it has loaded all of the items one at a time. When this happens, there is a noticeable UI delay while the ListView loads the items. The UI thread isn't blocked, though. The ListView is just hogging the UI thread with sequential calls to LoadMoreItemsAsync.
The ListView won't always exhaust all of the items though. Sometimes it will load 100, or 200, or 500 items. In each case, the pattern is: many calls of LoadMoreItemsAsync(1) followed by a single call to LoadMoreItemsAsync(> 1) if not all of the items have been loaded by the prior calls.
It only seems to occur on page load.
The issue is persistent on Windows Phone 8.1 as well as Windows 8.1.
What causes the problem
The issue seems to be very short lived awaited tasks in the LoadMoreItemsAsync method before you've added the items to the list (awaiting tasks after you've added the items to the list is fine).
The issue doesn't occur if you remove all awaits inside LoadMoreItemsAsync, thus forcing it to execute synchronously. Specifically, if you remove the dispatcher.RunAsync wrapper and await source.GetPagedItems (just mock the items instead), then the ListView will behave nicely.
Having removed all awaits, the issue will reappear even if all you add is a seemingly harmless await Task.Run(() => {}). How bizarre!
How to fix the problem
If most of the time spent in a LoadMoreItemsAsync call is waiting for a HTTP request for the next page of items, as I expect most apps are, then the issue won't occur. So, we can extend the time spent in the method by awaiting a Task.Delay(10), like this maybe:
await Task.WhenAll(Task.Delay(10), dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
foreach (I item in result)
this.Add(item);
}).AsTask());
I've merely provided a (hacky) workaround for your example, but not an explanation why. If anyone knows why this is happening, please let me know.
This is not the only thing that can cause this issue. If your ListView is inside a ScrollViewer, it will continue loading all of the items and ALSO will not virtualize properly, negatively impacting performance. The solution is to give your ListView a specific height.
I'm trying to build stock market Bar (snapshot) data at run-time using tick data. My stock data provider provides access to tick level data where I have an event called OnTick that is triggered whenever a new tick is sent by the data provider. I'm hoping to do one of the two below, or if someone can suggest a good option:
Option 1:
In this option I maintain a Bar object and update it each time I get a tick. The OnBar() event can be attached to a timer elapsed event (1 minute for 1 minute bars etc).
//TickMsg = double price, DateTime dttm
public void OnTick(TickMsg newTick)
{
TaskFactory.StartNew(){UpdateBar(newTick)};//Syntax not specific
}
UpdateBar()
{
//nextBar is a Bar object thats intialized to Open = 0, High = 0, Low = 0, Close = 0
if(nextBar.Open==0)
nextBar.Open = newTick.price;
if(newTick.price>nextBar.High)
nextBar.High = newTick.price;
if(newTick.price<nextBar.Low)
nextBar.Low = newTick.price;
nextBar.Close = newTick.price;
}
public void OnBar(Bar bar)
{
//Process the bar..perform calculations etc
bar = new Bar(0,0,0,0);//Reset the bar
}
Option 2:
In this option I'm just adding the tick to a list of ticks and perform the calculations when OnBar is called. The OnBar() event can be attached to a timer elapsed event (1 minute for 1 minute bars etc).
List <TickMsg> TickList;
public void OnTick(TickMsg newTick)
{
TickList.Add(newTick);
}
public void OnBar()//called on a timer
{
var low = TickList.Min();
var high = TickList.Max();
var close = (from entry in TickList orderby entry.TickMsg.dttm ascending).Last();
var open = (from entry in TickList orderby entry.TickMsg.dttm ascending).First();
TickList.Empty();
}
Questions:
Which approach is more processing intensive?
Which approach requires more memory?
Again, if someone has a suggestion on an alternative approach, I'm all ears.
Don't you need to display or access the bar before it's completed? In case option 2 seems not to achieve that. Option 1 I can never imagine will be a performance clog. And it will use less memory, as you don't seem to save the tick data to any variable.
I think the best approach it is the second.
When you reset the bar on first approach, the lowest price will never be less than zero, so, the low price on bar will be always zero.
Trade link is a good example on how it is done. Their tutorials cover this. Also since it is open source you can have a peek into how it is done. The getting started tutorial is here.
I am slowly learning how to use Reactive Extensions for .NET with WPF. There a few beginner examples about how simple it is to write drag-drop or drawing routines but they are all extremely simple. I'm trying to go one step further and it's not obvious to me what the "proper" way is.
The examples all show how you can define streams of events from MouseDown, MouseMove, and MouseUp
var mouseDown = from evt in Observable.FromEvent<MouseButtonEventArgs>(..., "MouseDown")
select evt.EventArgs.GetPosition(...);
var mouseMoves = from evt in Observable.FromEvent<MouseEventArgs>(..., "MouseMove")
select evt.EventArgs.GetPosition(...);
var mouseUp = Observable.FromEvent<MouseButtonEventArgs>(..., "MouseUp");
And then how you can easily do things during a MouseDrag (this displays the co-ordinates of the rectangle created from the starting drag point to the current mouse position)
var mouseDrag = from start in mouseDown
from currentPosition in mouseMoves.TakeUntil(mouseUp)
select new Rect(Math.Min(start.X, currentPosition.X),
Math.Min(start.Y, currentPosition.Y),
Math.Abs(start.X - currentPosition.X),
Math.Abs(start.Y - currentPosition.Y));
mouseDrag.Subscribe(x =>
{
Info.Text = x.ToString();
});
My question is, what is the "proper" way to accomplish a task at the end of the mouse drag? Originally, I thought I could do something like this:
mouseDrag.Subscribe(
onNext: x =>
{
Info.Text = x.ToString();
},
onCompleted: () =>
{
// Do stuff here...except it never gets called
});
Reading more of the documentation, though, it seems that onCompleted is called when there is no more data (ever) and when the object can be disposed.
So the first option that seems plausable is subscribing to the mouseUp event and doing something there.
mouseUp.Subscribe(x =>
{
// Do stuff here..
}
But then at this point, I may as well go back to just use the "normal" MouseLeftButtonUp event handler.
Is there another way to determine when the mouseDrag is "completed" (or when the TakeUntil(mouseUp)) occurs and perform some action then?
The sequence never completes because the source (MouseDown) never completes (it is an event). It's worth pointing out that a IObservable cannot call OnComplete of a subscriber more than once, it's part of the contract (OnNext* (OnCompleted|OnError)?).
To find out when themouseMove.TakeUntil(mouseUp) sequence completes, you'll need to hook into the call to SelectMany:
public static IDisposable TrackDrag(this UIElement element,
Action<Rect> dragging, Action dragComplete)
{
var mouseDown = Observable.FromEvent(...);
var mouseMove = Observable.FromEvent(...);
var mouseUp = Observable.FromEvent(...);
return (from start in mouseDown
from currentPosition in mouseMove.TakeUntil(mouseUp)
.Do(_ => {}, () => dragComplete())
select new Rect(Math.Min(start.X, currentPosition.X),
Math.Min(start.Y, currentPosition.Y),
Math.Abs(start.X - currentPosition.X),
Math.Abs(start.Y - currentPosition.Y));
).Subscribe(dragging);
}
Then you can use it like so:
element.TrackDrag(
rect => { },
() => {}
);
For the interest of clarity, here is the LINQ expression using the underlying extension methods:
return mouseDown.SelectMany(start =>
{
return mouseMove
.TakeUntil(mouseUp)
.Do(_ => {}, () => dragComplete())
.Select(currentPosition => new Rect(...));
})
.Subscribe(dragging);
That is, for each value from mouseDown a new sequence will be subscribed to. When that sequence completes, call dragComplete().