banner
yono

yono

哈喽~欢迎光临
follow
github

24_7_12_C言語入門_SCPIライブラリAPI紹介

背景故事#

大学時代はずっと遊んでいて、卒業した時にはほとんど何もできなかったが、大佬のこのライブラリが私を救ってくれた。

SCPI は文字列解析プロトコルの一種です。仕事を始めたばかりの頃、ライトを点けてシリアルポートを開いたら、SCPI プロトコルを実装するように言われました。その時はまだ未熟で、このプロトコルライブラリを立ち上げるのに 2 週間かかりました。

このライブラリの内容は本当に豊富で、非常に動的なポインタはなく、基本的にはバインディング性質の「静的」ポインタばかりで、C 言語があまり得意でない私でも徐々に理解できるようになりました。

約 2 ヶ月間ゆっくり使った後、その中の大部分の内容を閲覧し、C 言語も正式に入門しました。

そして、後続の新卒者にも、まずこのライブラリを見せるようにしています。

ソースコードのアドレスはこちらです。

j123b567/scpi-parser: Open Source SCPI device library (github.com)

このコードライブラリの優位性#

[!NOTE]

  1. これは純粋なプロトコル解析ライブラリであり、ハードウェアとは強く関連しておらず、関心を持つべきポートはデータの流入と流出のみです。非常に迅速に使用を開始できます(たとえ CodeBlocks や DEV-C++ のような学習環境でも)。
  2. これは業務機能を果たすことができるライブラリであり、SCPI プロトコル解析に必要なすべての機能やメカニズムを備えており、学習後の応用シーンは非常に広く、無駄な学習にはなりません。
  3. これは堅牢なライブラリであり、現在のところこのライブラリ内で致命的なバグを発見したことはありません。反例として、最近移植と修正を期待している freemodbus ライブラリには、致命的なバグや大小の設計欠陥が存在します。
  4. これは比較的シンプルなライブラリであり、rtos カーネル、ネットワークライブラリ、ファイルシステムライブラリなどと比べて、このライブラリは非常に理解しやすいと言えます。時間をかければ、オブジェクト指向、マクロ翻訳、マクロ条件コンパイル、コールバックバインディング、文字処理など、C 言語の一般的な構文や設計を学ぶことができます。

何を見ればいいか#

C 言語の入門者向けに書かれているため、このライブラリの構造が非常にシンプルであっても、紹介が必要です。全体のライブラリは以下の 2 つのフォルダのみです。

  • examples にはいくつかのサンプルがあり、ライブラリを使うために最初に見るべきものです。

  • libscpi にはすべてのライブラリソースコードがあります。このフォルダ内にはtestフォルダがあり、メインエントリを持つ単体テストが含まれています。実際のプロジェクトではlibscpi/testフォルダのソースコードを取り込む必要はありません。

    image-20241205131859389

examples内では、以下の 2 つのフォルダを重点的に見ることをお勧めします。

examples/common には例程がサポートする命令表と対応する命令のコールバックがあり、ここでライブラリ API の使い方を学ぶことができます。

examples/test-parser には最もシンプルな例程のメインエントリと、SCPI_Write ()、SCPI_Error () などのインターフェース関数の定義、少量の命令のコールバックがあり、命令表内でバインディングを見つけることができます。

image-20241205132043166

大まかな使用フローは以下の通りです。

  1. SCPI_Init()関数を使用してオブジェクト、デバイス ID、さまざまなポート関数をバインドします。
  2. SCPI_Input()関数を使用して、サポートされている完全な命令を入力し、ライブラリは自動的に命令コールバックをトリガーし、自動的にポート関数を使用して情報を送信します。

優れたライブラリは、使用がこれほど簡単です。

一部のライブラリ API の紹介#

公式ドキュメントにも API の紹介がありますが、ほとんど有効な情報はありません。結局、公式には一人しかいないので、参考にしてください。

About · SCPI parser (jaybee.cz)

Scpi-Def.c には、SCPI 処理プロセスで使用されるインターフェースパラメータ scpi interface が宣言されています。

命令パラメータ処理 API#

scpi_bool_t SCPI_ParamErrorOccurred(scpi_t* context);
関数内でエラーが発生したかどうかを検出し、エラーが存在する場合は処理を直ちに停止する必要があります。

