OberonCore
https://forum.oberoncore.ru/

omcBus - транспортная шина межмодульной передачи сообщений
https://forum.oberoncore.ru/viewtopic.php?f=90&t=258
Страница 1 из 1

Автор:  Сергей Губанов [ Пятница, 23 Июнь, 2006 08:22 ]
Заголовок сообщения:  omcBus - транспортная шина межмодульной передачи сообщений

(модератор 31.01.2012): загружено в коллекцию как omcBus

В результате нехитрых комбинаций соображений по поводу обобщенной транспортной шины передачи сообщений между объектами родилась идея передавать сообщения не только между объектами, но и между модулями. Модули в данном случае отличаются от объектов тем, что у модулей всегда есть своё уникальное имя. Естественно было бы посылать сообщения модулю зная только лишь его символьное имя.

Интерфейс шины межмодульной передачи сообщений можно определить, например, так:
Код:
DEFINITION Bus;

  TYPE
    Message = ABSTRACT RECORD END;

    HandleBusMessage = PROCEDURE (VAR msg: Message);

  PROCEDURE Send (IN destination: ARRAY OF CHAR; VAR msg: Message; OUT ok: BOOLEAN);

END Bus.

Каждый модуль заинтересованный в возможности принятия и обработки таких сообщений должен экспортировать процедуру типа HandleBusMessage с именем "HandleBusMessage". В таком случае любой кто захочет, сможет передать этому модулю сообщение Message зная только лишь символьное имя модуля получателя.

PROCEDURE Send (IN destination: ARRAY OF CHAR; VAR msg: Message; OUT ok: BOOLEAN);
Отправка сообщения модулю.

Предусловие:
destination - имя модуля получателя;
msg - сообщение для этого модуля;

Постусловие:
ok = TRUE если модуль destination был найден и была найдена и вызвана экспортированная им процедура HandleBusMessage. В противном случае ok = FALSE (либо такой модуль не найден, либо он не экспортирует нужной процедуры).


Пример использования транспортной шины межмодульной передачи сообщений:
Код:
MODULE TestBus;

  IMPORT Log, Bus;

  TYPE
    HelloMsg = RECORD (Bus.Message) END;
 
  PROCEDURE HandleBusMessage* (VAR msg: Bus.Message);
  BEGIN
    WITH msg: HelloMsg DO
      Log.String("HelloMsg received!!!"); Log.Ln
    ELSE
    END
  END HandleBusMessage;

  PROCEDURE Test*; (* (!)TestBus.Test *)
    VAR msg: HelloMsg; ok: BOOLEAN;
  BEGIN
    Bus.Send("TestBus", msg, ok) (* Отправка сообщения msg модулю "TestBus", т.е. самому себе *)
  END Test;

END TestBus.

А вот и полная реализация модуля Bus средствами Meta-программирования:
Код:
MODULE Bus;

  IMPORT Meta;

  TYPE
    Message* = ABSTRACT RECORD END;
   
    HandleBusMessage* = PROCEDURE (VAR msg: Message);

  PROCEDURE Send* (IN destination: ARRAY OF CHAR; VAR msg: Message; OUT ok: BOOLEAN);
    VAR m, p: Meta.Item;
    r: RECORD (Meta.Value) h: HandleBusMessage END;
  BEGIN ok := FALSE;
    Meta.Lookup(destination$, m);
    IF m.obj = Meta.modObj THEN
      m.Lookup("HandleBusMessage", p);
      IF p.obj = Meta.procObj THEN
        p.GetVal(r, ok);
        IF ok THEN r.h(msg) END
      END
    END
  END Send;

END Bus.

Для чего это можно использовать, наверное, и так понятно. Например, пусть есть модули А и В. Модуль В импортирует модуль А и вызывает его процедуры. Пусть теперь появился модуль А2 с таким же интерфейсом как и А, и нам вдруг захотелось, чтобы модуль В теперь работал с модулем А2. Поможет только перекомпиляция модуля В. Но ежели модуль В будет общаться с модулем А только зная его символьное имя, то надо будет просто заменить это имя на "А2", динамически, не останавливая работу. Кстати, после такого переключения, модуль А даже можно будет выгрузить из системы.

