以下如果未特殊聲明,都在JADE管理器中運行,然后再Eclipse控制台中查看!
JADE行為類
在前面的例子中,Agent所作的工作都定義在了setup方法中,實際上它具有的行為和執行的動作都應該定義在Behavious類中,我們可以對Behavior類生成實例,然后將任務或者動作代碼放在對Behavious類中的action方法中,action方法是必須要有的。Behavious類還有很多子類,分別對應着不同類型的Behaviour,包括SimpleBehaviour,SequencesBehaviour, ParallelBehavior, CyclicBehavior等。
一個Agent的行為表示它能夠執行的任務,通過繼承jade.core.behaviours.Behaviour來實現。然后在Agent類中通過addBehaviour()方法將行為加入進來。當一個Agent啟動(通過setup()方法后,行為可以在任何時間加入進來。
要定義Behaviour必須實現其action()方法,它定義了Agent的執行時的實際動作,而done()方法指名了一個行為是否已執行完畢,是否要從行為池中刪除。
一個Agent可以並發執行多個behaviour。每個Agent線程啟動后執行的過程如下:
SimpleBehaviour簡單行為
下面的例子中我們不在setup()中打印信息,而是把它放在一個簡單行為中:
在jadetest包里面新建一個java類命名為HelloWorldBehaviour,輸入下面代碼。配置中把變量改為-gui sb:jadetest.SimpleBehaviour,運行
package jadetest; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午5:20:11 */ import jade.core.Agent; import jade.core.Location; import jade.core.behaviours.SimpleBehaviour; public class HelloWorldBehaviours extends Agent { public void setup() { SimpleBehaviour hello_behaviour = new SimpleBehaviour(this) { boolean finished = false; // 覆蓋 Behaviour 類的action這一抽象方法 public void action() { System.out.println("HelloWorldBehaviour run: Hello World!"); System.out.println("-----About Me:-----"); System.out.println("My local name is:" + getLocalName()); System.out.println("My globally unique name is:" + getName()); System.out.println("-----About Here:-----"); Location l = here(); System.out.println("I am running in a location called:" + l.getName()); System.out.println("Which is identified uniquely as:" + l.getID()); System.out.println("And is contactable at:" + l.getAddress()); System.out.println("Using the protocol:" + l.getProtocol()); finished = true; } // done()在父類中也是一個抽象方法 public boolean done() { return finished; } }; addBehaviour(hello_behaviour); } // setup() }
然后使用智能體管理器添加智能體並運行:
輸出結果為:
HelloWorldBehaviour run: Hello World!
-----About Me:-----
My local name is:hwb
My globally unique name is:hwb@172.17.98.217:1099/JADE
-----About Here:-----
I am running in a location called:Main-Container
Which is identified uniquely as:Main-Container@172.17.98.217
And is contactable at:172.17.98.217
Using the protocol:jicp
這個程序相較於前面的例子多了action, done兩個函數,它們分別執行自己的操作。HelloWorldBehaviours類加載時定義一個簡單行為,這個簡單行為執行的操作由action,done來實現。然后,再通過加載語句(addBehaviour(hello_behaviour))執行這個簡單行為
簡單行為和循環行為(CyclicBehaviour)組合的例子
一個Agent中可以加入各種Behaviour構成CompositeBehaviour。
在Eclipse的jade工程中編寫下列程序,過程如前所描述。
package jadetest; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午8:46:29 */ import jade.core.Agent; import jade.core.behaviours.Behaviour; import jade.core.behaviours.CyclicBehaviour; import jade.core.behaviours.OneShotBehaviour; public class SimpleAgent extends Agent { private class FourStepBehaviour extends Behaviour { // SimpleBehaviour hello_behaviour = new SimpleBehaviour(this){ // 這兩條語句有很大不同嗎?上面的定義一個新的類,下面的修改了標准行為的執行內容 private int step = 1; public void action() { switch (step) { case 1: System.out.println("Operation 1"); break; case 2: System.out.println("Operation 2. Adding one-shot behaviour"); myAgent.addBehaviour(new OneShotBehaviour(myAgent) { // 增加了一個新的動作,即快照 public void action() { System.out.println("One-shot"); } });// myAgent.addbehaviour break; case 3: System.out.println("Operation 3"); break; case 4: System.out.println("Operation 4"); break; }// switch step++; } // action public boolean done() { return step == 5; // 判斷語句 } public int onEnd() { myAgent.doDelete(); System.out.println("Finished!"); return super.onEnd(); } } // class FourStepBehaviour /** Creates a new instance of SimpleAgent */ protected void setup() { System.out.println("Agent " + getLocalName() + " started."); addBehaviour(new CyclicBehaviour(this) { // 增加了一個循環行為 public void action() { System.out.println("Cycling"); } }); // //增加的循環行為 // Add the generic behaviour addBehaviour(new FourStepBehaviour()); }// //setup() }
輸出結果:
Agent sa started.
Cycling
Operation 1
Cycling
Operation 2. Adding one-shot behaviour
Cycling
Operation 3
One-shot
Cycling
Operation 4
Finished!
注意每個Agent內部都有一個ParallelBehaviour,我們如果加入多個behaviour到Agent中,他們會並行執行。behaviours加入到隊列的順序就是他們執行的次序。最后,behaviours可以動態的加入到Agent以及CompositeBehaviour。
總結:每個主體的執行都是從setup() 開始順序執行的。主體可以執行自定義的行為,如上例中的hello_behaviour ,FourStepBehaviour,也可以執行標准化的行為如OneShotBehaviour。
Agent通訊:ACL(Agent communication language)
JADE的Agent之間進行通信使用的acl語言遵循fipa acl規范。一個acl消息通常包含這些參數:sender:消息的發送者,用Agent標志AID表示; receivers,接受Agent消息的Agent可以是多個;Reply-to,應收到回應的接受者;Performative:標志發送消息的目的,即發送者想要通過發送消息干什么,通常有這樣一些常值:REQUEST, INFORM, ACCEPT_PROPOSAL, REJECT_PROPOSAL, PROPOSE;Content,消息的內容;內容語言,比如內容的編碼格式;ontology,雙方都能夠理解的消息內容的概念說明和語義描述。
簡單實例
發送者:
package jadetest; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午9:00:25 */ import jade.core.AID; import jade.core.Agent; import jade.core.behaviours.Behaviour; import jade.lang.acl.ACLMessage; public class SimpleSender extends Agent { protected void setup() { addBehaviour(new Behaviour(this) { private boolean finished = false; public void action() { System.out.println(getLocalName() + ": about to inform bob hello"); // we sleep here to give bob a chance to start. doWait(5000); AID r = new AID();// 作用是什么?表示消息的發送者 r.setLocalName("bob"); ACLMessage msg = new ACLMessage(ACLMessage.INFORM); // set performative msg.setSender(getAID()); msg.addReceiver(r); msg.setContent("Hello_BOB"); send(msg); System.out.println(getLocalName() + ": Send hello to bob"); System.out.println("the content is:" + msg.getContent()); // finished = true; doWait(5000); doDelete(); } // action public boolean done() { return finished; } }); // addbehavior }// setup }// Agent
這段代碼的主要執行過程為:構建一個AID,以此來指出該消息的目的Agent。這里我們指定目的為一個本地的Agent,名字為bob。建立一個ACL消息標志其performative為INFORM。設定Sender為自身,指定接收者為bob。然后發送消息內容。打印相關信息。
接收者:他的名字必須為bob
package jadetest; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午9:01:36 */ import jade.core.Agent; import jade.core.behaviours.SimpleBehaviour; import jade.lang.acl.ACLMessage; public class SimpleReceiver extends Agent { class DoSimpleReceiveBehaviour extends SimpleBehaviour { private boolean finished = false; public DoSimpleReceiveBehaviour(Agent Agent) { super(Agent); } public void action() { ACLMessage msg = receive(); if (msg != null) { System.out.println(getLocalName() + ": received the following message : "); System.out.println(msg.toString()); finished = true; myAgent.doDelete(); } else { System.out.println(getLocalName() + ":No message received, Blocking the behaviour till one is"); block(); } }// action public boolean done() { return finished; } }// DoSimpleReceiveBehaviour protected void setup() { DoSimpleReceiveBehaviour behaviour = new DoSimpleReceiveBehaviour(this); addBehaviour(behaviour); } }// Agent
接收者的代碼流程為:添加一個簡單行為,這一行為檢查現在是否有受到消息,若沒有,則執行block()方法組織目前的behaviour執行,直到有新的消息到達。
復雜實例
FIPA定義了一組交互協議,包括FIPA-request, FIPA-query, FIPA-request-when, FIPA-contract-net, FIPA-Iterater-net, FIPA-Auction-English, FIPA-Auction-Dutch.其中:
REQUEST-INFORM:A請求B做一些工作,B可以同意或拒絕。如果B同意,則會去完成並告訴A該工作已經完成。等等。
Query:A想知道一些事情,B可以同意或不同意,並將B的回應告訴A。
Propose:在給定一些precondition的條件下,提出一個proposal去執行某些動作。
在Eclipse中創建文件夾ips:其代碼文件有兩個,分別為
package ips; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午9:03:05 */ import jade.core.AID; import jade.core.Agent; import jade.domain.FIPANames.InteractionProtocol; import jade.lang.acl.ACLMessage; import jade.proto.SimpleAchieveREInitiator; public class SimpleRequestInitiator extends Agent { static class MarriageProposer extends SimpleAchieveREInitiator { protected MarriageProposer(Agent Agent, ACLMessage msg) { super(Agent, msg); } protected void handleAgree(ACLMessage msg) { System.out.println(myAgent.getLocalName() + ": 吼吼! " + msg.getSender().getLocalName() + " 已經同意嫁給我了, I'm so excited!"); } protected void handleRefuse(ACLMessage msg) { System.out.println(myAgent.getLocalName() + ": Oh no! " + msg.getSender().getLocalName() + " 拒絕了我, i feel sad."); } protected void handleInform(ACLMessage msg) { System.out.println(myAgent.getLocalName() + ":" + msg.getSender().getLocalName() + " has informed me of the status of my request." + " They said : " + msg.getContent()); } protected void handleNotUnderstood(ACLMessage msg) { System.out.println(myAgent.getLocalName() + ":" + msg.getSender().getLocalName() + " has indicated that they didn't understand."); } protected void handleOutOfSequence(ACLMessage msg) { System.out.println(myAgent.getLocalName() + ":" + msg.getSender().getLocalName() + "has sent me a message which I wasn't" + " expecting in this conversation"); } } protected void setup() { System.out.println(getLocalName() + ": about to propose marriage to bob "); doWait(5000); // wait for bob to be started. ACLMessage msg = new ACLMessage(ACLMessage.REQUEST); AID to = new AID(); to.setLocalName("bob"); msg.setSender(getAID()); msg.addReceiver(to); msg.setContent("Marry Me!"); msg.setProtocol(InteractionProtocol.FIPA_REQUEST); addBehaviour(new MarriageProposer(this, msg)); } }
還有:
package ips; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午9:03:57 */ import jade.core.AID; import jade.core.Agent; import jade.domain.FIPANames.InteractionProtocol; import jade.lang.acl.ACLMessage; import jade.proto.SimpleAchieveREResponder; public class SimpleRequestResponder extends Agent { static class MarriageResponder extends SimpleAchieveREResponder { public MarriageResponder(Agent Agent) { super(Agent, createMessageTemplate(InteractionProtocol.FIPA_REQUEST)); } protected ACLMessage prepareResponse(ACLMessage msg) { ACLMessage response = msg.createReply(); if (msg.getContent() != null && msg.getContent().equals("Marry Me!")) { System.out.println(myAgent.getLocalName() + ":" + msg.getSender().getLocalName() + " has asked me to marry him!"); AID sender; sender = msg.getSender(); if (sender.getLocalName().equals("baz")) { response.setPerformative(ACLMessage.AGREE); System.out.println(myAgent.getLocalName() + ":I'm going to agree."); } else { response.setPerformative(ACLMessage.REFUSE); System.out.println(myAgent.getLocalName() + ":I'm going to turn him down."); } } else { response.setPerformative(ACLMessage.NOT_UNDERSTOOD); System.out.println(myAgent.getLocalName() + ":I didn't understand what " + msg.getSender().getLocalName() + " just said to me."); } return response; } protected ACLMessage prepareResultNotification(ACLMessage inmsg, ACLMessage outmsg) { // what they have asked is now complete (or if it failed) ACLMessage msg = inmsg.createReply(); msg.setPerformative(ACLMessage.INFORM); msg.setContent("I Do!"); return msg; } } protected void setup() { System.out.println(getLocalName() + ": I wonder if anybody wants to marry me?"); addBehaviour(new MarriageResponder(this)); }// }
按照以前記載,先打開GUI管理器,主類依然為jade.Boot,參數為-gui,GUI管理打開之后先建立一個名為bob的Agent,對應的類為ips.SimpleRequestResponder,然后再建立一個名為baz的Agent,對應的類為ips.SimpleRequestInitiator,記住順序不能變,以下是輸出結果:
bob: I wonder if anybody wants to marry me?
baz: about to propose marriage to bob
bob:baz has asked me to marry him!
bob:I'm going to agree.
baz: 吼吼! bob 已經同意嫁給我了, I'm so excited!
baz:bob has informed me of the status of my request. They said : I Do!
上例中,應用了SimpleAchieveREInitiator和SimpleAchieveREResponder兩個基類,適用於兩個Agent之間的交互。可以看出發起者對於不同的回應有不同的執行動作。
技巧:從AMS中獲取所有Agent的AID。
package ips; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月16日 下午1:52:33 */ import jade.core.Agent; import jade.domain.AMSService; import jade.domain.FIPAAgentManagement.AMSAgentDescription; import jade.domain.FIPAAgentManagement.SearchConstraints; import jade.lang.acl.ACLMessage; public class AMSDump extends Agent { protected void setup() { AMSAgentDescription[] Agents = null; try { SearchConstraints c = new SearchConstraints(); c.setMaxResults(new Long(-1)); Agents = AMSService.search(this, new AMSAgentDescription(), c); } catch (Exception e) { System.out.println("Problem searching AMS: " + e); e.printStackTrace(); } ACLMessage msg = new ACLMessage(ACLMessage.INFORM); msg.setContent("Ping"); for (int i = 0; i < Agents.length; i++) { if (Agents[i].getName().equals(getAID())) { // 如果不是自己則加入到接收者數組中 continue; } msg.addReceiver(Agents[i].getName()); } } }
ACL高級特性之消息模板:
MessageTemplate class 利用MessageTemplate可以針對ACLMessage的每個屬性設置模式,以達到過濾消息的目的。為了可以構建更復雜的匹配規則,多個模式也可以進行and,or,not運算。最有用的一些規則或方法包括:通信行為匹配,發送者匹配,會話ID匹配。
比如MatchPerformative( performative ) 是通信行為的匹配。
這里 performative 可能是:
ACLMessage.INFORM
ACLMessage.PROPOSE
ACLMessage.AGREE
還有發送者匹配MatchSender( AID ),會話匹配MatchConversationID( String ),通信協議匹配MatchProtocol( String ) ,本體匹配MatchOntology( String)。
比如:MessageTemplate mt = MessageTemplate.and(
MessageTemplate.MatchPerformative( ACLMessage.INFORM ),
MessageTemplate.MatchSender( new AID( "a1", AID.ISLOCALNAME))) ;
相當於建立了一個模板,表示消息規則為INFORM行為並且發送者為“a1”。
接收過程如下:ACLMessage msg = receive( mt );
if (msg != null) { ... handle message }
block();
示例:
package jadePrime.acl; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午9:07:11 */ import jade.core.AID; import jade.core.Agent; import jade.core.behaviours.CyclicBehaviour; import jade.lang.acl.ACLMessage; import jade.lang.acl.MessageTemplate; public class Template extends Agent { MessageTemplate mt1 = MessageTemplate.and(MessageTemplate.MatchPerformative(ACLMessage.INFORM), MessageTemplate.MatchSender(new AID("a1", AID.ISLOCALNAME))); protected void setup() { // Send messages to "a1" and "a2" ACLMessage msg = new ACLMessage(ACLMessage.INFORM); msg.setContent("Ping"); for (int i = 1; i <= 2; i++) msg.addReceiver(new AID("a" + i, AID.ISLOCALNAME)); send(msg); // Set-up Behaviour 1 addBehaviour(new CyclicBehaviour(this) { public void action() { System.out.print("Behaviour ONE: "); ACLMessage msg = receive(mt1); if (msg != null) System.out.println("gets " + msg.getPerformative() + " from " + msg.getSender().getLocalName() + "=" + msg.getContent()); else System.out.println("gets NULL"); block(); } }); // Set-up Behaviour 2 addBehaviour(new CyclicBehaviour(this) { public void action() { System.out.print("Behaviour TWO: "); ACLMessage msg = receive(); if (msg != null) System.out.println("gets " + msg.getPerformative() + " from " + msg.getSender().getLocalName() + "=" + msg.getContent()); else System.out.println("gets NULL"); block(); } }); } }
package jadePrime.acl; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月14日 下午9:10:07 */ import jade.core.Agent; import jade.core.behaviours.CyclicBehaviour; import jade.lang.acl.ACLMessage; public class Responder extends Agent { protected void setup() { addBehaviour(new CyclicBehaviour(this) { public void action() { ACLMessage msg = receive(); if (msg != null) { ACLMessage reply = msg.createReply(); reply.setPerformative(ACLMessage.INFORM); reply.setContent(" Gossip....."); send(reply); reply.setPerformative(ACLMessage.PROPOSE); reply.setContent(" Really sexy stuff... cheap! "); send(reply); } block(); } }); } }
輸出結果:
Behaviour ONE: gets NULL
Behaviour TWO: gets 6 from ams=( (action ( agent-identifier :name template@192.168.23.1:1099/JADE :addresses (sequence http://jacksile:7778/acc )) (ACLMessage) ) (MTS-error ( agent-identifier :name a1@192.168.23.1:1099/JADE ) (internal-error "Agent not found: getContainerID() failed to find agent a1@192.168.23.1:1099/JADE")) )
Behaviour ONE: gets NULL
Base64:發送消息為java序列化對象
在JADE中,支持Agents之間通信的消息內容使用序列化的java對象,對本地應用,特別是所有Agent都用java實現的情況下也是很有用的.
看實例:一個ObjectSender負責發送一個Person對象,ObjectReceiver負責接收后打印出接收到的內容。
源文件:Person.java
package examples.Base64; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:34:47 */ import java.io.Serializable; import java.util.Date; public class Person implements Serializable { String name; String surname; Date birthdate; int age; Person(String n, String s, Date d, int a) { name = n; surname = s; birthdate = d; age = a; } public String toString() { return (name + " " + surname + " born on " + birthdate.toString() + " age = " + age); } }
文件2:ObjectSender.java
package examples.Base64; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:35:47 */ import jade.core.AID; import jade.core.Agent; import jade.lang.acl.ACLMessage; import java.io.IOException; import java.util.Date; public class ObjectSender extends Agent { protected void setup() { try { ACLMessage msg = new ACLMessage(ACLMessage.INFORM); msg.setContent("Ping"); AID personR = new AID("personR", AID.ISLOCALNAME); msg.addReceiver(personR); Person p = new Person("Name1", "Surname1", new Date(), 1); msg.setContentObject(p); msg.setLanguage("JavaSerialization"); send(msg); System.out.println(getLocalName() + " sent 1st msg " + msg); send(msg); } catch (IOException e) { e.printStackTrace(); } doDelete(); // kill itself because it has completed its task. } }
文件三:ObjectReceiver.java
package examples.Base64; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:37:17 */ import jade.core.Agent; import jade.core.behaviours.CyclicBehaviour; import jade.lang.acl.ACLMessage; import jade.lang.acl.UnreadableException; public class ObjectReceiver extends Agent { protected void setup() { addBehaviour(new CyclicBehaviour(this) { public void action() { try { ACLMessage msg = blockingReceive(); System.out.println(getLocalName() + " rx msg" + msg); if ("JavaSerialization".equals(msg.getLanguage())) { Person p = (Person) msg.getContentObject(); System.out.println(getLocalName() + " 獲取Java對象: "); System.out.println(p.getClass().getName()); System.out.println(p.toString()); } } catch (UnreadableException e3) { System.err.println(getLocalName() + " catched exception " + e3.getMessage()); } block(); } }); } }
外部應用程序調用Agent
JADE2.3以后的版本都提供了in-process接口來實現外部應用程序對Agent的調用。
我們可以通過jade.core.Runtime.instance()來獲得jade運行時的一個單獨得實例。有兩種方法可以用來創建一個jade主容器和一個jade遠程容器。主要調用過程如下:
先看簡單的例子:
package inprocess; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:48:37 */ import jade.core.Agent; import jade.core.behaviours.SimpleBehaviour; /** * * @author Administrator */ public class CustomAgent extends Agent { public void setup() { SimpleBehaviour helloBehaviour = new SimpleBehaviour(this) { boolean finished = false; public void action() { System.out.println("Hello World Behaviour run: 你好,世界!"); System.out.println("-----我是:-----"); System.out.println("我的本地名稱是:" + getLocalName()); System.out.println("我全局唯一的標志名稱為:" + getName()); finished = true; } public boolean done() { return finished; } }; addBehaviour(helloBehaviour); } }
以上是要調用的Agent類
package inprocess; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:49:43 */ import jade.core.Profile; import jade.core.ProfileImpl; import jade.core.Runtime; import jade.wrapper.AgentContainer; import jade.wrapper.AgentController; public class InprocessTest { public static void main(String args[]) { try { Runtime rt = Runtime.instance(); rt.setCloseVM(true); Profile pMain = new ProfileImpl(null, 8888, null); System.out.println("Launching a whole in-process platform..." + pMain); AgentContainer mc = rt.createMainContainer(pMain); // set now the default Profile to start a container ProfileImpl pContainer = new ProfileImpl(null, 8888, null); System.out.println("Launching the Agent container ..." + pContainer); AgentController custom = mc.createNewAgent("custom", "inprocess.CustomAgent", null); custom.start(); } catch (Exception e) { e.printStackTrace(); } } }// 以上是調用者,在Eclipse中調試通過。
再看一個例子,也是main函數中動態創建Agent的例子。可以通過先創建另一個容器,然后再這個容器中創建Aent。比如:
public static void main(String[] args) { // TODO code application logic here Runtime rt = Runtime.instance(); rt.setCloseVM(true); ContainerController cc = rt.createAgentContainer(new ProfileImpl(false)); AgentController pingAgent = null; try { // create Agent and start it (cc.createNewAgent("hello", "jadeclient.HelloWorldAgent", new Object[0])).start(); } catch (Exception e){} }
可以寫如下測試程序,這里用Eclipse平台,一個HelloWorldAgent,他將被動態創建並向主容器中的server發送一個消息:
package jadeclient; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:50:50 */ import jade.core.AID; import jade.core.Agent; import jade.core.behaviours.SimpleBehaviour; import jade.lang.acl.ACLMessage; public class HelloWorldAgent extends Agent { public void setup() { addBehaviour(new SimpleBehaviour(this) { boolean finished = false; AID server = new AID("server", AID.ISLOCALNAME); public void action() { System.out.println("我的本地名稱是:" + getLocalName()); ACLMessage msg = new ACLMessage(ACLMessage.INFORM); msg.setContent("消息內容"); msg.addReceiver(server); send(msg); System.out.println("已經往主容器中的server發送信息#"); block(1000); finished = true; } public boolean done() { return finished; } }); }; }
而server角色是收到消息后打印出來。
/* * ReceiverAgent.java * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package jadeclient; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:51:57 */ import jade.core.Agent; import jade.core.behaviours.CyclicBehaviour; import jade.lang.acl.ACLMessage; /** * * @author admin */ public class ReceiverAgent extends Agent { /** Creates a new instance of ReceiverAgent */ // 把接收到的信息打印出來 protected void setup() { addBehaviour(new CyclicBehaviour(this) { public void action() { ACLMessage msg = receive(); if (msg != null) System.out .println("收到信息 " + myAgent.getLocalName() + " <- " + msg.getContent()); block(); } }); } }
所有類寫完之后,這里運行的時候先打開GUI管理器,然后GUI界面下創建一個名叫server的Agent,其類為jadeclient. ReceiverAgent,然后再創建一個名叫hello的Agent,其類為jadeclient.HelloWorldAgent.,記住,順序不能變
在GUI界面管理器顯示如下,在Eclipse平台中下面顯示的是打印出的信息,可見在同一主機的不同容器中Agent通信與在同一容器中通信並無二樣。
看一個稍微復雜的例子:
package examples.inprocess; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:53:06 */ import jade.core.Profile; import jade.core.ProfileImpl; import jade.core.Runtime; import jade.core.behaviours.CyclicBehaviour; import jade.wrapper.AgentContainer; import jade.wrapper.AgentController; public class InProcessTest { // Simple class behaving as a Condition Variable public static class CondVar { private boolean value = false; synchronized void waitOn() throws InterruptedException { while (!value) { wait(); } } synchronized void signal() { value = true; notifyAll(); } } // End of CondVar class // This class is a custom Agent, accepting an Object through the // object-to-Agent communication channel, and displying it on the // standard output. public static class CustomAgent extends jade.core.Agent { public void setup() { // Accept objects through the object-to-Agent communication // channel, with a maximum size of 10 queued objects setEnabledO2ACommunication(true, 10); // Notify blocked threads that the Agent is ready and that // object-to-Agent communication is enabled Object[] args = getArguments(); if (args.length > 0) { CondVar latch = (CondVar) args[0]; latch.signal(); } // Add a suitable cyclic behaviour... addBehaviour(new CyclicBehaviour() { public void action() { // Retrieve the first object in the queue and print it on // the standard output Object obj = getO2AObject(); if (obj != null) { System.out.println("Got an object from the queue: [" + obj + "]"); } else block(); } }); } public void takeDown() { // Disables the object-to-Agent communication channel, thus // waking up all waiting threads setEnabledO2ACommunication(false, 0); } } // End of CustomAgent class public static void main(String args[]) { try { Runtime rt = Runtime.instance();// 獲取jade運行時 // Exit the JVM when there are no more containers around rt.setCloseVM(true); // 看運行參數中是否有-container if (args.length > 0) { if (args[0].equalsIgnoreCase("-container")) { // 創建一個默認的profile Profile p = new ProfileImpl(false); // p.setParameter(Profile.MAIN, "false"); // Create a new non-main container, connecting to the default // main container (i.e. on this host, port 1099) System.out.println("Launching the Agent container ..." + p); AgentContainer ac = rt.createAgentContainer(p); // 創建一個新的Agent AgentController dummy = ac.createNewAgent("inProcess", "jade.tools.DummyAgent.DummyAgent", new Object[0]); // 啟動它 System.out.println("Starting up a DummyAgent..."); dummy.start(); // 等10秒 Thread.sleep(10000); // 殺死這個Agent System.out.println("Killing DummyAgent..."); dummy.kill(); // 在同一虛擬機上創建另一個容器,NB, // NB. 兩個容器不能共享同一個 Profile對象!!! --> // 所以需再創建一個profile對象 p = new ProfileImpl(false); // p.putProperty(Profile.MAIN, "false"); AgentContainer another = rt.createAgentContainer(p); // 用兩個參數創建一個移動agnet Object[] arguments = new Object[2]; arguments[0] = "Hello World!"; arguments[1] = dummy; AgentController mobile = another.createNewAgent("Johnny", "examples.mobile.MobileAgent", arguments); mobile.start(); return; } } // 在8888端口運行一個完整的平台t // create a default Profile Profile pMain = new ProfileImpl(null, 8888, null); System.out.println("Launching a whole in-process platform..." + pMain); AgentContainer mc = rt.createMainContainer(pMain); // 使用默認的profile啟動一個容器 ProfileImpl pContainer = new ProfileImpl(null, 8888, null); System.out.println("Launching the Agent container ..." + pContainer); AgentContainer cont = rt.createAgentContainer(pContainer); System.out.println("Launching the Agent container after ..." + pContainer); System.out.println("Launching the rma Agent on the main container ..."); AgentController rma = mc.createNewAgent("rma", "jade.tools.rma.rma", new Object[0]); rma.start(); // Launch a custom Agent, taking an object via the // object-to-Agent communication channel. Notice how an Object // is passed to the Agent, to achieve a startup synchronization: // this Object is used as a POSIX 'condvar' or a Win32 // 'EventSemaphore' object... CondVar startUpLatch = new CondVar(); AgentController custom = mc.createNewAgent("customAgent", CustomAgent.class.getName(), new Object[] { startUpLatch }); custom.start(); // Wait until the Agent starts up and notifies the Object try { startUpLatch.waitOn(); } catch (InterruptedException ie) { ie.printStackTrace(); } // Put an object in the queue, asynchronously System.out.println("Inserting an object, asynchronously..."); custom.putO2AObject("Message 1", AgentController.ASYNC); System.out.println("Inserted."); // Put an object in the queue, synchronously System.out.println("Inserting an object, synchronously..."); custom.putO2AObject(mc, AgentController.SYNC); System.out.println("Inserted."); } catch (Exception e) { e.printStackTrace(); } } }
現在看看servlet能不能調用這個,在Eclipse中建立一個web工程,添加jade庫,建立一個servlet在servlet中進行調用Agent,看能不能輸出結果。
其中主要文件如下
文件名:StartServlet.java:
package examples; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午12:57:37 */ import jade.core.Profile; import jade.core.ProfileImpl; import jade.core.Runtime; import jade.wrapper.AgentContainer; import jade.wrapper.AgentController; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class StartServlet extends HttpServlet { protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>Servlet StartServlet</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>Servlet StartServlet at " + request.getContextPath() + "</h1>"); out.println("<font size=10 >你好</font>"); try { Runtime rt = Runtime.instance(); rt.setCloseVM(true); Profile pMain = new ProfileImpl(null, 8888, null); AgentContainer mc = rt.createMainContainer(pMain); // set now the default Profile to start a container ProfileImpl pContainer = new ProfileImpl(null, 8888, null); out.println("運行Agent容器 ..." + pContainer); AgentController rma = mc.createNewAgent("rma", "jade.tools.rma.rma", null); rma.start(); AgentController custom = mc.createNewAgent("custom", "examples.CustomAgent", null); custom.start(); out.println("我已經啟動了一個小Agent"); } catch (Exception e) { e.printStackTrace(); } out.println("</body>"); out.println("</html>"); out.close(); } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { processRequest(request, response); } public String getServletInfo() { return "Short description"; } }
CustomAgent.java:
package examples; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午1:03:40 */ import jade.core.Agent; import jade.core.behaviours.SimpleBehaviour; public class CustomAgent extends Agent { public void setup() { SimpleBehaviour helloBehaviour = new SimpleBehaviour(this) { boolean finished = false; public void action() { System.out.println("Hello World Behaviour run: 你好,世界!"); System.out.println("-----我是:-----"); System.out.println("我的本地名稱是:" + getLocalName()); System.out.println("我全局唯一的標志名稱為:" + getName()); finished = true; try { Thread.sleep(40000); } catch (java.lang.InterruptedException e) { e.printStackTrace(); } System.out.println("已經過了40秒鍾"); // 這里是為了測試關掉IE之后控制台上還會不會輸出信息。 } public boolean done() { return finished; } }; addBehaviour(helloBehaviour); } }
web.xml:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1"> <display-name>jade</display-name> <servlet> <servlet-name>StartServlet</servlet-name> <servlet-class>examples.StartServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>StartServlet</servlet-name> <url-pattern>/StartServlet</url-pattern> </servlet-mapping> <session-config> <session-timeout> 30 </session-timeout> </session-config> <welcome-file-list> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>index.jsp</welcome-file> <welcome-file>default.html</welcome-file> <welcome-file>default.htm</welcome-file> <welcome-file>default.jsp</welcome-file> </welcome-file-list> </web-app>
在url:http://localhost:8888/jade/StartServlet中看到如下結果:
Servlet StartServlet at /jade
你好 運行Agent容器 ...(Profile main=true local-host=59.73.87.114 port=8888 services=jade.core.mobility.AgentMobilityService;jade.core.event.NotificationService host=59.73.87.114 local-port=8888 mtps=[jade.mtp.http.MessageTransportProtocol] jvm=j2se) 我已經啟動了一個小Agent
在Tomcat控制台上輸出信息:
Hello World Behaviour run: 你好,世界!
-----我是:-----
我的本地名稱是:custom
我全局唯一的標志名稱為:custom@deepin-84e2a07b:8888/JADE
已經過了40秒鍾
同時圖形化JADE容器運行,其中“已經過了40秒鍾”是在瀏覽器關閉之后顯示,說明Agent容器及Agent啟動后一直運行着。
奇怪的是,我每次退出主容器,總顯示
Exception while removing reference: java.lang.InterruptedException
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)
at sun.java2d.Disposer.run(Disposer.java:107)
at java.lang.Thread.run(Thread.java:595)
AWT blocker activation interrupted: //后面這個是JADE GUI的bug么?
java.lang.InterruptedException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:474)
at sun.awt.AWTAutoShutdown.activateBlockerThread(AWTAutoShutdown.java:309)
at sun.awt.AWTAutoShutdown.setToolkitBusy(AWTAutoShutdown.java:226)
at sun.awt.AWTAutoShutdown.notifyToolkitThreadBusy(AWTAutoShutdown.java:118)
at sun.awt.windows.WToolkit.eventLoop(Native Method)
at sun.awt.windows.WToolkit.run(WToolkit.java:269)
at java.lang.Thread.run(Thread.java:595)
不知道什么問題?
一個Agent如何以程序的方式生成另一個Agent:
可以通過以下方式進行創建:
String name = "Alice" ; AgentContainer c = getContainerController(); try { AgentController a = c.createNewAgent( name, "Pong", null ); a.start(); } catch (Exception e){}
其中,createNewAgent方法的第一個參數是要創建的Agent的名稱,第二個是Agent的類名,實際使用時要包括他所在的命名空間(包名),第三個參數是要傳入的參數的名稱。
例子:
package jadePrime.acl; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午1:08:17 */ /** * Program which creates another Agent and sends ------------ it some messages comm2.java * */ import jade.core.AID; import jade.core.Agent; import jade.core.behaviours.SimpleBehaviour; import jade.lang.acl.ACLMessage; import jade.wrapper.AgentContainer; import jade.wrapper.AgentController; public class Comm2 extends Agent { String name = "Alice"; AID alice = new AID(name, AID.ISLOCALNAME); protected void setup() { AgentContainer c = getContainerController(); System.out.println("find container!"); try { AgentController a = c.createNewAgent(name, "jadePrime.acl.Pong", null); a.start(); System.out.println("++++pong has created:" + alice); } catch (Exception e) { System.out.println("Create Agent Error!"); addBehaviour(new SimpleBehaviour(this) { int n = 0; public void action() { ACLMessage msg = new ACLMessage(ACLMessage.INFORM); msg.setContent("Message #" + n); msg.addReceiver(alice); System.out.println("+++ Sending: " + n); send(msg); block(1000); } public boolean done() { return ++n > 3; } }); } } }
當我們創建一個Comm2的Agent時,在控制台上會打印出創建alice成功的消息。
如何收集當前AMS中Agent列表
package jadePrime.manage; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午1:09:53 */ import jade.core.AID; import jade.core.Agent; import jade.domain.AMSService; import jade.domain.FIPAAgentManagement.AMSAgentDescription; import jade.domain.FIPAAgentManagement.SearchConstraints; public class AMSDump extends Agent { protected void setup() { AMSAgentDescription[] Agents = null; try { SearchConstraints c = new SearchConstraints(); c.setMaxResults(new Long(-1)); Agents = AMSService.search(this, new AMSAgentDescription(), c); } catch (Exception e) { System.out.println("Problem searching AMS: " + e); e.printStackTrace(); } AID myID = getAID(); for (int i = 0; i < Agents.length; i++) { AID AgentID = Agents[i].getName(); System.out.println((AgentID.equals(myID) ? "*** " : " ") + i + ": " + AgentID.getName()); } doDelete(); // System.exit(0); } }// end class AMSDump
詳見:http://www.iro.umontreal.ca/%7Evaucher/Agents/JADE/primer4.html#5
JADE中如何使用DF(Directory Facilitator)Agent提供的黃頁服務。
這個比較好理解,DF相當於一個目錄服務器,每個提供服務的Agent可以向DF注冊其服務,然后,其他的Agent可以從DF中查詢該類服務,也可以訂閱這類服務,如果是后者,那么一旦這類服務被注冊到DF中,則訂閱方就可以收到。例程如下:
文件一, DFRegisterAgent.java 服務提供方
package yellowpages; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午1:11:14 */ import jade.core.Agent; import jade.domain.DFService; import jade.domain.FIPAException; import jade.domain.FIPANames; import jade.domain.FIPAAgentManagement.DFAgentDescription; import jade.domain.FIPAAgentManagement.Property; import jade.domain.FIPAAgentManagement.ServiceDescription; public class DFRegisterAgent extends Agent { protected void setup() { String serviceName = "unknown"; // 從命令行的參數中讀取服務的名稱,默認為unknown Object[] args = getArguments(); if (args != null && args.length > 0) { serviceName = (String) args[0]; } // 注冊服務 System.out.println("Agent " + getLocalName() + " registering service \"" + serviceName + "\" of type \"weather-forecast\""); try { // 必要的幾個步驟 DFAgentDescription dfd = new DFAgentDescription(); dfd.setName(getAID()); ServiceDescription sd = new ServiceDescription(); sd.setName(serviceName); sd.setType("weather-forecast");// 設置服務類型為天氣預報 sd.addOntologies("weather-forecast-ontology"); sd.addLanguages(FIPANames.ContentLanguage.FIPA_SL); // 使用服務的一方必須遵循的規范,和具有的本體知識 sd.addProperties(new Property("country", "Italy")); dfd.addServices(sd); DFService.register(this, dfd); } catch (FIPAException fe) { fe.printStackTrace(); } } }
文件二.
DFSubscribeAgent.java訂閱方
package yellowpages; /** * @function * @author Jacksile E-mail:tufujietec@foxmail.com * @date 2016年1月15日 下午1:12:26 */ import jade.core.AID; import jade.core.Agent; import jade.domain.DFService; import jade.domain.FIPAException; import jade.domain.FIPAAgentManagement.DFAgentDescription; import jade.domain.FIPAAgentManagement.Property; import jade.domain.FIPAAgentManagement.SearchConstraints; import jade.domain.FIPAAgentManagement.ServiceDescription; import jade.lang.acl.ACLMessage; import jade.proto.SubscriptionInitiator; import jade.util.leap.Iterator; public class DFSubscribeAgent extends Agent { protected void setup() { // Build the description used as template for the subscription DFAgentDescription template = new DFAgentDescription(); ServiceDescription templateSd = new ServiceDescription(); templateSd.setType("weather-forecast"); templateSd.addProperties(new Property("country", "Italy")); template.addServices(templateSd); SearchConstraints sc = new SearchConstraints(); // 最多能接受10個結果 sc.setMaxResults(new Long(10)); addBehaviour(new SubscriptionInitiator(this, DFService.createSubscriptionMessage(this, getDefaultDF(), template, sc)) { protected void handleInform(ACLMessage inform) { System.out.println("Agent " + getLocalName() + ": Notification received from DF"); try { DFAgentDescription[] results = DFService .decodeNotification(inform.getContent()); if (results.length > 0) { for (int i = 0; i < results.length; ++i) { DFAgentDescription dfd = results[i]; AID provider = dfd.getName(); // 同一個Agent可能提供很多服務,我們只對天氣預報感興趣 Iterator it = dfd.getAllServices(); while (it.hasNext()) { ServiceDescription sd = (ServiceDescription) it.next(); if (sd.getType().equals("weather-forecast")) { System.out.println("Weather-forecast service for Italy found:"); System.out.println("- Service \"" + sd.getName() + "\" provided by Agent " + provider.getName()); } } } } System.out.println(); } catch (FIPAException fe) { fe.printStackTrace(); } } }); } }
這樣,編譯后,我們為了體現發布服務和訂閱服務時間上的一致關系,我們從命令行來運行這些類。假設已編譯成功,文件標志在某目錄下yellowpages\ DFSubscribeAgent.class 和yellowpages\ DFRegisterAgent.class,我們在yellowpages目錄下運行命令
java jade.Boot –gui service1: yellowpages.DFRegisterAgent
系統運行主容器,同時控制台上顯示Agent service1 registering service “noname” of type "weather-forecast"
然后我們在圖形界面下,start new Agent,AgentName:subscriber ClassName:yellowpages.DFSubscribeAgent 添加后,控制台上會顯示相關啟動后的信息,但不會顯示收到服務的消息。然后再start new Agent,AgentName:service2 ClassName:yellowpages.DFRegisterAgent 參數設為:rainny,這是控制台上顯示注冊服務成功的消息和訂閱到服務的消息。顯示如下:
JADE中使用本體:
Agent之間進行通信要有相同的語言,詞匯和協議。在JADE中,Agents之間通信的方式可以有三種:1.使用字符串表達信息的內容,是最基本的方式.當消息內容是原子數據時,這種方式非常便利.2.消息內容使用序列化的java對象,對本地應用,特別是所有Agent都用java實現的情況下也是很有用的.3.為了jade能夠以FIPA格式隊消息進行編碼和解碼,擴展預先定義好的類,亦即使用本體的方式。這種方式允許jade的Agent可以同其他異質Agent系統進行互操作。這三種方式下對應設置和獲取內容的方法對應如下:
現在有一些已經存在的language來描述ontologies,如DAML+OIL和OWL,JADE並不直接支持這些ontologies,而是將ontologies編碼為java類(比如protégé可以將本體直接導出為java類)。
本體提供了Agent交互的語義支持,但必須與內容語言結合使用,后者是Agent交互的語法支持。JADE支持的三種內容語言包括:FIPA-SL類似lisp的語言族;Leap-encoding應用於嵌入式開發;JavaCodec,JADE特定的內容語言。