I am building a device controller in C# that controls the flow rate of pumps. There is Form1 and a Pump-Class, holding all properties and methods of the pump that are accessible through the Form. So far, everything works fine. However, there might be the case where there is more than one pump. Of course, I could run individual instances of the executable, but you know..
How can I create multiple instances of the pump-class according to my current demand. Maybe an extra Form upfront with an Add-Button, where it is possible to create as much pumps as needed?
Sure, this is comes down to a very basic question, but I am no professional programmer.. Any comments are highly appreciated.
Thanks in advance.
mulm
namespace PumpController
{
public partial class Form1 : Form
{
static Pump pump;
public Form1()
{
InitializeComponent();
cb_availablePorts.DropDownStyle = ComboBoxStyle.DropDownList;
cb_baudRates.DropDownStyle = ComboBoxStyle.DropDownList;
string[] availablePorts = SerialPort.GetPortNames();
foreach (string item in availablePorts)
{
cb_availablePorts.Items.Add(item);
}
string[] commonBaudRates = new string[] { "4800", "9600", "19200", "38400", "57600", "115200" };
foreach (string item in commonBaudRates)
{
cb_baudRates.Items.Add(item);
}
}
private void bt_valueUp_Click(object sender, EventArgs e)
{
if (!(pump == null))
{
pump.increaseValue();
}
}
private void bt_valueDown_Click(object sender, EventArgs e)
{
if (!(pump == null))
{
pump.decreaseValue();
}
}
private void Form_Closing(object sender, EventArgs e)
{
Hide();
if (!(pump == null))
{
pump.Close();
}
}
private void bt_connect_Click(object sender, EventArgs e)
{
if (!(cb_availablePorts.SelectedItem == null) && !(cb_baudRates.SelectedItem == null))
{
Pump pump = new Pump(Convert.ToString(cb_availablePorts.SelectedItem), Convert.ToInt32(cb_baudRates.SelectedItem));
}
}
}
}
The comments lead to part of the answer and are right as far as they go. However, one important thing has perhaps been overlooked -- the part about finding an(other) COM port.
Each time you instantiate a new Pump object, and add it to the List<Pump> collection, these objects will contend for the same COM port unless you allocate another port.
There is a well-accepted answer for this at enumerating COM ports. Hate to provide a link as the heart of my answer, but if you have a four-object list of Pump objects contending for a single serial port, you could have troubles.
Related
I'm currently working on a method that gives the user the possibility to add a handscanner to a dicitionary in order to scan some barcodes with it. (before i started the scanners were hardcoded in the dictionary). my colleague from which i got this project, implemented the rawinput_dll in order to get all of the necessary data from the barcode scanner. The method to get the data is shown below:
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (!Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
return;
}
else if (Scanners.ContainsKey(e.KeyPressEvent.DeviceName))
{
if (e.KeyPressEvent.KeyPressState == "MAKE")
{
return;
}
if (e.KeyPressEvent.VKeyName != "\n")
{
scanNumber += e.KeyPressEvent.VKeyName;
return;
}
devID = e.KeyPressEvent.DeviceName;
Debug.Print(devID);
Aufrufen(scanNumber);
scanNumber = "";
}
}
Basically there are three classes in this program (FrmMenu, FrmSettings and a Class for the Scanner itself). If you want to add settings for the program you click on a button that opens up a new instance of FrmSettings
private void BtnSettings_Click(object sender, EventArgs e)
{
FrmSettings settings = new FrmSettings();
settings.ShowDialog();
settings.BtnSave_Click(sender, e);
settings.Dispose();
}
In this form there 2 buttons where you can choose if you want to add a scanner that scans even numbers or one that scans odd ones. If you press one of the buttons you need to scan a barcode in order to get the information (VID of Scanner) which is used as key to add the new scanner to the dictionary.
private void OnKeyPressed(object sender, RawInputEventArg e)
{
if (newScanner == true)
{
devIDnew = e.KeyPressEvent.DeviceName;
scannerAnlegen(devIDnew);
}
}
scannerAnlegen is the methode that adds the scanner to the dict.
public void scannerAnlegen(string devIDnew)
{
if(EvenOrOdd == true)
{
Scanner ger = new Scanner("dev3", "even");
FrmMenu.Scanners.Add(devIDnew, ger);
newScanner = false;
}
else
{
Scanner ug = new Scanner("dev4", "odd");
FrmMenu.Scanners.Add(devIDnew, ug);
newScanner = false;
}
}
my problem rn is, that it seems like i cant get out of this OneKeyPressed method of the Settings class. the logic of the OneKeyPressed method of the FrmMenu Class is that it can only proceed if the scanner is in the dictionary. Adding the scanner seems to work because when i debug and try to add one scanner the second time it throws and exception and says something like "element with this key already added". But why does this code doesn't continue then?
I am new to C# and am looking for some advice on an issue I have been trying to solve in my Windows Form application.
I have an application that needs to continuously read data coming back to the program over a connected serial port. I have buttons that Open and Close the port via the user. I am having trouble configuring the "DataReceived" event handler to read the incoming data and display it in a textbox in the app.
I have been getting this error: "Cross-thread operation not valid: Control 'textBox4' accessed from a thread other than the thread it was created on." I see this is a thread error but I have not been able to figure out my issue.
namespace Program
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
getAvailabePorts();
}
private void getAvailabePorts()
{
String[] ports = SerialPort.GetPortNames();
comboBox1.Items.AddRange(ports);
}
public void button1_Click(object sender, EventArgs e)
{
try
{
if (comboBox1.Text == "" || comboBox2.Text == "")
{
textBox4.Text = "Please select port settings";
}
else
{
serialPort1.PortName = comboBox1.Text;
serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);
serialPort1.DataReceived += new SerialDataReceivedEventHandler(mySerialPort_DataReceived);
serialPort1.Open();
}
}
catch (UnauthorizedAccessException)
{
textBox4.Text = "Unauthorized Access";
}
public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort)sender;
textBox4.Text = sp.ReadExisting() + "\n";
}
private void button2_Click(object sender, EventArgs e)
{
serialPort1.Close();
textBox4.Clear();
}
}
}
}
First, welcome.
Before the "big" issue (marshalling data), let me warn you -- serial ports are tricky. For example, your call to "ReadExisting" may not return what you expect -- will return whatever is in the serial port buffer at the time, but more may come in, which will overwrite what is already in your text box. So you may want to append data your text box.
Now the real issue. As a commentor mentioned, you cannot post directly post data from another thread to the UI thread. Without you knowing, the serial port created a new thread to receive data.
You can handle this directly by modifying your receiver code as follows:
public void mySerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
SerialPort sp = (SerialPort) sender;
var dataRcvd = sp.ReadExisting();
object[] dataArray = new object[1];
dataArray[0] = dataRcvd;
BeginInvoke( new postDataDelegate( postData), dataArray );
}
private delegate void postDataDelegate( string d );
private void postData( string d)
{
textBox4.Text = d;
}
This will "marshall" the data to the UI thread so it can be used. There are many ways this can be done (and, many differences between how it is done in WPF vs. Winforms, so watch out for that). I hope this illustrates the point.
Another aside -- no need ot make the DataReceived method public -- it will work fine private.
Let's say I have a thread that waits for a user to click a button before advancing:
System.Threading.AutoResetEvent dialoguePause = new System.Threading.AutoResetEvent(false);
public void AskQuestion()
{
/* buttons containing choices created here */
dialoguePause.WaitOne();
/*Code that handles choice here */
}
public void Choice_Clicked(object sender, EventArgs e)
{
dialoguePause.Set();
}
How can I pass data from the thread Choice_Clicked is on to AskQuestion without relying on class variables? The best I can do is this:
System.Threading.AutoResetEvent dialoguePause = new System.Threading.AutoResetEvent(false);
string mostRecentChoice;
public void AskQuestion()
{
/* buttons containing choices created here */
dialoguePause.WaitOne();
MessageBox.Show("You chose " + mostRecentChoice + ".");
}
public void Choice_Clicked(object sender, EventArgs e)
{
mostRecentChoice = (sender as Button).Content.ToString(); //Ugly!
dialoguePause.Set();
}
There are many ways to achieve this:
Use a property in a Singleton or Monostate. There is always one instance of such property, so regardless which thread writes, and which reads, the will share it, as long as they are in one Application Domain.
Use messaging
If you are in same class, use field or property (look out for cross-threads!)
...
What I am trying to say, it depends on the application. I have no clue if this is WinForm, WPF or Web ...
So I'm making a Kinect application using buttons, and to navigate the app, I'm making new windows for each button. I'm come across an issue I haven't been able to find any help at all on, and would appreciate any help.
So to open the new window, I'm using this:
private void button1_Click(object sender, RoutedEventArgs e)
{
//SUPPOSED to uninitialize the Kinect
UninitializeKinectSensor(this.Kinect;
//To open the new window
Window1 newwindow = new Window1();
newwindow.Show();
//To close the first window...
Close();
{
SO that one line is supposed to uninitialize the Kinect so it'll be free for the new window to use, but when it goes to the new window, the Kinect freezes. If I use the mouse to go back to the first window, it works on the first window again, which it shouldn't.
I also added in this line in the initialization phase
public Window1()
{
//Other init code is here, but this is the line I added. It doesn't seem to do anything.
InitializeKinectSensor(this.Kinect);
}
Any help is greatly appreciated!! I'm sure it's something simple and I just failed miserably haha XD
Do you really have to create a new window instead of using pages?
In your MainWindow you create a frame that takes all the window and use this frame to navigate between pages. This way, you'll keep the focus of the kinect in your whole application.
Depends alot on what UninitializeKinectSensor is actually doing. Just as a quick fix, though, you can try calling uninitialize on a background worker and see if that helps at all.
Instead of using the "Show()" use "ShowDialog()".It's better if you can create a static class or method to initialize and uninitialized kinect.
public static void start()
{
KinectSensor.KinectSensors.StatusChanged += kinectSensorsStatusChanged;
DiscoverSensor();
}
private static void kinectSensorsStatusChanged(object sender, StatusChangedEventArgs e)
{
KinectSensor oldSensor = Kinect;
if (oldSensor != null)
{
UninitializeKinect();
}
var status = e.Status;
if (Kinect == null)
{
//updateStatus(status);
if (e.Status == KinectStatus.Connected)
{
Kinect = e.Sensor;
DiscoverSensor();
}
}
else
{
if (Kinect == e.Sensor)
{
//updateStatus(status);
if (e.Status == KinectStatus.Disconnected ||
e.Status == KinectStatus.NotPowered)
{
Kinect = null;
sensorConflict = false;
DiscoverSensor();
}
}
}
}
private static DispatcherTimer readyTimer;
private static void UninitializeKinect()
{
if (speechRecognizer != null && Kinect != null)
{
Kinect.AudioSource.Stop();
Kinect.SkeletonFrameReady -= kinect_SkeletonFrameReady;
Kinect.SkeletonStream.Disable();
Kinect.Stop();
//this.FrameSkeletons = null;
speechRecognizer.RecognizeAsyncCancel();
speechRecognizer.RecognizeAsyncStop();
}
if (readyTimer != null)
{
readyTimer.Stop();
readyTimer = null;
}
}
I have a base form that I use when calling 2 forms. Previously when calling the forms I didn't dispose of them, but I have found that reusing them, they would stay in memory and not get collected. So I have instead used a using statement instead to clear the memory, and all my problem are fixed.
But now a new problem arises, one that I had previously when testing my app with mono on Linux. I though it might be a mono specific problem, but since adding the using statement the same thing happens on my Windows machine. So it might just be that the Garbage Collector on Mono is different and was disposing properly of my forms.
Here is my problem I have a thread that I start to extract files in the background And I have progress bar telling me the progress, before using the dispose if I closed the form and reopened it my files extracted correctly and the progress bar was working fine. But now they work fine the first time, but if I reopen the form or the other one that has the same base, the extraction is not working, no files are extracted because I have a null exception when reporting the progress.
private void ExtractFiles()
{
Zip.ExtractProgress += new EventHandler<ExtractProgressArgs>(Utils_ExtractProgress);
Thread t = new Thread(new ThreadStart(Zip.ExtractZip));
t.IsBackground = true;
t.Start();
FilesExtracted = true;
}
void Utils_ExtractProgress(object sender, ExtractProgressArgs e)
{
UpdateProgress(e.Pourcentage);
}
private delegate void UpdateProgressDelegate(int Pourc);
private void UpdateProgress(int Pourc)
{
lock (this)
{
if (Progress.ProgressBar.InvokeRequired)
{
UpdateProgressDelegate del = new UpdateProgressDelegate(UpdateProgress);
Progress.ProgressBar.BeginInvoke(del, Pourc);
} else
{
Progress.Value = Pourc;
}
}
}
This code is in my BaseForm, the Progress control isn't null, but all of it's properties have null exceptions. So when checking if Invoked is required it raises an Null exception.
Here is my Zip.Extract method
public static event EventHandler<ExtractProgressArgs> ExtractProgress;
static ExtractProgressArgs Progress;
internal static void ExtractZip()
{
try
{
using (ZipFile zip = ZipFile.Read(Variables.Filename))
{
Progress = new ExtractProgressArgs();
Progress.TotalToTransfer = Convert.ToInt32(zip.Sum(e => e.UncompressedSize));
zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(zip_ExtractProgress);
Old = 0; New = 0;
foreach (ZipEntry item in zip)
{
item.Extract(Variables.TempFolder, ExtractExistingFileAction.OverwriteSilently);
}
}
} catch (Exception)
{
}
}
static long Old;
static long New;
static void zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
if (e.EventType == ZipProgressEventType.Extracting_EntryBytesWritten)
{
New = e.BytesTransferred;
Progress.Transferred += New - Old;
Old = e.BytesTransferred;
if (ExtractProgress != null)
{
ExtractProgress(e.CurrentEntry, Progress);
}
} else if (e.EventType == ZipProgressEventType.Extracting_AfterExtractEntry)
{
Old = 0;
}
}
Might be because my Zip.Extract is static? I have almost no knowledge of multi-threading, like synchronization, etc.
The short answer is yes, some of your problems are due to the static nature of those operations.
You should be able to resolve the problem by removing the static declarations from your Zip class and then creating an instance of it as needed.