Sockets in C++ and C# under Windows not working properly - c#

I'm using UDP sockets to communicate a C++ application with a C# application.
In the C# -> C++ direction everything seems to be working fine, but the other way around is the one that's driving me nuts.
Communication does work, but messages are getting way late (like 2 secs delay) in the C# app, even though they're being sent every frame (it's a 3D app), and the receiving code is executing every 10 ms.
I need real time so this is a very painful problem. Do you think this might be related to packet losses? Then why don't they get lost in the other direction?
EDIT:
C# app code for syncing data:
public void RecibeDatos()
{
if (MessageReceived && U != null && S != null)
{
MessageReceived = false;
//Console.WriteLine("listening for messages");
U.BeginReceive(ReceiveCallback, S);
}
}
public void ReceiveCallback(IAsyncResult ar)
{
UdpClient u = ((UdpState)(ar.AsyncState)).U;
IPEndPoint e = ((UdpState)(ar.AsyncState)).E;
receivedBytes = u.EndReceive(ar, ref e);
//int currentProtocol = (int) numero;
//ResetSignal = reset > 0;
//Console.WriteLine("Received: " + currentProtocol);
MessageReceived = true;
}
C++ Code for sending data:
float indiceFloat[1];
indiceFloat[0] = indice_protocolo_actual;
sender->setBuffer((void *)indiceFloat, sizeof(indiceFloat));
sender->sync();
sync method on J_Enviar (sender) class:
void J_Enviar::sync( void )
{
if(!_initialized) init();
if( _buffer == 0L )
{
fprintf( stderr, "Broadcaster::sync() - No buffer\n" );
return;
}
#if defined (WIN32) && !defined(__CYGWIN__)
unsigned int size = sizeof( SOCKADDR_IN );
sendto( _so, (const char *)_buffer, _buffer_size, 0, (struct sockaddr *)&saddr, size );
int err = WSAGetLastError ();
if (err!=0)
fprintf( stderr, "Broadcaster::sync() - error %d\n",err );
#else
unsigned int size = sizeof( struct sockaddr_in );
sendto( _so, (const void *)_buffer, _buffer_size, 0, (struct sockaddr *)&saddr, size );
#endif
}
Providing full SocketManager code for Receiving C# end:
using System;
using System.Net;
using System.Net.Sockets;
namespace WpfApplication1
{
public class SocketManager
{
private static SocketManager _instance = null;
static readonly object Padlock = new object();
private IPEndPoint E;
private UdpClient U;
private UdpState S;
private byte[] receivedBytes;
public static bool MessageReceived = true;
private SocketManager()
{
}
public byte[] ReceivedBytes
{
get { return receivedBytes; }
}
public static SocketManager Instance
{
get
{
lock(Padlock)
{
return _instance ?? (_instance = new SocketManager());
}
}
}
public void CreateReceivingSocket(IPAddress a, int puerto)
{
if(E==null || (E.Address != a && E.Port != puerto))
{
E = new IPEndPoint(a, puerto);
U = new UdpClient(puerto);
S = new UdpState { E = E, U = U };
}
}
public void ReceiveCallback(IAsyncResult ar)
{
UdpClient u = ((UdpState)(ar.AsyncState)).U;
IPEndPoint e = ((UdpState)(ar.AsyncState)).E;
receivedBytes = u.EndReceive(ar, ref e);
//int currentProtocol = (int) numero;
//ResetSignal = reset > 0;
//Console.WriteLine("Received: " + currentProtocol);
MessageReceived = true;
}
public void RecibeDatos()
{
if (MessageReceived && U != null && S != null)
{
MessageReceived = false;
//Console.WriteLine("listening for messages");
U.BeginReceive(ReceiveCallback, S);
}
}
public void CloseConnection()
{
if (E != null)
{
E.Port = 5502;
E = null;
}
if (U != null)
U.Close();
}
}
public class UdpState
{
public IPEndPoint E;
public UdpClient U;
}
}
And this is my dispatchertimerclick which makes the program receive each 10 ms:
_dispatcherTimer.Tick += DispatcherTimerTick;
_dispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, 1);
_dispatcherTimer.Start();
private void DispatcherTimerTick(object sender, EventArgs e)
{
_exp1Class.sendData();
_sm.RecibeDatos();
byte[] recibidos = _sm.ReceivedBytes;
if (recibidos != null)
{
float numero = BitConverter.ToSingle(recibidos, 0);
_exp1Class.CurrentProtocol = (int) numero;
}
}

