Вверх ↑
Этот топик читают: Гость
Разработчик
Ответов: 25681
Рейтинг: 2087
#46: 2017-05-23 19:11:26 ЛС | профиль | цитата
Ну и на чем остановились?
карма: 20

0
Ответов: 4347
Рейтинг: 678
#47: 2017-05-24 12:30:10 ЛС | профиль | цитата
Бери пока вариант Galkov-a, а там посмотрим.

И ещё, а зачем в THICOMEX.ExecuteRd вот это ReadStr := BufferRd + #0 ?
По-моему, должно быть так:
// Вместо этого:
//SetLength(BufferRd, FStat.cbInQue);
// Это:
if FStat.cbInQue > Length(BufferRd) then SetLength(BufferRd, FStat.cbInQue); // Экономим перераспределение памяти

// Вместо этого:
//ReadStr := BufferRd + #0;
//SetLength(ReadStr, BytesTrans);
// Это:
ReadStr := Copy(BufferRd, 1, BytesTrans);


Редактировалось 1 раз(а), последний 2017-05-24 12:31:03
карма: 25

0
Ответов: 9906
Рейтинг: 351
#48: 2017-05-24 16:16:07 ЛС | профиль | цитата
Вот именно, что пока

Если у кого есть работающее железо - скажите, вышеизложенные рассуждения соответствуют действительности, али как ???
Теория - это одно, а жизнь - другое.

В принципе, железо у меня есть (в нескольких экземплярах), но руки пока не доходят...
Сейчас делаю комплект п/плат для Изделия, пойдут в изготовление -- куплю "хороший" осциллограф и, вот тогда уже, начну плотно работать с софтом.

Однако, Netspirit, поставил задачу возможности некоторого "кольцевания". Она может иметь смысл, если распознавать еще и ошибки приема (тоже задача, между прочим, хотя там и нет принципиальных сложностей).
Ну начал он сыпать какими то там FrameError -- закрыть при этом порт без плясок с бубном, не самое плохое было бы.

Моя мысль пока такая: CloseCom состоит только из установки Event-а на закрытие одного из потоков.
Он, выйдя из "вечного цикла", устанавливает Event на закрытие другого потока.
Который, в свою очередь выйдя из "вечного цикла", убивает файл (CloseHandle(hFile)).
Сами потоки в режиме AutoFree, конечно же.
Закольцевать doOpen не получится, а вот doClose - вполне себе.

В качестве окончательной "шлифовки" можно было бы завести опциональное формирование пакета. На все случаи жизни ответа не дашь, конечно же.
Но, даже "опциональная парочка" могла бы принести пользу.
Например - таймаут, и концевой символ. Если св-ва "пустые" - то как и сейчас, пусть юзер этим занимается.
А вот коллеге kaban4ik-у, уже помощь была бы (см. его схему в этом топике).
У Modbus-ASCII - кажется (в смысле - по памяти) символ ';' является концевым для пакета.

Ну это такой как бы план, как бы работы

Редактировалось 1 раз(а), последний 2017-05-24 16:16:44
карма: 9

0
файлы: 1Интерфейсная плата.rar [523.3KB] [413]
Ответов: 4347
Рейтинг: 678
#49: 2017-05-24 17:40:47 ЛС | профиль | цитата
Galkov писал(а):
Моя мысль пока такая: CloseCom состоит только из установки Event-а на закрытие одного из потоков.
Он, выйдя из "вечного цикла", устанавливает Event на закрытие другого потока.
Который, в свою очередь выйдя из "вечного цикла", убивает файл (CloseHandle(hFile)).


Позволю себе сформулировать общий принцип проще: Если некоторый объект используется несколькими потоками, то (при поставленой задаче неблокирующего взаимодействия потоков) уничтожением этого объекта должен заниматься тот, кто последний решил его уничтожить. Эта задача не решается с помощью критических секций - так как таким "общим объектом" будет являться сама критическая секция, кроме того она является источником блокировок.
Решается же эта задача путём реализации в таком общем объекте механизма подсчета ссылок.

