Elsaの技術日記(徒然なるままに)

主に自分で作ったアプリとかの報告・日記を記載

C言語でPythonモジュール作成 ~part2~

C言語Pythonモジュールの続きになります!!
elsammit-beginnerblg.hatenablog.com


前回は誤ったソースを展開してしまいました。。。
C言語Pythonモジュールのバブルソートですが、出来ましたので報告!!
f:id:Elsammit:20200530194025j:plain

結論から申しますと、PyObjectとPyListObjectの扱いを誤っていたためでした(泣)。

C言語Pythonモジュールのバブルソートはこちらになります。

static PyObject* c_bubble(PyObject *self, PyObject *args){
    PyObject *receive_list,*buf;

    // 配列を抽出
    receive_list = args
    //receive_list = PySequence_List(args);

    for(int i=0;i<PyList_Size(receive_list);i++){
        for(int j=i+1;j<PyList_Size(receive_list);j++){
            if(PyLong_AsLong(PyList_GetItem(receive_list,j-1)) > 
               PyLong_AsLong(PyList_GetItem(receive_list,j))){
                *buf = receive_list[j-1];
                receive_list[j-1] = receive_list[j];
                receive_list[j] = *buf;
            }
        }
    }
    return receive_list;    // 配列を返却
}

自分が誤っていた点は2つ!!
 ①PyList_GetItem APIで返ってくる型はPyObjectであるため、整数型に変換するためにPyLong_AsLong APIが必要
 ②PyList型のオブジェクトから要素数を得るためにはPyList_Size APIを用いなければならない
誤っていたソースの時は渡されたPyList型のオブジェクト要素数によらず、4でループが回っておりました。。。
ちゃんと、仕様書を読め!!ということですね。。。
docs.python.org
docs.python.org

では、Pythonモジュール側のバブルソートの処理時間を要素数を変更しながら確認していきます。
結果は下記の通り。
f:id:Elsammit:20200531180851j:plain

モジュール化しても要素数10000だと5秒弱かかってしまいますが、pythonよりは断然早いですね!!

C言語でpythonモジュール作成

突然ですが、pythonって便利ですよね?
f:id:Elsammit:20200530194025j:plain

組込系やWeb系、アプリケーション、機械学習(ディープラーニング)に至るまで幅広く用いることが出来る!!
万能言語ですね!!下回りから上位までを網羅できる!!
ゆりかごから墓場まで!!安心感がすごいです!!。

ただ、、、実は自分、、、あまり好きな言語ではなんですよね。。。
だってだって、処理が遅いんだもん~~~~~!!
分かってるよ、、、分かってますとも。インタプリタ型言語だしね。
だけど、繰り返し処理を実行するだけで数秒掛かる時があるとか、酷い(泣)。
色々なライブラリが提供されており、これらを組み合わせればよいのだろうけど、自分で本当にやりたい処理に対して遠回りしなきゃならない時があったり。。。(泣)

。。。!?
自分で作ればよくない!?自分で作れるならそっちの方が自由度高くかつ高速処理可能なモノが作れね?www
ということで、C言語pythonモジュールを作成してみました!!
今回は、バブルソートpythonモジュールで書いてみます。
一部ソースコードに誤りがありました!!申し訳ございません。。。
うまく配列をC言語モジュールに渡せていないことが原因です。以下は別に作成した簡単なソースコードで確認します。

環境はラズパイ4を用います。

下記を参考に作成してみました。
qiita.com


まず、C言語pythonモジュールの流れですが、
 ①C言語pythonモジュールを作成
 ②ビルド環境作成
 ③pythonC言語をビルド
 ④pythonで生成物をimport
となります。

まず、"①C言語pythonモジュールを作成"です。
全体はこの通りです。

static PyObject* queue(PyObject *self,PyObject *args){
    int x,y,g;
    if (!PyArg_ParseTuple(args, "ii", &x, &y)){
        return NULL;
    }
    g = 0;
    for(int i=0;i<100;i++){
        for(int j=0;j<100;j++){
            if(i%2==0 && j%2==0){
                g = g - i -j;
            }else if(i%2==0 && j%2==1){
                g = g -i + j;
            }else if(i%2==1 && j%2==0){
                g = g + i - j;
            }else{
                g = g + i + j;
            }
        }
    }
    return Py_BuildValue("i",g);
}

