Asteriskのログ(cdr)をmysqlに格納したい

着信履歴を確認したいという要望があり、参照画面をPHPで作るために、発着呼のログをmysqlに格納する必要があった。

ほぼ、このサイト通りでいけた。

http://www.northwind.mydns.jp/samples/blog/2015/02/05/asterisk-cdr%E5%91%BC%E6%83%85%E5%A0%B1%E3%82%92odbc%E3%81%A7mysql%E3%81%B8%E6%A0%BC%E7%B4%8D/

Mysql/Asteriskはインストール済みの前提。

保存用のテーブルをmysqlに作る。

CREATE TABLE `cdr` (
`calldate` datetime NOT NULL default ‘0000-00-00 00:00:00′,
`clid` varchar(80) NOT NULL default ”,
`src` varchar(80) NOT NULL default ”,
`dst` varchar(80) NOT NULL default ”,
`dcontext` varchar(80) NOT NULL default ”,
`channel` varchar(80) NOT NULL default ”,
`dstchannel` varchar(80) NOT NULL default ”,
`lastapp` varchar(80) NOT NULL default ”,
`lastdata` varchar(80) NOT NULL default ”,
`duration` int(11) NOT NULL default ‘0’,
`billsec` int(11) NOT NULL default ‘0’,
`disposition` varchar(45) NOT NULL default ”,
`amaflags` int(11) NOT NULL default ‘0’,
`accountcode` varchar(20) NOT NULL default ”,
`userfield` varchar(255) NOT NULL default ”,
`uniqueid` VARCHAR(32) NOT NULL default ”,
`linkedid` VARCHAR(32) NOT NULL default ”,
`sequence` VARCHAR(32) NOT NULL default ”,
`peeraccount` VARCHAR(32) NOT NULL default ”
);

ALTER TABLE `cdr` ADD INDEX ( `calldate` );
ALTER TABLE `cdr` ADD INDEX ( `dst` );
ALTER TABLE `cdr` ADD INDEX ( `accountcode` );

mysql直接接続は非推奨らしいので、ODBC接続にします。
必要なライブラリのインストール。
$ yum install unixODBC unixODBC-devel libtool-ltdl libtool-ltdl-devel
$ yum install mysql-connector-odbc
自分の環境では、関連のモジュールはインストール済みだった。

$ odbcinst -q -d
[PostgreSQL]
[MySQL]

MySQLがでればOK。

asterisk用のODBC設定を追加。

$ vi /etc/odbc.ini
[asterisk-connector]
Description = MySQL connection to ‘asterisk’ database
Driver = MySQL
Database = asterisk
Server = localhost
UserName = asterisk
Password = asterpass
Port = 3306
Socket = /var/lib/mysql/mysql.sock

asteriskにODBC接続の設定を追加

$ vi /etc/asterisk/res_odbc.conf
[asterisk]
enabled => yes
dsn => asterisk-connector
username => asterisk
password => asterpass
pooling => no
limit => 1
pre-connect => yes

モジュールをロードするように設定。

$ vi /etc/asterisk/modules
preload => res_odbc.so
preload => res_config_odbc.so

res_odbc.soが無い場合は、ビルド時にodbcを追加していなかったということなので、ビルドし直す。
# ./configure
# make menuselect
# make install

ドライバがロードされたか確認。

$ asterisk -rvvvv
localhost*CLI> odbc show

ODBC DSN Settings
—————–

Name: asterisk
DSN: asterisk-connector
Last connection attempt: 1970-01-01 09:00:00
Pooled: No
Connected: Yes

asteriskでcdrをODBCで出力するという設定を追加

$ vi /etc/asterisk/cdr_adaptive_odbc.conf
[adaptive_connection]
connection=asterisk
table=cdr
alias start => calldate

再起動。

$ service asterisk restart

なんかうまくいかないときは、以下の出力を確認してみる。
$ asterisk -rvvv
$ module unload res_odbc.so
$ module load res_odbc.so