I don't see when you kick off your first BeginReceive. (Ah, it is done from your first timer tick I think?) It should be initaited as soon as you are ready to receive data. Secondly, your ReceiveCallback should take the received data and place it into a queue of some sort and immediately call BeginReceive again. Otherwise you are holding up thge arrival of the next data frame until the prior was consumed. Finally, watch for threading issues, as the Threading timer and the UDP callback each will run on seperate threads from your application main thread.
The only reason your code works at all is because you pre-initialized MessageReceived = true even before you receive any callbacks. When the first tick happens the call to RecibeDatos invokes BeginReceive because that bool was set to true.
Think of BeginReceive as saying "call me back when you have some data from the network". You don't need to poll the network using your timer. (You can choose to consume that data via your timer if your application requires that, but lets leave that aside for the moment).
Here are the rough steps:
First, upon start (or enable, run, etc) you should call BeginReceive. You will now receive a notification when data arrives.
Secondly, when the callback happens you complete that read of the data bytes using EndReceive. That data would typically be buffered or otherwise dispatched. You should then call BeginReceive again (within the callback) so that upon the next set of data being available you will again get notified. It becomes a kind of async loop.
The question is what to do with the data you are reading. You might consider placing the data into a queue, and then having your Timer pop these data frames off of the queue for processing. Be aware that the data can arrive multiple times between your Ticks.

Related

Write data using I2C windows iot and Arduino

