LinuxCNCでジョイスティック代わりにトラックボールを使う

経緯

我が家のNCフライスは,設置場所が想定以上に狭く,Z軸の調整用ハンドルが天井に当たってしまい,場合によっては接触して異音の原因となることがあった.調整用ハンドルを除去すれば問題は解決するが,エンドミルの位置決めなど,目視での軸移動が必要な際に不便であるのは必至であった.
そこで,Z軸調整の代替を目的としたジョイスティックを導入する.調整用ハンドルと同様に360度回転可能で,左右にひねれば上下し,材料にぶつかれば感触がフィードバックする機構があれば良い.しかし,現実的にはぶつかった感触を知るのは難しいので,前者の2つをサポートしてれば良い.
市販されている機器の中では,Kensington社が販売しているトラックボールのスクロールリング機能が,求めている機構と最も近い.幸い,4000円以下の廉価なモデルでもスクロールリングが備わっており,これをジョイスティックとして利用することにした.
XY軸移動用にトラックボールは役に立ちそうだが,一方の軸だけを動かす用途には向いていないだろう.アバウトな位置設定用として上手く利用したい.

f:id:sosoru_m:20170109201945j:plain:w300
▲Z軸当たってる

使用したモデル

Kensington社のOrbitTrackball with Scroll Ring 72337JP
https://d107iaxg6vni3j.cloudfront.net/mbank38730_560_560.jpg
https://www.kensington.com/ja/jp/4493/72337jp/orbit-scroll-ring-trackball
https://www.amazon.co.jp/gp/product/B004QE4JXA/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1
購入時は3800円程度.トラックボール・スクロールリングの他,ボタンは2つのみ.
普段使いのマウスとしてはちょっとボタンが少なすぎるかも.LinuxCNCが提供しているDebian環境では特に問題も起きずに認識した.

f:id:sosoru_m:20170113233500p:plain:w300
▲LinuxCNCのShow Hal Configurationで見たトラックボールの状態(loadrt後).トラックボールだと各軸の座標が浮動小数ではない?(参考:Shimalith氏のページでのアナログパッドは浮動小数点を返している)

設定したい機能

トラックボール XY軸移動に対応
スクロールリング Z軸移動に対応
左クリック ジョイスティック有効
右クリック ジョイスティック無効

HAL

LinuxCNCではHALと呼ばれる,ちょっとした設定記述用言語が用意されている.これは,ワイヤードロジックっぽい感じで書かねばならず,C言語というよりかはHDL系の言語に近い.
例えば,クリックでスイッチのON/OFFを切り替える場合,if文で現在と過去のマウスボタンの状態を見てスイッチを変えましょう,というわけにはいかず,まずマウスボタンの現在の状態を2入力and素子で受け取り,その出力をnot素子を通してもう一方のand入力に接続することで過去の状態を取得する.ほとんどデジタル電子回路である.

LinuxCNC側の設定

トラックボールを検出するには,まず

$ less /proc/bus/input/devices

にて,デバイスの名前を確認する.これはhalコードでデバイスを指定するときに必要になる.今回のデバイスは「Eagle」が含まれており,これを利用する.
Xがマウスとして認識した場合には回避する必要があるらしいが,当環境では発生しなかったため,設定は割愛する.(参考:Movement for atomic step(s) by mouse scroll wheel?には書いてある)
次に認識したデバイスを一般ユーザーでも扱えるようにするために,udevの設定を変更する.

$ sudo echo '' >> /etc/udev/rules.d/99-rtai.rules
$ sudo echo 'SUBSYSTEM=="input", MODE="0660", GROUP="plugdev"' >> /etc/udev/rules.d/99-rtai.rules

以上の設定が済めば,halrunコマンドの後に動作を確認する.

$ halrun
halrun : loadusr -W hal_input -KRAL Eagle
halrun :

halrunに何も言われなければOK.
MANを読め,みたいなことを言われたら,上のudevの設定が上手く行っていない可能性あり.その場合,rootで実行すれば成功する.

