まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:node

たまに子供や初心者を相手にプログラミングを教えてみせたりする際に Scratch を使っています:
Screenshot from 2018-05-04 15-28-23


MIT メディアラボが開発した GUI によるプログラミング開発言語および実行環境です。プログラムをエディタでガリガリと記述するような、いわゆる「コーディング」に相当する作業があまり必要なく、GUI を中心とした見た目に対する処理や変更をイベント単位に足していきながら少しずつ動くものを作っていくものです。そのためユーザーが完成形をイメージしながら作っていくことができ、コーディングそのものに不慣れな人がプログラミングを理解するのに向いていると思っています。

この Scratch は良くも悪くも GUI 中心の作業になるため、他の環境との連携が難しい(というか、そもそもどういう外部インターフェースが用意されているのかわかっていなかった)という印象を持っていました。Scratch の閉じた世界の中では初心者にも簡単でわかりやすい、という面があるのと同時に、Scratch 以外の環境とは繋がりにくいと思っていました。

で、なんかインターフェースが公開されてないかなあ・・・ と思って探していたら、この Scratch の国内第一人者でもある石原さんが書かれた一連のブログを見つけました:
Scratch(スクラッチ)を外部のプログラムなどとつなぐ「遠隔センサー接続」を解説する(その1)


↑このブログで書かれている内容は Scratch 1.4 の「遠隔センサーを有効に設定」して 42001 番ポートを開放した上で、TCP/IP のメッセージングによる連携を Ruby で行う、というものです。基本的にはほぼ同じ内容を Node.js で実装してみようと思い、調べた結果をまとめました。


なお、石原さんは「まちくえすと」の開発などで活躍されていてご存知の方もいらっしゃると思いますが、拙作マンホールマップiOS アプリ版の開発に尽力いただいたご縁もあり、そのお付き合いの中で Scratch のことも教えていただきました。僕にとっては「Scratch の先生」でもあります。石原さん、このブログとても助かりました。ありがとうございます!



まず、以下の作業を行う前提として、Scratch はローカルインストール可能なバージョン 1.4 を使う必要があります。公式サイトからオンライン版であるバージョン 2.0 以降を利用することも可能ですが、以下で紹介する Node.js 連携は Scratch 1.4 をローカルインストールして使う想定で記載しています。各種 OS 向けの Scratch 1.4 はこちらからダウンロードして導入してください(以下、Ubuntu 16.04 環境に Scratch 1.4 を導入した前提で記述します):
https://scratch.mit.edu/scratch_1.4/



まず最初に、Scrach を外部連携するためのポート開放を行います。Scratch 1.4 を起動し、「調べる」カテゴリ内「●●センサーの値」と書かれたブロックを右クリックして、「遠隔センサー接続を有効にする」を選択します:
Screenshot from 2018-05-04 15-29-09


すると「遠隔センサー接続が有効になりました」という確認ダイアログが表示されます:
Screenshot from 2018-05-04 15-29-39



この作業後、Scratch は 42001 番ポートで接続できるようになっています。つまり僕の以前のブログで紹介したような TCP/IP プログラミングによって Scratch との TCP/IP 接続が可能になっているのでした。試しに以下のような Node.js プログラム(scratch01.js)を書いて実行してみます(以前のブログで書いたものよりも更に簡略化し、最小限必要な内容だけにしています):
var net = require( 'net' );

//. 接続先 IP アドレスとポート番号
var host = '127.0.0.1';
var port = 42001;

//. 接続
var client = new net.Socket();
client.connect( port, host, function(){
  console.log( '接続: ' + host + ':' + port );
});

//. サーバーからメッセージを受信したら、その内容を表示する
client.on( 'data', function( data ){
  console.log( '' + data );
});

↑このプログラムを実行すると、127.0.0.1(自分自身)の 42001 番ポートに TCP/IP 接続し、接続先から何かメッセージが送られてくるとそのメッセージ内容を文字列化してそのまま表示する、というものです。これをまず node コマンドを使って実行しておきます:
$ node scratch01.js

これで 42001 番ポートに接続し、メッセージの受信待ち状態になりました。


