リモートターミナル RiT

# はじめに
MH ソフトウェア&サービスのUDP通信を利用した、リモートターミナルの紹介です。
工場の制御機器や画像センサは、UDP出力に対応してる機器があります。
これらの機器から出力される信号を、リモートで監視できます。

## UDPとは
User Datagram Protocolの略で、[ウィキペディア](https://ja.wikipedia.org/wiki/User_Datagram_Protocol)より

**TCPとは異なり、UDPはコネクションレス型であり、通信相手の応答を待つことなくデータを送り続けるプロトコルである。**

## TCPとは
Transmission Control Protocolの略です。コネクションします。つまり相手がいないと、エラーとなります。

## UDPのメリット
あくまでも自分の感覚ですが、
1. TCPより全体の通信時間が短い
2. 相手がいなくても送信できるため、エラー処理が簡素化できる
3. 簡素化できるため、スマホでも送受信が簡単にできる

# リモートターミナルRiT
Pythonを使用して作成されています。Pythonですので、WindowsPCやAndroidスマホで動作します。
もちろんMac環境、iPhone環境でも動くと思いますが、機器を持ち合わせておりませんので、未確認です。
GUIはtkinterです。

## Windows Python 3.8.6
![windows.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569469/d8fc0dc7-620a-c355-0eb9-a8abd988899c.jpeg)

## Android Pydroid 3 4.01_arm64
![android.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569469/8e11b008-a55b-7f6e-53c8-7af9be0b1b93.jpeg)

Pydroid 3では、アイコンが出ません。タイトルに日本語が使えません。設定があるのかもしれませんが、不明です。
アドバイス頂けますと、幸いです。

## Raspberry Pi 4 と Kuman K82
![K82.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569469/edd9995e-05cc-b4c9-a4f6-9b0c46da7711.jpeg)

[サーマルカメラ(サーモ AI デバイス TiD) 紹介編](https://qiita.com/MH-SS/items/c3d12abe871bfd62d1df)で紹介しました、Physical Computing Lab / Raspberry Pi4 Model B DIYメタルケースにRaspberry Piを入れて、[Kuman K82](https://www.amazon.co.jp/Kuman-Raspberry-Pi%E7%94%A8%E9%9B%BB%E6%BA%90%E3%83%AA%E3%83%AC%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89-%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%AD%E3%83%83%E3%83%88-3b%E7%94%A8%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%88%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB/dp/B0771MYFKB/ref=sr_1_1_sspa?dchild=1&hvadid=474646919699&hvdev=c&jp-ad-ap=0&keywords=k82&qid=1610763461&sr=8-1-spons&tag=yahhyd-22&psc=1&spLa=ZW5jcnlwdGVkUXVhbGlmaWVyPUEyMzVOTkZEVDdPNkpOJmVuY3J5cHRlZElkPUExMDQ1OTUzMTk2NUZGTEI2VTc0UiZlbmNyeXB0ZWRBZElkPUEyM1BNMDlWME1BTzc0JndpZGdldE5hbWU9c3BfYXRmJmFjdGlvbj1jbGlja1JlZGlyZWN0JmRvTm90TG9nQ2xpY2s9dHJ1ZQ==)を差し込んだだけの、シンプルな構成です。

# Rootクラス
## Form初期化
```
class Root(mhtk.Form):
    def __init__(self):
        self.__version__ = __version__

        self.root = self
        self.ini = Ini(fullpath=__file__)
        self.LANG = 'ja'

        self.id_name = self.ini.udp.id_name
        self.udp_port = self.ini.udp.port
        self.destination = self.ini.udp.destination

        minsize = (400, 180)
        super().__init__(
            dialog=True,
            font=('system'),
            icon=self.icon_file,
            minsize=minsize,
            name='main',
            position=self.ini.get_geometry(
                self.ini,
                self.ini.config,
                minsize),
            title=(lambda: 'Remote Terminal'
                   if self.LANG == 'en' else 'Remote Terminal')(),
            udp_port=self.udp_port,
            )

        # NOTE: Output Bit. This value is binary.
        self.output = tk.IntVar(value=0)
        self.output.trace('w', self.output_changed)

        self.create_io()

        self.menu = Menu(self)

        self.indicator = Indicator(self)
        self.indicator.pack(anchor=tk.CENTER)

        # NOTE: Logo
        self.logo = mhtk.IconFramePack(self)
        self.logo.pack(anchor=tk.CENTER)
```
minsizeを指定して、[サーマルカメラ(サーモ AI デバイス TiD) Python Form編](https://qiita.com/MH-SS/items/035682853d18dc2074f2)で紹介しましたFormクラス(form.py)を使用しています。

## I/O
create_ioで、GPIO用のクラス(mhrp.gpio)からI/Oを作成しています。

```python
    def create_io(self):
        """
        for K82.
        CH1 GPIO19 PIN35
        CH2 GPIO26 PIN37
        CH3 GPIO20 PIN38
        CH4 GPIO21 PIN40
        ================
        self.force_* value is
            -1: Not forced.
             0: Forced off.
             1: Forced on.
        """
        #self.input_list = [29, 31, 33, 35, 37]
        #self.output_list = [16, 18, 32, 36, 38, 40]

        self.input_list = [11, 13, 16, 18]
        self.force_input = []
        for _ in range(len(self.input_list)):
            self.force_input.append(-1)

        self.output_list = self.ini.config.output_io_list
        self.force_output = []
        for _ in range(len(self.output_list)):
            self.force_output.append(-1)

        self.io = mhrp.gpio.IO(
            'PIN',
            self.input_list,
            self.output_list,
            mhrp.gpio.BOTH,
            )
        self.io.input.bin.trace('w', self.io_callback)
```

## mhrp.gpio -> input/outpu.py -> bit.py
mhrpは**MH** ソフトウェア&サービスの**R**aspberry **P**i用のライブラリです。
この中には、input/output.pyから各クラスがインスタンス化されます。
さらにinput/output.puは、bit.pyのBitクラスをインスタンス化しています。

bit.py

```python
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import tkinter as tk


class Bit(list):
    def __init__(self, count, callback):
        self._count = count
        self._callback = callback
        for bit in range(count):
            self.append(tk.BooleanVar(value=False))
            self[bit].trace('w', self.BitChanged(bit, self._bit_changed))

    def _bit_changed(self, *args):
        self._callback(*args)

    class BitChanged():
        def __init__(self, index, callback):
            self._index = index
            self._callback = callback

        def __call__(self, *args):
            self._callback(self._index)
```
Bitクラスはlistを継承しています。
入力・出力の数だけlistにBitChangedをインスタンス化し、追加します。
各BitChangedクラスは、自分が何番目かのindexを持っています。オン/オフが切り替わった場合、callback関数を実行します。

## tkinterの trace 最強!
MH ソフトウェア&サービスでのPython GUIは、主にtkinterとなっています。
変数が変わった時に、関数を実行できる.traceがあります。

bit.py内で

```python
self[bit].trace('w', self.BitChanged(bit, self._bit_changed))
```
があります。

self._bit_changedは、self.BitChanged(bit, self._bit_changed)の部分で、インスタンス化されて、trace属性が付きます。

これはbitが変更された時、self._bit_changedが実行されます。
self._bit_changedはBitChangedクラスで、__call__によりインスタンスを関数の様に実行する事が出来ます。

```python
class BitChanged():
    def __init__(self, index, callback):
        self._index = index
        self._callback = callback

    def __call__(self, *args): # ここが実行されます
        self._callback(self._index)
```
単純なクラスですが、インスタンス化時に自分のビットとコールバック関数を覚えておいて、ビットが変更になったら、ビットをinput/output.pyへ戻すという感じです。

その他、traceは文字変数でも可能です。

***
[YouTube: リモートターミナル RiT](https://youtu.be/ovmSMvSjbog)
[web: RiT リモートターミナル](https://mhss.server-on.net/?f401c=Index&ae504=rit) (URLが変更されました)