1

我在使用ContractNet (交互协议)和使用JADE 多代理框架的GUI时遇到了一些问题。

特别是在handlePropose方法的重写中。我知道我的问题来自于 GUI 的使用。让我解释:

我的代理(发起者)使用第一个 GUI,单击后,对话从第二个代理(响应者)开始。根据协议,发起者因此向响应者发送了一个 CFP。代理 Responder 使用包含不同数据的 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(用于处理答案)也不起作用。启动器的主要 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。

我该如何解决?

4

3 回答 3

1

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

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

答案很复杂,所以我会尽量详尽。

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

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

在此处输入图像描述

  • 代表客户(或买家)的BuyerAgent:每个客户都有一份想要从 A 点移动到 B 点的货物清单。

在此处输入图像描述

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

在 ShipperAgent 中,单击“搜索”按钮开始搜索:启动Contract Net Interaction Protocol


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

在标准 FIPA 中:http ://www.fipa.org/specs/fipa00029/SC00029H.html 在此处输入图像描述

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

进一步你会注意到我必须采取的改变。

  1. ShipperAgent 向每个BuyerAgent 发送CFP。

  2. 每个买方代理:

    2.1 如果他有货,发送PROPOSE给ShipperAgent。

    2.2 如无货,向ShipperAgent 发送REFUSE。对于买方,协议结束。

因为这里很容易。使用嗅探器,我们可以观察到:

在此处输入图像描述

现在:

  1. 托运人代理:

    3.1 收到买家的一个或多个提案,并展示(见下图)。

    3.2 如果收到REFUSE(或在一定时间后没有收到任何东西),则终止与该买家的沟通。

以下是托运人以图形方式显示提案的方式:

在此处输入图像描述

现在由用户来选择想要哪些商品,哪些不想要。

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

在此处输入图像描述

  1. 托运人代理:

     4.1 如果用户选择了一个或多个商品提案(并点击执行),则向相应的BuyerAgent 发送一个ACCEPT_PROPOSAL,其中具体的商品想要(上一个提案的子集)。

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

  2. 买方代理:

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

    5.2 如果收到 ACCEPT_PROPOSAL ma 一件或多件商品不再可用,则发送 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
     */
}

我知道,我住了很多,但我不知道该怎么解释。但是,现在我的项目工作了。但我愿意接受任何建议。

于 2015-01-13T18:59:55.333 回答
0

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

我附上一张图片来更好地解释这种互动。

在此处输入图像描述

因此,在您的 CNI (ContractNetInitiator) 父行为中的 A 点,您想要启动子行为。您可以使用 CNI.registerHandlePropose(new 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);
      } 

    }
于 2015-01-06T11:56:23.990 回答
0

我经常遇到这类问题。这些是有限状态机行为,因此您应该能够暂停和恢复行为,但我不确定如何。我所做的是在发起方和响应方创建两个独立的交互行为。

Initiator                      Responder
|                                |
|                                |
| First behaviour                |The responder only has 1 behaviour
||        CFP->                 ||
||        <-Proposal            ||
|                               ||
| Second behaviour              ||
||        Accept prop->         ||
||        <-Response            ||
|                                |

需要记住的两点

(1)

确保保存对话 ID

msgRecieved.getConversationID

从第一个行为开始并在第二个行为中使用它。

msg.setConversationID().

(2) 第二种行为是另一个 Contract net 发起者,但在 prepareCFPs 方法中设置 MESSAGE 执行以接受提案

类 ContractServiceList 扩展 ContractNetInitiator {

    protected Vector prepareCfps(ACLMessage cfp) {

        ACLmessage AP= new ACLmessage(ACLmessage.ACCEPT_PROPOSAL)
.....

这些事情很难解释,所以我尝试附上一张图片,但有 2 个小代表点。

我现在有足够的代表点来附上我正在做的图片。

在此处输入图像描述

于 2014-12-21T16:31:16.473 回答