WCF - How to delay task for specified amount of time? - wcf

I have WCF WebService for Silverlight client.
Let's say client click "Make building".
Service will receive new task, and star counting time, until it's ready to make action (i.e add to database).
Time - how much time task will need to complete (i.e to construct building).
The point is how to delay task for the certain amount of time.
Also, is there a way to stream time from server to client ?
I have setup this:
[OperationContract]
public void GetTime()
{
foreach (IDuplexClient client in _clientDic.Values)
{
client.ShowStatus(DateTime.Now);
}
}
[OperationContract]
public void Login()
{
string clientID = OperationContext.Current.Channel.SessionId;
IDuplexClient client = OperationContext.Current.GetCallbackChannel<IDuplexClient>();
_clientDic.Add(clientID, client);
}
IDuplexClient:
[OperationContract(IsOneWay = true)]
void ShowStatus(DateTime status);
And client side:
_client.LoginAsync();
_client.GetTimeAsync();
_client.ShowStatusReceived += new EventHandler<ShowStatusReceivedEventArgs>(_client_ShowStatusReceived);
void _client_ShowStatusReceived(object sender, ShowStatusReceivedEventArgs e)
{
label1.Content = e.status.ToString();
}
It's working.. For first run. But time doesn't get refreshed, which is not what I want.
As well, after few forced refresh in browser, time stop to show at all.
public partial class MainPage : UserControl
{
Service1Client _client;
int time = 10000;
public MainPage()
{
InitializeComponent();
_client = new Service1Client(new PollingDuplexHttpBinding { DuplexMode = PollingDuplexMode.SingleMessagePerPoll, OpenTimeout = TimeSpan.FromMinutes(10), ReceiveTimeout = TimeSpan.FromMinutes(10) },
new EndpointAddress("http://localhost:44544/Service1.svc"));
_client.LoginAsync();
_client.DoWorkCompleted += new EventHandler<DoWorkCompletedEventArgs>(_client_DoWorkCompleted);
_client.DoWorkAsync();
_client.AddNewTaskAsync("testTaskzor", time);
_client.GetTimeAsync();
//_client.AddNewTaskCompleted += new EventHandler<AddNewTaskCompletedEventArgs>(_client_AddNewTaskCompleted);
_client.ShowStatusReceived += new EventHandler<ShowStatusReceivedEventArgs>(_client_ShowStatusReceived);
}
void _client_ShowStatusReceived(object sender, ShowStatusReceivedEventArgs e)
{
label1.Content = e.status.ToString();
}
void _client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)
{
//label1.Content = e.Result;
}
}
That' entire client code.
Although I finally fixed and time is streaming properly to client (it wa surpsingly easy it was enough to enlose foreach with while(true) statment, at least for now).
But on other side. When I close browser, and open it again, nothing show up. As well as after I refresh it, time do not show up at all.

The easiest way would be to implement the delay on the client side. You can't really delay a RESTful service like WCF without breaking the model.

Related

Cannot Interrupt HttpClient.GetStreamAsync by means of CancellationToken in ASP.NET Core

I'm trying to send an http request to an AXIS Camera in order to receive a stream.
Everything works fine except that I can't get to use CancellationToken to cancel the request when it is no more needed. I've the following architecture:
Blazor client:
// LiveCamera.razor
<img src="CameraSystem/getStream" onerror="[...]" alt="">
ASP.NET Core Server:
// CameraSystemController.cs
[ApiController]
[Route("[controller]")]
public class CameraSystemController : Controller
{
[HttpGet("getStream")]
public async Task<IActionResult> GetStream()
{
Stream stream = await Device_CameraStandard.GetStream();
if (stream != null) {
Response.Headers.Add("Cache-Control", "no-cache");
FileStreamResult result = new FileStreamResult(stream, _contentTypeStreaming) {
EnableRangeProcessing = true
};
return result;
} else {
return new StatusCodeResult((int)HttpStatusCode.ServiceUnavailable);
}
}
}
Class accessing the camera:
// Device_CameraStandard.cs
internal class Device_CameraStandard
{
private HttpClient _httpClient;
private static CancellationTokenSource _tokenSource;
private System.Timers.Timer _keepAliveTimer;
internal Device_CameraStandard() {
_keepAliveTimer = new System.Timers.Timer();
_keepAliveTimer.Interval = 3000;
_keepAliveTimer.Elapsed += KeepAliveTimeout;
_tokenSource = new CancellationTokenSource();
[...]
}
internal async Task<Stream> GetStream()
{
return await _httpClient.GetStreamAsync("http://[...]/axis-cgi/mjpg/video.cgi?&camera=1", _tokenSource.Token);
}
// Invoked periodically by client from LiveCamera.razor.cs, not included here
internal void KeepAlive()
{
LLogger.Debug("KeepAlive!");
_keepAliveTimer.Stop();
_keepAliveTimer.Start();
}
private void KeepAliveTimeout(object sender, ElapsedEventArgs e)
{
LLogger.Debug("Timeout!");
_keepAliveTimer.Stop();
_tokenSource.Cancel();
_tokenSource.Dispose();
_tokenSource = new CancellationTokenSource();
}
}
However, even if all clients leave LiveCamera.razor page and the _keepAliveTimer elapses and the CancellationTokenSource is canceled, the request is not canceled. I can see it from the fact that bandwidth usage does not decreases (the "receiving" bandwitdh, indicating that Server is still receiving data from camera), it only decreases if I close the browser tab.
Could you please help me to understand what am I doing wrong? Thanks
EDIT: In the end, even after following the suggestion of observing the token in all code parts where the returned stream was used, included the controller, I ended up discovering that the tag
// LiveCamera.razor
<img src="CameraSystem/getStream" onerror="[...]" alt="">
was causing the client to never stop sending requests. Thus I had to use a workaround to force client to stop sending requests before leaving LiveCamera.razor page.

