OPNET学习笔记之defer模块

来源:互联网 发布:淘宝买家退货调包报警 编辑:程序博客网 时间:2024/04/30 07:42

OPNET学习笔记之eth_mac_intf模块(ethcoax_station_adv节点模型)

前面已经描述了ethcoax_station_adv节点模型的基本结构,其中burstry_gen模块产生无格式包,发送给eth_mac_intf模块处理,本文分析其处理过程。

首先注意该模块上的包流:

stream : eth_mac_intf [0] -> sink [1]
stream : eth_mac_intf [1] -> mac [1]
stream : bursty_gen [0] -> eth_mac_intf [1]
stream : mac [1] -> eth_mac_intf [0]

该模块的进程模型为ethernet_mac_interface,状态机如下:

该模块没有local/global statistic,但是有两个attribute,分别为high dest address和low dest address,表示dest address的最大值和最小值。状态机中只有两个强制状态,其它为非强制状态。

一,init状态的入口代码:


eth_mac_higher_layer_intf_init ();    //调用该函数初始化,包括初始化状态变量 和注册进程




intf_mac_req_iciptr = op_ici_create ("mac_request");  //创建一个ICI结构体,以便与MAC层通信,注意返回值类型为Ici*,而"mac_request"是预定义好的结构体(在哪里定义???)(syntax:op_ici_create(fmt_name);)
op_ici_install (intf_mac_req_iciptr);           //绑定????,这个ICI被自动绑定到被唤醒进程向外发送的中断,注意ICI指针可以由op_ici_create()op_intrpt_ici()返回,其中前者返回一个新创建的ICI(在ICI发送进程),后者返回一个与即将到来的中断关联的ICI(在ICI接收进程)。


op_intrpt_schedule_self (op_sim_time (), 0);  //自中断,时间为当前时间

 

二,分析该模块的初始化函数eth_mac_higher_layer_intf_init ():

static void eth_mac_higher_layer_intf_init ()
{

FIN (eth_mac_higher_layer_intf_init ());


my_id = op_id_self ();  //获得自己模块的objid。


own_node_objid = op_topo_parent (my_id);  //获得自己模块所在节点的objid。


subnet_objid = op_topo_parent (own_node_objid);    //获得自己模块所在节点的子网的objid。


own_prohandle = op_pro_self ();        //获得当前运行进程的句柄,有何用处???(可以获取进程模型的各种属性)


op_ima_obj_attr_get (my_id, "process model", proc_model_name);  //通过objid获得当前进程模型的process model属性,送到proc_model_name,注意proc_model_name是char[20]。

//在model registry(进程登记库)中注册进程,并返回handle,注意model registry是进程注册的一个表,通过返回的handle操作,可以注册、查询、设置等。

//model registry的目的是在各层协议模块间共享数据,具体见《大解密》P286,

//进程登记库的目的是:(1)为仿真中所有进程提供全局的信息注册机制,(2)允许进程在节点内共享信息,进程可以设置多种类型的属性(3)进程注册后,可以用多种查询参数找到并访问其公开信息。相关文件为oms_pr.h和oms_pr.ex.c
own_process_record_handle = (OmsT_Pr_Handle) oms_pr_process_register (own_node_objid, my_id,
  own_prohandle, proc_model_name);  //注意返回值类型是OmsT_Pr_Handle

//利用返回的handle,设置进程在process registry的属性
oms_pr_attr_set (own_process_record_handle, "location", OMSC_PR_STRING, "mac_if",
"subnet", OMSC_PR_OBJID, subnet_objid, OPC_NIL);  //syntax:oms_pr_attr_set(pr_handle,attr0_name,attr0_type,attr0_value,……,OPC_NIL);,最后一个常量表示结束。登记了两个属性,分别为location 和subnet


//进出的stream index,这两个量会在wait状态的出口代码中被赋值,这里被设置为无效值。
outstrm_to_mac = OPC_INT_UNDEF;
instrm_from_mac = OPC_INT_UNDEF;

FOUT;
}

 

三,init2状态的入口代码:



op_intrpt_schedule_self (op_sim_time (), 0);  //自中断,时间为当前时间,目的是等待底层的MAC进程初始化并注册,目的是用于一些系统的初始化配置,比如你的网络开始运行之前,你需要为每一个节点分配适当的地址,并且你必须保证每一个节点配置了初始节点网络才能够开始运行。这就是用两个unforced init模块模块的原因。

 

四,wait状态的入口代码:同上。

出口代码:(要从节点内的进程登记库中提取MAC的流信息,所以必须等待所有底层的进程完成初始化才开始)


