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

私が愛用している時計は、こちらに記載したものです。

This is about my watch repair.

耐用年数をとうに越えており、すでに同じ時計は入手できませんが、電池交換を繰り返しながら、使い続けています。

It's already past its service life and the same watch is no longer available. However I continue to use it, changing the batteries repeatedly.

リストバンドはジョイント部が欠けており、もはやリストバンドの交換もできない状況です。

The wristband is missing a joint and the wristband can no longer be replaced.

ですので

So,

―― Amazonで購入したリストバンドを、瞬間接着剤で、強制的にジョイント

"I buy a wristband from Amazon, with instant glue, to force a joint"

しています。

リストバンドが劣化して、切断した場合には、その接続の部分を無理矢理引き剥がして、固化した接着剤をドライバで削ぎ取っています。

If the wristband deteriorates and is cut, the connection part is forcibly peeled off and the solidified adhesive is scraped off with a screwdriver.

案外なんとかなるものです。

It's surprisingly manageable.

時計に限らず、私は、"モノを直して使い続ける"のが好きです。

Besides watches, I like to "fix things and keep using them."

-----

デバイスを完全に停止するまで使い倒す ―― これは、あまり経済的な行為とは言えません。

"Use it until the device is completely shut down". This is not very economical.

国内消費にも、GDPにも貢献しません。

It does not contribute to domestic consumption or GDP.

私自身「既存システムの積極的な廃棄」をアジテーションしてきた、という自覚すらあります。

I even realize that I have agitated "active disposal of existing systems".

そんな私が『モノを大切にしましょう』などと、おこがましいことが言える立場ではありません。

I'm not in a position to say something, such as "Let's take good care of things."

私は、デバイスの「完全な死」を見届けたいだけです。

I just want to see the "complete death" of the device.

私がいかなる手を尽しても、絶対的に救えない、究極の死を。

The ultimate death that I can never save no matter what I do.

(続く)

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

取り敢えず、週末の成果(ちゃんと動くものではないので、そのまま使うことはお勧めしません)

"client1.go"の内容

package main

import (
	"flag"
	"log"
	"net/url"

	"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", "0.0.0.0:8080", "http service address") // テスト
//var addr = flag.String("addr", "localhost:8080", "http service address") // テスト
var upgrader = websocket.Upgrader{} // use default options

func main() {
	flag.Parse()
	log.SetFlags(0)
	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo2"}
	log.Printf("connecting to %s", u.String())

	c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
	if err != nil {
		log.Fatal("dial:", err)
	}
	defer c.Close()

	gl := GetLoc{
		ID:  1,
		Lat: 35.653976,
		Lng: 139.796841,
	}

	log.Printf("ID:%d", gl.ID)
	log.Printf("Lat:%f", gl.Lat)
	log.Printf("Lng:%f", gl.Lng)
	//err = c.WriteJSON(mt, gl)
	err = c.WriteJSON(gl)
	if err != nil {
		log.Println("write:", err)
	}
}

"server8.go"の内容

/*
// server7.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 server7.go      (適当なシェルから)
// http://localhost:8080  (ブラウザ起動)
*/

