JADE Agent 类可以不包含main()函数,但是必须包含setup()函数,由setup()来启动Agent并完成一些初始化工作。
Agent运行命令解析
Agent运行命令解析:java jade.Boot [options] [AgentSpecifier list]
Options | 含义 |
---|---|
-container | 表明Agent运行时将启动一个外部容器,Agent生存在这个容器下,这个容器必须在主容器中注册 |
-gui | JADE的RMA图形界面将被打开 |
-port | 指明主容器运行的端口号,缺省端口号为1099,知名端口号后,一台机器上可以启动多个主容器,例如java jade.Boot -gui -port 1088 |
-container-name | 给容器命名。例如,第一次启动,输入:java jade.Boot -gui-container-name yuyu,则将主容器命名为yuyu,再打开一个命令窗口,输入java jade.Boot -gui-container-container-name haha,则将新启动的外围容器命名为haha |
-platform-id | 该选项为Agent平台命名,将默认的平台标识符命名为用户喜欢的标识符 |
-name | 作用同-platform-id |
[AgentSpecifier list]定义启动的Agent列表,其中AgentSpecifier的形式为:AgentName:ClassName,ClassName中必须指出包名。可以启动多个Agent,每个Agent之间用;分隔。
熟悉Agent标识符
Agent类的getAID()
用来获取本地Agent的标识符。Agent标识符由全局唯一标识(GUID)和平台地址组成。GUID命名形式为< local-name >@< platform-name >。AID中包含的地址是
Agent所在平台的地址,这些地址在该agent与其他平台上的Agent通信时使用。
AID类提供了获取Agent本地名称、标识符、地址等一系列方法。如getLocalName()、getName()、getAllAddresses()
package MyFirstAgent;
import jade.core.Agent;
import java.util.Arrays;
import java.util.Iterator;
public class DemoAgent extends Agent {
@Override
protected void setup(){
System.out.println("AgentOne的AID:"+getAID());
System.out.println("AgentOne的local-name:"+getAID().getLocalName());
System.out.println("AgentOne的GUID:"+getAID().getName());
Iterator it=getAID().getAllAddresses();
while (it.hasNext())
{
System.out.println("-"+it.next());
}
}
}
输出:
E:\Develop\Java\jdk-12.0.2\bin\java.exe -Didea.launcher.port=2275 "-Didea.launcher.bin.path=E:\intellij idea2018\IntelliJ IDEA 2018.2.4\bin" -Dfile.encoding=UTF-8 -classpath "E:\学习笔记\yuguo\target\classes;E:\intellij idea2018\IntelliJ IDEA 2018.2.4\plugins\maven\repository\com\tilab\jade\jade\4.5.0\jade-4.5.0.jar;E:\intellij idea2018\IntelliJ IDEA 2018.2.4\plugins\maven\repository\commons-codec\commons-codec\1.3\commons-codec-1.3.jar;E:\intellij idea2018\IntelliJ IDEA 2018.2.4\lib\idea_rt.jar" com.intellij.rt.execution.application.AppMainV2 jade.Boot -gui AgentOne:MyFirstAgent.DemoAgent
7月 04, 2020 3:55:04 下午 jade.core.Runtime beginContainer
信息: ----------------------------------
This is JADE 4.5.0 - revision 6825 of 23-05-2017 10:06:04
downloaded in Open Source, under LGPL restrictions,
at http://jade.tilab.com/
----------------------------------------
7月 04, 2020 3:55:04 下午 jade.imtp.leap.LEAPIMTPManager initialize
信息: Listening for intra-platform commands on address:
- jicp://172.168.1.142:1099
7月 04, 2020 3:55:05 下午 jade.core.BaseService init
信息: Service jade.core.management.AgentManagement initialized
7月 04, 2020 3:55:05 下午 jade.core.BaseService init
信息: Service jade.core.messaging.Messaging initialized
7月 04, 2020 3:55:05 下午 jade.core.BaseService init
信息: Service jade.core.resource.ResourceManagement initialized
7月 04, 2020 3:55:05 下午 jade.core.BaseService init
信息: Service jade.core.mobility.AgentMobility initialized
7月 04, 2020 3:55:05 下午 jade.core.BaseService init
信息: Service jade.core.event.Notification initialized
7月 04, 2020 3:55:05 下午 jade.mtp.http.HTTPServer <init>
信息: HTTP-MTP Using XML parser com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser
7月 04, 2020 3:55:05 下午 jade.core.messaging.MessagingService boot
信息: MTP addresses:
http://PC-20191102IUIM:7778/acc
7月 04, 2020 3:55:05 下午 jade.core.AgentContainerImpl joinPlatform
信息: --------------------------------------
Agent container Main-Container@172.168.1.142 is ready.
--------------------------------------------
AgentOne的AID:( agent-identifier :name AgentOne@172.168.1.142:1099/JADE :addresses (sequence http://PC-20191102IUIM:7778/acc ))
AgentOne的local-name:AgentOne
AgentOne的GUID:AgentOne@172.168.1.142:1099/JADE
-http://PC-20191102IUIM:7778/acc
通过以下的方式获得已知本地名称Agent的AID:
String localname = "Peter";
AID id =new AID (localname,AID.ISLOCALNAME);
类似的已知Agent的GUID后,我们可以用如下的方式获得其AID
String guid = "Peter@foo-platform";
AID id = new AID(guid,AID.ISGUID);
可以向Agent传递参数:
System.out.println("My arguments are:");
Object[] args = getArguments();
if (args!=null){
for (int i = 0; i < args.length; i++) {
System.out.println("-"+args[i]);
}
}
Agent终止:
takeDown()
方法清理,doDelete()
终止
为Agent添加行为
setup()方法执行后,Agent就在AMS中注册成功。它的实际工作任务通常在所添加的行为Behaviour中执行,也就是说,一个活动的Agent相当于一个完成任务的主体,而真正的任务载体时Behaviour类的实例。
每一个Behaviour都是jade.core.behaviours.Behaviours类的子类,一个Behaviour可以嵌套另一个Behaviour。在Agent生命周期的任何时刻都可以调用Behaviour执行某种任务,方法是使用Agent类的addBehaviout(Behaviour)
方法。而且要实现两个抽象方法:action()
和done()
在action()
中定义Agent需要实现的任务;done()
方法返回一个布尔值来判断这个Behaviour是否完成其任务,是否应该将其移出其所在的Agent所管理的行为队列,每个Agent可与i并行执行多个Behaviour。
编写一个FirstBehaviour类,将类添加到Agent的行为队列中
package MyFirstAgent;
import jade.core.behaviours.Behaviour;
public class FirstBehaviour extends Behaviour {
private int numA=5;
@Override
public boolean done() {
return true;
}
@Override
public void action() {
if ((numA%2)!=0){
System.out.println("numA is an odd number");
}
else {
System.out.println("numA is an even number");
}
}
}
package MyFirstAgent;
import jade.core.Agent;
import java.util.Arrays;
import java.util.Iterator;
public class DemoAgent extends Agent {
@Override
protected void setup(){
//添加行为
this.addBehaviour(new FirstBehaviour());
}
}
与其他Agent通信
基本原理
JADE中的Agent之间的通信基于异步通信模式。
每一个JADE Agent都有一个邮箱,即Agent消息队列,其他Agent发送来的信息都投入到这个邮箱中。一旦邮箱中收到消息,系统会给Agent发出通知,由程序元编写程序让Agent有选择地处理消息。
遵循FIPA规范,JADE Agent之间地通信所交换地是ACL消息,每一条消息都是继承了JADE.lang.acl.ACLMessage类的一个对象,包含了由FIPA规范制定的一系列消息属性,主要消息域如下:
- 消息的发送者;
- 消息接收者列表;
- 消息原语,如REQUEST原语表示发送方请求接收方执行某种操作;
- 消息的具体内容;
- 用来表达消息内容的语言,如English;
- 用来说明消息内容中某些词汇或短语的本体Ontology;
- 其他。
准备发送消息的Agent应该创建一个新的ACLMessage对象,给他的属性赋予适当的值,然后调用Agent.send()
方法发送消息。同样地,一个准备接收消息的Agent可以调用receive()
或者blockingReceive()
方法接收消息。
发送消息
发送消息就是填写好ACLMessage对象的各个域,然后调用Agent类的send()方法。
本例中让DemoAgent类的一个Agent实例作为信息发送方,让他向另一个运行时名字为alice的Agent发送信息,通知Alice明天考试。
- 编写一个发送消息的行为类,在Behaviour类的子类SendBehaviour中编写发送消息的代码,而发送消息的send()方法的主体是Agent,而不是Behaviour,需要在SendBehaviour的构造函数中将SengBehaviour与DemoAgent类关联起来。
package MyFirstAgent;
import jade.core.AID;
import jade.core.behaviours.Behaviour;
import jade.lang.acl.ACLMessage;
public class SendBehaviour extends Behaviour {
private DemoAgent sendagent=null;
@Override
public void action() {
ACLMessage msg=new ACLMessage(ACLMessage.INFORM);
msg.addReceiver(new AID("Alice",AID.ISLOCALNAME));
msg.setLanguage("English");
msg.setContent("There will be an examination tomorrow");
sendagent.send(msg);
}
@Override
public boolean done() {
return true;
}
public SendBehaviour(DemoAgent a){
sendagent=a;
}
}
- 修改setup()方法,添加SendBehaviour
package MyFirstAgent;
import jade.core.Agent;
import java.util.Arrays;
import java.util.Iterator;
public class DemoAgent extends Agent {
@Override
protected void setup(){
this.addBehaviour(new SendBehaviour(this));
}
}
接收消息
由于运行时,Agent的实例在时刻监听发送给它的消息,一次性行为不适合处理消息的实时接收,所以,在消息接收时,我们使用了可以处理循环操作的特殊的Behaviour类:CyclicBehaviour
package MyFirstAgent;
import jade.core.Agent;
import jade.core.behaviours.Behaviour;
import jade.core.behaviours.CyclicBehaviour;
import jade.lang.acl.ACLMessage;
public class DemoAgentSecond extends Agent {
@Override
protected void setup() {
Behaviour loop=new CyclicBehaviour() {
@Override
public void action() {
ACLMessage msg=receive();
if(msg!=null){
System.out.println("I received this message:"+msg.getContent());
}
}
};
this.addBehaviour(loop);
}
}
从代码的形式上看是写在同一个文件中,但实际上编译的时候仍会产生两个.class文件,一个对应Agent,一个对应CyclicBehaviour。
同时运行两个Agent输出:
I received this message:There will be an examination tomorrow