static PyMethodDef PracticeMethods[] = {
    {"queue", (PyCFunction)queue, METH_O, "c_test: queue"},
    {NULL,NULL,0,NULL}
};

static struct PyModuleDef practicetmodule ={
    PyModuleDef_HEAD_INIT,
    "c_test",
    NULL,
    -1,
    PracticeMethods
};

PyMODINIT_FUNC PyInit_practice(void){
    return PyModule_Create(&practicetmodule);
}

queue関数は実際にやらせたい処理になります。
残りの処理でqueue関数をモジュールとして、登録処理を実施しています。

次にpythonでビルドするために、setup.pyを作成します。
setup.pyは下記の通りになります。
ここは、参考にさせていただいたソースコードを持ってきて、
対象となるc言語のファイル名だけ変更しました。

from distutils.core import setup, Extension
setup(name='practice',
        version='1.0',
        ext_modules=[Extension('practice', ['c_test.c'])]
)

setup.pyでC言語pythonモジュールにするため、下記でビルドしました。

python setup.py build_ext -i

では実際にpythonでモジュールを利用してみたいと思います。
今回pythonで書いた処理とCモジュールを用いた処理で比較するために、
このようなソースを作成。

import practice as c_practice
import time

start = time.time()
print(c_practice.queue(0,0)) #C言語モジュールの実行.
elapsed_time = time.time() - start
start2 = time.time()
g = 0
#pythonコードの実行.
for i in range(100):
    for j in range(100):
        if i % 2 == 0 and j % 2==0:
            g = g -i - j
        elif i % 2 ==0 and j % 2==1:
            g = g - i+ j
        elif i % 2 == 1 and j % 2==0:
            g = g + i -j
        else:
            g = g + i + j
elapsed_time2 = time.time() - start2

print("C import time is {0}".format(elapsed_time)+"[sec]")
print("python time is {0}".format(elapsed_time2)+"[sec]")

こちらで作成したpythonモジュールを呼び出し、

import practice as c_practice

こちらで実行。

test_list = c_practice.queue(arry)   #c言語のモジュールで実行した場合.

では最後に比較してみます!!
結果はこの通り!!
時間は、各要素数(i,j)毎に5回実施してその平均値となります。
f:id:Elsammit:20200531092704j:plain

pythonコードはやはり遅いですね。。。w
素数(i,j)が5000だと30秒近くかかっている。。。
一方、C言語モジュールを用いた場合には要素数が5000であっても600ms程度で終了します。
モジュール化すると段違いで処理が早くなりますね!!
もしpythonで処理が重い場合にはモジュール化を実施してみるとよさそうです!!
python嫌いな理由が一つ解消しましたww。

追伸:
バブルソートについてはまたどこかでチャレンジしたいと思っております。
実はこのブログを読んだ友達がおかしなことに気づき、連絡頂いたことが原因で発覚しました!!
持つべきものは友達ですね!!

後、要素数10000でpythonコードだと110秒(2分弱)以上かかってましたw。

RaspberryPiでChrome OS利用 ~Part2~

RaspberryPiでChrome OS利用、にてインストール手順を記載しました。
合わせて、日本語入力手順も書きたかったのですが、どうしてもうまくいきませんでした(泣)。
elsammit-beginnerblg.hatenablog.com

うまくいかないことが悔しくて、思い切って詳しそうな先輩に聞きながら対策を練りました!!
が、、、結論を申しますと簡単には無理!!だということが分かりました!!
 ・キーボード入力設定用のパッケージを入れてイメージファイルを作り直す
 ・Fedora8 ~ Fedora14で用いられているキーボード入力設定用のパッケージのソースコードをラズパイに入れてビルド・インストール
が必要なようです(なぜFedoraなのかはこれから書きます)!!

では、何を実施して判断したのかを書いていきたいと思います!!
①localectlコマンド(言語・地域設定コマンド)が使えないか?
 ⇒パッケージとして用意されておらず(chrome brewでもインストールできず)