package main

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

	"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

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()

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

		gl := new(GetLoc)

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

		log.Printf("ID:%d", gl.ID)
		log.Printf("Lat:%f", gl.Lat)
		log.Printf("Lng:%f", gl.Lng)

		if err != nil {
			log.Println("read:", err)
			break
		}
	}
}

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 {
			//mt, message, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
			_, _, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
			if err != nil {
				log.Println("read:", err)
				break
			}

			// JSON(位置情報)を無理やり入れてみた

			gl := GetLoc{
				ID:  1,
				Lat: 35.653976,
				Lng: 139.796842,
			}

			//log.Printf("recv_serv: %s", gl)
			//err = c.WriteJSON(mt, gl)
			err = c.WriteJSON(gl)
			if err != nil {
				log.Println("write:", err)
				break
			}
		}
	*/

	gl := GetLoc{
		ID:  1,
		Lat: 35.653976,
		Lng: 139.796842,
	}

	//log.Printf("recv_serv: %s", gl)
	//err = c.WriteJSON(mt, gl)
	err = c.WriteJSON(gl)
	if err != nil {
		log.Println("write:", err)
	}

}

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

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

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>PruneCluster - Realworld 50k</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);

    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)がデフォルト

	/*
	// 送信5発(3秒単位)を送信
	ws.onopen = function (event) {
		for (let i = 0; i < 1; i++){
			ws.send("Ebata is great"); 
			//print("send: Ebata is great");
	
			//print("Start sleep");
			sleep(1000);
			//print("End sleep");
		}
	}
	*/

	var markers = [];

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

		// データをマーカーとして登録
		var marker = new PruneCluster.Marker(obj.lat, obj.lng);
		markers.push(marker);
		leafletView.RegisterMarker(marker);

        // leafletView.ProcessView();  // 変更が行われたときに呼び出されなければなりません。		
	}


	/*
	// 100人分を登録する
    var size = 1;
    var markers = [];
    for (var i = 0; i < size; ++i) {
        var marker = new PruneCluster.Marker(35.654543 + (Math.random() - 0.5) * 0.00001 * size, 139.795534 + (Math.random() - 0.5) * 0.00002 * size);

        markers.push(marker);
		leafletView.RegisterMarker(marker);
		
	}
	*/
	

	// ランダムウォークさせる
    window.setInterval(function () {
        for (i = 0; i < 1; ++i) {
			//var coef = i < size / 8 ? 10 : 1;
			var coef = 10;
            var ll = markers[i].position;
            ll.lat += (Math.random() - 0.5) * 0.00001 * coef;
            ll.lng += (Math.random() - 0.5) * 0.00002 * coef;
        }

        leafletView.ProcessView();  // 変更が行われたときに呼び出されれなければならない
	}, 500);


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


    map.addLayer(leafletView);
</script>