UCMA 3.0 - Recording an incoming call

I am new to UCMA and I am learning as I go through examples. I am trying to build 2 Lync clients A and B with the scenario as follows,
A calls B
B answers
A plays audio
B records it using Recorder.
I am stuck at trying to record the call at B. For B its an incoming call. I need to attach the audiovideoflow to the recorder, but I am not sure on how to do it. I will appreciate any help.
Apologies on the unformatted code, I am not sure how to format it properly, I tried.
Thanks.
Kris
Client B Code:
Accepts an incoming call
Records the media received in the incoming call. ***This is the part I have trouble
using System;
using System.Threading;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Collaboration.AudioVideo;
using Microsoft.Rtc.Signaling;
using Microsoft.Rtc.Collaboration.Lync;
namespace Microsoft.Rtc.Collaboration.LyncUAS
{
public class LyncUAS
{
#region Locals
private LyncUASConfigurationHelper _helper;
private UserEndpoint _userEndpoint;
private AudioVideoCall _audioVideoCall;
private AudioVideoFlow _audioVideoFlow;
private Conversation _incomingConversation;
//Wait handles are only present to keep things synchronous and easy to read.
private AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
private EventHandler<AudioVideoFlowConfigurationRequestedEventArgs> _audioVideoFlowConfigurationRequestedEventHandler;
private EventHandler<MediaFlowStateChangedEventArgs> _audioVideoFlowStateChangedEventHandler;
private AutoResetEvent _waitForAudioVideoCallEstablishCompleted = new AutoResetEvent(false);
private AutoResetEvent _waitForAudioVideoFlowStateChangedToActiveCompleted = new AutoResetEvent(false);
private AutoResetEvent _waitForPrepareSourceCompleted = new AutoResetEvent(false);
#endregion
#region Methods
/// <summary>
/// Instantiate and run the DeclineIncomingCall quickstart.
/// </summary>
/// <param name="args">unused</param>
public static void Main(string[] args)
{
LyncUAS lyncUAS = new LyncUAS();
lyncUAS.Run();
}
private void Run()
{
string filename = "received.wma";
_helper = new LyncUASConfigurationHelper();
// Create a user endpoint, using the network credential object
// defined above.
_userEndpoint = _helper.CreateEstablishedUserEndpoint("Lync UAS" /*endpointFriendlyName*/);
_userEndpoint.RegisterForIncomingCall<AudioVideoCall>(On_AudioVideoCall_Received);
Console.WriteLine("Waiting for incoming call...");
_autoResetEvent.WaitOne();
Console.WriteLine("came after call is connected");
//start recording for audio.
Recorder recorder = new Recorder();
recorder.StateChanged += new EventHandler<RecorderStateChangedEventArgs>(recorder_StateChanged);
recorder.VoiceActivityChanged += new EventHandler<VoiceActivityChangedEventArgs>(recorder_VoiceActivityChanged);
//**********This is the issue, currently _audioVideoFlow is null, it is not attached to the flow
//So this will fail, how to attach _audioVideoFlow to an incoming call ?? HELP !!!
// recorder.AttachFlow(_audioVideoFlow); ------------> HELP!
WmaFileSink sink = new WmaFileSink(filename);
recorder.SetSink(sink);
recorder.Start();
Console.WriteLine("Started Recording ...");
_autoResetEvent.WaitOne();
recorder.Stop();
Console.WriteLine("Stopped Recording ...");
recorder.DetachFlow();
Console.WriteLine("Exiting");
Thread.Sleep(2000);
}
private void audioVideoFlow_StateChanged(object sender, MediaFlowStateChangedEventArgs e)
{
Console.WriteLine("Flow state changed from " + e.PreviousState + " to " + e.State);
//When flow is active, media operations can begin
if (e.State == MediaFlowState.Active)
{
// Flow-related media operations normally begin here.
_waitForAudioVideoFlowStateChangedToActiveCompleted.Set();
}
// call sample event handler
if (_audioVideoFlowStateChangedEventHandler != null)
{
_audioVideoFlowStateChangedEventHandler(sender, e);
}
}
void recorder_VoiceActivityChanged(object sender, VoiceActivityChangedEventArgs e)
{
Console.WriteLine("Recorder detected " + (e.IsVoice ? "voice" : "silence") + " at " + e.TimeStamp);
}
void recorder_StateChanged(object sender, RecorderStateChangedEventArgs e)
{
Console.WriteLine("Recorder state changed from " + e.PreviousState + " to " + e.State);
}
void On_AudioVideoCall_Received(object sender, CallReceivedEventArgs<AudioVideoCall> e)
{
//Type checking was done by the platform; no risk of this being any
// type other than the type expected.
_audioVideoCall = e.Call;
// Call: StateChanged: Only hooked up for logging, to show the call
// state transitions.
_audioVideoCall.StateChanged += new
EventHandler<CallStateChangedEventArgs>(_audioVideoCall_StateChanged);
_incomingConversation = new Conversation(_userEndpoint);
Console.WriteLine("Call Received! From: " + e.RemoteParticipant.Uri + " Toast is: " +e.ToastMessage.Message);
_audioVideoCall.BeginAccept(
ar =>
{
try {
_audioVideoCall.EndAccept(ar);
Console.WriteLine("Call must be connected at this point. "+_audioVideoCall.State);
_autoResetEvent.Set();
} catch (RealTimeException ex) { Console.WriteLine(ex); }
}, null);
}
//Just to record the state transitions in the console.
void _audioVideoCall_StateChanged(object sender, CallStateChangedEventArgs e)
{
Console.WriteLine("Call has changed state. The previous call state was: " + e.PreviousState +
" and the current state is: " + e.State);
if (e.State == CallState.Terminated)
{
Console.WriteLine("Shutting down");
_autoResetEvent.Set();
_helper.ShutdownPlatform();
}
}
#endregion
}
}
I think I have figured out what's not quite right here.
Your Code
// Create a user endpoint, using the network credential object
// defined above.
_userEndpoint = _helper.CreateEstablishedUserEndpoint("Lync UAS" /*endpointFriendlyName*/);
_userEndpoint.RegisterForIncomingCall<AudioVideoCall>(On_AudioVideoCall_Received);
Console.WriteLine("Waiting for incoming call...");
_autoResetEvent.WaitOne();
Console.WriteLine("came after call is connected");
//start recording for audio.
Recorder recorder = new Recorder();
recorder.StateChanged += new EventHandler<RecorderStateChangedEventArgs>(recorder_StateChanged);
recorder.VoiceActivityChanged += new EventHandler<VoiceActivityChangedEventArgs>(recorder_VoiceActivityChanged);
//**********This is the issue, currently _audioVideoFlow is null, it is not attached to the flow //So this will fail, how to attach _audioVideoFlow to an incoming call ?? HELP !!!
// recorder.AttachFlow(_audioVideoFlow); ------------> HELP!
Looking good so far. I'm assuming you're establishing and such in your CreateEstablishedUserEndpoint method, but I'm not seeing where you're getting the value for _audioVideoFlow.
I'm guessing you might be doing it elsewhere, but on the off chance that's actually where you're running into problems, here's that bit:
Simplest pattern to get AVFlow
public static void RegisterForIncomingCall(LocalEndpoint localEndpoint)
{
localEndpoint.RegisterForIncomingCall
<AudioVideoCall>(IncomingCallDelegate);
}
private static void IncomingCallDelegate(object sender, CallReceivedEventArgs<AudioVideoCall> e)
{
e.Call.AudioVideoFlowConfigurationRequested += IncomingCallOnAudioVideoFlowConfigurationRequested;
}
private static void IncomingCallOnAudioVideoFlowConfigurationRequested(object sender, AudioVideoFlowConfigurationRequestedEventArgs e)
{
AudioVideoFlow audioVideoFlow = e.Flow; // <--- There's your flow, gentleman.
}
Now, instead of registering for your incoming call, just call RegisterForIncomingCall(_userEndpoint);.
Your AVFlow will be hanging off e.Flow above, you could then pass that into your recorder: recorder.AttachFlow(e.Flow) or simply assign the flow to a field in your class and autoResetEvent.WaitOne(); and set that up where you're setting that up now.
Obviously this is a pretty naive implementation. A lot can go wrong in those few lines of code (exception handling/static event handler memory leak comes immediately to mind); don't forget to wire up events related to status changes on the conversation/call and endpoints, as well as any of the recovery related items.

