WaitForDrawerClose blocks printing - c#

I'm writing an application that uses Pos for .Net, and I'm noticing that if you call WaitForDrawerClose, then you won't be able to print receipts until it has returned.
This is not desirable behavior. Is there another way to wait for the cash drawer to close without blocking the printer?
I've looked into the OnDrawerStateChanged Event, but that is a protected member of CashDrawerBase, and I'm not entirely sure how to access it.
Here is my SSCCE:
static void Main(string[] args)
{
var posExplorer = new PosExplorer();
var waitTask = WaitForCloseAsync(posExplorer);
System.Threading.Thread.Sleep(500);
PrintText(posExplorer);
waitTask.Wait();
}
public static Task WaitForCloseAsync(PosExplorer posExplorer)
{
var result = Task.Factory.StartNew(() =>
{
Console.WriteLine("waiting");
var cashDrawer = GetCashDrawer(posExplorer);
cashDrawer.Open();
cashDrawer.Claim(1000);
cashDrawer.DeviceEnabled = true;
cashDrawer.WaitForDrawerClose(10000, 4000, 500, 5000);
cashDrawer.Release();
cashDrawer.Close();
Console.WriteLine("waited");
});
return result;
}
public static void PrintText(PosExplorer posExplorer)
{
Console.WriteLine("printing");
var printer = GetPosPrinter(posExplorer);
printer.Open();
printer.Claim(1000);
printer.DeviceEnabled = true;
var text = "abc\x1B|1lF";
printer.PrintNormal(PrinterStation.Receipt, text);
printer.Release();
printer.Close();
Console.WriteLine("printed");
}
public static CashDrawer GetCashDrawer(PosExplorer posExplorer)
{
var deviceInfo = posExplorer.GetDevices(DeviceCompatibilities.Opos)
.Cast<DeviceInfo>()
.Where(d => d.Type == "CashDrawer")
.ToList();
var device = deviceInfo.FirstOrDefault(d => d.Compatibility == DeviceCompatibilities.Opos);
if (device == null)
{
return null;
}
else
return (CashDrawer)posExplorer.CreateInstance(device);
}
private static PosPrinter GetPosPrinter(PosExplorer posExplorer)
{
var deviceInfo = posExplorer.GetDevices(DeviceCompatibilities.Opos)
.Cast<DeviceInfo>()
.Where(d => d.Type == "PosPrinter")
.ToList();
var device = deviceInfo.FirstOrDefault(d => d.Compatibility == DeviceCompatibilities.Opos);
if (device == null)
{
return null;
}
else
{
return (PosPrinter)posExplorer.CreateInstance(device);
}
}

so, what I did was essentially this: Instead of using (WaitForClose), I just poll it like this:
for (var i = 0; i < 15; i++)
{
cashDrawer = GetCashDrawer(posExplorer);
cashDrawer.Open();
cashDrawer.Claim(1000);
cashDrawer.DeviceEnabled = true;
if (!cashDrawer.DrawerOpened)
{
Console.WriteLine("waited");
return;
}
cashDrawer.Release();
cashDrawer.Close();
System.Threading.Thread.Sleep(1500);
}
Console.WriteLine("timed out");
It's not ideal, but it doesn't lock the printer up either, so It'll have to to for now.

Related

How to wait for 1 second before continuing async thread?