その他の設定のポイントを以下に示す.

  • 各軸の移動とトラックボール・スクロールリングの移動を対応するには,input.0-rel-?-positionとaxis.?.jog-countsを接続すれば良いが,整数型から浮動小数点型に型変換する必要があるため,conv-floats-32を通して接続する必要がある.
  • ジョイスティックの有効無効を左クリックと右クリックに対応させるにはRS型フリップフロップを用いて対応している.また,jog-enableを切り替えるとトラックボールのボタンが効かなくなる恐れがあるため,jog-scaleを変更し,左クリックが押されるとjogscale=0.1,右クリックが押されるとjogscale=0に設定される.
  • 左クリックはbtn-mouse~を使用する.

halコード

# load remote joystick
loadusr -W hal_input -KRAL Eagle

# define components
loadrt conv_float_s32 count=3
loadrt and2 count=2 
loadrt not count=2
loadrt mux2 count=3

# set clock
addf conv-float-s32.0 servo-thread
addf conv-float-s32.1 servo-thread
addf conv-float-s32.2 servo-thread
addf and2.0 servo-thread
addf and2.1 servo-thread
addf not.0 servo-thread
addf not.1 servo-thread
addf mux2.0 servo-thread
addf mux2.1 servo-thread
addf mux2.2 servo-thread

# setting joystick
setp axis.0.jog-vel-mode 0
setp axis.1.jog-vel-mode 0
setp axis.2.jog-vel-mode 0

setp axis.0.jog-enable 1
setp axis.1.jog-enable 1
setp axis.2.jog-enable 1

# connecting as RS-FF; Q --> muxer.sel
net rsff-sb and2.0.in0 <= input.0.btn-mouse-not
net rsff-rb and2.1.in0 <= input.0.btn-right-not
net rsff-nand-s and2.0.out => not.0.in 
net rsff-nand-r and2.1.out => not.1.in 
net rsff-qb not.1.out => and2.0.in1
net rsff-q not.0.out => and2.1.in1 mux2.0.sel mux2.1.sel mux2.2.sel

# set muxer and connects for changing jog-scale
setp mux2.0.in0 0 
setp mux2.0.in1 0.01 
setp mux2.1.in0 0 
setp mux2.1.in1 0.01 
setp mux2.2.in0 0 
setp mux2.2.in1 0.01 

net x-jog-scale mux2.0.out => axis.0.jog-scale 
net y-jog-scale mux2.1.out => axis.1.jog-scale 
net z-jog-scale mux2.2.out => axis.2.jog-scale 

# connecting moving trackballs with positioning axes, through float casting
net x-float conv-float-s32.0.in <= input.0.rel-y-position
net x-s32 conv-float-s32.0.out => axis.0.jog-counts

net y-float conv-float-s32.1.in <= input.0.rel-x-position
net y-s32 conv-float-s32.1.out => axis.1.jog-counts

net z-float conv-float-s32.2.in <= input.0.rel-wheel-position
net z-s32 conv-float-s32.2.out => axis.2.jog-counts
  • 入力信号をnet文で2回以上使用するとエラーが出る.出力が複数にわたる場合(例えばRS-FFのQ端子)には,スペース区切りで複数指定可能.(参考:Basic HAL)
  • 動作確認後,直感的な動作では無かったため,rel-x-positionとrel-y-positionを入れ替えた.
  • RS-FFのR=S=1は禁止だから,同時に押すとどうなるのか気になるが,ソフトが落ちることは無かった.不定な動作になりそうだが.

動作確認

X軸Y軸の対応が直感的では無かったため修正したが,おおむね期待通り動作した,ジョイスティックだとトップスピードで軸移動するっぽいのが少し不安.
Z軸のスクロールリングは,1周で約80ノッチに相当しており,現在は,0.8mm/周で運用している.感覚的な不具合があれば追記したい.
f:id:sosoru_m:20170113234115p:plain:w300
▲適当に動かせば適当に動いてくれる