iOSアプリのアップデート

agreementの承認とかを放置していたら、ストアから削除された自作アプリ。
重い腰を上げて、アップデートを試みました。

ビルドが通るまでの軌跡はこちら。
http://blog.as-toybox.com/archives/647

ようやく新しいバージョンを審査へ提出します。

まず、itunes Connetで新しいバージョンを作成します。

itunes_connect

itunes_connect

その後、連絡先情報などを更新して、提出準備完了です。

その前に実機確認しなきゃと思ったら、またエラーです。

Could not locate device support files.
This iPhone 6 Plus is running 10.1(14B72), which may not be supported by this version of Xcode.

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_18_48

Xcodeを最新版にアップデートする必要があるようです。

アップデートした再度起動したら、次はこれ。

Development cannot be enabled while your device is locked.
Please unlock your device and reattach.

flagraisingappdelegate_m

iPhoneを一旦外す

設定->一般->リセット->位置情報とプライバシーをリセット

つなぎ直す

信頼

今度は、これ、、、

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_19_00

よくみたら何か処理をしている。待たないと駄目か。

flagraisingappdelegate_m

デバイスをdeveloper portalに登録しないといけないよ、と言われたので、Regsiter Device

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_19_05

はい、つぎ。

App installation faild
This application’s application-identifier entitilement does not match that of the installed application.
These values must match for an upgrade to be allowed.

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_19_07

iPhoneに古いバージョンが入っていたからですね。
実機から消して、再実行すればOK。

実機の動作確認OK!

Xcodeからアプリをアップします。

メニュー → Product → Scheme → Edit Scheme

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_19_12

Build Configration を Release に変更

OKボタンを押す。

XcodeでDeviceを「Generic iOS Device」に設定

flagraisingappdelegate_m

メニュー → product → Archive

ビルドが完了すると Organizer が自動で開き Archive したアプリが表示される

organizer

Validate をクリック

使用するアカウントを聞かれる。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_19_16

Provisioning Profile を選択し、Validate ボタンをクリック

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_20_13

エラー発生。

iTunes Store operation faild…
Missing required icon file. The bundle does not caontain an app icon for iPhone/iPod Touch of exactly ‘120×120′ pixels, in .png format for iOS versions >= 7.0.

organizer

必要なサイズのアイコンが含まれてないとのこと。

TARGETS->General->App Icons and Launch ImagesのところのUse Asset Catalogをクリックすると、Resourcesの中にImages.xcassetsが追加されます。

sc_sp_01_png__png_%e7%94%bb%e5%83%8f__1392x826_px__-_%e8%a1%a8%e7%a4%ba%e5%80%8d%e7%8e%87__94__

Images.xcassetsをクリックすると、

images_xcassets

のような画面になるので、これにちょうどいい画像を登録していけばいいようです。

images_xcassets

ドラッグ・アンド・ドロップですべての場所に画像を登録します。

では、再度ビルド!
また、エラーになってしまいました。

The app icon set named “App Icon” did not have any applicable content.

images_xcassets

アイコンのサイズが違うと、、、
横着して、同じファイルをすべての場所に当てはめたのがだめだったようです。

真面目に正しく、x2用 120×120、x3用 180×180で作り直して設定。

今度はビルド成功。

では、再度、Archive。
Organizerが開いたら、Validate…。
Summaryがでたら、Validateボタン。

成功!!

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_21_07

Doneを押す。

Upload to App Store…をクリック。

organizer

また、アカウントを聞かれるので、選択して、Choose 。

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_21_08

Send xxxxxxx to Apple:が表示されたら、Uploadボタン

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_21_09

なんでー

Archive upload failed due to he issues listed below.
This action could not be completed. Try again.(-22432)

organizer

たまたま失敗しただけのような気がするので、もう一度Upload。

今度は上手くいったと思ったのに、、、

WARNING ITMS-90076: “Potential Loss of Keychain Access. The previous version of software has an application-identifier value of [‘xxxxxxx’] and the new version of software being submitted has an application identifier of [‘yyyyyyy’]. This will result in a loss of keychain access.”