そして Scratch 側からはこの(42001 番ポートの)接続先に対してメッセージを送信してみます。この処理を行うには「制御」カテゴリ内「●●を送る」ブロックを使います:
Screenshot from 2018-05-04 15-40-55


同ブロックを右クリックして「新規」を選択し、メッセージの名前を任意に指定します。以下の例ではメッセージの名前に Hello と指定してます:
Screenshot from 2018-05-04 15-42-14


するとブロックの表示内容が「Hello を送る」と切り替わります。この状態で同ブロックを何度かマウスでクリックします(クリックするたびにこのブロックが処理を実行します):
Screenshot from 2018-05-04 15-42-41


すると先に実行していた Node.js プログラムのコンソール画面に(最初の制御文字に続いて) 
broadcast "Hello"
という文字行がクリックするたびに表示されます:
Screenshot from 2018-05-04 15-46-59


この結果から Scratch の「●●を送る」ブロックを1回実行すると
broadcast "(指定したメッセージ)"

という文字列が送信される、ということが分かりました。これで Scratch から Node.js のプログラムにメッセージを送信し、Node.js プログラム側でその内容を受信することができました。


この応用で逆に Node.js から Scratch にメッセージを送ることもできます。例えば scratch02.js を以下の内容で作成します(接続後に 'broadcast "a"' というメッセージを Scratch に送信する、という内容です):
//. scratch02.js
var net = require( 'net' );
var host = '127.0.0.1';
var port = 42001;

var client = new net.Socket();
client.connect( port, host, function(){
  console.log( '接続: ' + host + ':' + port );

  var msg = 'broadcast "a"';
  var len = msg.length;
  var buf = pack( len );
  client.write( buf + msg );  //. msg の内容を Scratch へ送信
});

//. 4バイトのバイト列(Buffer)に変換
function pack( n ){
  var m1 = n % 256;
  n = Math.floor( n / 256 );
  var m2 = n % 256;
  n = Math.floor( n / 256 );
  var m3 = n % 256;
  var m4 = Math.floor( n / 256 );

  return new Buffer( [ m4, m3, m2, m1 ] );
}



そして Scratch 側では 'broadcast "a"' というメッセージを受け取ったら何らかの処理がスタートする、というプログラムを書いておきます(下の例ではスプライト1の猫のキャラクターが画面内を歩き始める処理がスタートする、という内容にしています):
Screenshot from 2018-05-05 15-54-06


この状態で scratch02.js を実行すると、TCP/IP 通信によって Scratch に接続し、broadcast "a" というメッセージが送られ、Scratch のスプライト1のキャラクターが壁に跳ね返りながら動き回る処理がスタートします(特に止める方法を用意していないので、止めるには画面右上の赤丸部分をクリックしてください):
Screenshot from 2018-05-05 16-02-12


これで Scratch から Nodejs にメッセージを送ったり、逆に Node.js から Scratch にメッセージを送る、ということが実現できました。

 

IBM Cloud からはオープンソースのブロックチェーン基盤である Hyperledger Fabric をベースとしたマネージドなブロックチェーン環境が提供されています。この 3/21 からは Starter Membership Plan という、名前の通りに小規模向けの比較的安価なプランも提供されるようになりました。2018/03/25 時点ではベータ版で、通常プランと比較していくつかの制約はありますが、無料で利用できます。早速使ってみました。今回のブログエントリではサービスを選択して利用可能にする所までの手順を紹介します。あくまでベータ版であり、今後の仕様変更等により以下の内容は正しい情報ではなくなる可能性もあります。以下の内容は 2018/03/25 時点での情報であることをご了承ください。


また、以下で紹介する IBM Cloud から提供されているブロックチェーンサービスは、IBM Cloud の無料アカウント版であるライトプランからは利用できません(2018/03/25 時点)。以下で紹介する Starter Membership Plan のベータ版は無料ですが、ライトプランのアカウントからはサービスを選択することはできません。実際に試す場合はクレジットカードを登録するなどしてスタンダードプラン等に移行してからご利用ください。


IBM Cloud にログインしてからカタログを表示し、ブロックチェーンカテゴリーを選択すると、対象サービスが "Blockchain" だけに絞り込まれます。これを選択します:
2018032401


