[C#]複数のExcelのブック(シート)をマージする。

複数のExcelブックのシートをマージする。
複数のブック上のシートを1つのファイルにマージさせる方法が探しても意外と見つからずとりあえず作ってみた。
VBAであればネットに少しあるみたい。

動作環境
Windows7
Excel 2007/2010(必須)

エクセルのAPIはCOM
C#でのCOM参照は使いづらい。本来、.NetFrameworkで使用されるのが前提になっていないからなのか、APIの情報が自動ででてこない。要は式がDynamicになってしまう。
例えば、全体をtry-catchで囲っているのに、APIがdynamic型なので、次の場合、個別に対応が必要になる。

try
{
    int TabColor = 0;   // int型
    try
    {
        TabColor = ws.Tab.Color;   // エクセルAPIは有効な場合は数値、無効値は"False"が入る
    }
    catch
    {
        TabColor = 0;     // 無効値("False")がintに代入しようとしたため例外が発生する。その場合、0とする。
    }
    // 処理
}
catch
{
    // 処理
}

コツは一つのApplicationインスタンス内で全て行う。
当初、ファイル単位でApplication(Microsoft.Office.Interop.Excel.Applicationクラス)を起こして、Workbook型のインスタンスを作って、Worksheetをcopyしていたが、うまくいかず、、、。エクセルは一つのアプリケーションが起動していると、そのエクセルで次のファイルを起動する、という仕様になっていて、このエクセル内で開いているファイルならコピーができる。ということは、ひとつのApplicationインスタンス内でやってみればうまくいくんじゃないかなぁって思って、やってみたらうまくいった。

エクセルAPIを使うときの注意点。
・シートやセルのはじめのインデックスは”1″、例えば次のソースは誤り。

// A1のセルを参照
var val = ws.Cells[0,0].Value;     // [1,1]が正解

・エクセルがバックグランドで動作するので、APIを呼ぶ側のアプリケーションが異常終了するとプロセスが残る。
デバッグとかで途中で止めたり、異常終了したまま放置すると、EXCELという名前のゾンビプロセスが増殖する。
それを防ぐために、実行時に注意を促す。以下例。

if (Process.GetProcesses().Any(p => p.ProcessName.IndexOf("EXCEL") != -1))
{
    MessageBox.Show("エクセルを閉じてください!!");
    return;
}

動作画面

今回作成したソースはこちら

[Python]twitterのstreaming APIは日本語によるフィルタリングが未対応。

streming APIを利用する。
twitterのstreaming APIを利用してストリーム上に流れるツイートの情報をフィルターを利用して取得することができます。
Python用のtwitter APIラッパーであるtweepyを利用して、ストリーム上に流れるツイートのうち、あるキーワードを含むツイートを取得しようと試みた所、日本語キーワードは使えないことがわかりました。

tweepy
https://github.com/tweepy/tweepy

なお、tweepyの使用方法は下記のサイト様を参考にさせていただいた。
kk6のメモ帳* – tweepyがUser Streamsに対応していた
http://kk6.hateblo.jp/entry/20110817/1313564125

tweepyを利用してstreming APIを利用する。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import codecs, simplejson, time

from tweepy.streaming import StreamListener, Stream
from tweepy.auth import OAuthHandler
from tweepy.api import API
from datetime import timedelta

def get_oauth():
    # 以下4つのキー等は適宜取得して置き換えてください。
    consumer_key = 'XXXXXXXXXXXXX'
    consumer_secret = 'YYYYYYYYYYYY'
    access_key = 'XXXXXXXX-XXXXXXXXXXXXXXXXXXX'
    access_secret = 'XXXXXXXXXXXXXXXXXXXX'
    auth = OAuthHandler(consumer_key, consumer_secret)
    auth.set_access_token(access_key, access_secret)
    return auth

class AbstractedlyListener(StreamListener):
    def on_status(self, status):
        status.created_at += timedelta(hours=9)
        text = status.text
        
        print "-------------------\n"
        print "tweet " + str(status.created_at) +"\n"
        print text + "\n"

if __name__ == '__main__':
    auth = get_oauth()
    stream = Stream(auth, AbstractedlyListener(), secure=True)
    stream.filter(track=["AKB"])

ここでは、”AKB”をキーワードに使用しています。さすがにAKBとなるとツイートの量が半端ではありません。”AKB”はアルファベットで構成されているため、次のようなツイートも取得出来ます。

◯ "AKB選抜総選挙で大島優子は何位になるか?"
◯ "ところでさ、AKB大島優子とHKT指原莉乃どっち好き?"
◯ "     akbの大島優子"

ダブルクォーテーション内がツイートの全体の長さだとして、アルファベットによるフィルタリングはほぼ問題なく取得出来ます。

日本語のキーワードは未対応。
“大島優子”の様に日本語によるキーワードはtwitter API自体が未対応で、今のところ使いドコロがありません。

× "AKB選抜総選挙で大島優子は何位になるか?"
× "ところでさ、AKB大島優子とHKT指原莉乃どっち好き?"
× "     akbの大島優子"

唯一、ツイート全体の長さとキーワードが一致した時のみ、取得することができます。

◯ "大島優子"

未対応であることは、twitter API documentに記載されている事項です。

Streaming API request parameters
https://dev.twitter.com/docs/streaming-apis/parameters#track

Non-space separated languages, such as CJK and Arabic, are currently unsupported.
“日​​中韓やアラビア語などの非スペース区切り言語では、現在サポートされていません。”

将来的に正規表現とか指定できたらいいのにね。
それにしても毎秒すごい数のツイートのなかから自分が知りたいものを教えてくれるわけだからすごいなぁって思うわけですが、やはり日本語をキーワードに指定したいなぁって思うので、将来的に正規表現とかまでできればいいなぁ~

[Node.js]LINQを使って要素を列挙して表示する。

for文使わないで列挙する。
npm linq(linq.js)を使って、要素の表示(ダンプ)は簡単に出来ます。

ベースソース

var Enumerable = require('linq');

var a = "{\"tag\" : \"aaa\" , \"res\" : [{\"key\":\"val1\"},{\"key\":\"val2\"},{\"key\":\"val3\"}]}";
var b = "{\"tag\" : \"bbb\" , \"res\" : [{\"key\":\"val2\"},{\"key\":\"val1\"},{\"key\":\"val4\"}]}";

var json_a = JSON.parse(a);
var json_b = JSON.parse(b);
var list = json_a.res.concat( json_b.res );

連想配列listに対してLINQを実行する。

◇WriteLine()のみ

Enumerable.From(list).WriteLine();

実行結果

{ key: 'val1' }
{ key: 'val2' }
{ key: 'val3' }
{ key: 'val2' }
{ key: 'val1' }
{ key: 'val4' }

◇列挙要素の指定

Enumerable.From(list).WriteLine("$.key");

実行結果

val1
val2
val3
val2
val1
val4

◇要素を並び替えて列挙

Enumerable.From(list).OrderBy("$.key").WriteLine("$.key");

実行結果

val1
val1
val2
val2
val3
val4

[Node.js]Node.jsでLINQしてみる。

npm linqパッケージの使用。
LINQ坊なので、Node.js内でLINQ(Language Integrated Query)を使用してみたいと思います。

linq – linq.js – LINQ for JavaScript library packaged for node.js
https://github.com/mihaifm/linq
これってneue.ccさんのlinq.jsをNode.js用に移植したものだったんですねー。

ということで、linqのtutorial.jsを眺めていてもなとなくわかるのですが、本家linq.jsをダウンロードして、reference.htmを実行してみると色々サンプルが載っているので参考にしてみてください。

とりあえず、個人的によく使う、Select,Where,Take,Skip,Any,Countについてのみ。
前回のエントリーで使用したRSSフィードをJSON化したものに対して、LINQ操作をしてみたいと思います。
RSS(2.0)フィードはrss-channel-itemノードが配列になっているので、この配列をベース(From)にしてLINQ操作を行います。

ベースソース

var Enumerable = require('linq');

var request = require('request');
var url = 'http://kimux.net/?feed=rss2';

request(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        var json = xml2json(body);
        /*
        jsonに対してLINQ操作
        */
    }
});