②system-setup-keyboardコマンドが使えないか?
 ⇒パッケージとして用意されておらず(chrome brewでもインストールできず)
③system-config-keyboardコマンドが使えないか?
 ⇒パッケージとして用意されておらず(chrome brewでもインストールできず)
④/sys/class/input配下にキーボード関連のファイルはないか?
 ⇒入力系のファイルはありそうだが、言語設定のようなファイルやコードはなし
⑤キーボード設定はinitプロセスで実行しているようなので、initプロセスでどのような処理を実行しているか調査
 ⇒/sbin/initをgrepしたところ、upstartが使われていることが分かった。
upstartが使われているディストリビューションは?
 ⇒Fedora9 ~Fedora14やubuntu12で用いられている
  これらのディストリビューションからキーボード系のソースを抜き出して自分でビルドし直す必要があるよう

ということで、パッケージインストールでは不可能!!
ソースコードを持ってきてビルドして設定できるようにするしかないようです。。。
f:id:Elsammit:20200527221513p:plain

というかカーネル層のバージョン低すぎないか?Fedora14とか10年近く前やぞ?w
現在そんな古いバージョンのソースコードなんて転がっているのかな?
少なくとも自分はソースコードからビルドするなんて面倒だし、日本語入力出来たところで使えないコマンド多すぎて汎用性なさ過ぎだし、
ということでこれ以上は、いいや!!

もし、どうしても!!という方いらっしゃいましたら、、、
こちらを使えばソースコード取得できるかも!!
qiita.com
そして、ソースコードをラズパイに保存してmakeとかを用いればインストール出来るはず!!

以上、キーボードでの日本語変換方法の検討でした。
依頼主にこのことは後で伝えるようにいたしますw。

簡単なWebアプリ作成 ~無駄な会議を撲滅しよう!! ~ part4~

今回はWebアプリのレイアウトを整えてみたので、その報告と備忘録です。
と言っても、デザインセンスがないのと初めてレイアウトを作成を行ったのでとてもダサいw。
世の中便利なもので、無償のテンプレートが数多く用意されていますが、"一度は自分で作ってみよう!!"と思い、テンプレートに頼らず作成しました。

ベースは前回までに作成したアプリを用います。
elsammit-beginnerblg.hatenablog.com

まずは勉強!!こちらの記事を利用してどのようにレイアウト設計すればよいか学びました。
http://information-bibouroku.hatenablog.com/entry/2018/02/07/102402:embed:Webの基本レイアウトまとめとサンプルコード - 備忘録
初心者向け!CSSでできるレイアウト例4選 | UX MILK

まずはどのようなレイアウトにするか、ですが、面白そうなデザインになりそうなこちらにしました!!
f:id:Elsammit:20200526203224j:plain

では、実際に作っていきます。
まずはcssですが、このように書きました。
文字は中央揃えとしました。
またB、C、Dの位置指定では、float:left;、float:right;で設定する用です。

body {
    width:1180px;
    margin:0 auto;
}
#Subject {        /*A*/
    height:50px;
    text-align: center;
}
#Left{        /*B*/
    width:280px;
    height:500px;
    float:left;
    margin-top:10px;
}
#Input {        /*C*/
    height:500px;
    width:580px;
    margin-left:290px;
    text-align: center;
    border-block-color: #000000;
    border-style: solid; 
    border-width: 1px;
    margin-top:10px;
}
#Right{        /*D*/
    width:280px;
    height:320px;
    float:right;
    margin-left:5px;
    margin-top:10px;
}
#Last {         /*E*/
    height:50px; 
    margin-top:10px;
}

合わせて画像をページに貼り付けたかったため、cssにて貼り付ける画像のサイズを指定します。
画像サイズの設定は下記としました。

p.sample img {
    width: 280px;
    height:220px;
}
p.Timebackground img{
    height:350px;
    width:100px;
}

次にejs(html)ですが、全てを載せてしまうと長文になってしまうため、概要のみ載せます。
実際のソースコードはこちらをご参照下さい。
github.com

