Observable with Timer for specifc value - c#

Not sure about the title of my question but hopefully I can explain what I am trying to do.
I want to have a Timer to inject a value into a sequence but when a specific value is observed.
I want the Timer to be cancelled when any other value is entered into the sequence.
public enum State
{
Connected,
Disconnected,
DisconnectedRetryTimeout
}
var stateSubject = new Subject<State>();
var connectionStream = stateSubject.AsObservable();
var disconnectTimer =
Observable.Return(State.DisconnectedRetryTimeout)
.Delay(TimeSpan.FromSeconds(30))
.Concat(Observable.Never<State>());
var disconnectSignal =
disconnectedTimer
.TakeUntil(connectionStream.Where(s => s == State.Connected))
.Repeat();
var statusObservable =
Observable.Merge(connectionStream, disconnectSignal)
.DistinctUntilChanged();
So when nothing is in the stream (i.e. new) no timer.
When Connected|DisconnectedRetryTimeout is add no timer.
When Disconnected is add I want the timer to start
If Connected is on the stream before the timer fires I want the timer cancelled
The Timer should only fire once, until Disconnected is received again.
Pretty new to RX and ran out of ideas on this one.
Any help much appreciated.

If I understand the problem correctly: We start with a state stream that will emit either Connected or Disconnected messages. We want to enrich this with DisconnectedRetryTimeout message that appears if a Disconnected message sits on the stream for 30 seconds without a Connected message appearing.
One way to do this has the following idea:
Project the Connected/Disconnected stream into the DisconnectedRetryTimeout stream as follows:
First strip duplicates with DistinctUntilChanged as we only want the first Disconnected message of a batch to start a timer.
If Disconnected is received, project this event as a stream that emits DisconnectedRetryTimeout after 30 seconds
If Connected is received, just project an infinite and empty stream (Observable.Never)
With the above, we end up with a stream of streams, so now we use Switch which flattens this by always taking the most recent stream. So if Connect appears whilst the timer is in flight, the Never stream will replace the timer stream.
Now we can merge this with the original de-duped stream. Note that we publish the de-duped stream because we will be subscribing to it twice - if we don't do this, and the source is cold then we can run into problems. See more about that here.
var stateSubject = new Subject<State>();
var state = stateSubject.DistinctUntilChanged().Publish().RefCount();
var disconnectTimer = state
.Select(x => x == State.Disconnected
? Observable.Timer(TimeSpan.FromSeconds(30))
.Select(_ => State.DisconnectedRetryTimeout)
: Observable.Never<State>())
.Switch();
var statusObservable =
state.Merge(disconnectTimer);
EDIT: Simpler Version
You can do this all in one go and drop the publish step and merge - by using StartWith we can push the Disconnected events through the timer stream, and with Observable.Return we can push the Connected through replacing the empty Never stream:
var statusObservable = stateSubject
.DistinctUntilChanged()
.Select(x => x == State.Disconnected
? Observable.Timer(TimeSpan.FromSeconds(5))
.Select(_ => State.DisconnectedRetryTimeout)
.StartWith(State.Disconnected)
: Observable.Return(State.Connected))
.Switch();

Related

c# make my a specific line to timeout after x seconds c#

I have a line in C# which does not work very reliable and does not time out at all and runs for infinity.
to be more precise i am trying to check the connection to a proxy WebClient.DownloadString
I want it to timeout after 5 seconds without making the full method asynchronous
so the code should be like this:
bool success = false
do_this_for_maximum_5_seconds_or_until_we_reach_the_end
{
WebClient.DownloadString("testurl");
success = true;
}
it will try to download testurl and after it did download it it will set success to true. If DownloadString takes more than 5 seconds, the call is canceled, we do not reach the the line where we set success to true, so it remains false and i know that it field.
The thread will remain frozen while we try to DownloadString, so the action is not taking parallel. The ONLY difference to a normal line would be that we set a timeout after 5 seconds
Please do not suggest alternatives such as using HttpClient, because i need a similar codes also for other places, so i simply want a code which will run in a synchronous application (i have not learned anything about asynchronus programing therefore i would like to avoid it completely)
my approach was like suggested by Andrew Arnott in this thread
Asynchronously wait for Task<T> to complete with timeout
however my issue is, I am not exactly sure what type of variable "SomeOperationAsync()" is in his example (i mean it seems like a task, but how can i put actions into the task?), and the bigger issue is that VS wants to switch the complete Method to asynchronos, but i want to run everything synchronous but just with a timeout for a specific line of code.
In case the question has been answered somewhere kindly provide a link
Thank you for any help!!
You should use Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive and add using System.Reactive.Linq; - then you can do this:
var downloadString =
Observable
.Using(() => new WebClient(), wc => Observable.Start(() => wc.DownloadString("testurl")))
.Select(x => new { success = true, result = x });
var timeout =
Observable
.Timer(TimeSpan.FromSeconds(5.0))
.Select(x => new { success = false, result = (string)null });
var operation = Observable.Amb(downloadString, timeout);
var output = await operation;
if (output.success)
{
Console.WriteLine(output.result);
}
The first observable downloads your string. The second sets up a timeout. The third, uses the Amb operator to get the result from which ever of the two input observables completes first.
Then we can await the third observable to get its value. And then it's a simple task to check what result you got.

