使用 JADE多智能体框架时,使用 ContractNet (交互协议)和GUI 时遇到一些问题。
特别是在覆盖 handlePropose 方法时。 我知道我的问题来自使用GUI。让我解释一下:
我的代理(发起人)使用第一个GUI,然后点击一下 对话从第二个代理(响应者)开始。根据 协议,发起者因此向响应者发送了CFP。中介 响应者以包含不同数据的PROPOSE响应。
从这里开始,一切都好。现在......
我希望代理启动器在返回回复之前可以检查 数据...即在JTable上为用户发布它们!用户会 通过GUI检查提案,并选择是否接受 点击按钮。
- 如果接受,则发起者发送ACCEPT_PROPOSAL。
- 如果不接受,则发起人发送REJECT_PROPOSAL。
这应该在方法 handleProposal 中完成。这是我的代码:
@Override
protected void handlePropose(final ACLMessage propose, final Vector acceptances) {
try {
System.out.println("Agent "+getLocalName()
+": receive PROPOSE from "+propose.getSender().getLocalName());
final ACLMessage reply = propose.createReply();
Vector<Goods> goods = (Vector<Goods>) propose.getContentObject();
// the JTable's GUI for visualize the list of data:
final GoodsChoiceBox gcb = new GoodsChoiceBox(propose.getSender().getName(), goods);
// the problem:
gcb.getExecuteJButton().addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
System.out.println("Agent "+getLocalName()+": send ACCEPT PROPOSAL ");
acceptances.addElement(reply);
}
});
// similar case, but for REJECT:
// gcb.getAbortJButton().addActionListener(... bla bla
gcb.setVisible(true);
} catch (UnreadableException e){
e.printStackTrace();
}
}
.....但是,显然,不起作用。
在Initiator代理中,ContractNet行为被中止...所以handleInform,handleRefuse和handleFailure(用于处理答案)也不起作用。 Initiator的主要GUI被阻止。还有其他问题...
相反,如果我这样做(没有JButton,另一个GUI和ActionListener):
@Override
protected void handlePropose(final ACLMessage propose, final Vector acceptances) {
try {
System.out.println("Agent "+getLocalName()
+": received PROPOSE from "+propose.getSender().getLocalName());
final ACLMessage reply = propose.createReply();
Vector<Goods> goods = (Vector<Goods>) propose.getContentObject();
// the JTable's GUI for visualize the list of data:
final GoodsChoiceBox gcb = new GoodsChoiceBox(propose.getSender().getName(), goods);
reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
System.out.println("Agente "+getLocalName()+": ACCEPT PROPOSAL di "+propose.getSender().getLocalName());
acceptances.addElement(reply);
} catch (UnreadableException e){
e.printStackTrace();
}
}
.... 有效。
我知道问题是ActionListener及其多线程性质。 但我需要GUI。
我该如何解决?
答案 0 :(得分:1)
我试着回答自己。我不确定这是最好的解决方案,但肯定有效。
请注意,在找到此解决方案之前,我已经详细记录了指南和教程(在http://jade.tilab.com/上),并与其他人面对面 JADE开发人员(在邮件列表中http://jade.tilab.com/pipermail/jade-develop/)
答案很复杂,所以我会尝试详尽无遗。
在我的项目中,我必须处理两种不同类型的代理。
这两个代理已注册到黄页服务。
在ShipperAgent中,单击“搜索”按钮开始搜索:启动合同网络交互协议。
在标准FIPA中:http://www.fipa.org/specs/fipa00029/SC00029H.html
在JADE指南中可以找到:http://jade.tilab.com/doc/programmersguide.pdf(第35页)
此外,您将注意到我必须采取的更改。
ShipperAgent向每个BuyerAgent发送CFP。
每个BuyerAgent:
2.1如果他有货物,请向ShipperAgent发送一份保证书。
2.2如果没有货物,请向ShipperAgent发送REFUSE。对于买方,协议结束。
因为这里很容易。有了嗅探器,我们可以观察到:
现在:
ShipperAgent:
3.1买家收到一个或多个PROPOSE,并显示(见下图)。
3.2如果收到REFUSE(或在一段时间后没有收到任何东西),则结束与这些买家的沟通。
以下是托运人如何以图形方式显示提案:
现在由用户选择哪些商品需要哪些商品不需要。
为了实现这一点,我不得不为代理商自己创建某种“内部通信”:GUI(在3.1中),一旦点击执行,就向代理发送消息。它可能看起来不够优雅,但它似乎是不会破坏协议方ShipperAgent的唯一方法。
ShipperAgent:
4.1如果用户选择了一个或多个商品提案(并单击执行),则向相应的BuyerAgent发送ACCEPT_PROPOSAL,其中包含特定商品(先前提案的子集)。
4.2如果用户没有选择任何商品(或点击取消),则向相应的BuyerAgent发送REJECT_PROPOSAL。结束与该买家的沟通。
BuyerAgent:
5.1如果收到ACCEPT_PROPOSAL,检查货物是否仍然可用(同时任何其他托运人可以“保留”),如果是,则发送INFORM。
5.2如果收到ACCEPT_PROPOSAL,则一个或多个商品不再可用,发送FAILURE。
5.3如果收到REJECT_PROPOSAL,则结束与ShipperAgent的通信。
简而言之(例如):
<强> 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 强> 用于显示提案的GUI ...
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
*/
}
我知道,我住的很多,但我不知道怎么解释。 但是,现在我的项目工作了。但我对任何建议持开放态度。
答案 1 :(得分:0)
我经常遇到这种问题。这些是有限状态机器行为,所以你应该能够暂停和恢复行为,但我不知道如何。我所做的是在启动器端创建两个单独的交互行为,在响应者端创建一个。
Initiator Responder
| |
| |
| First behaviour |The responder only has 1 behaviour
|| CFP-> ||
|| <-Proposal ||
| ||
| Second behaviour ||
|| Accept prop-> ||
|| <-Response ||
| |
要记住两点
(1)
确保保存conversationID
msgRecieved.getConversationID
从第一个行为开始,并在第二个行为中使用它。
msg.setConversationID().
(2) 第二种行为是另一个合同净发起人,但在prepareCFPs方法中设置MESSAGE performative以接受提案
类ContractServiceList扩展了ContractNetInitiator {
protected Vector prepareCfps(ACLMessage cfp) {
ACLmessage AP= new ACLmessage(ACLmessage.ACCEPT_PROPOSAL)
.....
这些事情很难解释,所以我试图附上一张图片,但有2个小代表。
我现在有足够的重复点来附上我正在做的照片。
答案 2 :(得分:0)
我刚刚意识到这个问题还有另一个解决方案。第二种解决方案涉及使用ChildBehaviours和数据存储。可以启动暂停父行为的子行为。然后必须在孩子完成后恢复父行为。
我附加图片以更好地解释这种互动。
因此,在您的CNI(ContractNetInitiator)父行为中的A点,您要启动子行为。你可以使用CNI.registerHandlePropose(新的Childbehaviour)来做到这一点。
这就是setup()方法应该是这样的:
protected void setup()
{
ContractNetInitiator parentBehave= new ContractNetInitiator (null, null, GlobDataStore);
ContractNetInitiator.registerHandlePropose(new ChildBehavoiur (GlobDataStore));
addBehaviour(CNI);
}
在您的子行为中,您必须检查父项(GlobDataStore)中的数据并返回要传回的消息。代码如下:
class ChildBehaviour extends OneShotBehaviour{
@Override
public void action() {
//evaluate globalestore here;
ACLMessage CNIresponse=new ACLMessage();
if(true)
{
storeNotification(ACLMessage.ACCEPT_PROPOSAL, CNIresponse);
}
else
{
storeNotification(ACLMessage.REJECT_PROPOSAL, CNIresponse);
}
}
public void storeNotification(int performative, ACLMessage original)
{
// Retrieve the incoming request from the DataStore
String incomingCFPkey = (String) ((ContractNetResponder) parent).CFP_KEY;
incomingCFPkey = (String) ((ContractNetResponder) parent).CFP_KEY;
ACLMessage incomingCFP = (ACLMessage) getDataStore().get(incomingCFPkey);
// Prepare the notification to the request originator and store it in the DataStore
ACLMessage notification = incomingCFP.createReply();
notification.setPerformative(performative);
notification.setContent(original.getContent());
String notificationkey = (String) ((ContractNetResponder) parent).PROPOSE_KEY;
getDataStore().put(notificationkey, notification);
}
}