CNCフライスでHOME位置割り出し用パーツを作る

はじめに

NCフライスは,軸移動の限界位置を知るため,軸の端の部分に光センサー(フォトインタラプタ)が設置されていて,この位置より先には動かせないような安全機構がある.
制御ソフトにもよるが,LinuxCNCでは軸位置の基準であるHOME位置を知るために使われていて,起動時にこのセンサーがちょうど引っかからない場所を自動で見つける手順(Homing)を踏まないと,Gコードを流し込めない.
しかし,我が家のNCフライスは設置場所の都合上,Y軸方向の移動に制限があり,付属しているHOME位置割り出し用のパーツ(フォトインタラプタの遮光板)では,限界位置を割り出せない.
LinuxCNCのHomingは必須であるから,これまでは適当なアルミ定規で光センサーを引っかけて,Y軸のHOME位置を適当に設定していた.
さすがにこのままでは良くないので,今回は,ちゃんとパーツを作ってやって,LinuxCNCが自動でHomingが出来るようにすることが目的である.

f:id:sosoru_m:20170109200737j:plain:w300
▲作成したパーツを設置したところ(銅箔の基板)

f:id:sosoru_m:20170109200657j:plain:w300
▲手に持っているのが付属していたパーツ,裏の基板が今回作成したパーツ

条件

  • スピンドル回転数S 12000rpm
  • 送り速度F 300mm/min
  • 被切削材 厚さ1.6mmの片面生基板(材質:CEM-3)
  • 直径2mmの超硬エンドミルを使用.

コード

OpenSCADのコード.実際のパーツの角の丸めは1mm(長穴は6mm).これはGコード生成の際,VCarveで付加したので,下記コードでは得られない.
(また,長穴については6mm空けるべき所を5mmで開けていたので修正した)

difference(){
    polygon(points=[[0,0],[65,0],[65,40],[45,40],[45,10],[0,10]]);
    translate([50,29,0], center=true) square([10,6]);
}

流れ

付属していたパーツの寸法を測って,足りない長さを加味しつつ,OpenSCADで形を作る.生成したDXFデータをVCarveにインポート.角を丸めてGコードとして出力する.これをLinuxCNCに渡す.
秋月で購入した片面の生基板の両端に,強力な両面テープを張る(厚さ1mm位).面出しを行ったMDF板にこれを固定.切削を行う.
(一番最初はテープで4辺を固定してたけど,全然固定できていなくて失敗した.)

f:id:sosoru_m:20170109202514j:plain:w300
▲秋月の生基板.赤線で囲んだ両端に両面テープを張る

f:id:sosoru_m:20170109202937j:plain:w300
▲削りたて

f:id:sosoru_m:20170109200107j:plain:w300
🔺作成したパーツ,切削時に材料がビビった様な音がしていた.手元のノギスではピッタリ寸法が出ていたので問題ない.

作成したパーツは長穴の寸法が1mm狭かったのでヤスリで削って調整した.調整後,実際に設置してみてHoming動作確認.問題なし.

反省点

  • 材料の固定で妥協しちゃいけない.
  • 長穴の寸法.5mmのネジを入れるのに5mmしか開けていなくて入らない.ひどいミスをした.
  • ビビり音の原因については,材料の固定が甘いというよりも,NCのZ軸が鉄板に接触して,切削時に鉄板自体が震えて異音を生じたと見ている.これについては後日対策を行う.

f:id:sosoru_m:20170109201945j:plain:w300
▲NCフライスのZ軸が上の鉄板に触れている.ちゃんと回転はするものの,回りずらい感触がする.NCフライスの端の部分では,回りずらさは感じられない.

  • 切削作業自体を夜中の3時に行ったので,今回のビビり音は近隣住民の迷惑になったかもしれない.反省.

CNCフライスでMDF板を面だしする

はじめに

