2020/10,江端さんの技術メモ

スマホから位置情報を取得するところまできたが、https(wss)対応にしないと位置情報が取り出せないようです(かなりしつこく聞いていましたが、すっかり忘れていました)。

で、今のGolangのサーバプログラムをhttps対応にすべく、色々試したのですが、なんかこれで動いているようなので、要点だけ記載します。

Step.1 

まず、

C:\Users\ebata\WssSample\goClient>go run goClient.go conn.go hub.go tls: first record does not look like a TLS handshake がなんともならない件

の、mkcertを使って生成した鍵(“algo.key"と"algo.crt")をディレクトリに放り込む

Step.2

serverXX.goの、サーバ作成部を以下のように変更する

/*
		log.Fatal(http.ListenAndServe(*addr, nil)) // localhost:8080で起動をセット
	*/

	var httpErr error
	if _, err := os.Stat("./algo.crt"); err == nil {
		fmt.Println("file ", "algo.crt found switching to https")
		if httpErr = http.ListenAndServeTLS(*addr, "algo.crt", "algo.key", nil); httpErr != nil {
			log.Fatal("The process exited with https error: ", httpErr.Error())
		}
	} else {
		httpErr = http.ListenAndServe(*addr, nil)
		if httpErr != nil {
			log.Fatal("The process exited with http error: ", httpErr.Error())
		}
	}

Step.3

htmlファイルの、"http://"を、片っ端から"https://"に書き換える。

さくっと動いて気持ち悪いのですけど、まあ動くのであれば、なんでも良いです。

 

2020/10,江端さんの技術メモ

スマホから使えるようにした(今は、ランダムウォークだけ)。

/*
// server22.go ペアはclient9.go

// Copyright 2015 The Gorilla WebSocket Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build ignore

// 使い方
// go run server13.go      (適当なシェルから)
// http://localhost:8080  (ブラウザ起動)
// http://localhost:8080/smartphone (スマホ起動)
*/

package main

import (
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
)

// GetLoc GetLoc
type GetLoc struct {
	ID  int     `json:"id"`
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
	//Address string  `json:"address"`
}

//var addr = flag.String("addr", "localhost:8080", "http service address")
var addr = flag.String("addr", "0.0.0.0:8080", "http service address") // テスト

var upgrader = websocket.Upgrader{} // use default options

var chan2_1 = make(chan GetLoc)

var maxid = 0

var mutex sync.Mutex

func echo2(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil) // cはサーバのコネクション
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	//mutex := new(sync.Mutex)

	for {
		//mt, message, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
		//_, _, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)

		//mutex.Lock()  // ここに置くとデッドロックしてしまう

		gl := new(GetLoc)

		err := c.ReadJSON(&gl) // クライアントからのメッセージの受信

		mutex.Lock()

		// 原因不明の対処処理
		if gl.ID == 0 && gl.Lat < 0.01 && gl.Lng < 0.01 {
			mutex.Unlock()
			break
		} else if gl.ID < -1 { // 受理できないメッセージとして返信する
			//条件分岐 (変なIDが付与されているメッセージは潰す)
			//if (gl.ID > maxid) || (gl.ID < -1) { // 受理できないメッセージとして返信する

			gl.ID = -1
			gl.Lat = -999
			gl.Lng = -999
			err2 := c.WriteJSON(gl)
			if err2 != nil {
				log.Println("write1:", err2)
				mutex.Unlock()
				break
			}
		} else { // それ以外は転送する
			log.Printf("echo2 after c.WriteJSON(gl) ID:%d", gl.ID)
			log.Printf("echo2 after c.WriteJSON(gl) Lat:%f", gl.Lat)
			log.Printf("echo2 after c.WriteJSON(gl) Lng:%f", gl.Lng)

			if err != nil {
				log.Println("read:", err)
				mutex.Unlock()
				break
			}
			fmt.Printf("echo2 before chan2_1 <- *gl\n")
			chan2_1 <- *gl
			fmt.Printf("echo2 after chan2_1 <- *gl\n")

			//で、ここで受けとる
			//gl2 := new(GetLoc)
			fmt.Printf("echo2 before gl2 := <-chan2_1\n")
			gl2 := <-chan2_1
			maxid = gl2.ID // ID最大値の更新
			log.Printf("echo2 after gl2 := <-chan2_1 ID:%d", gl2.ID)
			log.Printf("echo2 after gl2 := <-chan2_1 Lat:%f", gl2.Lat)
			log.Printf("echo2 after gl2 := <-chan2_1 Lng:%f", gl2.Lng)

			fmt.Printf("echo2 before err2 := c.WriteJSON(gl2)\n")
			err2 := c.WriteJSON(gl2)
			fmt.Printf("echo2 after err2 := c.WriteJSON(gl2)\n")
			if err2 != nil {
				log.Println("write2:", err2)
				mutex.Unlock()
				break
			}
			fmt.Printf("end of echo2\n")

		}

		mutex.Unlock()
	}
}