In discord.net, how do I make all messages automatically deleted every minute?

So I have a verify channel on my discord server which verifies you if you type in //verify , but I want to make it so all messages are automatically deleted every minute on that channel. How do I do that?
One minute delay
Well if you really need that 1 Minute delay you can have a List of Message Id's (ulongs). On your MessageReceived method you can do something like if(message.Channel.Id == YOURCHANNELID) YOURMESSAGELIST.Add(message.Id); On your Client Ready event you would start an async Timer (from System.Threading) with a delay on 1 Minute. So always on your Timer Tick:
var guild = MainClass.DiscordClient.GetGuild(YOURCHANNELID);
var channel = guild.GetTextChannel(YOURCHANNELID);
await channel.DeleteMessagesAsync(YOURMESSAGELIST);
YOURMESSAGELIST.Clear();
Delete message when receiving
What would be way easier and probaply cleaner is deleting every message from that channel as soon as you receive it which mean that you have an if in your MessageReceived method similar to this: if(message.Channel.Id == YOURCHANNELID) await message.DeleteAsync();
I hope that helps you.

Express Cannot set headers after they are sent

I have a situation where I am running an interactive C# console-program from node/express. The program runs in an infinite loop, accepts a string from the command-line, and echoes it back.
The following code works for the first time I call http://localhost:3000?command=hello
Next time around, Node crashes by reporting Can't set headers after they are sent.
If the move the const script = spawn('/Users/amarshanand/shadowClient/myscript.sh'); in the sendToShell(), it works, but since I have to start a new shell and the script, it takes a lot longer.
How can I make it work like start once and accept command for each request.
const express = require('express')
const app = express()
const { spawn } = require('child_process');
const script = spawn('/Users/amarshanand/shadowClient/myscript.sh');
const sendToShell = (command, done) => {
script.stdout.on('data', (stdout) => {
console.log(`stdout: ${stdout}`);
done(stdout);
});
script.stderr.on('data', (stderr) => {
console.log(`error: ${stderr}`);
});
script.stdin.write(`${command}\n`);
}
app.get('/', (req, res) => {
sendToShell(req.query.command, result => res.send(`${result}`));
})
app.get('/getstate', (req, res) => {
res.send('state');
})
app.post('/setstate:state', (req, res) => res.send('posted state'));
app.listen(3000, () => console.log('Example app listening on port 3000!'))
That particular error occurs when you try to send more than one response to an incoming request. When I examine your code, I see that this particular piece of code:
script.stdout.on('data', (stdout) => {
console.log(`stdout: ${stdout}`);
done(stdout);
});
Can receive the data event more than once and when it does, it will call done(stdout) more than once which will cause the caller to call res.send() more than once.
With streams, you have no idea how many times the data event will be called. It could be called only once or it could be called many times with lots of small pieces of data.
In addition, you only have one script that all your requests use. So, each time you call sendToShell(), you add yet another script.stdout.on('data', ...) event handler so they will pile up and you will have duplicates causing you to call done() more than once for each data event. If you're going to stick with this structure, then you need a way to know when all the data has been sent for the last command and then you need to remove that event handler so they don't pile up.
FYI, this code also has concurrency issues because multiple requests could come into your server that cause you to run a command and you'd have no idea which response belongs with which command. If you're going to keep just one shell open, they you probably need to queue commands to the shell so you don't send the next command or set up its event handlers to read the response until the previous command is done. That way you won't be reading the response from the wrong command.

