米軍、IS系「計画者」をドローン攻撃で殺害

バイデンが報復を宣言した、その翌日にこれだ。良くも悪くも「さすがアメリカ」と思った。

ただTVでも「ドローンで殺害」とか言ってたが、これはもはや「無人爆撃機」か、もしかすると「無人狙撃機」である。爆弾抱いての「人海戦術」な敵とのレベル差がありすぎて殆ど滑稽だが、ふと「これをタリバン相手に使っていたらこうはなってなかったんじゃないか?」と思った。

それに加え、IS-Kへの制裁はタリバンを利することにはならんのだろか?  とも。あの一帯の紛争事情というのは、いつまで経っても不可解なことばかりだ。

八甲田山 – 死人に口なし

一昨日の豪雨と交通機関の乱れから、遅れに遅れたバスの車中で読んだのが「八甲田山 消された真実」。映画「八甲田山」は公開時(=はるか昔のちょうど今頃)に観たし、新田次郎の原作も読んだが以来「?」な部分がずっと気になっていたので、というのがその動機。まあそのベタなタイトルから内容は大方予想できたが、まずはそのワタクシ的「?」から。

1. 健さんを率いる秋吉久美子

高倉健、もとい「徳島大尉」率いる弘前第三十一連隊が急峻な雪道でヒイコラなところを、案内人秋吉久美子、もとい「サワ」が笑顔でヒョイヒョイ登って行く。そりゃこの局面で「村の長が選んだ最適者はオンナだった」はアリかもしれん。が、まだ「軍人=士農工商の士」で男尊女卑だったであろうあの時代にこれはあり得ないのでは? てか、それ以前にこの「案内人ありき」なヌルい雪中行軍を「実戦を念頭」な訓練と呼べるのか?

2. 徳島・神田の対面

映画で徳島は北大路欣也、もとい神田の遺体と2度対面するが、最初のそれで既にウルウルだったのが2度めのそれで「ハァ?」となった。自らも遭難スレスレで神田の死に遭遇した徳島がそれを放置し田茂木野へ向かったのは理解できるとして、そこで「また」神田と栗原小巻、もといその妻とともにさめざめ落涙という展開。ここで混乱した人、多かったのでは?

そして最大の「?」は

3. そもそもこれは誰のせいなの?

原作と映画は三國連太郎、もとい山口大隊長の越権・独断による指揮系統の乱れがその主因としていた。が、実戦ならまだしも訓練でこれほどの兵士を死なせた責任を大隊長ひとりに負わせてチャンチャン、はあり得ないのでは?

ここからは読了後の感想。本書は生存者による数多の証言と資料から、そのいちばんの原因・悪玉が青森第五連隊長津川謙光にあるとし、更には加山雄三、もとい倉田大尉らの証言の嘘とその理由をも断罪する。そして遂には弘前三一連隊の高倉健、もとい徳島大尉の悪辣さまでをも暴いている。

# 正直これはイタかった

これまでの疑問に対し、この著者ならではの見解が得られたのは良かった … が、詰まるところそれは「未熟だった当時のこの国」で起きた「事故」というのが真相なのかな、と思う。舞台は違えど、こういうのは今でもそこら中にあるんだし。

responderのwebsocketでbroadcast

ひさびさのresponderネタ。テーマは「接続中のwebsocketクライアント全員に対する一斉送信専用メソッド」。送信専用というところがキモで「自らもクライアントとなって自らに接続する」なんてぇ無駄でカッコ悪いことはしない。これまで「作っちゃ忘れちゃ」の繰り返しで毎回思い出すのに苦労していたので、その備忘録として。

responderでwebsocketサーバを構築して「接続中のクライアントからのメッセージを全クライアントに送信する」と、それだけなら本家サンプルのままでいい。が、本件の実現のためにはまず「接続中のクライアント各々」の情報を保持する必要がある。これは接続時に得られる’sec-websocket-key’を文字通りキーとしたdictとすれば良い。

# またこれを参照することによって、クライアントから受信したメッセージを「そのクライアント以外」にだけ送信することができる

次にいよいよbroadcastだが、ここでresponderが動作しているスレッド(以下「メインスレッド」とする)からクライアントに向けてsend_????しようとしても、awaitをつけると「SyntaxError: ‘await’ outside async function」でエラー。ならばとawaitを外すと今度は「RuntimeWarning: coroutine ‘***’ was never awaited」と警告が出て送信されない。原因はといえば「responderのwebsocketはイベントループで動作しているから」。要はそれが何なのかをわかってないからこうなるのであって、結論からいうと

  1. 予めメインスレッドのイベントループを取得しておく
  2. 上記1に対しコルーチン(=async修飾子つきのdef)を登録する

とすることで解決した。以下ソース。Python3.7.9/Ubuntuで動作確認したもの。メインスレッド以外(注1)からも使いやすいようにするため、ブロードキャストしたいメッセージをキューにputする仕組みになっている。念のためdictの操作を排他的に行うようLockを使っているが、これは不要かも。

注1: メインスレッド以外のスレッドからこれをするには、上記1を使わないと「There is no current event loop in thread …」となって動かない

import responder
from starlette.websockets import WebSocket, WebSocketDisconnect
from typing import Dict
import asyncio
from threading import Thread, Lock
from queue import Queue, Empty, Full


class WebSocketServer(object):
    def __init__(self, *, address: str = '0.0.0.0', port: int = 8080, location: str = '/'):
        self.address = address
        self.port = port
        self.location = location

        self.clients: Dict[str, WebSocket] = {}
        self.locker = Lock()

        self.elo = asyncio.get_event_loop()  # Eventloop Object
        self.bcPutTimeoutSecs = 5
        self.bcGetTimeoutSecs = 5
        self.bcCounter = 0
        self.bcQueue = Queue()  # Queue for message
        self.bcThread = Thread(target=self._bcWatcher, daemon=True)
        self.bcThread.start()

        self.api = responder.API()
        self.api.add_route(route=self.location, endpoint=self.wsSession, websocket=True)

        self.api.run(address=self.address, port=self.port)

    async def wsSession(self, ws: WebSocket):
        await ws.accept()
        key = ws.headers['sec-websocket-key']
        with self.locker:
            if key not in self.clients.keys():
                self.clients[key] = ws
        while True:
            try:
                message = await ws.receive_text()
            except (WebSocketDisconnect,) as e:
                break
            else:
                for k, v in self.clients.items():
                    if k != key:
                        await v.send_text(data=message)  # no exception ???

        with self.locker:
            if key in self.clients.keys():
                del self.clients[key]
        await ws.close()

    async def _onAir(self, *, message: str) -> None:
        with self.locker:
            for k, v in self.clients.items():
                await v.send_text(data=message)

    def _bcWatcher(self):
        while True:
            try:
                message = self.bcQueue.get(timeout=self.bcGetTimeoutSecs)
            except (Empty,) as e:
                self.broadCast(message='Queue is empty!')  # for debug
            else:
                self.elo.run_until_complete(future=self._onAir(message=message))
                self.bcCounter += 1

    # ---------------------------------------------------------------------------------
    def broadCast(self, *, message: str):
        try:
            self.bcQueue.put(item=message, timeout=self.bcPutTimeoutSecs)
        except (Full,) as e:
            print(e)
            pass
    # ---------------------------------------------------------------------------------


if __name__ == '__main__':
    def main():
        S = WebSocketServer()
        pass


    main()