Работа с пулом gprs модемов

116
26 августа 2019, 07:50

Имеется пул gprs модемов, подключенных к COM - портам. Список клиентов, с которыми должны связываться модемы, гораздо больше количества модемов. Поэтому есть два списка - один с наименованием модемов, другой - со списком телефонных номеров,с которыми модемы должны связываться. Опрос производится в двух циклах. 1 Перебор модемов

    private async void Work()
    {
        clsCom eqP;
        while (true)
        {
            for (var j = 0; j < lstCOM.Items.Count; j++)
            {
                //Dispatcher.Invoke(new Action(() => eqP = (clsCom)lstCOM.Items[j]));
                eqP = (clsCom) lstCOM.Items[j];
                if (eqP.Status == "Ожидает")
                {
                    await Task.Run(() => ModemPolling(eqP.Title));
                }
            }
        }
    }
  1. перебор телефонных номеров

    async void ModemPolling(string PortN)
    {
        int Jp = lstModem.Items.Count;
        int n = 0;
        phModel phMod;
        string telN;
        string SNum;
        clsProcess proc;
        do
        {
            for (int j = 0; j < Jp; j++)
            {
                //Dispatcher.Invoke(new Action(() => phMod = (phModel) lstModem.Items[j]));
                phMod = (phModel)lstModem.Items[j];
                if (phMod.Status == 0)
                {
                    Dispatcher.Invoke(new Action(() => ((phModel) lstModem.Items[j]).Status = 1));
                    telN = ((phModel) lstModem.Items[j]).PnoneN;
                    SNum = ((phModel)lstModem.Items[j]).SerialN;
                    proc = new clsProcess();
                    proc.onStr += onProcStr;
                    proc.onEnd += onEndProc;
                    await Task.Run(()=> proc.ModemProc(PortN, telN, SNum));
                }
            }
            n++;
        } while (n < 6);
    }
    

    Здесь clsProcess - класс, в котором производятся все действия с модемом.

proc.onStr += onProcStr; - фиксация событий в этом классе (вызывается при получении сообщений от модема.

proc.onEnd += onEndProc; - фиксация окончания опроса модема (полученные данные заносятся в БД)

proc.ModemProc(PortN, telN, SNum) - непосредственно процедура опроса модема.

Однако, после запуска нормально проходит только первая итерация. Нормально подключаются все свободные модемы, и нормально отрабатывают. Потом или программа наглухо виснет,либо вываливается с ошибкой: "Дескриптор SafeHandle был закрыт'".

Подскажите, в чем моя ошибка?

Answer 1

Написал вам примерчик.

У меня нет модемов, потому пришлось просто промоделировать их работу. По сути же вы можете скачать здесь этот пример и доработать в классе модема метод CallNumberAsync() и заполнение списка номеров телефонов.

Вот класс телефонного номера, у номера может быть 3 типа состояния и событие изменения этого состояния.

public enum NumberStatesType
{
    WaitingCall, //ожидает звонка на него
    Calling, //происходит звонок и соединение с этим номером
    Called //этот номер обзвонен
}
public class PhoneNumber : INotifyPropertyChanged
{
    //ctor
    public PhoneNumber(string number)
    {
        if (String.IsNullOrEmpty(number)) throw new ArgumentException(nameof(number));
        Number = number;
        OutputNumber = Number;
        State = NumberStatesType.WaitingCall;
    }
    /// <summary>
    /// Номер
    /// </summary>
    public string Number { get; private set; }
    /// <summary>
    /// Отображаемое в списке номеров
    /// </summary>
    private string _OutputNumber;
    public string OutputNumber
    {
        get => _OutputNumber;
        set
        {
            _OutputNumber = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(OutputNumber)));
        }
    }
    /// <summary>
    /// Состояние номера
    /// </summary>
    public NumberStatesType State { get; private set; }
    /// <summary>
    /// Событие изменения номера
    /// </summary>
    public event EventHandler<PhoneNumberStateChangedEventArgs> NumberStateChanged;
    //INPC
    public event PropertyChangedEventHandler PropertyChanged;
    /// <summary>
    /// Изменение состояние номера
    /// </summary>
    /// <param name="stateChangedEventArgs"></param>
    public void ChangeNumberState(PhoneNumberStateChangedEventArgs stateChangedEventArgs)
    {
        if (stateChangedEventArgs == null)
            throw new ArgumentNullException(nameof(stateChangedEventArgs));
        //изменяем состояние
        this.State = stateChangedEventArgs.NewState;
        //вызываем событие
        NumberStateChanged?.Invoke(this, stateChangedEventArgs);
    }
}