func echo(w http.ResponseWriter, r *http.Request) {

	c, err := upgrader.Upgrade(w, r, nil) // cはサーバのコネクション
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	/*	ここでロックして待つ */

	for {

		fmt.Printf("echo before gl := <-chan2_1\n")
		gl := <-chan2_1
		fmt.Printf("echo after gl := <-chan2_1\n")

		fmt.Printf("echo before err = c.WriteJSON(gl) gl2.id = %d\n", gl.ID)
		fmt.Printf("echo before err = c.WriteJSON(gl) gl2.lat = %f\n", gl.Lat)
		fmt.Printf("echo before err = c.WriteJSON(gl) gl2.lng= %f\n", gl.Lng)
		err = c.WriteJSON(gl)
		if err != nil {
			log.Println("WriteJSON1:", err)
		}
		fmt.Printf("echo after err = c.WriteJSON(gl)\n")

		fmt.Printf("echo before err = c.RreadJSON(gl)\n")
		gl2 := new(GetLoc)
		err2 := c.ReadJSON(&gl2)
		fmt.Printf("echo after err = c.ReadJSON(&gl2) gl2.id = %d\n", gl2.ID)
		fmt.Printf("echo after err = c.ReadJSON(&gl2) gl2.lat = %f\n", gl2.Lat)
		fmt.Printf("echo after err = c.ReadJSON(&gl2) gl2.lng= %f\n", gl2.Lng)
		if err2 != nil {
			log.Println("ReadJSON:", err2)
		}
		// ここからチャネルで返す
		fmt.Printf("echo before chan2_1 <- *gl2 gl2.id = %d\n", gl2.ID)
		fmt.Printf("echo before chan2_1 <- *gl2 gl2.lat = %f\n", gl2.Lat)
		fmt.Printf("echo before chan2_1 <- *gl2 gl2.lng = %f\n", gl2.Lng)
		chan2_1 <- *gl2
		fmt.Printf("echo after chan2_1 <- *gl2\n")
		fmt.Printf("end of echo\n")
	}

}

func home(w http.ResponseWriter, r *http.Request) {
	homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}

func smartphone(w http.ResponseWriter, r *http.Request) {
	smartphoneTemplate.Execute(w, "ws://"+r.Host+"/echo2")
}

func main() {
	flag.Parse()
	log.SetFlags(0)

	http.HandleFunc("/echo2", echo2)           // echo関数を登録 (サーバとして必要)
	http.HandleFunc("/echo", echo)             // echo関数を登録 (サーバとして必要)
	http.HandleFunc("/", home)                 // home関数を登録
	http.HandleFunc("/smartphone", smartphone) // smartphone関数を登録
	log.Fatal(http.ListenAndServe(*addr, nil)) // localhost:8080で起動をセット
}

var smartphoneTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script> 

function obj(id, lat, lng){
	this.id = id;
	this.lat = lat;
	this.lng = lng;
}

function random(min, max){
	return  Math.random()*(max-min) + min;
}

// var personal_id;

var lat = 35.654543;
var lng = 139.795534;  