</body>
</html>
`))

未分類

(昨日の続きです)

(Continuation from yesterday)

江端:「まあ、それはさておき、『一旗』って何だ?」

Ebata: "Well, aside from that, what's a 'the flag' ?"

嫁さん:「『故郷に錦を飾る』とも言うね」

Wife: "It's also called 'return to their hometown in glory'"

江端:「『一旗』から想起されるイメージって、ミュージシャン、アイドル、役者、くらいしか思い付かないんだけど」

Ebata: "The only images that 'the flag' reminds me of are musicians, idols and actors"

嫁さん「あるいは、政治家とか野球選手・・・いずれにしても、この程度が私達のイメージが限界かな」

Wife: "Or a politician or a baseball player... Anyway, I guess this is the limit of our image.

江端:「自分の望みのことが達成できれば、それは、『一旗上がった』ということにならないのかな? 自分なりの目標に対して、それを達成すれば、足るというような」

Ebata: "If they can achieve what they want, is that 'they could raise the flag' ? I suppose it's good enough for my own goals, if I achieve them."

嫁さん「・・・いや、それはちょっと違うかな」

Wife:"...no, that's not enough.

江端:「?」

Ebata:"?"

嫁さん:「『故郷に錦を飾る』と合わせて考えれば、それは、第三者にとって ―― 特に出身地の ―― 多くの人間が、共通して、"成功"と認識できる何かでなければ、成立しない」

Wife: "If they use the phrase of "return to their hometown in glory", the flag should be something that could be perceived as a "success." for many people, especially in their hometown."

江端:「・・・多くの人が共通して価値のあると認められるもの ―― つまり"金"か」

Ebata: "...what most people consider to be of common value -- 'money'"

嫁さん「あるいは、有名になること ―― "名声"も含まれるかな」

Wife: "Or to be famous -- I guess that includes 'fame'"

江端:「第三者から観測可能な"金"と"名声"というとになると ―― ああ、なるほど、『ミュージシャン』などは、分かりやすい」

Ebata: "When it comes to 'money' and 'fame' that can be observed by a third party - ah, I see, 'musicians' is easy to understand.

嫁さん「そうだねえ。そこに至るプロセスが、努力と能力だけでなく、運も必要であり、加えて"その可能性が恐しく小さい"という要件も必要だね」

Wife: "Yes, you're right. The process of getting there requires not only effort and ability, but also luck, and in addition, 'the possibility should be terribly small'.

-----

正直、私は、

Honestly, I thought that

―― 面倒くさいな

"It's troublesome"

と思いました。

私は、「自分の意志で目指す成功」であるならともかく、「他人の目に映る成功」なんぞの為に、自分が振り回されることなど、ゴメンです。

I don't want to be at the mercy of "success in the eyes of others", but "success of my own volition.

-----

最後にいらんことを書き残します。

Finally, I'll leave you with something unnecessary.

こういう、他人視点の"一旗"幻想を持っている方は、このページの一読をお勧めします。

If you have an illusion of the flag in the eyes of others, let you read this article.

文字を読むのが面倒であるなら

If you can't be bothered to read the words, it is O.K. to watch these figures.

この図と、この図と、この図、を見るだけでも、結構です。

参考くらいにはなると思います。

I think it's at least a reference.

未分類

  1. 内容:位置情報をサーバからクアイアント(Webブラウザ)に1回のみ一回だけ送信して、その情報を、適当にランダムウォークさせるだけのプログラム
  2. 方針 できるだけ、プログラムは汚いまま残す
/*
// server7.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 server7.go      (適当なシェルから)
// http://localhost:8080  (ブラウザ起動)
*/

package main

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

	"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

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 {
			//mt, message, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
			_, _, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
			if err != nil {
				log.Println("read:", err)
				break
			}

			// JSON(位置情報)を無理やり入れてみた

			gl := GetLoc{
				ID:  1,
				Lat: 35.653976,
				Lng: 139.796842,
			}

			//log.Printf("recv_serv: %s", gl)
			//err = c.WriteJSON(mt, gl)
			err = c.WriteJSON(gl)
			if err != nil {
				log.Println("write:", err)
				break
			}
		}
	*/

	gl := GetLoc{
		ID:  1,
		Lat: 35.653976,
		Lng: 139.796842,
	}

	//log.Printf("recv_serv: %s", gl)
	//err = c.WriteJSON(mt, gl)
	err = c.WriteJSON(gl)
	if err != nil {
		log.Println("write:", err)
	}

}

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

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

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>PruneCluster - Realworld 50k</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"/>      


</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);

    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)がデフォルト

	/*
	// 送信5発(3秒単位)を送信
	ws.onopen = function (event) {
		for (let i = 0; i < 1; i++){
			ws.send("Ebata is great"); 
			//print("send: Ebata is great");
	
			//print("Start sleep");
			sleep(1000);
			//print("End sleep");
		}
	}
	*/

	var markers = [];

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

		// データをマーカーとして登録
		var marker = new PruneCluster.Marker(obj.lat, obj.lng);
		markers.push(marker);
		leafletView.RegisterMarker(marker);

        // leafletView.ProcessView();  // 変更が行われたときに呼び出されなければなりません。		
	}


	/*
	// 100人分を登録する
    var size = 1;
    var markers = [];
    for (var i = 0; i < size; ++i) {
        var marker = new PruneCluster.Marker(35.654543 + (Math.random() - 0.5) * 0.00001 * size, 139.795534 + (Math.random() - 0.5) * 0.00002 * size);

        markers.push(marker);
		leafletView.RegisterMarker(marker);
		
	}
	*/
	
	/*
		ちなみにオブジェクトの削除は、以下の様らしい
		// Remove all the markers
		pruneCluster.RemoveMarkers();

		// Remove a list of markers 
		pruneCluster.RemoveMarkers([markerA,markerB,...]);  リスト単位で消去する、ことか → marker[0],maker[1] と指定して消していくのだと思われ

		// こんなのがあった random.10000-delete.html
		document.getElementById('delete').onclick = function () {
			if (size >= 1000) {
				var deleteList = markers.splice(0, 1000);
				leafletView.RemoveMarkers(deleteList);
				size -= 1000;
			}
			return false;
		};

	*/
	
	// ランダムウォークさせる
    window.setInterval(function () {
        for (i = 0; i < 1; ++i) {
			//var coef = i < size / 8 ? 10 : 1;
			var coef = 10;
            var ll = markers[i].position;
            ll.lat += (Math.random() - 0.5) * 0.00001 * coef;
            ll.lng += (Math.random() - 0.5) * 0.00002 * coef;
        }

        leafletView.ProcessView();  // 変更が行われたときに呼び出されれなければならない
	}, 500);


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


    map.addLayer(leafletView);
</script>



</body>
</html>
`))

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

最近、NHKの「ドキュメント72時間」を見ています。

Recently, I've been watching NHK's "documentary series 72 Hours".

ある一定の場所で、72時間、市井の人々の取材を放送する番組です。

It is a program that broadcasts 72 hours of coverage of the people of the city in a certain location.

■NHKスペシャルのように、『聞き漏らさずに視聴しなければ』というような緊張間もなく、

