I have a C# form application to send CAN messages.
At the start, I start a thread which monitors incoming messages, and displays them on a DataGridView.
Currently the app is set so that the user can send a CAN message one at time, and view its response.
All this works fine, and reliably.
Phase 2 of my application requires me to send a full list of CAN messages automatically.
For this I need to send a CAN message, and wait for its response, and then send the next.
How can I monitor the receive thread, and halt my send function long enough for the response to be received?
As the send message is not in a separate thread, Thread.Sleep(n) halts the entire program.
My approach:
public class DataViewModel
{
public AsynchronousCommand Send;
public DataViewModel()
{
Send = new AsynchronousCommand(() =>
{
SendData();
});
}
private void SendData()
{
}
}
public class SomewhereInForm
{
private DataViewModel dataViewModel = new DataViewModel();
public SomewhereInForm()
{
dataViewModel.Send.Executed += SendOnExecuted;
}
private void SendOnExecuted(object sender, CommandEventArgs args)
{
}
private void DoSome()
{
dataViewModel.Send.DoExecute(new int());
}
}
It's example with 1 command. You can put commands in List, whatever.
If you don't want build your own library witch commands, use this: Commands in MVVM
Related
As stated in the title i'm interested in the management (reading is sufficient by now) of the the incoming and outgoing data from the WebView2 istance user are using.
I'have try do add listener to WebMessageResourceRequested and WebMessageReceived but i have the sensation i missinterpreted their behaviour becasue they don't work as i expected (so is very likely that i am using that handler in a wrong way)
public partial class SimpleWebView2 : Form
{
public SimpleWebView2 ()
{
InitializeComponent();
this.Initialization();
}
private async void Initialization ()
{
await webView21.EnsureCoreWebView2Async();
webView21.CoreWebView2.WebResourceRequested += new EventHandler<CoreWebView2WebResourceRequestedEventArgs>(this.webView21_WebMessageResourceRequested);
}
private void webView21_WebMessageResourceRequested(object sender, CoreWebView2WebResourceRequestedEventArgs e)
{
//does not fire
}
}
A common solution is Fiddler. Just start it and you'll see the inbound and outbound traffic of any application, including the one that is using WebView2.
I have a remote computer with Redis. From time to time new entries are added to it (key-value pair). I want Redis to send notifications to my C# Service about events like this (i'm interested in value part). I've searched online and found simple code example to subscribe my Service to Redis. How to make Redis send notifications?
Service:
public partial class ResultsService : ServiceBase
{
private ConnectionMultiplexer connection = ConnectionMultiplexer.Connect(ConfigurationManager.AppSettings["RedisConnection"]);
private const string ChatChannel = "__keyspace#0__:*";
public VerificationResultsService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
Start();
}
public void Start()
{
var pubsub = connection.GetSubscriber();
pubsub.Subscribe(ChatChannel, (channel, message) => MessageAction(message));
while (true)
{
}
}
private static void MessageAction(RedisValue message)
{
// some handler...
}
}
Making redis send automatic keyspace notifications is a redis server configuration piece, which can be enabled via the .conf file (notify-keyspace-events), or via CONFIG SET at runtime; the documentation for this is here.
You can see how this works with example code:
using StackExchange.Redis;
using System;
using System.Linq;
static class P
{
private const string ChatChannel = "__keyspace#0__:*";
static void Main()
{
// connect (allowAdmin just lets me use ConfigSet)
using var muxer = ConnectionMultiplexer.Connect("127.0.0.1,allowAdmin=true");
// turn on all notifications; note that this is server-wide
// and is NOT just specific to our connection/code
muxer.GetServer(muxer.GetEndPoints().Single())
.ConfigSet("notify-keyspace-events", "KEA"); // KEA=everything
// subscribe to the event
muxer.GetSubscriber().Subscribe(ChatChannel,
(channel, message) => Console.WriteLine($"received {message} on {channel}"));
// stop the client from exiting
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
}
which works like:
However, in many scenarios you may find that this is too "noisy", and you may prefer to use either a custom named event that you publish manually when you do things that need notification, or (again manually) you could make use of the streams features to consume a flow of data (streams can be treated as a flow of events in the "things that happened" sense, but they are not delivered via pub/sub).
Im using MVVM Light Messenger in my WPF application, and something is not working as expected.
my view-model registered with a token. im using 'long' objects as tokens.
my code registered for example with the token 5, then sends request to a service.
when the service replies it handles in my second view-model, which will then send the message with the same token.
When i debug and print the registration and sending of the messages it seems OK, but for some reason not all the messenger are received by the registered.
My registration and handling looks as follows:
private void registerTest()
{
long tokenId = getNextToken();
ExtraData data = new ExtraData();
Messenger.Default.Register<MyMsg>(this, tokenId, (m) => recieve(m,data));
}
private void receive(MyMsg m,ExtraData data)
{
Messenger.Default.Unregister<MyMsg>(this, m.tokenId);
}
My sending looks as follows:
private void sendTest(long tokenId)
{
Messenger.Default.Send(new MyMsg(tokenId), tokenId);
}
I always register with token X before its received in my sendTest,
but for some reason, sometimes when sendTest(X) is called, its not received.
Anyone has any idea whats going on?
You should have your ExtraData as a class property on your message to be able to interact with it from different sources.
public class MyMsg {
public int TokenId {get;set;}
public ExtraData Data {get;set;}
}
public void registerTest()
{
Messenger.Default.Register<MyMsg>(this, recieve);
}
public void recieve(MyMsg myMsg)
{
Messenger.Default.Unregister<MyMsg>(this, myMsg.TokenId);
//Note that you can also access m.Data here if you need to
}
public void sendTest()
{
var myMsg = new MyMsg {TokenId = GetNextToken(), Data = new ExtraData()};
Messenger.Default.Send(myMsg);
}
I'm creating a wpf application which performs many tasks in the backgound, but still requires the UI to be responsive and to display the status of the various background tasks. It also has the option to not display the UI at all, in which case the status messages should be discarded without creating an instance of the main form at all.
What I've attempted is to remove
StartupUri="MainWindow.xaml"
from App.xaml. Then, in App.xaml.cs, I have
`
public App()
{
Startup += new StartupEventHandler(App_Startup);
}
void App_Startup(object sender, StartupEventArgs e)
{
// Code which loads application settings is here
if (pf.ShowUI)
{
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
// The background processes will begin here.
}`
This shows the main form, if necessary, and starts all the background processes. This part works.
In order to send messages from the background to the UI, I've implemented a very basic messenger:
`
internal interface IMessageHandler
{
void ReceiveMessage(string message);
}
internal class Messenger
{
private static List<IMessageHandler> _handlers;
internal static void AddHandler(IMessageHandler handler)
{
_handlers.Add(handler);
}
internal static void RemoveHandler(IMessageHandler handler)
{
try
{
_handlers.Remove(handler);
}
catch (Exception ex) {}
}
internal static void Broadcast (string message)
{
foreach (IMessageHandler handler in _handlers)
{
handler.ReceiveMessage(message);
}
}
}`
The main form implements the IMessageHandler interface, and adds itself to the Messenger as a handler when it starts up. Any process that needs to send a status to the main form just needs to call the Broadcast method of the messenger.
The problem I'm having, is that the messages are not being shown on the form until the background processes complete, and the UI is locked up until then as well.
The code in the UI which handles receiving messages is as follows:
`
public void ReceiveMessage(string message)
{
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action<string>(AddText),
message);
}
private void AddText(string text)
{
Label myLabel = new Label();
myLabel.Content = text;
stackPanel1.Children.Add(myLabel);
if (stackPanel1.Children.Count > 5)
{
stackPanel1.Children.RemoveAt(0);
}
}`
Why are my background processes freezing my UI? What can I do to prevent it? And why is my UI not updating with the status messages?
Maybe this is your problem:
Dispatcher.Invoke(DispatcherPriority.Normal,
new Action<string>(AddText),
message);
Try change this to,
Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new Action<string>(AddText),
message);
Because when you use Invoke, the method gets executed and the application waits for it to complete, but with BeginInvoke the method is invoked Asychnronously and the application continues to execute while the method referenced in BeginInvoke is executed.
Read this: whether to use Invoke or BeginInvoke
Use the below code to avoid freezing the UI. In my application I have used a BackgroundWorker class. By changing anything on the form using code, a run time error is thrown.
I am using the below code to avoid this and it works perfectly for me.
Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(delegate()
{
rtf_status.AppendText("Validating XML against schema...Please wait\n");
}));
Note the part after the between braces ('{}') this is where you should place your code if you wish to change something on the form.
I have been task with (ha) creating an application that will allow the users to schedule a command line app we have with a parameter.
So the command line app takes an xml and "runs it"
So bottom line I either need to create a windows service or learn how to interact with the Task Scheduler service already running on the box (version 1 Xp /2003)
At first I though it would be easy have a service run and when a job is submitted, calculate the time between now and run and set up a timer to wait that amount of time. This is better then checking every minute if it's time to run.
Were I hit a wall is I relized I do not know how to communicate with a running windows service. Except maybe create a file with details and have the service with a file watcher to load the file and modify the schedule.
So the underlying questions are how can I execute this psedo code
from client
serviceThatIsRunning.Add(Job)
Or ineracting with the task schedule or creating .job files using c# 3.5
Edit:
To clarify I created a small sample to get my thoughts on "paper"
So I have a Job Class
public class Job
{
#region Properties
public string JobName { get; set; }
public string JobXML { get; set; }
private Timer _JobTimer;
public Timer JobTimer
{
get
{
return _JobTimer;
}
}
#endregion
public void SetJobTimer(TimeSpan time)
{
if (_JobTimer != null)
{
_JobTimer.Dispose();
}
_JobTimer = new Timer(new TimerCallback(RunJob), null, time, time);
}
private void RunJob(Object state)
{
System.Diagnostics.Debug.WriteLine(String.Format("The {0} Job would have ran with file {1}", JobName, JobXML));
}
public override string ToString()
{
return JobName;
}
public void StopTimer()
{
_JobTimer.Dispose();
}
}
Now I need to create an App to house these Jobs that is constantly running, that is why I though of Windows Services, and then a Windows app to allow the user to work with the Job List.
So the question is if I create a Windows Service how do I interact with methods in that service so I can change the JobList, add, delete, change.
Here is a small windows app I created to show that the Job class does run. Interesting point, If I am doing this correctly, I do not add the Job to a listbox and the Add method exits the Job Timer portion still runs and does not get picked up by the Garbage Collector.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnAddJob_Click(object sender, EventArgs e)
{
Job job = new Job();
job.JobName = txtJobName.Text;
job.JobXML = txtJobXML.Text;
job.SetJobTimer(new TimeSpan(0, 0, Convert.ToInt32(JobTime.Value)));
// ??Even If I don't add the Job to a list or ListBox it seems
// ??to stay alive and not picked up by the GC
listBox1.Items.Add(job);
}
private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listBox1.SelectedIndex > -1)
{
Job job = listBox1.Items[listBox1.SelectedIndex] as Job;
txtJobName.Text = job.JobName;
txtJobXML.Text = job.JobXML;
}
}
private void btnRemove_Click(object sender, EventArgs e)
{
Job job = listBox1.Items[listBox1.SelectedIndex] as Job;
job.StopTimer();
listBox1.Items.Remove(job);
}
private void btnCollect_Click(object sender, EventArgs e)
{
GC.Collect();
}
}
If you want to schedule a task using the task scheduler it could be as simple as below. You just need to customize the command line arguments that you pass to schtasks for your needs. See this link for a detailed explanation of command line arguments.
Process p = Process.Start("schtasks", commandArgs);
p.WaitForExit();
If you want to start multiple tasks that run at different time intervals, you can
create for instance a class JobThread that defines a timer that is initialized using the Initialize method:
m_timer = new Timer(new TimerCallback(this.timerHandler), null, this.Interval, this.Interval);
Furthermore, this class defines a List of Job objects. These jobs are executed from the timerHandler.
Finally, you create a singleton JobManager class that defines a Start and Stop method.
In the Start method you do something like this:
foreach (var jobThread in this.m_jobThreads)
{
jobThread.Initialize();
}
This JobManager has also a Initiliaze method that accepts a XmlNode parameter. This method will parse the Xml-job you pass from the command-line.
There was an answer on this thread that is no longer there but, I am going to try to create a listener by keeping a port open
WCF through Windows Services
http://msdn.microsoft.com/en-us/library/ms733069.aspx
Also adding the attribute
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
Helps to keep state of the service.