window.addEventListener("load", function(evt) {
    var output = document.getElementById("output");
    var input = document.getElementById("input");
    var ws;
    var print = function(message) {
        var d = document.createElement("div");
        d.textContent = message;
        output.appendChild(d);
	};

	var personal_id = 0;

	
	///// 起動時のボタン
	// disabled属性を削除
	document.getElementById("open").removeAttribute("disabled");
	document.getElementById("open").style.color = "black";

	// disabled属性を設定 (closeボタンを非活性化)
	document.getElementById("close").setAttribute("disabled", true);
	document.getElementById("close").style.color = "White";	

	// disabled属性を設定 (sendボタンを非活性化)
	document.getElementById("send").setAttribute("disabled", true);
	document.getElementById("send").style.color = "White";	


		
	document.getElementById("open").onclick = function(evt) {
		console.log("document.getElementById open");

		// disabled属性を設定 (openボタンを非活性化)
		document.getElementById("open").setAttribute("disabled", true);
		document.getElementById("open").style.color = "White";		

		// disabled属性を削除
		document.getElementById("send").removeAttribute("disabled");
		document.getElementById("send").style.color = "black";	

		// disabled属性を削除
		document.getElementById("close").removeAttribute("disabled");
		document.getElementById("close").style.color = "black";	

		////////// 削除2
		// ws = new WebSocket("{{.}}");
		////////// 削除2終り


		////////// 削除1
		//var send_obj = new obj(0, 35.654543,139.795534);  // 最初は"0"でエントリ

		//console.log("open:send_obj");	
		//console.log(send_obj.id);	
		//console.log(send_obj.lat);
		//console.log(send_obj.lng);		

		//var json_obj = JSON.stringify(send_obj);
		//ws.send(json_obj);
		/////////// 削除1終り


        if (ws) {
            return false;
        }
		
		
		////////// 追加2
		ws = new WebSocket("{{.}}");
		////////// 追加2終り

		
        ws.onopen = function(evt) {
			print("OPEN");
		
			//ws = new WebSocket("{{.}}");
			
			////////// 追加1			
			var send_obj = new obj(0, 35.654543,139.795534);  // 最初は"0"でエントリ

			console.log("open:send_obj");	
			console.log(send_obj.id);	
			console.log(send_obj.lat);
			console.log(send_obj.lng);		

			var json_obj = JSON.stringify(send_obj);
			ws.send(json_obj);
			/////////// 追加1終り

		}
		
        ws.onclose = function(evt) {

			print("CLOSE");
            ws = null;
        }

		ws.onmessage = function(evt) {  // 受信したメッセージはここに飛んでくる
			print("RESPONSE: " + evt.data);  // jsonメッセージの内容を表示
			// データをJSON形式に変更
			var obj = JSON.parse(evt.data);

			personal_id = obj.id; // IDの取得(何回も取る必要はないが)
			console.log("personal_id");			
			console.log(personal_id);

			
			if ((Math.abs(obj.lat) > 90.0) || (Math.abs(obj.lng) > 180.0)){ // 異常な座標が入った場合は、マーカーを消去する
				console.log("before ws.close()");
				ws.close();
				console.log("after ws.close()");
			}
		}
		
        ws.onerror = function(evt) {
            print("ERROR: " + evt.data);
        }
        return false;
    };
	
	document.getElementById("send").onclick = function(evt) {

		console.log("document.getElementById send");

		// disabled属性を設定 (openボタンを非活性化)
		document.getElementById("open").setAttribute("disabled", true);
		document.getElementById("open").style.color = "White";	
	
		// disabled属性を削除
		document.getElementById("send").removeAttribute("disabled");
		document.getElementById("send").style.color = "black";	

		// disabled属性を削除
		document.getElementById("close").removeAttribute("disabled");
		document.getElementById("close").style.color = "black";	
	
		if (!ws) {
			console.log("return false send");
			return false;			
		}

		lat += random(0.5, -0.5) * 0.00001 * 10 * 5;
		lng += random(0.5, -0.5) * 0.00002 * 10 * 5

		
		//var send_obj = new obj(personal_id, 35.654543,139.795534);  // idでエントリ
		var send_obj = new obj(personal_id, lat, lng);  // idでエントリ

		console.log("send:send_obj");	
		console.log(send_obj.id);	
		console.log(send_obj.lat);
		console.log(send_obj.lng);		

		var json_obj = JSON.stringify(send_obj);
		ws.send(json_obj);		

		/*
        print("SEND: " + input.value);
        ws.send(input.value);
		return false;
		*/

		return false;	
    };

	document.getElementById("close").onclick = function(evt) {
		console.log(" document.getElementById close");

		// disabled属性を削除
		document.getElementById("open").removeAttribute("disabled");
		document.getElementById("open").style.color = "black";

		// disabled属性を設定 (closeボタンを非活性化)
		document.getElementById("close").setAttribute("disabled", true);
		document.getElementById("close").style.color = "White";	

		// disabled属性を設定 (sendボタンを非活性化)
		document.getElementById("send").setAttribute("disabled", true);
		document.getElementById("send").style.color = "White";			


        if (!ws) {
            return false;
		}
	
		var send_obj = new obj(personal_id, 999.9, 999.9);  // 最初は"0"でエントリ

		console.log("close:send_obj");
		console.log(send_obj.id);		
		console.log(send_obj.lat);
		console.log(send_obj.lng);		

		var json_obj = JSON.stringify(send_obj);
		ws.send(json_obj);

        //ws.close();  // これはws.onmessageの方で実施
        return false;
    };
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server, 
"Send" to send a message to the server and "Close" to close the connection. 
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<!-- <p><input id="input" type="text" value="Hello world!"> -->
<button id="send">Send</button>
<button id="close">Close</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
`))

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>PruneMobile</title>

	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.css"/>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.js"></script>

	<script src="http://kobore.net/PruneCluster.js"></script>           <!-- これ、いずれローカルホストから取れるように換える -->
	<link rel="stylesheet" href="http://kobore.net/examples.css"/>      <!-- これも、いずれローカルホストから取れるように換える -->

	<!-- goのテンプレートのローカルって、どこになるんだろう? -->

</head>
<body>
<div id="map"></div>

<script>

	ws = new WebSocket("{{.}}"); // websocketの確立

	/*
	var print = function(message) {
		var d = document.createElement("div");
		d.textContent = message;
		output.appendChild(d);
	};
	*/

	// 引数にはミリ秒を指定。(例:5秒の場合は5000)
	function sleep(a){
  		var dt1 = new Date().getTime();
  		var dt2 = new Date().getTime();
  		while (dt2 < dt1 + a){
			dt2 = new Date().getTime();
		}
  		return;
	}

    var map = L.map("map", {
        attributionControl: false,
        zoomControl: false
    }).setView(new L.LatLng(35.654543, 139.795534), 18);
    // }).setView(new L.LatLng(35.598563, 139.475528), 18); 広袴

    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        detectRetina: true,
        maxNativeZoom: 18
    }).addTo(map);

    var leafletView = new PruneClusterForLeaflet(1,1);  // (120,20)がデフォルト

	ws.onopen = function (event) {
	}

	var markers = [];

	//var helicopterIcon = L.icon({ iconUrl: 'http://sintef-9012.github.io/PruneCluster/examples/helicopter.png', iconSize: [48, 48] });
    //var airplaneIcon = L.icon({ iconUrl: 'http://sintef-9012.github.io/PruneCluster/examples/airplane.png', iconSize: [48, 48] });

	// 受信すると、勝手にここに飛んでくる
	ws.onmessage = function (event) {
		// データをJSON形式に変更
		var obj = JSON.parse(event.data);

		console.log("233");	
		console.log(obj.id);
		console.log(obj.lat);						
		console.log(obj.lng);	

		if (obj.id == 0){  // idが未登録の場合
			console.log("obj.id == 0")
			// データをマーカーとして登録
			var marker = new PruneCluster.Marker(obj.lat, obj.lng);

			// 参考資料  http://embed.plnkr.co/WmtpkEqSDJFuFeuiYP54/
			//var marker = new PruneCluster.Marker(obj.lat, obj.lng, {
			//	//popup: "Bell 206 " + i,
			//	icon: helicopterIcon
			//});


			console.log(marker.hashCode);		
			markers.push(marker);
	
			leafletView.RegisterMarker(marker);
	
			console.log(markers);
			console.log(markers.length)

			obj.id = marker.hashCode;
			//ws.send(marker.hashCode); // テキスト送信
			var json_obj = JSON.stringify(obj);
			ws.send(json_obj);			
		} else if ((Math.abs(obj.lat) > 90.0) || (Math.abs(obj.lng) > 180.0)){ // 異常な座標が入った場合は、マーカーを消去する
			console.log("Math.abs(obj.lat) > 180.0)")
			for (let i = 0; i < markers.length; ++i) {
				if (obj.id == markers[i].hashCode){
					console.log(i)
					console.log(obj.id)										
					console.log("obj.id == markers[i].hashCode")

					//leafletView.RemoveMarkers(markers[obj.id]);  // これでは消えてくれません
					// 1つのマーカーを消すのに、面倒でも以下の2行が必要
					var deleteList = markers.splice(i, 1);					
					leafletView.RemoveMarkers(deleteList);

					// 以下失敗例リスト
					//leafletView.RemoveMarkers(markers[i].hashCode);  //これはダメ
					//leafletView.RemoveMarkers(markers[obj.id],'item');
					//leafletView.ProcessView(); // 試しに入れてみる
					//leafletView.RemoveMarkers(markers[i-1]);
					//leafletView.RemoveMarkers(markers);					
					break;
				}
			}
			obj.lat = 91.0;
			obj.lng = 181.0;
			var json_obj = JSON.stringify(obj);
			ws.send(json_obj);				
		} else {
			// 位置情報更新
			console.log("else")
			for (let i = 0; i < markers.length; ++i) {
				if (obj.id == markers[i].hashCode){
					var ll = markers[i].position;
					ll.lat = obj.lat;
					ll.lng = obj.lng;
					break;
				}
			}
			var json_obj = JSON.stringify(obj);
			ws.send(json_obj);	
		}
	}

	// 位置情報の更新
    window.setInterval(function () {
        leafletView.ProcessView();  // 変更が行われたときに呼び出されれなければならない
	}, 1000);

	// サーバを止めると、ここに飛んでくる
	ws.onclose = function(event) {
		//print("CLOSE");
		ws = null;
	}


    map.addLayer(leafletView);
</script>



</body>
</html>
`))