There's no need to be tense like the NHK specials, where I have to watch it without missing a beat.

■他の民放番組のように、特異な行動や言動をする人を、面白可笑しく編集するでもなく(自分の街をインタビューされたシーンは、かなり不快だった)

It's not like other commercial TV shows that edit out people who act and behave in a peculiar way for fun or funny.(The scene where they interviewed someone from my town was quite uncomfortable.)

肩の力を抜いて見られるので、結構気にいっています。

I like it a lot because I can relax to watch it.

もっとも、この「ドキュメント72時間」にしても、市井の人々の「いい感じのコメント」や「琴線にふれるコメント」だけを集めている、という感は否めません。

However, it cannot be denied that this "documentary series 72 Hours" is a collection of "good comments" and "heartful comments" from the people.

しかし、番組として構成する以上、それは仕方のないことです。

But as long as it's structured as a program, I think that it cannot be helped.

-----

先日、嫁さんと2人で、夕食を食べながら、「東京・隅田川 花火のない静かな夏に」を見ていました。

The other day, my wife and I were watching "Tokyo Sumida River, Quiet Summer without Fireworks" while eating dinner.

そこでは、「一旗上げようと東京にやって来て」「それが叶わずに今に至る」という高齢者の方のインタビューシーンが出てきました。

There was an interview scene with an elderly man who said that "he came to Tokyo to make a name for himself", and "he couldn't do it".

先日の日記にも記載したのですが、

I mentioned it in my diary the other day.

―― "一旗"って何だろう?

"What does "the flag" mean?"

と、ずっと考えています。

I keep thinking about it.

-----

江端:「『東京に出てくれば、何かチャンスがある』という発想が良く分からん。そもそも、東京に、語るほどのメリットがあるのか?」

Ebata: "I don't understand the idea that "there are opportunities if you come to Tokyo". In the first place, are there any advantages to Tokyo that are worth talking about?

嫁さん:「大学の数、仕事の数、出会う人間の数、娯楽の数、手に入れられる情報とその速度、その他もろもろ違うでしょう」

Wife: "The number of colleges, jobs, people you meet, entertainments, and the information you can get and the speed at which you can get it, and everything else will be different"

江端:「まあ、私の場合、ネットと通販があれば、十分に足るけど ―― まあ、その通りかなぁ。特に「人脈」については、都会は圧倒的に有利かもしれない」

Ebata: "Well, in my case, the internet and mail order are enough for me. However, I guess you're right. Especially when it comes to "networking", the city may have a huge advantage.

嫁さん:「まあ、パパの場合は『人間嫌い』だから、そのメリットはないと思うけど」

Wife: "Well, in your case, I don't think you have the benefit of that because you are a misanthrope.

(続く)

(To be continued)

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

ようやく動いた。疲れた。JSONを送り込むのは、明日以降ね。

忘備録

  • カレントディレクトリの中にdistというディレクトリを作って、LeafletStyleSheet.css PruneCluster.d.ts PruneCluster.js を放り込んだ
  • カレントディレクトリの中にLeafletStyleSheet.css,PruneCluster.d.ts,PruneCluster.js,examples.cssを放り込んでおいた
  • 上記はあまり意味なかったらしい(プログラムの下を御参照)。

 

使い方は、

>go run server5.go

http://localhost:8080

で起動する。

/*
// server5.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 server5.go      (適当なシェルから)
// http://localhost:8080  (ブラウザ起動)
*/

package main

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

	"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

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 {
		//mt, message, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
		_, _, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
		if err != nil {
			log.Println("read:", err)
			break
		}

		// JSONを無理やり入れてみた

		gl := GetLoc{
			ID:  1,
			Lat: 35.653976,
			Lng: 139.796842,
		}

		//log.Printf("recv_serv: %s", gl)
		//err = c.WriteJSON(mt, gl)
		err = c.WriteJSON(gl)
		if err != nil {
			log.Println("write:", err)
			break
		}

		/*
			log.Printf("recv_serv: %s", message) // 受信したメッセージの表示
			err = c.WriteMessage(mt, message)    // 受信したメッセージの返送
			if err != nil {
				log.Println("write:", err)
				break
			}
		*/
	}
}

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

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

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>PruneCluster - Realworld 50k</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="PruneCluster.js"></script>

    <link rel="stylesheet" href="examples.css"/>
</head>
<body>
<div id="map"></div>