scpi_bool_t SCPI_ParamInt32(
    scpi_t* context,
    int32_t* value,
 scpi_bool_t mandatory);
contextから32ビット符号付きパラメータを取得し、valueに代入します。mandatoryがtrueでパラメータがない場合、-109エラー(無パラメータエラー)を生成します。mandatoryがfalse(通常は使用しない)であれば、パラメータは式文字列である必要があり、そうでなければ-151エラー(無効文字列エラー)を生成します。

scpi_bool_t SCPI_ParamInt64(
    scpi_t* context,
    int64_t* value,
 scpi_bool_t mandatory);
上記の32ビット符号付きパラメータ取得ロジックと同様に、64ビット符号付きパラメータを取得します。

上記の2つの関数と同様のロジックを持つAPI関数は以下の通りです。

SCPI_ParamUInt32() 無符号32ビットデータを取得します。
SCPI_ParamUInt64() 無符号64ビットデータを取得します。
SCPI_ParamDouble() double型データを取得します。
SCPI_ParamFloat() float型データを取得します。
SCPI_ParamBool() bool型データを取得します。

SCPI_ParamChoice(
 scpi_t * context,
 const scpi_choice_def_t * options,
 int32_t * value,
 scpi_bool_t mandatory);
オプションリストから値を取得します。*optionsはデータを抽出するためのパラメータで、オプションのインデックスをValueに代入します。

SCPI_ParamCopyText(
 scpi_t * context,
 char * buffer,
 size_t buffer_len,
 size_t * copy_len,
 scpi_bool_t mandatory);
データを抽出してbufferに代入します。

SCPI_ParamCharacters(
 scpi_t * context,
 const char ** value,
 size_t * len,
 scpi_bool_t mandatory);
文字パラメータを抽出してvalueに代入し、lenは抽出成功した文字の長さです。

SCPI_ParamArbitraryBlock(
 scpi_t * context,
 const char ** value,
 size_t * len,
 scpi_bool_t mandatory);
任意のブロックプログラムデータを取得し、valueに代入し、lenは抽出成功した文字です。

SCPI_ParamNumber(
   scpi_t * context,
   const scpi_choice_def_t * special,
   scpi_number_t * value,
 scpi_bool_t mandatory);
次のパラメータを数字または単位付き数字または特定のルールの数字として解析し、valueに代入します。特定のルールで解析する必要がある場合、specialは解析ルールを示します:MINimum、MAXimum、DEFaylt、UP、DOWNなど、詳細はscpi_choice_numbers_def[]の定義を参照してください。

回伝生成 API#

結果生成処理 API によって生成された回伝パラメータは、インターフェースパラメータ scpi interface に格納され、最終的にすべて SCPI_Write () 関数を呼び出します。現在、この関数は:回伝パラメータを uart4 ポートから送信します。

size_t
SCPI_ResultArbitraryBlock(
   scpi_t * context,
   const char * data,
  size_t len);