<!DOCTYPE HTML>
<html>
    <head>
        <meta charset="utf-8">
        <title>会議用タイマー</title>
        <link rel="stylesheet" href="layout.css">
    </head>
    <body>
        <div id="Subject">
            ~ここがA~
        </div>
        <div id="Left">
            <p class="sample">
                ~ここがB~
            </p>
        </div>
        <div id="Right">
            ~ここがD~
        </div>
        <div id="Input">
            ~ここがC~
        </div>
        <div id="Last">
            ~ここがE~
        </div>
        <script type="text/javascript" src="TimerProcess.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
    </body>
</html>

ここで注意しなければならないのは、
B、Dを指定してからCを記載しなければならないことです。
B、C、Dの順に書くとレイアウトがおかしくなるようです。

最終的に出来たWebアプリですが、、、こちらになります。
※右下にボタンがありますが、こちらは後で記事にしたいと思います。
うん!!センス 0www。
もっとデザインセンス身につけなければwww。
f:id:Elsammit:20200526205644j:plain

やっとここまで出来た!!
後は集めたデータをグラフやリストで表示させて最後かな?
前回のChrome OSの日本語入力について、進捗あったのでこちらもまとめたいし、、、
まだまだやりたいことがたくさん!!

と、言うことで!!
次回は何を書くか分からないけどゆっくり進めていこう。

RaspberryPiでChrome OS利用

簡単なWebアプリ作成 ~無駄な会議を撲滅しよう!!~は一旦中断。
今回はRaspberryPiへChrome OSを利用してみたいと思います。
ある人より、"raspberryPi 4でChrome OSインストールしてみたけど、日本語入力出来ないんだけどどうしたらいいだろう?"と相談を受けたので、実際に動かしてみました!!
ただ、、、結論から申しますと日本語入力できるようにできませんでした(泣)。
Chrome拡張機能アプリを入れてみたけどダメで、じゃあコマンドで、、と思ったけどパッケージインストールできないし。。。
すでにimg化されたデータ用いたけど、自分でbuildしなければならないのかな?
だけど、buildのためには色々前準備必要そうで面倒だし、最新版のChrome OSだとLinuxコマンド使える設定あるらしいので、、、
ラズパイ用のimgでもこちらが使えるようになるまで待とうかな?

では、備忘録も兼ねて実施したことまとめて行きます。
用いたのはraspberryPi 4です。

まず、今回用いたOSはChromiumOSからフォークしたLinuxベースのオペレーショングシステムであるFydeOSです。
note.com

FydeOSはgithubにてソースコードやビルド手順が掲載されています。
https://note.com/dodemoe/n/n6c73cf8b8e45github.com
また同様にimgファイルも下記で管理されています。
github.com

imgファイルはxz圧縮されているので、解凍してからμSDに書き込みを行います。
windowsなどの場合には、"7-Zip"、balenaEtcherという書き込みソフトを用いる用ですが、
Linux好きな私はxzコマンド+ddコマンドを用いて解凍と書き込みを行いました。
μSDをPCに挿入して、コマンド実行!!

xz -d chromiumos_test_image_r80r1-rpi4b.img.xz
sudo dd bs=4M if=/mnt/chromiumos_test_image_r80r1-rpi4b.img of=/dev/sdc

そして、ラズパイにμSDを挿入して電源ON!!
初期設定を行って、、、当たり前だけど日本語に設定して、、、と。
おおっ~!!着いた着いたw。
f:id:Elsammit:20200524145445j:plain

確かに日本語で表示されるけど日本語入力が出来ない。
Googleアプリの拡張機能を自分でインストールして、と。
変化なし。。。

ベースがLinuxで作られているということが分かり、じゃあコマンドで!!と思い、"Ctrl+Alt+T"でコマンドブロンㇷ゚トを表示させ、shellコマンドを入力し、シェル実行可能なコンソールを立ちあげた。

Chrome OSディストリビューション管理ソフトはchromebrewというコマンドのようなので、
まずはchromebrewをインストール!!

wget -q -O - https://raw.github.com/skycocker/chromebrew/master/install.sh | bash

これで手動で入れられると思い、意気揚々と