<script>
    var map = L.map("map", {
        attributionControl: false,
        zoomControl: false
    }).setView(new L.LatLng(35.654543, 139.795534), 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)がデフォルト

    var size = 100;
    var markers = [];
    for (var i = 0; i < size; ++i) {
        var marker = new PruneCluster.Marker(35.654543 + (Math.random() - 0.5) * 0.00001 * size, 139.795534 + (Math.random() - 0.5) * 0.00002 * size);

        markers.push(marker);
        leafletView.RegisterMarker(marker);
    }

    window.setInterval(function () {
        for (i = 0; i < size / 2; ++i) {
            var coef = i < size / 8 ? 10 : 1;
            var ll = markers[i].position;
            ll.lat += (Math.random() - 0.5) * 0.00001 * coef;
            ll.lng += (Math.random() - 0.5) * 0.00002 * coef;
        }

        leafletView.ProcessView();
    }, 500);

    map.addLayer(leafletView);
</script>
</body>
</html>
`))

ところが、今朝、PC断ち上げて再起動させてみたところ、昨日の状況に戻ってしまいました。

試しに、自分のサーバ(kobore.net)に、PruneCluster.jsexamples.cssをアップして、さらにdistというディレクトリ掘って、LeafletStyleSheet.cssを放りこんでみたら動きました。

<script src="PruneCluster.js"></script>
<script src="http://kobore.net/PruneCluster.js"></script>

<link rel="stylesheet" href="examples.css"/>
→ <link rel="stylesheet" href="http://kobore.net/examples.css"/>

まあ、いずれ修正するとして、動くならなんでも良いので、このまま進めます。

 

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

なんで、JSON.parse()で、パースができないのだろう? と3時間くらい悩んだあげく、大文字を小文字変換されていることに、ようやく気がつきました。

ひどくない?

気がつかなかった私が悪いの?

/*
// server4.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 server2.go      (適当なシェルから)
// http://localhost:8080  (ブラウザ起動)
*/

package main

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

	"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

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 {
		//mt, message, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
		_, _, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
		if err != nil {
			log.Println("read:", err)
			break
		}

		// JSONを無理やり入れてみた

		gl := GetLoc{
			ID:  1,
			Lat: 35.653976,
			Lng: 139.796842,
		}

		//log.Printf("recv_serv: %s", gl)
		//err = c.WriteJSON(mt, gl)
		err = c.WriteJSON(gl)
		if err != nil {
			log.Println("write:", err)
			break
		}

		/*
			log.Printf("recv_serv: %s", message) // 受信したメッセージの表示
			err = c.WriteMessage(mt, message)    // 受信したメッセージの返送
			if err != nil {
				log.Println("write:", err)
				break
			}
		*/
	}
}

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

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

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

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;
}


ws = new WebSocket("{{.}}");

// 送信5発(3秒単位)を送信
ws.onopen = function (event) {
	for (let i = 0; i < 5; i++){
		ws.send("Ebata is great"); 
		print("send: Ebata is great");
	
		print("Start sleep");
		sleep(1000);
		print("End sleep");
	}
}

// 受信すると、勝手にここに飛んでくる
ws.onmessage = function (event) {
   	//console.log(event.data);
	print("RESPONSE: " + event.data);
	var obj = JSON.parse(event.data);

	// test code  (JSON.parse()が大文字を無視することを確認するテスト)
	//const json = '{"ID":1, "Lat":35.653976, "Lng":139.796842}';
	//print("json:" + json);
	//var obj = JSON.parse(json);	

	print("obj:" + obj);  	
	print("ID:" + obj.id);     // obj.IDなら読み取れない
	print("Lat:" + obj.lat);   // obj.Latなら読み取れない
	print("Lng:" + obj.lng);   // obj.Lngなら読み取れない
}

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

</script>
</head>
<body>

<div id="output"></div>

</body>

</html>
`))

 

 

 

 

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

本日、東京証券取引所のシステムがダウンしました。

Today, the Tokyo Stock Exchange system is down.

原因については不明ですが、システムエンジニアとして申し上げることができるとしたら、

I'm not sure what caused this, but as a systems engineer, if I could offer something to you, it would be

――『各期、連休等の初日と最終日は、システムは停止するものと考えておくこと』が肝要

"It is essential to assume that the system will be shut down on the first and last day of each term, consecutive holidays, etc."

ということです。

システム保守や、システムリプレースは、期末に行われ、その起動は期の初日に行われることがあるからです。

