NI製品ディスカッション

キャンセル
次の結果を表示 
次の代わりに検索 
もしかして: 

インボークノードの出力ノードがCの配列の場合の対策

お世話になります。

 

LabVIEW8.6.1を使用しております。

 

フロントパネルに三菱シーケンサにアクセスするためのMXComponentのActiveXコンテナを

配置して、インボークノードを使用して三菱シーケンサと通信を行っております。

 

添付図中央にあるIActEazyIF3/ReadDeviceBlockインボークノードのlplDataノードは

本来配列なのですが、LabVIEWがポインタの引数を単一のスカラ値と判断しているようで

図のように配列のワイヤになっていません。

 

ReadDeviceBlockはマニュアルには下記のように記載されています。

 

Visual C++
lRet = object.ReadDeviceBlock(szDevice, lSize, *lplData)
Long lRet 戻り値(Output)
CString szDevice デバイス名(Input)
Long lSize 読出し点数(Input)
Long *lplData 読み出したデバイス値(Output)

 

szDevice(varDevice)にて指定したデバイスから,lSize(varSize)分のデバイス値を一括して読み出します。
読み出したデバイス値は,lData(lplDataまたはlpvarData)に格納されます。
lData(lplDataまたはlpvarData)は,lSize(varSize)以上の配列を確保してください。

 

図のようにシーケンサのD100番地から2個データを取得してそれを配列に入れたいのですが、

LabVIEWでは出来ません。

Visual C++ では出来ました。

 

CとLabVIEWでは配列の型が違うことが分かっておりますが、LabVIEWで2個読み取る事は出来ないでしょうか?

 

現状対応としてはD100番地とD101番地それぞれReadDeviceBlockを2回実行して2個のデータを読み出していますが、

この方法ですとCでも同様ですが、あるアドレスに対して2バイト書き込みと1バイト読み出しが別々のスレッドで

行われると競合が発生し、タイミングによっては正しく読み取りが出来ません。

 

何か対処法がありましたら教えて下さい。

 

よろしくお願いします。

 

0 件の賞賛
メッセージ1/7
7,012件の閲覧回数

lplDataが (long *) なので、long の配列が格納されたデータ領域の「先頭アドレス(DWORD)」そのものが、

long値(I32) として返ってきている感じでしょうか。あたかも、(long)((long *)lplData) とキャストしたかのように・・・

 

ちょっと試してみていただきたいですが、添付のVIをサブviとしてダイアグラムに配置し、当該インボークノードにつないだ

lSize と同じものと、インボークノードの出力 lplData を接続すると、サブviからの出力の返り値 [I32] が所定の配列に

なりそうでしょうか。

 

添付のVIは、予め lSize の数だけ要素を持つ [I32] 配列を受け皿として用意し、そのデータ領域に、lplData が指す

メモリ領域の先頭から (lSize*4) バイトだけコピーします(kernel32.dll の RtlMoveMemory() を呼び出し)。

RtlMoveMemory() は本来2つのポインタを取りますが、第1引数に1次元配列データのポインタ、第2引数を I32 に

することで、lplData の中身(データの先頭アドレス)を渡しています。

 

もし、lplData が、本来の配列データの先頭アドレスを保持しているなら、これで [I32] へ持っていけるかなと。

 

なお、*lplData の long値データは little endian とみなされ、LabVIEW側で自動的に big endian に直して

格納してくれるようで、エンディアン変換の手間は要らないようです。

(逆に、元データが big endian だと、LabVIEWがひっくり返してしまうので自分で戻さないといけないかも)

 

※ lSize と lplData は必ず対応するものを接続して下さい。用意した受け皿の配列アドレスと大きさで

  RtlMoveMemory() を呼び出しているので、バッファオーバーランや不正な上書きは起こらないと思いますが、

  lplData が無関係な値を指していると、不正な memory read になるかも知れません。

  (lSize が 0 でも大丈夫そうです。lplData が NULL のときはcaseで排除はしていますが・・)

 

0 件の賞賛
メッセージ2/7
6,989件の閲覧回数

M.Shiraishiさん

 

アドバイスありがとう御座います。

 

lplDataには三菱シーケンサD100番地の値(アドレスではなく)が戻りますので、(long)((long *)lplData)

のキャストでは無く *((long *)lplData) になります。

 

作成して頂いたviを添付図のように配線して試しました。

やはり、アドレスが返ってきている訳ではないので、100427-sample-subvi-1.viの結果は0が二つ

入っているだけです。

 

MXComponentのActiveXをラップするActiveXを自作することで対応出来そうですが、

ActiveXのコードを書いた経験が無いため、最小限の時間と労力で実現出来る方法を調べております。

 

ちなみにlplDataの値が0以外の値であっても、RtlMoveMemory()を使用しているLabVIEWが

クラッシュすることはありませんでした。

 

取り急ぎご報告させて頂きました。

 

今後ともよろしくお願いします。

 

0 件の賞賛
メッセージ3/7
6,960件の閲覧回数

h.seki 様

 

>lplDataには三菱シーケンサD100番地の値(アドレスではなく)が戻りますので、(long)((long *)lplData)

>のキャストでは無く *((long *)lplData) になります。

 

うーむ、なるほど、そうでしたか。

それって本当に、「lplData」ではなく「*((long *)lplData」、あるいは問題点を明示すると「((long *)lplData)[0]」が

返ってしまっているのですね。

(だとすると、元々のインボークノードの作られ方に大いに問題があるような・・・)

 

値(配列の第1要素)が返っているのだとすると、その値の格納されていたアドレスが分からない限り、その隣の

データを取ってくるのは難しそうですね・・・・・残念。。。

 

0 件の賞賛
メッセージ4/7
6,954件の閲覧回数
M.Shiraishiさん

返信ありがとう御座いました。

ActiveXのリファレンスをコードインターフェースノードに配線して、リファレンスをC内で
キャストしてReadDeviceBlock()メソッド(インボーク)を実行するような荒業が出来ればなん
とかなりそうなのですが、、、

これは独り言でした。

引き続き調べたいと思います。

0 件の賞賛
メッセージ5/7
6,943件の閲覧回数

その後、どうなったか教えて下さい。

 

私も、同じで、データを1個ずつ呼んでいます

0 件の賞賛
メッセージ6/7
5,837件の閲覧回数

MoveBlock関数をご使用して頂ければ実現できるかと思います。

詳細はこちらのディスカッションを参照してください。

SVanessa_0-1706751270422.png

SVanessa_1-1706751321876.png

 

0 件の賞賛
メッセージ7/7
2,231件の閲覧回数