I have around 10 commands of Tx,Rx with time interval of 100 millisecond for each. So Basically a TxRx pair should be executed within every 100 milliseconds. unless the stop button is clicked. Where am I getting Wrong?
- Form1 btn_Start
set the time Interval to request and receive the message
private void btn_Start_Click(object sender, EventArgs e)
{
Start();
}
private void Start()
{
btn_Start.Enabled = false;
btn_Stop.Enabled = true;
int Interval = 100;
if (CANTimer.Enabled == false)
{
// CAN Timer confinguration
CANTimer.Enabled = true;
CANTimer.Interval = Convert.ToInt32(fCANInterval); //Interval;
CANTimer.Tick = new System.EventHandler(this.CANTimer_Tick);
CANTimer.Start();
}
else if (CANTimer.Enabled && Program_config_num == 0)
{
CANTimer.Stop();
CANTimer.Interval = Convert.ToInt32(fCANInterval); //Interval;
CANTimer.Enabled = true;
CANTimer.Start();
}
}
- Form1 CANTimer_Tick
Execute the command to Send and Receive Messages in every 100 milliseconds, till the btn_Stop is clicked
private void CANTimer_Tick(object sender, EventArgs e)
{
ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
ThreadPool.GetAvailableThreads(out workerThreads, out completionPortThread);
int unit_index = 1;
if(IsConnected == false)
{
IsConnected = CANClass.connect();
lbl_Status.Text = "Connected";
}
else
{
Req1_Send = CAN.Send_Req1(unit_index, CANClass); //Sends request for Msg1 to MCU through CANClass
if ((Req1_Send == true) && (Msg1_Received == false))
{
ThreadPool.QueueUserWorkItem(ReadMsg1); //Reads the message and display
}
Req2_Send = CAN.Send_Req2(unit_index, CANClass); //Sends request for Msg2 to MCU through CANClass
if ((Req2_Send == true) && (Msg2_Received == false))
{
ThreadPool.QueueUserWorkItem(ReadMsg2); //Reads the message and display
}
Req3_Send = CAN.Send_Req3(unit_index, CANClass); //Sends request for Msg3 to MCU through CANClass
if ((Req3_Send == true) && (Msg3_Received == false))
{
ThreadPool.QueueUserWorkItem(ReadMsg3); //Reads the message and display
}
............... till the Msg10
}
}
- btn_Stop
Stop the connection
private void btn_Stop_Click(object sender, EventArgs e)
{
Stop();
}
private void Stop()
{
btn_Start.Enabled = true;
btn_Stop.Enabled = false;
CanPacket.CanStop();
IsConnected = false;
}
```
CodePudding user response:
The thing with hardware controllers is that (usually) you don't know exactly how long they'll take to fill up the RX Buffer so that you can get the packet response from the TX-RX pair. If you're doing it on a timer tick you're between the terrible risk of buffer underrun (possibly hanging the controller) or wasting time by patting the wait time with margins.
Often it's better to loop in a Task where you perform the IO synchronously (hopefully this involves actual handshaking) for your "10 Commands" and then get the "100 ms spacing" after all the TX-RX pairs have completed and before the next iteration of the loop.
private async void checkBoxRun_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxRun.Checked)
{
richTextBox.Clear();
await Task.Run(() => loop());
labelIndicator.BackColor = Color.LightGray;
}
}
private async Task loop()
{
do
{
await readCanBusSingle();
if (!checkBoxRun.Checked) return;
// Here's where the spacing of 100 ms or whatever happens.
// I made it longer for test.
await Task.Delay(TimeSpan.FromSeconds(1));
}
while (checkBoxRun.Checked);
}
private async Task readCanBusSingle()
{
string text;
if (_busController.TryConnect())
{
BeginInvoke((MethodInvoker)delegate
{
labelIndicator.BackColor = Color.LightGreen;
richTextBox.SelectionFont = new Font(richTextBox.Font, FontStyle.Bold);
richTextBox.AppendText($"{Environment.NewLine}{DateTime.Now}: REQUEST{Environment.NewLine}");
richTextBox.SelectionFont = new Font(richTextBox.Font, FontStyle.Regular);
});
foreach (RequestID requestID in Enum.GetValues(typeof(RequestID)))
{
var packet = new Packet { RequestID = requestID };
// HARWARE CONTROLLER: Push some bytes into TXQueue
_busController.SendReq(unitIndex: 1, packet: packet);
// HARWARE CONTROLLER: Wait for RXQueue bytes to be present
// Often this means a spin (not an await) until the full
// reapi=onse is available in the MCU.
await Task.Run(() =>
{
while(_busController.Busy)
{
Task.Delay(10).Wait();
}
});
if (packet.MockResponse is uint[] bytes)
{
text = string.Join(" ", bytes.Select(_ => _.ToString("X4")));
}
else
{
text = $"{packet.MockResponse}";
}
BeginInvoke((MethodInvoker)delegate
{
switch (packet.RequestID)
{
case RequestID.REQ1:
richTextBox.SelectionColor = Color.Navy;
break;
case RequestID.REQ2:
richTextBox.SelectionColor = Color.DarkGreen;
break;
case RequestID.REQ3:
richTextBox.SelectionColor = Color.Maroon;
break;
default: throw new InvalidOperationException();
}
richTextBox.AppendText($"{text}{Environment.NewLine}");
richTextBox.ScrollToCaret();
});
}
}
else
{
BeginInvoke((MethodInvoker)delegate
{
labelIndicator.BackColor = Color.LightSalmon;
richTextBox.SelectionColor = Color.Red;
richTextBox.AppendText($"{Environment.NewLine}Connection lost{Environment.NewLine}");
richTextBox.ScrollToCaret();
});
}
}
Where:
public enum RequestID { REQ1, REQ2, REQ3 }
public class McuController
{
internal bool TryConnect()
{
switch (Rando.Next(10))
{
case 0:
// The mock controller fails every now and then
return false;
default:
return true;
}
}
internal async void SendReq(int unitIndex, Packet packet)
{
Busy = true;
await packet.SetMockResponse(TimeSpan.FromMilliseconds(Rando.Next(10, 51)));
Busy = false;
}
public static Random Rando { get; } = new Random();
public bool Busy { get; private set; }
}
public class Packet
{
public RequestID RequestID { get; set; }
public int UnitID { get; set; }
// MOCK
public object MockResponse { get; set; }
public async Task SetMockResponse(TimeSpan mockDelay)
{
await Task.Delay(mockDelay);
switch (RequestID)
{
case RequestID.REQ1:
// String
MockResponse =
$"Unit {UnitID}";
break;
case RequestID.REQ2:
// ByteArray
MockResponse =
Enumerable.Range(0, 8)
.Select(_ => (uint)Rando.Next(45000, 55000))
.ToArray();
break;
case RequestID.REQ3:
MockResponse =
mockDelay;
break;
default: throw new InvalidOperationException();
}
}
}