【ラズパイ】【いろいろ計測モニター】HTTPサーバを実装する(修正)

すいません、

昨日のこれ、修正します。

何が問題なのかというと、複数のスレッドが同じデバイスにアクセスしている、と言うことです。

デバイスにアクセスするときには「一連のアクセス処理」が発生します。

温湿度計にアクセスする場合は、I2Cインターフェースを使用しているので、データの要求・応答に2つの端子にパルスパターンを送受信しているはずです。

この処理を複数のスレッドで同時に行った場合、正しくアクセス処理が行われない場合があります。

アクセス処理が競合しちゃうんですね。

これを防ぐために、こういったマルチスレッドのプログラミングでは「待ち合わせ処理」(一方の処理が完了するまで、もう一方の処理を待つ)というのが必要なのですが、

今回は規模が小さいので、アクセスを行うスレッドを一つだけ(メインのスレッドのみ)にして、他のスレッドからはデバイスにアクセスしないようにします。

Humidity = 0
Temperature = 0

def __main__():
    global sw
    global Humidity
    global Temperature

    GPIO.setmode(GPIO.BCM)
    GPIO.setup(22,GPIO.IN) 
    GPIO.add_event_detect(22, GPIO.FALLING, callback=callback, bouncetime=300)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    roop = 10 * 60 * 60
    mode = 1
    
    thread = threading.Thread(target=httpServe)
    thread.start()
    
    try:
        while True:
            Humidity = AM2320.GetHum()
            Temperature = AM2320.GetTemp()

            if sw == True:
                GLCD.GLCDDisplayClear()
                mode += 1
                if mode > 4:
                    mode = 1
                sw = False

            if mode == 1:
                if roop >= 10 * 60 * 60:
                    GLCD.GLCDDisplayClear()
                    Weather.RequestAPI()
                    weather = Weather.GetWeather()
                    temp = Weather.GetTemp()
                    roop = 0

                GLCD.GLCDPuts(1, 0, "Date :")
                GLCD.GLCDPuts(5, 8, datetime.datetime.now().strftime('%Y:%m:%d %A '))
                GLCD.GLCDPuts(1, 16, "Weather :")
                GLCD.GLCDPuts(10,24, weather)
                GLCD.GLCDPuts(10,32, "Temp : " + format(temp) + 'C')
                GLCD.GLCDPuts(1, 40, "Time : " + datetime.datetime.now().strftime('%H:%M'))
                GLCD.GLCDPuts(1, 48, "Humidity    : " + Humidity + '%')
                GLCD.GLCDPuts(1, 56, "Temperature : " + Temperature + 'C')

                roop += 1

            #中略

            time.sleep(1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

#中略

class StubHttpRequestHandler(BaseHTTPRequestHandler):
    server_version = "HTTP Stub/0.1"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_GET(self):
        enc = sys.getfilesystemencoding()

        data = {
            'datetime' : datetime.datetime.now().strftime('%Y:%m:%d %H:%M:%S'),
            'temperature': Temperature,
            'humidity': Humidity,
        }

        encoded = json.dumps(data).encode()

        self.send_response(HTTPStatus.OK)
        self.send_header("Content-type", "text/html; charset=%s" % enc)
        self.send_header("Content-Length", str(len(encoded)))
        self.end_headers()

        self.wfile.write(encoded)     

__main__()

これで競合は回避できるはず。

【ラズパイ】【いろいろ計測モニター】HTTPサーバを実装する

測定した温度、湿度をWindows側で表示させたいと思います。

前回HATでやったときはラズパイ側をクライアント、Windows側とサーバとして動作させましたが、

今回は逆で、ラズパイ側をサーバ、Windows側をクライアントとして実行させます。

常に稼働している方をサーバにする方が何かと都合が良い。

こちらの記事を参考にしました。

https://qiita.com/linxuesong/items/8ac98102c24b8f587a16

ただし、このままサーバとして稼働させると、他の処理が出来なくなってしまいます。

なので、HTTPサーバの処理を、既存のループ処理とは別スレッドで実行する必要があります。

なので、スレッドの扱いについて、こちらの記事を参照。

https://qiita.com/tchnkmr/items/b05f321fa315bbce4f77

最終的に組み上がったコードはこちら。

import os
import sys
import urllib.parse
import json
import RPi.GPIO as GPIO
import time
import datetime
import calendar
import GLCD
import AM2320
import Weather
import threading

from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
from http import HTTPStatus

PORT = 8000
sw = False

def __main__():
    global sw
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(22,GPIO.IN) 
    GPIO.add_event_detect(22, GPIO.FALLING, callback=callback, bouncetime=300)
    GLCD.PinsInit(20, 7, 8, 9, 18, 19, 10, 11, 12, 13, 14, 15, 16, 17)
    GLCD.GLCDInit()
    GLCD.GLCDDisplayClear()

    roop = 10 * 60 * 60
    mode = 1
    
    thread = threading.Thread(target=httpServe)
    thread.start()
    
    try:
        while True:

            #既存のループ処理

            time.sleep(1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

def callback(channel):
    global sw
    sw = True

def httpServe():
    handler = StubHttpRequestHandler
    httpd = HTTPServer(('',PORT),handler)
    httpd.serve_forever()

class StubHttpRequestHandler(BaseHTTPRequestHandler):
    server_version = "HTTP Stub/0.1"

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_GET(self):
        enc = sys.getfilesystemencoding()

        data = {
            'datetime' : datetime.datetime.now().strftime('%Y:%m:%d %H:%M:%S'),
            'temperature': AM2320.GetTemp(),
            'humidity': AM2320.GetHum(),
        }

        encoded = json.dumps(data).encode()

        self.send_response(HTTPStatus.OK)
        self.send_header("Content-type", "text/html; charset=%s" % enc)
        self.send_header("Content-Length", str(len(encoded)))
        self.end_headers()

        self.wfile.write(encoded)     

__main__()

Windows側からHTTPのGETリクエストを受信すると、時刻、温度、湿度をJson形式で応答を返します。

今日の北海道は暑いぜ。

GoToトラベルの還付金申請

こないだ泊まったホテル、GoToキャンペーンでの還付金対象だという事が、チェックイン時に判明しました。

まぁいろいろ問題になっているキャンペーンですけど、

もらえるならもらっちゃおう。

https://goto.jata-net.or.jp

上のサイトに、申し込み方法が書いてあります。

大きく分けて、旅行会社のプランの中にキャンペーン適用されている物と、旅行後に申請する物の2パターンあり、オイラは後者の方です。

必要な書類も上のリンクからダウンロード出来ます。

上にある申請書類の内、②と③はホテルから発行されるものです。無くさないように。

こちらで記入するのは①と④です。

Excelをダウンロードして、記入、印刷して提出です。

あとは、金融口座確認用のコピー(キャッシュカードなど)と、

本人確認用書類のコピー(運転免許証など)を用意すればOK。

ただし、申請の受付は8月14日からなので、まだ送付できません。

宛先も公開されていません。

まずは、8月14日まで待つべし。

ちなみに、実費の半分が還付されますので、オイラの場合は2450円戻ってきます。(少な!)

利用できる制度は利用しようぜ、税金払ってるんだからな!

Pixel4aが発表されたぜ!仕様を確認する!

ようやく来たぜ正式発表。

Googleストアにラインナップされています。

リリース日は8月20日。

お値段、何と、Pixel3aより安いんです。

42900円。

それなのに、メモリ6GB、ストレージ128GB、プロセッサもSnapdragon600番台から700番台にスペックアップしています。

しかし、かなり削られた機能もありまして、

まず、アクティブエッジが無い。

端末をぎゅっと握るとGoogleアシスタントが起動するアレ。

まぁ、オイラもPixel3使っているけど、アクティブエッジはあまり使ってない。

そして、当然ながらMotion Senseも無い。

勿論ワイヤレス充電機能も。

そして、防水機能も無い。

下二つはPixel3aから搭載していないけど、Pixel3からの買い換え、と考えると、保留。

まだうちのPixel3が現役で稼働しているので、これが壊れたら購入候補に入るかもしれない。

ただ、もうストレージの容量がパンパンなのよね。

最近のゲーム容量食いまくりで。

重たいゲームはスマホでプレイするつもりは無い(むしろ今メインはPCゲーム)ので、ミドルレンジのスペックでも十分耐えられると思う。

悩む。

いや、今使っているの壊れたら買おう。

【ダイエット支援】【食事管理】データ詳細処理を作成する。

前回までの状況はこちら。

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/diet-mng

今回はこの画面を作成していきます。

前回作成した画面の、

Editをクリックしたときの、遷移先の画面です。

まずは、画面遷移のところを作成する。

EatingListComponent.vue

                        <td class="edit"><a @click="onClickEdit(data.date)">Edit</a></td>


export default {
    data() {
        return {
            url: "eating/detail"
        };
    },
    methods: {
        onClickEdit: function(date) {
            window.location = this.url + "/" + date;
        },

Editをクリックすると、onClickEdit()が実行され、window.locationにURLを設定することで、そのURLに遷移します。

で、遷移先の画面。

web.php

Route::get('/eating/detail/{date}', 'Eating\EatingController@detail')->name('eating/detail');
EatingController.php

class EatingController extends Controller
{
    public function detail($date)
    {
        return view('eatingdetail', ['date' => $date]);
    }
eatingdetail.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <div class="panel panel-default">
                <div class="panel-heading">食事管理</div>

                <div class="panel-body">
                    @if (session('status'))
                        <div class="alert alert-success">
                            {{ session('status') }}
                        </div>
                    @endif
                </div>
                <eating-detail-component date={{$date}}></eating-detail-component>
            </div>
        </div>
    </div>
</div>
@endsection

詳細画面のURLはeating/detail/(日付)となります。

この(日付)の部分が巡り巡って、eating-detail-componentに渡ります。

<eating-detail-component date={{$date}}></eating-detail-component>

ここで一度詰まった。

:date={{$date}}と書いてはダメみたい。

で、表示するデータの取得。

class EatingManagementRepository
{
    private $paramNames = ['date', 'item', 'protein', 'liqid', 'carbo', 'calorie'];

    public function getDetails($user, $date)
    {
        $eatings = $user->EatingManagements()
            ->where(DB::raw('date_format(date, "%Y-%m-%d")'), $date)
            ->get();
        
        $retDatas = [];
        $index = [0, 0, 0, 0];
        foreach($eatings as $eating) {
            $timezone = $eating->timezones()->first();
            for($j = 1; $j < count($this->paramNames); $j++) {
                $retDatas[$timezone->id - 1][$index[$timezone->id - 1]][$this->paramNames[$j]] = $eating->{$this->paramNames[$j]};
            }
            $index[$timezone->id - 1]++;
        }
        return $retDatas;
    }

今回もかなりカオスな処理。

時間帯によってデータを分けたかったので、

retData[時間帯番号][index][詳細データ]

という形にデータを作成しています。

詳細データの連想配列の名前は、定義済みの$paramNamesの値を使用します。

これをAPIで呼び出します。

class ApiController extends Controller
{
    /**
     * 一日分のデータを取得する
     */
    public function detail(Request $request)
    {
        return response()->json(['dataLists' => $this->eatingManagement->getDetails(Auth::user(), $request->contents['date'])]);
    }
web.php

Route::post('api/eating/detail', 'Eating\ApiController@detail');
<template>
    <div>
        <div>
            <p id="navi">> <a href="/home">HOME</a> / <a href="/eating">食事管理</a></p>
            <p>{{date}}</p>
            <table class="eatingdetail">
                <caption>朝</caption>
                <tbody>
                    <tr>
                        <th class="item">品名</th>
                        <th class="protein">タンパク質</th>
                        <th class="liqid">脂質</th>
                        <th class="carbo">炭水化物</th>
                        <th class="calorie">カロリー</th>
                        <th class="edit"></th>
                    </tr>
                    <tr v-for="data in datalists[0]">
                        <td class="item">{{ data.item}}</td>
                        <td class="protein">{{ data.protein}}</td>
                        <td class="liqid">{{ data.liqid}}</td>
                        <td class="carbo">{{ data.carbo}}</td>
                        <td class="calorie">{{ data.calorie}}</td>
                        <td class="edit"><a @click="onClickEdit(data.date)">Edit</a></td>
                    </tr>
                </tbody>
            </table>
            <table class="eatingdetail">
                <caption>昼</caption>
                <tbody>
                    <tr>
                        <th class="item">品名</th>
                        <th class="protein">タンパク質</th>
                        <th class="liqid">脂質</th>
                        <th class="carbo">炭水化物</th>
                        <th class="calorie">カロリー</th>
                        <th class="edit"></th>
                    </tr>
                    <tr v-for="data in datalists[1]">
                        <td class="item">{{ data.item}}</td>
                        <td class="protein">{{ data.protein}}</td>
                        <td class="liqid">{{ data.liqid}}</td>
                        <td class="carbo">{{ data.carbo}}</td>
                        <td class="calorie">{{ data.calorie}}</td>
                        <td class="edit"><a @click="onClickEdit(data.date)">Edit</a></td>
                    </tr>
                </tbody>
            </table>
            <table class="eatingdetail">
                <caption>夜</caption>
                <tbody>
                    <tr>
                        <th class="item">品名</th>
                        <th class="protein">タンパク質</th>
                        <th class="liqid">脂質</th>
                        <th class="carbo">炭水化物</th>
                        <th class="calorie">カロリー</th>
                        <th class="edit"></th>
                    </tr>
                    <tr v-for="data in datalists[2]">
                        <td class="item">{{ data.item}}</td>
                        <td class="protein">{{ data.protein}}</td>
                        <td class="liqid">{{ data.liqid}}</td>
                        <td class="carbo">{{ data.carbo}}</td>
                        <td class="calorie">{{ data.calorie}}</td>
                        <td class="edit"><a @click="onClickEdit(data.date)">Edit</a></td>
                    </tr>
                </tbody>
            </table>
            <table class="eatingdetail">
                <caption>間食</caption>
                <tbody>
                    <tr>
                        <th class="item">品名</th>
                        <th class="protein">タンパク質</th>
                        <th class="liqid">脂質</th>
                        <th class="carbo">炭水化物</th>
                        <th class="calorie">カロリー</th>
                        <th class="edit"></th>
                    </tr>
                    <tr v-for="data in datalists[3]">
                        <td class="item">{{ data.item}}</td>
                        <td class="protein">{{ data.protein}}</td>
                        <td class="liqid">{{ data.liqid}}</td>
                        <td class="carbo">{{ data.carbo}}</td>
                        <td class="calorie">{{ data.calorie}}</td>
                        <td class="edit"><a @click="onClickEdit(data.date)">Edit</a></td>
                    </tr>
                </tbody>
            </table>
        </div>
    </div>
</template>

<script>
export default {
    props: {
        date: String
    },
    data() {
        return {
            showInputDialogContent: false,
            showEditDialogContent: false,
            showDeleteDialogContent: false,
            datalists: [],
            param: {},
            contents: {
                date: "",
            },
        };
    },
    created: function() {
        this.updateList();
    },
    methods: {
        onClickEdit: function(date) {
        },
        invokeUpdateList: function() {
        },
        updateList: function() {
            this.datalists = [];
            this.contents.date = this.date;
            this.param.contents = this.contents;
            var self = this;
            axios.post('/api/eating/detail', this.param).then(function(response){
                response.data.dataLists.forEach(element => {
                    var data = [];
                    element.forEach(element2 => {
                        data.push({
                            item: element2.item,
                            protein: element2.protein,
                            liqid: element2.liqid,
                            carbo: element2.carbo,
                            calorie: element2.calorie
                        })
                    })
                    self.datalists.push(data);
                });
            }).catch(function(error){
            });
        }
    }
}
</script>

responseの処理がネストになるという、またまたカオスな処理。

でもこれでJS側でもdatalists[時間帯ID][index].詳細データという形になります。

これをテンプレートに表示させれば良い。

【さっぽろ夏割】ホテルWBF札幌ノースゲート

久しぶりに泥酔した。

さっぽろ夏割プランを利用して、ホテルWBF札幌ノースゲートに一泊してきました。

https://www.hotelwbf.com/sapporo-northgate/

さっぽろ夏割を適用すれば5000円割引になります。

さらに、札幌市内のお店で使用できる3000円分のクーポンつき。

https://www.sapporo-summer2020.com/shop_use/

さらにラウンジが利用できる宿泊プランでした。

ラウンジでは、ビールやお菓子が食べ飲み放題なのです。

久しぶりに泥酔レベルまで飲んだね。

さらに、チェックインの時に聞いたのですが、

GoToキャンペーンで、申請すれば宿泊料金の一部が還付されるようです。

いやー久しぶりにリフレッシュしたわ。

明日からダイエットと節約頑張る。

久しぶりのお風呂(サウナ)からのビール

いやー

力抜ける。

久しぶりにガーデンズキャビンさんのお風呂に行ってきました。

思った以上に混んでいませんでした。

ホテル側でお風呂を利用しているお客の人数を管理していて、混み合わないように人数制限しているようです。

広いお風呂、ほぼ貸し切りでした。

そして、サウナで出来上がったら、居酒屋でビール。

前回、ホットペッパーでお食事券を予約当日に購入したものの、利用開始が購入日の次の日からだったので、今回ようやく使用できます。

唐揚げ1個50円。

唐揚げとザンギの違いは、明確な区別は無いそうですが、ザンギは衣に卵黄を混ぜてあるヤツが多いそうです。

ウナ串一本99円。

あ、土用の丑の日、今日だっけ。

いやー完全にリラックスして力が抜けました。

そして、今日はもう一つ贅沢をしちゃいます。

これが最後です(何回目)

【モラタメ】キッコーマン 超焼肉のたれ 甘口

ああ、そういえば注文してたっけな。

大量。

中身は全て同じです。

超具材焼肉のたれ。

これは、焼肉を食えと言うことか。

焼きました。

ソースは思った以上にどろっとしてますね。

お肉にめちゃくちゃ絡みつきます。

これは甘口で、オイラは普段辛口を食べているのですが、全然クセが無く、飽きが来ない味付けになっていました。

なので、用意したお肉全部ペロッと食べてしまいました。

美味いね。

ごちそうさまでした。

まだ5本残っているけど、ボチボチ消費します。

【実質北海道一周】都市と距離データを作成する。

前回までの状況はこちら。

さて、GPSから距離を求めることが出来たので、

今度は実際に使用する都市間距離のデータを作成します。

どこかに便利なデータが公開されていないかなー

って探してみたのですが、

ありませんでした。

公開されているデータが無いので、自分で作りました。

チマチマとGoogle Mapを使って、北海道一周で通る都市と、その都市間の距離を測定しました。

ルート検索を使うと、走行距離が出てくるので、このデータを、とりあえずはExcelに入力。

これをエディターにコピペして、置換を使ってタブスペースを削除。

ちなみに、ルートとしては、札幌をスタートして小樽へ向かい、反時計回りで一周して、石狩から札幌に戻るルートです。

疲れた。

【北海道大戦】市町村選択処理を実装する。

最新ソースはこちら(gitHub)

https://github.com/takishita2nd/HokkaidoWar

前回作成したこれ。

これの「担当市町村を選択」の処理を作成していきます。

まずは、状態の定義を作成しましょうか。

    class HokkaidoWar
    {
        Player _player = null;

        enum GameStatus
        {
            SelectCity,
            ActionEnemy,
            ActionPlayer,
            ShowResult,
            GameEnd
        }

        GameStatus gameStatus;

上からstatus=1~5に相当します。(実際に振られている値は違いますが)

        public void Run()
        {
            while (asd.Engine.DoEvents())
            {
                asd.Vector2DF pos = asd.Engine.Mouse.Position;

                switch (gameStatus)
                {
                    case GameStatus.SelectCity:
                        var info = Singleton.GetInfomationWindow();
                        info.ShowText(pos, "都市を選択してください\r\n");
                        onMouse(pos);
                        break;
                }

                if (asd.Engine.Mouse.LeftButton.ButtonState == asd.ButtonState.Push)
                {
                    switch (gameStatus)
                    {
                        case GameStatus.SelectCity:
                            _player = new Player(getCity(pos));
                            gameStatus = GameStatus.ActionEnemy;
                            break;
                    }
                }
                asd.Engine.Update();
            }
            asd.Engine.Terminate();
        }

マウスカーソルを動かすと、テキストウィンドウに「都市を選択してください」と表示されます。それに加えて、マップの上にカーソルがあると、その都市の情報も表示します。

マウスをクリックすると、その選択した都市でプレイヤーデータを作成します。

    class Player
    {
        private City city;
        public Player(City city)
        {
            this.city = city;
        }
    }

そして、statusを「敵の行動」に遷移させます。

自分、ぼっちですが何か?