Вот класс модема, идея в том, что у модема может быть 2 состояния: свободен и занят, и событие ModemStateChanged, подписавшись на которое можно получать процесс работы модема.

public enum ModemStatesType
{
    Free, //модем свободен
    Busy, //модем занят
}
public class Modem
{
    //для моделирования работы модема, в реальном прил. не нужно!
    private static Random _random = new Random();
    //ctor
    public Modem(string name)
    {
        if (String.IsNullOrEmpty(name)) throw new ArgumentException(nameof(name));
        Name = name;
        State = ModemStatesType.Free;
    }
    /// <summary>
    /// Название модема
    /// </summary>
    public string Name { get; private set; }
    /// <summary>
    /// Состояние модема
    /// </summary>
    public ModemStatesType State { get; private set; }
    /// <summary>
    /// Событие изменения состояния модема
    /// </summary>
    public event EventHandler<ModemStateChangedEventArgs> ModemStateChanged;
    /// <summary>
    /// Изменение состояния модема
    /// </summary>
    /// <param name="stateChangedEventArgs"></param>
    public void ChangeModemState(ModemStateChangedEventArgs stateChangedEventArgs)
    {
        if (stateChangedEventArgs == null)
            throw new ArgumentNullException(nameof(stateChangedEventArgs));
        //изменяем состояние
        this.State = stateChangedEventArgs.NewState;
        //вызываем событие
        ModemStateChanged?.Invoke(this, stateChangedEventArgs);
    }
    /// <summary>
    /// Звонок модема на необходимый номер
    /// </summary>
    /// <param name="number"></param>
    /// <returns></returns>
    public async Task CallNumberAsync(PhoneNumber number)
    {
        if (number == null) throw new ArgumentNullException(nameof(number));
        //изменяем статус у номера
        var numberStateArgs =
            new PhoneNumberStateChangedEventArgs($"{number.Number} набирается...",
            number.State, NumberStatesType.Calling);
        number.ChangeNumberState(numberStateArgs);
        //изменяем статус у модема
        var modemStatusArgs =
            new ModemStateChangedEventArgs($"{this.Name} набирает {number.Number}",
            this.State, ModemStatesType.Busy);
        this.ChangeModemState(modemStatusArgs);
        //>>>Начало моделирования работы модема
        //типа набираем номер и ждем ответа
        await Task.Delay(TimeSpan.FromSeconds(1));
        //номер может быть занят
        bool numberIsBusy = _random.Next(10) % 2 == 0 ? true : false;
        if (numberIsBusy)
        {
            //изменяем статус у номера
            numberStateArgs =
                new PhoneNumberStateChangedEventArgs($"{number.Number} занят!",
                number.State, NumberStatesType.WaitingCall);
            number.ChangeNumberState(numberStateArgs);
            //изменяем статус у модема
            modemStatusArgs =
                new ModemStateChangedEventArgs($"{this.Name} номер {number.Number} занят!",
                this.State, ModemStatesType.Free);
            this.ChangeModemState(modemStatusArgs);
            //выходим
            return;
        }
        //типа соединяемся
        //изменяем статус у номера
        numberStateArgs =
            new PhoneNumberStateChangedEventArgs($"{number.Number} соединение...",
            number.State, NumberStatesType.Calling);
        number.ChangeNumberState(numberStateArgs);
        //изменяем статус у модема
        modemStatusArgs =
            new ModemStateChangedEventArgs($"{this.Name} соединяется по {number.Number}...",
            this.State, ModemStatesType.Busy);
        this.ChangeModemState(modemStatusArgs);
        await Task.Delay(TimeSpan.FromSeconds(1));
        //типа соединились и идет приемо-передача данных
        //изменяем статус у номера
        numberStateArgs =
            new PhoneNumberStateChangedEventArgs($"{number.Number} cоединение установлено.",
            number.State, NumberStatesType.Calling);
        number.ChangeNumberState(numberStateArgs);
        //изменяем статус у модема
        modemStatusArgs =
            new ModemStateChangedEventArgs($"{this.Name} соединение установлено по {number.Number}.",
            this.State, ModemStatesType.Busy);
        this.ChangeModemState(modemStatusArgs);
        await Task.Delay(TimeSpan.FromSeconds(3));
        //типа приемо-передача данных закончена
        //изменяем статус у номера
        numberStateArgs =
            new PhoneNumberStateChangedEventArgs($"{number.Number} обзвонен!",
            number.State, NumberStatesType.Called);
        number.ChangeNumberState(numberStateArgs);
        //изменяем статус у модема
        modemStatusArgs =
            new ModemStateChangedEventArgs($"{this.Name} звонок закончен по {number.Number}.",
            this.State, ModemStatesType.Free);
        this.ChangeModemState(modemStatusArgs);
        //<<<Конец моделирования работы модема
    }
}