So I have a Arduino and Raspberry pi 3 with windows IoT connected as show in the image below:
[][1
I want to read from, and write to, the Arduino slave using the RPI as master with I2C. I have the following code so far:
C# Master:
private I2cDevice arduio; // Used to Connect to Arduino
private DispatcherTimer timer = new DispatcherTimer();
public MainPage()
{
this.InitializeComponent();
Initialiasecom();
}
public async void Initialiasecom()
{
var settings = new I2cConnectionSettings(0x40); // Slave Address of Arduino Uno
settings.BusSpeed = I2cBusSpeed.FastMode; // this bus has 400Khz speed
string aqs = I2cDevice.GetDeviceSelector("I2C1"); // This will return Advanced Query String which is used to select i2c device
var dis = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(aqs);
arduio = await I2cDevice.FromIdAsync(dis[0].Id, settings);
timer.Tick += Timer_Tick; // We will create an event handler
timer.Interval = new TimeSpan(0, 0, 0, 0, 500); // Timer_Tick is executed every 500 milli second
timer.Start();
}
private async void Timer_Tick(object sender, object e)
{
byte[] response = new byte[2];
byte[] request = new byte[] { 0x40, 0x40 };
try
{
arduio.Read(response); // this funtion will request data from Arduino and read it
arduio.Write(request); // this function will send data to Arduino
}
catch (Exception p)
{
//Windows.UI.Popups.MessageDialog msg = new Windows.UI.Popups.MessageDialog(p.Message);
Debug.WriteLine(p.Message);
//await msg.ShowAsync(); // this will show error message(if Any)
}
text.Text = response[0].ToString();
}
Slave Arduino:
include <Wire.h>
#define SLAVE_ADDRESS 0x40
byte response[1]; // this data is sent to PI
void setup() {
Wire.begin(SLAVE_ADDRESS); // join i2c bus with address slaveAddress
Wire.onReceive(I2CReceived);
Wire.onRequest(I2CRequest);
}
void loop() {
delay(100);
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void I2CRequest() {
response[0] = (byte)17;
Wire.write(response, 2); // return data to PI
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void I2CReceived(int howMany) {
Serial.println("test");
while (1 < Wire.available()) { // loop through all but the last
char c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
}
int x = Wire.read(); // receive byte as an integer
Serial.println(x); // print the integer
}
I can successfully read data from the Arduino and print them to the textblock on windows IoT. But I also want to write text to the Arduino. I tried something but it isn't working. Can someone please explain how to write data.
I really need some help and I'm no professional programmer so please keep it as simple as possible. If something is wrong with my current code please comment so I can try to improve my code.
I think the issue is due to the missing of Serial.begin,in fact the data sent from master to slaver has been received,just not printed.Please try again with the following code on slaver.
#include <Wire.h>
#define SLAVE_ADDRESS 0x40
byte response[1]; // this data is sent to PI
static int index = 0;
void setup() {
Serial.begin(9600);
Wire.begin(SLAVE_ADDRESS); // join i2c bus with address slaveAddress
Wire.onReceive(I2CReceived);
Wire.onRequest(I2CRequest);
}
void loop() {
delay(100);
}
// function that executes whenever data is requested by master
// this function is registered as an event, see setup()
void I2CRequest() {
Serial.println("I2C-Request");
response[0] = (byte) index ++ ;
Wire.write(response, 2); // return data to PI
}
// function that executes whenever data is received from master
// this function is registered as an event, see setup()
void I2CReceived(int howMany) {
Serial.println("I2C-Received");
while (1 < Wire.available()) { // loop through all but the last
char c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
}
int x = Wire.read(); // receive byte as an integer
Serial.println(x); // print the integer
}
I tested with my Arduino UNO,it works.The received data could be shown in serial monitor.

Obtaining Millisecond Resolution (cycle Time) in Windows Embedded Application

I am making an Application in C# which reads CAN (Control Area Network) messages and sends them too. I need to do it in 10 milliseconds. The OS i am using is Windows Embedded 7 Pro.
public void ID0008Update10ms(DataTable supportPoints, int a)
{
System.TimeSpan timer10ms = System.TimeSpan.FromMilliseconds(10);
intialiseCAN();
while (a==1)
{
Stopwatch t = Stopwatch.StartNew();
sendCAN();
getCAN();
ID0006NavComStatus(supportPoints);
string state = Convert.ToString(gNavStatus);
while (t.Elapsed < timer10ms)
{ /*nothing*/}
}
}
The issue is the sendCAN() and reciveCAN() dynamically load the .dll file
public int sendCAN(ref can_msg msg, IntPtr pDll)
{
if (pDll == IntPtr.Zero)
{
MessageBox.Show("Loading Failed");
}
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "CAN_Transmission");
CAN_Transmission sendCAN = (CAN_Transmission)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(CAN_Transmission));
int result = sendCAN( ref msg);
return result;
}
This makes the cycle slow, I am not able to send the Message within 10ms. Can anyone propose a better way. Please remember. I use Windows Embedded.
You should move as much as possible outside the loop. If it doesn't absolutely have to be there, move it. Along the lines of....
private CAN_TransmissionMethod CAN_Transmission;
//Cache the delegate outside the loop.
private bool InitialiseCAN2(IntPtr pDll)
{
if (pDll == IntPtr.Zero)
{
Log("Loading Failed");
return false;
}
IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(pDll, "CAN_Transmission");
CAN_Transmission = (CAN_TransmissionMethod)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(CAN_Transmission));
return true;
}
public int sendCAN(ref can_msg msg)
{
if (CAN_Transmission == null)
return -1;//Can't send, no delegate, Log, Fail, Explode... make a cup of tea.
int result = CAN_Transmission( ref msg);
return result;
}
public void ID0008Update10ms(DataTable supportPoints, int a)
{
System.TimeSpan timer10ms = System.TimeSpan.FromMilliseconds(10);
intialiseCAN();
initialiseCAN2(pDll)
while (a==1)
{
Stopwatch t = Stopwatch.StartNew();
sendCAN(ref thereIsSupposedToBeAMessageHere);
getCAN(ref probablySupposedToBeSomethingHereToo);
ID0006NavComStatus(supportPoints);
string state = Convert.ToString(gNavStatus);
while (t.Elapsed < timer10ms)
{ /*nothing*/}
}
}