Озвучу термины:
- "освобождение объекта" - в привычных нам методах программирования - вызов Free/Destroy. В случае совместного доступа нескольких потоков вызов Destroy одним потоком недопустим, пока этот объект используется в других потоках. В контексте объекта с подсчетом ссылок - уменьшение счетчика ссылок, который, достигнув 0 уничтожит объект по Destroy.
- "владелец объекта" - объект (или поток), метод которого отвечает за уменьшение счетчика ссылок совместного объекта (освобождение). Первым владельцем объекта становится тот, кто его создаёт - тут других вариантов нет. Он может передать этот объект во владение кому-то другому, затем сразу же забыть о его существовании (так как новый владелец будет отвечать за уничтожение нашего объекта и может его уничтожить). Если же владелец хочет использовать объект одновременно с другими потоками, то только владелец может передать его кому-то другому, предварительно увеличив счетчик использования объекта. Таким образом владелец получает гарантию, что совместный объект не будет уничтожен ни одним из владельцев, пока каждый владелец заинтересован в его существовании.
В Pipe-сервере это выглядит так:


Ну, а CloseHandle(hFile) по идее прерывает WaitForSingleObject, и последующие функции работы с этим хендлом показывают соответствующую ошибку - достаточно правильно обработать.

Редактировалось 1 раз(а), последний 2017-05-25 10:48:25
карма: 25

0
Ответов: 9906
Рейтинг: 351
#50: 2017-09-21 14:17:52 ЛС | профиль | цитата
Уважаемые коллеги... Не закончил я пока своих издевательств с COM-портом (через FT232RL). Хотя пока все и работает...

Но соображения по поводу элемента Synchronize появились.
Напомню, что мне не нравится:
Galkov писал(а):
... против "индуизма" кодинга (Вы используете AM_SYNC_METHOD, nesco - WM_DEFERREDEVENT, а Кладов - CM_EXECPROC)

Отсюда вопрос - а если так:
unit hiSynchronize;

interface

uses Kol,Share,Debug;

type
ThiSynchronize = class(TDebug)
private
TM:TThreadMethod; // поле FMethod фиктивного TThread
DT:Pdata;
procedure SyncMethod;
public
_data_Data: THI_Event;
_event_onSync: THI_Event;
procedure _work_doSynchronize(var _Data:TData; Index:Word);
end;

implementation

type
{$ifndef F_P}
TThreadEx = object(TThread) end;
PThreadEx = ^TThreadEx;
{$else}
TThreadEx = class(TThread) end;
PThreadEx = TThreadEx;
{$endif}

procedure ThiSynchronize._work_doSynchronize;
begin
DT := @_Data;
PThread(integer(@@TM)-integer(@@PThreadEx(nil).FMethod)).Synchronize(SyncMethod);
end;

procedure ThiSynchronize.SyncMethod;
var D:TData;
begin
D := ReadData(DT^, _data_Data);
_hi_OnEvent(_event_onSync, D);
end;

end.
Мне думается, что и DeferredEvent можно по этому принципу делать...

Редактировалось 1 раз(а), последний 2017-09-21 14:20:59
карма: 9

0
Ответов: 4347
Рейтинг: 678
#51: 2017-09-21 15:14:15 ЛС | профиль | цитата
Это чтобы не дублировать KOL-овскую реализацию, а использовать сразу её?
карма: 25

0
Разработчик
Ответов: 25681
Рейтинг: 2087
#52: 2017-09-21 19:19:42 ЛС | профиль | цитата
Galkov писал(а):
Мне думается, что и DeferredEvent можно по этому принципу делать...

Интересно бы глянуть на пример реализации DeferredEvent по этому принципу
карма: 20

0
Ответов: 9906
Рейтинг: 351
#53: 2017-09-23 05:50:43 ЛС | профиль | цитата
nesco писал(а):
Интересно бы глянуть на пример реализации DeferredEvent по этому принципу

Ну например:
unit hiSynchronize;

interface

uses Windows,Kol,Share,Debug;

type
ThiSynchronize = class(TDebug)
private
PT:integer; // Адрес фиктивного потока TThread
TM:TThreadMethod; // Реальное поле FMethod фиктивного потока TThread
DT:TData;
procedure SyncMethod;
public
_data_DataBefore:THI_Event;
_data_DataAfter:THI_Event;
_event_onSync:THI_Event;
constructor Create;
procedure _work_doSynchronize(var _Data: TData; Index: Word);
procedure _work_doDeferred(var _Data: TData; Index: Word);
end;

implementation

