作成したいのはDLLです。このDLLはRS232Cで外部装置と通信します。外部装置とDLLを両
方作成するのでプロトコルなどは自由にできます。
困っているのはこのDLLを複数のアプリケーションが同時に使うということです。
そしてかなり頻繁に通信をするとの事です。通信は主に読み出しでたまに書き込みが発生
するようです。通信のつどオープンクローズするのはパフォーマンスの点でだめなことが
わかっています。
ですから、方針としては、はじめにロードされたときにシリアルをオープンし、ハンドル
を取得して後はこのハンドルを通じて読み書きを行うという事になると思います。
私はマルチスレッドとかマルチプロセスとかの経験が無いのでどうしたら良いか考えてい
るところです。それでここで何かアドバイスをいただきたくて質問しました。
まず、ハンドルの共有はどうするのが良いか。それから同時に通信しないように排他処理
が必要だと思いますがどうすればよいのか、受信データや送信データの受け渡しはDLL内
の関数への引数で問題ないのかを教えていただければ幸いです。
もちろん方針そのものへのアドバイスでも良いです。
よろしくお願いします。
言い忘れました、OSはWindows XP embedded
DLL作成言語はVisual C++ 2010 または2008です。
シリアル通信アプリを作って、アプリはソケット通信でシリアル通信アプリと
繋いたほうが簡単じゃないでしょうか。
こんな感じで
[アプリ]-[通信DLL]-socket-
[アプリ]-[通信DLL]-socket-[シリアル通信アプリ]-RS232C-[デバイス]
[アプリ]-[通信DLL]-socket-
PPP…ですかね。
http://ja.wikipedia.org/wiki/Point-to-Point_Protocol
先日、シリアルモニタのログから通信内容を可視化するアプリを…
ということでPPPやらIPパケットやらTCPパケットやらを…。
回答ありがとうございます。
socketはネットワーク通信用だと思っていました。その様な使い方ははじめて知りまし
た。使い慣れている人にとっては簡単かも知れませんが、私には難しいです。
またPPPもネットワークで使われているプロトコルですよね。やはり私には難しいです。
私がプロトコルなんて書いてしまったから、そちらに行ってしまったのでしょうか。
すみません、忘れてください。プロトコルというほど複雑な仕様ではありません。
コマンドとレスポンス程度に考えていました。例えば0x30を送ると現在の内部フラグを
1バイト返すとか。その程度です。今までH8などの組み込み用プログラムを作ってきたの
で、windowsのアプリケーションはあまり経験がありません。アプリケーションが1つなら
私でも出来ますが、複数のアプリケーションが1つのシリアルポートで通信するという事
に不安を感じています。
単に DLL だと、 DLL のデータ領域は複数 EXE で共有されないので
(DLL のコード領域は共有される=省メモリ)
期待している処理にならない・・・と思うのだが。
そもそも COM ポートってハードウェア的に1:1接続、
ソフトウェア的にも1プログラムが排他利用ってのが自然な使い方だけど、
複数プログラムから連続的に(矛盾する)コマンドを送ってうまく動くのかな?
> 今までH8などの組み込み用プログラムを作ってきたの
> で、windowsのアプリケーションはあまり経験がありません。
tetrapodさんの意見にもありますが、RS232Cは、1:1接続なので、
> 複数のアプリケーションが1つのシリアルポートで通信するという事
> に不安を感じています。
基本的にこれは無理です。
一つできるとしたら、
RS232C用のスレッドを一つ作って、それを複数のアプリケーションと通信している
ようにみせかける。
ぐらいですね。
実際は出来ていないです。
H8などで最近よく使うI2Cとは違うことはいいですよね。
もし、実現させないと仕様的にまずいのなら、LAN接続等を考えた方がいいと思います。
複数のプログラムがほぼ同時に COMx: の先につながっている機器と交信する、
という仕様の装置+ソフトを、俺は作ったことがある。
異なるプログラムがほぼ同時にコマンドを発行したとして
・両者の電文は区別できる必要があるのかないのか?
・ソフトは装置への送信コマンドをどう取り扱うか?
・装置はどう返信するのか?
・ソフトは装置からの返信をどう取り扱うのか?
などなどの仕様をあらかじめきっちり決めて、
「あたかも機器⇔PC間は1:1通信しているように見える」よう実装すればいい。
事実上 COMx: は1つのプログラムからしか開けないので
DLL 1個で済ますことはおそらく不可能。
通信だけを担当する EXE が1つ必要になる。
solid 氏の実装がよい例だね。
プロセス間通信に SOCKET を使う必然は無いとは思うが...
使ってはダメな理由もないし、実際プロセス境界を越えるにはよい手だけど。
多分、通信部分がディスパッチャの役目を果たさないといけないと
思うので、果たしてうまく行くのかなぁと言うが正直な話ですね。
既に話題に出ていますけれど、外部装置の方の仕様次第で
話がかなり変わってきそうです。
外部装置が一つの要求を受けている時に他の要求を受けられない
つまりシーケンシャルにしか処理できない仕様なら
ディスパッチャの方でほぼ同時に来た要求をプールして
順番に処理するような仕組みが必要になると思います。
外部機器が賢くて要求処理中に他の要求の受付も可能なら
要求と返信の対応だけをディスパッチャが管理すれば良いと思うので
少しは楽が出来そうです。但し、シリアルに処理していて
果たしてパフォーマンスが出せるのかは別の話。
いずれにせよ、通信部分で要求と返信の管理を行って
未返信の要求に関しては情報を保持し続ける必要があると思います。
外部装置から返信が来た時に要求元に返信内容を送り返す必要がある為です。
なので状況によってはリソースが足りないなんて自体も考慮に入れる必要が
あると思います。(要求速度に返信速度がついていかない場合など)
また、通信部分と各アプリケーション間のやり取りに関しては
独自のインターフェイスを用意する必要があると思います。
この辺は既にtetrapodさんが書かれている通りですね。
皆さんありがとうございます。
tetrapodさんが作ったことがあるという
>複数のプログラムがほぼ同時に COMx: の先につながっている機器と交信する、
>という仕様の装置+ソフト
>「あたかも機器⇔PC間は1:1通信しているように見える」よう実装すればいい。
をまさに私も作りたい(作らなくてはならない)のです。
外部装置はこの通信部分にはあまり手間をかけられないので、シーケンシャルな処理とな
ると思います。
みなさんの意見としては、アプリケーションから直接呼ばれるDLLと実際に外部機器と通
信するexeが必要というのが多数なのですが、2つに分けると結局DLLとexe間でまたやり取
りや調整が必要になるので出来れば1つのDLLだけで済ませたいと思っています。
そうすると通信部分もDLLが持つのですが、実際に通信できるのは1つだけということにな
ります。それは排他処理で何とかなると思っています。不安なのは本当に複数のDLLが1つ
の機器と通信できるのかです。私が考えている方法は最初にオープンしたDLLが共有メモ
リにそのハンドルを書き込み、排他処理によって通信の順番が回って来たDLLが共有メモ
リに書き込まれたハンドルを使って通信する。という方法です。つまり他のDLLがオープ
ンした時のハンドルを使って通信できれば可能だと思うのですが、大丈夫でしょうか。
それからもうひとつわからないのは外部機器からレスポンスが返ってきた時、それをどう
やって上位アプリに伝えるのが良いのか、ということです。DLLは上位アプリからコマン
ドを受け取ったらACKを返します。そうすることで上位アプリがレスポンス待ちのために
他の処理を止めないですむからです。外部機器からレスポンスがDLLに返ってきたらDLLが
アプリにレスポンスが返ってきたことを知らせたいのですが、それにはどんな方法が適し
ているのかを知りたいです。おそらくイベントだとかメッセージとかだと思うのですが、
それらは発信先を指定する必要があるでしょう。それをどうやって知ればよいのでしょう
か。DLLの関数の引数にアプリのハンドルを入れておけばよいのかな。などと思っていま
すが、経験がないものでよくわかりません。
まず、上位APと通信部(ここではあえてDLLともEXEとも書きません)との
やり取りに関しては通常のシリアルポートを開いて書き込みと言う
インターフェイスに拘る必要は無いと思いますが、
あえてシリアルポートを開くのと同じ方式でやらなければならない
仕様なのでしょうか?
シリアルポートの動作をエミュレートする必要が無いのであれば、
いっその事、もっとすっきりした動きにしてしまった方が
わかりやすそうな気もしますけれど。
非同期で動作する事を前提にAPとどういうシーケンスでやり取りするのが
わかりやすいかを考えてインターフェイスの設計をした方が良いと思います。
ACKを返すと言う部分を関数呼び出しで返信があるまでWAITするのではなくて
すぐに関数としては返って来るようにして結果は別の手段で取得するという
事だと解釈して書いていますから違っていたら聞き流してください。
呼び出し側に通知する手段をウインドウメッセージで行うなら
呼び出し側から予めウインドウハンドルを受け取っておく必要が有ります。
まあ、これが一番簡単でしょうね。
呼び出し側はメッセージをうけったら結果取得用の関数を呼び出すとか
にしておけばよいでしょうし。
イベントを使うケースは御自分で調べて見てください。
イベント使った同期に関しては調べればたくさん出てくるはずです。
あと、割と具体的な方法まで考えておられるようなので
そこまで考えているのであれば、一度テスト的に簡易実装をして
見てはいかがでしょうか。
プロセスが分かれるのでプロセスを跨いでもきちんと排他出来るような
同期オブジェクトを使えば実現は出来そうな気がします。
但し、パフォーマンスが出せるかどうかはまた別の問題と言う気もします。
あと、非同期にするのであれば、要求処理が簡潔していない状態での
次の要求をどう扱うのかはきちんと決めて使う側にも納得してもらって
おく必要があると思います。
リソースにしても無限では有りませんし、アプリが結果を取りに来るまで
結果の保持が必要となると結果を複数溜めておくようにするのかとか
複数溜まった結果の中から取り出したい結果を特定する方法はとか
色々、考えないといけない事が多くなりそうです。
これが同期処理ならもう少し単純に考えられそうな気がするんですけどね。
>の機器と通信できるのかです。私が考えている方法は最初にオープンしたDLLが共有メモ
>リにそのハンドルを書き込み、排他処理によって通信の順番が回って来たDLLが共有メモ
>リに書き込まれたハンドルを使って通信する
このような場合、CreateFile()だと思うけど
このCreateFileが成功したときに返ってくるハンドルは、プロセス固有の値。
共有メモリに書き込んでも、他プロセスからはただの数字でしかない。
ハンドルを他プロセスで利用するためにDuplicateHandleって関数があるけど、
CreateFileで開いたハンドルの場合、
DupulicateHandleしても、CreateFileした結果と同じ値が返ってくるから
そもそもCreateFile()で共有フラグつけて、複数開けない対象なら、
DuplicateHandleもエラーになるんじゃないかな
で、RS232Cのバーコードリーダーを使ったときは、共有では開けなかった(経験のみ)
PATIOさんアドバイスありがとうございます。上位とのやり取りはまったく決まって
いません。ですからシリアルポートの動作をエミュレートする必要はありません。
ryoさんありがとうございます。ハンドルはプロセス固有でしたか。それで皆さん通信部
を1つ独立したアプリとしてアドバイスしてくれていたわけですね。
皆さんのおかげで方向性がかなり見えてきました。本当にありがとうございました。
少し皆さんの好意に甘えすぎたかなと反省しています。
ここからは自分でやってみます。