Callback is not invoked on the client

I have a self-hosted service that processes long running jobs submitted by a client over net.tcp binding. While the job is running (within a Task), the service will push status updates to the client via a one-way callback. This works fine, however when I attempt to invoke another callback to notify the client the job has completed (also one-way), the callback is never received/invoked on the client. I do not receive any exceptions in this process.
My Callback contract looks like this:
public interface IWorkflowCallback
{
[OperationContract(IsOneWay = true)]
[ApplySharedTypeResolverAttribute]
void UpdateStatus(WorkflowJobStatusUpdate StatusUpdate);
[OperationContract(IsOneWay = true)]
[ApplySharedTypeResolverAttribute]
void NotifyJobCompleted(WorkflowJobCompletionNotice Notice);
}
Code from the service that invokes the callbacks: (not in the service implementation itself, but called directly from the service implementation)
public WorkflowJobTicket AddToQueue(WorkflowJobRequest Request)
{
if (this.workflowEngine.WorkerPoolFull)
{
throw new QueueFullException();
}
var user = ServiceUserManager.CurrentUser;
var context = OperationContext.Current;
var workerId = this.workflowEngine.RunWorkflowJob(user, Request, new Object[]{new DialogServiceExtension(context)});
var workerjob = this.workflowEngine.FindJob(workerId);
var ticket = new WorkflowJobTicket()
{
JobRequestId = Request.JobRequestId,
JobTicketId = workerId
};
user.RegisterTicket<IWorkflowCallback>(ticket);
workerjob.WorkflowJobCompleted += this.NotifyJobComplete;
workerjob.Status.PropertyChanged += this.NotifyJobStatusUpdate;
this.notifyQueueChanged();
return ticket;
}
protected void NotifyJobStatusUpdate(object sender, PropertyChangedEventArgs e)
{
var user = ServiceUserManager.GetInstance().GetUserWithTicket((sender as WorkflowJobStatus).JobId);
Action<IWorkflowCallback> action = (callback) =>
{
ICommunicationObject communicationCallback = (ICommunicationObject)callback;
if (communicationCallback.State == CommunicationState.Opened)
{
try
{
var updates = (sender as WorkflowJobStatus).GetUpdates();
callback.UpdateStatus(updates);
}
catch (Exception)
{
communicationCallback.Abort();
}
}
};
user.Invoke<IWorkflowCallback>(action);
}
protected void NotifyJobComplete(WorkflowJob job, EventArgs e)
{
var user = ServiceUserManager.GetInstance().GetUserWithTicket(job.JobId);
Action<IWorkflowCallback> action = (callback) =>
{
ICommunicationObject communicationCallback = (ICommunicationObject)callback;
if (communicationCallback.State == CommunicationState.Opened)
{
try
{
var notice = new WorkflowJobCompletionNotice()
{
Ticket = user.GetTicket(job.JobId),
RuntimeOptions = job.RuntimeOptions
};
callback.NotifyJobCompleted(notice);
}
catch (Exception)
{
communicationCallback.Abort();
}
}
};
user.Invoke<IWorkflowCallback>(action);
}
In the user.Invoke<IWorkflowCallback>(action) method, the Action is passed an instance of the callback channel via OperationContext.GetCallbackChannel<IWorkflowCallback>().
I can see that the task that invokes the job completion notice is executed by the the service, yet I do not receive the call on the client end. Further, the update callback is able to be invoked successfully after a completion notice is sent, so it does not appear that the channel is quietly faulting.
Any idea why, out of these two callbacks that are implemented almost identically, only one works?
Thanks in advance for any insight.

