StaudtEngineering / LidaRx

C# / .NET Reactive Extensions (Rx) for LIDAR scanners
GNU Lesser General Public License v3.0
8 stars 1 forks source link

Always "0 points in [400;1000]mm range per second" #1

Closed Soeren3003 closed 7 years ago

Soeren3003 commented 7 years ago

I have copied your code, but it doesnt work really ... The only thing which is working is the speed setup.

"0 points in [400;1000]mm range per second"

Sometimes the programm throws the following exception by running the connect method:

Ausnahme ausgelöst: Staudt.Engineering.LidaRx.Drivers.Sweep.Exceptions.SweepProtocolErrorException in mscorlib.dll

My code looks like these:

public partial class Form1 : Form
{
    public Form1()
    {
    InitializeComponent();

            Test();
        }

        static async void Test()
        {
            using (var sweep = new SweepScanner("COM4"))
            {
                await sweep.ConnectAsync();
                await sweep.SetMotorSpeedAsync(SweepMotorSpeed.Speed10Hz);

                await sweep.SetSamplingRateAsync(SweepSampleRate.SampleRate1000);

                await sweep.StartScanAsync();

                // log errors to the console
                sweep.OnlyStatusEvents(LidarStatusLevel.Error).Subscribe(ev =>
                {
                    Console.WriteLine($"Error: {ev.Message}");
                });

                // using the data stream for multiple subscriptions
                var pointsBetween400and1000mm = sweep
                    .OnlyLidarPoints()                    // filter away all those status messages
                    .Where(pt => pt.Distance > 400)        // unit is mm
                    .Where(pt => pt.Distance <= 1000);    // unit is mm

                // buffer in 1second long samples
                pointsBetween400and1000mm
                    .Buffer(TimeSpan.FromSeconds(1000))
                    .Subscribe(buffer =>
                    {
                        Console.WriteLine($"{buffer.Count} points in [400;1000]mm range per second");
                    });

                // this narrows down the point stream to points in the -45 to +45 degree range
                pointsBetween400and1000mm
                    .PointsInAzimuthRange(-45, 45)
                    .Subscribe(pt =>
                    {
                        // write the points to disk?!
                    });

                // buffer the lidar points in scans
                sweep.OnlyLidarPoints()
                    .BufferByScan()
                    .Subscribe(scan =>
                    {
                        Console.WriteLine($"Got {scan.Count} points for scan {scan.Scan}");
                        Console.WriteLine($"Most distant point: {scan.Points.Max(pt => pt.Distance)}mm");
                        Console.WriteLine($"Closest point: {scan.Points.Min(pt => pt.Distance)}mm");
                    });

                Console.ReadLine();      // wait here 'till user hits the enter key
                await sweep.StopScanAsync();
            }
        }
    }
pysco68 commented 7 years ago

Hello Soeren,

so, basically the issue is that you just took the console app code and dropped it into a Winforms context, where a few things behave differently. Notably Console.ReadLine() which does return immediately; this means that you start, then stop scanning in just a few instructions.

So either you'll have to switch to a console application or split things in a way that they work in the Win-forms context (which would be put one half of the code [setup + subscriptions] in - for ex. - the load handler of the form, and then put the stop scan call and disposal stuff into the form closed handler)

Regarding the SweepProtocolErrorException I can only guess that it happens when you stop your application the hard way (SIGKILL) before the driver could issue a stop scan signal to Sweep. The Scanner doesn't stop sending data then and when the driver tries to send setup commands it doesn't get the expected answer.

Soeren3003 commented 7 years ago

Oh well, thats make sense, but now i copied my method into a console-app and it doesent run, too. The programm runs only to await sweep.ConnectAsync(); and after that it breaks up without a error message or something else. The COM4 is the port with is showing me in the sweep-demo.

`class Program { static void Main(string[] args) { Test(); } static async void Test() { using (var sweep = new SweepScanner("COM4")) { await sweep.ConnectAsync(); await sweep.SetMotorSpeedAsync(SweepMotorSpeed.Speed10Hz); await sweep.SetSamplingRateAsync(SweepSampleRate.SampleRate1000);

            await sweep.StartScanAsync();

            // using the data stream for multiple subscriptions
            var pointsBetween400and1000mm = sweep
                .OnlyLidarPoints()                  // filter away all those status messages
                .Where(pt => pt.Distance > 400)     // unit is mm
                .Where(pt => pt.Distance <= 1000);  // unit is mm

            // buffer in 1second long samples
            pointsBetween400and1000mm
                .Buffer(TimeSpan.FromSeconds(1000))
                .Subscribe(buffer =>
                {
                    Console.WriteLine($"{buffer.Count} points in [400;1000]mm range per second");
                });

            // this narrows down the point stream to points in the -45 to +45 degree range
            pointsBetween400and1000mm
                .PointsInAzimuthRange(-45, 45)
                .Subscribe(pt =>
                {
                        // write the points to disk?!
                    });

            // buffer the lidar points in scans
            sweep.OnlyLidarPoints()
                .BufferByScan()
                .Subscribe(scan =>
                {
                    Console.WriteLine($"Got {scan.Count} points for scan {scan.Scan}");
                    Console.WriteLine($"Most distant point: {scan.Points.Max(pt => pt.Distance)}mm");
                    Console.WriteLine($"Closest point: {scan.Points.Min(pt => pt.Distance)}mm");
                });

            sweep.StopScan();
        }
    }
}`
pysco68 commented 7 years ago

Hi again,

you're running into the same issue as previously - I guess you're not really familiar with the async/await stuff(?)

When you call an async method the compiler basically starts a new thread (in which the callee is executed) then immediately returns, so basically you call to Test() does return immediately, then main() ends and the program finishes.

One possibility would be to have the signature of Test() be static async Task Test() (note the return type) and then call Test().Wait(); which - even if it's not really clean code - does what you want. Btw. when you see async void in a method signature which is not an event handler you're probably doing something wrong! It's actually pretty dangerous and can lead to hard to debug errors as exceptions can't be properly caught from the calling side etc...

the async await stuff is really cool, but there are a few potentially confusing things happening under the covers, so you might want to get familiar with TPL and await/async.

Here's some good reading:

https://docs.microsoft.com/en-us/dotnet/csharp/async https://codeblog.jonskeet.uk/category/eduasync/

Good luck!