次の画面でリージョンやプランを選択します。今回紹介する Starter Membership Plan は 2018/03/25 時点では米国南部リージョンからのみ提供されています。そのためロケーションを選択する箇所で「米国南部」を選ぶようにしてください:
2018032402


画面を下にスクロールすると利用プランの選択画面になります。上記で米国南部リージョンを選択しているとここが選択肢になっていて、Starter Membership Plan か Enterprise Membership Plan が選べるようになっているはずです(米国南部以外のリージョンだと有料の Enterprise Membership Plan のみ)。ここで Starter Membership Plan を選択して「作成」ボタンをクリックします:
2018032403


なお上図にも書かれているように Starter Membership Plan の場合、以下のような制約事項があります。シングルピア環境なので多数決をとるようなスマートコントラクトを実装することはできませんが、アプリケーションとしての実装はほぼ変わらないものを動かすことが可能になります:
・組織は2つまで
・各組織ごとに1ピアまで


サービスが作成されるとダッシュボード内のサービス一覧内にブロックチェーンサービスが表示されるようになります:
2018032404


作成したブロックチェーンサービスを選択するとブロックチェーンサービスの概要が表示されます(この辺りはプランによる違いはないと思われます)。ここから実際にブロックチェーンを操作したり管理するためのダッシュボードを開いてみます。「管理」タブの「起動」ボタンをクリックします:
2018032405


すると別ウィンドウでブロックチェーン環境のダッシュボードが表示されます。下画面ではトランザクションの順番を決定するオーダラーや、CA(認証局)、そしてピアとなるサーバー環境が動いている様子が確認できます:
2018032406


これまで自分で Hyperledger Fabric 環境を構築しようとすると、サーバー環境を用意した上でこれらの各種サーバーを自分で作って運用する必要がありました。そこまでの構築作業とサーバーとしての運用部分を IBM Cloud に任せて、実際のアプリケーション開発・実行やユーザー登録といった実務に集中できる環境として提供されていることが分かります。ブロックチェーンに興味のある方は是非今のうちに色々使ってみてはいかがでしょうか?

なお、今回は IBM Cloud 上にサービスを作る作業までを紹介しました。次回以降でこのサービスにビジネスネットワークアプリケーションをデプロイして動かせるようになるまでの手順を紹介する予定です。



タイトル通りですが、「『じゃじゃじゃじゃーん』をリズムに変換する Node-RED 用ノード」を作ってみました。


ほとんどの人はこの時点でどんな処理をするノードか理解ができてないと思うので、その補足です。このノードを作るきっかけになったのはこちらのまとめサイトでした:
【超能力者】知恵袋「何の曲か分かりますか?てんてててん てんてんてて」→「戦場のメリークリスマスですね」「なんで分かるんだ!?」


要するに「じゃじゃじゃじゃーん」という日本語テキスト(音階なし、正確なリズムもわからない)だけを頼りに「交響曲第5番、『運命』」のように曲を探し出すという(超)能力を持った人がいて、こりゃすげー!と。

なるほど、面白そうだ。最終的にはこれと同じことをシステム化できないかなあ、と考えているわけですが、その開発の途中で「とりあえずは日本語テキストからリズムを取り出す」仕組みを作り、そこから少々脱線して「その仕組を Node-RED のノードにしてみた」のが今回紹介するノード: node-red-contrib-dotnsf-jajajajan です:
https://www.npmjs.com/package/node-red-contrib-dotnsf-jajajajan

2017092300


利用するにはまず Node-RED 環境を(ローカルでも IBM Bluemix でも)用意し、そこからこのノードを導入します。ローカルであれば以下のコマンドでインストールできます:
$ npm install node-red-contrib-dotnsf-jajajajan

IBM Bluemix の Node-RED の場合はメニューからパレット管理機能を使ってインストールします:
2017101401


パレットの管理画面において「ノードを追加」タブを選び、検索文字列に "jajaja" などと入力すると見つかります(私のミスで複数バージョンが登録されてしまいました。最新バージョンのものを指定して「ノードを追加」してください):
2017101402


この状態で Node-RED を起動すると機能カテゴリ内に音符アイコンの "jajajajan" というノードが追加されて、キャンバス上で使えるようになります:
2017092301


