Visual Studio 2017 + Xamarinでクロスプラットフォーム開発(1)
背景
クロスプラットフォームアプリを作りたくて環境構築にひたすら手間取った話。
環境:
・windows10 Pro
・Visual Studio 2017
・Xamarin 4.4
・mac Sierra 10.12.4
・Xcode 8.3.2
・Xamarin Studio 6.3(build 863) <-成功
・Visual Studio for Mac <-失敗
Windowsメインで、macでエミュレーターを起動させました。
発生した問題
①Visual StudioのXamarinクロスプラットフォーム アプリテンプレートのバグ
mac OSおよびXcodeを最新にし、Visual Studio for Macをインストール。
Visual Studio for Macにてテストアプリがビルド&iOSエミュが立ち上がることを確認し、今度はWindows側でテストアプリにてビルドできるか確認。
しかしビルド時にいくつかの画像ファイルが読み込めないトラブルが発生した。
プロジェクトディレクトリ内のアプリ名.iOS/Resources 内にあるテンプレートからコピーされるはずの画像類、例えば「Default-568h@2x.png」など。
エラーメッセージから該当パスを確認するとブランク(真っ黒)になった画像ファイルが見つかった。
理由は不明だが、テンプレートからコピーする際にバイナリが壊れる模様。何度か試しても同様の結果になった。ちなみにテンプレートの大本の画像は無事だった。
仕方なくテンプレートをXamarin関係の別のものを使うことにし、人気順で一番上にきた「Prism Template Pack」を使用。
Prism Template Pack - Visual Studio Marketplace
インストール後に新規追加されたテンプレートを試すと上記問題は解消した。
Xamarinのテンプレートは試験運用中ということで覚悟はしていたがこんな問題が起こるとは想定外だった。
ちなみにAutofacとかDrylocとかなにそれ怖い知らない。これも覚えた方がいいのかなぁ
②iOS.exe conflicts with another assembly
続いてビルドしたところ、アプリ名+iOS.exeが競合しているというエラーが出現。
エラーメッセージで調べてもごく最近起こっている問題のようで、明確な解決策を見つけることができなかった。
もちろんMac側のビルドキャッシュを削除したり、該当ディレクトリに異常はないかはチェックした。
途方にくれていたがふとVisual Studio for Macはプレビュー版だったことを思い出し、いったんアンインストール。
Xamarin Studioを入れた。
インストール後すぐにXamarin Studioの更新通知が。Xamarinで使うSDKを更新するぜ!という内容だったが、これが効いたのか無事Visual Studio 2017側からビルド成功、エミュレータが立ち上がることを確認した。
上記問題等により1日潰れてしまったが、エミュレータが立ち上がった時は震えた。
(そもそもMacで開発すればいいのでは?と思われるかもしれないが、自分は生粋のwindowerであるため却下)
次はIsolatedStorageあたりを使ってアプリ領域内にストレージを確保する処理を作りたい。
ちなみに自分はWPFアプリは開発経験があるが、iOS、Androidアプリはさっぱりなので大変苦労すると思う。
以上
Twitter APIで連打でリクエスト投げたときの話
背景
環境:
・wordpress
・x-server
・PHP5.6.22くらい
・ライブラリ TwistOAuth(非phar)
Twitter APIにてsearchリクエストを連打で投げる必要がある案件にあたったけど、リクエスト上限(180req/15min)にすぐ引っかかるような要求仕様だったので苦労した話
TwistOAuth
すげーいい。これがあればすぐプログラム組める。使いやすい。
でもリクエスト上限に達した場合に例外吐くだけなのでリスポーン処理が組みづらい。
対策
TwistOAuth内のTwistOAuth.phpでgetリクエストする際のcurlのオプションと、関数decodeを修正した。
public function get
public function get($url, $params = array(), $proxy = '') {
$ch = $this->curlGet($url, $params, $proxy);
curl_setopt($ch, CURLOPT_HEADER, true);//--追記
$response = curl_exec($ch);
return self::decode($ch, $response);
}
private static function decode
private static function decode($ch, $response) { self::checkCurlError($ch); $info = curl_getinfo($ch); $header = substr ($response, 0, $info["header_size"]);//--追記 $body = substr ($response, $info["header_size"]);//--追記 $response = $body;//--追記
||途中省略||
$limitRemain = -1;//残りリクエスト可能回数 $limitReset = -1;//リクエスト回数復活時間unixtime $headerArr = explode("\n", $header); $headerArr = array_map('trim', $headerArr); foreach($headerArr as $headerRow){ $headerRowData = explode(":", $headerRow); if(is_array($headerRowData) && count($headerRowData)==2){ if($headerRowData[0]=="x-rate-limit-remaining"){ //echo "x-rate-limit-remaining --- ".$headerRowData[1]."<BR>"; if(is_numeric($headerRowData[1])){ $limitRemain = intval($headerRowData[1],10); } }else if($headerRowData[0]=="x-rate-limit-reset"){ //echo "x-rate-limit-reset --- ".$headerRowData[1]."<BR>"; if(is_numeric($headerRowData[1])){ $limitReset = intval($headerRowData[1],10); } } } } if($limitRemain!=-1 && $limitRemain===0 && $limitReset!=-1){ $nowTime = time(); $waittime = $limitReset-time()+60; if($waittime<=0){$waittime=960;} update_option('waittime', $waittime); }else{ update_option('waittime', 0); } return $obj;
function getにレスポンスヘッダを返すためにCURLOPT_HEADERをtrueに。
function decodeの頭でヘッダとレスポンスbodyを分離。
function decodeで正常パターンだった場合のreturn $obj前に残りリクエスト回数解析処理を追加。
意図としては、
Twitter APIでは現在のリクエスト可能回数を取得するエンドポイントがあるが、いちいち確認してたら無駄にリソースを食ってしまう。
そこで、get時に返ってくるレスポンスヘッダを見るようにし、レスポンスヘッダ内の値
・x-rate-limit-remaining (残りリクエスト可能回数)
・x-rate-limit-reset (リクエスト可能回数リセット時間:unixtime)
を毎回確認するようにした。
$waittime = $limitReset-time()+60;
にてリセットされる時刻-現在時刻にて待ち時間を作成。+60は気分で入れた。
環境はwordpressなので、サイトオプションに"waittime"を作成。残りリクエスト可能回数が0以下だった場合に
update_option('waittime', $waittime)
にて次回復活時間をオプションに登録、プログラム上位にてこの値を確認するようにした。
上位でこの"waittime"を都度確認して、0以外だったら
wp_clear_scheduled_hook( 'イベントフック名' ); wp_schedule_event( time()+$waittime, '時間設定名', 'イベントフック名' ); update_option('waittime', 0);
とかやればいい感じにリクエスト回数復活まで待ってくれるはず。
コードが雑なのは勘弁してください。
以上
(はてぶコード内で<をエスケープするにはどうすればええんや...)
XamarinとAndroid Studioの共存
背景
環境:
・Windows 10
・Visual Studio Community 2017
Xamarinでアプリを作ろうとしてインストールしたけどベースにしたいアプリがAndroid Studioで組まれたものだったので、とりあえずXamarin入れた後にAndroid Studio入れて、SDKはXamarinで入れたものを使おうとしたらパスにスペースが入ってたらあかんで!って言われてどうにかSDKを共存させようとした話。
SDKサイズ大きいのでね、節約したいですよね。
Xamarin側のSDKパスを変更
ツール->オプション->Xamarin->Androidの設定
よりパスをAndroid Studioで入れたものに変更
デフォルトだと C:\Users\xxxx\AppData\Local\Android\sdk
SDK ManagerのGUIが上がらない
上記パスに変更したらGUIが立ち上がらなくなった。
いろいろ調べても解決しなかったので途方に暮れていたらSDKのディレクトリ内のSDK Readme.txtに書いてあった。
解決
To start the SDK Manager, please execute the program "SDK Manager.exe". From the command-line you can also directly trigger an update by executing: tools\android.bat update sdk --no-ui
…つまりあれか、Android Studioで入れたやつは勝手に"--no-ui"オプションをつけて立ち上げているのでは?と思い試しにXamarinで入れたSDKディレクトリ内のandroid.batをAndroid Studioで入れたSDK内に上書きした。
(.batの差分とったけどそれっぽい違いがあった。Android Studioから起動するSDK Managerは専用のGUIっぽいしね。)
…
…
立ち上がったやんけ!
SDKはAndroid Studioですでに入れたものがちゃんと出ている。
あーでも、Android StudioでSDK Manager上げたらおかしくなるんじゃないの?と思って立ち上げてみたけどぱっと見問題なし。
とりあえずこれで無駄にディスク容量食わずに開発できそうです。
ただNDKはXamarinとAndroid Studioで要求バージョン違うので、とりあえず別個に入れてます。(あとで統一するかも)
注意点として
・XamarinでインストールしたSDK Managerは直接削除せずに「プログラムと機能」から削除すべき
・Visual StudioのインストーラーにてXamarinが入った状態で「変更」をクリックすると勝手にSDKが復活するので押さないこと(なんだこのクソ仕様)
(追記)
・Android SDKディレクトリ内のadb.exeがVisual Studioを終了しても落ちない。VS落とした後に再度Androidをビルドしようとするとうまくいかなくなるかもしれないので都度落とした方がいいかも
以上
wordpress rss feed 設定メモ
今までrssフィードをページに表示する際にはページのテンプレートに表示処理を書いて、
キャッシュの更新間隔等の設定は「wp-includes\feed.php」内のset_cache_durationを直接変更していた。
これではwordpressを更新した際に変更した設定がデフォルトに戻されるので、
今までは顧客納品時にwordpressの更新はしないでくださいなどと言っていた。
しかし普通に解決方法があったので懺悔がてらメモに記載。
今までやっていた記載。
テンプレートファイル
//WrodPressのfeed.phpの呼び出し include_once ABSPATH . WPINC . '/feed.php'; // 目的のFeedを取得 $feed = fetch_feed('rssのurl'); //以下 $feed内のメンバ関数にて表示処理
wp-includes\feed.php
function fetch_feed( $url ) { ・ ・ ・ //$feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifetime', 12 * HOUR_IN_SECONDS, $url ) ); $feed->set_cache_duration( apply_filters( 'wp_feed_cache_transient_lifetime', 30 * MINUTE_IN_SECONDS, $url ) ); //更新間隔を30分ごとに変更 }
解決方法
テンプレートファイル
//WrodPressのfeed.phpの呼び出し include_once ABSPATH . WPINC . '/feed.php'; // 目的のFeedを取得 $feed = fetch_feed('rssのurl'); $feed ->set_cache_duration(1800); $feed ->init(); //以下 $feed内のメンバ関数にて表示処理
上記のように普通にインスタンスからset_cache_duration設定できたっぽい。知らなかった。
今まですごい頭悪いことしてたんだな。。。お客様今まで大変申し訳ございませんでした。
以上
Puredata(Pd-extended) + Raspberry Pi (1)
Raspberry Piにpuredata(pd-extended)をインストールします。
Raspbian Jessieでは一工夫必要なので参考になればと
H/W、S/W構成
・Raspberry Pi 2 Model B
OS:Raspbian Jessie
準備
まずはリポジトリ取得先編集
$ sudo vi /etc/apt/sources.list
deb-src http://archive.raspbian.org/raspbian/ jessie main contrib non-free rpi
上記をコメントインして更新
$ sudo apt-get update
先に必要になるパッケージを入れておきます。
$ sudo apt-get install ttf-dejavu-core libfftw3-3 libftgl2 libgsl0ldbl liblua5.1-0 libquicktime2
インストール
そして次はPd-extendedの取得です。
とりあえずゴミ掃除が面倒なのでtmpディレクトリに移動してからパッケージを取ってきます。そしてインストール。
$ cd /tmp/ $ wget https://puredata.info/downloads/pd-extended-0-43-3-on-raspberry-pi-raspbian-wheezy-armhf/releases/1.0/Pd-0.43.3-extended-20121004.deb $ sudo dpkg -i Pd-0.43.3-extended-20121004.deb
インストールできたらあとはパーミッションを変更しておきます。
$ sudo chmod 4755 /usr/bin/pd-extended
そして、Raspbian JessieではAlsa関連の設定ファイルの場所がいろいろ変っているのですが、どうもPd-extendedは以前のAlsaの設定ファイルを読みに行っているような感触なのでAlsaの設定変更が効かなかったりと面倒なことになります。
ですので、新しいAlsa設定ファイルを旧設定ファイルの位置にコピーしておきます。
$ sudo cp /usr/share/alsa/alsa.conf /etc/modprobe.d/alsa-base.conf
/usr/share/内のファイルが新しいAlsaのファイル類で、
これを/etc/modprobe.d/にコピーしておきます。
/etc/modprobe.d/が昔のAlsa設定ファイルのあった場所です。
名前も昔のものに変えておきます。
GUI環境でターミナルよりpd-extendedと叩けば立ち上がります。
Raspberry Piのデフォルト環境ではPdのウインドウに「Alsa input error」なる表示が出ておりびっくりするかと思いますが、そもそもデフォルトだと音声入力機器がないので仕方がないエラーです。
気になる場合はとりあえずPdのオーディオ設定から入力デバイスのチェックを外すとエラーが出なくなると思います。
iPhone5 + Bluetooth + Raspberry Pi
前回Raspberry Pi(以下RPi)にBluetoothドングルを装着して周囲のデバイスをスキャンするところまで行いました。
Raspberry Pi + Bluetooth - もっふもふの電子回路
今回はiPhoneとのコネクション確立を行います。
H/W、S/W構成
・Raspberry Pi 2 Model B
OS:Raspbian Jessie
・Bluetooth ドングル Princeton PTM-UBT7
・iPhone 5 iOS9.2
結論
先に結論から言いますと、コネクション(ペアリング)は確立できました。
しかし、実現させたかった動作は、
iPhoneからRPiを検索、検出、ペアリングの動作であり、それはまだ未達成です。
今回はRPiからiPhoneに対しペアリングを行う方法を記載します。
前回の残課題
前回RPiにて
sudo hcitool lescan
BLEデバイスの検出を試みましたが何も見つかりませんでした。
iPhoneくらいは引っかかるだろうと思っていたので腑に落ちなかったため少しそれについて調べました。
海外サイトを巡ってみるとBeacon Toolkitというアプリを使ってiPhoneをiBeaconとして設定してあげると検出できるとのこと。
raspberry pi - Detecting proximity of an iPhone 5s from a RaspberryPi - Stack Overflow
Beacon Toolkitを App Store で
意味がわかりませんがものは試しということでさっそくやってみました。
Beacon Toolskitにて適当なビーコンを設定。
RPiにてlescan。
あっさり見つかる。
???
つまりlescanはビーコンを探すコマンドなの??
いや、そもそもBLEってのがモードの一種ってこと?
lescanってのはBLEのみを探すの??
Beacon ToolkitでiPhoneをBLEモードで実行させたから検出できたってこと?
混乱してきました
これちょっと後で調べる
iPhoneとのペアリング
ペアリングには最初blueman(GUIで簡単にBluetoothの設定ができるパッケージ)を使って行おうとしましたが、
iPhone側に一瞬ダイアログが表示されてすぐに切断されるという現象が発生し、失敗に終わりました。
grepでエラーを見るとDBusFailedError: No such file or directory
などと言っておりbluemanのヘルプサイトにて
DBus エラー
おそらく DBus を使って bluetooth デーモンと対話するために必要なグループにユーザーが属していません。このグループは /etc/dbus-1/system.d/bluetooth.conf で定義されています:
$ cat /etc/dbus-1/system.d/bluetooth.conf | grep "group="
グループが lp の場合、ユーザーアカウントにグループを追加してください:
# gpasswd -a [user] lp
と言っていたので対応するも変わらず。
諦めてbluezとセットでインストールされたbluetoothctlを使いました。
(bluetoothctlは対話式のツールなので、めんどくさいと思って最初使いませんでした)
最初コマンドから
[bluetooth]# scan on
にてデバイスを検索し、見つかったiPhoneに対し接続を試みましたが、
bluemanと同様iPhone側に一瞬ダイアログが表示されてすぐに切断されるという現象が発生。
Bluetooth ヘッドセット - ArchWiki
ペアリングは成功するが、接続が失敗する
bluetoothctl で以下のエラーが表示される場合:
[bluetooth]# connect 00:1D:43:6D:03:26
Attempting to connect to 00:1D:43:6D:03:26
Failed to connect: org.bluez.Error.Failed
Grepでエラーを見ると
a2dp-source profile connect failed
とか言っているので、なんかよくわからないプロファイルで繋ごうとしてるのかと思いましたが、
Intel EdisonでBluetoothを試してみる | Workpiles
こちらのサイトを見て、agentをPIN番号要求ではなくDisplayYesNoに設定してみるとあっさり繋がる。
なぜ???
[bluetooth]# agent DisplayYesNo [bluetooth]# pair <dev> [bluetooth]# connect <dev>
bluetoothctlにてagent DisplayYesNoを行うことが肝だったみたい。
意味がわからないのでこれも後で調べよう
bluemanでもこのあたりの設定を変えれば繋がったんだろうか。。。
(そんなのネットに書いてなかったし、自分の環境依存問題?)
そしてiPhoneからのBT検索ではまったくRPiが見つからないので、
(Windows、Windows Phoneからは見つかる)これも課題だなぁ

プリンストン Bluetooth USBアダプター (通信距離 25m:Ver4.0接続、10m:Ver3.0 Class2接続) PTM-UBT7
- 出版社/メーカー: プリンストン
- 発売日: 2014/10/06
- メディア: Personal Computers
- この商品を含むブログ (1件) を見る
Raspberry Pi + Bluetooth
とある目的のためにRapberry PiにBluetooth ドングルを差してiPhone等とコミュニケーションさせたい。
そのために行ったことをつらつらと。
H/W、S/W構成
・Raspberry Pi 2 Model B
OS:Raspbian Jessie
Varsion:Linux raspberrypi 4.1.13-v7+ #826 SMP PREEMPT Fri Nov 13 20:19:03 GMT 2015 armv7l GNU/Linux
・Bluetooth ドングル Princeton PTM-UBT7
過去に別用途で購入(たしか安かったから)したものを引用。一応Bluetooth Low Energy(BLE)対応っぽいけど苦労したくないならPlanex辺りを購入した方が無難か。
BlueZ のインストール
まず現在bluezがインストールされていないことを確認
dpkg -l | grep blue
BluezはLinuxで一般的に使われているBluetoothスタックらしいです。apt-getでインストールできるものは古そうだったので、ビルドすることにしました。
まずいろいろ作業するためにDocuments内に作業ディレクトリを作成
cd Documents/
mkdir -p work/bluetooth
記事執筆時点(2016/01/02)での最新バージョンはどうやら5.37の模様。
https://www.kernel.org/pub/linux/bluetooth/
ここで最新のものを選択して取ってくる。
$ wget http://www.kernel.org/pub/linux/bluetooth/bluez-5.37.tar.xz
取ってきたら解凍して
$ tar xvf bluez-5.37.tar
解凍したソースディレクトリ内に入り、コンフィグを設定
$ cd bluez-5.37/
$ ./configure --disable-systemd --enable-library
依存関係とかいろいろ確認されるが、自分の環境では以下のエラーが発生
checking for linux/if_alg.h... yes
checking for GLIB... no
configure: error: GLib >= 2.28 is required
GLibが存在しないらしいので、関連するものをまとめて入れ込み(そもそも先に入れておくものらしい)
$ sudo apt-get install libdbus-1-dev libdbus-glib-1-dev libglib2.0-dev libical-dev libreadline-dev libudev-dev libusb-dev make
ごちゃっと
再度コンフィグを設定すると、
$ ./configure --disable-systemd --enable-library
・
・
checking for GLIB... yes
・
・
checking that generated files are newer than configure... done
今度はうまくいったっぽい。次はビルドしてインストール。
makeで10~15分ほどかかります。
$ make
$ sudo make install
続けてmakeでインストールされないものを手動でインストール
$ sudo cp attrib/gatttool /usr/local/bin/
libbluetooth-dev の各種ヘッダファイルをビルドしたもので置き換え
$ sudo cp -ipr lib/ /usr/include/bluetooth.5.37
$ cd /usr/include
BTドングルの状況をチェック
$ lsusb
Bus 001 Device 005: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode)
Bus 001 Device 004: ID 0d8c:0014 C-Media Electronics, Inc.
Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp. SMSC9512/9514 Fast Ethernet Adapter
Bus 001 Device 002: ID 0424:9514 Standard Microsystems Corp.
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Device 005にBTドングルさんがいますね。
ドングル詳細情報はID指定で
$ sudo lsusb -v -d 0a12:
状態は
$ hciconfig
hci0: Type: BR/EDR Bus: USB
BD Address: 00:1B:DC:XX:XX:XX ACL MTU: 310:10 SCO MTU: 64:8
DOWN
RX bytes:580 acl:0 sco:0 events:31 errors:0
TX bytes:368 acl:0 sco:0 commands:30 errors:0
DOWNなので認識してるけど寝ている状態ですかね。
(MACアドレスはマスクしてます)
BTドングルの起動
$ sudo hciconfig hci0 up
$ hciconfig
hci0: Type: BR/EDR Bus: USB
BD Address: 00:1B:DC:XX:XX:XX ACL MTU: 310:10 SCO MTU: 64:8
UP RUNNING
RX bytes:1154 acl:0 sco:0 events:61 errors:0
TX bytes:736 acl:0 sco:0 commands:60 errors:0
UP RUNNINGになったので起動できたと思われます。
Bluetooth デバイスのスキャン
$ sudo hcitool lescan
LE Scan ...
本来ここで手持ちのBLEデバイスたちが表示されるはずが、自分は特に所有していなかったため何も表示されず(ちゃんと動いてんのかこれ、、、)
$ hcitool scan
では一応手持ちのiPhone5等が表示されました。
(あれっiPhone5ってBLE対応してないんだっけ?)
また、デバイスからRPiを発見できるように
sudo hciconfig hci0 piscan
にて設定。
参考にさせていただいたサイト
kakakikikekeのブログ: RaspberryPi に BlueZ をインストールして BLE を検知してみた
次はPCやiPhone等別デバイスとのコミュニケーションを行う予定です。

プリンストン Bluetooth USBアダプター (通信距離 25m:Ver4.0接続、10m:Ver3.0 Class2接続) PTM-UBT7
- 出版社/メーカー: プリンストン
- 発売日: 2014/10/06
- メディア: Personal Computers
- この商品を含むブログ (1件) を見る