• 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状态的入口代码:

    /* This function initializes its state variables and  registers itself in the process registry */
    eth_mac_higher_layer_intf_init ();    //调用该函数初始化,包括初始化状态变量 和注册进程

    /* Set up an interface control information (ICI) structure to */
    /* communicate parameters to the MAC layer process. We will */
    /* leave this ICI installed for the entire simulation. */
    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接收进程)。

    /* Schedules a self interrrupt */
    op_intrpt_schedule_self (op_sim_time (), 0);  //自中断,时间为当前时间

     

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

    static void eth_mac_higher_layer_intf_init ()
    {
    /** Initializes the state variables for this process model  and registers itself in the model-wide reigtry */
    FIN (eth_mac_higher_layer_intf_init ());

    /* Gets its own id */
    my_id = op_id_self ();  //获得自己模块的objid。

    /* Gets the object id of the node to which this module belongs */
    own_node_objid = op_topo_parent (my_id);  //获得自己模块所在节点的objid。

    /* Gets the objid of its subnet */
    subnet_objid = op_topo_parent (own_node_objid);    //获得自己模块所在节点的子网的objid。

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

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

    /* Gets a process record handle to record its process in  the registry  */

    //在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

    /* Sets the required attributes in the process registry */

    //利用返回的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 numbers to the mac layer and from the mac layer which */
    /* which will be set in the exit execs of the wait state */

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

    FOUT;
    }

     

    三,init2状态的入口代码:

    /* Schedule a self interrupt to wait for lower layer Ethernet MAC process*/
    /*  to initialize and register  itself in the model-wide process registry. */
    op_intrpt_schedule_self (op_sim_time (), 0);  //自中断,时间为当前时间,目的是等待底层的MAC进程初始化并注册,目的是用于一些系统的初始化配置,比如你的网络开始运行之前,你需要为每一个节点分配适当的地址,并且你必须保证每一个节点配置了初始节点网络才能够开始运行。这就是用两个unforced init模块模块的原因。

     

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

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

    /* Obtain the MAC layer information for the local MAC process from the model-wide registry. */
    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))

    /* If the MAC process registered itself, then there must be a valid match*/ 

    record_handle_list_size = op_prg_list_size (proc_record_handle_list_ptr); //判断是否是正好一个match的进程
    if (record_handle_list_size != 1)
    {
    /* An error should be created if there are more  than one Ethernet-MAC process in the local node,  or if no match is found.   */  
     op_sim_end ("Error: either zero or several Ethernet MAC processes found in the interface", "", "", "");  //多于一个,出错了
    }
    else
    {
    /* Obtain a handle on the process record. */  //正确,只有一个,通过在进程登记库中的位置,获得MAC进程的handle,并送到process_record_handle
    process_record_handle = (OmsT_Pr_Handle) op_prg_list_access (proc_record_handle_list_ptr, OPC_LISTPOS_HEAD);

    /* Obtain the module objid for the Ethernet MAC module. */  //获得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

    /* Obtain the address handle maintained by the MAC process. */  //获得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);

    /* Set the variable to indicate the MAC address of the associated MAC layer process.*/
     mac_address = ne_address;      //得到mac地址

    /* Obtain the stream numbers connected to and from the Ethernet MAC layer process.   */
    //获得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)
    {
    /* This is a unconnected node. Do nothing */
    }
    else
    {
    /* Determine address range for destination assignment. */
    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);
    }

    /* If any single address is specified as broadcast, indicate this as an error */
    /* Both the low and high destination addresses together can be specified as broadcast */  //注意-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);
    }

    /* Check whether highest destination address is not lower than the lowest destination */
    /* address (also check it is not a broadcast address or MAX_DEST_ADDR). */
    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);

    /* If so, then set the lowest destination address to the minimum destination address */
    /* and the highest destination address to the highest destination address. */
    low_dest_addr = MIN_DEST_ADDR;
    high_dest_addr = MAX_DEST_ADDR;
    }

    /* Determine our destination or destination range. Start by initializing the range indices, which are used if a range is specified.  */
    //确定目的地址的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))  //前面判断地址范围有问题时
    {
    /* We randomly pick our destination from universal address set. */
    destination_address = OMSC_AA_AUTO_ASSIGN;
    }
    else if (low_dest_addr == high_dest_addr)
    {
    /* We have a unique destination address specified. Check its validity. */
    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
    {
    /* We randomly pick our destination addresses from a subset of the */
    /* universal address set. Determine the lower and upper indices of this */
    /* subset in the global address array. */
    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);

    /* Check validity of the lower bound. */
    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.");
    }
    }

    /* Now determine the upper bound. */
    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);

    /* Check validity of the upper bound. */
    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中断,其它为应用层中断。

    /* Definition for the transition conditions  */
    #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)

    首先执行出口代码:

    /* The only interrupt expected in this state is a stream interrupt. It can be either from the MAC*/
    /* layer for a packet destined for this node or from the application layer for a packet destined for some other node.*/
    intrpt_type = op_intrpt_type ();  //首先得到中断类型和stream index
    intrpt_strm = op_intrpt_strm ();

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

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

    /* A packet has arrived from the application layer. Pick a random  */
    /* destination, unless an explicit destination is specified. Don't try infinite times.*/
    num_tries = 0;
    do 
     {
     /* Increment the try counter.  This will prevent an infinite loop. */  //不能无穷的尝试发送
     num_tries++;

     /* Pick a random destination address unless the destination address  is already set to a specific value. */
     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
     {
     /* Set this information in the interface control information to be sent to the MAC layer. */
     op_ici_attr_set_int64 (intf_mac_req_iciptr, "dest_addr", curr_dest_addr);   //向MAC层提交ICI信息,内容是mac地址

     /* Send the packet to the lower layer */
     op_pk_send (pkptr, outstrm_to_mac);  //发送包,注意op_pk_send()函数会默认销毁包
     }

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

    /* A packet arrived from the MAC layer. Since the MAC  layer would have forwarded this only if it were  */
    /* destined for this node, forward this packet to the application layer. */
    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()系列函数。

  • 相关阅读:
    VBScript把json字符串解析成json对象的2个方法
    vue+php接口
    td标签 内容垂直、水平居中
    win7 安装 IIS 配置ASP 【原创】
    PS 实用技巧
    通信原理实践(一)——音频信号处理
    德飞莱STM32单片机学习(一)——下载环境搭建
    电赛总结(四)——波形发生芯片总结之AD9854
    电赛总结(四)——波形发生芯片总结之AD9851
    电赛总结(四)——波形发生芯片总结之AD9834
  • 原文地址:https://www.cnblogs.com/loopever/p/2599951.html
Copyright © 2020-2023  润新知