このノードの挙動を確認するために以下のようなシンプルなフローを作ってみます。入力カテゴリの inject ノードと、出力カテゴリの debug ノード、そしてこの jajajajan ノードを配置し、以下のように配線します:
2017092302


inject ノードをダブルクリックして、Payload の種類をデフォルトの timestamp から string に変更し、その値を上記で示したような日本語のリズムテキストにします。以下の例ではまさに『運命』の有名な部分である「じゃじゃじゃじゃーん、ララララーン」としています(「じゃ」でも「ラ」でも同じように処理することができることを確認するため、わざと別の表現にしています)。ここで指定したテキストが msg.payload として jajajajan ノードに渡されて処理されます。入力し終わったら「終了」ボタン:
2017092303


これでフローの準備はできました。動作を確認するため「デプロイ」します:
2017092304


デプロイできたら、画面右ペインを "debug" タブに切り替えたことを確認してから、inject ノードの左にあるボタン部分をクリックします。クリックしたタイミングで指定したテキスト(今回の例では「じゃじゃじゃじゃーん、ララララーン」)が msg.payload として jajajajan ノードに渡されます。
2017092305


クリックすると、不定のタイミングで debug タブに文字が表示されてゆくことが確認できます。このタイミングに関してはネットワーク依存があることと、Node.js 上で動く Node-RED が非同期処理を行うことから必ずしも厳密なタイミングではないのですが、なんとなく「じゃ」「じゃ」「じゃ」「じゃーん」「ラ」「ラ」「ラ」「ラーン」のリズムで1行ずつ表示されている(はず)です:
2017092306


出力されている内容を詳しく見てみると、"8" とか "2" とか数字が表示されています。実はこれは音符の長さを表していて、"8" は「8分音符」、"2" は「半音符(2分音符)」を意味して、その長さに合わせて debug 出力も制御されています。現在の仕様では "8" では 0.5 秒後、"2" では 2 秒後に次の音符が表示されるようにしています(なので "8" の次はすぐに表示されますが、"2" の次が表示されるまでには少し時間がかかります)。結果的になんとなく「じゃ」「じゃ」「じゃ」「じゃーん」「ラ」「ラ」「ラ」「ラーン」というリズムに合わせて1行ずつ表示されているように見える・・・のではないかと思っています(苦笑):
2017092307


試しに『メヌエット』の出だし部分である「タンタタタタタンタンタン」で試すとこんな感じです。最初の「タン」が4分音符で、そこから8分音符が4回続いて・・・というリズムで解釈されていることがわかります:
2017092308


と、またこんなくっだらないノードを作ってしまった。。どうしても使ってみたいという変わった人がいたら使ってください。ちなみにソースコードはこちらです(日本語処理の部分はかなりハードコードに依存しているので、他の言語用に移植するのが難しいかも・・):
https://github.com/dotnsf/node-red-contrib-dotnsf-jajajajan


脱線した話を元に戻すと、このノードで実現している仕組みを使ってテキストをリズムに変えて、取り出したリズムの情報をあらかじめ楽譜を元にリズムだけを取り出した楽曲のデータベースから曖昧検索して近いものを探す、という方法で冒頭で紹介したような超能力者みたいな仕組みをシステム化できないか、と考えています。こちらも諦めているわけではなく、ある程度は動くようになっている(楽譜そのものと、楽譜から主旋律を探す仕組みに苦戦している)ので、人前に出せるようになったら公開するかもしれません。まあ、そうそう簡単なものではないと分かっている上に自分が音楽や楽器が得意ではないので、まだ躓いてすらいない落とし穴が待っているかもしれませんが・・・

複数の楽器向けに書かれている楽譜から主旋律(というのかな?人間がよく知ってるパート)を探す方法を知っている人がいたら教えていただけるとうれしいです。

IBM Bluemix からも提供されている IBM の DBaaS サービスである dashDB に Node.js からアクセスする方法を紹介します。実際には dashDB だけでなく、DB2 のサービスやオンプレミスデータベースへも同様に応用できますが、今回は Bluemix 上の DB2/dashDB 関連サービスを例に紹介します:
2017063002


