CANOpen数据存档文件

数据存档文件,用于保存所有从节点的字典配置。

在从节点初始化时,从节点将上报boot_up报文。主节点收到boot_up报文后,将对从节点的字典和数据存档文件进行对比,如果不匹配,则需要通过sdo报文对从节点字典进行重新配置,并根据配置决定是否命令从节点将新的配置存入非易失性存储器。

主站可以为每个从站配置一个节点数据存档文件,节点数据存档文件的格式如图:


/* DCF入口 */
typedef struct 
{
	UNS16 Index;		//索引
	UNS8 Subindex;	//子索引
	UNS32 Size;			//字节数
	UNS8 *Data;			//数据指针
}dcf_entry_t;

void SaveNode(CO_Data *d, UNS8 nodeId);
static UNS8 read_consise_dcf_next_entry(CO_Data *d, UNS8 nodeId);
static UNS8 write_consise_dcf_next_entry(CO_Data *d, UNS8 nodeId);
UNS8 init_consise_dcf(CO_Data *d,UNS8 nodeId);

#ifdef _MSC_VER
#define inline _inline
#endif

/* 启动节点 */
void start_node(CO_Data *d, UNS8 nodeId)
{
	/* 节点开始运行 */
	masterSendNMTstateChange(d, nodeId, NMT_Start_Node);
	/* 节点进入运行状态 */
	d->NMTable[nodeId] = Operational;
}

/* 该函数被post_SlaveBootup函数调用,检查是否要获取数据存档文件并启动节点 */
UNS8 check_and_start_node(CO_Data *d, UNS8 nodeId)
{
	/* 如果该节点的数据存档文件正在初始化,则直接退出 */
	if(d->dcf_status != DCF_STATUS_INIT)
		return 0;

	/* 初始化节点nodeId的数据存档文件,并读取该节点的数据存档文件中配置的所有条目的数据 */
	if((init_consise_dcf(d, nodeId) == 0) || (read_consise_dcf_next_entry(d, nodeId) == 0))
	{
		/* 如果没有配置该节点数据存档文件,则直接启动该节点 */
		start_node(d, nodeId);

		return 1;
	}
	/* 开始读取该节点的数据存档文件后,将状态设置为读取检查 */
	d->dcf_status = DCF_STATUS_READ_CHECK;

	return 2;
}

/* 启动该节点并检查是否有节点还未进入运行态 */
void start_and_seek_node(CO_Data *d, UNS8 nodeId)
{
  UNS8 node;
  
	/* 启动该节点 */
	start_node(d, nodeId);
	
	/* 检查是否有节点还未进入运行态,如果有则检查是否要获取数据存档文件并启动节点 */
	for(node = 0; node < NMT_MAX_NODE_ID; node++)
	{
		if(d->NMTable[node] != Initialisation)
			continue;
		
		/* 检查是否要获取数据存档文件并启动节点 */
		if(check_and_start_node(d, node) == 2)
			return;
	}

  /* 所有如果已经有从站进入运行态,则必须将主站设置为运行态 */
	setState(d, Operational);
}