2020/10,江端さんの忘備録

viというのは、Windowsを除く、ほぼ全てのOSに標準実装されている、テキストエディタです。

"vi" is a text editor that comes standard in almost all operating systems, except Windows.

ですので、システムの構築や運用や保守に際しても、viさえ使えれば、最低限のことができます。

Therefore, as long as you can use vi, you can do the least building, operating and maintaining systems.

しかし、viは、分かりにくく、使いにくいことでも、有名です。

However, vi is also notoriously difficult to understand and use.

もちろん、使い込めば、こんなにも便利なツールはないというのも事実で、今なお根強いユーザがいます。

Of course, it's also true that once you use it, there is no tool more useful than this, and it still has power users.

# ちなみに私も、使えるviの操作は、全部で4つです。

# By the way, I also have a total of 4 vi operations that I can use.

-----

1年程前、ラズパイ上で作ったアプリケーションを、社内の事業部に技術移管する作業を行っていた時のことです。

About a year ago, I was in the process of transferring the technology of an application I had created on Raspberry pi to an internal business unit.

そこで集ったメンバは、20代、30代、40代のエンジニアと、その部署の部長を含めた5人でした。

There were five members gathered there, including engineers in their 20s, 30s, and 40s, and the head of the department.

私は、事業部が持ち込んできたラズパイに、アプリケーションを移管する作業と、そのレクチャーを行っていました。

I was working on transferring the application to a Raspberry pi, which was brought in by the division, and giving a lecture on it.

また、ネットワーク環境などに整合性を合わせる為に、構築情報を書き換える必要がありました。

I also had to rewrite the build information to make it more consistent with the network environment and other factors.

-----

私のレクチャーの話をもっとも理解して、私の作業手順のミスを指摘したのは、その「部長さん」だけでした。

It is the "manager" of the department, who understood my lecture deeply, and pointed out the errors in my operating procedures, was

私が、作業中に、

When I was working on it, and just muttering to myself

江端:「ああ、くそ! emacsが入っていなかったか。viは苦手なんだよな」

Ebata: "Oh, shit! Didn't have "emacs" on it. "vi" isn't good for me"

と呟いていたたところ、その部長さんが、

the general manager said

『私が、viで書き換えます』

"I'll rewrite it by "vi".

と申し出られて、ビックリしました。

I was surprised when he offered to do so

部長さんのスキルはさておき、

Aside from skills of the general manager.

―― 20代、30代、40代のエンジニアが、雁首そろえて、沈黙し続けていた

"All engineers in their twenties, thirties, and forties kept silent"

に、驚きました。

was that. I thought,

『こりゃ、UNIXコマンドすら触ったことがない、という感じだな』

"It's like they've never even touched a UNIX command before"

と思いました。

まあ、システムの利用者であれば、「UNIX? 何それ?」と言っても良いと思います。

Well, if you're a user of the system, I think it's safe to say "UNIX? What's that?"

が、システムを構築し、サービスを提供する側のエンジニアが「果たして、それで良いのか?」とは思います。

But as an engineer who builds systems and provides services, I have to ask myself, "Is that really good enough?

私も判断がつきかねています ―― 単なる「じじいのボヤキ」かもしれません。

I'm not sure I can judge it either -- maybe it's just "old man's blabbermouth".

それでも、はっきりと言えることは「ラズパイは、UNIX(Linux)コマンド使えないと、1mmも動かせない」です。

Nevertheless, what I can say clearly is "If you can't use UNIX (Linux) commands, you can't run Raspberry pi".

-----

ちなみに、私の勤務する会社の社是は『技術で社会に貢献する』です。

Incidentally, my companay motto is "Contribute to society through technology".

この一点のみで、会社と私は繋っているといっても、過言ではありません。

It would not be an exaggeration to say that this is the only link between the company and me.

2020/10,江端さんの忘備録

以前、マグカップの代替として、計量カップを購入する話をしました。

I've talked before about buying a measuring cup as a mug.

この計量カップ、絶好調です。

This measuring cup is really great.

例えば、カフェオレを作る場合、計量カップに、インスタントコーヒーを投入した後、100ccの牛乳を入れ、300ccの水を加え、電子レンジに放り込むだけで、

For example, to make a cafe au lait, simply pour the instant coffee into a measuring cup, add 100cc of milk, 300cc of water, and toss it into the microwave.

―― いつでも、一定品質のカフェオレが飲める

"constant quality cafe au lait at all times."

ということは、とても素晴しいことです。

So that's wonderful.

-----

嫁さん:「それが『素晴しい』と思える感性が理解できない」

Wife: "I don't understand the sensitivity of thinking that's 'wonderful'"

江端:「家族全員分、購入しようかと思っているくらいだが・・・」

Ebata: "I'm even thinking of buying one for the whole family..."

嫁さん:「絶対やめてね」