dashDB は行指向/列指向型のテーブルをどちらも作成することができるリレーショナル・データベースのサービスですが、そのデータベースシステムとしての実体は IBM DB2 です。というわけで、このライブラリを使ってアクセスします:
https://www.npmjs.com/package/ibm_db

2017063001


まず以下のコマンドを実行して ibm_db をインストールします(このコマンドだけで DB2 ODBC Driver ごとインストールされます):
$ npm install ibm_db


そして以下のようなコードを用意して dashDB にアクセスします:

(settings.js)
exports.db_host = 'dashdb-entry-yp-XXXXXXXX.services.dal.bluemix.net';
exports.db_port = 50000;
exports.db_name = 'BLUDB';
exports.db_username = 'dashNNNN';
exports.db_password = 'PASSWORD';

(sample.js)
var ibm_db = require( 'ibm_db' );
var settings = require( './settings' );

var db_str = "DATABASE=" + settings.db_name
  + ";HOSTNAME=" + settings.db_host
  + ";UID=" + settings.db_username
  + ";PWD=" + settings.db_password
  + ";PORT=" + settings.db_port
  + ";PROTOCOL=TCPIP";
var sql = "select OBJECTID, NAME from SAMPLES.GEO_CUSTOMER limit 10";

ibm_db.open( db_str, function( err, conn ){
  if( err ) return console.log( err );

  conn.query( sql, function( err, data ){
    if( err ) console.log( err );
    else console.log( data );

    conn.close( function(){
      console.log( 'done.' );
    });
  });
});

settings.js の中身はユーザー名やパスワードといった dashDB に接続するためのサービス資格情報です。IBM Bluemix の画面から取得できる値を使って、実際の値で書き換えて使ってください:

2017063003


アプリケーションの実体は sample.js です。今回の例ではシンプルに接続して、サンプルデータとして GEO_CUSTOMER テーブルから OBJECTID と NAME の値を 10 件だけ取得する、という SQL (青字部分)を実行しました。また settings.js で定義した情報を取り出して接続文字列(赤字部分)を生成しています。

node コマンドで sample.js を実行して、以下のような結果が表示されれば成功です:
$ node sample.js
[ { OBJECTID: 1322, NAME: 'Kami Labarbera' },
  { OBJECTID: 1323, NAME: 'Johnathon Tunney' },
     :
  { OBJECTID: 1587, NAME: 'Althea Alcazar' } ]
done.







 

Node.js の処理内で unzip を実現する方法を紹介します。アップロードなどで zip ファイルを受取って、それをダイナミックに展開して特定のファイルを取り出す、といった仕組みを Node.js で実現する場合に必要な実装の例です。

この仕組みを実現するために、node-unzip という便利なライブラリがあるので、これを使うことにします:
https://www.npmjs.com/package/unzip

2017070601


fs ライブラリと併用して、こんな感じで使います(zip ファイル内の全ファイルを展開する例):
var fs = require( 'fs' );
var unzip = require( 'unzip' );

  :
  :

fs.createReadStream( './uploads/archive.zip' )
    .pipe( unzip.Extract( { path: './tmp/' } ) );

特定のファイルだけ(以下の例では拡張子が ".xml" のものだけ)を展開する場合は以下のようにします:
var fs = require( 'fs' );
var unzip = require( 'unzip' );

  :
  :

fs.createReadStream( './uploads/archive.zip' )
    .pipe( unzip.Parse() )
    .on( 'entry', function( entry ){
      var filename = entry.path;  //. ファイル名
      var type = entry.type;  //. 'Directory' または 'File'
      var size = entry.size;   //. ファイルサイズ

      if( filename.toLowerCase().endsWith( ".xml" ) ){
//. ".xml" で終わるファイル名だった場合のみ展開 entry.pipe( fs.createWriteStream( './tmp/' + filename ) ); }else{ entry.autodrain(); } });

そもそもの元ファイルが zip 圧縮されていたり、大量のファイルデータをアップロードして登録したい場合などは、目的のファイルを zip して、1回でまとめてアップロードできると便利なのですが、この方法であれば受け取った zip を展開して・・・という処理が実現できます。


このページのトップヘ