NCフライスに用いる被切削材の土台として、MDF板を使用したい。土台として使用する前に、面を削って平坦化する(面だし)。
今回の場合、四隅をクランプで固定+両面テープで固定し、S12000、F800にて切削を行うと上手く面出しを行える。
f:id:sosoru_m:20170106201133j:plain:w300

条件

  • スピンドル回転数S 12000rpm
  • 送り速度F 800mm/min
  • 被切削材 厚さ9mmのMDF板
  • 直径2mmの超硬エンドミルを使用.

板の表面全面を深さ2mm削り取る(本当は0.5mmぐらいで良いはず.切削材が反って失敗したため大きめに削り取っている.)

流れ

今回は,実際に主軸スピンドルを回転させて,実際に切削する様子を見るのが一つの目的である.ある種,定番となっている土台の面出しをさせてみる.
また,前回,NCフライスの動作確認としてボールペンの替え芯を主軸に持たせてプロットを行ったが,土台の歪みにより,プロット時に位置に依存した歪みが生じていた.今回は,厚さ9mmのMDF板を面出しし,これをベースにプロットすることで,低歪みの地図を見たい.

しかし,面出し一つでも上手く切削することができなかった,どうやら,MDF板の固定が甘く,板の中心から離れた場所を削ろうとすると,板が土台から剥がれ凹型に反り,結果として面を削りすぎてしまう.

f:id:sosoru_m:20170106201133j:plain:w300
▲両面テープを5本にしたまま,クランプで4隅を固定した.この方法であれば上手く面出しできる.
ただし,クランプの影響で,板全面は削れない,余白ができる.

試行錯誤する中で送り速度を下げてみたが,あまり影響はなかった.板を固定する方が重要である.
面出しできたので,前回の地図をプロットした.

f:id:sosoru_m:20170106201409j:plain:w300
▲面だし時の木くずの影響か,インクの出ない部分があるが,上手く角が出ている.ペンの軌跡を見る限り,歪みはなさそうだった.

f:id:sosoru_m:20170106201454j:plain:w300
▲確認できる限り,最小のピッチは0.25mm程度だった.寸法通りの間隔で線が引けている.

反省点

木を切削クズが予想よりも厄介で,ちり取りだけでは処理できない.掃除機が必要.アルミ等の金属であれば切削液が使えるので,今回よりも影響が少なそうであるが,木材に水は使えない,結局のところ,集塵機の装備も必要.
面だししても,MDF材は水に弱い.切削液を使用する切削には使えない.値は張るが,厚めのアクリル板等が必要.ケチるならスチレンボード等も使えるかもしれない(耐水性があれば).

CNCフライスで地図を書かせる

はじめに

NCフライスの動作を確認するため、主軸にボールペンの替え芯を持たしてプロットさせた。
紙の土台の反り、ボールペン芯の歪みの問題が発生したものの、
結果として、0.25mmピッチ(目視)でのプロットが可能であることを確認した。

f:id:sosoru_m:20170106201409j:plain:w300

条件

  • 送り速度 F 200 mm/min
  • Z軸送り速度 Fz 200 mm/min

(Z軸送り速度も同じ速度にする。終点まで書き終わらずに引き上がる可能性あり)

  • ボールペンの替え芯を主軸に取り付けるため、3.125mmのコレットを使用して装着。

使用した替え芯はuni JetStream 0.5mm SXR-80-38である.(売り場で定規を当てて測って決めた)そのままでは長すぎたため,半分程度に切断した.

プロットしたファイル

川崎市の地図。下のサイトから拝借
http://www.dtpmap.com/free_9_11.html

dxfファイルをGコードに変換するにはdxf2gcodeを用いた.(途中からVCarveに切り替えたが,大きな変更はない)

流れ