Wife: "Absolutely not"

長女:「そんなものより、お洒落な食器を購入してよ」

Senior daughter: "Don't worry about that, buy me some fancy dishes"

江端:「いいよ。私も食器は好きだ」

Ebata: "Okay. I like the dishes too"

私は観光に行くと、その地方の特産の陶器を、自分用のお土産として購入しています。

Whenever I go sightseeing, I buy local pottery for myself as a souvenir.

-----

江端:「しかし、陶器の内側に、目盛が付いているものが、見あたらなくてな」

Ebata: "But I can't find anything with a scale on the inside of the pottery"

嫁さん:「なんで、そんなもの・・・」

Wife: "Why is that...?"

江端:「いや、これを使えば、『どれくらいのシチューを皿に投入したか』、そして『現在、どれくらいの速度でシチューを食べているか』が、一目瞭然だろう?」

Ebata: "Well, it will tell me at a glance 'how much stew I've thrown into your plate' and 'how fast I're currently eating the stew', won't it?"

嫁さん:「・・・」

Wife: "...

長女:「パパは、そういうことが『嬉しい』んだ」

Senior daughter: "Daddy, that's what makes you 'happy'"

江端:「嬉しい」

Ebata:"It makes me happy"

長女:「そうか・・・。パパが『嬉しい』なら、私も『嬉しい』よ」

Senior daughter: "Well... If Daddy's 'happy', then I'm 'happy' too"

2020/10,江端さんの忘備録

緊急案件発生につき、今、次女に、(超速習で)C言語とGo言語とJavaScriptを教えています。

I'm currently teaching (super-fast) C, Go language and JavaScript to my junior daughter due to an urgent project that has arisen.

もちろん、プログラム初学者であれば、"Python"あたりが常道であることは、よく理解しています。

Of course, you know well that "Python" is the way to go for beginners in programming.

初学者に、"C言語"を履修させるなんぞ、狂気の沙汰であることも、よく理解しています。

I am well aware of the insanity of making a beginner take a course in "C language".

-----

これは、次女からの懇願されて行っていることです。

This is being done at the request of my junior daughter.

しかし、所定の期日までに履修を完了させる為には、私は「私の母国語」しか教えることができないのです。

However, in order to complete the course by the prescribed deadline, I can only teach "my native language".

-----

故に ―― これは「虐待」ではありません。

Therefore -- this is not "abuse",

「教育」です。

but "education"

通報しないで下さい。

Please don't call the police.

2020/10,江端さんの技術メモ

常日頃から御指導頂いているSさんから、Bad Elf 2300の位置情報をキャプチャするhtmlファイルの内容を教えて頂いた。忘れないように、残しておく。

Bad ElfをBTでリンクしたiPadで稼働を確認済み(iPhoneでは稼働確認できなかった)

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <title>geolocation-sample</title>
</head>
<body>
  <div id="output"></div>
 
<script>
    var output = document.getElementById('output');
 
    // 位置情報の取得に成功した際のコールバック
    const successCallback = (position) => {
        console.log(position);
		output.innerHTML += "<P>==========";
		output.innerHTML += "<P>time:" + position.timestamp;
		output.innerHTML += "<P>latitude:" + position.coords.latitude;
		output.innerHTML += "<P>longitude:" + position.coords.longitude;
		output.innerHTML += "<P>altitude:" + position.coords.altitude;
		output.innerHTML += "<P>accuracy:" + position.coords.accuracy;
		output.innerHTML += "<P>altitudeAccuracy:" + position.coords.altitudeAccuracy;
		output.innerHTML += "<P>heading:" + position.coords.heading;	
		output.innerHTML += "<P>speed:" + position.coords.speeed;	
    };
 
    // 位置情報の取得に失敗した際のコールバック
    const errorCallback = (err) => {
        console.log(err);
		output.innerHTML += "Error\n";		
    };
 
    // 位置を監視する構成オプション
    // オプションの内容は次のリンクに書かれています。
    // https://developer.mozilla.org/ja/docs/Web/API/PositionOptions
    const options = {
        enableHighAccuracy: true,
        timeout: 5000,
        maximumAge: 0
    };
 
    let watchPositionID;
 
    window.onload = () => {
        // navigator.geolocation.watchPositionについては次のURLにかかれています。
        // https://developer.mozilla.org/ja/docs/Web/API/Geolocation/watchPosition
        watchPositionID = navigator.geolocation.watchPosition(successCallback, errorCallback, options);
    };
 
    // ブラウザーを閉じる前に位置の監視を止めます
    window.onbeforeunload = () => {
        navigator.geolocation.clearWatch(watchPositionID);
    }
</script>
</body>
</html>

2020/10,江端さんの技術メモ

ワーシャル-フロイド法
(ダイクストラは個別ルートでは早いが、先に全ルート計算しておくなら、
こっちの方法の法が速いこともある)

と、

STLのリストの使い方(ファンクションへのリストの渡し方とか、リストの複製の作り方とか)
などの、便利な技が仕込まれているので貼っておく