What I'm trying to achieve here is for the thread to await at FishData fishData = await Fishing().WaitOrCancel(cancelToken); until a not null fishData is returned, else just keep on waiting and checking TryHook() again every 1 second.
I have also tried replacing the await Task.Delay(1000); with Thread.Sleep() but they just doesn't seems to work and instead kept on causing crashes. also tried InvokeRepeating and coroutines but everything got too messy and I'm not even sure I implemented them correctly.
public async Task<bool> Fish(int ID)
{
reelingSuccess = false;
FishingRodData fishingRodData = inventory.fishingRods[ID];
if (currentCell.fill == CellFill.Water)
{
CancellationToken cancelToken = new CancellationToken();
try
{
FishData fishData = await Fishing().WaitOrCancel(cancelToken);
bool reelSuccess = await ReelingFish(fishingRodData.cycleTime, fishingRodData.tolerance, fishData.cycles, fishData.hits).WaitOrCancel(cancelToken);//, fishData.speed
if (reelSuccess) print("successfully reeled in fish");
else print("failed reeling in fish! better luck next time!");
inventory.interactBtn.onClick.RemoveAllListeners();
inventory.interactBtn.onClick.AddListener(delegate { inventory.itrct(); });
}
catch (OperationCanceledException) { print("fishing has been cancelled!"); }
return true;
} return false;
}
async Task<FishData> Fishing()
{
**await Task.Delay(1000);**
print("try hook");
FishData fish = TryHook();
if (fish == null) fish = await Fishing();
return fish;
}
ps: forgive my english
EDIT: here's the TryHook Code
its basically trying to catch a fish based on probability which will mostly return a null, but once every few calls from FishData fish = TryHook(); in Fishing(), it might return a fish.
FishData TryHook()
{
FishData fish = null;
for (int i = 0; i < fishLs.Length; i++)
{
fishLs[i].minValue = inventory.fishes[i].minValue;
fishLs[i].maxValue = inventory.fishes[i].maxValue;
fishLs[i].probability = inventory.fishes[i].probability;
}
int index = GetRandomValue(fishLs);//get random index in fishLs but based on probability instead of Random.Range()
if (index != -1) { print("caught fish ind: " + index); fish = inventory.fishes[index]; }
return fish;
}
EDIT2: not sure if this is important, but this is how the Fish() is called.
void Start()
{
itrct = new interact(Interact);
fs = new Fish(activities.Fish);
rf = new removeFront(RemoveFront);
tryActivities.Add(new Func<bool>(() => activities.DoNothing()));
//tryActivities.Add(new Func<bool>(() => activities.Fish(currentSlot.basicData.typeID).Result));
tryActivities.Add(new Func<bool>(() => fs(currentSlot.basicData.typeID).Result));
tryActivities.Add(new Func<bool>(() => activities.UseStove(currentSlot.basicData.typeID)));
}
public void Interact()//id of currently hold item
{
Debug.Log("interact");
if (currentSlot != null && currentSlot.basicData.allID != -1 && TryPlaceOrUse())//check for hand activity
{
Debug.Log("successfully do in-hand activity");
}
else if (currentCell.objects.Count > 1 && currentCell.objects[^1].GetComponent<SpawnedObjectData>().allID != -1 &&
tryActivities[(int)allBasicData[currentCell.objects[^1].GetComponent<SpawnedObjectData>().allID].myActivity]())//check activity infront
{
//await tryActivities[2];
Debug.Log("successfully do in-front activity");
}
else Debug.Log("show emotes~ there's not hing to do ~~");
}
async Task<FishData> Fishing()
{
while(true)
{
await Task.Delay(1000);
print("try hook");
FishData fish = TryHook();
if (fish != null)
{
return fish;
}
}
}
and then:
var data = await Fishing();
// do what you want with your fish here

how to wait c# webbrowser to complete document load in TPL