P. S. Следующим логическим шагом конечно же будет реализация remoting-шины, позволяющей посылать сообщения модулям запущенным на других компьютерах, т.е. по адресу: "адресIP:порт:модуль" ;-).

Автор:  Илья Ермаков [ Пятница, 23 Июнь, 2006 12:29 ]
Заголовок сообщения: 

Хорошая идея... Нечто подобное я делал в MtSubscribe, только там модуль подписывает некоторую (ые) свою процедуру на сообщение с определенным именем. Однако писал я это дело давно - и сейчас сделал бы по-другому - подписку не по имени сообщения, а по имени его типа.

Автор:  Сергей Губанов [ Воскресенье, 25 Июнь, 2006 17:33 ]
Заголовок сообщения: 

http://www.delphikingdom.ru/asp/talktopic.asp?ID=368
№ 290 24-06-2006 14:10
Илья Ермаков писал(а):
Если говорить о сетевом взаимодействии... Сейчас у меня как раз есть задача организовать параллельные расчеты в сети на взаимодействующих BlackBox-приложениях. Я предполагаю реализовать интерфейс для параллельного взаимодействия следующим образом: среды просто вызывают процедуры друг друга, посылая команды вида Module.Proc и передавая в качестве параметра процедур расширенные записи некоторого базового типа. Билиотека параллельного взаимодействия (БПВ) будет разбирать через метапрограммирование структуру передаваемой записи и выгружать ее поля в линейный поток, передая данные через сокетное соединение вместе с именем типа записи. На "другом конце провода" БПВ по имени типа создает экземпляр того же типа и вызывает требуемую процедуру с передачей параметра. Процедура определяет, какой тип имеет переданный параметр, и либо обрабатывает его, либо игнорирует. Т.е. "тип сообщения" непосредственно ассоциирован с типом объекта в смысле языка программирования. Более того, в стандартном BlackBox Framework есть поддержка перманентных объектов, которая позволяет сериализовать в линейный поток произвольный граф ссылающихся друг на друга перманентых объектов. Поэтому через сеть можно будет легко передать такие объекты, например, графические отображения и т.п. Framework является отличным фундаментом для решения подобных задач, в нем есть все необходимые "кирпичики".

Очень интересно. Я тоже об этом подумывал.

Кстати, на счёт "разбирать через метапрограммирование структуру", на сколько я понимаю, если структура не содержит указателей (а она не должна содержать указателей, коль скоро передаётся на другую машину), то её вроде просто можно побайтово скопировать, т.е. не разбирать через метапрограммирование поля структуры...

Автор:  Сергей Губанов [ Воскресенье, 25 Июнь, 2006 22:59 ]
Заголовок сообщения: 

Сергей Губанов писал(а):
побайтово скопировать

Например, сочиним какую-нибудь эдакую хитрую структурку:
Код:
  TYPE
    Message* = ABSTRACT RECORD END;
   
    T1* = EXTENSIBLE RECORD (Message)
      x*: BYTE
    END;
   
    A1* = ARRAY 2 OF T1;

    T2* = RECORD (Message)
      x*: BYTE;
      t*: T1;
      a*: A1;
      b*: ARRAY 4 OF RECORD x: BYTE END;
    END;

Затем запишем в её поля последовательно 1, 2, 3,...; скопируем её содержимое в массив a и распечатаем элементы массива:
Код:
  PROCEDURE Test0*; (* (!)TestSerialization.Test0 *)
    CONST N = SIZE(T2);
    VAR i: INTEGER;
    r: T2;
    a: ARRAY N OF BYTE;
  BEGIN
    r.x := 1; r.t.x := 2; r.a[0].x := 3; r.a[1].x := 4;
    r.b[0].x := 5; r.b[1].x := 6; r.b[2].x := 7; r.b[3].x := 8;
    FOR i := 0 TO N-1 DO a[i] := 0 END;
    SYSTEM.MOVE(SYSTEM.ADR(r), SYSTEM.ADR(a[0]), N);
    FOR i := 0 TO N-1 DO
      L.String("a["); L.Int(i); L.String("] ="); L.Int(a[i]); L.Ln
    END
  END Test0;

Результат:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
a[4] = 5
a[5] = 6
a[6] = 7
a[7] = 8
Значит, вроде как можно, вместо мета-разбора полей, просто побайтово копировать (ну, естественно, это справедливо только если она передаётся на компьютер с тем же порядком big/litle-endian нумерацией битов, но, практически, кроме как на x86 платформе Блэкбокса всё равно пока что-то не видно :D ).