/*
  g++ -g wf.cpp -o wf
 
  ワーシャル-フロイド法
  (ダイクストラは個別ルートでは早いが、先に全ルート計算しておくなら、
  こっちの方法の法が速いこともある)
  
  と、

  STLのリストの使い方(ファンクションへのリストの渡し方とか、リストの複製の作り方とか)
  などの、便利な技が仕込まれているので貼っておく


 
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <list>   // list 利用のため

using namespace std;

//int d[100][100];  // d[i][k]:ノードiからノードkへの距離 
//int via[100][100];  // d[i][k]の間にある(少くとも1つの)中継ノード

double d[100][100];  // d[i][k]:ノードiからノードkへの距離 
int via[100][100];  //  d[i][k]の間にある(少くとも1つの)中継ノード

list<int> path[100][100];   // int 型の list を宣言  
 

#if 1
// 中継パスの表示ルーチン(再帰呼出し用)
void printPath1_aux(int begin, int end) {
  if (via[begin][end] == begin) {
	if (begin != end)
	  printf("%02d -> ", begin);
	return;
  }
  
  printPath1_aux(begin, via[begin][end]);
  printPath1_aux(via[begin][end], end);
}
#endif

// 中継パスの表示ルーチン(再帰呼出し用)
void printPath1_aux(int begin, int end, list<int>* p) {
  if (via[begin][end] == begin) {
	if (begin != end){
	  // printf("%02d -> ", begin);
	  p->push_back(begin);
	}
	return;
  }
  
  printPath1_aux(begin, via[begin][end], p);
  printPath1_aux(via[begin][end], end, p);
}


 
// 中継パスの表示ルーチン
#if 1
void printPath1(int start, int goal) {
  printPath1_aux(start, via[start][goal]);
  printPath1_aux(via[start][goal], goal);
  printf("%02d\n", goal);
}
#endif 

void printPath1(int start, int goal, list<int> *p ) {
  printPath1_aux(start, via[start][goal], p);
  printPath1_aux(via[start][goal], goal, p);
  // printf("%02d\n", goal);
  p->push_back(goal);

}

 
int main(void)
{
  // 変数の初期化
  for(int i = 0; i < 100; i++){
	for(int j = 0; j < 100; j++){
	  d[i][j] = 999.9; // 距離の初期化(でっかい値を入力しておく(INT_MAXは足し算の時に桁上がりが起こるので使わない)
	  via[i][j] = i; // ノードiからノードkへの経由値の初期化 
	}
  }
 
 #if 0
  // 確認用の表示
  printf("\n[STEP1]\n");
 
  for(int i = 0; i < 100; i++){
	for(int k = 0; k < 100; k++){
	  printf("d[%d][%d]):%f\t",i,k,d[i][k]);
	  printf("via[%d][%d]):%d\n",i,k,via[i][k]);
	}
  }
#endif

  //// ここからは実際の距離を手書き
 
  for(int i = 0; i < 100; i++){
	d[i][i] = 0; //// 同じノードへの距離は0になるので、上書き
  }
 
  //ノード番号の通番を以下のようにする
  // [0][2] → "02", [4][9] → "49", [9][[9] → "99"
  // 座標は1ケタ内に留める

  for (int y = 0; y < 5; y++){
	for (int x = 0; x < 9; x++){

	  int n_num = x * 10 + y;

	  // + ( 1, 0)
	  int x_new = x + 1;
	  int y_new = y;

	  if (x_new < 9){
		int n_num_next = x_new * 10 + y_new;
		d[n_num][n_num_next] = 0.069;
		
		printf("1:d[%02d][%02d]=%f\n",n_num, n_num_next, d[n_num][n_num_next]);

	  }

	  // + (-1, 0)
	  x_new = x - 1;
	  y_new = y;

	  if (x_new > -1 ){
		int n_num_next = x_new * 10 + y_new;
		d[n_num][n_num_next] = 0.069;
		printf("2:d[%02d][%02d]=%f\n",n_num, n_num_next, d[n_num][n_num_next]);
	  }

	  // + ( 0, 1)
	  x_new = x;
	  y_new = y + 1;

	  if (y_new < 5 ){
		int n_num_next = x_new * 10 + y_new;
		d[n_num][n_num_next] = 0.069;
		printf("3:d[%02d][%02d]=%f\n",n_num, n_num_next, d[n_num][n_num_next]);
	  }

	  // + ( 0,-1)
	  x_new = x;
	  y_new = y - 1;

	  if (y_new > -1 ){
		int n_num_next = x_new * 10 + y_new;
		d[n_num][n_num_next] = 0.069;
		printf("4:d[%02d][%02d]=%f\n",n_num, n_num_next, d[n_num][n_num_next]);
	  }
	}
  }

  // 実験用上書き
  d[02][12] = 0.025;  
  d[12][22] = 0.025;  
  d[22][32] = 0.025;  
  d[32][42] = 0.025;  
  d[42][52] = 0.025;  
  d[52][62] = 0.025;  
  d[62][72] = 0.025;  
  d[72][82] = 0.025;  

  d[12][02] = 0.025;  
  d[22][12] = 0.025;  
  d[32][22] = 0.025;  
  d[42][32] = 0.025;  
  d[52][42] = 0.025;  
  d[62][52] = 0.025;  
  d[72][62] = 0.025;  
  d[82][72] = 0.025;  


#if 1
  // 確認用の表示
  printf("\n[STEP2]\n");
 
  for(int i = 0; i < 99; i++){
	for(int k = 0; k < 99; k++){
	  printf("d[%d][%d]):%f\t",i,k,d[i][k]);
	  printf("via[%d][%d]):%d\n",i,k,via[i][k]);
	}
  }
#endif
 

  // 経路長計算
  for (int k =0; k < 99; k++){  
	for (int i =0; i < 99; i++){
	  for(int j = 0; j < 99; j++){
		if(d[i][j] > d[i][k] + d[k][j]){
		  d[i][j] = d[i][k] + d[k][j];
		  via[i][j] = k; //更新処理
		}
	  }
	}
  }

 
#if 0
  // 計算結果
  printf("\n[STEP3]\n");
 
  for(int i = 0; i < 99; i++){
	for(int k = 0; k < 99; k++){
	  printf("d[%d][%d]):%f\t",i,k,d[i][k]);
	  printf("via[%d][%d]):%d\n",i,k,via[i][k]);
	}
  }
#endif 

#if 1
  // 経路パス表示
  printf("\n[Path]\n");
  for(int i = 0; i < 99; i++){
	for(int k = 0; k < 99; k++){
	  if (d[i][k] < 99.9){
		printf("d[%02d][%02d]:%f ",i,k,d[i][k]);
		printPath1(i, k);
		printPath1(i, k, &(path[i][k]));
	  }
	}
  }
#endif
  
  
  // イテレータ (反復子) の定義
  list<int>::iterator pos;

  list<int> l = path[83][04];
  // イテレータをずらしながら、全てのデータを取り出す。
  for(pos = l.begin(); pos!=l.end(); ++pos){
      cout << *pos << "\n";
  }

  printf("\n");


  // https://cpprefjp.github.io/reference/algorithm/copy.html
  // back_inserter を使って l2 へ設定。
  // back_inserter は要素をコピーするときに l2.push_back() するイテレータを作る関数。

  //std::list<int> l2;
  list<int> l2;  

  //std::copy(l.begin(), l.end(), back_inserter(l2));
  copy(l.begin(), l.end(), back_inserter(l2));

  // l2.erase(v.begin() + 2);       //  3番目の要素(9)を削除
  l2.erase(l2.begin());       // 先頭の要素を削除


  for(pos = l2.begin(); pos!=l2.end(); ++pos){
      cout << *pos << "\n";
  }


}

 

2020/10,江端さんの忘備録

私は、大学時代を除き、ティーンエイジャ全般を俯瞰して眺めると、あまり楽しくなかったように思います。

I don't think it was a lot of fun when I looked at teenagers in general, except when I was in college.

万一、私が著名人になって、私の通っていた高校から講演依頼があったとしても『絶対に断わる』だろうなぁ、と思っています。

Even if I become a celebrity and receive a lecture request from the high school I attended, I think I will definitely refuse.

高校には、私をいじめていた奴が2人いて、今でもその2人の名前を覚えていて、今でも私は許していないから、ということもあるのですが ―― それだけでもありません。

There were two guys in high school who were bullying me, and I still remember their names, and I still don't forgive them. But that's not all.

つまるところ、私は「学校」が「嫌い」だったのだと思います。

After all, I had hated "school".

しかし、「大学」は大好きだったので、私が憎悪してきたのは、

However, I loved "university", so I think that what I hated was

―― 「クラス」という「箱」

A "box" called "class"

だったのだと思います。

-----

■教師にその所掌範囲と責任範囲を明確にさせ、それ以外の管理対象(生徒)は無視しても良いと決められた「管理領域」

- "Management area" where it was decided that the teacher should clarify the scope of jurisdiction and responsibility, and the other management targets (students) could be ignored.

■無目的に強制的に集められたにも関わらず、特に理由もなく、他のクラスと比較され、勝たなければならないように仕向けられる「クラスター」

- "Cluster" that was forced to be gathered for no purpose, but is compared to other classes and forced to win for no apparent reason.

■そして、私のやることを妨害し、協力するどころか足をひっぱり続けた、邪悪な「クラスメイト」

- Evil "classmates" who interfered with what I was doing and kept pulling my legs instead of cooperating.

-----

運動会やら文化祭やらのこの時期になると、

At this time of the athletic and school festival, I remember

―― 私が、休日を返上してたった一人で作った文化祭の展示物の前で、集合写真を取ったクラスの連中

"The classmate who took a group photo in front of the cultural festival exhibit that I made by myself after returning from the holidays"

を思い出します。

あれから何十年を経過した今であっても、私は、彼らを許すことができません。

Even now, decades after that, I can't forgive them.

-----

だから、もしも私が著名人になって、私の通っていた高校から講演依頼があったとしても

So, even if I become a celebrity and receive a lecture request from the high school I attended, I think

『絶対に断わる』

"I will definitely refuse"

と思うのです。

2020/10,江端さんの忘備録

査読をお願いしていた機関から、私の執筆中の原稿のドラフトが戻ってきました。

My draft in progress came back from the institution I had asked to review it.

一部を抜粋します。

Here are some excerpts.

===== ここから =====

===== From here ======

なお、本文における「リスク」及び「不安」は、以下の意味で用いている。

The terms "risk" and "anxiety" in the text are used with the following meanings.

「リスク」とは、行動変容対象者(個人、企業)が受ける不利益の可能性であり、行動変容誘導システムによる行動変容の要請に承諾することで変化する。

"Risk" is the possibility of disadvantages to the behavior change target (individuals, companies), and changes by accepting the behavior change request by the behavior change guidance system.

例えば、個人においては、ウイルス性の感染症に罹患して、病欠を強いられて収入が断たれる可能性、及び、死亡する可能性などである。

For example, an individual may suffer from a viral infectious disease and be forced to take sick leave and lose his or her income, or may die.

また、企業においては、従業員に感染者が発生することによって、営業活動が中断させられ、経済的な損失を受ける可能性などである。

In addition, in a company, the occurrence of an infected person in an employee may interrupt business activities and cause financial loss.

一方「不安」とは、行動変容対象者が行動変容誘導システムによる行動変容の要請に承諾することで、他者とは異なる行動をとり、自分だけが不利益(解雇及び赤字業績など)を受けるかもしれないと感じる心配又は恐怖の感情である。

On the other hand, "anxiety" means that a person who is subject to behavior change accepts a request for behavior change by the behavior change guidance system, and acts differently from others, and only he / she suffers disadvantages (dismissal, deficit performance, etc.) feelings of anxiety or fear that you may feel.

なお、本文では、リスクとして、ウイルス性などの感染症に罹患する感染リスクを例にとって説明するが、リスク及び不安は感染症に対してのみ生じるものではない。

In this text, the risk of infection with a viral or other infectious disease will be explained as an example, however, risks and anxieties do not only occur for infectious diseases.

例えば、地震及び巨大台風などの災害では、対象者は避難しても自宅で待機してもリスクがあり、そのいずれを選ぶにしても、他人と異なる行動をする決断には不安が伴う。

For example, in a disaster such as an earthquake or a huge typhoon, there is a risk that the subject may evacuate or wait at home, and regardless of which one is selected, the decision to act differently from others is accompanied by anxiety.

これらのリスク及び不安は、行動変容の要請の承諾を妨げる要因となっている。

These risks and anxieties are factors that hinder the acceptance of behavior change requests.

以下、行動変容誘導システムについてより詳細に説明する。

Hereinafter, the behavior change guidance system will be described in more detail.

===== ここまで =====

===== To here ======

上記の抜粋を纏めると、

To summarize the above excerpts, that is,

―― 人を動かしたいなら「"リスク"を数値化して、"不安"を"恐怖"に持ち込め」

"If you want to move people, Quantify the 'risk' and Raise 'anxiety' to 'fear'"

です。

『「不安を煽って、世界を動かそう」という発想が、そもそも、古今東西の独裁者と変わらんなぁ』とも思いました。

I also thought, "The idea of 'agitating anxiety and moving the world' is the same as the dictator of the east and west in the first place."

まあ、最近は、独裁者などという立派なモノでなくても、誰でもSNSを使って普通にできるようになってきています。

Well, these days, anyone can use SNS to do it normally, even if it's not a dictator.

でも、「数値化」は、見たことがないかな。

But I wonder if I have never seen "quantification".

-----

私が提案している、この「行動変容誘導システム」は、

About this "behavior change guidance system" that I am proposing, even if I rewrite the following, the meaning seems to be understood.

「独裁者意志決定支援システム」

"Dictator decision support system"

とか

「炎上を援用した売名システム」

"Brand name system that uses the Internet flame"

と書き直しても、意味は通じそうです。

-----

まあ、審査の段階で、確実に弾かれるとは思いますが。

Well, I think it will definitely be rejected at the examination stage.

2020/10,江端さんの忘備録

昨日の日記に関連するお話は、これにて最後にしたいと思います。

This is the last story related to yesterday's diary.

-----

今回、絶対的な一次リソースとしての『「LGBT」支援の度が過ぎる』を読み返しました。

This time, I read back "The level of support for "LGBT" is too high" as an absolute primary resource.

が、当時と同じように、この文章の「読みにくさ」には、困りました。

However, as at that time, I was troubled by the "difficulty of reading" of this sentence.

"文章構成"がなっていなくて、"内容"に入りこむことができなかったからです。

I couldn't get into the "content" because the "sentence structure" was so bad.

-----

そもそも、私は、この文章を"論文"と称呼されていることに、強い違和感を感じます。

In the first place, I feel a strong sense of discomfort in calling this sentence a "paper".

これが"論文"なら「私」が可哀想過ぎます。

If this is a "paper", then "I" is too pitiful.

私は、論文とは、

I think that paper is

『仮説と、エビデンス(証拠)と、客観的なデータ(または計算)と、そこから導かれるロジカルな結論を記載するもの』

"A statement of a hypothesis, evidence, objective data (or calculations), and the logical conclusions to be drawn from them"

と、思っています。

そして、論文の執筆プロセスは、

And the process of writing a paper is that

- 査読者にボロクソに(但しロジカルに)批判されて、

- I am severely (but logically) criticized by the peer reviewers.

- 何度も検討や計算をやりなおして、

- I have to go through all the reviews and calculations again and again.

- それでも受理されず、

- the paper isn't accepted.

- 再投稿を繰り返し、

- I repost it repeatedly

- ようやく掲載の許諾を得る

- I finally get permission to publish it.

というものだと思っています。

-----

という訳で、私から、この文章の著者への提案です。

So, this is my suggestion to the author of this article.

一度、「新潮45」で発表された内容について、大学等でちゃんと論文指導を受けて、国内の学会に「査読付き論文」として提出してみるのはいかがでしょうか。

Once, about the contents released at "Shincho 45", you received proper dissertation guidance at universities etc. and how about submitting it as a "peer-reviewed paper" to a domestic academic society?

本人の思想がどうあれ、論文としての体(てい)を成していれば、原則、学会は論文を受理します(というか、受理しなければならないハズです)。

Regardless of the person's thoughts, the academic society will accept the dissertation (or rather, it must be accepted) as long as it forms the body of the dissertation.

本人の主観か客観的事実かが明確でない箇所があれば、「引用文献を明示しろ」と指導が入ります。

If there is a part where it is not clear whether the person is subjective or objective, you will be instructed to "specify the cited document".

面倒ですが安心ではあります。

It's a hassle, but it's safe.

国内の学会が受理してくれなけば、外国の学会に提出すれば良いです。

If the domestic academic society does not accept it, you can submit it to a foreign academic society.

英文翻訳が必要になるとは思いますが、翻訳は外注すれば足ります。

I think you will need an English translation, but it is enough to outsource the translation.

もし、世界中のどの学会にも受理して貰えなければ(そんなことはないとは思いますが)、査読のないカンファレンスペーパーの投稿と、学会講演を行うという手もあります。

If it is not accepted by any academic society in the world (I don't think that is the case), you can post an unrefereed conference paper and give a lecture at the academic society.

事前にアナウンスしておけば、多くの聴講者を集めることもできるでしょう。

If you announce it in advance, you will be able to attract a large number of listeners.

-----

先ずは、出身大学でお世話になった先生に、ご相談することからお勧めします。

First of all, I recommend that you consult with the teacher who helped you at your university.

学会や先生から拒否されたら、一番最後に、私(江端)にご相談下さい。

If you are rejected by an academic society or a teacher, please contact me (Ebata) as the last line.

-----

新潮45の『「LGBT」支援の度が過ぎる』を、

According to "The level of support for "LGBT" is too high" in Shincho45

■きちんとした査読付き論文で読める

- If I can read it in a proper peer-reviewed dissertation

または

or

■ロジカルな内容での講演を聴講できる

- If I can listen to a lecture with logical content

のであれば、私は、支援を惜しみません(本当です)。

I'm willing to help (it's true).