0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

PerlでCisco Unified IP Phone Services Application

Last updated at Posted at 2024-12-22

はじめに

Cisco IP Phone 7962を買いました。
Cisco Unified IP Phone Services Applicationで画面表示をカスタマイズできるデバイスを買ったら?そう、エオルゼア時計ですね!

問題は今まで使っていたCisco IP Phone 7965はカラー液晶だったのでnode-canvasでカラー画像を生成して表示させていましたが、7962にはグレースケールフォーマット画像を渡してあげないといけません。
…が、node-canvasは現時点でグレースケールフォーマットでの画像書き出しができないとのこと。

せっかくなので、エオルゼア時間を求めるコードをPerlに移植して画像もPerlで作ってしまいましょう。

エオルゼア時間について

(初めての方もいると思うのでまずはおさらいを)

FINAL FANTASY XIV(FF14) のゲーム中では、現実時間(日本標準時)をベースとした独自の時間、エオルゼア時間が流れています。
エオルゼア時間は現実時間の 約20.57倍 のスピードで進みます。また、日本時間日曜日0時0分には、エオルゼア時間も0時0分になります。

この特性を利用することによりエオルゼア時間は、ゲーム外でも主に下記の手法にて算出することができます。

  • epoch乗算法:日本時間epoch秒を約20.57倍したものをエオルゼア時間epoch秒と扱い、エオルゼア時分を求める
  • 週epoch乗算法:日本時間の日曜日0時0分を起点とする現実時間epoch秒を約20.57倍したものをエオルゼア時間週epoch秒と扱い、エオルゼア時分を求める
  • ルックアップテーブル法:エオルゼア時間の周期性をまとめたルックアップテーブルをもとにエオルゼア時分を求める

ちなみに手法の名称は今私が勝手に付けました。

開発環境等

  • Debian GNU/Linux 12
  • Perl v5.36.0 (System Perl)
  • systemd
  • nginx
  • plack
  • Imager
  • NotoSansMono

必要条件

下記の各種パッケージ・モジュールをインストールしてください。
Debian系以外のディストリビューションをお使いの方は別途ググってください。(などと)

commands
sudo apt install fonts-noto-mono
sudo apt install cpanminus
sudo apt install libfreetype6-dev
sudo cpanm Plack::Builder Imager

エオルゼア時間をPerlで求める

以前私が投稿した記事に掲載したルックアップテーブル法を用いたエオルゼア時間算出のC++コードを下記のようにPerlに移植しました。
ついでに(?)24時間制で時を求められるように変更しました。

eorzea_time
use constant ET_LOOKUP_TABLE => [
    [0, 12342],
    [0, 24685],
    [0, 37028],
    [1,  6171],
    [1, 18514],
    [1, 30857],
    [1, 43199]
];

sub get_et {
    my ($tp) = @_;
    my $day_of_week = $tp->day_of_week;
    my $hour = $tp->hour;
    my $min = $tp->min;
    my $sec = $tp->sec;

    my $count = $day_of_week * 144 + $hour * 6 + int($min / 10);
    my $lt_index = ($count == 0) ? -1 : ($count - 1) % 7;
    my $base_is_pm  = ($lt_index == -1) ? 0 : &ET_LOOKUP_TABLE->[$lt_index]->[0]; 
    my $base_et_sec = ($lt_index == -1) ? 0 : &ET_LOOKUP_TABLE->[$lt_index]->[1];
    my $diff_from = int($min / 10) * 10;
    my $diff = ($min - $diff_from) * 60 + $sec;
    my $sec_of_et = $base_et_sec + ($diff * 20) + (($diff * 57) / 100) + ($base_is_pm ? 43200 : 0);
    my $et_min = int($sec_of_et / 60) % 60;
    my $et_hour = $sec_of_et / 3600 % 24;
    return {
        hour => $et_hour,
        min => $et_min
    };
}

Imagerで表示する画像をPSGIアプリケーションで生成する

開発中 Imager::Font でフォントをロードする際にエラーが発生しました。
libfreetype6-dev パッケージがないと FreetypeFontの読み込みがうまくいかないようです。
当該パッケージインストール後 sudo cpanm --reinstall Imager とすると、読み込みができるようになります。

この箇所のコードは Appendix:Perlコード全文 でご確認ください。
お気づきかと思いますが、いわゆるペライチPSGIアプリケーションです。

静的XMLを用意する

画像だけ変わればよいので、Application XMLは固定です。
※画像のURLでアクセスできるよう、後ほどnginxを設定します。

cisco_et.xml
<?xml version="1.0" encoding="utf-8"?>
<CiscoIPPhoneImageFile WindowMode="Wide">
<Title>Cisco IP Phone Image File</Title>
<Prompt></Prompt>
<LocationX>0</LocationX>
<LocationY>0</LocationY>
<URL>http://○○.○○.○○.○○/api2/cisco_et.png</URL>
<SoftKeyItem>
<Name>Cancel</Name>
<URL>SoftKey:Exit</URL>
<Position>3</Position>
</SoftKeyItem>
</CiscoIPPhoneImageFile>

nginxを設定する

すみません、Node.jsで作ってあるところに入れこんだので若干おかしなことになっています。
/api2/ 以下にアクセスがあった場合、PSGIアプリケーションに渡されます。