chrew list available

を実行!!。
あっ、、、あれ?ないぞ?キーボード管理出来そうなソフトが見つからない。。。
あまり充実していないのかな?困った。。。

もう少し調べてみようかな?。。。

後、、、現在、ChromebookLinuxコマンドが使える設定が可能なようだが、fydeOSにはなかった(泣)。
今後、搭載されるかもしれないのでしばらく待って様子見かな?
makitat.com


・追伸
fydeOSはsshが標準で有効になっています。
ログインは、
 ・ユーザ名 :root
 ・パスワード:test0000
でできます。
ただ、冒頭でも記載した通り中コマンドが充実しておらず、使いたいコマンドがないパターンが多々あり(apt-getやrpm等)ました。
かゆいところに手が届かない感が半端ないw。
オモチャを作る > ブラウザの閲覧
が目的の自分としては、よっぽどの理由がない限りはRasbianに軍配かな?と思いました。

簡単なWebアプリ作成 ~無駄な会議を撲滅しよう!!~ part3

part2に引き続きWebアプリの作成をしていきたいと思います!!
前回までの作業は下記を参考に。
elsammit-beginnerblg.hatenablog.com

今回は、サーバサイドでのDBへのデータRead/Write部分についてです。
DBですが、"役職データ"と"会議毎の情報"の2つのテーブルを用意しました。
各テーブルですがこのようにしました!!
※各役職毎に人数を登録しておくのはセンスなかったかな。。。
f:id:Elsammit:20200523173021j:plain
f:id:Elsammit:20200523172838j:plain

次にサーバサイドでの"役職データ"と"会議毎の情報"テーブルへの登録です。
今回DBはsqlite3を用いたため、こちらを宣言します。
※DB名は"database.sqlite3"としました。

const sqlite = require('sqlite3').verbose();                                          
const db = new sqlite.Database('db/database.sqlite3');

"会議毎の情報"テーブルへの登録は、

function Set_MeetingList(SumSalary,Manager_mem,gl_mem,cheif_mem,employ_mem,Meeting_time,Meeting_Name){
    const Set_MeetingMoney = "insert into MeetingList values(current_timestamp,'" + Meeting_Name + "'," +
                              SumSalary + "," + Manager_mem + "," + gl_mem + "," + cheif_mem + "," + employ_mem + ",'" + Meeting_time.toString() + "');"
    db.get(Set_MeetingMoney, function (err, rows) {
        console.log("Set OK");
    });
}

とし、insert intoで登録されたデータを蓄積するようにしました。

次に"役職データ"ですが、各役職毎に同じ月給を毎回登録するのは処理として無駄であるため、
登録された月給とは異なる月給であった場合にのみテーブルに登録するようにしました。

function Set_Memsalary(intput_salary){
    p = Read_Memsalary();
    var i =0;
    p.then(function(Mem_array){
        Mem_array.forEach(function(array){
            if(array.salary != intput_salary[i]){
                SubSet_Memsalary(array.name,intput_salary[i]);
            }
            i++;
        });
    });
}

function SubSet_Memsalary(input_name,input_salary){
    const Set_Memsalary = "update salary set salary ="+ input_salary +  " where name = '" + input_name +"';";
    db.get(Set_Memsalary,function(err,res){
        console.log("set OK");
    });
}

最後に、Webアプリ立ち上げ時に各役職毎の月給を初期値として登録するのだが、
テーブルへのRead処理は、

function Set_Memsalary(intput_salary){
    p = Read_Memsalary();
    var i =0;
    p.then(function(Mem_array){
        Mem_array.forEach(function(array){
            if(array.salary != intput_salary[i]){
                SubSet_Memsalary(array.name,intput_salary[i]);
            }
            i++;
        });
    });
}

function Read_Memsalary(){
    return new Promise(resolve =>{
        const GetMemSalary = "select * from salary;"
        var Mem_array = []
         db.all(GetMemSalary, function (err, rows) {
             rows.forEach(function(row){
                Mem_array.push({name:row.name,salary:row.salary});
             });
             resolve(Mem_array);
         });
    });
}