WCF REST Service: InstanceContextMode.PerCall not working

I have implemented a REST service for WCF. The service offers one function that can be called by many clients and this function takes more than 1 minute to complete. So what I wanted is that for each client, a new object is used, so that many clients can be handled at a time.
My interface looks like this:
[ServiceContract]
public interface ISimulatorControlServices
{
[WebGet]
[OperationContract]
string DoSomething(string xml);
}
And the (test) implementation of it:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall]
public class SimulatorControlService : SimulatorServiceInterfaces.ISimulatorControlServices
{
public SimulatorControlService()
{
Console.WriteLine("SimulatorControlService started.");
}
public string DoSomething(string xml)
{
System.Threading.Thread.Sleep(2000);
return "blub";
}
}
The problem now is: if I use a client that creates 10 (or whatever number) threads, each of it calling the service, they dont run concurrently. This means, the calls are being handled one after each other. Does anybody have an idea why this happens?
Added: client-side code
Spawning threads:
for (int i = 0; i < 5; i++)
{
Thread thread = new Thread(new ThreadStart(DoSomethingTest));
thread.Start();
}
Method:
private static void DoSomethingTest()
{
try
{
using (ChannelFactory<ISimulatorControlServices> cf = new ChannelFactory<ISimulatorControlServices>(new WebHttpBinding(), "http://localhost:9002/bla/SimulatorControlService"))
{
cf.Endpoint.Behaviors.Add(new WebHttpBehavior());
ISimulatorControlServices channel = cf.CreateChannel();
string s;
int threadID = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("Thread {0} calling DoSomething()...", threadID);
string testXml = "test";
s = channel.StartPressureMapping(testXml);
Console.WriteLine("Thread {0} finished with reponse: {1}", threadID, s);
}
}
catch (CommunicationException cex)
{
Console.WriteLine("A communication exception occurred: {0}", cex.Message);
}
}
Thanks in advance!
Since the service is controlled by a GUI, the "UseSynchronizationContext" attribute was needed to solve the problem:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode=ConcurrencyMode.Multiple, UseSynchronizationContext=false)]

