数据存档文件,用于保存所有从节点的字典配置。
在从节点初始化时,从节点将上报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);
}