Вот кодбихайнд формы

public partial class MainView : Form, IMainView
{
    private BindingSource _bsModems = new BindingSource();
    private BindingSource _bsPhoneNumbers = new BindingSource();
    private BindingSource _bsLogs = new BindingSource();
    private BindingSource _bsStartCalling = new BindingSource();
    private BindingSource _bsCancelCalling = new BindingSource();
    public MainView()
    {
        InitializeComponent();
        SetBindings();
        SetFirstLogRecord();
    }
    /// <summary>
    /// Установка привязок
    /// </summary>
    private void SetBindings()
    {
        _bsModems.DataSource = typeof(List<Modem>);
        _listBoxModems.DataSource = _bsModems;
        _listBoxModems.DisplayMember = nameof(Modem.Name);
        _bsPhoneNumbers.DataSource = typeof(List<PhoneNumber>);
        _listBoxPhoneNumbers.DataSource = _bsPhoneNumbers;
        _listBoxPhoneNumbers.DisplayMember = nameof(PhoneNumber.OutputNumber);
        _bsLogs.DataSource = typeof(List<LogRecord>);
        _listBoxLogs.DataSource = _bsLogs;
        _listBoxLogs.DisplayMember = nameof(LogRecord.Message);
        _bsStartCalling.DataSource = typeof(ButtonEventHandler);
        _buttonStartCalling.DataBindings.Add("Enabled", _bsStartCalling,
            nameof(ButtonEventHandler.Enabled));
        _bsCancelCalling.DataSource = typeof(ButtonEventHandler);
        _buttonCancelCalling.DataBindings.Add("Enabled", _bsCancelCalling,
            nameof(ButtonEventHandler.Enabled));
    }
    /// <summary>
    /// Добавление начальных записей в журнал
    /// </summary>
    private void SetFirstLogRecord()
    {
        var logs = new List<LogRecord> { new LogRecord(1, "Программа запущена") };
        _bsLogs.DataSource = logs;
    }
    /// <summary>
    /// Добавление записи в журнал
    /// </summary>
    /// <param name="logMessage"></param>
    public void AddLogRecord(string logMessage)
    {
        if (String.IsNullOrEmpty(logMessage)) throw new ArgumentNullException(nameof(logMessage));
        int nextId = _bsLogs.Count + 1;
        _bsLogs.Add(new LogRecord(nextId, logMessage));
        _bsLogs.MoveLast();
    }
    /// <summary>
    /// Изменение отображаемого состояния номера в списке номеров
    /// </summary>
    /// <param name="number"></param>
    /// <param name="e"></param>
    public void ChangeNumberOutput(PhoneNumber number, PhoneNumberStateChangedEventArgs e)
    {
        if (number == null) throw new ArgumentNullException(nameof(number));
        if (e == null) throw new ArgumentNullException("Аргумент состояния пуст!");
        _bsPhoneNumbers.Position = _bsPhoneNumbers.IndexOf(number);
        number.OutputNumber = e.Message;
    }
    /// <summary>
    /// Модемы
    /// </summary>
    public List<Modem> Modems
    {
        get => _bsModems.List as List<Modem>;
        set
        {
            _bsModems.Clear();
            _bsModems.DataSource = value;
        }
    }
    /// <summary>
    /// Телефонные номера
    /// </summary>
    public List<PhoneNumber> PhoneNumbers
    {
        get => _bsPhoneNumbers.List as List<PhoneNumber>;
        set
        {
            _bsPhoneNumbers.Clear();
            _bsPhoneNumbers.DataSource = value;
        }
    }
    /// <summary>
    /// Кнопка запуска
    /// </summary>
    public ButtonEventHandler StartCalling
    {
        get => _bsStartCalling.Current as ButtonEventHandler;
        set
        {
            if (_bsStartCalling.Count > 0) return;
            _bsStartCalling.Add(value);
            _buttonStartCalling.Click += value.Handler;
            value.CheckEnabled();
        }
    }
    /// <summary>
    /// Кнопка отмены
    /// </summary>
    public ButtonEventHandler CancelCalling
    {
        get => _bsCancelCalling.Current as ButtonEventHandler;
        set
        {
            if (_bsCancelCalling.Count > 0) return;
            _bsCancelCalling.Add(value);
            _buttonCancelCalling.Click += value.Handler;
            value.CheckEnabled();
        }
    }
}

