Try   HackMD

帶你閱讀一座程式 函式拆解II

tags: libmodbus

Copyright 2021, 月下麒麟


今日主角: modbus_set_slave(ctx, SERVER_ID)

Trace go
Reference: trace here

modbus_set_slave(ctx, SERVER_ID);

進行查找其函式定義

int modbus_set_slave(modbus_t *ctx, int slave) { if (ctx == NULL) { errno = EINVAL; return -1; } return ctx->backend->set_slave(ctx, slave); }

發現定義僅寫,若指標為空,則不繼續往下執行
另外,回傳的內容為指標(ctx)去取得結構modbus_t的成員backend,

但該成員(backend)其型別是另一結構modbus_backend_t,
繼續指向modbus_backend_t,並欲取得成員set_slave。

到這裡,進入第二層結構定義裡面,

typedef struct _modbus_backend { //... int (*set_slave) (modbus_t *ctx, int slave); //... } modbus_backend_t;

同時,你也會有疑問,那然後呢? 抵達世界的盡頭 ~.~

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

別緊張,請善用搜尋~ modbus_backend_t

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

找出所有有關該結構名稱存在的地方
除了在modbus-private.h 定義處出現外,
分別在modbus-rtu.c modbus-tcp.c

關鍵就是利用struct賦予初值,而初值的內容為函式名稱
同時,會將函式名稱套入到 結構定義處

const modbus_backend_t _modbus_rtu_backend = { //... _modbus_set_slave, //函式名稱 //... }; //=====分隔線,不同位置的程式碼===== int (*set_slave) (modbus_t *ctx, int slave); //將函式名稱套入於此

這個技巧運用到了結構初值的賦予,與function point


小補充:

const modbus_backend_t _modbus_rtu_backend = { //... _modbus_set_slave, //... };

那你可能也觀察到 _modbus_rtu_backend 這個struct variable要做什麼用?
對先對modbus_backend_t進行搜尋,
會發現到modbus-rtu.c 與 modbus-tcp.c都有它的身影,

這與上一篇文章的初始化有關帶你閱讀一座程式 函式分解I

const modbus_backend_t _modbus_tcp_backend = {}; const modbus_backend_t _modbus_rtu_backend = {};

可以觀察到利用結構modbus_backend_t去創建兩個賦予結構初值的陣列,
並且將該宣告的陣列給予名稱 _modbus_tcp_backend , _modbus_rtu_backend

賦予名稱最主要的意義為,可以讓它被接續使用

進行名稱搜訊

ctx->backend = &_modbus_tcp_backend; ctx->backend = &_modbus_rtu_backend;

呼應上一篇,就是讓此處的初始化指向該陣列。


鏡頭拉回來,進行_modbus_set_slave的解析

static int _modbus_set_slave(modbus_t *ctx, int slave) { /* Broadcast address is 0 (MODBUS_BROADCAST_ADDRESS) */ if (slave >= 0 && slave <= 247) { ctx->slave = slave; } else { errno = EINVAL; return -1; } return 0; }

判斷slave號碼是介於0~247的範圍,
這部分可以參考Modbus Serial Specification裏頭有載名。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →


今日配角: header_length, checksum_length, max_adu_length

這三個結構成員出現在modbus_backend_t結構裡面。
會發現到不是還有一個backend_type嗎? 為何沒放進來?
因為該成員變數僅用來區分tcp, rtu。

那要介紹這三個主要為跟Modbus Protocol有關

/*RTU*/ _MODBUS_BACKEND_TYPE_RTU, //0 _MODBUS_RTU_HEADER_LENGTH, //1 -->server address (slave address) _MODBUS_RTU_CHECKSUM_LENGTH, //2 MODBUS_RTU_MAX_ADU_LENGTH, //256 //=====分隔線,不同位置的程式碼===== /*TCP*/ _MODBUS_BACKEND_TYPE_TCP, //1 _MODBUS_TCP_HEADER_LENGTH, //7 _MODBUS_TCP_CHECKSUM_LENGTH, //0 MODBUS_TCP_MAX_ADU_LENGTH, //260

這裡僅說明各成員之數值,與其Modbus Specification做對應。
Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

這段說明書上的文字就很詳細的定義解釋了。

最後一上圖,Reference:實例研討: 從 C++ 學習 C 高級技巧

今天就寫到這,感謝您的閱讀。