环境配置
java11,解压all文件夹,添加CLASS_PATH:jade.jar
启动
java -cp <classpath> jade.Boot -gui
但是我用的是
java jade.Boot -gui
Agent Class
3.3 代理终止
即使在打印欢迎消息后没有其他事情可做,我们的代理仍然在运行。为了使其终止,必须调用其doDelete()方法。与setup()方法类似,该方法由JADE运行时在代理启动时立即调用,旨在包括代理初始化。takeDown()方法在代理终止之前调用,旨在包括代理清理操作。
3.4 向代理传递参数
代理可以在命令行上指定启动参数。可以通过Agent类的getArguments()方法检索这些参数,作为Object数组。如第2章所述,我们希望我们的BookBuyerAgent将要购买的书籍的标题作为命令行参数获取。
// Put agent initializations here
protected void setup() {
// Printout a welcome message
System.out.println(“Hello! 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(“Trying to buy “+targetBookTitle);
}
else {
// Make the agent terminate immediately
System.out.println(“No book title specified“);
doDelete();
}
}
结果:
C:\jade>java jade.Boot buyer:BookBuyerAgent(The-Lord-of-the-rings)
...
...
5-mag-2008 11.11.00 jade.core.AgentContainerImpl joinPlatform
INFO: --------------------------------------
Agent container Main-Container@NBNT2004130496 is ready.
--------------------------------------------
Hello! Buyer-agent buyer@NBNT2004130496:1099/JADE is ready.
Trying to buy The-Lord-of-the-Rings
Behaviour Class
Agent 实际上要执行的任务通常在“ Behaviour ”中完成。Behaviouur 表示代理可以执行的任务,并且实现为一个类的对象,该类extends于jade.core.behaviours.Behaviour。
Behaviour 类必须实现 action() 方法,该方法实际上定义了行为在执行时要执行的操作,以及done()方法(返回一个布尔值),该方法指定行为是否已完成并且必须从代理正在执行的行为池中移除。
行为调度和执行
一个代理可以同时执行多个行为。然而,需要注意的是,在代理中行为的调度不是抢占式的(像Java线程),而是协作式的。这意味着当一个行为被调度执行时,其action()方法被调用并运行直到返回。因此,是程序员定义了代理何时从执行一个行为切换到执行下一个行为。
One-shot behaviour
# 立即完成,其action()方法仅执行一次。jade.core.behaviours.OneShotBehaviour已经通过返回true实现了done()方法,并且可以方便地扩展为实现单次行为。
public class MyOneShotBehaviour extends OneShotBehaviour {
public void action() {
// 执行操作 X
}
}
Cyclic behaviour
# 永不完成,其action()方法每次调用时执行相同的操作。
# jade.core.behaviours.CyclicBehaviour已经通过返回false实现了done()方法,并且可以方便地扩展为实现周期行为。
public class MyCyclicBehaviour extends CyclicBehaviour {
public void action() {
// 执行操作 Y
}
}
Generic behaviours
# 嵌入状态并根据该状态执行不同的操作。当满足给定条件时完成。
public class MyThreeStepBehaviour extends Behaviour {
private int step = 0;
public void action() {
switch (step) {
case 0:
// 执行操作 X
step++;
break;
case 1:
// 执行操作 Y
step++;
break;
case 2:
// 执行操作 Z
step++;
break;
}
}
public boolean done() {
return step == 3;
}
}
在指定时间点调度操作
JADE提供了两个现成的类(位于jade.core.behaviours包中),可以通过它们轻松实现在给定时间点执行某些操作的行为。
WakerBehaviour,其action()和done()方法已经实现,以在给定的超时(在构造函数中指定)到期后执行handleElapsedTimeout()抽象方法。在执行handleElapsedTimeout()方法后,行为完成。
# 在“Adding waker behaviour”打印出现后的10秒后执行操作 X
public class MyAgent extends Agent {
protected void setup() {
System.out.println("Adding waker behaviour");
addBehaviour(new WakerBehaviour(this, 10000) {
protected void handleElapsedTimeout() {
// 执行操作 X
}
});
}
}
TickerBehaviour,其action()和done()方法已经实现,以在每次执行后等待一定的时间周期(在构造函数中指定)来重复执行onTick()抽象方法。TickerBehaviour永远不会完成。
# 每隔10秒定期执行操作 Y
public class MyAgent extends Agent {
protected void setup() {
addBehaviour(new TickerBehaviour(this, 10000) {
protected void onTick() {
// 执行操作 Y
}
});
}
}
ACLMessage Class
发送信息
下面的代码通知一个昵称为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) {
// Process the message
}
阻塞
假设收到信息的Agent是一个卖家,它需要时刻检测时候收到信息,但是Agent的线程开始了一个持续循环,这会消耗极大的CPU资源。为了避免这种情况,我们希望仅在接收到新消息时才执行OfferRequestsServer行为的action()方法。为了做到这一点,我们可以使用Behaviour类的block()方法。这个方法将行为标记为“阻塞”,这样代理就不会再安排它进行执行。当新消息插入代理的消息队列时,所有被阻塞的行为都会再次可用于执行,这样它们就有机会处理接收到的消息。因此,action()方法必须修改如下。Strongly 建议各位收消息写成这个模式。
public void action() {
ACLMessage msg = myAgent.receive();
if (msg != null) {
// Message received. Process it
...
}
else {
block();
}
}
模板
对于卖家Agent,提出询问行为 和 购买行为 都是周期性行为,其action()方法始于对myAgent.receive()的调用,那么我们怎么确保他们收到的是他们需要的信息呢?
为了解决这个问题,我们必须修改到目前为止所呈现的代码,通过在调用receive()方法时指定适当的“模板”来实现。当指定了模板时,receive()方法将返回与其匹配的第一条消息(如果有的话),而忽略所有不匹配的消息。
这些模板被实现为jade.lang.acl.MessageTemplate类的实例,该类提供了许多工厂方法,以非常简单和灵活的方式创建模板。
我们使用CFP(Call For Proposal)执行表现形式的消息携带请求报价,而使用ACCEPT_PROPOSAL执行表现形式的消息携带提案接受,即购买订单。因此,我们修改OfferRequestsServer的action()方法,以便调用myAgent.receive()忽略除了其执行表现形式为CFP的消息之外的所有消息。
public void action() {
MessageTemplate mt = MessageTemplate.MatchPerformative(ACLMessage.CFP);
ACLMessage msg = myAgent.receive(mt);
if (msg != null) {
// CFP Message received. Process it
...
} else {
block();
}
}
复杂对话
JADE在jade.proto包中提供了丰富的支持,用于实现遵循交互协议的对话。特别是我们实现的对话遵循了“合同网络”协议,可以很容易地利用jade.proto.ContractNetInitiator类进行实现。然而,交互协议的支持超出了本教程的范围。有关详细信息,请参阅JADE程序员指南和Javadoc。
DF 交互
一个代理想要发布一个或多个服务,必须向DF提供一个描述,其中包括其AID,可能还包括其他代理需要了解的语言和本体,以及已发布服务的列表。对于每个已发布的服务,都提供了一个描述,其中包括服务类型、服务名称、用于利用该服务所需的语言和本体,以及一些特定于服务的属性。在jade.domain.FIPAAgentManagement包中包含的DFAgentDescription、ServiceDescription和Property类代表了这三个提及的抽象概念。要发布一个服务,代理必须创建一个适当的描述(作为DFAgentDescription类的实例),并调用DFService类的register()静态方法。通常情况下(但不一定如此),服务注册(发布)是在setup()方法中完成的,如下所示,以书卖家代理为例。
protected void setup() {
...
// Register the book-selling service in the yellow pages
DFAgentDescription dfd = new DFAgentDescription();
dfd.setName(getAID());
ServiceDescription sd = new ServiceDescription();
sd.setType(“book-selling”);
sd.setName(“JADE-book-trading”);
dfd.addServices(sd);
try {
DFService.register(this, dfd);
} catch (FIPAException fe) {
fe.printStackTrace();
}
...
}
当代理终止时,最好取消注册已发布的服务。
protected void takeDown() {
// Deregister from the yellow pages
try {
DFService.deregister(this);
} catch (FIPAException fe) {
fe.printStackTrace();
}
// Close the GUI
myGui.dispose();
// Printout a dismissal message
System.out.println(“Seller-agent “+getAID().getName()+” terminating.”);
}