Excel-DNA で XLL を作る(その8)

 今回は、Excel 用の 「緊急地震速報アドイン」を作ってみます。概略としては以下のようなものです。

  1. アドインを起動すると、直近の速報内容を警報音とともにメッセージボックスに表示する。
  2. (こんな感じ↓)

  3. メッセージボックスを閉じると警報音を停止し、速報された地震の詳細を掲載したWebページを開く。
  4. (こんな感じ↓)

  5. 速報を監視して、新しい速報が出たら、警報音とともにメッセージボックスを表示する。
  6. メッセージボックスを閉じると警報音を停止し、地震の詳細を掲載したWebページを開く。
  7. 上記 3. 4.を繰り返す。

 さて、緊急地震速報の取得方法ですが・・・。
twitter で緊急地震速報を呟くアカウント(@eew_jp さん)の呟きを、TwitterAPI で監視します。TwitterAPI から JSON でデータを取得し、DynamicJson で解析して新規のものであれば、警報音とともに内容を表示します。リアルタイムな処理の手段としては、Excel-DNA で XLL を作る(その7)で作成した電力供給状況アドインと同様、RTD サーバーを利用します。ですので、コードもほぼ同じです(笑)

 さっそく、記述していきましょう。 今回は、DyanmicJson のソース(DynamicJson.cs)を一緒にビルドします(のでDLLは要りません)。DynamicJsonクラスを実装するために、System.Runtime.Serialization と System.Xml と System.Xml.Linq と Microsoft.CSharp への参照設定が必要です。DynamicJson.cs をプロジェクトフォルダにコピーしたら、ソリューションエクスプローラからプロジェクトに追加します。(DynamicJson については前回のPOST参照)
なお、一緒にビルドしますので DynamicJsonクラスに定義されている public static なメソッドも Excel に公開されてしまいます。なので、DynamicJson.cs にも using ExcelDna.Integration を追加し、public static なメソッドに、[ExcelFunctionAttribute(IsHidden=true)] 属性を追加します。
(こんな感じ↓)