type
{$ifndef F_P}
TThreadEx = object(TThread) end;
PThreadEx = ^TThreadEx;
{$else}
TThreadEx = class(TThread) end;
PThreadEx = TThreadEx;
{$endif}

var Dummy:TThreadMethod;

constructor ThiSynchronize.Create;
begin
inherited;
PT := integer(@@TM)-integer(@@PThreadEx(nil).FMethod);
TM := SyncMethod;
end;

procedure ThiSynchronize._work_doSynchronize(var _Data: TData; Index: Word);
begin
DT := ReadData(_Data, _data_DataBefore);
SendMessage(Applet.Handle, CM_EXECPROC, 0, PT);
end;

procedure ThiSynchronize._work_doDeferred(var _Data: TData; Index: Word);
begin
DT := ReadData(_Data, _data_DataBefore);
PostMessage(Applet.Handle, CM_EXECPROC, 0, PT);
end;

procedure ThiSynchronize.SyncMethod;
var D:TData;
begin
D := ReadData(DT, _data_DataAfter);
_hi_OnEvent(_event_onSync, D);
end;

Initialization
PThread(integer(@@Dummy)-integer(@@PThreadEx(nil).FMethod)).Synchronize(Dummy);
end.

Netspirit писал(а):
не дублировать KOL-овскую реализацию

Пожалуй, ДА.
Фактически одна задача решается тремя способами. Три сообщения, три обработчика...
Не то, чтобы это неправильно работало. Но - не по человечески как-то.
Ну и имеет такой стиль свое название - "индусский код".
Мне понятно, что Кладов неправильно код написал (универсальней надо).
Применять его код (вместо написания своего) - то еще удовольствие... Вопрос соотношения цены и получаемого результата

Редактировалось 4 раз(а), последний 2017-09-24 10:47:35
карма: 9

0
Разработчик
Ответов: 25681
Рейтинг: 2087
#54: 2017-09-23 11:07:11 ЛС | профиль | цитата
Galkov писал(а):
Ну например:

Ага, интересно.

Редактировалось 1 раз(а), последний 2017-09-23 11:08:05
карма: 20

0
Разработчик
Ответов: 25681
Рейтинг: 2087
#55: 2017-09-30 00:57:34 ЛС | профиль | цитата
Вот, набросал код DeferredEvent по мотивам идеи Galkov-a. Мне в этом компоненте катастрофически не нравился Sleep в качестве задержки перед вызовом события. Нашел код, который не вызывает у нас блокировку обработчика до окончания задержки. Надо бы протестировать, если пойдет, то заменю этим кодом штатный.
карма: 20

0
файлы: 1DeferredEvent.zip [1.5KB] [319]
Ответов: 9906
Рейтинг: 351
#56: 2017-09-30 13:42:42 ЛС | профиль | цитата
Ох и любишь же ты задачу усложнять.

Казалось бы, всего-то сделать PostMessage - и исходная задача решена.
Так нет же, ее надо усложнить добавлением задержки.
Для решения исходной задачи нужны три точки: входная doDeferredEvent, выходная onDeferredEvent, верхняя Data. Ну и одноименное свойство (надо ли оно - тоже вопрос).
А в результате мы имеем ДОПОЛНИТЕЛЬНО: свойства Delay и InData, вход doStop, и выход onStop.
Да и тестировать надо будет именно эти дополнительные заморочки (особое внимание обратить надо на WM_CLOSE во время этого Delay).
Чем мы занимаемся - уже и понять нельзя... То ли развязку события делаем, то ли альтернативный таймер собираем.

То ли дело у Netspirit-а, три точки - и нет никаких проблем с пониманием.

А я бы в его элементе добавил бы анализ потока из которого идет вызов doSynchronize, и если из основного - делал бы PostMessage.
И все. Получилось бы два в одном, простом как сибирский валенок.

Мне больше нравится, когда оно "дешево и сердито"

Редактировалось 4 раз(а), последний 2017-09-30 14:58:00
карма: 9

0
Разработчик
Ответов: 25681
Рейтинг: 2087
#57: 2017-09-30 15:55:41 ЛС | профиль | цитата
Galkov писал(а):
Да и тестировать надо будет именно эти дополнительные заморочки (особое внимание обратить надо на WM_CLOSE во время этого Delay).