Автор:  Илья Ермаков [ Понедельник, 26 Июнь, 2006 13:04 ]
Заголовок сообщения: 

Да, можно копировать и побайтно. Но это уже детали реализации.

Автор:  Илья Ермаков [ Пятница, 30 Июнь, 2006 02:17 ]
Заголовок сообщения: 

Сергей, я бы хотел включить Вашу идею в готовящийся Advanced BlackBox Framework (с упоминаем авторства :-) ). Я предлагаю следующий интерфейс:

Код:
DEFINITION AbfBus;
       (* Передать конкретному модулю. Если он не загружен, то будет загружен *)
   PROCEDURE Broadcast (IN module: ARRAY OF CHAR; VAR msg: Message; OUT ok: BOOLEAN);
       (* Передать широковещательно всем загруженным модулям.
Возвращается число модулей, которые имеют HandleBusMsg *)
   PROCEDURE BroadcastAll (VAR msg: Message; OUT count: INTEGER);
       (* Передать всем загруженным модулям указанной подсистемы *)
   PROCEDURE BroadcastSub (IN sub: ARRAY OF CHAR; VAR msg: Message; OUT count: INTEGER);
       (* Передать сообщение всем модулям, подписанным на него *)
   PROCEDURE Send (VAR msg: Message);
       (* Подписать модуль на определенный тип сообщения *)
   PROCEDURE Subscribe (IN mod, msgType: ARRAY OF CHAR);
       (* Отписать модуль *)
   PROCEDURE UnSubscribe (IN mod, msgType: ARRAY OF CHAR);

END AbfBus.

Автор:  Сергей Губанов [ Пятница, 30 Июнь, 2006 11:24 ]
Заголовок сообщения: 

Илья Ермаков писал(а):
Сергей, я бы хотел включить Вашу идею в готовящийся Advanced BlackBox Framework (с упоминаем авторства :-) ). Я предлагаю следующий интерфейс:

Круто! Я конечно обоими руками "за".

Только вроде слова Broadcast и Send обозначают ровно наоборот, чем то что тут написано, их надо взаимозаменить. Send - послать по конкретному адресу, а Broadcast - послать всем.

А, кажется, я понял. Вы писали, что у Вас уже используется Subscribe-механизм и, наверное, слово Send Вы уже использовали для него, так что это слово занято. Ничего страшного, ведь ещё есть слово "SendTo" - послать по указанному адресу.

Вот на счёт того что модуль может быть не загружен, это Вы очень правильно заметили. Стоит ли его загружать или не стоит? Кто должен решать? Может быть надо ввести дополнительный флаг forceLoad: BOOLEAN, если forceLoad = FALSE, значит модуль-получатель не надо загружать если он не загружен, иначе - загрузить его?..

Итого:
PROCEDURE Subscribe (IN mod, msgType: ARRAY OF CHAR);
Подписать модуль на определенный тип сообщения.

PROCEDURE UnSubscribe (IN mod, msgType: ARRAY OF CHAR);
Отписать модуль.

PROCEDURE Send (VAR msg: Message);
Передать сообщение всем модулям, подписанным на него.


PROCEDURE SendTo (VAR msg: Message; IN module: ARRAY OF CHAR; forceLoad: BOOLEAN; OUT ok: BOOLEAN);
Передать сообщение msg модулю module. Если этот модуль в данный момент времени не загружен и forceLoad = TRUE, то загрузить его. Результат ok = TRUE говорит о том, что сообщение было доставлено.


PROCEDURE Broadcast (VAR msg: Message; OUT count: INTEGER);
Передать широковещательно всем загруженным модулям всех подсистем. Возвращается число модулей, которым это сообщение было фактически доставлено (имеющим HandleBusMsg).

PROCEDURE BroadcastTo (VAR msg: Message; IN subsystem: ARRAY OF CHAR; OUT count: INTEGER);
Передать широковещательно всем загруженным модулям указанной подсистемы subsystem. Возвращается число модулей, которым это сообщение было фактически доставлено (имеющим HandleBusMsg).

Автор:  Илья Ермаков [ Пятница, 30 Июнь, 2006 14:56 ]
Заголовок сообщения: 

