インドア派の俺がバイクパッキングを試行錯誤し始めた記録
背景
500万オーバーのスポーツセダンから100分の1の5万円台のCannondaleのクロスバイクQuick7 にダウングレードしてしまったw
同時期にフロントディレーラーが壊れてしまった20年以上前に買ったGTのMTBを購入したクロスバイク のカスタマイズ しようとシマノのパーツを物色していたら、適合しそうなパーツを見つけて、修理できてしまって、2台体制に。
そのおかげで、MTB、クロスバイクの2台になり、家族でサイクリングする時は奥さんがクロスバイクで出かけるようになった。
夏場のソロ活動でのリュックフリーと家族でサイクリングに行く際に荷物運び役で活躍したい
ソロで早朝多摩川に行ったりする際は、クロスバイク。 モンベル のサイクールパック20を使っているが、夏場は背中の汗が気になりすぎるので、 大き目なサドルバッグを付けたいが、160cmと低身長でおまけに短足なので、700Cだとサドルがあまり上げられず、 スペースがきつい。
そんな厳しい制約でもドッペルギャンガーのサドルバッグはギリギリ使えた。
子供はよく転ぶので、バンドエイドと傷口の消毒液を入れるバッグ代わりに以下の救急セットを常備することにした。
山やキャンプに行く習慣がないので、バイクパッキングを機にスタッフバッグを購入
R250のツールボトルが良さげと聞き、ボトルケージに装備することに
飲み物はR250のステムバッグを導入することに
Cannondale quick7のバイクパッキング完成図
1994年モデルのGTのbackwoodsのバイクパッキング
GTの独特なフレームなのと、小柄なのでSサイズのフレームで、フレームバッグは絶望的。
今度もドッペルギャンガーが強い見方をしてくれた。
ちょっとペダリング時に荷物と干渉するけど、まぁ、家族でゆるポタなので、それほど気にしない。
ボトルケージの代わりにこちらもステムバッグを装着
R250のステムバッグに比べると安っぽいですが、ドリンクボトルサイズのスタッフバッグ付きでコスパは最高。
MTBのバイクパッキング
関連記事
node-ffi-napiでPythonを動かして、PyObjC経由でCocoa APIを叩いて結果を別スレッドでHTTPで受け取った
@kjunichiです。この記事はQiita Node.js Advent Calendar 2019 6日の記事です。
背景
たまーに、流行のGitHub見て転職案内してくれるサイトに登録してるので、 KeyCast.js見ました!なんてメールをいただくことがありますが。 AIからのメールなのはバレバレでもメンタル豆腐なので、この数年メンテしておらず心苦しくになることがあるので、 重い腰を上げて対応しようとしたら、NodObjCが不安定すぎて、別の方法を模索している。
ってのと、Keycast.jsが以前のアドカレがきっかけで作成したので、新ネタ 思いつかなかったというのもある。
Node.jsで直接Cocoaを扱えなくなった?!
node-ffiはここ数年更新がされていない状態が続いている。
NAPIに対応した node-ffi-napiが作られている。これはNode.jsにスレッドを入れた方で、Node学園祭でもスピーカで 来日された @addaleaxさんがメンテしている。
しかし、こちらのNAPI版のffi-napiではNodObjCをサポートしないっぽい。以下のISSUE参照。
ということで、今回は、Cocoa APIをPythonのライブラリであるPyObjCから叩くことにした。 PyObjCは開発が活発なようで今年もアップデートされているのを観測済み。
HomebrewなPythonへの対応
dylibではなく、.framework形式で共有ライブラリがインストールされるので、node-ffi-napiで通常の処理ではアクセスできない。
node-ffi-napiのコードを眺めて以下のようにすれば解決した。
const handle = ffi.DynamicLibrary('/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/Python') const libpython={} libpython.Py_SetProgramName = ffi.ForeignFunction( handle.get('Py_SetProgramName'), 'void', [voidPtr] )
ffi.DynamicLibraryにてFrameworkの中身?を指定して、そのハンドルを取得して、 取得したハンドルに対してgetメソッドで個々のframework内のシンボル名を指定することでCで言うところの関数ポインタが 取得できて、これを使って、ffi.ForeiginFuncionでJSでの関数を取り出す事ができる。
Pythonからの結果を受け取るのがめんどくさい問題
Node.jsからnode-ffi-napiを経由してPythonを経由してPyObjCでMacOS側からキーイベントを受け取ることはできるが、 これを受取には、Node.js側で、node-ffi-napiを経由してコールバック関数をPython側に渡す必要が出てくる。
これ、できなくはないが、めんどくさい。コールバック関数の型定義が複雑(個人の感想です)
ところで、PythonもNode.jsもhttp通信が追加モジュールなしで行える!
これだ! Python側で取得したキーイベントをJSONでNode.js側にPOSTすれば解決だ。
HTTPクライアントとHTTPサーバーを一つのプログラムで動かすには
シングルプロセスで、シングルスレッドではNode.jsからPythonを呼び出して、Python側でHTTPを呼び出しても、 Node.js側が受信待ち状態になってないので、アウト!という落とし穴が待ち受けていたw
しかし、この問題の対策として、Node.js側でスレッドを追加してマルチスレッドにすることで対応できる!
// HTTPサーバーを別スレッドで動かす worker = new Worker(path.join(__dirname, "../httpserver.js"), { workerData: 0 }) worker.on('message', (msg) => { //console.log(`${msg}`) event.reply('nsevent', msg) }) // Pythonを呼び出す const voidPtr = ref.refType(ref.types.void); const handle = ffi.DynamicLibrary('/usr/local/Cellar/python/3.7.5/Frameworks/Python.framework/Versions/3.7/Python') libpython.Py_SetProgramName = ffi.ForeignFunction( handle.get('Py_SetProgramName'), 'void', [voidPtr] ) libpython.Py_Initialize = ffi.ForeignFunction( handle.get('Py_Initialize'), 'void', [] ) libpython.PyRun_SimpleString = ffi.ForeignFunction( handle.get('PyRun_SimpleString'), 'int', ['string'] ) libpython.Py_Finalize = ffi.ForeignFunction( handle.get('Py_Finalize'), 'void', [] ) libpython.Py_SetProgramName(null) libpython.Py_Initialize() const pyscript = fs.readFileSync(path.join(__dirname, 'api.py')) libpython.PyRun_SimpleString(pyscript)
httpserver.js
// server.js // worker thread const { parentPort } = require('worker_threads') const http = require('http') const server = http.createServer() server.on('request', (req, res) => { if (req.method === 'POST') { var data = '' //POSTデータを受けとる req.on('data', (chunk) => { data += chunk }) .on('end', () => { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.write('hello world') res.end() parentPort.postMessage(data) }) } //console.dir(req) }) server.listen(3000) server.on('listening', () => { // ここで random port が取れる const port = server.address().port console.log(`port = ${port}`) parentPort.postMessage(`port = ${port}`) }) parentPort.on('message', (msg) => { process.exit() })
Cocoa APIの罠
前述の一連の方法を素のnode.jsで動かそうとするとCocoa APIの呼び出しで
(スクショ撮り忘れてるので、後日貼ります。。)
というようにmacOSからダメだしされる。
electronで動かすことで対処できる。おそらくmacOSのアプリとして必要なAPIの呼び出しをelectronが済ませてくれるからと 思われる。
また、コマンドラインから動かす場合、使っているターミナルソフト(標準ならTerminal.app)にコントロールパネルの セキュリティ設定でアクセス許可を与える必要がある。
electronをツールなりでパッケージ化して.appにして動かす場合は、.appを一度起動して、同様にコントロールパネルの セキュリティ設定でアクセス許可を与える必要がある。
electronをパッケージ化して発覚した問題
どうもworkerを作る際にasar形式の中のファイルにアクセスできないっぽい。
対応策としては、.appの中のResourceにworkerで読み込む.jsファイルを格納して物理的に独立したファイルとして 用意。
const builder = require('electron-builder') const Platform = builder.Platform const fs = require('fs') builder.build({ targets: Platform.MAC.createTarget(), config: { 'appId': 'com.hatenablog.abrakatabura.keycastjs', 'target': 'dir' } }).then(()=>{ fs.copyFileSync('./httpserver.js','dist/mac/keycastjsdev.app/Contents/Resources/httpserver.js') }).catch((e) =>{ console.log(e) })
実行結果
ごちゃごちゃ書いてもコードが動かないと説得力ないので、動画を上げました。
成果物
ごめんなさい、Keycast.jsにこれらの成果を反映させた版は担当の日までに間に合いませんでした。。 出来れば、年内に、Githubに上げたいです。
実験用リポジトリを晒しておきます。
git clone cd keycastjsdev npm install node build.js open dist/mac/keycastjsdev.app
まとめ
- Node.jsでマルチスレッドを使った。
- プログラム内でHTTP通信することで、割と疎結合でPythonとNode.jsをつなぐことが出来た。
- Node.jsの12系でnpm installしたネイティブモジュールはElectron v7系でもそのまま動くことが分かった。
- Electronをアプリ化してスレッドを使う際は.jsファイルをasarの外に置く必要がありそう。
- PythonでのJSONをPOSTする方法が学べた。
- Node.jsでのPOSTされたJSONを受取る方法が学べた。
関連記事
- MavericksでNodObjCを試すには
- それでもNode.jsをMacアプリ化するをやる
- Cocoa APIのAXIsProcessTrusterdとシステム環境設定のセキュリティとプライバシーの関係
- NAPI版のnode-ffi-napiを使ってNode.jsからPythonを動かす
9年前の記事
5年前の記事
3年前の記事
2年前の記事
1年前の記事
日本初のAmazonのブラックフライデーで買った品々
こんばんは。先日、成田空港側のホテルに秋の家族旅行に行き、空港には行かずに、ホテルのプールで遊んだり、博物館や美術館に行って来た@kjunichiです。
買ったもの
IT関係
3、4年前のCheeroのモバイルバッテリーではiPhone Xsの充電が上手く出来なくて、なかなか買えなかった。 セールではよく見かけるブランドで、PSE取得との記述があり、試して見ることに。
USB-Cの端子があったので、2019年モデルの15インチMacBook Proを充電できるか試したが、ダメだったw。 と思いきや!
USB-C端子は、実は充電用でした。。 通常のUSBからUSB-Cに変換して、それをMacBookに繋いだら普通に充電できました!
iPhone Xsは問題なく充電できた。
奥さんがもらった激安アクションカメラがファイルの削除がひどくめんどくさく、なぜかMacBook Proにつなげると リードオンリーになってしまうので、めんどくさいから空いてるSDカードを購入。
防災関連
備えあれば憂いなしという事で、こういったセールでチマチマ揃えようかと。
キッチン関連
組み立て中は、奥さんにディスられまくったが、完成後、台所に設置したら、高評価w。
置き配されると、重くて嫌だったので、1つだけ注文。無事ヤマトの配達員の方が玄関まで届けてくれました。
関連記事
- コロナ禍で念願の在宅勤務の夢がかなった際に購入して役立ったモノたち
- 2018年アマゾンのサイバーマンデーで買ったもの
- 2018年アマゾンのサイバーマンデーで買ったもの その2
- 2023年Amazonのブラックフライデーで買ったもの