Serial port: Polling vs Event-Driven approach

I am using Win32 API (Overlapped IO) for serial port communication. I have used PInvoke to call the Win32 API from my C# code.
In my previous implementation, the receiver thread reads data by using Polling mechanism (Infinite while loop reads RX buffer periodically). Now, I am trying to use Event-Driven approach to read the data (Using WaitCommEvent API Call). We expect the new implementation will reduce the CPU usage and Speed-Up the communication.
But, After changing to Event-Driven approach, the communication is actually slowed down, taking twice the time to read when compared to Polling mechanism.
Can anyone suggest which one of these is the best approach improve the communication speed and reduce the CPU usage?
Below is my code snippet:
private void ReadDataForWindows()
{
var data = new byte[255];
// Specify a set of events to be monitored for the port.
if (SetCommMask(this.Handle, EV_DEFAULT))
{
while (this.Handle != (IntPtr)INVALID_HANDLE_VALUE)
{
uint eventMask = 0;
// Wait for an event to occur for the port.
eventMask = this.WaitForEvent();
if (eventMask == EV_RXCHAR)
{
this.ReadFromRXBuffer(data);
}
else if (eventMask == EV_ERR)
{
uint errors;
ClearCommError(this.Handle, out errors, IntPtr.Zero);
}
// Re-specify the set of events to be monitored for the port.
if (!SetCommMask(this.Handle, EV_DEFAULT))
{
break;
}
}
}
}
private void ReadFromRXBuffer(byte[] data)
{
uint dwBytesTransferred;
// Loop for waiting for the data.
do
{
// Read the data from the serial port.
this.Read(data, 255, out dwBytesTransferred);
// Display the data read.
if (dwBytesTransferred > 0)
{
if (this.DataReceived != null)
{
var dataReceived = new byte[dwBytesTransferred];
for (var i = 0; i < dwBytesTransferred; i++)
{
dataReceived[i] = data[i];
}
this.DataReceived(this, new ByteDataReceivedEventArgs(dataReceived, Convert.ToInt32(dwBytesTransferred)));
}
}
} while (dwBytesTransferred > 0);
}
private uint WaitForEvent()
{
uint eventMask = 0;
IntPtr evPtr = Marshal.AllocHGlobal(Marshal.SizeOf(eventMask));
Marshal.WriteInt32(evPtr, 0);
if (WaitCommEvent(this.Handle, evPtr, this.RxOvr.MemPtr) == false)
{
int error = Marshal.GetLastWin32Error();
// Operation is executing in the background
if (error == ERROR_IO_PENDING)
{
this.ReceiveSignal.WaitOne();
}
else
{
this.Fault = "WaitCommEvent() Failed. System Returned Error Code: " +
error.ToString();
return 0;
}
}
eventMask = (uint)Marshal.ReadInt32(evPtr);
return eventMask;
}

SerialPort in c# , Data Receive Incomplete Message

