Имеется пул 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));
}
}
}
}
перебор телефонных номеров
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 был закрыт'".
Подскажите, в чем моя ошибка?
Написал вам примерчик.
У меня нет модемов, потому пришлось просто промоделировать их работу. По сути же вы можете скачать здесь этот пример и доработать в классе модема метод 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);
}
}
Виртуальный выделенный сервер (VDS) становится отличным выбором
Для печати на принтере Argox, подключенном к USB-порту использую команду
В операционной системе есть куча текста который можно локализироватьДопустим, мне нужно узнать как называется окно "Save As" в локализации на даном...
Работаю над проектом в Visual Studio C#В проекте есть несколько форм и в каждом из них я использую oledbconnection и oledbadapter для подключения БД