任意のブロックデータに(#1+バイトデータ長)ヘッダーを追加し——ヘッダーの変更はSCPI_ResultArbitraryBlockHeader()関数定義を参照し、\r\nの尾を追加し、SCPI_Write()関数を呼び出して送信します。

この関数は実際に以下の2つの関数を呼び出します。

size_t
SCPI_ResultArbitraryBlockHeader(
   scpi_t * context,
 size_t len);
運算データブロックに追加すべきヘッダーを追加して送信します。

size_t
SCPI_ResultArbitraryBlockData(
   scpi_t * context,
   const char * data,
 size_t len);
エラーチェック付きのデータブロック送信関数で、contextのarbitrary_remindingパラメータが指定された長さlen未満の場合、システムエラーSCPI_ERROR_SYSTEM_ERRORを生成します。

size_t
SCPI_ResultText(
 scpi_t * context,
 const char * data);
「を含むデータを検索し、「を含む文字列を結果に書き込みます(あまり役に立たないAPIかもしれません)、おそらくFUNCです。

size_t
SCPI_ResultBool(
 scpi_t * context,
 scpi_bool_t val);
bool値を結果に書き込みます。

size_t
SCPI_ResultCharacters(
 scpi_t * context,
 const char * data,
 size_t len);
原始文字列結果を出力に書き込み、最初に回伝区切り文字「,」を送信し、その後に文字列を送信します。

size_t
SCPI_ResultMnemonic(
 scpi_t * context,
 const char * data);
SCPI_ResultCharacters()文字列送信関数と等価ですが、本関数のlenはsizeofです。
size_t
SCPI_ResultArbitraryBlock(
   scpi_t * context,
   const char * data,
  size_t len);
任意のブロックデータに(#1+バイトデータ長)ヘッダーを追加し——ヘッダーの変更はSCPI_ResultArbitraryBlockHeader()関数定義を参照し、\r\nの尾を追加し、SCPI_Write()関数を呼び出して送信します。

この関数は実際に以下の2つの関数を呼び出します。

size_t
SCPI_ResultArbitraryBlockHeader(
   scpi_t * context,
 size_t len);
運算データブロックに追加すべきヘッダーを追加して送信します。

size_t
SCPI_ResultArbitraryBlockData(
   scpi_t * context,
   const char * data,
 size_t len);
エラーチェック付きのデータブロック送信関数で、contextのarbitrary_remindingパラメータが指定された長さlen未満の場合、システムエラーSCPI_ERROR_SYSTEM_ERRORを生成します。

size_t
SCPI_ResultText(
 scpi_t * context,
 const char * data);
「を含むデータを検索し、「を含む文字列を結果に書き込みます(あまり役に立たないAPIかもしれません)、おそらくFUNCです。

size_t
SCPI_ResultBool(
 scpi_t * context,
 scpi_bool_t val);
bool値を結果に書き込みます。

size_t
SCPI_ResultCharacters(
 scpi_t * context,
 const char * data,
 size_t len);
原始文字列結果を出力に書き込み、最初に回伝区切り文字「,」を送信し、その後に文字列を送信します。

size_t
SCPI_ResultMnemonic(
 scpi_t * context,
 const char * data);
SCPI_ResultCharacters()文字列送信関数と等価ですが、本関数のlenはsizeofです。

以下の結果を書き込むAPIは、Baseパラメータがない場合、すべて10進数に変換して文字列として書き込みます。

size_t
SCPI_ResultDouble(
 scpi_t * context,
 double val);
ダブル精度値を結果に書き込み、最初に結果区切り文字「,」を送信し、その後に値を送信します。

上記の関数と同様のロジックを持つAPI関数は以下の通りです。
SCPI_ResultFloat(scpi_t * context,float val) float値を書き込みます。
SCPI_ResultInt16(scpi_t * context,int16_t val) 符号付き16ビット値を書き込みます。
SCPI_ResultInt32(scpi_t * context,int32_t val) 
SCPI_ResultInt64(scpi_t * context,int64_t val) 
SCPI_ResultInt8(scpi_t * context,int8_t val) 
SCPI_ResultUInt16(scpi_t * context,uint16_t val) 無符号16ビット値を書き込みます。
SCPI_ResultUInt32(scpi_t * context,uint32_t val)
SCPI_ResultUInt64(scpi_t * context,uint64_t val)
SCPI_ResultUInt8(scpi_t * context,uint8_t val) 
SCPI_ResultUInt16Base(scpi_t * context,uint16_t val,int8_t base) 無符号16ビット値をBase進数に変換し、文字列として結果に書き込みます。
SCPI_ResultUInt32Base(scpi_t * context,uint32_t val,int8_t base) 
SCPI_ResultUInt64Base(scpi_t * context,uint64_t val,int8_t base) 
SCPI_ResultUInt8Base(scpi_t * context,uint8_t val,int8_t base) 

配列形式での回伝生成 API#

前の部分の回伝生成 API と似ており、この部分の API は配列をインターフェースパラメータ scpi interface の結果ストレージに格納し、SCPI_Write () 関数をトリガーします。

size_t 
SCPI_ResultArrayDouble(
    scpi_t * context,
    const double * array,
    size_t count,
    scpi_array_format_t format);
arrayが指すcount個のダブル要素を配列に変換し、formatはシステムのエンディアンを選択します。

SCPI_ResultArrayFloat(scpi_t * context,const Float * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayInt16(scpi_t * context,const int16_t * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayInt32(scpi_t * context,const int32_t * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayUInt64(scpi_t * context,const uint64_t * array,
size_t count,scpi_array_format_t format);
SCPI_ResultArrayUInt8(scpi_t * context,const uint8_t * array,
size_t count,scpi_array_format_t format);

データを文字列に変換する API#

デフォルトではインターフェースパラメータの回伝値に対して操作を行いません。

size_t
SCPI_DoubleToStr(
    double val,
    char * str,
    size_t len);
ダブル精度値を文字列に変換し、strが指すアドレスに値を代入し、lenは許可される最大バッファサイズのバイト長です。

SCPI_FloatToStr(float val,char * str,size_t len);
SCPI_Int32ToStr(int32_t val,char * str,size_t len);
SCPI_Int64ToStr(int64_t val,char * str,size_t len);
SCPI_UInt32ToStrBase(uint32_t val,char * str,size_t len,int8_t base)変換された文字列はbase進数です。
SCPI_UInt64ToStrBase(uint64_t val,char * str,size_t len,int8_t base);

size_t
SCPI_NumberToStr(
    scpi_t * context,
    const scpi_choice_def_t * special,
    scpi_number_t * value,
    char * str,
    size_t len);
特殊ルールの下で数字を単位付き文字列に変換し、SCPI_ParamNumberは一対の逆関数です。

拡張されたパラメータ処理 API#

scpi_bool_t
SCPI_ChoiceToName(
    const scpi_choice_def_t * options,
    int32_t tag,
        const char ** text);
optionsは{文字列、int_tデータ}の表構造であり、この関数はtagデータに基づいて表を参照し、対応する文字列のアドレスをtextが指すアドレスに代入します。

scpi_bool_t
SCPI_ParamIsNumber(
    scpi_parameter_t * parameter,
    scpi_bool_t suffixAllowed);
パラメータが数字型であるかどうかを確認します。通常はSCPI_Parameter(context, &param, mandatory)を使用してパラメータを取得した後に使用します。

scpi_bool_t
SCPI_ParamIsValid(
    scpi_parameter_t * parameter);
この関数は代入関数でエラーが発生したかどうかを確認します。

scpi_bool_t
SCPI_ParamToChoice(
    scpi_t * context,
    scpi_parameter_t * parameter,
    const scpi_choice_def_t * options,
    int32_t * value);
optionsは{文字列、int_tデータ}の表構造であり、この関数はcontext内のパラメータを参照し、パラメータを文字列に変換します。通常はSCPI_Parameter(context, &param, mandatory)を使用してパラメータを取得した後に使用します。
scpi_bool_t
SCPI_ParamToDouble(
    scpi_t* context,
    scpi_parameter_t* parameter,
    double* value);
パラメータをダブル値に変換します。通常はSCPI_Parameter(context, &param, mandatory)を使用してパラメータを取得した後に使用します。

上記の関数と類似のロジックを持つ関数は以下の通りです。
SCPI_ParamToFloat(scpi_t* context,scpi_parameter_t* parameter,float* value);
SCPI_ParamToInt32(scpi_t* context,scpi_parameter_t* parameter,int32_t* value);
SCPI_ParamToInt64(scpi_t* context,scpi_parameter_t* parameter,int64_t* value);
SCPI_ParamToUInt32(scpi_t* context,scpi_parameter_t* parameter,uint32_t* value);
SCPI_ParamToUInt64(scpi_t* context,scpi_parameter_t* parameter,uint64_t* value);

scpi_bool_t
SCPI_Parameter(
    scpi_t* context,
    scpi_parameter_t* parameter,
    scpi_bool_t mandatory);
コマンドラインから1つのパラメータを取得し、parameterに代入します。parameterにはデータの長さやデータ型も格納されます。

コマンド処理 API#

int32_t
SCPI_CmdTag(
    scpi_t* context);
検出されたコマンドタグを返します;理解できません、テスト待ちです。

scpi_bool_t
SCPI_CommandNumbers(
    scpi_t* context,
    int32_t* numbers,
        size_t len);
コマンドリスト内で、許可された数字の位置を指定し、処理関数内でこれらの位置の数字をnumbersに埋め込むことができます。lenは配列の長さです。例えば{.pattern = "TEST#:NUMbers#", .callback = TEST_Numbers,}のように、TEST3:NUMbers2を受け取った場合、SCPI_CommandNumbers(context,&data[0],2)を使用すると、data[0]に3が代入され、data[1]に2が代入されます。
読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。