function xml2json(body) {
    return JSON.parse(require('xml2json').toJson(body));
}       

Select 1

        // Select - すべての記事のタイトルを列挙 ①
        console.log("\nSelect 1 - ");
        Enumerable.From(json.rss.channel.item)
            .Select("$.title")
            .WriteLine();

Selectにはイテレータ($)を使って要素にアクセスし目的のプロパティを取り出すやり方が基本形なのかな。

Select 2

        // Select - すべての記事のタイトルを列挙 ②
        console.log("\nSelect 2 - ");
        var titles = 
            Enumerable.From(json.rss.channel.item)
                .Select("$.title");
        titles.ForEach("console.log($)");

Selectの戻りは単純に要素の配列ではなくEnumerableとなっているので、継続してLINQ操作をすることができます。

Select 3

        // Select - すべての記事のタイトルを列挙 ③
        console.log("\nSelect 3 - ");
        Enumerable.From(json.rss.channel.item)
            .WriteLine("$.title");

単に表示させるだけならWriteLineに表示する要素を指定することも可能。
Select1,2,3 実行画面

Select 1 2 3 -
[Node.js]XMLをJSONに変換して操作する。
[C#]JSON.Netを使ってWebサービスから取得したJSONを扱うときのメモ。
[雑記]Nortonさんの延長画面がソレっぽく見える件。
[雑記]上原ひろみちゃんにWONDER AIRPORTに出て欲しかった。
[上原ひろみ]オフィシャル スマートフォンサイトオープン!
[雑記]Windows8は普及するのか。
[上原ひろみ]Solo@BlueNoteToKyo 2日目2nd!
[Webアプリ]nginxを使ってWebSocketのリバースプロキシを設定する。
[PC]VMware Player 5でUbuntuを手動インストールする方法。
[Webアプリ]WebSocketとGoogleMapsでリアルタイム監視マップを作る。その3。

Select 4

        // Select - すべての記事のタイトルを列挙 ④
        console.log("\nSelect 4 - ");
        Enumerable.From(json.rss.channel.item)
            .Select(function(value, index){
                return index + ":" + value.title;
            })
            .WriteLine();

indexを知ることも可。これは.Netよりも楽。
Select4 実行画面

Select 4 -
0:[Node.js]XMLをJSONに変換して操作する。
1:[C#]JSON.Netを使ってWebサービスから取得したJSONを扱うときのメモ。
2:[雑記]Nortonさんの延長画面がソレっぽく見える件。
3:[雑記]上原ひろみちゃんにWONDER AIRPORTに出て欲しかった。
4:[上原ひろみ]オフィシャル スマートフォンサイトオープン!
5:[雑記]Windows8は普及するのか。
6:[上原ひろみ]Solo@BlueNoteToKyo 2日目2nd!
7:[Webアプリ]nginxを使ってWebSocketのリバースプロキシを設定する。
8:[PC]VMware Player 5でUbuntuを手動インストールする方法。
9:[Webアプリ]WebSocketとGoogleMapsでリアルタイム監視マップを作る。その3。

Where

        // Where - タイトルに"雑記"を含む記事を表示
        console.log("\nWhere - ");
        Enumerable
            .From(json.rss.channel.item)
            .Where("$.title.indexOf('雑記') != -1")
            .Select("$.title")
            .WriteLine();

functionを使用することもできるけどイテレータを使うのが簡単で分かりやすい。
Where 実行画面

Where -
[雑記]Nortonさんの延長画面がソレっぽく見える件。
[雑記]上原ひろみちゃんにWONDER AIRPORTに出て欲しかった。
[雑記]Windows8は普及するのか。

ちなみに、次の様な書き方も出来る。

        // Where - タイトルに"雑記"を含む記事を表示
        console.log("\nWhere - ");
        Enumerable
            .From(json.rss.channel.item)
            .Where("p=>p.title.indexOf('雑記') != -1")
            .Select("$.title")
            .WriteLine();

Count

        // Count - タイトルに"雑記"を含む記事の件数
        console.log("\nCount - ");
        var cnt = 
            Enumerable
                .From(json.rss.channel.item)
                .Count("$.title.indexOf('雑記') != -1");
        console.log("Count = " + cnt);

LINQはWhereを使用しないで、Countに直接条件を書くことができます。
Count 実行画面

Count -
Count = 3

Any

        // Any - タイトルに"雑記"を含む記事の有無
        console.log("\nAny - ");
        var any = 
            Enumerable
                .From(json.rss.channel.item)
                .Where("$.title.indexOf('雑記') != -1")
                .Any();
        console.log("Any = " + any);

Any 実行画面

Any -
Any = true

Take

        // Take - 最新記事2件のタイトルを表示
        console.log("\nTake - ");
        Enumerable
            .From(json.rss.channel.item)
            .Take(2)
            .Select(function(value, index){
                return index + ":" + value.title;
            })
            .WriteLine();

Take 実行画面

Take -
0:[Node.js]XMLをJSONに変換して操作する。
1:[C#]JSON.Netを使ってWebサービスから取得したJSONを扱うときのメモ。

Skip

        // Skip - 最新記事2件目以降のタイトルを表示
        console.log("\nSkip - ");
        Enumerable
            .From(json.rss.channel.item)
            .Skip(2)
            .Select("$.title")
            .WriteLine();

Skip 実行結果

Skip -
[雑記]Nortonさんの延長画面がソレっぽく見える件。
[雑記]上原ひろみちゃんにWONDER AIRPORTに出て欲しかった。
[上原ひろみ]オフィシャル スマートフォンサイトオープン!
[雑記]Windows8は普及するのか。
[上原ひろみ]Solo@BlueNoteToKyo 2日目2nd!
[Webアプリ]nginxを使ってWebSocketのリバースプロキシを設定する。
[PC]VMware Player 5でUbuntuを手動インストールする方法。
[Webアプリ]WebSocketとGoogleMapsでリアルタイム監視マップを作る。その3。

まとめ
.NetFrameworkのLINQを知っていれば、すぐにマスターできるのでは。LINQを使えばループと条件分岐を大幅に減らすことができるのでコードが短く書けますしね。

[Node.js]XMLをJSONに変換して操作する。

RSSフィード(XML)をJSON化して操作してみる。
Node.jsでXMLをJSONに変換して読み込ませてみます。

使用環境は次の通り。
Node.js version v0.8.16
npm request   // httpリクエスト用
npm xml2json // XML->JSON変換用

RSSフィードを取得して、記事タイトルと投稿日時を表示する。
本サイトのRSSフィードをhttpリクエストで取得し、XMLをJSONに変換後、記事のタイトルと投稿日時を列挙させてみます。

RSSフィード(RSS2.0)の一部

<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" version="2.0">
<channel>
<title>Kimux.Net</title>
<atom:link href="http://kimux.net/?feed=rss2" rel="self" type="application/rss+xml"/>
<link>http://kimux.net</link>
<description>プログラムと趣味と。</description>
<lastBuildDate>Wed, 10 Apr 2013 01:12:51 +0000</lastBuildDate>
<language>ja</language>
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<generator>http://wordpress.org/?v=3.5.1</generator>
<item>
<title>[C#]JSON.Netを使ってWebサービスから取得したJSONを扱うときのメモ。</title>
<link>http://kimux.net/?p=1238</link>
<comments>http://kimux.net/?p=1238#comments</comments>
<pubDate>Mon, 08 Apr 2013 08:32:35 +0000</pubDate>
<dc:creator>m-kimura</dc:creator>
<category>
<![CDATA[ C# ]]>
</category>
<category>
<![CDATA[ プログラム ]]>
</category>
<guid isPermaLink="false">http://kimux.net/?p=1238</guid>
<description>
<![CDATA[
Webサービスが提供するWebAPIからJSON形式で情報をもらう。 今やWebAPIなどの受け渡しに使うのはJSONが当たり前になっています。 そこで、C#でWebAPIなどからJSON形式の文字列を受信した時の扱いに &#8230; <a href="http://kimux.net/?p=1238">続きを読む <span class="meta-nav">&#8594;</span></a>
]]>
</description>
<content:encoded>

getRss.js

var request = require('request');
var url = 'http://kimux.net/?feed=rss2';

request(url, function (error, response, body) {
    if (!error && response.statusCode == 200) {
        var json = xml2json(body);

        for (var i in json.rss.channel.item) {
            console.log("title:" + json.rss.channel.item[i].title);
            console.log("date :" + json.rss.channel.item[i].pubDate);
        }
    }
});

function xml2json(body) {
    return JSON.parse(require('xml2json').toJson(body));
}

実行結果

$ node getRss.js
title:[C&amp;#35;]JSON.Netを使ってWebサービスから取得したJSONを扱うときのメモ。
date :Mon, 08 Apr 2013 08:32:35 +0000
title:[雑記]Nortonさんの延長画面がソレっぽく見える件。
date :Tue, 02 Apr 2013 17:04:40 +0000
title:[雑記]上原ひろみちゃんにWONDER AIRPORTに出て欲しかった。
date :Sat, 30 Mar 2013 18:59:47 +0000
title:[上原ひろみ]オフィシャル スマートフォンサイトオープン!
date :Fri, 29 Mar 2013 11:30:36 +0000
title:[雑記]Windows8は普及するのか。
date :Thu, 28 Mar 2013 06:36:38 +0000
title:[上原ひろみ]Solo@BlueNoteToKyo 2日目2nd!
date :Sat, 23 Mar 2013 16:48:40 +0000
title:[Webアプリ]nginxを使ってWebSocketのリバースプロキシを設定する。
date :Fri, 22 Mar 2013 06:21:02 +0000
title:[PC]VMware Player 5でUbuntuを手動インストールする方法。
date :Fri, 22 Mar 2013 04:27:16 +0000
title:[Webアプリ]WebSocketとGoogleMapsでリアルタイム監視マップを作る。その3。
date :Thu, 21 Mar 2013 15:30:10 +0000
title:[Webアプリ]WebSocketとGoogleMapsでリアルタイム監視マップを作る。その2。
date :Tue, 19 Mar 2013 15:09:11 +0000

XMLの属性と要素はJSONに変換するとどうなるのか。
属性値と要素が含まれるXMLをxml2jsonでJSONに変換すると、ノードの最後に要素が格納されます。

JSON変換後の一部。

{"rss":
    {
        "version":2,
        "xmlns:content":"http://purl.org/rss/1.0/modules/content/",
        "xmlns:wfw":"http://wellformedweb.org/CommentAPI/",
        "xmlns:dc":"http://purl.org/dc/elements/1.1/",
        "xmlns:atom":"http://www.w3.org/2005/Atom",
        "xmlns:sy":"http://purl.org/rss/1.0/modules/syndication/",
        "xmlns:slash":"http://purl.org/rss/1.0/modules/slash/",
        "channel":
            {"title":"Kimux.Net","atom:link":{"href":"http://kimux.net/?feed=rss2","rel":"self","type":"application/rss+xml"},"link":"http://kimux.net","description":"プログラムと趣味と。","lastBuildDate":"Wed, 10 Apr 2013 01:12:51 +0000","language":"ja","sy:updatePeriod":"hourly","sy:updateFrequency":1,"generator":"http://wordpress.org/?v=3.5.1",
    

XMLパースが面倒だったので、なんかJSONにしたら楽なのかなぁって思ったから、ちょっと試してみたけど、マジ楽だったw

[C#]JSON.Netを使ってWebサービスから取得したJSONを扱うときのメモ。

Webサービスが提供するWebAPIからJSON形式で情報をもらう。
今やWebAPIなどの受け渡しに使うのはJSONが当たり前になっています。
そこで、C#でWebAPIなどからJSON形式の文字列を受信した時の扱いについてメモ。JSONの扱いにはJSON.Netを使用します。
ここでは、JSON.Netを使用してJSON文字列を連想配列により操作する方法とユーザー定義(クラス定義)に置き換える方法を試します。

JSON.Net
http://json.codeplex.com/

実際のWebサービスを使って試す。
HeartRailsExpressのWebAPIを使用して全国の鉄道情報をJSONで受け取り、C#フォームアプリケーションで使用したいと思います。

HeartRailsExpress – API
http://express.heartrails.com/api.html

今回使用するAPIは、
都道府県情報取得 API,路線情報取得 API,駅情報取得 APIの3つです。

やりたいこと。
・全国の路線、駅情報を提供するWebAPIからJSONを取得。
・WebAPIを使用して、都道府県名の列挙。
・WebAPIを使用して、都道府県別の路線情報の列挙。
・WebAPIを使用して、路線情報から駅情報の取得。
・駅情報を利用して地図上に位置を表示する。

実行画面

※このアプリケーションでは取得したJSON文字列とJSON文字列から生成したXML(XDocument)をTextBoxにそれぞれ表示しています。

連想配列によるデータの操作。
JSONの連想配列によるデータの操作はJSON.Netのデシリアライズ関数(JsonConvert.DeserializeObject関数)にNewtonsoft.Json.Linq.JContainer型を返すように指定します。

◇都道府県名の列挙。路線情報の列挙。
都道府県情報APIは次のJSONを返します。

{
    "response" : {
        "prefecture" : ["北海道","青森県",・・・,"沖縄県"]
    }
}       

JSON.Netを使ってNewtonsoft.Json.Linq.JContainer型に変換しリストボックスに表示します。
※argにはWebAPIから取得したJSON文字列が入っています。

                    var jobj = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JContainer>(arg);
                    listBoxKen.Items.AddRange(jobj["response"]["prefecture"].ToArray());

路線情報は都道府県名の列挙を行うやりかたと同じです。

                    var jobj = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JContainer>(arg);
                    listBoxRosen.Items.AddRange(jobj["response"]["line"].ToArray());

◇駅情報の列挙。
駅情報には緯度経度、前の駅名、次の駅名など様々な情報が含まれます。

{
    "response" : {
        "station" : [
             {"x":139.738535,"next":"大崎","prev":"田町","y":35.628135,"line":"JR山手線","postal":"1080075","name":"品川","prefecture":"東京都"},
             {"x":139.728246,"next":"五反田","prev":"品川","y":35.61994,"line":"JR山手線","postal":"1410032","name":"大崎","prefecture":"東京都"}
        ]
    }
}       

駅情報の中から駅名(name)だけを列挙し、リストボックスに表示します。

                    var jobj = JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JContainer>(arg);
                    listBoxEki.Items.AddRange(jobj["response"]["station"].Select(p=>p["name"]).ToArray());

ユーザー定義(クラス定義)によるデータの操作。
JSON文字列をユーザー定義型に変換するにはクラス定義が必要になります。クラスを定義する際の注意すべきことはJSONのキーとクラスのプロパティ名が完全一致、JSONの値とクラスの型が変換可能である、ということが重要です。

◇クラス定義

    class MyJsonFormat
    {
        /// <summary>
        /// 都道府県情報
        /// </summary>
        public class PrefectureResponse
        {
            public class Prefecture
            {
                public string[] prefecture { get; set; }
                public Prefecture()
                {
                    prefecture = new string[] { };
                }
            }
            public Prefecture response { get; set; }

            public PrefectureResponse()
            {
                response = new Prefecture();
            }
        }

        /// <summary>
        /// 路線情報
        /// </summary>
        public class LineResponse
        {
            public class Line
            {
                public string[] line { get; set; }
                public Line()
                {
                    line = new string[] { };
                }
            }

            public Line response { get; set; }
            public LineResponse()
            {
                response = new Line();
            }
        }

        /// <summary>
        /// 駅情報
        /// </summary>
        public class StationResponse
        {
            public class Station
            {
                public double x{get;set;}
                public string next{get;set;}
                public string prev{get;set;}
                public double y { get; set; }
                public string line { get; set; }
                public string postal { get; set; }
                public string name { get; set; }
                public string prefecture { get; set; }

                public Station()
                {
                    x = y = 0;
                    next = prev = line = postal = name = prefecture = "";
                }
            }

            public class Stations
            {
                public Station[] station { get; set; }

                public Stations()
                {
                    station = new Station[] { };
                }
            }

            public Stations response { get; set; }

            public StationResponse()
            {
                response = new Stations();
            }
        }
    }

◇都道府県名の列挙。路線情報の列挙。
JSON.Netのデシリアライズ関数に定義したPrefectureResponseクラスを指定します。PrefectureResponseクラスのインスタンスから都道府県名(prefecture)を取り出し、リストボックスに表示します。

                    var jobj = JsonConvert.DeserializeObject<MyJsonFormat.PrefectureResponse>(arg);
                    listBoxKen.Items.AddRange(jobj.response.prefecture);

路線情報の列挙もJSONをLineResponseクラスへと変換し、路線名をリストボックスに表示します。

                    var jobj = JsonConvert.DeserializeObject<MyJsonFormat.LineResponse>(arg);
                    listBoxRosen.Items.AddRange(jobj.response.line);

◇駅情報の列挙。
駅情報の列挙もJSONをStationResponseクラスへと変換し、駅名(name)を取り出してリストボックスに表示します。

                    var jobj = JsonConvert.DeserializeObject<MyJsonFormat.StationResponse>(arg);
                    listBoxEki.Items.AddRange(jobj.response.station.Select(p => p.name).ToArray());

後のことを考えるなら、クラス定義を使用したほうがよい。
その場限りのコードであればわざわざクラス定義を書く必要性が無いかもしれませんが、ユーザー定義型に変換してしまったほうがデータの扱いが後々楽になります。

今回作成したソースはこちら

[Webアプリ]nginxを使ってWebSocketのリバースプロキシを設定する。

nginxのWebSocketのリバースプロキシはversion 1.3.13から対応。
nginx本体にWebSocketのリバースプロキシが対応したみたいなので、SSL接続まで使ってみました。

nginx.org – WebSocket proxying
http://nginx.org/en/docs/http/websocket.html

バージョンは1.3.13から対応しているようです。stable版ではないので、ソースからコンパイルします。

テストとして、nginxのリバースプロキシ機能を使用して、(http)80,(https)443でlibwebsocketsのテストサーバーに接続してみます。

パッケージダウンロードと展開(2013/03/22時点での最新)

$wget http://nginx.org/download/nginx-1.3.14.tar.gz
$tar -zxvf nginx-1.3.14.tar.gz

SSLを有効にしてコンパイル & インストール

$./configure --with-http_ssl_module
$make
$sudo make install

リバースプロキシを設定してlibwebsocketsのWebSocketサーバーに接続してみる。
ここを参考にして、WebSocketのリバースプロキシを設定します。
※ちなみに、この設定はnginx1.3.13以前のバージョンでも有効な設定であり、WebSocketをクライアントが繋ぎにいこうとするとhttpハンドシェイクがWebSocketサーバー側に伝わります。しかし、この場合はサーバーのみクライアントの接続を認識しますが、クライアント側に応答が返らない状態になります。

◇Port:80 http libwebsocketsのリバースプロキシの設定例。
設定ファイルパス(default):/usr/local/nginx/conf/nginx.conf
libwebsocketsはtest-server/libwebsockets-test-server(同PC内で起動 Port:7681で待受)を使用します。

http{
// 略
       server {
                listen 80;
                server_name localhost;

                location / {
                        proxy_pass http://localhost:7681/;
                        proxy_http_version 1.1;
                        proxy_set_header Upgrade $http_upgrade;
                        proxy_set_header Connection "upgrade";
                }
        }
}

libwebsokcets-test-serverの起動

$ ./libwebsockets-test-server
lwsts[46535]: libwebsockets test server - (C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - licensed under LGPL2.1
lwsts[46535]: Initial logging level 7
lwsts[46535]: Library version: 1.2
lwsts[46535]:  Started with daemon pid 0
lwsts[46535]:  static allocation: 5512 + (16 x 1024 fds) = 21896 bytes
lwsts[46535]:  canonical_hostname = ubuntuserver
lwsts[46535]:  Compiled with OpenSSL support
lwsts[46535]:  Using non-SSL mode
lwsts[46535]:  per-conn mem: 248 + 1328 headers + protocol rx buf
lwsts[46535]:  Listening on port 7681

nginxの起動

$sudo /usr/local/nginx/sbin/nginx -s stop  // 停止
$sudo /usr/local/nginx/sbin/nginx          // 起動

ブラウザでアクセスしてみる。
httpで接続!正常に動作します。
Img20130322145850
libwebsocketsはエイリアスに”/”のみでアクセスし、WebSocketのサブプロトコルが”/”,”dumb-increment-protocol”,”lws-mirror-protocol”で動作が変わります。(nginx.confに”root”パスを書かなくてよいのはlibwebsocketsにhttpサーバーの機能があるからです)。nginxの設定だけでサブプロトコルも引き継ぎられているようです。

アクセスがないソケットはnginxによって閉じられてしまう。
どうやら動きのないソケットはnginxによって閉じられてしまうようです。
Img20130322150035
“dumb-increment-protocol”はサーバーから一方的にデータ(カウンター値)を受信しているのに対し、”lws-mirror-protocol”は任意のタイミングで送受信が発生するので何もしないと約110秒くらいでソケットが閉じられてしまいます。今のところこのtimeoutの設定はわかりません。

◇Port:443 SSL libwebsocketsのリバースプロキシの設定例。
設定ファイルパス(default):/usr/local/nginx/conf/nginx.conf
libwebsocketsはtest-server/libwebsockets-test-server(同PC内で起動 Port:7681で待受)を使用します。

http{
// 略
        server {
                listen 443;
                server_name localhost;

                ssl on;
                ssl_certificate /home/user/work/libwebsockets/libwebsockets-1.22-chrome26-firefox18/test-server/libwebsockets-test-server.pem; // テストサーバーの証明書
                ssl_certificate_key /home/user/work/libwebsockets/libwebsockets-1.22-chrome26-firefox18/test-server/libwebsockets-test-server.key.pem; // テストサーバーの秘密鍵

                location /{
                        proxy_pass https://localhost:7681/;
                        proxy_http_version 1.1;
                        proxy_set_header Upgrade $http_upgrade;
                        proxy_set_header Connection "upgarede";
                }
        }
}

libwebsokcets-test-serverのSSL起動
※configure時に”–enable-openssl”オプションが必要。

$ ./libwebsockets-test-server -ssl
lwsts[46628]: libwebsockets test server - (C) Copyright 2010-2013 Andy Green <andy@warmcat.com> - licensed under LGPL2.1
lwsts[46628]: Initial logging level 7
lwsts[46628]: Library version: 1.2
lwsts[46628]:  Started with daemon pid 0
lwsts[46628]:  static allocation: 5512 + (16 x 1024 fds) = 21896 bytes
lwsts[46628]:  canonical_hostname = ubuntuserver
lwsts[46628]:  Compiled with OpenSSL support
lwsts[46628]:  Using SSL mode
lwsts[46628]:  per-conn mem: 248 + 1328 headers + protocol rx buf
lwsts[46628]:  Listening on port 7681

nginxの起動

$sudo /usr/local/nginx/sbin/nginx -s stop  // 停止
$sudo /usr/local/nginx/sbin/nginx          // 起動

ブラウザでアクセスしてみる。
httpsで接続!!正常に動作します。
Img20130322151711
ちなみに、動きの無いソケットが閉じられるのはhttpと同じ現象。

[Webアプリ]WebSocketとGoogleMapsでリアルタイム監視マップを作る。その3。

リアルタイム監視マップ作成続き。ブラウザで操作編。

やりたいこと。
・全国の拠点(工場や事務所)で発生したイベント(警報、警告など)をWebSocketとGoogleMapsを使ってリアルタイム監視したい。
・重大な警告が発生したら発生場所を示すマーカーを点滅表示させたい。
・重大な警告が発生したら警告音を鳴らしたい。尚、警告状態が収束したら警告音を止めること。
・デバッグ用にブラウザからイベントを送信できるようにしたい。←NEW

ブラウザからイベントを発生させる。
JavaScriptでJSON文字列を作成し、監視マップに反映させてみます。

実行画面(音出ます)

今回作成したソースはこちら

[Webアプリ]WebSocketとGoogleMapsでリアルタイム監視マップを作る。その2。

WebSocketを使用してのWebアプリ作成続き。
JavaScriptをあまりやったことがないので、四苦八苦しながら。。

やりたいこと。
・全国の拠点(工場や事務所)で発生したイベント(警報、警告など)をWebSocketとGoogleMapsを使ってリアルタイム監視したい。
・重大な警告が発生したら発生場所を示すマーカーを点滅表示させたい。←NEW
・重大な警告が発生したら警告音を鳴らしたい。尚、警告状態が収束したら警告音を止めること。←NEW

イベントの発生場所の点滅と警告音を鳴らしてみる。
重大な警告レベルのイベントが発生したらマップ上のマーカーを点滅させるのと、警告音を鳴らし続ける機能を追加してみる。
重大なイベントがない場合は警告音を止める。

サウンドはこちらのサイトの方のを使わせていただいた。
音楽素材/魔王魂
http://maoudamashii.jokersounds.com/

実行画面(音出ます)

今回作成したソースはこちら

[C#]WebSocketを試してみる。クライアントSSL通信編。

WebSocketでSSL通信。
この投稿ではSSL通信を行うサーバーを試したのでクライアント編。
SSL通信を行うにはWebSocketサーバーへの接続URIが”ws://”ではなく”wss://”になります。

基本的にSSLの検証が通ればソース変更の必要はなし。
C#に関わらず、各WebSocketライブラリはWebSocketの規格に準拠して作られているはずなので、”SSL/TLS Support”とかライブラリのドキュメントに書いてあればSSLが使えるのだと思います。
SSLは通信が安全であるかを証明したうえで通信を行います。.NetFrameworkなどは検証を行う仕組みが存在するので、そこが通ってしまえばソースコードの改修は要らないはずです。

ちゃんとやろうとするとちょっと面倒だし、SSLを使った開発やテストなどの目的で検証を無理やり通してしまうやり方もあるので、ここでは無理やり突破してSSL通信を試します。

WebSocket4Net編
WebSocketインスタンスのAllowUnstrustedCertificateプロパティにtrueを設定します。

            var ws = new WebSocket("wss://192.168.1.22:7681/");
            ws.AllowUnstrustedCertificate = true; // 信頼されない検証を通す

System.Net.WebSockets.ClientWebSocket編
System.Net.WebSockets.ClientWebSocketはWindows8向けです。
下記の記事などを参照に通します。

@IT – SSL通信で信頼されない証明書を回避するには?
http://www.atmarkit.co.jp/fdotnet/dotnettips/867sslavoidverify/sslavoidverify.html

接続前に検証を通すコールバックを設定します。

                    ServicePointManager.ServerCertificateValidationCallback += (s, certificate, chain, sslPolicyError)=>true; // 信頼されない検証を通す

                    await _ws.ConnectAsync(new Uri(textBox1.Text), CancellationToken.None); // WebSocketの接続

ちなみにWebSocket4Netはソケットの接続部分がラップされているので、System.Net.WebSockets.ClientWebSocketと同じ方法ではダメでした。

実行画面(左上:WebSocket4Net,左下:ClientWebSocket,右:libwebsocketsサーバー)

Windows8上でブラウザでのWebSocket(SSL)接続ができず。。。
SSL通信を無理やり通している為か、Windows8上でChrome,FireFox,IEを使用してwssによる接続を試しましたが、どれも接続完了しませんでした。
IE10に関しては無理やり突破すらできないようです。
Img20130319172309