Я пытаюсь ответить себе. Я не уверен, что это лучшее решение, но, безусловно, работает.
Обратите внимание, что прежде чем перейти к этому решению, я хорошо задокументирован с найденными руководствами и учебными пособиями (на http://jade.tilab.com/) и столкнулся с другими разработчиками JADE (в списках рассылки http://jade.tilab.com/pipermail/jade-develop/)
Ответ сложен, поэтому я постараюсь быть исчерпывающим.
В моем проекте мне приходится иметь дело с двумя разными типами агентов.
- Агент ShipperAgent, который представляет одного грузоотправителя: он отслеживает транспортные средства, принадлежащие грузоотправителю, доступные и товары, "зарезервированные" у него.
![введите здесь описание изображения](https://i.stack.imgur.com/ythZy.png)
- Агент BuyerAgent, представляющий клиентов (или покупателей): у каждого покупателя есть список товаров, которые нужно переместить из точки А в точку Б.
![введите здесь описание изображения](https://i.stack.imgur.com/heLok.png)
Два агента зарегистрированы в службе желтых страниц.
В ShipperAgent, нажав кнопку "ПОИСК", вы начнете поиск: запустите Протокол взаимодействия с сетью контрактов.
Объясните Протокол взаимодействия с сетью контрактов и мой случай
В стандарте FIPA: http://www.fipa.org/specs/fipa00029/SC00029H.html ![введите здесь описание изображения](https://i.stack.imgur.com/Dkygs.jpg)
В JADE руководство можно найти здесь: http://jade.tilab.com/doc/programmersguide.pdf (стр. 35)
Далее вы заметите изменения, которые мне пришлось предпринять.
ShipperAgent отправляет CFP каждому BuyerAgent.
Каждый агент покупателя:
2.1 если у него есть товар, отправить ПРЕДЛОЖЕНИЕ ShipperAgent.
2.2 если товара нет, направить в ShipperAgent ОТКАЗ. А для покупателя протокол заканчивается.
Так как здесь легко. С помощью сниффера мы можем наблюдать:
![введите здесь описание изображения](https://i.stack.imgur.com/iQL6L.png)
В настоящее время:
Агент грузоотправителя:
3.1 получает одно или несколько ПРЕДЛОЖЕНИЙ от покупателей и отображает (см. изображение ниже).
3.2 в случае получения ОТКАЗА (или не получения чего-либо по прошествии определенного времени), прекращение связи с данным покупателем.
Вот как грузоотправитель графически отображает предложения:
![введите здесь описание изображения](https://i.stack.imgur.com/BIDHC.png)
Теперь пользователь сам выбирает, какие товары хочет, а какие нет.
Чтобы добиться этого, мне пришлось создать своего рода «внутреннюю связь» с самим агентом: графический интерфейс (в 3.1) после нажатия «Выполнить» отправляет сообщение агенту. Это может показаться неэлегантным, но, похоже, это единственный способ избежать сбоя ShipperAgent на стороне протокола.
![введите здесь описание изображения](https://i.stack.imgur.com/n6LbF.png)
Агент грузоотправителя:
4.1, если пользователь выбрал одно или несколько предложений товаров (и нажал «Выполнить»), отправляет соответствующему BuyerAgent сообщение ACCEPT_PROPOSAL, где указаны конкретные товары, которые он хочет (подмножество предыдущего предложения).
4.2, если пользователь не выбирает товар (или нажимает «Отмена»), отправляет соответствующему агенту покупателя сообщение REJECT_PROPOSAL. Прекращает общение с этим покупателем.
Агент покупателя:
5.1 если получает ACCEPT_PROPOSAL, проверяет, что товары все еще доступны (любые другие грузоотправители могут тем временем «зарезервировать» их) и, если это так, отправляет INFORM.
5.2 если получает ACCEPT_PROPOSAL ma один или несколько товаров больше не доступны, отправляет FAILURE.
5.3, если он получает REJECT_PROPOSAL, прекращает связь с агентом-отправителем.
![введите здесь описание изображения](https://i.stack.imgur.com/RjJAZ.png)
Вкратце это (например):
![введите здесь описание изображения](https://i.stack.imgur.com/O86Tj.png)
Код
BuyerAgent.java Я создаю диспетчера, который всегда готов принять CFP. Как только он получит и запустит протокол, на стороне покупателя: запустите SearchJobResponder.
/*
* ...
*/
final MessageTemplate template = MessageTemplate.and(
MessageTemplate.MatchProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET),
MessageTemplate.MatchPerformative(ACLMessage.CFP) );
// SSResponderDispatcher:
SSResponderDispatcher dispatcher = new SSResponderDispatcher(this, template) {
BuyerAgent b = (BuyerAgent) this.myAgent;
protected Behaviour createResponder(ACLMessage initiationMsg) {
// SearchJobResponder for single cfp:
return new SearchJobResponder(b, initiationMsg);
}
};
addBehaviour(dispatcher);
/*
* ...
*/
ShipperAgent.java Поиск всех покупателей, создание CFP и запуск протокола на стороне грузоотправителя: запуск SearchJobInitiator.
/*
* ...
*/
ACLMessage cfp = new ACLMessage(ACLMessage.CFP);
AID[] buyerAgents = searchBuyers(); // search all buyerAgents
for (AID buyer : buyerAgents)
cfp.addReceiver(buyer);
addBehaviour(new SearchJobInitiator(this, cfp));
/*
* ...
*/
SearchJobInitiator.java Это была сложная часть...
/*
* ...
*/
public class SearchJobInitiator extends ContractNetInitiator {
ShipperAgent shipperAgent;
public SearchJobInitiator(ShipperAgent a, ACLMessage cfp) {
super(a, cfp);
shipperAgent=a;
// Very important:
registerHandleAllResponses(new HandleProposes());
}
@Override
protected Vector<?> prepareCfps(ACLMessage cfp) {
long now = System.currentTimeMillis();
cfp.setConversationId("contractNet-by-"
+shipperAgent.getAID().getLocalName()+now);
cfp.setContent("Fammi delle proposte di lavoro");
/*
* filtering...
*/
cfp.setProtocol(FIPANames.InteractionProtocol.FIPA_CONTRACT_NET);
cfp.setReplyByDate(new Date(now+10000));
//cfp.setReplyWith("cfp"+System.currentTimeMillis()) //useless, is overwrited at the end
return super.prepareCfps(cfp);
}
//inner class for handling a single proposal
public class HandleProposes extends Behaviour {
private static final long serialVersionUID = 1L;
private Vector<ACLMessage> proposes;
private Vector<ACLMessage> acceptances;
private int numberOfProposes;
public void onStart() {
proposes = (Vector<ACLMessage>) getDataStore().get(ALL_RESPONSES_KEY);
acceptances = (Vector<ACLMessage>) getDataStore().get(ALL_ACCEPTANCES_KEY);
numberOfProposes=proposes.size();
for (Iterator I=proposes.iterator(); I.hasNext();) {
ACLMessage propose = (ACLMessage) I.next();
// Very important:
if (propose.getPerformative()==ACLMessage.PROPOSE)
myAgent.addBehaviour(new HandleSinglePropose(propose, acceptances));
else
numberOfProposes--;
}
}
public void action() {
if (!done())
block();
}
public boolean done() {
return (acceptances.size()==numberOfProposes);
}
/*
* Inner class for handle a single proposal and display it:
*/
public class HandleSinglePropose extends Behaviour {
private ACLMessage propose;
private Vector<ACLMessage> acceptances;
private boolean finish=false;
public HandleSinglePropose (ACLMessage propose, Vector<ACLMessage> acceptances) {
this.propose=propose;
this.acceptances=acceptances;
// This is GUI in 3.1 point
GoodsChoiceBox gcb = new GoodsChoiceBox(shipperAgent, this, propose); // fill the JTable
gcb.setVisible(true);
}
@Override
public void action() {
MessageTemplate mt = MessageTemplate.and(
MessageTemplate.MatchSender(shipperAgent.getAID()),
MessageTemplate.and(
MessageTemplate.MatchReplyWith("response"+propose.getReplyWith()),
MessageTemplate.or(
MessageTemplate.MatchPerformative(ACLMessage.ACCEPT_PROPOSAL),
MessageTemplate.MatchPerformative(ACLMessage.REJECT_PROPOSAL)
) ) ) ;
// Read data from GUI. The user accept or reject:
ACLMessage decisionFromGUI = shipperAgent.receive(mt);
if (decisionFromGUI != null) {
ACLMessage reply = propose.createReply();
// bla bla...
finish=true;
HandleProposes.this.restart();
} else {
block();
}
}
public boolean done() {
return finish;
}
public void handleChoice(ACLMessage propose, boolean bool, Vector<Goods> selectedGoods) {
ACLMessage reply;
if (bool){
reply = new ACLMessage(ACLMessage.ACCEPT_PROPOSAL);
//...
} else {
reply = new ACLMessage(ACLMessage.REJECT_PROPOSAL);
//...
}
reply.addReceiver(shipperAgent.getAID());
reply.setReplyWith("response"+propose.getReplyWith());
shipperAgent.send(reply);
}
} // closes HandleSinglePropose
} // closes HandleProposes
}
SearchJobResponder.java Ответчик прост. Единственное замечание: я расширяю SSContractNetResponder, а не расширяю ContractNetResponder.
public class SearchJobResponder extends SSContractNetResponder {
BuyerAgent buyerAgent;
public SearchJobResponder(BuyerAgent a, ACLMessage cfp) {
super(a, cfp);
buyerAgent = a;
}
/*
* override methods...
*/
}
GoodsChoiceBox.java Графический интерфейс для отображения предложений...
public GoodsChoiceBox(final Agent agent, final HandleSinglePropose behaviour, final ACLMessage propose){
/*
* graphics stuff
*/
// if goods selected and press Execute
behaviour.handleChoice(propose,true,selectedGoods);
//else
behaviour.handleChoice(propose,false,null);
/*
* bla bla
*/
}
Я знаю, я много размышлял, но я не знал, как еще объяснить. Тем не менее, сейчас мой проект работает. Но я открыт для любых предложений.
person
Gioce90
schedule
13.01.2015