default
server {
        listen 80 default_server;
        listen [::]:80 default_server;
        root /home/user/web/static/;
        server_name _;
        location ~ ^/(.*)\.xml$ {
                add_header Refresh "1";
        }
        location /api/ {
                proxy_pass http://localhost:3000;
        }
        location /api2/ {
                proxy_pass http://localhost:5000;
        }
}

PSGIアプリケーションをデーモン化する

/etc/systemd/system/cisco_et.service
[Unit]
Description=Cisco Eorzea Image App Server

[Service]
User=user
PIDFile=/run/cisco_et.pid
WorkingDirectory=/home/user/sysperl/
Type=simple
ExecStart=/usr/local/bin/plackup /home/user/sysperl/cisco_et.psgi
ExecReload=/bin/kill -s HUP $MAINPID
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

実際の動作等

ブラウザで /api2/cisco_et.png にアクセスすると、次のような画像が表示されます。

cisco_et.png

また、PBXサーバー側でXML ApplicationのURLを設定すると実機がidle中、下記のような表示がなされます。

DSC_2818.JPG

さらに改造後

私の所属団体のRainyVillageのメンバーに見せたところ『こうしたらもっと面白くない?』となり、最終的にこうなりました。

おわりに

明日の担当は papixさん です。お楽しみに!

Appendix:Perlコード全文(改造前)

cisco_et.psgi
#!/usr/bin/env perl

# ====================================================== #
#  Copyright (c) 2024 CIB-MC                             #
#  Released under the MIT license                        #
#  https://opensource.org/licenses/mit-license.php       #
# ====================================================== #

use strict;
use warnings;
use utf8;

use Time::Piece;
use Time::Seconds;
use Plack::Builder;
use Imager;
use Imager::Color;

our $VERSION = '0.1';

use constant BLACK => Imager::Color->new(gray =>   0);
use constant WHITE => Imager::Color->new(gray => 255);
use constant IMG_WIDTH => 320;
use constant IMG_HEIGHT => 144;
use constant FONT => Imager::Font->new(
        file  => '/usr/share/fonts/truetype/noto/NotoSansMono-Bold.ttf',
        aa => 1
);
use constant FONT_SIZE_LABEL => 18;
use constant FONT_SIZE_TIME => 42;

use constant ET_LOOKUP_TABLE => [
    [0, 12342],
    [0, 24685],
    [0, 37028],
    [1,  6171],
    [1, 18514],
    [1, 30857],
    [1, 43199]
];

sub get_et {
    my ($tp) = @_;
    my $day_of_week = $tp->day_of_week;
    my $hour = $tp->hour;
    my $min = $tp->min;
    my $sec = $tp->sec;

    my $count = $day_of_week * 144 + $hour * 6 + int($min / 10);
    my $lt_index = ($count == 0) ? -1 : ($count - 1) % 7;
    my $base_is_pm  = ($lt_index == -1) ? 0 : &ET_LOOKUP_TABLE->[$lt_index]->[0]; 
    my $base_et_sec = ($lt_index == -1) ? 0 : &ET_LOOKUP_TABLE->[$lt_index]->[1];
    my $diff_from = int($min / 10) * 10;
    my $diff = ($min - $diff_from) * 60 + $sec;
    my $sec_of_et = $base_et_sec + ($diff * 20) + (($diff * 57) / 100) + ($base_is_pm ? 43200 : 0);
    my $et_min = int($sec_of_et / 60) % 60;
    my $et_hour = $sec_of_et / 3600 % 24;
    return {
        hour => $et_hour,
        min => $et_min
    };
}

sub cisco_et_png {
    my ($tp) = @_;
    my $et = get_et($tp);
    my $img = Imager->new(xsize=> IMG_WIDTH, ysize => IMG_HEIGHT, channels=>1);
    $img->box(color => WHITE, xmin => 0, ymin => 0, xmax => IMG_WIDTH, ymax => IMG_HEIGHT, filled => 1);
    $img->align_string(
        string => 'Eorzea Time',
        color => BLACK,
        x => IMG_WIDTH * 0.27,
        y => IMG_HEIGHT * 0.4,
        halign => 'center',
        valign => 'bottom',
        font => FONT,
        size => FONT_SIZE_LABEL
    );
    $img->align_string(
        string => sprintf("%02d:%02d", $et->{hour}, $et->{min}),
        color => BLACK,
        x => IMG_WIDTH * 0.27,
        y => IMG_HEIGHT * 0.5,
        halign => 'center',
        valign => 'top',
        font => FONT,
        size => FONT_SIZE_TIME
    );
    $img->align_string(
        string => 'Local Time',
        color => BLACK,
        x => IMG_WIDTH * 0.73,
        y => IMG_HEIGHT * 0.4,
        halign => 'center',
        valign => 'bottom',
        font => FONT,
        size => FONT_SIZE_LABEL
    );
    $img->align_string(
        string => sprintf("%02d:%02d", $tp->hour, $tp->min),
        color => BLACK,
        x => IMG_WIDTH * 0.73,
        y => IMG_HEIGHT * 0.5,
        halign => 'center',
        valign => 'top',
        font => FONT,
        size => FONT_SIZE_TIME
    );
    $img->write(data => \my $data, type => 'png') or die $img->errstr;
    return $data;
}

sub api2_cisco_et {
    my $tp = localtime;
    my $data = &cisco_et_png($tp);
    return ["200", ["Content-Type" => "image/png"], [$data]];
}

builder {
    mount '/api2/cisco_et.png' => \&api2_cisco_et;
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?