JADE Con​​tractNet和GUI问题

时间:2014-12-02 12:00:36

标签: java user-interface actionlistener agents-jade multi-agent

使用 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。

我该如何解决?

3 个答案:

答案 0 :(得分:1)

我试着回答自己。我不确定这是最好的解决方案,但肯定有效。

请注意,在找到此解决方案之前,我已经详细记录了指南和教程(在http://jade.tilab.com/上),并与其他人面对面 JADE开发人员(在邮件列表中http://jade.tilab.com/pipermail/jade-develop/

答案很复杂,所以我会尝试详尽无遗。

在我的项目中,我必须处理两种不同类型的代理。

  • ShipperAgent ,代表一个托运人:它会跟踪托运人拥有的车辆,可用的车辆以及从中“保留”的货物。

enter image description here

  • 代表客户(或买方)的 BuyerAgent :每个客户都有一个要从A点移到A点的商品清单B点。

enter image description here

这两个代理已注册到黄页服务

在ShipperAgent中,单击“搜索”按钮开始搜索:启动合同网络交互协议


解释合同网络交互协议和我的案例

在标准FIPA中:http://www.fipa.org/specs/fipa00029/SC00029H.html enter image description here

在JADE指南中可以找到:http://jade.tilab.com/doc/programmersguide.pdf(第35页)

此外,您将注意到我必须采取的更改。

  1. ShipperAgent向每个BuyerAgent发送CFP。

  2. 每个BuyerAgent:

    2.1如果他有货物,请向ShipperAgent发送一份保证书。

    2.2如果没有货物,请向ShipperAgent发送REFUSE。对于买方,协议结束。

  3. 因为这里很容易。有了嗅探器,我们可以观察到:

    enter image description here

    现在:

    1. ShipperAgent:

      3.1买家收到一个或多个PROPOSE,并显示(见下图)。

      3.2如果收到REFUSE(或在一段时间后没有收到任何东西),则结束与这些买家的沟通。

    2. 以下是托运人如何以图形方式显示提案:

      enter image description here

      现在由用户选择哪些商品需要哪些商品不需要。

      为了实现这一点,我不得不为代理商自己创建某种“内部通信”:GUI(在3.1中),一旦点击执行,就向代理发送消息。它可能看起来不够优雅,但它似乎是不会破坏协议方ShipperAgent的唯一方法。

      enter image description here

      1. ShipperAgent:

        4.1如果用户选择了一个或多个商品提案(并单击执行),则向相应的BuyerAgent发送ACCEPT_PROPOSAL,其中包含特定商品(先前提案的子集)。

        4.2如果用户没有选择任何商品(或点击取消),则向相应的BuyerAgent发送REJECT_PROPOSAL。结束与该买家的沟通。

      2. BuyerAgent:

        5.1如果收到ACCEPT_PROPOSAL,检查货物是否仍然可用(同时任何其他托运人可以“保留”),如果是,则发送INFORM。

        5.2如果收到ACCEPT_PROPOSAL,则一个或多个商品不再可用,发送FAILURE。

        5.3如果收到REJECT_PROPOSAL,则结束与ShipperAgent的通信。

      3. enter image description here

        简而言之(例如):

        enter image description here


        代码

        <强> 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个小代表。

我现在有足够的重复点来附上我正在做的照片。

enter image description here

答案 2 :(得分:0)

我刚刚意识到这个问题还有另一个解决方案。第二种解决方案涉及使用ChildBehaviours和数据存储。可以启动暂停父行为的子行为。然后必须在孩子完成后恢复父行为。

我附加图片以更好地解释这种互动。

enter image description here

因此,在您的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);
      } 

    }