HamBook – Rev 38

Subversion Repositories:
Rev:
using HamBook.Radios.Generic;
using HamBook.Radios;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Serilog;
using HamBook.Properties;
using Serilog.Events;
using System.Media;
using System.Reflection;
using RJCP.IO.Ports;
using System.Diagnostics;
using System.IO;
using Toasts;
using System.Drawing;
using Configuration;
using System.Security.Cryptography;
using Org.BouncyCastle.Crypto.Engines;

namespace HamBook
{
    public class BandScan
    {
        private CancellationTokenSource _scanningCancellationTokenSource;
        private CancellationToken _scanningCancellationToken;
        private Thread _scanThread;
        private CatAssemblies _catAssemblies;
        private int _minFrequency;
        private int _maxFrequency;
        private SerialPortStream _serialPort;
        private Configuration.Configuration _configuration;
        private int _currentFrequency;

        private BandScan() 
        {
        }

        public BandScan(CatAssemblies catAssemblies, int min, int max, SerialPortStream serialPort, Configuration.Configuration configuration) : this()
        {
            _catAssemblies = catAssemblies;
            _minFrequency = min;
            _maxFrequency = max;
            _serialPort = serialPort;
            _configuration = configuration;

        }

        public void Start(int stepFrequency, int pauseTime, int pauseDetectTime, bool autoTune = false)
        {
            if (_scanThread != null)
            {
                if (!_scanningCancellationToken.IsCancellationRequested)
                {
                    _scanningCancellationTokenSource.Cancel();
                }

                _scanThread.Join();
                _scanThread = null;
            }

            _scanningCancellationTokenSource = new CancellationTokenSource();
            _scanningCancellationToken = _scanningCancellationTokenSource.Token;

            _scanThread = new Thread(new ParameterizedThreadStart(Scan));
            _scanThread.Start(new BandScanParameters(stepFrequency, pauseTime, pauseDetectTime, autoTune));
        }

        public async Task Stop()
        {
            if (_scanThread != null)
            {
                if (!_scanningCancellationToken.IsCancellationRequested)
                {
                    _scanningCancellationTokenSource.Cancel();
                }

                _scanThread.Join();
                _scanThread = null;
            }
        }

        private async void Scan(object obj)
        {
            var bandScanParameters = (BandScanParameters)obj;

            if (!_serialPort.IsOpen)
            {
                _serialPort.Open();
            }

            _serialPort.DiscardInBuffer();

            try
            {

                _currentFrequency = _minFrequency;

                do
                {
                    var taskCompletionSource = new TaskCompletionSource<bool>();

                    using (var soundPlayer = new SoundPlayer(Assembly.GetExecutingAssembly().GetManifestResourceStream("HamBook.Effects.pot.wav")))
                    {
                        soundPlayer.Play();
                        await _catAssemblies.CatWriteAsync<int>("FA", new object[] { _currentFrequency }, _scanningCancellationToken);
                    }

                    if (bandScanParameters.AutoTune)
                    {
                        await _catAssemblies.CatWriteAsync<TunerState>("AC", new object[] { TunerState.TUNER_ON }, _scanningCancellationToken);

                        await _catAssemblies.CatWriteAsync<TunerState>("AC", new object[] { TunerState.TUNING_START }, _scanningCancellationToken);

                        do
                        {
                            await Task.Delay(TimeSpan.FromSeconds(1), _scanningCancellationToken);

                            try
                            {
                                if (await _catAssemblies.CatReadAsync<TunerState>("AC", new object[] { }, _scanningCancellationToken) != TunerState.TUNING_START)
                                {
                                    break;
                                }
                            }
                            catch (Exception)
                            {
                                // retry
                            }

                        } while (!_scanningCancellationToken.IsCancellationRequested);
                    }

                    _catAssemblies.CatWrite<InformationState>("AI", new object[] { InformationState.ON });

                    try
                    {
                        using (var memoryStream = new MemoryStream())
                        {

#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
                            Task.Delay(TimeSpan.FromSeconds(bandScanParameters.PauseTime), _scanningCancellationToken)
                                .ContinueWith(_ => taskCompletionSource.TrySetResult(true), CancellationToken.None);
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed

                            await taskCompletionSource.Task;

                            await _serialPort.CopyToAsync(memoryStream, _serialPort.BytesToRead, _scanningCancellationToken);

                            memoryStream.Position = 0L;

                            var result = Encoding.ASCII.GetString(memoryStream.ToArray());

                            Parallel.ForEach(result.Split(Radios.Generic.Constants.EOT), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, async (split, state) =>
                            {
                                if (string.IsNullOrEmpty(split))
                                {
                                    state.Stop();
                                }

                                try
                                {
                                    switch (_catAssemblies.CatParse<BusyState>("BY", new object[] { $"{split}{Radios.Generic.Constants.EOT}" }))
                                    {
                                        case BusyState.ON:
                                            if (_configuration.Notifications.TryGetNotificationState(NotificationType.SignalScanDetect, out var notificationState))
                                            {
                                                state.Stop();
                                            }

                                            var toastFrom = new ToastForm(
                                                $"{Resources.Signal_detected_during_scan}",
                                                $"{Resources.Frequency}: {_currentFrequency}Hz",
                                                notificationState.LingerTime,
                                                Constants.AssemblyIcon);

                                            toastFrom.Show();

                                            await Task.Delay(TimeSpan.FromSeconds(bandScanParameters.PauseDetectTime), _scanningCancellationToken);
                                            state.Stop();
                                            break;
                                    }
                                }
                                catch (TargetInvocationException exception) when (exception.InnerException is UnmatchedRadioResponseException)
                                {
                                    // suppress
                                }
                                catch (Exception exception)
                                {
                                    Log.Warning(exception, Resources.Error_encountered_while_scanning_for_signal);
                                }
                            });
                        }
                    }
                    catch(Exception exception)
                    {
                        Log.Warning(exception, Resources.Error_encountered_while_scanning_for_signal);
                    }
                    finally
                    {
                        _catAssemblies.CatWrite<InformationState>("AI", new object[] { InformationState.OFF });
                    }

                    _currentFrequency = _currentFrequency + bandScanParameters.StepFrequency;
                    if (_currentFrequency > _maxFrequency)
                    {
                        _currentFrequency = _minFrequency;
                    }
                    if (_currentFrequency < _minFrequency)
                    {
                        _currentFrequency = _minFrequency;
                    }

                } while (!_scanningCancellationToken.IsCancellationRequested);
            }
            catch (Exception exception)
            {
                Log.Error(exception, Resources.Scanning_aborted);
            }
            finally
            {
                if (_serialPort.IsOpen)
                {
                    _serialPort.Close();

                    _serialPort.DiscardInBuffer();
                }
            }
        }
    }
}