/* 检查数据存档文件sdo并判断下一步要做什么动作 */
static void CheckSDOAndContinue(CO_Data *d, UNS8 nodeId)
{
	UNS32 abortCode = 0;
	UNS8 buf[4], match = 0;
	UNS32 size = 4;
	
	/* 如果状态为读取检查,说明已经开始读取 */
	if(d->dcf_status == DCF_STATUS_READ_CHECK)
	{
		/* 获取sdo读取到的结果 */
		if(getReadResultNetworkDict(d, nodeId, buf, &size, &abortCode) != SDO_FINISHED)
			goto dcferror;
		
		/* 检查读取到的大小和本地配置的是否匹配 */
		if(size == d->dcf_size)
		{
			match = 1;
			
			/* 检查读取到的内容和配置的是否匹配 */
			while(size--)
			{
				if(buf[size] != d->dcf_data[size])
					match = 0;
			}
		}
		/* 如果匹配,继续读取该节点数据存档文件中配置的下一个条目 */
		if(match) 
		{
			/* 如果已经读完该节点配置的数据存档文件中的所有条目 */
			if(read_consise_dcf_next_entry(d, nodeId) == 0)
			{
				/* 开始并检查节点 */
				start_and_seek_node(d, nodeId);
			}
		}
		/* 如果读取的数据存档文件和配置的数据存档文件不匹配,则将配置数据存档文件写到节点字典中 */
		else 
		{
			/* 初始化节点nodeId的数据存档文件,写数据存档指针指向的条目数据并将指针偏移到下一个条目 */
			if((init_consise_dcf(d, nodeId) == 0) || (write_consise_dcf_next_entry(d, nodeId) == 0))
				goto dcferror;                
			
			/* 将状态配置为写 */
			d->dcf_status = DCF_STATUS_WRITE;
		}
	}
	/* 如果状态为写 */
	else if(d->dcf_status == DCF_STATUS_WRITE)
	{
		/* 检查写字典结果是否成功 */
		if(getWriteResultNetworkDict(d, nodeId, &abortCode) != SDO_FINISHED)
			goto dcferror;
		
		/* 写该节点数据存档文件中指针指向的条目的数据,并将指针偏移到该节点配置的下一个条目 */
		if(write_consise_dcf_next_entry(d, nodeId) == 0)
		{
#ifdef DCF_SAVE_NODE
			/* 所有条目都写完,通知从站保存字典到非易失性存储器 */
			SaveNode(d, nodeId);
			/* 将状态设置为保存 */
			d->dcf_status = DCF_STATUS_SAVED;
#else
			/* 所有条目都写完,将状态设置为初始化 */
			d->dcf_status = DCF_STATUS_INIT;
			/* 启动该节点并检查是否有其它节点还未进入运行态,如果有则检查是否要获取数据存档文件并启动其它节点 */
			start_and_seek_node(d,nodeId);
#endif
		}
	}
	/* 保存状态 */
	else if(d->dcf_status == DCF_STATUS_SAVED)
	{
		/* 检查是否写成功 */
		if(getWriteResultNetworkDict(d, nodeId, &abortCode) != SDO_FINISHED)
			goto dcferror;
		
		/* 重启节点 */		
		masterSendNMTstateChange(d, nodeId, NMT_Reset_Node);
		/* 如果保存完该节点后,需要将数据存档入口状态设置初始化 */
		d->dcf_status = DCF_STATUS_INIT;
		/* 因为节点重启还会boot_up,因此将节点状态设置为未知状态 */
		d->NMTable[nodeId] = Unknown_state;
	}

	return;

dcferror:
	MSG_ERR(0x1A01, "SDO error in consise DCF", abortCode);
	MSG_WAR(0x2A02, "server node : ", nodeId);

	d->NMTable[nodeId] = Unknown_state;
}

/* 初始化节点nodeId的数据存档文件 */
UNS8 init_consise_dcf(CO_Data *d, UNS8 nodeId)
{
	UNS32 errorCode;
	ODCallback_t *Callback;
	UNS8 *dcf;

	/* 查找节点的数据存档文件入口 */
	d->dcf_odentry = (*d->scanIndexOD)(0x1F22, &errorCode, &Callback);
	if(errorCode != OD_SUCCESSFUL)
		goto DCF_finish;

	/* 节点号不能大于数据存档入口配置的条目数 */
	if(nodeId > d->dcf_odentry->bSubCount) 
		goto DCF_finish;

	/* 该节点数据存档入口配置的字节大小,为零说明没有配置 */
	if(!d->dcf_odentry->pSubindex[nodeId].size) 
		goto DCF_finish;

	/* 该节点的数据存档文件入口配置指针 */
	dcf = *(UNS8 **)d->dcf_odentry->pSubindex[nodeId].pObject;

	/* 初始化数据存档文件条目的偏移指针(数据存档文件配置的前4字节是条目数) */
	d->dcf_cursor = dcf + 4;

	/* 初始化数据存档文件已初始化条目计数器 */
	d->dcf_entries_count = 0;

	/* 数据存档文件状态设为初始化 */
	d->dcf_status = DCF_STATUS_INIT;

	return 1;
DCF_finish:	
	return 0;
}