This is because system maintenance and system replacement may take place at the end of the period, and its launch may take place on the first day of the period.

どんなに事前にチェックしても、「テスト環境」と「運用環境」は違います。

No matter how much you check in advance, there is a difference between a "test environment" and an "operational environment".

現実世界の、全ての状況を想定したテストというのは、夢物語です。

Testing for all situations is just a dream.

どんなシステムも、基本的には「ダウン」する運命にあるのです。

Any system is essentially doomed to go "down".

で、その「ダウン」のポテンシャルが最大化するのが、「各期や連休等の初日と最終日」ということです。

And that "down" potential is maximized on the first and last day of each period, holiday, etc.

-----

ところで、システムを様々なシナリオで攻撃することで、製品の弱点を見つけ出す役割の人を「テスター」と言います。

By the way, the person responsible for finding weaknesses in a product by attacking the system in various scenarios is called a "tester".

米国赴任中に、私、テスター用のプログラムを作り、各種の攻撃を仕掛け続けていたのですが、

While I was stationed in the U.S., I wrote a program for our testers that kept launching all sorts of attacks.

―― なんとなく、チームメンバから嫌な顔をされていました。

"Somehow, I was getting a bad look from my team members"

特に、開発者は、私がパーティションに顔を出すだけで、「あの日本人が、また来た」という顔をしていたように思います。

In particular, the developers seemed to get that "that Japanese guy is here again" look on their faces when I just showed up at the partition.

無理はありません。

It's reasonable.

そりゃ、作ったばかりのコードは、自分の創作物であり、自分の子どもです。

That's because the code they just created is their own creation and their own child.

それを、いきなり「苛められる」のですから、愉快であろうはずがありません。

It can't be pleasant because they are suddenly "tortured".

-----

逆に、私が作った開発物を、他のテスターによって攻撃されることがありましたが ―― 私も不快でした。

On the other hand, there have been times when I've been attacked by other testers on developments I've made -- and I've been uncomfortable, too.

それが、仕事であることは分かっていても、です。

Even though I know it's just a work.

そんな日は、『ダウンの再現条件を、ちゃんとスペックアウトしてこいよ!』などと愚痴を言いながら、強い酒を飲んでいました。

On a day like that, I said that "spec out the conditions for reproducing down" and I was drinking with complaining about such things.

まあ、仕事というのものは、理不尽なものである、ということです。

Well, I can say that works are unreasonable things

-----

ともあれ、システムエンジニアの一人として、

Anyway, as a systems engineer,

―― ダウンしないシステムなんて、存在しない

"Any system goes down"

ということだけは、申し上げたいのです。

I would like to say that.

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

golangのtemplateを試してみたくて(javascriptに疲れてきて)、Webブラウザから一回だけメッセージを送信するプログラム(server2.go)を書いてみました。
# Echoプログラムはいままで使ってきたプログラムを使わせて貰っています。
「GolangでCUIでWebsocketを試したい」にドンピシャのソースコードはこちら

やっていることは、サーバを立ち上げて、ブラウザからhttp://localhost:8080を起動して、(1)WebSocketをオープンして、(2)テキストメッセージを"5つ"送付して、(3)サーバから戻ってきたメッセージを受信・表示して、(4)クローズしているだけ

/*
// server3.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 server2.go      (適当なシェルから)
// http://localhost:8080  (ブラウザ起動)
*/

package main

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

	"github.com/gorilla/websocket"
)

//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

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 {
		mt, message, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
		if err != nil {
			log.Println("read:", err)
			break
		}
		log.Printf("recv_serv: %s", message) // 受信したメッセージの表示
		err = c.WriteMessage(mt, message)    // 受信したメッセージの返送
		if err != nil {
			log.Println("write:", err)
			break
		}
	}
}

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

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

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

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;
}


ws = new WebSocket("{{.}}");

// 送信5発(3秒単位)を送信
ws.onopen = function (event) {
	for (let i = 0; i < 5; i++){
		ws.send("Ebata is great"); 
		print("send: Ebata is great");
	
		print("Start sleep");
		sleep(3000);
		print("End sleep");
	}
}

// 受信すると、勝手にここに飛んでくる
ws.onmessage = function (event) {
   	//console.log(event.data);
   	print("RESPONSE: " + event.data);
}

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

</script>
</head>
<body>

<div id="output"></div>

</body>

</html>
`))