f:id:sosoru_m:20170106223753j:plain:w300
▲最初、百均で買ってきた下敷きをマスキングテープで貼り付け、その上に紙を乗せた状態でプロットしていた。LinuxCNCのサンプルデータは割ときれいに出力できるが、角の多い図形、たとえば地図をプロットさせると、軸の移動時に替え芯が歪み、角が丸まったままプロットされる。このままでは精度のよいプロットはできない。

f:id:sosoru_m:20170106223115p:plain:w300
▲動作確認用として、15cm四方に3mm角を敷き詰めたdxfファイルをプロットさせたところ、プロットする位置によっても図形の歪みが見られる。これは土台として使用している下敷きの反りによるものであった。

下敷きのエッジをテープで固定しただけでは、反りを押さえ込むのは不十分なようなので、両面テープ(再生紙のポピュラーなやつ)で底面とフライス盤とを固定した。また、替え芯を切り詰めて軸の歪みを抑えた。この方法は割とうまくいくようで、図形の歪みが改善されているように見える。

f:id:sosoru_m:20170106202501j:plain:w300
▲地図をプロットしてみても、先ほどよりはだいぶ改善されたが、未だ位置に依存した図形の歪みが見られ、完全ではない。

f:id:sosoru_m:20170106201409j:plain:w300
▲その後、面出しを行ったMDF板を用い、これを土台にしてプロットを行った。(面出しの記事は別記事参照)紙はエッジをテープにて固定する。
一部インクが出ていないが、この方法であれば、きちんと歪みなくプロットされていることがわかる。

結論

土台に関しては、エッジをテープで固定するだけでは不十分で、両面テープを用いても、とても精度のよい結果が得られるとは言えない。これは薄いアクリル板(1mm)の使用が原因だろう。
面出しを行った9mmのMDF板を両面テープ+4隅をクランプで固定した場合に、最も精度よくプロット可能である。

LinuxCNCでCNCフライスを制御する

はじめに

一般的にNCフライスの制御にはMach3が使われることが多い.しかし,複雑な切削を行う場合(=長いGコードを読み込む場合)には,有料のライセンスを購入する必要があり,ソフトの機能に制約がある.
一方で,LinuxCNCと呼ばれるオープンソースなNCフライス制御ソフトも開発されており,こちらには機能制約はない.(機能的にはこちらの方が汎用的らしい?)
購入したNCフライスはMach3を想定して設定されているが,ライセンス代をケチるためにLinuxCNCで頑張ってみることにした.

なお,使用したNCフライスは以下の「黒い奴」である.

item.rakuten.co.jp

黒い奴にはMach3用の設定が提供されているので、これを元にLinuxCNCを設定する

コマンド

↓LinuxCNCの起動(レイテンシ調査もこっち.Sample Configurations->apps->latency.ツリービューを色々弄ると見つけられる.生成した設定ファイルはMy Configurationsに登録されるはず.)

./linuxcnc

↓LinuxCNC設定ファイル生成画面の起動

./stepconf

LinuxCNCのインストール

以下のサイトから落としてくれば良い.リアルタイム性能を引き出すために,Linuxカーネルに細工をする必要があり,細工済みの起動イメージが配布されている.これを用いるのが一番楽だ.
これを書いている人は,ソースからビルドを試みたが,あまりに躓く点が多いので,一度諦めている.

linuxcnc.org

配布されている起動イメージではvncserverが入っていない.また,sshd_configではパスワード認証を許可していないので,ログイン周りで弄らないといけないかもしれない.

起動出来た場合,最初にlinuxcncのレイテンシを調べておく(latency-histogram).レイテンシが数usec以内に収まっていない場合には,対策が必要かもしれない.
レイテンシ軽減の一つの策として,カーネルの起動コマンドにisolcpusを追加し,割り込み用のcpuを定義する方法がある.レイテンシは減るが,実質的にcpu数が一つ減るので処理速度とのトレードオフである.

f:id:sosoru_m:20170106180857j:plain
▲レイテンシの少ない例。isocpusを有効にした場合さらに改善する.