です。
nodejsの仕様なのか、expressの仕様なのか関数コール時に並列処理となってしまうため、
"new Promise"、".then"を用いました。
また、テーブルからselect fromで読み出す際には、"db.all"にする必要があります。
db.allから得られたデータ群をforEachにより分離して配列に登録する流れです。

以上、part2~part3でサーバサイド側の処理の報告でした。
ソースコードはこちらに格納しています。
github.com

次はレイアウト整えて見栄えよくしようかな?
せっかくDBに会議データを保存しているから集計結果の可視化をやろうかな?
。。。やりたいことが多いw

簡単なWebアプリ作成 ~無駄な会議を撲滅しよう!!~ part2

前回、簡単なWebアプリを作成した。
elsammit-beginnerblg.hatenablog.com

が、、、やはり、、、
 ・各会議時間や参加人数を保存おきたい
 ・後で見返せるようにしたい
と思い、サーバーサイドの作成とDBへの登録を実施したので報告!!
ソースコードは下記に格納しておりますのでご自由に。
github.com


作成した構成はこんなイメージ。
f:id:Elsammit:20200521210532j:plain

サーバーサイドとして、"nodejs"を用いました。
また、nodejsのフレームワークとして、expressを用いました。
DBにはsqliteを用いました。

nodejsとは、"サーバサイドで動作するjavascript"のこと。
詳しくは下記をどうぞ!!
eng-entrance.com

後expressのセットアップは下記でお願いします。
qiita.com

で、フォルダ構成はこう。
※今回、複数の変数をサーバーサイドからクライアント側に送信したい関係でejsを用いました。
 ejsだと<%= 値 %>でクライアント側に送信できるので便利w
f:id:Elsammit:20200521211442j:plain

"無駄な会議を撲滅しよう!!"のストップボタンを押下した時のクライアントサイドの処理は下記。
ajaxを用いて、post requestでjsonを送信!!
postで送信しているデータは、、、
 ・会議で発生した金額(money)
 ・各役職の参加人数(Manager_num、GL_num、Chief_num、Employee_num)
 ・各役職の月給(Manager_salary、GL_salary、Chief_salary、Employee_salary)
 ・会議時間(Meeting_time)
 ・会議名(Meeting_Name)
にしました。

function sendToServer(){
    var send_money = document.form_sw.Meeting_Money.value;
    return $.ajax({
        url:"http://自分のURL/SendMoney",//phpファイルのURL
        type: "post",
        data: {"money":send_money,
                "Manager_num":document.form_salary.Manager_Num.value,
                "GL_num":document.form_salary.GL_Num.value,
                "Chief_num":document.form_salary.Chief_Num.value,
                "Employee_num":document.form_salary.Employee_Num.value,
                "Manager_salary":document.form_salary.Manager_salary.value,
                "GL_salary":document.form_salary.GL_salary.value,
                "Chief_salary":document.form_salary.Chief_salary.value,
                "Employee_salary":document.form_salary.Employee_salary.value,
                "Meeting_time":document.form_sw.counter.value,
                "Meeting_Name":document.form_sw.Meeting_Name.value
            },	
        dataType: 'text',
        success: function(){	// 転送成功時.
            console.log("success");	
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {	// 転送失敗時.
            console.log("error");
        }
    })
}

サーバーサイド側では、クライアントからのjsonを受け取り、DBに登録するための処理を実行。
req.bodyに受信したjsonデータが格納されているため、各々のデータをDBに登録していく。

app.post('/SendMoney',(req,res)=>{  // フロントエンドからの受信.
Set_MeetingList(req.body.money,req.body.Manager_num,req.body.GL_num,req.body.Chief_num,req.body.Employee_num,req.body.Meeting_time,req.body.Meeting_Name);
    var Memsalary = [req.body.Manager_salary,req.body.GL_salary,req.body.Chief_salary,req.body.Employee_salary];    // 役職毎の月給配列(部長、課長、係長、社員の順).
    Set_Memsalary(Memsalary);  // 役職毎の月給をDBへ保存するための関数コール.

    res.end()
});

今回はここまで!!
サーバサイドでのDBへの登録については後日記載していきます。