%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88_2016_10_30_21_25

プロビジョニングファイルファイルを作り直しかな、、、

ちょっと設定を変えて、Archiveし直してみると、もうそのバージョンはアップロードされてるよと言う。

おや?っと思って、iTunes connectを見てみる。

itunes_connect_%e3%82%a2%e3%83%83%e3%83%95%e3%82%9a%e3%83%ad%e3%83%bc%e3%83%88%e3%82%99%e3%82%92%e9%81%b8%e6%8a%9e_-_google_%e6%a4%9c%e7%b4%a2

ここの表示が変わっていないので、アップロードされていないものと思ったら、クリックすると、アップロードされたアプリが表示された。
選択して、保存する。

そして「審査へ提出」ボタンをクリック!

画像が足りないとか、年齢制限のところでエラーになったので、設定し直し。

再度「審査へ提出」!!

・輸出コンプライアンス
暗号化機能は変えてないのでいいえ
・コンテンツ配信権
アクセスしないのでいいえ
・Advertising Identifer(通称:IDFA)
広告は表示しないのでいいえ
※たぶんこれでいいはず、、、

「送信」!!

審査待ちになりました!

パチパチパチパチ

久しぶりにXcodeでiOSプロジェクトを開いたらいろいろエラーだった件

ひさしぶりに開いたiOSアップのプロジェクトがビルドできなくなってた。

The file “/Users/as……dist.plist” could not be opend.
Verify the value of the CODE_SIGN_ENTITLEMENTS build setting for target “xxxxx” and build configuration “Debug” is correct and that the file exists on disk.

flagraising_xcodeproj

ファイルの場所を移動したから、provisioning profileが読めなくなったか?

Build Settingのsigningにパスを指定するところがあったので、現状に合わせて直す。

flagraising_xcodeproj

すると次はこれ。

XXXXX requires a provisioning profile.
Select a provision profile for the “Debug” build configuration in the project editor.

flagraising_xcodeproj

どうも単純な話では無さそう。
Google先生に聞くと、Automatically manage signingにチェックを入れるといいらしい。
Apple Accountへのログインを求められるので、ログインする。
チームを選んだ結果がこれ。

The ‘Apple Developer Program License Agreement’ has been updated. In order to access certain membership resources, you must accpet the latest license agreemnt.

No profiles for ‘xxxxx’ were found
Xcode couldn’t find a provisioning profile matching ‘xxxx’

flagraising_xcodeproj

一つ目は、Apple Developer Program で何かAcceptが必要そう。
iTunes Connect にログインするとこれがでた。

itunes_connect

続けると押すと、さらに警告が。

itunes_connect

まあ、今回の作業のきっかけは、先日販売数を見ようと思ってiTunes Connectを開いたら、アプリが削除されたというメッセージを見たから。
さきは長そうだ。
まずは、payment関係を片付けないといけないらしい。

Agreements, Tax, and Bankingのページで、Requestボタンが有効になってたので、これをクリックして、Agreemntを承認する。
あいかわらず、このサイトはわかりにくい、、、
とりあえず、警告は無くなった。

Xcodeに戻ってみると、ビルドはできるようになった。

しかし、
The ‘Apple Developer Program License Agreement’ has been updated. In order to access certain membership resources, you must accpet the latest license agreemnt.
のエラーは残ったまま。

一旦終了して、開き直す。

flagraising_xcodeproj

直った!

ビルドも成功するもののエミュレーターが起動して、アプリが立ち上がるとエラーで止まってしまう。
ソースコードの修正も必要そうだ、、、

Method possibly missing a [super awakeFromNib] call
というwarningは、
– (void)awakeFromNib
{
[super awakeFromNib];
}

のように、[super awakeFromNib];を追加すれば解決。

main_m

このエラーの原因がわからない。

いろいろ調べているとOutletの接続もれらしい。