f:id:sosoru_m:20161024215649p:plain
▲レイテンシが多い例.カーネルレベルでのリアルタイム処理の対策を行っていない.

レイテンシはすなわち出力波形のジッターとして出てくるはず.オシロスコープで実際の波形を見たわけではないが,ジッターはレイテンシ以下の数値にはならないと思う.

stepconf

レイテンシが満足できたら,次はNCフライス制御の設定を行う.
パラレルポートのピン位置や,軸の回転ステップ数や範囲についてを設定する.
LinuxCNC 2.7.8ではMach3の設定を読み込めるが,読み込んだ全ての設定が反映されているかはとても怪しい.(ピン位置の設定は反映されていなかった)

Mach3の設定画面を見つつ,stepconfを進めていけば良い.


f:id:sosoru_m:20170106182524p:plain
▲この画面はあまり設定を変更する必要がなかった.Mach3のKernelSpeedとだいたい一致するように Max step rate を設定してやればよい?

f:id:sosoru_m:20170106183023p:plain
▲ピンアサイン設定.ここは悩まされた。
Mach3と対応していない一部の設定項目について説明する.StepやDirection,Home,EStopは省略する.

項目 Mach3での呼称 説明
Spindle PWM Spindle 主軸スピンドルに与えるPWM信号.
Amplifier Enable Enable1 ステッピングモータの通電切り替え?
Spindle On Output #1 スピンドル回転のオン/オフ
Coolant Flood Output #2 クーラントのオン/オフ(任意負荷)

Mach3のLowActiveはstepconfのinvertedと対応する.
また,EmergencySwitchの入力信号が,Mach3と論理が一致しないようなので,注意する.
Mach3を使いこなしていないから,OutputやEnableに関する設定がどのようにソフトの挙動と対応するかは知らない.これは試行錯誤で設定を行った.
ちなみに,二つ先の画面で軸の移動量をテストする機能があるので,ピンアサインに関して一部動作確認できる.

f:id:sosoru_m:20170106184455p:plain
▲これは重要ではない.オプションでおまけのGUIを表示できるよってやつ.スピンドル回転数などを表示するときに用いるらしい.

f:id:sosoru_m:20170106184837p:plain
▲これは重要.軸の移動量を決定する.灰色背景のテキストボックスは「編集可能」,もっとユーザビリティを考えてほしい.Mach3ではmmあたりのステップ数(画像一番下の数値)のみ決定するが,LinuxCNCでは一回転あたりのステップ数を指定する.軸のリードピッチが分からないと変換できないので,調べる必要がある.
右上のボタンで移動量をテストできる.(もちろん,ピンアサインが間違っていたら動かない.)

f:id:sosoru_m:20170106185605p:plain
▲重要だが躓く要素は少ない.PWM RateはMach3でも同様の設定項目はある.画像では最高回転数の時をPWM(=Duty比)1.0と設定している.

これで設定は終了で,あとは設定ファイルを保存するか聞かれるはず.設定をLinuxCNCに読ませて上手く動けば成功.

おわり

以上,是非とも設定で躓き,泥水を啜り,地べたから這い上がりつつも,なんとか動作確認をすませ,時間を浪費してほしい.なお,Mach3を2万円で購入するのであれば,以上の設定はすべて無駄である.

LinuxCNC(uspace ver) On CentOS6.8 の導入

(2016/10/25追記,CentOS 6.5として扱ってたけど,6.8でした.ごめんなさい.タイトル修正済み)

目的

古いマシン(Intel Atom 330 @ 1.60GHz, 2048GB RAM)を使って,NCフライスを制御したい.CentOS 6.56.8のほぼminimalな環境は整備されていたので,まずは,非リアルタイムなLinuxCNCが走るか確認した.

とりあえず,起動出来た感じ.

準備

  • LinuxCNCはウィンドウを出してくるので,とりあえずデスクトップ環境を入れておく.

vncserverも設定しておいた.

