Чтобы отправить сообщение другому агенту, необходимо заполнить поля объекта ACLMessage и вызвать метод send() класса Agent. Приведенный ниже код демонстрирует, как создать и отправить сообщение, информирующее агента по имени Peter о том, что сегодня идет дождь.
ACLMessage msg = new ACLMessage(ACLMessage.INFORM);
msg.addReceiver(new AID(“Peter”, AID.ISLOCALNAME));
msg.setLanguage(“English”);
msg.setOntology(“Weather-forecast-ontology”);
msg.setContent(“Today it’s raining”);
send(msg);
Отправленные сообщения платформа JADE автоматически ставит в очередь доставленных сообщений агента-получателя. Агент может получить к ним доступ, вызвав метод receive(), который возвращает первое сообщение из очереди или null, если очередь пуста:
ACLMessage msg = receive();
if (msg!= null) {
// Обработка сообщения
}
Посылка сообщений в приложении «Торговля книгами»
В приложении «Торговля книгами» используется сообщение CFP ( Call for Proposal ) для извещения о том, что агент покупателя Buyer-agent отправил агенту продавца Seller-agent запрос на приобретение книги. Сообщение PROPOSE содержит предложение продавца, а сообщение ACCEPT_PROPOSAL содержит заказ покупателя. Сообщение REFUSE агента продавца сообщает о том, что книги, запрошенной агентом покупателя, нет в каталоге. В обоих типах сообщений, отправляемых агенту покупателя, содержится название книги.
Содержанием сообщения PROPOSE должна быть цена на книгу. Ниже приведен пример, демонстрирующий, каким образом создается и оправляется сообщение CFP покупателем:
// Сначала в функции srtup считывается аргумент «название книги» - targetBookTitle
protected void setup () {
System.out.println(" Hallo! Buyer-agent "+getAID().getName()+ " is ready.");
// Get the title of the book to buy as a start-up argument
Object[] args = getArguments();
if (args!= null && args.length > 0) {
targetBookTitle = (String) args[0];
System.out.println("Target book is " +targetBookTitle);
…
}}
// Затем рассылается запрос на заданную книгу всем агентам-продавцам:
private class RequestPerformer extends Behaviour {
…
// Message carrying a request for offer
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
for (int i = 0; i < sellerAgents.lenght; ++i) {
cfp.addReceiver(sellerAgents[i]);
}
cfp.setContent(targetBookTitle);
…
}
Блокирование режима работы агента для ожидания сообщения
Часто необходимо реализовать поведение агента, который обрабатывает сообщения от других агентов. В нашем случае это OfferRequestsServer and PurchaseOrdersServer – в каждом из них требуется обработка заявок покупателя. Такое поведение должно быть повторяющимся, а при каждой итерации необходимо проверять наличие новых сообщений и обрабатывать их. Методы аналогичны, рассмотрим реализацию OfferRequestsServer.
Реализация поведения агента-продавца – обработка заявки:
public class OfferRequestsServer extends CyclicBehaviour {
public void action() {
ACLMessage msg = myAgent.receive();
if (msg!= null) {
//Сообщение принято, обработаем его
String title = msg.getContent();
ACLMessage reply = msg.createReply();
Integer price = (Integer) catalogue.get(title);
if (price!= null) {
// Запрашиваемая книга есть у продавца. Возвращаем ее цену
reply.setPerformative(ACLMessage.PROPOSE);
reply.setContent(String.valueOf(price.intValue()));
} else {
// Запрашиваемая книга отсутствует у продавца
reply.setPerformative(ACLMessage.REFUSE);
reply.setContent("not-available");
}
myAgent.send(reply);
}
}
}
На рисунке можно увидеть последовательность сообщений:
В коде (выше) поведение OfferRequestsServer реализовано как внутренний класс класса BookSellerAgent. Это упрощает реализацию, т.к. обеспечивает прямой доступ к каталогу книг для продажи, но не является обязательным.
Метод createReply () класса ACLMessage автоматически создает новую настройку ACLMessageproperly получателя и все поля, используемые для управления сеансом (conversation - id, reply - with, in - reply - to).
В соответствии с Рис.3.3 (ЛР№3), при добавлении поведения, описанного выше, поток агента входит в непрерывный цикл, что слишком нагружает процессор. Чтобы избежать этого, метод action() поведения OfferRequestsServer следует выполнять лишь тогда, когда появится новое сообщение. Для этого можно использовать метод block() класса поведений. Этот метод помечает поведение как «заблокированное», так что агент не планирует следующих выполнений. Когда в очередь сообщений агента добавится новое сообщение, все заблокированные режимы снова становятся доступными для исполнения:
public void action() {
ACLMessage msg = myAgent.receive();
if (msg!= null) { // Message received. Process it
...
}
else {block();
}
Приведенный выше код является типичным (и рекомендуемым) шаблоном для реализации приема сообщений внутри поведения агента.
Выбор сообщений с указанными характеристиками из очереди сообщений
Т.к. оба режима OfferRequestsServer и PurchaseOrdersServer являются циклическими, их метод action () которых начинается с вызова метода myAgent. receive(), что порождает следующую проблему: как определить, что поведение OfferRequestsServer выбирает из очереди сообщений только сообщения, в которых содержится запрос о наличии книги, а поведение PurchaseOrdersServer – только сообщения, содержащие заказы на поставку?
Для разрешения этой проблемы нужно изменить код, указав соответствующие “шаблоны” при вызове метода receive(). Когда шаблон указан, метод receive () возвращает первое сообщение (если оно есть), соответствующее данному шаблону, игнорируя все неподходящие сообщения. Подобные шаблоны реализованы как экземпляры класса jade.lang.acl.MessageTemplate, который предоставляет набор методов, позволяющих просто и гибко создавать шаблоны.
Как было сказано выше Посылка сообщений в приложении «Торговля книгами», для запроса о наличии книги используется сообщение CFP, и сообщение PROPOS E – для передачи предложения о заказе. Поэтому необходимо изменить метод action() класса OfferRequestServer так, чтобы вызов myAgent.receive() игнорировал все сообщения, кроме CFP.
public void action() {
//Этот шаблон призван выбрать только сообщения типа CFP
MessageTemplate mt = MessageTemplate. MatchPerformative (ACLMessage.CFP);
ACLMessage msg = myAgent.receive(mt);
if (msg!= null) {
// CFP Message received. Process it
...
}
else {
block();
}
}
Выдержка из таблицы методов класса Class MessageTemplate:
static MessageTemplate | MatchPerformative (int value) This Factory Method returns a message template that matches any message with a given performative. |
static MessageTemplate | MatchProtocol (java.lang.String value) This Factory Method returns a message template that matches any message with a given:protocol slot. |
static MessageTemplate | MatchReceiver (AID [] values) This Factory Method returns a message template that matches any message with a given:receiver slot. |
Сложные переговоры
Режим RequestPerformer (поведение агента Bookbuyer) представляет собой пример поведения, проводящего «сложные» переговоры. В данном случае, «переговоры» – это последовательность сообщений, которыми обмениваются два или более агентов с четко
определенными причинными и временными отношениями. Поведение RequestProposal должно послать CFP -сообщение нескольким агентам - известным агентам-продавцам, получить обратно все ответы и, в случае, если получен хотя бы один ответ типа PROPOSE, позже послать сообщение
ACCEPT_PROPOSAL агенту-продавцу, который сделал предложение с минимальной ценой, и получить обратно ответ INFORM. Всякий раз, когда происходит обмен сообщениями, необходимо определить управляющие поля в сообщениях, участвующих в обмене. Это позволит легко и однозначно создавать шаблоны, соответствующие возможным ответам.
Import jade.core.AID;