/*--------------------------------------------------------------------------
* DynamicJson
* ver 1.2.0.0 (May. 21th, 2010)
*
* created and maintained by neuecc <ils@neue.cc>
* licensed under Microsoft Public License(Ms-PL)
* http://neue.cc/
* http://dynamicjson.codeplex.com/
*--------------------------------------------------------------------------*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using ExcelDna.Integration;    //  このusing句を追加する

namespace Codeplex.Data
{
    
    public class DynamicJson : DynamicObject
    {
        private enum JsonType
        {
            @string, number, boolean, @object, array, @null
        }

        // public static methods

        /// <summary>from JsonSring to DynamicJson</summary>
        [ExcelFunctionAttribute(IsHidden=true)]   //  属性を追加する
        public static dynamic Parse(string json)
        {
            return Parse(json, Encoding.Unicode);
        }

        /// <summary>from JsonSring to DynamicJson</summary>
        [ExcelFunctionAttribute(IsHidden = true)]  //  属性を追加する
        public static dynamic Parse(string json, Encoding encoding)
        {
            using (var reader = JsonReaderWriterFactory.CreateJsonReader(encoding.GetBytes(json), XmlDictionaryReaderQuotas.Max))
            {
                return ToValue(XElement.Load(reader));
            }
        }

        /// <summary>from JsonSringStream to DynamicJson</summary>
        [ExcelFunctionAttribute(IsHidden = true)]  //  属性を追加する
        public static dynamic Parse(Stream stream)
        {
            using (var reader = JsonReaderWriterFactory.CreateJsonReader(stream, XmlDictionaryReaderQuotas.Max))
            {
                return ToValue(XElement.Load(reader));
            }
        }

        /// <summary>from JsonSringStream to DynamicJson</summary>
        [ExcelFunctionAttribute(IsHidden = true)]  //  属性を追加する
        public static dynamic Parse(Stream stream, Encoding encoding)
        {
            using (var reader = JsonReaderWriterFactory.CreateJsonReader(stream, encoding, XmlDictionaryReaderQuotas.Max, _ => { }))
            {
                return ToValue(XElement.Load(reader));
            }
        }

        /// <summary>create JsonSring from primitive or IEnumerable or Object({public property name:property value})</summary>
        [ExcelFunctionAttribute(IsHidden = true)]  //  属性を追加する
        public static string Serialize(object obj)
        {
            return CreateJsonString(new XStreamingElement("root", CreateTypeAttr(GetJsonType(obj)), CreateJsonNode(obj)));
        }
・・・以下略

 つぎに、TwitterAPI をつかって、@eew_jp さんの呟きを取得し、速報内容と参照URLを保持する「Dataクラス」を記述します。今回利用した TwitterAPI の詳細についてはこちらをご覧ください。前回は動的オブジェクトの変数に var を使いましたが、C#2010 の流儀に従って、dynamic を利用しました(笑)
(こんな感じ↓)

using System;
using Codeplex.Data;   // DynamicJsonクラスの名前空間
using System.Net;    // WebClient クラスの利用に必要
using ExcelDna.Integration;    // 関数、マクロの属性を設定するのに必要
using System.Media;    // 音源の再生に必要
using System.Diagnostics;    // Webページの表示に必要

public static class Data
{
    public static SoundPlayer sp = new SoundPlayer("alert.wav"); // 音源(alert.wav)を鳴らす
    public static string warning;   // 速報内容を保持
    public static string url;    // 速報の地震詳細を掲載したサイトのURLを保持

    [ExcelFunctionAttribute(IsHidden = true)]  // 関数を隠す
    public static bool Get()
    {
        try
        {
            dynamic sol = new WebClient().DownloadString(@"https://api.twitter.com/1/users/show/eew_jp.json");
            dynamic status = DynamicJson.Parse(sol);
            foreach (dynamic item in status)
            {
                if (item.Key != null && item.Key == "status") //  status を抽出
                {
                    if (warning != item.Value.text)    //  text を抽出
                    {
                        warning = item.Value.text;    //  text を保持
                        url = warning.Remove(0,warning.IndexOf("http:"));   
                        url = url.Remove(url.IndexOf(" #"));    //  URLを抽出
                        string msg = warning.Remove(warning.IndexOf("http:"));    //  メッセージ生成
                        sp.PlayLooping();    //  警報音を鳴らす
                        MessageBox.Show(msg, "地震速報");    //  メッセージ表示
                        sp.Stop();    //  警報音停止
                        Process process = new Process();
                        Uri uri = new Uri(url);    // url(文字列)をUriにパース
                        process.StartInfo.FileName = uri.ToString();   // uri から URL文字列に
                        process.Start();    //  Webページ表示
                        return true;
                    }
                }
            }
            return true;
        }
        catch (Exception e)
        {
            MessageBox.Show(e.ToString());
            return false;
        }
    }
}

 つづいて、RTD サーバーを記述します(前回とほぼ同じです)。ただし、TwitterAPI の制限によって、150回/時 の縛りがあるので(将来は大幅に緩和されるらしいですが)監視インターバルは25秒ごとに設定します。これ以上頻繁にアクセスしてもキャッシュが返されるだけですので意味がないばかりでなく、制限をオーバーするとアクセス停止になってしまいますので(笑)
(こんな感じ↓)

using ExcelDna.Integration.Rtd;    // RTDサーバーを利用するのに必要
using System.Windows.Forms;    // Timer を利用するのに必要

public class EarthQuakeServer : IRtdServer
{
    private IRTDUpdateEvent _callback;
    private Timer _timer;
    private int _topicId;
    private const int INTERVAL = 25000;  //  監視インターバル(25秒)

    #region IRtdServer Members
    public object ConnectData(int topicId, ref Array Strings, ref bool GetNewValues)
    {
        _topicId = topicId;
        _timer.Start();
        return 1;
    }
    public void DisconnectData(int topicId)
    {
        _timer.Stop();
    }
    public int Heartbeat()
    {
        return 1;
    }
    public Array RefreshData(ref int topicCount)
    {
        Data.Get();
        object[,] results = new object[2, 1];
        results[0, 0] = _topicId;
        results[1, 0] = GetTime();
        topicCount = 1;
        _timer.Start();
        return results;
    }
    public int ServerStart(IRTDUpdateEvent CallbackObject)
    {
        Data.Get();
        _callback = CallbackObject;
        _timer = new Timer();
        _timer.Tick += Callback;
        _timer.Interval = INTERVAL;
        return 1;
    }
    public void ServerTerminate()
    {
        if (_timer != null)
        {
            _timer.Dispose();
            _timer = null;
        }
    }
    private string GetTime()
    {
        return DateTime.Now.ToString("HH:mm:ss.fff");
    }
    private void Callback(object sender, EventArgs e)
    {
        _timer.Stop();
        _callback.UpdateNotify();
    }
    #endregion
}

最後に、Excel から呼ばれる関数&コマンドを記述します。
今回はワークシート関数と、マクロコマンドを定義してみました(笑)
(こんな感じ↓)

using ExcelDna.Integration;

public static class EarthQuakeMethod
{
    [ExcelCommand(MenuName = "&地震速報", MenuText = "&警報を有効にする")]
    public static void EarthQuakeAlert()
    {
        object x = XlCall.RTD("EarthQuakeServer", null, "");
    }
    [ExcelFunctionAttribute(Description="",Name="地震速報取得")]
    public static string EarthQuakeAlertFunction()
    {
        object x = XlCall.RTD("EarthQuakeServer", null, "");
        return "最終アクセス=" + DateTime.Now.ToString("hh:mm:ss") + Data.url;
    }
}

これで完成です(笑)

あ・・・alert.wav という名前の音源を用意して、dll,dna,xll と同じフォルダに入れておく必要があります。
どんな音源でも良いのですが・・・リアリティのある音源がお勧めです。個人的には某放送協会の音源が良いかと(笑)
緊急地震速報 音源 mp3」で、検索すると見つかると思います。リアリティあり過ぎかな?
mp3 フォーマットでダウンロードして、午後のこ~だ などで wav ファイルに変換します♪ (※利用にあたっては著作権に留意しましょう)

でもこれ・・・無理に Excel アドインにしなくても・・・なんて野暮な突っ込みは禁止です!(笑)

では、また(笑)

今回作成したプロジェクトのソースはこちら

This entry was posted in Excel, .NetFramework and tagged , , . Bookmark the permalink.

2 Responses to Excel-DNA で XLL を作る(その8)

  1. 通すがり says:

    参考になりました

Leave a Reply

Your email address will not be published. Required fields are marked *