http://linuxcnc.org/docs/devel/html/code/building-linuxcnc.html
を見ながら作業を進めた.

configure編

最終的には下記コマンドでconfigure可能:
./configure --with-realtime=uspace --without-libusb-1.0 -enable-non-distributable=yes

ハマったこと:

  • usblib-1.0が入らない?usblibがあるけど認識していない.今回は必要ないので,--without-libusb-1.0を付加
  • libmodbusの導入.configureはパッケージの有無を確認しているので,rpm化する必要あり
  • tkimgの導入.Debian系ではパッケージあり,CentOSではナシ.Img-Sourceを落としてきてmakeする.--prefixが指定されていないので注意
  • -enable-non-ditributable=yesが必要?
  • Python2.7の導入.CentOS6.5の公式リポジトリだと2.6なので,2.7を導入する必要あり.(configure時に--enable-sharedを付けないと,make時のリンクで引っかかる.)(2.6と切り替えられるようにしないとyum等が動作しない.update-alternativesで対応可)
  • 他にも足りないの聞かれるが,公式リポジトリからyum installで対応可

make編

configureしたらmakeすればおk.

ハマったこと:

  • gcc(stdlibc++)4.9.4の導入.公式リポジトリは4.4.7なので,C++11系コードをコンパイルできない.(atomicを使ってる)(gccのmakeでmakeinfoないって言われる.texinfoパッケージ入れる→config.status内のmissing makeinfo→makeinfoに変更で解決?)
  • boostのバージョンが古い?BOOST_FORCEINLINEに対応してないので,1.6.2をいれる

実行編

linuxcnc-dev/script/linuxcncを実行.メガネペンギンが出てくれば成功

f:id:sosoru_m:20161024215649p:plain
↑latency-histogramの実行結果.右が1000usec周期でパルスを出す場合.真ん中がとんがってる(=遅延なしの場合が多い)ので頑張ってるけど,プロットの枠外に広がるほどバラツキがあるようだ.左は25usec.遅延バラバラで全く駄目.

補足(TCLLIBPATHの結合がおかしい?)

latency-histogramを実行すると,tclshがpackage require Img出来ないとぬかしてくる.
linuxcncは,tclshに渡す前にrip-environmentを実行して環境を整えてくれているのだけど,環境変数$TCLLIBPATHを再定義する際に,パス文字列の結合にスペースでは無くコロンを使っているので,tclshは正しくパッケージを読み込めなくなるみたい.

scripts/rip-environment:

if [ -z "$TCLLIBPATH" ]; then
    TCLLIBPATH=$EMC2_HOME/tcl
else
    TCLLIBPATH=$EMC2_HOME/tcl:$TCLLIBPATH

↓

if [ -z "$TCLLIBPATH" ]; then
    TCLLIBPATH=$EMC2_HOME/tcl
else
    TCLLIBPATH="$EMC2_HOME/tcl $TCLLIBPATH"

にすれば動いてくれる.

つづく

予想通り普通のカーネルでは役に立たないので,リアルタイムカーネルを入れましょう.つづく

Accessが複数開かれている時にGetObject()する方法

はじめに

  • ExcelAccessをはじめとしたOffice系のソフトは,オートメーションなる機構が準備されているので,各々連携することが可能である.
  • 具体的には,CreateObject()やGetObject()といった関数で,各ソフトのApplicationオブジェクトに相当する参照を取得でき,例えば,ExcelからAccessにクエリを投げて,得られた結果をワークシートに転送する,といったマクロを作成可能である.
  • いや,MSDN*2は最初の引数に使いたいファイルパスを指定すれば選択できそうな雰囲気ではあるが,試してみたところ,新しいインスタンスが開かれ,取得された参照が破棄されると同時にインスタンスも閉じられた.うまくやる方法があるのかもしれないが,ここでは操作するインスタンスを指定できないとしよう.
  • そこで,ウィンドウタイトルから開いているファイルを推測し,そのウィンドウのハンドルを基にしてAccessibleObjectFromWindow()関数を呼び出し,目的のインスタンスの参照をつかむ方法が,首尾良く行きそうなので報告する.

