« 2020年8月 | トップページ | 2020年10月 »

2020年9月

2020年9月27日 (日)

restlet serverを立ち上げる

今から6年程前に結構一生懸命になってデモ用のRestletサーバーを構成したことがある。その頃のことを思い出して、久々にRestletサーバーを試してみた。目的はRaspberry Piの話し相手の作成。

環境は以下のとおり:
Centos 7.8
Java 1.8.0
Restlet 2.4.3

Step-1
Javaのインストール
# yum install java-1.8.0-openjdk java-1.8.0-openjdk-devel

Step-2
Restletのインストール
以下からzipをダウンロードする。
https://restlet.talend.com/downloads/current/
展開先は$HOME/workにした。

Step-3
環境変数の設定
Homeの.bash_profileに以下を設定する
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.262.b10-0.el7_8.x86_64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar:$HOME/work/restlet-jse-2.4.3/lib/org.restlet.jar:$home/work
なお、Class編集を含むコーディング環境は$home/workとしている。
備忘録として、、、上記環境変数の即時有効化には以下のコマンドを実行。
source ./.bash_profile

Step-4
コードの作成
作ったJAVAコードは以下の3つ:
ServerMain.java:Port8010でServerを立ち上げ、Dispatcherを呼ぶ
ServerDispatcher.java:コマンドに従ってFunctionを呼ぶ(この時点ではDataInpuのみを呼び出している)
DataInput.java:Dispatcherに呼び出されるFunction(中身はこの時点では空)

以下がソースコード。昔のコードを引っ張りだして今時点で不要な部分はざっくりとそぎ落としてあるけれどもimportはそのままなので、余分なライブラリがImportされているかも(だろう)。

ServerMain.java

import org.restlet.Component;
import org.restlet.data.Protocol;
import org.restlet.resource.ServerResource;