proc_record_handle_list_ptr = op_prg_list_create ();  //功能是分配一个新的空表
oms_pr_process_discover (my_id, proc_record_handle_list_ptr, "node objid", OMSC_PR_OBJID, own_node_objid,
  "protocol", OMSC_PR_STRING, "mac",OPC_NIL);  //在进程登记库中发现符合条件的进程,这里是本节点内部的协议为“mac”的进程,(syntax:oms_pr_process_discover(neighber_objid,pr_handle_lptr, attr0_name, attr0_type, attr0_value,...., OPC_NIL))

 

 

record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr); //判断是否是正好一个match的进程
if (record_handle_list_size != 1)
{
  
 op_sim_end ("Error: either zero or several Ethernet MAC processes found in the interface", "", "", "");  //多于一个,出错了
}
else
{
  //正确,只有一个,通过在进程登记库中的位置,获得MAC进程的handle,并送到process_record_handle
process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, OPC_LISTPOS_HEAD);

 

  //获得MAC模块的objid并记录到变量mac_module_objid中,注意mac模块应登记了objid
oms_pr_attr_get (process_record_handle, "module objid", OMSC_PR_OBJID, &mac_module_objid);

 //pr就是process record

  //获得MAC模块的属性,包括mac地址和auto address handle???
oms_pr_attr_get (process_record_handle, "address", OMSC_PR_INT64, &ne_address);
oms_pr_attr_get (process_record_handle, "auto address handle", OMSC_PR_POINTER, &oms_aa_handle);

 


 mac_address = ne_address;      //得到mac地址

 


//获得MAC模块连接的stream index,并送到instrm_from_mac和outstrm_to_mac。
oms_tan_neighbor_streams_find (my_id, mac_module_objid, &instrm_from_mac, &outstrm_to_mac);
}

//---------------------------------------------------------------------------下面是获取目的地址,可以是设定或随机选择的

if (oms_aa_handle == OPC_NIL)
{

}
else
{

if ((op_ima_obj_attr_get (my_id, "low dest address", &low_dest_addr) == OPC_COMPCODE_FAILURE) ||  //从属性中获取地址范围
(op_ima_obj_attr_get (my_id, "high dest address", &high_dest_addr) == OPC_COMPCODE_FAILURE))
{
eth_mac_higher_layer_intf_error ("Unable to read destination address range.", OPC_NIL, OPC_NIL);
}

 


  //注意-1是广播地址???
if (((low_dest_addr == -1) && (high_dest_addr != -1)) ||
((high_dest_addr == -1) && (low_dest_addr != -1)))
{
eth_mac_higher_layer_intf_error ("Both the Lowest and Highest Destination addresses must be specified as Broadcast", "Any one attribute cannot be Broadcast", OPC_NIL);
}

 



if ((high_dest_addr < low_dest_addr) && (high_dest_addr >= 0))
{
eth_mac_higher_layer_intf_warn ("Specified lowest destination address is greater than specified highest destination address.",
"Setting the lowest destination address to Minimum Dest Address, and the highest to Maximum Dest Address.", OPC_NIL);

 



low_dest_addr = MIN_DEST_ADDR;
high_dest_addr = MAX_DEST_ADDR;
}

 


//确定目的地址的range
low_dest_index = OMSC_AA_UNINIT_ADDR;
high_dest_index = OMSC_AA_UNINIT_ADDR;
if ((low_dest_addr == MIN_DEST_ADDR) && (high_dest_addr == MAX_DEST_ADDR))  //前面判断地址范围有问题时
{

destination_address = OMSC_AA_AUTO_ASSIGN;
}
else if (low_dest_addr == high_dest_addr)
{

if (low_dest_addr == -1)
destination_address = OMSC_AA_BROADCAST;
else if (oms_aa_dest_addr_check (oms_aa_handle, low_dest_addr) == OPC_TRUE)
destination_address = low_dest_addr;  //正确的唯一地址
else
{
sprintf (err_msg, "%d", low_dest_addr);
eth_mac_higher_layer_intf_error ("Invalid destination MAC address.", "No MAC was found with the address:",err_msg);
}
}
else
{



if (low_dest_addr == MIN_DEST_ADDR)
low_dest_index = 0;
else
{
low_dest_index = oms_aa_address_find (oms_aa_handle, low_dest_addr);

 


if (low_dest_index < 0)
{
sprintf (err_msg, "%d", low_dest_addr);
eth_mac_higher_layer_intf_error ("Invalid destination MAC address.", "No MAC was found with the address:", err_msg);
}
else if (oms_aa_dest_addr_check (oms_aa_handle, low_dest_addr) == OPC_FALSE)
{
sprintf (err_msg, "MAC address specified as the lowest destination address (%d)", low_dest_addr);
eth_mac_higher_layer_intf_error (err_msg, "is not configured as a valid destination address.",
"Most probably that MAC belongs to a switch or bridge node.");
}
}

 


if (high_dest_addr == MAX_DEST_ADDR)
high_dest_index = oms_aa_max_addr_index_get (oms_aa_handle);
else
{
high_dest_index = oms_aa_address_find (oms_aa_handle, high_dest_addr);

 


if (high_dest_index < 0)
{
sprintf (err_msg, "%d", high_dest_addr);
eth_mac_higher_layer_intf_error ("Invalid destination MAC address.", "No MAC was found with the address:", err_msg);
}
else if (oms_aa_dest_addr_check (oms_aa_handle, high_dest_addr) == OPC_FALSE)
{
sprintf (err_msg, "MAC address specified as the highest destination address (%d)", high_dest_addr);
eth_mac_higher_layer_intf_error (err_msg, "is not configured as a valid destination address.",
"Most probably that MAC belongs to a switch or bridge node.");
}
}
}
}

 //-----------------------------------------------------地址完成