AccessibleObjectFromWindow()関数

MSDN*3を参照.

こいつでウィンドウハンドルから,インスタンスの参照を得られる.VBAでは引数の扱いに注意が必要な点がある.

  • 第2引数は取得したいオブジェクトのIDを渡す.Office系の参照が欲しい場合はOBJID_NATIVEOMを指定する.
  • 第3引数の REFIID riid はGUID渡すべきであるが,VBAのGUIDを指定すると怒られる.Declareな関数では使えないらしい?ので,同等の構造体をユーザー定義する.Office系の参照を得るためには,IDispatchに相当するGUIDを渡す.

EnumWindows()関数

MSDN*4を参照.

ウィンドウを列挙してくれる.第1引数にコールバック関数を渡す必要があるが,なんと,VBAはAddressOf演算子を使えば渡せるらしい.

GetWindowText()関数

MSDN*5を参照.

ウィンドウタイトルを得る.VBAでは固定長文字列を宣言する方法があって,宣言の後に「* 256」を付け足せば良いらしい.

Access複数開かれている場合に参照を取得する例

  • Access複数開かれている場合を例にした実装を張る.
  • Main()からEnumWindows()を呼び出し,目的のインスタンス("tesuto.accdb"なるファイルを開いたAccessインスタンス)が存在したら,フォーム"hoge"を開く.
  • EnumWindows()のコールバック関数では,ウィンドウタイトルを調べ,目的のファイルを開いていそうだったら,Accessインスタンスの参照の取得を試みる.
Option Explicit

Public AccessWndHandle As Long
Public AccessApplication As Object

Private Const OBJID_NATIVEOM = &HFFFFFFF0

Private Declare Function GetWindowText Lib "User32" _
Alias "GetWindowTextA" _
(ByVal Hwnd As Long, ByVal lpString As String, ByVal cch As Long) As Long

Private Declare Function AccessibleObjectFromWindow Lib "oleacc" _
(ByVal Hwnd As Long, ByVal dwId As Long, _
ByRef riid As UUID, ByRef ppvObject As Object) As Long
     
Private Declare Function EnumWindows Lib "User32" (ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long

Private Type UUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(7) As Byte
End Type

Public Function EnumWindowsCallBackFunc(ByVal Hwnd As Long, ByVal lParam As Long) As Boolean
    Dim title As String * 256

    If False = GetWindowText(Hwnd, title, Len(title)) Then
        GoTo Continue
    End If
    
    If InStr(title, "tesuto") > 0 And InStr(title, "Access") > 0 Then
        Dim IID_IDispatch As UUID
        With IID_IDispatch
            .Data1 = &H20400
            .Data4(0) = &HC0
            .Data4(7) = &H46
        End With
        
        If 0 <> AccessibleObjectFromWindow(Hwnd, OBJID_NATIVEOM, IID_IDispatch, AccessApplication) Then
            GoTo Continue
        End If
        
        AccessWndHandle = Hwnd
        EnumWindowsCallBackFunc = False
        Exit Function
    End If
    
Continue:
    EnumWindowsCallBackFunc = True
    Exit Function
End Function

Public Sub Main()
    AccessWndHandle = 0
    Call EnumWindows(AddressOf EnumWindowsCallBackFunc, 0)
    
    If AccessWndHandle = 0 Then
        Exit Sub
    End If
    
    AccessApplication.DoCmd.OpenForm "hoge"
End Sub

*1:インスタンスとは,現在コンピュータで動いているプログラム,と取って欲しい.例えば,タスクマネージャで「EXCEL.EXE」が複数存在するとき,複数インスタンスが存在すると表現する.

*2:GetObject 関数 (VBScript)

*3:AccessibleObjectFromWindow

*4:EnumWindows 関数

*5:GetWindowText 関数