/* 获取该节点数据存档文件中指针指向的条目的索引、子索引、数据指针、数据大小,并将指针偏移到下一个条目 */
UNS8 get_next_DCF_data(CO_Data *d, dcf_entry_t *dcf_entry, UNS8 nodeId)
{
  UNS8 *dcfend;
  UNS32 nb_entries;
  UNS32 szData;
  UNS8 *dcf;
	
	/* DCF入口是否配置 */
  if(!d->dcf_odentry)
     return 0;
  
	/* 每个节点作为一个子条目存在,节点号不能大于子条目数 */
	if(nodeId > d->dcf_odentry->bSubCount)
     return 0;

	/* 该节点的数据存档文件配置的字节大小 */
	szData = d->dcf_odentry->pSubindex[nodeId].size;
	/* 该节点的数据存档文件配置指针 */
  dcf = *(UNS8 **)d->dcf_odentry->pSubindex[nodeId].pObject;
	/* 该节点的数据数据存档文件条目数(数据存档文件配置的前4字节) */
  nb_entries = UNS32_LE(*((UNS32 *)dcf));
  /* 该节点数据存档文件的结束指针 */
	dcfend = dcf + szData;

	/* 该节点的数据存档文件至少包含索引、子索引、和数据大小,共7个字节,并节点的数据存档文件已初始化条目数小于配置的条目数 */
	if((UNS8 *)d->dcf_cursor + 7 < (UNS8 *)dcfend && d->dcf_entries_count < nb_entries)
	{
#ifdef CANOPEN_BIG_ENDIAN
    dcf_entry->Index = *(d->dcf_cursor++) << 8 | *(d->dcf_cursor++);
#else
		/* 该条目索引 */
    memcpy(&dcf_entry->Index, d->dcf_cursor, 2);
		d->dcf_cursor += 2;
#endif
		/* 该条目子索引 */
		dcf_entry->Subindex = *(d->dcf_cursor++);
		
#ifdef CANOPEN_BIG_ENDIAN
    dcf_entry->Size = *(d->dcf_cursor++) << 24 | *(d->dcf_cursor++) << 16 | *(d->dcf_cursor++) << 8 | *(d->dcf_cursor++);
#else
		/* 该条目数据存储区大小 */
    memcpy(&dcf_entry->Size, d->dcf_cursor, 4);
    d->dcf_cursor += 4;
#endif
		/* 该条目数据存储区指针 */
    d->dcf_data = dcf_entry->Data = d->dcf_cursor;
		/* 该条目数据存储区大小 */
    d->dcf_size = dcf_entry->Size;
		/* 该节点下一条数据存档文件条目的指针 */
		d->dcf_cursor += dcf_entry->Size;
    /* 该节点的数据存档文件已初始化条目数加一 */
		d->dcf_entries_count++;

    return 1;
  }

  return 0;
}

/* 写该节点数据存档文件中指针指向的条目的数据,并将指针偏移到该节点配置的下一个条目 */
static UNS8 write_consise_dcf_next_entry(CO_Data* d, UNS8 nodeId)
{
	UNS8 Ret;
	dcf_entry_t dcf_entry;
	
	/* 获取该节点数据存档文件中指针指向的条目的索引、子索引、数据指针、数据大小,并将指针偏移到下一个条目 */
	if(!get_next_DCF_data(d, &dcf_entry, nodeId))
		return 0;
	
	/* 将数据写过去 */
	Ret = writeNetworkDictCallBackAI(d, nodeId, dcf_entry.Index, dcf_entry.Subindex, (UNS8)dcf_entry.Size, 
																	 0, dcf_entry.Data, CheckSDOAndContinue, 0, 0);
	if(Ret)
			MSG_ERR(0x1A02,"Erreur writeNetworkDictCallBackAI",Ret);

	return 1;
}

/* 读取该节点数据存档文件中指针指向的条目的数据,并将指针偏移到该节点配置的下一个条目 */
static UNS8 read_consise_dcf_next_entry(CO_Data *d, UNS8 nodeId)
{
	UNS8 Ret;
	dcf_entry_t dcf_entry;
	
	/* 获取该节点数据存档文件中指针指向的条目的索引、子索引、数据指针、数据大小,并将指针偏移到下一个条目 */
	if(!get_next_DCF_data(d, &dcf_entry, nodeId))
		return 0;
	
	/* 读取该节点数据存放文件中指针指向的条目的数据 */
	Ret = readNetworkDictCallbackAI(d, nodeId, dcf_entry.Index, dcf_entry.Subindex, 0, CheckSDOAndContinue, 0);
	if(Ret)
		MSG_ERR(0x1A03,"Erreur readNetworkDictCallbackAI", Ret);
    
	return 1;
}

/* 通知从站保存字典到非易失性存储器 */
void SaveNode(CO_Data *d, UNS8 nodeId)
{
	UNS8 Ret;
	UNS32 data = 0x65766173;
	
	Ret = writeNetworkDictCallBackAI(d, nodeId, 0x1010, 1, 4, 0, (void *)&data, CheckSDOAndContinue, 0, 0);
	if(Ret)     
		MSG_ERR(0x1A04, "Erreur writeNetworkDictCallBackAI", Ret);
}


猜你喜欢

转载自blog.csdn.net/lushoumin/article/details/79684519
今日推荐