Why WCF Discovery Udp channel gets aborted

I want the server to constantly track for available clients using WCF Discovery.
public void Start()
{
findCriteria = new FindCriteria(typeof(ITestRunnerAgent))
{
Scopes = {new Uri(scope)},
Duration = TimeSpan.FromMilliseconds(DiscoveryIntervalInMiliseconds)
};
discoveryClient = GetInitilizedDisoveryClient();
discoveryClient.FindAsync(findCriteria);
}
private DiscoveryClient GetInitilizedDisoveryClient()
{
var client = new DiscoveryClient(new UdpDiscoveryEndpoint());
client.FindProgressChanged += OnFindProgressChanged;
client.FindCompleted += OnFindCompleted;
return client;
}
private void OnFindCompleted(object sender, FindCompletedEventArgs e)
{
if (!e.Cancelled)
{
// HERE! Sometimes e.Error is not null, but as described in question
discoveryClient.FindAsync(findCriteria);
}
}
Unfortunately, sometimes at the point specified by comment i get an aborted Udp channel:
The communication object,
System.ServiceModel.Channels.UdpChannelFactory+ClientUdpDuplexChannel,
cannot be used for communication
because it has been Aborted.
Has anyone ideas why?
It could be that some network infrastructure at your office is droping the connections.
You should write your code to check for aborted communication, and recover from it.
To recover you could close down the aborted channel and create a new one.
Well, this doesn't answer your question, but I feel a little wary about your code. It seems fundamentally correct, but it feels like your discovery could be running very fast. I would implement recurring discovery in a separate thread with some sleep time just to make the network happier. Just a thought to clean up the code. Sorry if this doesn't help.
public void Start()
{
var bw = new System.ComponentModel.BackgroundWorker();
bw.DoWork += new System.ComponentModel.DoWorkEventHandler(DiscoveryThread);
bw.RunWorkerAsync();
}
private void DiscoveryThread(object sender, System.ComponentModel.DoWorkEventArgs e)
{
var client = new DiscoveryClient(new UdpDiscoveryEndpoint());
var findCriteria = new FindCriteria(typeof(ITestRunnerAgent))
{
Scopes = {new Uri(scope)},
Duration = TimeSpan.FromMilliseconds(DiscoveryIntervalInMiliseconds)
};
while(true)
{
client.Find(findCriteria);
// lock, clear, and add discovered endpoints to a global List of some sort
System.Threading.Thread.Sleep(3000);
}
}
As it is asynchronous operation the thread terminates after executing FindAsync(criteria) method. just wrote Console.Readline() after method call or use Autoreset event hold the thread.