А это презентер

public class MainPresenter : IMainPresenter
{
    private bool _isCalling = false;
    public IAppController Controller { get; private set; }
    public IMainView View { get; private set; }
    //ctor
    public MainPresenter(IAppController controller, IMainView view)
    {
        Controller = controller ?? throw new ArgumentNullException(nameof(controller));
        View = view ?? throw new ArgumentNullException(nameof(view));
        View.StartCalling = new ButtonEventHandler(OnStartCalling, CanStartCalling);
        View.CancelCalling = new ButtonEventHandler(OnCancelCalling, CanCancelCalling);
        //подгружаем данные
        LoadData();
    }
    private void LoadData()
    {
        View.Modems = Controller.DataContext.GetModems();
        View.PhoneNumbers = Controller.DataContext.GetPhoneNumbers();
        View.AddLogRecord($"В работе всего модемов: {View.Modems.Count}");
        View.AddLogRecord($"Всего телефонных номеров: {View.PhoneNumbers.Count}");
    }
    /// <summary>
    /// Доступность Кнопки отмены обзвона
    /// </summary>
    /// <returns></returns>
    private bool CanCancelCalling()
    {
        return _isCalling;
    }
    /// <summary>
    /// Кнопка отмены обзвона
    /// </summary>
    private void OnCancelCalling()
    {
        _isCalling = false;
        View.CancelCalling.CheckEnabled();
    }
    /// <summary>
    /// Доступность кнопки запуска обзвона
    /// </summary>
    /// <returns></returns>
    private bool CanStartCalling()
    {
        return !_isCalling;
    }
    /// <summary>
    /// Кнопка запуска обзвона
    /// </summary>
    private async void OnStartCalling()
    {
        //флаг обзвона
        _isCalling = true;
        //кнопки
        View.CancelCalling.CheckEnabled();
        View.StartCalling.CheckEnabled();
        //подписка на события
        SubscribeToEventsInModemAndNumbers();
        //пока есть в коллекции необзвоненные телефоны
        while (View.PhoneNumbers.Any(n => n.State != NumberStatesType.Called))
        {
            //проверяем флаг обзвона
            if (!_isCalling)
            {
                View.AddLogRecord("Обзвон прерван!");
                break;
            }
            //создаем для каждого модема задачу обзвона
            var tasks = new List<Task>();
            foreach (var modem in View.Modems)
            {
                tasks.Add(Calling(modem));
            }
            //ждем завершения всех задач обзвона
            await Task.WhenAll(tasks);
        }
        //отписка от событий
        UnsubscribeToEventsInModemAndNumbers();
        //если обзвон не был отменен, значит он закончен
        if (_isCalling)
        {
            View.AddLogRecord("Обзвон закончен!");
            _isCalling = false;
        }
        //кнопки
        View.CancelCalling.CheckEnabled();
        View.StartCalling.CheckEnabled();
    }
    /// <summary>
    /// Звонок по номеру через выбранный модем
    /// </summary>
    /// <param name="modem"></param>
    /// <returns></returns>
    private async Task Calling(Modem modem)
    {
        //находим свободный номер
        var number = View.PhoneNumbers.FirstOrDefault(n => n.State == NumberStatesType.WaitingCall);
        if (number == null) return;
        //звоним на номер
        await modem.CallNumberAsync(number);
    }
    /// <summary>
    /// Подписка на события от модемов и номеров
    /// </summary>
    private void SubscribeToEventsInModemAndNumbers()
    {
        //подписываемся на события модемов
        foreach (var modem in View.Modems)
        {
            modem.ModemStateChanged += Modem_ModemStateChanged;
        }
        //подписываемся на события номеров
        foreach (var number in View.PhoneNumbers)
        {
            number.NumberStateChanged += Number_NumberStateChanged;
        }
    }
    /// <summary>
    /// Отписка от событий от модемов и номеров
    /// </summary>
    private void UnsubscribeToEventsInModemAndNumbers()
    {
        //отписываемся на события модемов
        foreach (var modem in View.Modems)
        {
            modem.ModemStateChanged -= Modem_ModemStateChanged;
        }
        //отписываемся на события номеров
        foreach (var number in View.PhoneNumbers)
        {
            number.NumberStateChanged -= Number_NumberStateChanged;
        }
    }
    /// <summary>
    /// Обработчик события изменения состояния у номера
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Number_NumberStateChanged(object sender, PhoneNumberStateChangedEventArgs e)
    {
        var number = sender as PhoneNumber;
        //отображаем в списке номеров состояние этого номера
        View.ChangeNumberOutput(number, e);
    }
    /// <summary>
    /// Обработчик события изменения состояния у модема
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void Modem_ModemStateChanged(object sender, ModemStateChangedEventArgs e)
    {
        //пишем в лог
        View.AddLogRecord(e.Message);
    }
}
READ ALSO
Помогите разобраться с принтером Argox

Помогите разобраться с принтером Argox

Для печати на принтере Argox, подключенном к USB-порту использую команду

114
Как достать строку из локализации OS?

Как достать строку из локализации OS?

В операционной системе есть куча текста который можно локализироватьДопустим, мне нужно узнать как называется окно "Save As" в локализации на даном...

143
Как изменить путь к БД в C#

Как изменить путь к БД в C#

Работаю над проектом в Visual Studio C#В проекте есть несколько форм и в каждом из них я использую oledbconnection и oledbadapter для подключения БД

126
Условный маппинг в AutoMapper

Условный маппинг в AutoMapper

Допустим, есть вот такое семейство классов:

118