Полностью согласен.

Автор:  Сергей Губанов [ Пятница, 30 Июнь, 2006 15:56 ]
Заголовок сообщения: 

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

Не смотря на возможную опасность для всей системы, наверное, всё же не надо пресекать повторный вызов, а надеяться на благоразумие программистов - в конце концов от обычной бесконечной рекурсии или зависшего цикла всё равно ни кто не застрахован, а тут ситуация примерно такая же, ну, только межмодульная.

Автор:  Илья Ермаков [ Воскресенье, 09 Июль, 2006 23:50 ]
Заголовок сообщения: 

Вот, в модуле AbfBus выпущенного Advanced BlackBox Framework реализовал идею межмодульной шины. Дополнительно сделал асинхронный вариант с постановкой сообщений в очередь до востребования.

Автор:  Евгений Темиргалеев [ Понедельник, 30 Январь, 2012 18:06 ]
Заголовок сообщения:  Re: omcBus - транспортная шина межмодульной передачи сообщен

Оформляю AbfBus без асинхронного варианта как omcBus.

Есть информация, что кто-то был против термина "шина" применительно к данному модулю. Пожалуйста, отпишитесь, пока можно название скорректировать.

Автор:  Евгений Темиргалеев [ Понедельник, 30 Январь, 2012 21:48 ]
Заголовок сообщения:  Re: omcBus - транспортная шина межмодульной передачи сообщен

Message заменяется на ANYREC (переписка 26.01.2011):
Евгений Темиргалеев писал(а):
Сергей Губанов писал(а):
Код:
    Message = ABSTRACT RECORD END;

    HandleBusMessage = PROCEDURE (VAR msg: Message);
Сергей, а заведение коренного типа чем-то обосновано? Шина специфических сообщений не определяет, тупо передаёт. Для получения метаинформации, которая используется в реализации, тип переменной роли не играет. Может достаточно просто
HandleBusMessage = PROCEDURE (VAR msg: ANYREC);
?
Сергей Губанов писал(а):
Да, лучше просто ANYREC.
Евгений Темиргалеев писал(а):
ANYREC позволит избавить модуль-обработчик от потребности импортировать модуль-шину, если ему самому ничего слать не надо, а подписывает его на сообщение отдельный конфигурационный модуль

Или я ошибаюсь в чём-то?
Сергей Губанов писал(а):
Да, имортировать необходимости нет.

Автор:  Евгений Темиргалеев [ Вторник, 31 Январь, 2012 10:12 ]
Заголовок сообщения:  Re: omcBus - транспортная шина межмодульной передачи сообщен

Попутно интерфейс сделан более естественным:
1) PROCEDURE Subscribe (IN mod, msgMod, msgType: ARRAY OF CHAR; forceLoad: BOOLEAN);
PROCEDURE UnSubscribe (IN mod, msgMod, msgType: ARRAY OF CHAR);
заменено на:
PROCEDURE Subscribe (IN mod, type: ARRAY OF CHAR; forceLoad: BOOLEAN);
PROCEDURE Unsubscribe (IN mod, type: ARRAY OF CHAR);

2) PROCEDURE BroadcastTo (IN sub: ARRAY OF CHAR; VAR msg: ANYREC; OUT res: INTEGER);
Снято ограничение sub # "".

Исправлено несколько недочётов (журнал --- в тексте модуля).

Автор:  Евгений Темиргалеев [ Вторник, 31 Январь, 2012 10:39 ]
Заголовок сообщения:  Re: omcBus - транспортная шина межмодульной передачи сообщен

Выложено: http://oberoncore.ru/bbcc/subs/omc/bus

Автор:  Евгений Темиргалеев [ Вторник, 31 Январь, 2012 11:59 ]
Заголовок сообщения:  Re: omcBus - транспортная шина межмодульной передачи сообщен

"Пока не остыло", решил ещё упростить: отпилена (вместе с зависимостью от Kernel и SYSTEM) подписка на сообщения: Subscribe, Unsubscribe, Send (можно сделать отдельным модулем)

Некоторый опыт практического применения показал, что она не совсем востребована.

Страница 1 из 1 Часовой пояс: UTC + 3 часа
Powered by phpBB® Forum Software © phpBB Group
https://www.phpbb.com/