Hi I have this two blocks of code
when I navigate the browser to a url and try to wait for it I get a deadlock I think=) any help suggestions are much appreciated!
I'm just adding some more text here so I am able to post the question :/ sorry
foreach (var davaType in davaTypes)
{
Parallel.ForEach(years, year =>
{
ts.Add(Task.Run(() =>
{
var th = new Thread(async () =>
{
await doWorkAsync(year, davaType, tarafType);
Application.Run();
});
th.IsBackground = true;
th.SetApartmentState(ApartmentState.STA);
th.Start();
}));
});
}
and
public static async Task doWorkAsync(int year, string davaType, string tarafType)
{
using (var browser = new MyBrowser())
{
Console.WriteLine("Im Created Here");
browser.Navigate("some url");
while (!browser.MyLoaded)
{
Console.WriteLine("waiting");
await Task.Delay(10);
}
Console.WriteLine("complete");
browser.Document?.GetElementById("DropDownList1")?.SetAttribute("value", davaType);
browser.Document.GetElementById("DropDownList3")?.SetAttribute("value", tarafType);
browser.Document.GetElementById("DropDownList4")?.SetAttribute("value", year.ToString());
browser.Document.GetElementById("Button1").MyClick();
await browser.WaitPageLoad(10);
Console.WriteLine("Im DoneHere");
}
}
You can use TaskCompletionSource to create a task and await for it after the page is loaded.
See: UWP WebView await navigate.
I have experienced this problem before and the solution I have applied is as follows;
private void waitTillLoad()
{
WebBrowserReadyState loadStatus = default(WebBrowserReadyState);
int waittime = 100000;
int counter = 0;
while (true)
{
loadStatus = webBrowser1.ReadyState;
Application.DoEvents();
if ((counter > waittime) || (loadStatus == WebBrowserReadyState.Uninitialized) || (loadStatus == WebBrowserReadyState.Loading) || (loadStatus == WebBrowserReadyState.Interactive))
{
break; // TODO: might not be correct. Was : Exit While
}
counter += 1;
}
counter = 0;
while (true)
{
loadStatus = webBrowser1.ReadyState;
Application.DoEvents();
if (loadStatus == WebBrowserReadyState.Complete)
{
break; // TODO: might not be correct. Was : Exit While
}
counter += 1;
}
}
You need to create only one separate thread for WinForms. Other threads will be created by individual browsers themselves. What you do need is handling their DocumentCompleted event. The first time the document completes you enter and submit data, the second time it completes you get the result. When all browsers finish, you exit the winforms thread and you're done.
Note: To launch bots from winforms thread, I made use of Idle event handler, just because it was the first solution that came to my mind. You may find better way.
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleBrowsers
{
class Program
{
static void Main(string[] args)
{
string url = "https://your.url.com";
string[] years = { "2001", "2002", "2003"};
string[] davas = { "1", "2", "3" };
string taraf = "1";
int completed = 0, succeeded = 0;
var bots = new List<Bot>();
var started = false;
var startBots = (EventHandler)delegate (object sender, EventArgs b)
{
if (started)
return;
started = true;
foreach (var dava in davas)
foreach (var year in years)
bots.Add(new Bot { Year = year, Dava = dava, Taraf = taraf, Url = url }.Run((bot, result) =>
{
Console.WriteLine($"{result}: {bot.Year}, {bot.Dava}");
succeeded += result ? 1 : 0;
if (++completed == years.Length * davas.Length)
Application.ExitThread();
}));
};
var forms = new Thread((ThreadStart) delegate {
Application.EnableVisualStyles();
Application.Idle += startBots;
Application.Run();
});
forms.SetApartmentState(ApartmentState.STA);
forms.Start();
forms.Join();
Console.WriteLine($"All bots finished, succeeded: {succeeded}, failed:{bots.Count - succeeded}.");
}
}
class Bot
{
public string Url, Dava, Taraf, Year;
public delegate void CompletionCallback(Bot sender, bool result);
WebBrowser browser;
CompletionCallback callback;
bool submitted;
public Bot Run(CompletionCallback callback)
{
this.callback = callback;
browser = new WebBrowser();
browser.ScriptErrorsSuppressed = true;
browser.DocumentCompleted += Browser_DocumentCompleted;
browser.Navigate(Url);
return this;
}
void Browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
if (browser.ReadyState == WebBrowserReadyState.Complete)
{
if (submitted)
{
callback(this, true);
return;
}
var ok = SetValue("DropDownList1", Dava);
ok &= SetValue("DropDownList3", Taraf);
ok &= SetValue("DropDownList4", Year);
if (ok)
ok &= Click("Button1");
if (!(submitted = ok))
callback(this, false);
}
}
bool SetValue(string id, string value, string name = "value")
{
var e = browser?.Document?.GetElementById(id);
e?.SetAttribute(name, value);
return e != null;
}
bool Click(string id)
{
var element = browser?.Document?.GetElementById(id);
element?.InvokeMember("click");
return element != null;
}
}
}

Starting tasks inside another task is duplicating my WebRequests

