Reputation: 75
I am building a windows desktop app using .NET MAUI. The app connects to a LoRa scanner utilizing SerialPort
. The scanner scan for LoRa networks and gets data from a dummy LoRa transmitter every second for around 8 times. The Scanner reads the data transmitted which contains the network name, signal strength, longitude, latitude etc. In my UI I have a section dedicated for Network names and I have been trying to populate the list with the network name/s from the SerialPort
buffer data.
The issue that I am facing, is that the network name is only showing once or sometimes not appearing at all, when in fact it has to show about 8 times when I run the scan. In my case now it is one network with the same network name. However, if I have multiple dummy LoRa transmitters, only one or none will show in the UI.
I am currently making the connection in the code behind of the UI (planning to move it to a separate file when I get it to work). Also I am using MainThread.BeingInvokeOnMainThread
to show the names in the xaml list (Not Working). Here is what I did so far in the myPage.xaml.cs
:
namespace Scanner_MAUI.Pages;
public partial class RealTimeData : ContentPage
{
private SerialPort serialPort;
public RealTimeData()
{
InitializeComponent();
BindingContext = this;
NetworkNames = new ObservableCollection<string>();
//NetworkListView.ItemsSource = NetworkNames;
}
class ScannerData
{
public string Name { get; set; }
//...
}
private void StartMenuItem_Clicked(object sender, EventArgs e)
{
// TODO: Connect to the LoRa scanner and start scanning
ConnectToScanner();
}
public void ConnectToScanner()
{
// Clear the NetworkNames list before connecting to the scanner
//NetworkNames.Clear();
try
{
serialPort = new SerialPort("COMx", baudrate);
serialPort.Open();
Debug.WriteLine("Serial Port conn created");
Debug.WriteLine("Serial Port Is Open: " + serialPort.IsOpen);
var data = new byte[] { (byte)'1', 13 };
serialPort.Write(data, 0, data.Length);
// TODO: Handle the scanner's output and display the information in your app
serialPort.DataReceived += SerialPort_DataReceived;
//Debug.WriteLine("test");
}
catch (Exception ex)
{
// Handle the exception
Debug.WriteLine($"Failed to connect to the scanner: {ex.Message}");
}
}
public void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
//NetworkNames = new ObservableCollection<string>();
//NetworkNames.Clear();
try
{
while (serialPort.BytesToRead > 0)
{
string DataIn = serialPort.ReadLine();
//Debug.WriteLine("Data received from the scanner: " + DataIn);
// Process the received data and extract the fields
//ScannerData scannerData = ProcessReceivedData(DataIn);
//Task.Delay(1000).Wait();
foreach (string name in NetworkNames)
{
Debug.WriteLine($"Name: {name}");
}
//ProcessReceivedData(DataIn);
MainThread.BeginInvokeOnMainThread(() =>
{
ProcessReceivedData(DataIn);
NetworkListView.ItemsSource = NetworkNames;
});
}
}
catch (OperationCanceledException)
{
// Handle the cancellation if needed
Debug.WriteLine("Reading data from the scanner was canceled.");
}
catch (Exception ex)
{
// Handle other exceptions
Debug.WriteLine($"Failed to read data from the scanner: {ex.Message}");
}
}
public ObservableCollection<string> NetworkNames { get; set; } = new ObservableCollection<string>();
private ScannerData ProcessReceivedData(string data)
{
ScannerData scannerData = new ScannerData();
// Parse and extract relevant information from the data string
string[] fields = data.Split(new string[] { ", " }, StringSplitOptions.None);
Debug.WriteLine($"Received data fields count: {fields.Length}");
//Debug.WriteLine($"Received data fields: {string.Join(", ", fields)}");
if (fields.Length >= 8)
{
scannerData.Message = fields[0].Split(':')[1].Trim();
string input = fields[1];
string startMarker = "b'";
string endMarker = "'";
int startIndex = input.IndexOf(startMarker) + startMarker.Length;
int endIndex = input.IndexOf(endMarker, startIndex);
if (startIndex >= 0 && endIndex >= 0)
{
string extractedString = input.Substring(startIndex, endIndex - startIndex);
scannerData.Name = extractedString;
// Add non-null and non-empty names to the collection
//if (!string.IsNullOrEmpty(scannerData.Name))
//{
NetworkNames.Add(extractedString);
//}
//NetworkNames.Add(scannerData.Name); // Add network name to the list
}
....More code
}
else
{
Debug.WriteLine("Insufficient fields in the data string.");
}
return scannerData;
}
private double? ParseNullableDouble(string value)
{
if (double.TryParse(value, out double result))
return result;
return null;
}
Finally here is the list in the xaml file:
<ListView x:Name="NetworkListView" ItemsSource="{Binding NetworkNames}">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is the results that I get in this image below:
In the case of this image the networks' names should be displayed in the left column. In the console the names are shown just fine.
For some reason sometimes one name is shown in the left column.
What approach I should take to make it possible to show the network names while the scanning is going? What am I doing wrong!
Please note I am a beginner. Your help is much appreciated!
Upvotes: 1
Views: 635
Reputation: 75
Late answer. Thanks everyone for your tips it Helped.
I did the following changes and now the expected behavior works.
in the SerialPortConn file I added an ObservableCollection
and added the names to it from the MainThread
in the SerialPort_DataReceived (located in the same file) function:
public void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
while (serialPort.BytesToRead > 0)
{
string DataIn = serialPort.ReadLine();
//Debug.WriteLine("Data received from the scanner: " + DataIn);
// Process the received data and extract the fields
Network data = ProcessReceivedData(DataIn);
MainThread.BeginInvokeOnMainThread(() =>
{
// Check if the data name is not empty or null
if (!string.IsNullOrEmpty(data.Name))
{
// Find the existing network with the same name, if it exists
Network existingNetwork = NetworkNames.FirstOrDefault(network => network.Name == data.Name);
if (existingNetwork != null)
{
existingNetwork.RSSI = data.RSSI;
existingNetwork.Name = data.Name;
Debug.WriteLine("strength: " + strength);
Debug.WriteLine("existingNetwork: " + existingNetwork.Name + " " + existingNetwork.RSSI);
}
else
{
// Create a new network with the name and initialize its RSSIList with the current RSSI value
NetworkNames.Add(new Network { Name = data.Name, RSSI = data.RSSI });
// Print the network name and its RSSIList for debugging purposes
foreach (Network network in NetworkNames)
{
Debug.WriteLine("NetworkName: " + network.Name + " " + network.RSSI);
}
foreach (Network network in NetworkValues)
{
Debug.WriteLine("NetworkValues: " + network.Name + " " + network.RSSI);
}
}
});
}
}
In the Content page I added the following line assigning the NetworkNames
ObservableCollection
to the listView
in the xaml
file.
NetworkListView.ItemsSource = scannerConn.NetworkNames;
And finally in the xaml file I took away the ItemsSource
as it is defined already in the behind code:
<ListView x:Name="NetworkListView">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Upvotes: 0