數據存檔文件,用于保存所有從節點的字典配置。
在從節點初始化時,從節點將上報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_ENDIANdcf_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_ENDIANdcf_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);
}
總結
以上是生活随笔為你收集整理的CANOpen数据存档文件的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。