Starting again with Threads
After getting Activation By Personalisation(ABP) and Over The Air Activation(OTAA) working on my RAK3172 test rig I was looking at the code and SerialDataReceivedEventHandler was really ugly.
After some experimentation in the BreakOutSerial project I decided to reimplement the RAK3172 command processing. In the new code a Thread reads lines of text from the SerialPort and processes them. I have replaced the Join and Send(Confirmed) methods with ones that block only while the command are sent to the RAK3172. Then, when completed the OnJoinCompletion or OnMessagesConfirmation event handlers are called.
private Result SendCommand(string command)
{
if (command == null)
{
throw new ArgumentNullException(nameof(command));
}
if (command == string.Empty)
{
throw new ArgumentException($"command invalid length cannot be empty", nameof(command));
}
serialDevice.ReadTimeout = (int)CommandTimeoutDefault.TotalMilliseconds;
serialDevice.WriteLine(command);
this.atExpectedEvent.Reset();
if (!this.atExpectedEvent.WaitOne((int)CommandTimeoutDefault.TotalMilliseconds, false))
return Result.Timeout;
return result;
}
public void SerialPortProcessor()
{
string line;
while (true)
{
this.serialDevice.ReadTimeout = -1;
Debug.WriteLine("ReadLine before");
line = serialDevice.ReadLine();
Debug.WriteLine($"ReadLine after:{line}");
// check for +EVT:JOINED
if (line.StartsWith("+EVT:JOINED"))
{
OnJoinCompletion?.Invoke(true);
continue;
}
if (line.StartsWith("+EVT:JOIN FAILED"))
{
OnJoinCompletion?.Invoke(false);
continue;
}
if (line.StartsWith("+EVT:SEND CONFIRMED OK"))
{
OnMessageConfirmation?.Invoke();
continue;
}
// Check for A/B/C downlink message
if (line.StartsWith("+EVT:RX_1") || line.StartsWith("+EVT:RX_2") || line.StartsWith("+EVT:RX_3") || line.StartsWith("+EVT:RX_C"))
{
string[] fields1 = line.Split(' ', ',');
int rssi = int.Parse(fields1[3]);
int snr = int.Parse(fields1[6]);
line = serialDevice.ReadLine();
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss} UNICAST :{line}");
line = serialDevice.ReadLine();
Console.WriteLine($"{DateTime.UtcNow:HH:mm:ss} Payload:{line}");
string[] fields2 = line.Split(':');
int port = int.Parse(fields2[1]);
string payload = fields2[2];
OnReceiveMessage?.Invoke(port, rssi, snr, payload);
continue;
}
try
{
this.serialDevice.ReadTimeout = 3000;
Debug.WriteLine("ReadLine Result");
line = serialDevice.ReadLine();
Debug.WriteLine($"ReadLine Result after:{line}");
switch (line)
{
case "OK":
result = Result.Success;
break;
case "AT_ERROR":
result = Result.Error;
break;
case "AT_PARAM_ERROR":
result = Result.ParameterError;
break;
case "AT_BUSY_ERROR":
result = Result.BusyError;
break;
case "AT_TEST_PARAM_OVERFLOW":
result = Result.ParameterOverflow;
break;
case "AT_NO_NETWORK_JOINED":
result = Result.NotJoined;
break;
case "AT_RX_ERROR":
result = Result.ReceiveError;
break;
case "AT_DUTYCYLE_RESTRICTED":
result = Result.DutyCycleRestricted;
break;
default:
result = Result.Undefined;
break;
}
}
catch (TimeoutException)
{
result = Result.Timeout;
}
atExpectedEvent.Set();
}
The code is not suitable for production but it confirmed my thread based approach works. I need to add code to shutdown the message processing thread in a controlled way, support for Class B & C devices, replace the OnJoinCompletionHandler timer magic numbers and soak test for 5-7 days.
In the Visual Studio 2019 debug output I could see messages getting sent and then after a short delay they were visible in the TTN console.