Web socket stops responding after awhile

I have a windows service that uses a websocket (from http://sta.github.io/websocket-sharp/) to conenct to Slack and monitor messages.
My connection code looks something like this:
ws = new WebSocket(connection.Url);
ws.OnMessage += async (sender, e) =>
{
var msg = JsonConvert.DeserializeObject<MessageFromSlack>(e.Data);
if (msg.Type == "message" && msg.Text != null && msg.User != UserId)
{
if (userMatcher.IsMatch(msg.Text))
{
await ProcessDirectMessage(msg);
}
await ProcessMessage(msg);
}
if (msg.Type == "channel_joined")
{
await ChannelJoined(msg.ChannelModel.Id);
}
};
ws.OnClose += (sender, e) =>
{
var reason = e.Reason;
var code = e.Code;
System.Diagnostics.Debug.WriteLine($"{code}:{reason}");
};
ws.Connect();
Basically it waits for a message and then if it's directed # my bot, it'll call ProcessDirectMessage and if not it'll call ProcessMessage. The details of those functions are, I think, unimportant (they do some matching looking for key phrases and respond by sending a message back).
This all works fine. For a while. But after some period of time (usually more than a day), it just stops responding altogether. My OnMessage handler never gets hit. I thought that maybe what is happening is the websocket is getting closed on the server side, so I added the OnClose handler, but that never seems to get hit either.
Does anybody have an idea what might be happening here? Is there a way to keep the connection alive, or else reconnect it when it dies?
By the nature of TCP connection - the only reliable way to detect its gone is to write something to it. If you are just reading (waiting for data to arrive) - you can do that for a very long time while the other side is long time dead. That happens if that other side did not close connection gracefully (which involves an exchange of some TCP packets).
Web socket protocol defines special Ping frame, and corresponding Pong frame, which you should use to avoid situation described in the question. From time to time you should send Ping frame and wait (for a certain timeout) for server to respond with Pong frame. If it did not respond in given timeout - assume connection is dead and reconnect.
As far as I know - library you use does not automatically send ping requests on your behalf. However, it allows you to do that via Ping method.
You need to configure timeout with
ws.WaitTime = TimeSpan.FromSeconds(5);
And then, from time to time (for example - when you did not receive any new messages in last X seconds), do:
bool isAlive = ws.Ping();
There is also boolean property which does the same:
bool isAlive = ws.IsAlive;
This is a blocking call (both of the above). It will return true if server replied with Pong during ws.WaitTime interval, and false otherwise. Then you can act accordingly.

Fail Fast with WebClient

Using a modified WebClient, I download data periodically from a service with the following characteristics:
The data download (~1GB) can take around 20 minutes
Sometimes the service decides not to return any data at all (request hangs), or takes minutes to hours to return the first byte.
I would like to fail fast in the event that the service does not return any data within a reasonable (configurable) amount of time, yet allow plenty of time for a download that is making progress to succeed.
It seems that the WebRequest.Timeout property controls the total time for the request to complete, while ReadWriteTimeout controls the total time available to read data once the data transfer begins.
Am I missing a property that would control the maximum amount of time to wait between establishing the connection and the first byte returning? If there is no such property, how can I approach the problem?
I am not aware of any additional timeout property that will achieve the result you are looking for. The first thought that comes to mind is attaching a handler to DownloadProgressChanged that will update a flag to indicate data has been received (not always accurate though).
Using a Timer or EventWaitHandle you could then block (or handle async if you prefer) for a short period of time and evaluate whether any data has been received. The code below is not a fully fleshed out example, but an idea of how it may be implemented.
using (var manualResetEvent = new ManualResetEvent(false))
using (var client = new WebClient())
{
client.DownloadProgressChanged += (sender, e) => manualResetEvent.Set();
client.DownloadDataAsync(new Uri("https://github.com/downloads/cbaxter/Harvester/Harvester.msi"));
if (!manualResetEvent.WaitOne(5000))
client.CancelAsync();
}
In the above example, the manualResetEvent.WaitOne will return true if DownloadProgressChanged was invoked. You will likely want to check e.BytesReceived > 0 and only set for non-zero values, but I think you get the idea?

Categories