public class ServerMain extends ServerResource{
public static void main(String[] argsthrows Exception {
    // Create a new Component.
    Component component = new Component();

    // Add a new HTTP server listening on port 8010.
    component.getServers().add(Protocol.HTTP8010);

    // Attach the sample application.
    component.getDefaultHost().attach("/command",
            new ServerDispatcher());

    // Start the component.
    component.start();
   }

ServerDispatcher.java

import org.restlet.Application;
import org.restlet.Restlet;
import org.restlet.routing.Router;

public class ServerDispatcher extends Application {

    /**
     * Creates a root Restlet that will receive all incoming calls.
     */
    @Override
    public synchronized Restlet createInboundRoot() {
        // Create a router Restlet that routes each call to a new instance
        Router router = new Router(getContext());

        // Defines only one route
        router.attach("/datain/{keyword}"DataInput.class);
  
        return router;
    }
}

DataIn.java

import org.restlet.resource.Get;
import org.restlet.resource.ServerResource;
import org.restlet.Restlet;
import org.restlet.Client;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.MediaType;

 /**
 * Resource which has only one representation.
 * 
 */
public class DataInput extends ServerResource {

    public static int count;
            
    @Get
    public String represent() {

        String str = getReference().getPath();
        System.out.println( str );
        int index = str.indexOf("/datain/");
        index += "/datain/".length();
        String indata =  str.substring(index);
        System.out.println("input :" + indata );

    return "return from datain";

    }
}

 

それぞれのJavaコードをコンパイルしてClassファイル化しておく。
以下、ServerMainの実行。クライアントからのコマンドの実行結果(19:58:07)が出力されている。インプットデータはtestと表示している。

# java ServerMain
Starting the internal [HTTP/1.1] server on port 8010
Starting ServerDispatcher application
/command/datain/test
input :test
2020-09-27 19:58:07 192.168.1.8 - - 8010 GET /command/datain/test - 200 18 0 56 http://192.168.1.62:8010 curl/7.55.1 -

上記出力をさせた、実際にクライアントから実行したコマンドは以下。testをパラメータとして渡している。

>curl http://192.168.1.62:8010/command/datain/test
return from datain
>

これで基本形が出来たことになる(と言うか昔のコードを復活したことになる)。

次のステップはRaspberry Piからコマンド(多分温度計の値)を送ってみたいと思う。
で、今日はここまで。

 

2020年9月 7日 (月)

WindowsでのボリュームのUUID取得方法

mountvolコマンドがボリュームごとのUUIDを教えてくれる。以下、実際に実行した結果。

> mountvol

MOUNTVOL [ドライブ:]パス /L
MOUNTVOL [ドライブ:]パス /P
MOUNTVOL /R
MOUNTVOL /N
MOUNTVOL /E
MOUNTVOL drive: /S

パス マウント ポイントを常駐させる既存の NTFS ディレクトリ
を指定します
ボリューム名
マウント ポイントのターゲットとなるボリューム名を指定しま
す。
/D 指定されたディレクトリからボリューム マウント ポイント
を削除します。
/L 指定されたディレクトリのマウントされているボリューム
の一覧を表示します。
/P 指定されたディレクトリからボリューム マウント ポイントを削除
してボリュームをマウント解除し、ボリュームをマウントできな
くします。
ボリューム マウント ポイントを作成して、もう一度ボリュームを
マウントできるようにします。
/R システムに存在しないマウント ポイント ディレクトリとレジストリ
設定を削除します。
/N 新しいボリュームの自動マウントを無効にします。
/E 新しいボリュームの自動マウントを再び有効にします。
/S EFI システム パーティションを与えられたドライブにマウントします。

現在のマウント ポイントとボリューム名の考えられる値:

\\?\Volume{d8c96858-013e-448c-88c0-aad7073adb6c}\
C:\

\\?\Volume{d6814e02-d6cf-4770-a63c-7326713e8cc5}\   << 復旧ディスクイメージか?
*** マウント ポイントなし ***

\\?\Volume{a703afbb-9e7f-49f9-8853-78e9c6799f32}\
D:\

\\?\Volume{6f058994-0000-0000-0004-000000000000}\  << USB HDD
I:\

\\?\Volume{0a58b3d2-0000-0000-0000-100000000000}\  << USB HDD
J:\

\\?\Volume{306a6679-b98e-4d50-9ab8-23c80712d71f}\
*** マウント ポイントなし ***

\\?\Volume{9140591b-20d1-11ea-90ad-806e6f6e6963}\   << DVDドライブ
E:\

\\?\Volume{6313d6d5-263b-11ea-90b4-005056c00008}\   << 仮想DVDドライブ
G:\

\\?\Volume{8f7a1c8c-e53e-11ea-90cd-806e6f6e6963}\   <<仮想DVDドライブ
F:\

 

2020年9月 6日 (日)

Raspberry Pi 4 Model でWebIOPiを動かす

WebIOPiをインストールしてみた。

Googleとダウンロード先は見つかるけれども、結構古い。ここが曲者。
Blue Backsのラズパイの教科書ではpatchを当てるよとある。

で、以下を実行。

pi@raspberrypi:~/raspi/WebIOPi-0.7.1 $ wget https://raw.githubusercontent.com/doublebind/raspi/master/webiopi-pi2bplus.patch
--2020-09-06 16:24:16-- https://raw.githubusercontent.com/doublebind/raspi/master/webiopi-pi2bplus.patch
raw.githubusercontent.com (raw.githubusercontent.com) をDNSに問いあわせています... 151.101.108.133
raw.githubusercontent.com (raw.githubusercontent.com)|151.101.108.133|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 9308 (9.1K) [text/plain]
`webiopi-pi2bplus.patch' に保存中

webiopi-pi2bplus.patch 100%[=========================================>] 9.09K --.-KB/s 時間 0.004s

2020-09-06 16:24:16 (2.06 MB/s) - `webiopi-pi2bplus.patch' へ保存完了 [9308/9308]

pi@raspberrypi:~/raspi/WebIOPi-0.7.1 $ patch -p1 -i webiopi-pi2bplus.patch
patching file htdocs/webiopi.js
patching file python/native/cpuinfo.c
patching file python/native/gpio.c
patching file python/webiopi/utils/version.py
patching file python/webiopi/protocols/http.py
patching file python/webiopi/utils/thread.py

幾つかのファイルにパッチが当たったようだ。この状態でsetup.shを実行した。
その後、教科書に書いているように以下を実行。

wget https://raw.githubusercontent.com/neuralassembly/raspi/master/webiopi.service
sudo mv webiopi.service /etc/systemd/system

実際上記を実行していないと、webiopiは起動エラーになった。中身がよくわからないけれども、必須であることは理解した。で、webiopiの起動をおこなう。

$ sudo service webiopi start 

$ sudo systemctl status webiopi を見る限り、正常に起動はしているようだ。しかし、表示がおかしい。各GPIOのIN/OUTが四角い箱の中に表示されない。端子番号をクリックしても反応がない。

 

Webioping

Googleって先人の知恵に学ぼうとしたけれども、パッチを当てる前のファイルに対する処理方法しか見つけることができず、知恵に学ぶことができない。そもそも先人の知恵ではWebIOPiの0.7.1は24ピン対応で40ピン対応になっていないから、そこの部分からの対応処理になっているけれども、パッチは40ピン対応のもので、その部分は対応不要となっているようだ。で、CPUの判別の所に絞ってコードを見てみた。
どうやらハードコーディングされているCPUモデル以外はRevision=NULLになるようだ。以下を叩いてCPUモデルを取り出すと、、、
cat /proc/cpuinfo
BCM2711であることが分かる。Revisionはc03112だ。get_rpi_revision()的には結構すっとんでるRevisionのようだけれども、これってRaspberry Pi 4の4のことかもしれない。ということで、以下にコードを書き換えて setup.shを実行した。

ソースファイル WebIOPi-0.7.1/python/native/cpuinfo.c 

char *get_cpuinfo_revision(char *revision)
{
  FILE *fp;
  char buffer[1024];
  char hardware[1024];
  int rpi_found = 0;

  if ((fp = fopen("/proc/cpuinfo", "r")) == NULL)
    return 0;

  while(!feof(fp)) {
    fgets(buffer, sizeof(buffer) , fp);
    sscanf(buffer, "Hardware : %s", hardware);
    if (strcmp(hardware, "BCM2708") == 0)
      rpi_found = 1;
    else if (strcmp(hardware, "BCM2709") == 0)
      rpi_found = 1;
    else if (strcmp(hardware, "BCM2835") == 0)
      rpi_found = 1;
    else if (strcmp(hardware, "BCM2711") == 0) /* 追加部分 */
      rpi_found = 1;                                            /* 追加部分 */
    sscanf(buffer, "Revision : %s", revision);
  }
  fclose(fp);

  if (!rpi_found)
    revision = NULL;
  return revision;
}

int get_rpi_revision(void)
{
  char revision[1024] = {'\0'};

  if (get_cpuinfo_revision(revision) == NULL)
    return -1;

  if ((strcmp(revision, "0002") == 0) ||
      (strcmp(revision, "1000002") == 0 ) ||
      (strcmp(revision, "0003") == 0) ||
      (strcmp(revision, "1000003") == 0 ))
    return 1;
  else if ((strcmp(revision, "0004") == 0) ||
             (strcmp(revision, "1000004") == 0 ) ||
             (strcmp(revision, "0005") == 0) ||
             (strcmp(revision, "1000005") == 0 ) ||
             (strcmp(revision, "0006") == 0) ||
             (strcmp(revision, "1000006") == 0 ))
    return 2;
  else if ((strcmp(revision, "a01041") == 0) ||
             (strcmp(revision, "a21041") == 0 ))
    return 3;
  else // assume rev 4
    return 4;
}

変更は正解だったようで、WebIOPiは以下を表示してくれた。Pin 22であるGPIO 25に繋がっているLEDもピン番号をクリックすることでON/OFF出来ることを確認。

Webiopiok

まぁ、ハードウエアの状態を操作・表示するアプリな訳だから、ハードウエア・モデルの確認は必須だわな。ということで、分かってしまえば「当たり前だろ」って言われそうな結末とも言える。

なんだかんだで4時間くらいかかったかなぁ。。。。

Raspberry Pi チャタリング問題

Raspberry Piでモールス練習機(電鍵で打つ方)を作ろうとおもった。案外簡単かと思ったら思わぬところでつまずいている。
思わぬところと言うのは多分正しくなくて、想定していた事が起きてるんだろうけれど、その想定が範囲を超えているってことだろうか。

問題はチャタリング対策。

以下のコードで実験してみた。電鍵が押されているときはLED点灯、電鍵を戻すと消灯。それぞれの時間幅を測定する。24番チャネルの両エッジでCallbackが呼ばれるように設定。Bouncetime=150。ゆっくりだとこれで(ほぼ)問題なく動く。

import RPi.GPIO as GPIO
from time import sleep
import time

def my_callback(channel):
    global ledstate
    global last_time

    ut = time.time()

    if channel==24:
        ledstate = not ledstate
     if ledstate == GPIO.HIGH:
            GPIO.output(25, GPIO.HIGH)
            print("HIGH")
            print(ut-last_time)
            last_time = ut

        else:
            GPIO.output(25, GPIO.LOW)
            print("LOW ")
            print(ut-last_time)
            last_time = ut

GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(24, GPIO.BOTH, callback=my_callback, bouncetime=150)

ledstate = GPIO.LOW
last_time = 0

try:
    while True:
        sleep(0.01)

except KeyboardInterrupt:
    pass

GPIO.cleanup()

しかし、実際に電鍵を叩いてみると短点は案外短く、普通に打っても100ms以下になる。で、Bouncetimeを短くするとチャタリングを拾ってしまう。つまり、電鍵ってのはチャタリングが多発するけれども、それをアナログ信号として扱うからチャタリングが多少発生しても苦にならない使い方をしてきたから問題ないわけで、これをデジタル的に処理しようとするとローパスフィルター的な仕組みを組み込まないと使い物にならないってことかと。

なかなか奥が深い。

あと、RISING・FALLINGのそれぞれのエッジでCallbackが呼ばれるように作ってみたけれども(以下サンプルコード)、これはこれで大きな問題があった。たとえばそれぞれのbouncetimeを200msに設定してあったとしても、RISINGエッジ処理にはbouncetimeはゆうこうだけれどもRISINGエッジ処理中にFALLINGエッジが発生するとRISINGエッジ処理のbouncetimeとは無関係に即時的に処理される。なので交互にのんびり処理するつもりが訳のわからないことになる。

def my_callback_rise(channel):
   global event_time
   ut = time.time()

   if channel==24:
      GPIO.output(25, GPIO.HIGH)
      event_time = ut

def my_callback_fall(channel):
   global event_time
   ut = time.time()

   if channel==23:
      GPIO.output(25, GPIO.LOW)
      event_time = ut - event_time
      print(event_time)


GPIO.setmode(GPIO.BCM)
GPIO.setup(25, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(24, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.add_event_detect(24, GPIO.RISING, callback=my_callback_rise, bouncetime=200)
GPIO.add_event_detect(23, GPIO.FALLING, callback=my_callback_fall, bouncetime=200)

2020年9月 4日 (金)

Linux:SCSIアドレスとデバイス名の関係

ストレージの移行をシュミレーションしてみた。

サーバーに接続してるストレージ装置をいったん切り離して、なんだかんだで再接続することを想定。ここでの”なんだかんだ”には新旧ストレージ装置間のマイグレーションなんかも含む想定(つまり中身は全く同じ2つのストレージの入れ替え作業)。基本的にSCSIアドレスは変えない想定なれど、場合によってはTARGET IDが変わったり、LUNの順番が変わったりする場合もある。そんなこんなを想定して、以下実験結果。

先に結論を言うと、

  • ストレージのLUN順番は変えないのが原則。TARGET IDは変わってもよい。
  • blkidコマンドで各ストレージのUUIDを確認することで、デバイス名の変化有無を確認すること。

実験結果

環境
ESXiで仮想マシーンを作成(CentOS 7.8)
ディスクを追加(1GBを5つ)し、SCSIアドレスを変更することで以下を実験。

1. 初期disk配置とボリューム作成
disk1 (1:0) -> /dev/sdb << パーティションに直接mkfs (物理ボリュームは作成せず)
disk2 (1:1) -> /dev/sdc -+---- lv-cde1 << LVM上の論理ボリューム
disk3 (1:2) -> /dev/sdd -+-+
disk4 (1:3) -> /dev/sde -+-+-- lv-cde2 << LVM上の倫理ボリューム
disk5 (1:4) -> /dev/sdf << パーティションを作成しないまま

sdc, sdd, sdeでVolume Groupを作成し、Logical Volumeを2つ作成した。容量が1GBと小さいためか、それぞれsdcとsdd、sddとsdeにまたがる形で作成された(3つのPhysical Volumeにまたがらなかた)


2. SCSIアドレスの変更、但しアドレス順(昇順)は変えない。
disk1 (2:0) -> /dev/sdb
disk2 (2:1) -> /dev/sdc -+---- lv-cde1
disk3 (2:2) -> /dev/sdd -+-+
disk4 (2:3) -> /dev/sde -+-+-- lv-cde2
disk5 (2:4) -> /dev/sdf

デバイス名は変わらない。

[root@localhost ~]# blkid
/dev/sr0: UUID="2020-04-22-00-54-00-00" LABEL="CentOS 7 x86_64" TYPE="iso9660" PTTYPE="dos"
/dev/sda1: UUID="1635ceff-7bb0-4e6a-a87a-c619572ff03b" TYPE="xfs"
/dev/sda2: UUID="Y686S2-ikqg-pJve-lVy1-JTuZ-seKl-gnCVfw" TYPE="LVM2_member"
/dev/sdb1: UUID="0b05e45c-08a4-4984-b103-7bbdc0cdfb43" TYPE="xfs"
/dev/sdc1: UUID="UOKV4v-F4pE-ZbmA-01NF-X3Is-dBWG-12OH63" TYPE="LVM2_member"
/dev/sdd1: UUID="RzmoRR-MXij-n514-uIoc-032L-o248-buUXlE" TYPE="LVM2_member"
/dev/sde1: UUID="pV0zWa-KSSW-pYpZ-rFTx-VILu-dxNk-9seLd7" TYPE="LVM2_member"
/dev/mapper/centos-root: UUID="68f6ae90-ae9c-41fd-8e5e-d9388b408a3f" TYPE="xfs"
/dev/mapper/centos-swap: UUID="9eb7dd62-13de-410b-934e-07bd1ff91e6b" TYPE="swap"
/dev/mapper/poc--vg-lv--cde1: UUID="1252bce6-2b96-4844-9301-da3218b4a7aa" TYPE="xfs"
/dev/mapper/poc--vg-lv--cde2: UUID="4640987d-f67e-437f-b0fa-c94fd2e5107e" TYPE="xfs"
[root@localhost ~]#


3.SCSIアドレスを、逆順に変更。
disk1 (1:4) -> /dev/sdf
disk2 (1:3) -> /dev/sde -+---- lv-cde1
disk3 (1:2) -> /dev/sdd -+-+
disk4 (1:1) -> /dev/sdc -+-+-- lv-cde2
disk5 (1:0) -> /dev/sdb

物理ストレージに対するデバイス名が変わる。デバイス名はSCSIバススキャンの順序(らしい)。

LVM上のLogical Volumeの構成物理ボリュームは変更後の物理ボリューム名に自動で変更されている。
よってLVM上の論理ボリュームはディスクアドレス変更の影響を受けずにアクセスが可能となっている。
一方、/dev/sdbは/dev/sdfに変わってしまうので注意が必要。

何が何に変わったかはUUIDを確認することでわかる。上記ステップ2での/dev/sdb1のUUIDが以下では/dev/sdf1のUUIDになっているので、/dev/sdb1をマウントしていたマウントポイントは/dev/sdf1をマウントするように変更することになる。
[root@localhost ~]# blkid
/dev/sr0: UUID="2020-04-22-00-54-00-00" LABEL="CentOS 7 x86_64" TYPE="iso9660" PTTYPE="dos"
/dev/sda1: UUID="1635ceff-7bb0-4e6a-a87a-c619572ff03b" TYPE="xfs"
/dev/sda2: UUID="Y686S2-ikqg-pJve-lVy1-JTuZ-seKl-gnCVfw" TYPE="LVM2_member"
/dev/sdc1: UUID="pV0zWa-KSSW-pYpZ-rFTx-VILu-dxNk-9seLd7" TYPE="LVM2_member"
/dev/sdd1: UUID="RzmoRR-MXij-n514-uIoc-032L-o248-buUXlE" TYPE="LVM2_member"
/dev/sde1: UUID="UOKV4v-F4pE-ZbmA-01NF-X3Is-dBWG-12OH63" TYPE="LVM2_member"
/dev/sdf1: UUID="0b05e45c-08a4-4984-b103-7bbdc0cdfb43" TYPE="xfs"
/dev/mapper/centos-root: UUID="68f6ae90-ae9c-41fd-8e5e-d9388b408a3f" TYPE="xfs"
/dev/mapper/centos-swap: UUID="9eb7dd62-13de-410b-934e-07bd1ff91e6b" TYPE="swap"
/dev/mapper/poc--vg-lv--cde1: UUID="1252bce6-2b96-4844-9301-da3218b4a7aa" TYPE="xfs"
/dev/mapper/poc--vg-lv--cde2: UUID="4640987d-f67e-437f-b0fa-c94fd2e5107e" TYPE="xfs"
[root@localhost ~]#

« 2020年8月 | トップページ | 2020年10月 »