I use the code below to check some pdf files online and return a string accordingly.
The problem is: When I added the second Task.Factory.StartNew() it started duplicating all requests, but still returning only one answer(as it should be).
I need this to be as fast as possible so I can't waste time sending two requests to the server.
public static void Main(string[] args)
{
var listT = new List<string>()
{
"24006025062"
};
var task = listT.Select(x => Task.Factory.StartNew(() => TesteTask(x)));
Task.WaitAll(task.ToArray(), TimeSpan.FromSeconds(120));
List<string> results = new List<string>();
foreach (var result in task)
{
results.Add(result.Result);
}
}
private static string TesteTask(string codCart)
{
var teste = new Consulta();
var retorno = string.Empty;
var session = teste.GetCaptcha();
for (int i = 0; i < 10; i++)
{
session.CaptchaResolvida = QuebraCaptcha(session.CaptchaCodificada).CaptchaResolvida;
if (session.CaptchaResolvida.Length > 0)
{
var links = teste.Consulta(codCart, session).Retorno;
if (links.Any())
{
var tasks = links.Select(x => Task.Factory.StartNew(() => Executa(teste, session, x)));
Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(120));
var modelList = from Result in tasks select Result.Result;
retorno = teste.FinalizaProcesso(modelList.ToList());
break;
}
}
}
return retorno;
}
private static string Executa(Consulta teste, Model<Request> session, string link)
{
var retorno = string.Empty;
for (int i = 0; i < 10; i++)
{
var CaptchaResolvida = QuebraCaptcha(teste.GetCaptchaPdf(session)).CaptchaResolvida;
if (CaptchaResolvida != null && CaptchaResolvida != string.Empty)
{
var status = teste.BaixaPdf(link, CaptchaResolvida, session);
if (status != string.Empty)
{
retorno = status;
break;
}
}
}
return retorno;
}
Ps: This is my first post on stack overflow, if I'm not clear enough please let me know!
You are getting this behavior because you are iterating twice on the Select returned IEnumerable. Try this:
public static void Main(string[] args)
{
var listT = new List<string>()
{
"24006025062"
};
var task = list
.Select(x => Task.Factory.StartNew(() => TesteTask(x)))
.ToArray();
Task.WaitAll(task, TimeSpan.FromSeconds(120));
List<string> results = new List<string>();
foreach (var result in task)
{
results.Add(result.Result);
}
}
By moving the ToArray() just after the Select() it creates the results IEnumerable only once instead of twice.
Hope it helps!

Multithreading using Task.Factory and Entity Framework?