五,idle状态的入口代码为空,当收到中断时,根据条件执行出口代码:

注意在header block中定义的条件:当stream index是从mac来时为MAC中断,其它为应用层中断。


#define MAC_LAYER_PKT_ARRVL  (intrpt_type == OPC_INTRPT_STRM && intrpt_strm == instrm_from_mac)
#define APPL_LAYER_PKT_ARRVL (intrpt_type == OPC_INTRPT_STRM && intrpt_strm != instrm_from_mac)

首先执行出口代码:



intrpt_type = op_intrpt_type ();  //首先得到中断类型和stream index
intrpt_strm = op_intrpt_strm ();

 

 
pkptr = op_pk_get (intrpt_strm);  //得到包指针,注意由于包需要在不同的状态处理,所以pkpt是state variable,始终有效

 

五,appl layer arrival状态,由于该状态是强制状态,执行入口代码后马上跳回idle:这里只是分配了mac地址和ICI信息。



num_tries = 0;
do 
 {
   //不能无穷的尝试发送
 num_tries++;

 
 if (low_dest_index + high_dest_index > 0)  //地址没有设定,可能的最高加最低地址大于零,即非广播地址,
  {
  oms_aa_dest_addr_from_range_get (oms_aa_handle, &integer_mac_address, low_dest_index, high_dest_index);  //用该函数随机获取地址范围内的一个地址,并送到integer_mac_address。
  curr_dest_addr = integer_mac_address;
  }
 else
  {
  integer_mac_address = destination_address;  //地址已经确定
  oms_aa_dest_addr_get (oms_aa_handle, &integer_mac_address);  //获取一个随机地址从address list
  curr_dest_addr = integer_mac_address;
  }
 } while ((curr_dest_addr == mac_address) && (num_tries < TRY_THRESH));  //目的地址不能是自己,也不能无限循环

if (curr_dest_addr == mac_address)
 eth_mac_higher_layer_intf_error ("Unable to choose remote address.", OPC_NIL, OPC_NIL);

if (curr_dest_addr == OMSC_AA_UNINIT_ADDR)
 {
 op_pk_destroy (pkptr);
 }
else
 {
 
 op_ici_attr_set_int64 (intf_mac_req_iciptr, "dest_addr", curr_dest_addr);   //向MAC层提交ICI信息,内容是mac地址

 
 op_pk_send (pkptr, outstrm_to_mac);  //发送包,注意op_pk_send()函数会默认销毁包
 }

六,mac layer arr状态,对于mac层的包,直接发送给应用层,由于本模型应用层是sink模块,所以直接发送即可。



op_pk_send (pkptr, 0);  //默认的stream index为0的是连接到sink模块

 

总结:eth_mac_intf模块是在应用层与mac层之间的接口,功能是(1)从上层传递的包,随机(或指定)一个mac地址,然后发送给mac层,其中地址作为ICI信息发送。(2)mac层传过来的包直接发给上层,因为上层是sink模块,直接把包销毁,所以不需要做其它的工作了。

注意的:(1)自中断等待其它进程完成初始化。(2)进程登记库及相关函数(pr),用于得到本节点内其它模块的属性,共享信息。(3)用ICI在层间传递信息,这里是传递mac地址到mac层。(4)自动分配地址,oms_aa_dest_addr_from_range_get()系列函数。


转载地址:http://blog.sina.com.cn/s/blog_b14ce4cd01017o71.html

0 0