this class is not key value coding-compliant for the key view.
というエラーってわかりにくいけど、viewって変数の接続がうまくいっていないらしい。
一旦削除すると上記exceptionは発生しなくなる。
しかし、いろいろやっているうちに、こんどはviewがconnectされてないよというエラーが発生する。
再度つなぎ直すと直った。
おそらく最初の接続がおかしくなっていたので、一旦削除して接続し直しで、直ったらしい。

シミュレーターが起動しても真っ黒な原因は、

flagraising_xcodeproj

ここ↑の選択を間違えていた。

Terminating app due to uncaught exception ‘NSInternalInconsistencyException’, reason: ‘Application windows are expected to have a root view controller at the end of application launch’

の場合は、

AppDelegate.m
– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

// [window addSubview:viewController.view];
// ⇂
[window setRootViewController:viewController];

}

ようやく、シミュレーターでアプリが起動した!!
明日中に、再申請までいけるかな?
さて、もう寝ないと。

[PHP]ロックファイルを使った排他制御

ちょうど良いロックファイルを使った排他制御がなかったので、作ってみた。
APPPATHは定数なので、ご利用の環境に合わせて、書き換えてください。

<?php

class Filelock
{
    private $lock_file_dir = APPPATH . '/tmp/lock';

    function __construct(  ) {
       if(!file_exists($this->lock_file_dir)) {
           mkdir($this->lock_file_dir);
       }
    }

    public function get_path($process_name) {
        return $this->lock_file_dir."/".$process_name.'.lock';
    }

    public function create($process_name) {
        $fpath = $this->get_path($process_name);
        touch($fpath);

        $pid = getmypid();
        file_put_contents($fpath, $pid);
    }

    public function delete($process_name) {
        unlink($this->get_path($process_name));
    }

    public function check($process_name) {
        $path = $this->get_path($process_name);

        if (file_exists($path)) {
            $pid = file_get_contents($path);
            if (!self::exists_pid($pid)){
                self::delete($process_name);
                return true;
            } else {
                return false;
            }
        }
        return true;
    }

    public static function exists_pid ($pid) {
        if ($pid === '') {
            return true;
        }

        exec("ps {$pid} ", $_output, $_status);

        if($_status == 0 ) {
            return true;
        }

        return false;

    }
}

使い方

        // 同時実行防止チェック
        $key = basename(__FILE__);
        $filelock = new Filelock();
        if(!$filelock->check($key)) {
            echo "already running task. ".$key.PHP_EOL;
            exit(0);
        }
        $filelock->create($key);

        // 処理本体....

        // 同時実行防止ロックファイル解放
        $filelock->delete($key);

ajaxで取得したjavascriptを適用させる方法

ajaxでデータと一緒にjavasciptを取得して、実行したかった。
サーバー側でfunctionを動的に生成して、その関数をjavascriptで実行したかったんです。

最初は、htmlでscriptタグを出力しておいて、

$('script_area').html(script);

みたいにして出力してみたものの当然実行されず。
evalしても駄目。

google先生で探し回ったところ、appendChildで出力されれば適用されることがわかった。

以下はサンプルコード。

            function add_script(script) {
              var s = document.createElement("script");
              s.type = "text/javascript";
              s.innerHTML = script;
           
              var ele = document.getElementById("display_area");
              ele.appendChild(s);
            }
           
            function display_script() {
                var parm = {
                    url : $('#url').val(),
                    mode : $('input[name=mode]:checked').val()
                };
                var url = "/test";
                $.ajax({
                    url: url,
                    data: parm,
                    dataType: 'json',
                    success : function(json) {
                            add_script(json.scripts);
                    },
                    error : function (XMLHttpRequest, textStatus, errorThrown) {
                        console.log(XMLHttpRequest); // XMLHttpRequestオブジェクト
                        console.log(textStatus); // status は、リクエスト結果を表す文字列
                        console.log(errorThrown); // errorThrown は、例外オブジェクト
                    }
                });
            }