I have an application that is notifying each subscriber in our SMS database of an update to our system. It uses the entity framework to select each record and then creates a new Task to send a message to that person. Theoretically, this should be a fast process. I'm doing something wrong because it is only getting one complete response every second or two.
I think that the problem has to do with the way I'm setting up the Tasks in Task.Factory.StartNew(). It is acting like it is running synchronously, but I want it to run asynchronously.
If I am completely off-base with how I am using Tasks, please let me know. I got my inspiration from this post.
Here's my code:
class Program
{
static List<MessageToSend> Messages = new List<MessageToSend>();
static Entities oDatabase = new Entities();
static SMS.API oAPI = new SMS.API();
const string sAuthToken = "*****";
const string sNotificationMessage = "*****";
static void Main(string[] args)
{
foreach (var subscriber in oDatabase.SMS_Subscribers.Where(x => x.GlobalOptOut == false))
{
MessageToSend oMessage = new MessageToSend();
oMessage.ID = subscriber.ID;
oMessage.MobileNumber = subscriber.MobileNumber;
var recentlySentMessage = oDatabase.SMS_OutgoingMessages.Where(x => x.Message == sNotificationMessage && x.MobileNumber == oMessage.MobileNumber && x.Sent > new DateTime(2014, 3, 12)).FirstOrDefault();
if (recentlySentMessage != null)
{
oMessage.Completed = true;
continue;
}
Task t = Task.Factory.StartNew(() =>
{
try{
var keywordID = oDatabase.SMS_SubscribersKeywords.Where(x => x.SubscriberID == oMessage.ID).First().KeywordID;
var keyword = oDatabase.SMS_Keywords.Where(x => x.ID == keywordID).First();
oMessage.DemographicID = keyword.DemographicID;
oMessage.Keyword = keyword.Keyword;
SendNotificationMessage(oMessage);
}
catch (Exception oEx){ //Write exception to console}
});
Thread.Sleep(15);
}
while (Messages.ToList().Any(x => !x.Completed)){ //wait till all are completed}
}
public static void SendNotificationMessage(object message)
{
MessageToSend oMessage = (MessageToSend)message;
try
{
SMS.APIResponse oResponse = oAPI.SendMessage(sAuthToken, oMessage.DemographicID, oMessage.Keyword, oMessage.MobileNumber, sNotificationMessage);
if (oResponse.Success){ //Write success to console }
else{ //Write failure to console }
}
catch (Exception oEx){ //Write Exception to console }
oMessage.Completed = true;
}
}
class MessageToSend
{
public long ID { get; set; }
public long DemographicID {get;set;}
public string MobileNumber { get; set; }
public bool Completed { get; set; }
public string Keyword { get; set; }
public MessageToSend(){ Completed = false; }
}
EDIT: The inside of the foreach block now looks like this:
MessageToSend oMessage = new MessageToSend();
oMessage.ID = subscriber.ID;
oMessage.MobileNumber = subscriber.MobileNumber;
int keywordID = 0;
SMSShortcodeMover.SMS_Keywords keyword;
var recentlySentMessage = oDatabase.SMS_OutgoingMessages.Where(x => x.Message == sNotificationMessage && x.MobileNumber == oMessage.MobileNumber && x.Sent > new DateTime(2014, 3, 12)).FirstOrDefault();
if (recentlySentMessage != null)
{
oMessage.Completed = true;
continue;
}
try
{
keywordID = (int)oDatabase.SMS_SubscribersKeywords.Where(x => x.SubscriberID == oMessage.ID).First().KeywordID;
keyword = oDatabase.SMS_Keywords.Where(x => x.ID == keywordID).First();
} catch (Exception oEx){ //write exception to console, then continue; }
Task t = Task.Factory.StartNew(() =>
{
oMessage.DemographicID = keyword.DemographicID;
oMessage.Keyword = keyword.Keyword;
SendNotificationMessage(oMessage);
});
Thread.Sleep(15);
}
EDIT 2:
I updated my code again, now I gather all of my data before I go into the send. It still is hanging somewhere, but it gets all 52,000 rows of data in about 5 seconds now. The code looks like this:
var query =
(from subscriber in oDatabase.SMS_Subscribers
where subscriber.GlobalOptOut == false
where !(from x in oDatabase.SMS_OutgoingMessages
where x.Message == sNotificationMessage
where x.MobileNumber == subscriber.MobileNumber
where x.Sent > new DateTime(2014, 3, 12)
select x).Any()
join sk in oDatabase.SMS_SubscribersKeywords
on subscriber.ID equals sk.SubscriberID
join k in oDatabase.SMS_Keywords on sk.KeywordID equals k.ID into ks
from k2 in ks.Take(1)
select new MessageToSend()
{
ID = subscriber.ID,
MobileNumber = subscriber.MobileNumber,
DemographicID = k2.DemographicID,
Keyword = k2.Keyword
}).ToList();
foreach( var q in query){
Task t = Task.Factory.StartNew(() => SendNotificationMessage(q));
Tasks.Add(t);
Thread.Sleep(80);
}
Task.WaitAll(Tasks.ToArray());
If I were you I would try to execute all of the database calls at once, before trying to sen your messages.
Try doing this:
var query =
from subscriber in oDatabase.SMS_Subscribers
where subscriber.GlobalOptOut == false
where !(from x in oDatabase.SMS_OutgoingMessages
where x.Message == sNotificationMessage
where x.MobileNumber == subscriber.MobileNumber
where x.Sent > new DateTime(2014, 3, 12)
select x
).Any()
join sk in oDatabase.SMS_SubscribersKeywords
on subscriber.ID equals sk.SubscriberID
join k in oDatabase.SMS_Keywords on sk.KeywordID equals k.ID into ks
from k2 in ks.Take(1)
select new
{
ID = subscriber.ID,
MobileNumber = subscriber.MobileNumber,
DemographicID = k2.DemographicID,
Keyword = k2.Keyword
};
var tasks =
from x in query.ToArray()
let message = new MessageToSend()
{
ID = x.ID,
MobileNumber = x.MobileNumber,
DemographicID = x.DemographicID,
Keyword = x.Keyword
}
select Task.Factory.StartNew(() => SendNotificationMessage(message));
Task.WaitAll(tasks.ToArray());
I don't have your database, so I can't test this, but something like this should work if this isn't exactly right.
It doesn't surprise me that it's taking 1-2 seconds for each iteration of the for each loop because you have 3 separate database calls that are executing synchronously. A database call is pretty slow in the grand scheme of things. One way to approach this would be to have a method that has everything in the foreach block except the Task code and then use task to call it. Just need to be careful that nothing inside the Task Method ends up blocking.
I.e.
var tasks = new List<Task>();
foreach (var subscriber in oDatabase.SMS_Subscribers.Where(x => x.GlobalOptOut == false))
{
tasks.Add(Task.Factory.StartNew(() => SendNotificationTask(subscriber));
Thread.Sleep(15);
}
//Might want to to use Task.WhenAll instead of WaitAll. Just need to debug it and see what happens.
Task.WaitAll(tasks.ToArray());
public void SendNotificationTask(SomeType subscriber)
{
MessageToSend oMessage = new MessageToSend();
oMessage.ID = subscriber.ID;
oMessage.MobileNumber = subscriber.MobileNumber;
int keywordID = 0;
SMSShortcodeMover.SMS_Keywords keyword;
////Database call 1
var recentlySentMessage = oDatabase.SMS_OutgoingMessages.Where(x => x.Message == sNotificationMessage && x.MobileNumber == oMessage.MobileNumber && x.Sent > new DateTime(2014, 3, 12)).FirstOrDefault();
if (recentlySentMessage != null)
{
oMessage.Completed = true;
}
else
{
try
{
////Database call 2
keywordID = (int)oDatabase.SMS_SubscribersKeywords.Where(x => x.SubscriberID == oMessage.ID).First().KeywordID;
////Database call 3
keyword = oDatabase.SMS_Keywords.Where(x => x.ID == keywordID).First();
} catch (Exception oEx){ //write exception to console, then continue; }
oMessage.DemographicID = keyword.DemographicID;
oMessage.Keyword = keyword.Keyword;
SendNotificationMessage(oMessage);
}
}

Pause and Resume Subscription on cold IObservable

Using Rx, I desire pause and resume functionality in the following code:
How to implement Pause() and Resume() ?
static IDisposable _subscription;
static void Main(string[] args)
{
Subscribe();
Thread.Sleep(500);
// Second value should not be shown after two seconds:
Pause();
Thread.Sleep(5000);
// Continue and show second value and beyond now:
Resume();
}
static void Subscribe()
{
var list = new List<int> { 1, 2, 3, 4, 5 };
var obs = list.ToObservable();
_subscription = obs.SubscribeOn(Scheduler.NewThread).Subscribe(p =>
{
Console.WriteLine(p.ToString());
Thread.Sleep(2000);
},
err => Console.WriteLine("Error"),
() => Console.WriteLine("Sequence Completed")
);
}
static void Pause()
{
// Pseudocode:
//_subscription.Pause();
}
static void Resume()
{
// Pseudocode:
//_subscription.Resume();
}
Rx Solution?
I believe I could make it work with some kind of Boolean field gating combined with thread locking (Monitor.Wait and Monitor.Pulse)
But is there an Rx operator or some other reactive shorthand to achieve the same aim?
Here's a reasonably simple Rx way to do what you want. I've created an extension method called Pausable that takes a source observable and a second observable of boolean that pauses or resumes the observable.
public static IObservable<T> Pausable<T>(
this IObservable<T> source,
IObservable<bool> pauser)
{
return Observable.Create<T>(o =>
{
var paused = new SerialDisposable();
var subscription = Observable.Publish(source, ps =>
{
var values = new ReplaySubject<T>();
Func<bool, IObservable<T>> switcher = b =>
{
if (b)
{
values.Dispose();
values = new ReplaySubject<T>();
paused.Disposable = ps.Subscribe(values);
return Observable.Empty<T>();
}
else
{
return values.Concat(ps);
}
};
return pauser.StartWith(false).DistinctUntilChanged()
.Select(p => switcher(p))
.Switch();
}).Subscribe(o);
return new CompositeDisposable(subscription, paused);
});
}
It can be used like this:
var xs = Observable.Generate(
0,
x => x < 100,
x => x + 1,
x => x,
x => TimeSpan.FromSeconds(0.1));
var bs = new Subject<bool>();
var pxs = xs.Pausable(bs);
pxs.Subscribe(x => { /* Do stuff */ });
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
Thread.Sleep(500);
bs.OnNext(true);
Thread.Sleep(5000);
bs.OnNext(false);
It should be fairly easy for you to put this in your code with the Pause & Resume methods.
Here it is as an application of IConnectableObservable that I corrected slightly for the newer api (original here):
public static class ObservableHelper {
public static IConnectableObservable<TSource> WhileResumable<TSource>(Func<bool> condition, IObservable<TSource> source) {
var buffer = new Queue<TSource>();
var subscriptionsCount = 0;
var isRunning = System.Reactive.Disposables.Disposable.Create(() => {
lock (buffer)
{
subscriptionsCount--;
}
});
var raw = Observable.Create<TSource>(subscriber => {
lock (buffer)
{
subscriptionsCount++;
if (subscriptionsCount == 1)
{
while (buffer.Count > 0) {
subscriber.OnNext(buffer.Dequeue());
}
Observable.While(() => subscriptionsCount > 0 && condition(), source)
.Subscribe(
v => { if (subscriptionsCount == 0) buffer.Enqueue(v); else subscriber.OnNext(v); },
e => subscriber.OnError(e),
() => { if (subscriptionsCount > 0) subscriber.OnCompleted(); }
);
}
}
return isRunning;
});
return raw.Publish();
}
}
Here is my answer. I believe there may be a race condition around pause resume, however this can be mitigated by serializing all activity onto a scheduler. (favor Serializing over synchronizing).
using System;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using System.Reactive.Subjects;
using Microsoft.Reactive.Testing;
using NUnit.Framework;
namespace StackOverflow.Tests.Q7620182_PauseResume
{
[TestFixture]
public class PauseAndResumeTests
{
[Test]
public void Should_pause_and_resume()
{
//Arrange
var scheduler = new TestScheduler();
var isRunningTrigger = new BehaviorSubject<bool>(true);
Action pause = () => isRunningTrigger.OnNext(false);
Action resume = () => isRunningTrigger.OnNext(true);
var source = scheduler.CreateHotObservable(
ReactiveTest.OnNext(0.1.Seconds(), 1),
ReactiveTest.OnNext(2.0.Seconds(), 2),
ReactiveTest.OnNext(4.0.Seconds(), 3),
ReactiveTest.OnNext(6.0.Seconds(), 4),
ReactiveTest.OnNext(8.0.Seconds(), 5));
scheduler.Schedule(TimeSpan.FromSeconds(0.5), () => { pause(); });
scheduler.Schedule(TimeSpan.FromSeconds(5.0), () => { resume(); });
//Act
var sut = Observable.Create<IObservable<int>>(o =>
{
var current = source.Replay();
var connection = new SerialDisposable();
connection.Disposable = current.Connect();
return isRunningTrigger
.DistinctUntilChanged()
.Select(isRunning =>
{
if (isRunning)
{
//Return the current replayed values.
return current;
}
else
{
//Disconnect and replace current.
current = source.Replay();
connection.Disposable = current.Connect();
//yield silence until the next time we resume.
return Observable.Never<int>();
}
})
.Subscribe(o);
}).Switch();
var observer = scheduler.CreateObserver<int>();
using (sut.Subscribe(observer))
{
scheduler.Start();
}
//Assert
var expected = new[]
{
ReactiveTest.OnNext(0.1.Seconds(), 1),
ReactiveTest.OnNext(5.0.Seconds(), 2),
ReactiveTest.OnNext(5.0.Seconds(), 3),
ReactiveTest.OnNext(6.0.Seconds(), 4),
ReactiveTest.OnNext(8.0.Seconds(), 5)
};
CollectionAssert.AreEqual(expected, observer.Messages);
}
}
}
It just works:
class SimpleWaitPulse
{
static readonly object _locker = new object();
static bool _go;
static void Main()
{ // The new thread will block
new Thread (Work).Start(); // because _go==false.
Console.ReadLine(); // Wait for user to hit Enter
lock (_locker) // Let's now wake up the thread by
{ // setting _go=true and pulsing.
_go = true;
Monitor.Pulse (_locker);
}
}
static void Work()
{
lock (_locker)
while (!_go)
Monitor.Wait (_locker); // Lock is released while we’re waiting
Console.WriteLine ("Woken!!!");
}
}
Please, see How to Use Wait and Pulse for more details

Categories