pRTI中的chat例子的Java版分析

来源:互联网 发布:汽车维修管理系统源码 编辑:程序博客网 时间:2024/04/30 18:48

 

//总体来看,这个例子相当简单。每个联邦成员都订阅一个交互communication,订阅一个Participant对象类的属性name。并且,同时都发布一个交互communication,发布一个Participant对象类的属性name。

 

//因此,当一个新的联邦成员产生时,他会通过得到别的联邦发布的name属性感知别的联邦的存在。

//

package se.pitch.chat1516;

import hla.rti1516.*;

import se.pitch.prti1516.Encoder;

import se.pitch.prti1516.FederateAmbassadorImpl;

import se.pitch.prti1516.RTI;

import java.io.BufferedReader;

import java.io.File;

import java.io.InputStreamReader;

import java.util.HashMap;

import java.util.Map;

//此前无话,都是对类库的 import

 

class Chat extends FederateAmbassadorImpl {

private RTIambassador _rtiAmbassador;

private final String[] _args;

private InteractionClassHandle _messageId;

private ParameterHandle _parameterIdText;

private ParameterHandle _parameterIdSender;

private ObjectClassHandle _participantId;

private ObjectInstanceHandle _userId;

private AttributeHandle _attributeIdName;

private String _username;

private volatile boolean _reservationComplete;

private volatile boolean _reservationSucceeded;

private final Object _reservationSemaphore = new Object();

private static final int CRC_PORT = 8989;

private static final String FEDERATION_NAME = "ChatRoom";

//以上为变量定义

 

private static class Participant {

private String _name;

Participant(String name) { _name = name; }

public String toString() { return _name; }

}

 

private final Map _knownObjects = new HashMap();

 

 

//以下为主程序

public static void main(String[] args)

{

new Chat(args).run();

}

 

private Chat(String[] args) { _args = args; }  //这个是chat类的构造函数

 

private void run()

{

try

{

                //以下程序接收命令行参数或者直接由用户输入CRC host的IP地址

BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

String rtiHost = "";

if (_args.length > 0) { rtiHost = _args[0]; }

else

{

System.out.print("Enter the IP address of the CRC host [localhost]: ");

rtiHost = in.readLine();

if (rtiHost.length() == 0) { rtiHost = "localhost";}

}

 

//以下为RTI初始化

try

{

_rtiAmbassador = RTI.getRTIambassador(rtiHost, CRC_PORT);

}

catch (Exception e) {

System.out.println("Unable to connect to CRC on " + rtiHost + ":" + CRC_PORT);

return; }

 

//以下为destroy联邦

try

{

_rtiAmbassador.destroyFederationExecution(FEDERATION_NAME);

}

catch (FederatesCurrentlyJoined ignored) { }

catch (FederationExecutionDoesNotExist ignored) { }

 

try

{

File fddFile = new File("Chat.xml");

_rtiAmbassador.createFederationExecution(FEDERATION_NAME, fddFile.toURL());

 //上面一句是核心,产生联邦执行,注意,初始化时要读取chat.xml文件

} catch (FederationExecutionAlreadyExists ignored) { }

 

_rtiAmbassador.joinFederationExecution("Chat", FEDERATION_NAME, this, null);

//上面一句是加入联邦

 

// Subscribe and publish interactions

//下面是得到交互和参数句柄,分别是交互Communication,参数Message和Sender

_messageId = _rtiAmbassador.getInteractionClassHandle("Communication");

_parameterIdText = _rtiAmbassador.getParameterHandle(_messageId, "Message");

_parameterIdSender = _rtiAmbassador.getParameterHandle(_messageId, "Sender");

 

//下面是订阅交互Communication和发布交互Communication

_rtiAmbassador.subscribeInteractionClass(_messageId);_rtiAmbassador.publishInteractionClass(_messageId);

 

// Subscribe and publish objects

//下面是得到对象类句柄

_participantId = _rtiAmbassador.getObjectClassHandle("Participant");

 

//得到对象类中Name属性句柄,并将该属性加入一个attributeSet集合

_attributeIdName = _rtiAmbassador.getAttributeHandle(_participantId, "Name");

AttributeHandleSet attributeSet = _rtiAmbassador.getAttributeHandleSetFactory().create();attributeSet.add(_attributeIdName);

 

//订阅属性集和发布属性集,其实这时属性集中只有一个属性,即name属性

_rtiAmbassador.subscribeObjectClassAttributes(_participantId, attributeSet);_rtiAmbassador.publishObjectClassAttributes(_participantId, attributeSet);

 

// Reserve object instance name and register object instance

 

//这个do--while循环的功能不麻烦,就是保证注册对象的名字的唯一性。但是实现机制不简单。联邦需要和RTI实施两次

//交互,一次是联邦调用reserveObjectInstanceName函数通知RTI需要预订某名字;一次是RTI通过后面的

//objectInstanceNameReservationSucceeded函数和objectInstanceNameReservationFailed函数两个回调

//通知联邦,名字预订的结果是成功还是失败。

//这里复杂性还体现在几个变量的同步机制上,尤其是在多线程环境下

do

{

System.out.print("Enter your name: ");

_username = in.readLine();

try

{

_reservationComplete = false;

 

//此函数属于标准RTI函数,功能是注册一个对象之前必须先预订其名字,RTI将保证其唯一性

_rtiAmbassador.reserveObjectInstanceName(_username);

synchronized (_reservationSemaphore)

{

// Wait for response from RTI

while (!_reservationComplete)

{

try { _reservationSemaphore.wait(); }//wait阻塞,等待notify唤醒

catch (InterruptedException ignored) { }

}

}

if (!_reservationSucceeded)

{

System.out.println("Name already taken, try again.");

}

} catch (IllegalName e) { System.out.println("Illegal name. Try again."); }

catch (RTIexception e)

{

System.out.println("RTI exception when reserving name: " + e.getMessage());

return;

}

}while (!_reservationSucceeded);

 

 

//联邦注册对象实例,参数分别是对象类句柄和用户输入的名字name

_userId = _rtiAmbassador.registerObjectInstance(_participantId, _username);

//注意到,通常registerObjectInstance函数调用是和discoverObjectInstance回调一并使用的。但是本例中并不是这样

//联邦中并没有实现discoverObjectInstance接口。则联邦对别的联邦注册对象的感知,是通过名字属性

//(updateAttributeValue和reflectAttributeValue函数对)的传递来实现的。

System.out.println("Type messages you want to send. To exit, type . ");

while (true)

{

System.out.print("> ");

String message = in.readLine();

if (message.equals(".")) { break; }

ParameterHandleValueMap parameters=_rtiAmbassador.getParameterHandleValueMapFactory().create(1);

parameters.put(_parameterIdText, Encoder.makeHLAunicodeString(message));parameters.put(_parameterIdSender, Encoder.makeHLAunicodeString(_username));

//发送交互

_rtiAmbassador.sendInteraction(_messageId, parameters, null);

}

 

//上面程序中,用户敲击.符号,发出退出命令之前,系统一直在此循环,接收用户输入的消息并发送交互

 

//下面程序功能:当用户敲击.符号时,联邦结束

_rtiAmbassador.resignFederationExecution(ResignAction.DELETE_OBJECTS_THEN_DIVEST);

try

{

_rtiAmbassador.destroyFederationExecution(FEDERATION_NAME);

}

catch (FederatesCurrentlyJoined ignored) { }

_rtiAmbassador = null; }

catch (Exception e) { e.printStackTrace();

}

}

 

public final void receiveInteraction(InteractionClassHandle interactionClass, ParameterHandleValueMap theParameters, byte[] userSuppliedTag, OrderType sentOrdering, TransportationType theTransport)

{

 

//这个是回调函数,当联邦成员接收到交互时被RTI调用

if (interactionClass.equals(_messageId))

{

if (!theParameters.containsKey(_parameterIdText))

{

System.out.println("Bad message received: No text.");

return;

}

if (!theParameters.containsKey(_parameterIdSender))

{

System.out.println("Bad message received: No sender.");

return;

}

//上面是检查消息中是否包含必要的信息

 

String message = Encoder.stringFromHLAunicodeString((byte[]) theParameters.get(_parameterIdText));String sender = Encoder.stringFromHLAunicodeString((byte[]) theParameters.get(_parameterIdSender));System.out.println(sender + ": " + message); System.out.print("> ");

}

}

 

public final void objectInstanceNameReservationSucceeded(String objectName)

{

synchronized (_reservationSemaphore)

{

_reservationComplete = true;

_reservationSucceeded = true;

_reservationSemaphore.notify();

}

}

 

public final void objectInstanceNameReservationFailed(String objectName)

{

synchronized (_reservationSemaphore)

{

_reservationComplete = true;

_reservationSucceeded = false;

_reservationSemaphore.notify();

}

}

 

 

//这个也是回调函数。当责任联邦成员删除其管辖的对象时(通常是调用RTI接口函数deleteObjectInstance),订阅了这个对象类所有或者部分属性的联邦成员将收到由RTI为每一个删除实例而调用的服务 removeObjectInstance

//此例中并不是专业。现猜测是前面的

//resignFederationExecution(ResignAction.DELETE_OBJECTS_THEN_DIVEST)掉用间接调用了deleteObjectInstance函数

 

public void removeObjectInstance(ObjectInstanceHandle theObject, byte[] userSuppliedTag, OrderType sentOrdering) {

Participant member = (Participant) _knownObjects.remove(theObject);

if (member != null)

{

System.out.println("[" + member + " has left]");

}

}

 

 

//回调函数。当责任联邦成员修改了管辖对象的属性时,订阅了该属性的联邦成员将被RTI调用这个回调函数

//与updateAttributeValues配对使用,即当联邦调用updateAttributeValues函数时,会触发RTI调用这个回调函数

//此例中也不典型。 因为本联邦中其实并没有任何地方显式调用updateAttributeValues。而是在provideAttributeValueUpdate回调

//中间接调用updateAttributeValues

//而接下来的问题是:provideAttributeValueUpdate回调函数是和requestAttributeValueUpdate函数配对使用的。本联邦中也没有

//对requestAttributevalueUpdate函数的显式调用

//现在猜测(也经过调试证明):假设A联邦先启动,B后启动。则当B调用subscribeObjectClassAttributes函数后并且交出CPU控制权

//时(比如调用in.readline时),RTI就会调用reflectAttributeValues函数使B感知A联邦所维护的对象及其属性name。

//因此,猜测是B调用subscribeObjectClassAttributes函数时,调用了requestAttributevalueUpdate函数

 

public final void reflectAttributeValues(ObjectInstanceHandle theObject, AttributeHandleValueMap theAttributes, byte[] userSuppliedTag, OrderType sentOrdering, TransportationType theTransport)

{

if (!_knownObjects.containsKey(theObject))

{

if (theAttributes.containsKey(_attributeIdName))

{

String memberName = Encoder.stringFromHLAunicodeString((byte[]) theAttributes.get(_attributeIdName));

Participant member = new Participant(memberName);

System.out.println("[" + member + " has 。joined]");

System.out.print("> "); _knownObjects.put(theObject, member);

}

}

}

 

 

//这个函数是回调函数。当其他联邦成员调用requestAttributeValueUpdate请求时,RTI将回调这个函数。而在这个函数内部,将再次调用联邦代理的函数updateAttributeValues显示地再次更新属性

public final void provideAttributeValueUpdate(ObjectInstanceHandle theObject, AttributeHandleSet theAttributes, byte[] userSuppliedTag)

{

if (theObject.equals(_userId) && theAttributes.contains(_attributeIdName))

{

new Thread()

{

public void run()

{

updateMyName();

}

}.start();

}

}

 

//由上面方法provideAttributeValueUpdate调用

private void updateMyName()

{

try

{

AttributeHandleValueMap attributeValues = _rtiAmbassador.getAttributeHandleValueMapFactory().create(1);

attributeValues.put(_attributeIdName, Encoder.makeHLAunicodeString(_username));_rtiAmbassador.updateAttributeValues(_userId, attributeValues, null);

} catch (RTIexception ignored) { }

}

 

}  //类Chat定义结束

原创粉丝点击