Да, ты прав, с WM_CLOSE траблы. Может действительно закопать этот Delay со всем примочками? InData вроде каши не просит, можно и оставить.
карма: 20

0
Ответов: 9906
Рейтинг: 351
#58: 2017-09-30 22:15:13 ЛС | профиль | цитата
nesco писал(а):
Может действительно закопать

Видишь ли, nesco, я сторонник эффективного минимализма...
Но, конечно же - ни на чем не настаиваю. Просто излагаю мысли, вытекающие из этого "минимализма".

И если закапывать все по максимуму, то получится примерно один элемент. Являющийся небольшим расширением элемента Netspirit-а.
Верхних точек на одну больше стало... От того, что при "отложенности" надо читать ДО, в то время, как при "синхронизации" надо ПОСЛЕ - никуда как бы и не денешься.
Теоретически, можно бы и эту точку выкинуть, но это уже противоречит эффективности. Много суеты ради неубедительного результата
Примерно такая доработка, без потери совместимости

unit hiSynchronize;

interface

uses Windows,Kol,Share,Debug;

type
ThiSynchronize = class(TDebug)
private
PT:integer; // Адрес фиктивного потока TThread
TM:TThreadMethod; // Реальное поле FMethod фиктивного потока TThread
DT:TData;
procedure SyncMethod;
public
_data_inData:THI_Event;
_data_Data:THI_Event;
_event_onSync:THI_Event;
constructor Create;
procedure _work_doSynchronize(var _Data: TData; Index: Word);
end;

implementation

type
{$ifdef F_P FreePascal}
TThreadEx = class(TThread) end;
PThreadEx = TThreadEx;
var MainThreadID:dword;
{$else Delphi}
TThreadEx = object(TThread) end;
PThreadEx = ^TThreadEx;
{$endif}

constructor ThiSynchronize.Create;
begin
PT := integer(@@TM)-integer(@@PThreadEx(nil).FMethod);
TM := SyncMethod;
end;

procedure ThiSynchronize._work_doSynchronize(var _Data: TData; Index: Word);
type F = procedure(hWnd, Msg, wParam, lParam:dword); stdcall;
var FP:pointer;
begin
DT := ReadData(_Data, _data_inData);
FP := @SendMessage; // Вариант вызова из параллельного потока
if GetCurrentThreadID = MainThreadID then // - из главного потока
FP := @PostMessage;
F(FP)(Applet.Handle, CM_EXECPROC, 0, PT);
end;

procedure ThiSynchronize.SyncMethod;
var D:TData;
begin
D := ReadData(DT, _data_Data);
_hi_OnEvent(_event_onSync, D);
end;

var Dummy:TThreadMethod;
Initialization
{$ifdef F_P FreePascal}MainThreadID := GetCurrentThreadID;{$endif}
PThread(integer(@@Dummy)-integer(@@PThreadEx(nil).FMethod)).Synchronize(Dummy);
end.

Редактировалось 1 раз(а), последний 2017-09-30 22:17:28
карма: 9

0
Разработчик
Ответов: 25681
Рейтинг: 2087
#59: 2017-09-30 23:23:11 ЛС | профиль | цитата
Galkov писал(а):
Примерно такая доработка, без потери совместимости

Подождем, что скажет Netspirit. И ты бы еще *.ini файл выложил для полного комплекта.
карма: 20

0
Ответов: 9906
Рейтинг: 351
#60: 2017-09-30 23:37:20 ЛС | профиль | цитата
INI - он несколько дискуссионный, в плане хинтов...
Но для себя я что-то должен же был написать:
заготовка для INI

[About]
Version=2.00
Author=NetSpirit,Galkov
Mail=Galkov@inbox.ru

[Type]
Class=Element
Info=Вызов события в главном потоке из параллельного/главного потока
Tab=System

[Property]

[Methods]
doSynchronize=Вызвать событие onSync в главном потоке. Если запрос из главного потока, то onSync - отложенное|1|
onSync=Событие происходит в главном потоке после вызова doSynchronize|2|
*inData=Данные для выдачи событием onSync, читаемые до запроса на событие|4|
*Data=Данные для выдачи событием onSync, читаемые по запрашиваемому событию|4|

Редактировалось 4 раз(а), последний 2017-10-01 12:59:14
карма: 9

0
Сообщение
...
Прикрепленные файлы
(файлы не залиты)