I'm working on Serialport. I'm facing a new problem that once I receive data my data are incomplete. How can I check if my data are complete then process them, and if not, don't process them?
Here are my data receive and my send function:
private void Send(byte[] cmd)
{
bResponse = new byte[0];
Write(cmd);
}
void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
int iCount = comPort.BytesToRead;
byte[] bBuffer = new byte[iCount];
comPort.Read(bBuffer, 0, iCount)
if (bBuffer.Length == 1 && bBuffer[0] == ACK)
Write(new byte[] { ENQ });
else if (bBuffer.Length == 1 && bBuffer[0] == NAK)
{
Debug.WriteLine("Incomplete Message detected!");
}
else
{
bResponse = bResponse.Concat(bBuffer).ToArray();
rResponse = Decode(bResponse);
Write(new byte[] { ACK });
}
}
I know my data are received in a few packages and I need to wait until the response is complete, but I don't know based on the code above. How should I check whether the data are complete to determine whether to wait? (P.S: The size of the received response varies.)
There is no built-in concept of completeness or packet size.
You'll have to append to a buffer until you see some recognizable end-of-packet pattern that you (or someone else) defined as part of the protocol specification. - And then probably time out after a while if you haven't seen what you are looking for.
Example of old project, notice the firstindex, lastindex, you put in a character to know the length, the start/end character is predefined and can be any character you choose, just be sure not to take any common characters
This is for tcp/ip, but same principle can be used for serialport
public void ReceiveMessage(IAsyncResult ar)
{
int bytesRead;
try
{
lock (client1.GetStream())
{
bytesRead = client1.GetStream().EndRead(ar);
}
string messageReceived = System.Text.Encoding.ASCII.GetString(data, 0, bytesRead);
received = messageReceived;
int firstindex = received.IndexOf((char)2);
int lastindex = received.IndexOf((char)3);
if (firstindex > 0 && lastindex > 0)
{
string first = received.Substring(firstindex, 1);
string last = received.Substring(lastindex, 1);
}
lock (client1.GetStream())
{
client1.GetStream().BeginRead(data, 0, System.Convert.ToInt32(client1.ReceiveBufferSize), ReceiveMessage, null);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
I have some code for you.
First, you implement the DataReceived Event (as you have done already). This event is only called when there is data to process. While I would not call it interrupt-based (as in "realtime capable") is is definitely not polling. Which is good.
Second: When the event is called you may have only one byte, but there may be more bytes. To capture each packet you need to implement an custom buffer.
Third: After you append one byte to your buffer, you check whether the buffer contains a valid packet. If so, process it. If not: Append another one. If no bytes are left, wait for the event to be called again.
In code it looks like this:
const BUFFERLENGTH = 17; // Bytes
byte[] buffer = new byte[BUFFERLENGTH];
private void COM_Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var port = (SerialPort)sender;
while (port.BytesToRead > 0)
{
var data = (byte)port.ReadByte();
Read(data);
}
}
private void Read(byte value)
{
// Append Byte to buffer
System.Buffer.BlockCopy(buffer, 1, buffer, 0, BUFFERLENGTH- 1);
buffer[BUFFERLENGTH - 1] = value;
// Check for valid Packet
if (IsValidPacket(buffer))
{
// Yeah! Gotcha :-)
// Now copy your Packet from the Buffer to your struct/whatever
}
}
private bool IsValidPacket(byte[] buffer)
{
// Todo: Check whether buffer contains a valid Packet.
// Some think like:
// return buffer != null && buffer[0] == 0xF4 && buffer[2] == buffer.length
throw new NotImplementedException();
}
Note that I did not "append the byte to the buffer". I discarded the first byte, shifted every byte by one position and inserted the new byte at the end. If a valid Packet was found I could just copy it in one block into a struct. So the buffer size is always constant and exactly as long as one packet.
This may not be the fastest code out there (because it's reading each byte separately) but it works well for me :-)
Oh, and remember to use Begininvoke() if you want to display that stuff in your GUI.

Connect from C# to a digital scale

I'm trying to figure out a method of connecting from C# code to a digital scale. The particular scale is an Ohaus SP202 digital scale which comes with a USB connection. I would like to read the weight measured on the scale programmatically. I don't have the scale yet, I'm just doing the research before hand.
Has anyone done this before? I've been doing research on the internet and didn't find anything worth mentioning yet.
USB Hardware communication popularly works one of three ways.
Proprietary software talks to hardware via proprietary driver.
Devices have a Serial emulation chip (e.g. FTDI) When you plug in the scale you just need to install the Virtual Comm Port drivers and the device shows up as a comm port on your system. Then it is as easy as using System.IO.Ports.SerialPort to talk to the device.
The device implements a HID profile and will be available through your OS's HID system. I've used this .NET HID library on Windows to successfully talk with barcode scanners that implement HID. The HID library will send data to you from the hardware in chunks that you decipher based on the hardware you are talking to.
With method 2 and 3 you will just need to find the data format for the scale that you are talking to. The scales that I have used send updates every second or so with the weight and other information shown on the hardware UI, such as if the load has stabilized or not.
Looking at their discussion forums it looks like their scales use method 2 (http://ohaus.com/support/forum_messages.asp?topicid=584) and that you need to Poll the scale by sending a "P\r\n" it will then respond with the characters that are shown on the display (http://ohaus.com/support/forum_messages.asp?topicid=802).
I haven't used that specific scale, but I have connected to other digital scales previously. Basically it is usually just doing serial communications via the USB to Com converter.
If the scale has an API for this all the better, but if not then you will just be using System.IO.Ports.SerialPort which is pretty standard serial programming. A starter article here
I don't know any details on this scale, but I've done some USB stuff.
Its most likely using usb interrupt to transfer the data. All usb mice also use interrupt, so if you can figure out how to read the mouse signal (with using an HID api), then it should be exactly the same as the scale, except the data format returned would be completely different.
run on ftw
Scales was one of the first devices I ever had to read data from. At that time, it was just a serial port and I'm guessing that you'd still be able to read from the serial port with SerialPort.Net.
Here's the code for Serial Port transfer on Windows
Reference
using Device.Net;
using Device.Net.Windows;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
namespace SerialPort.Net.Windows
{
public class WindowsSerialPortDevice : DeviceBase, IDevice
{
#region Fields
private readonly int _BaudRate;
private readonly byte _ByteSize;
private bool disposed;
private readonly Parity _Parity;
private SafeFileHandle _ReadSafeFileHandle;
private readonly StopBits _StopBits;
private ushort ReadBufferSize { get; }
#endregion
#region Public Properties
public bool IsInitialized => _ReadSafeFileHandle != null && !_ReadSafeFileHandle.IsInvalid;
/// <summary>
/// TODO: No need to implement this. The property probably shouldn't exist at the base level
/// </summary>
public IApiService ApiService { get; }
public ConnectedDeviceDefinition ConnectedDeviceDefinition { get; private set; }
#endregion
#region Constructor
public WindowsSerialPortDevice(
string deviceId,
int baudRate = 9600,
StopBits stopBits = StopBits.One,
Parity parity = Parity.None,
byte byteSize = 8,
ushort readBufferSize = 1024,
ILoggerFactory loggerFactory = null,
IApiService apiService = null) : base(
deviceId,
loggerFactory,
(loggerFactory ?? NullLoggerFactory.Instance).CreateLogger<WindowsSerialPortDevice>())
{
ApiService = apiService ?? new ApiService(null);
ConnectedDeviceDefinition = new ConnectedDeviceDefinition(DeviceId, DeviceType.SerialPort);
if ((byteSize == 5 && stopBits == StopBits.Two) || (stopBits == StopBits.OnePointFive && byteSize > 5))
throw new ArgumentException(Messages.ErrorInvalidByteSizeAndStopBitsCombo);
if (byteSize is < 5 or > 8)
throw new ArgumentOutOfRangeException(nameof(byteSize), Messages.ErrorByteSizeMustBeFiveToEight);
if (baudRate is < 110 or > 256000)
throw new ArgumentOutOfRangeException(nameof(baudRate), Messages.ErrorBaudRateInvalid);
if (stopBits == StopBits.None)
throw new ArgumentException(Messages.ErrorMessageStopBitsMustBeSpecified, nameof(stopBits));
ReadBufferSize = readBufferSize;
_BaudRate = baudRate;
_ByteSize = byteSize;
_StopBits = stopBits;
_Parity = parity;
}
#endregion
#region Public Methods
public Task InitializeAsync(CancellationToken cancellationToken = default) => Task.Run(Initialize, cancellationToken);
private uint Write(byte[] data) => data == null ? 0 : ApiService.AWriteFile(_ReadSafeFileHandle, data, data.Length, out var bytesWritten, 0) ? (uint)bytesWritten : 0;
public override Task<uint> WriteAsync(byte[] data, CancellationToken cancellationToken = default)
{
ValidateConnection();
return Task.Run(() =>
{
var bytesWritten = Write(data);
Logger.LogDataTransfer(new Trace(false, data));
return bytesWritten;
}, cancellationToken);
}
public override Task<TransferResult> ReadAsync(CancellationToken cancellationToken = default)
{
ValidateConnection();
return Task.Run(() =>
{
var buffer = new byte[ReadBufferSize];
var bytesRead = Read(buffer);
var transferResult = new TransferResult(buffer, bytesRead);
Logger.LogDataTransfer(new Trace(false, transferResult));
return transferResult;
}, cancellationToken);
}
public override Task Flush(CancellationToken cancellationToken = default)
{
ValidateConnection();
return Task.Run(() => ApiService.APurgeComm(_ReadSafeFileHandle, APICalls.PURGE_RXCLEAR | APICalls.PURGE_TXCLEAR),
cancellationToken);
}
public override void Dispose()
{
if (disposed)
{
Logger.LogWarning(Messages.WarningMessageAlreadyDisposed, DeviceId);
return;
}
disposed = true;
Logger.LogInformation(Messages.InformationMessageDisposingDevice, DeviceId);
if (_ReadSafeFileHandle != null)
{
_ReadSafeFileHandle.Dispose();
_ReadSafeFileHandle = new SafeFileHandle((IntPtr)0, true);
}
base.Dispose();
}
public void Close() => Dispose();
#endregion
#region Private Methods
private void Initialize()
{
_ReadSafeFileHandle = ApiService.CreateReadConnection(DeviceId, FileAccessRights.GenericRead | FileAccessRights.GenericWrite);
if (_ReadSafeFileHandle.IsInvalid) return;
var dcb = new Dcb();
var isSuccess = ApiService.AGetCommState(_ReadSafeFileHandle, ref dcb);
_ = WindowsHelpers.HandleError(isSuccess, Messages.ErrorCouldNotGetCommState, Logger);
dcb.ByteSize = _ByteSize;
dcb.fDtrControl = 1;
dcb.BaudRate = (uint)_BaudRate;
dcb.fBinary = 1;
dcb.fTXContinueOnXoff = 0;
dcb.fAbortOnError = 0;
dcb.fParity = 1;
#pragma warning disable IDE0010 // Add missing cases
dcb.Parity = _Parity switch
{
Parity.Even => 2,
Parity.Mark => 3,
Parity.Odd => 1,
Parity.Space => 4,
Parity.None => 0,
_ => 0
};
dcb.StopBits = _StopBits switch
{
StopBits.One => 0,
StopBits.OnePointFive => 1,
StopBits.Two => 2,
StopBits.None => throw new ArgumentException(Messages.ErrorMessageStopBitsMustBeSpecified),
_ => throw new ArgumentException(Messages.ErrorMessageStopBitsMustBeSpecified),
};
#pragma warning restore IDE0010 // Add missing cases
isSuccess = ApiService.ASetCommState(_ReadSafeFileHandle, ref dcb);
_ = WindowsHelpers.HandleError(isSuccess, Messages.ErrorCouldNotSetCommState, Logger);
var timeouts = new CommTimeouts
{
WriteTotalTimeoutConstant = 0,
ReadIntervalTimeout = 1,
WriteTotalTimeoutMultiplier = 0,
ReadTotalTimeoutMultiplier = 0,
ReadTotalTimeoutConstant = 0
};
isSuccess = ApiService.ASetCommTimeouts(_ReadSafeFileHandle, ref timeouts);
_ = WindowsHelpers.HandleError(isSuccess, Messages.ErrorCouldNotSetCommTimeout, Logger);
Logger.LogInformation("Serial Port device initialized successfully. Port: {port}", DeviceId);
}
private uint Read(byte[] data)
=>
ApiService.AReadFile(_ReadSafeFileHandle, data, data.Length, out var bytesRead, 0)
? bytesRead
: throw new IOException(Messages.ErrorMessageRead);
private void ValidateConnection()
{
if (!IsInitialized)
{
throw new InvalidOperationException(Messages.ErrorMessageNotInitialized);
}
}
#endregion
}
}

Categories

Resources