未分類

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

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

市役所から肝炎ウイルス検診の案内が来まして、現在、市の指定の診療所に来ています。

The city hall has sent me a guide to hepatitis virus screening, and I am currently at a clinic.

背景は良く分かっていませんが、「生涯1度だけの検診 + 無料」ということなので、受診してきました。

I don't know the background well, but I went to see it because it means "one-time examination + free of charge".

それに、セルシン(精神安定剤)の処方もして貰うことにしました。

I also decided to get a prescription for celsine (tranquilizer).

-----

私、結構長いこと、人間ドッグで「脂肪肝」を指摘されて続けていました。

I have been pointing out "fatty liver" in human dogs for quite a long time.

ダイエット(のコラム執筆)と、断酒によって、ようやく、これから逃れることができるようになりました。

By dieting (writing a column) and abstaining from alcohol, I was finally able to escape from this.

断酒の効果であるかかどうかは、はっきりと分からないのですが、「肩コリ」「腰痛」が、劇的に改善されました。

I'm not sure if it's the effect of abstinence, but "shoulder stiffness" and "backache" have improved dramatically.

「不眠」は、"アルコール"が"セルシン"に代わっただけ、という見方もできますが、大きな改善点は、『うたたね』ができるようになったことです。

It can be said that "insomnia" is just a replacement of "alcohol" with "selcin", but the major improvement is the ability to take a "nap".

これによって、仕事(特に連続10数時間のコラム執筆)中に、10分間程度の仮眠できるようになったことは、大きいです。

It is great that I was able to take a nap for about 10 minutes during work (especially writing a column for more than 10 hours in a row).

まあ、「アルコールを摂取することで睡眠時間を確保していた」という事実は、「アルコールに依存することで、無理矢理、眠りを得ていた」と言える訳で、つまるところ

Well, the fact that "I was able to secure sleep time by consuming alcohol" can be said to be "I was forced to sleep by depending on alcohol", after all, I can't deny

『アルコール依存症』

"Alcoholism"

を、否定することはできないでしょう。

-----

以前もお話しましたが、アルコール依存症であった私にとって、コンビニのお酒の棚の列は、今なお結構な「地獄ロード」です。

As I said before, for me, who was an alcoholic, the rows of liquor shelves at convenience stores are still quite a "hell road".

あれ、マジでキツイです。

That is really hard.

あのロードを止まらずに突っ切るのは、相当な精神力や胆力を必要とします。

It takes a great deal of mental strength and courage to pass through that road without stopping.

「酒が飲めないことは、人生の半分を損している」と言われますが ―― 全く同感です。

It is said that "No drink, no life". I totally agree.

お酒というのは、人類が生み出した至宝の飲料です。

Alcohol is a treasure drink produced by humankind.

それでも、私が酒を断っているのは、

Still, the reason I refuse to drink is

―― まだまだ、書きたい物(コラムとプログラム)が沢山残っているから

"There are still a lot of things I want to write (columns and programs)"

です。

-----

とは言え、始終、頭がクリアで居続けるのは、なかなか辛いものです。

However, it's hard to stay clear from beginning to end.

コラムを書き上げた時とか、プログラムが動いた時は「ご褒美に、一杯やりたい」です。

When I finish writing the column, or when the program works well, I want to give a drink as a reward.

それ故、私は、安全性が担保され、習慣性がなく、依存性もなく、一定時間後には完全に正気に戻る(合法であることは言うまでもない)という ―― そういう、薬や嗜好品の開発をやりたい。

Therefore, I I want to do development of kind of medicine or luxury item, with safe, non-addictive, non-dependent, and completely going back to sane after a period of time (not to mention legal).

------

リタイア後に、再度大学受験をして、化学科に入学するのもいいな、と考えています。

After retirement, I think it would be good to take the university entrance exam again and enroll in the Department of Chemistry.

志望動機を問われたら

If I am asked about my motive, I will answer

『安全性を完全に担保した、究極の覚醒剤の開発です』

"Development of the ultimate stimulant that completely guarantees safety"

と答える予定です。

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

Access to XMLHttpRequest at 'http://localhost:8080/api/loc/2' from origin 'null' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
test.html:30 GET http://localhost:8080/api/loc/2 net::ERR_FAILED

が取れなくて困っていたので、以下のアドインを入れて強制的に解決した(デフォルトで、"なんでもOK")としておけばよさそう)。

ちなみに、これを使うと、Twitterで通信障害「"問題が発生しました" "やりなおす"」が発生するようなので、実験が終ったら、解除しておいた方が良いです。

ちなみに、上記の問題はjavascriptで発生している問題ですが、Go連携でなんとかしようかと思っていたところで、Sさんから、以下の情報を頂きました。

[Golang] gorilla/muxのCORS対処法

多分、今日あたりからぶつかりそうな問題でした。

で、今ぶつかっている問題ですが、JSON.parseから、情報が取れないんですよね。なんでかなー、色々試しているんだけど、もう疲れてきたなぁ。VSCでデバッグしているんですけど、dataの中身がスカスカなんですよねー。うん困った。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0.1//EN" "http://www.w3.org/TR/html4/strict.dtd">

<html lang="ja">
<head>
<meta http-equiv="Content-Type" Content="text/html;charset=Shift_JIS">
<meta http-equiv="Content-Script-Type" content="text/javascript">

<title>同期通信テスト</title>

<script>
    // (1)XMLHttpRequestオブジェクトを作成
    var xmlHttp = new XMLHttpRequest();


    // (2)onreadystatechangeイベントで処理の状況変化を監視
    //xmlHttp.onreadystatechange = function(){
    //    if(this.readyState == 4 && this.status == 200){
    //        //console.log(this.responseText);
    //        data = this.response
    //    }
    //}

    //var data;

    // (3)HTTPのGETメソッドとアクセスする場所を指定
    xmlHttp.open("GET", "http://localhost:8080/api/loc/2", true);
    //xmlHttp.onload = function(){
    //    if (xmlHttp.status >= 200 && xmlHttp.status < 200){
    //        data = JSON.parse(xmlHttp.responseText);
    //    } else {
    //        console.log("error");
    //    }
    //}

    //xmlHttp.responseType = 'json'

    // (4)HTTPリクエストを送信 
    xmlHttp.send();  

    var data = JSON.parse(xmlHttp.responseText);

    //alert(xmlHttp.responseText);
    console.log(xmlHttp.response);
    console.log(xmlHttp.responseText);
    JSON.parse(data.responseText);
    //console.log(data);
    //var user = JSON.parse(this.responseText);   
    //var user = JSON.parse(data);   
    //var user = JSON.parse(this.responseText);
    //alert(user);
</script>

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

サーバ側 loc_rest_server.go というファイル名で保存して、
>go run loc_rest_server.go
で起動


package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"time"

	"github.com/gorilla/mux"
)

/*

type GetLoc struct {
	Message string `json:"message"`
	Name    string `json:"name"`
}
*/

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

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

// ErrorResponse error response
type ErrorResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

// locService loc service
func locService(ctx context.Context, number string, tm time.Time) (*GetLoc, error) {

	if number == "1" {
		return &GetLoc{
			ID:  number,
			Lat: "35.653976",
			Lng: "139.796842",
		}, nil
	}

	if number == "2" {
		return &GetLoc{
			ID:  number,
			Lat: "35.653758",
			Lng: "139.794192",
		}, nil
	}

	return nil, nil
}

// AppHandler application handler adaptor
type AppHandler struct {
	h func(http.ResponseWriter, *http.Request) (int, interface{}, error)
}

func (a AppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	encoder := json.NewEncoder(w)
	status, res, err := a.h(w, r)
	if err != nil {
		log.Printf("error: %s", err)
		w.WriteHeader(status)
		encoder.Encode(res)
		return
	}
	w.WriteHeader(status)
	encoder.Encode(res)
	return
}

/*
// GetLoc GetLoc
func (app *App) GetLoc(w http.ResponseWriter, r *http.Request) (int, interface{}, error) {
	res, err := locService(r.Context(), "", time.Now())
	if err != nil {
		app.Logger.Printf("error: %s", err)
		e := ErrorResponse{
			Code:    http.StatusInternalServerError,
			Message: "something went wrong",
		}
		return http.StatusInternalServerError, e, err
	}
	app.Logger.Printf("ok: %v", res)
	return http.StatusOK, res, nil
}
*/

// GetLocWithNumber GetLoc with number
func (app *App) GetLocWithNumber(w http.ResponseWriter, r *http.Request) (int, interface{}, error) {
	val := mux.Vars(r)
	//res, err := locService(r.Context(), val["id"], time.Now())
	res, err := locService(r.Context(), val["id"], time.Now())
	if err != nil {
		app.Logger.Printf("error: %s", err)
		e := ErrorResponse{
			Code:    http.StatusInternalServerError,
			Message: "something went wrong",
		}
		return http.StatusInternalServerError, e, err
	}
	app.Logger.Printf("ok: %v", res)
	return http.StatusOK, res, nil
}

// App application
type App struct {
	Host   string
	Name   string
	Logger *log.Logger
}

func main() {
	host, err := os.Hostname()
	if err != nil {
		log.Fatal(err)
	}

	app := App{
		Name:   "my-service",
		Host:   host,
		Logger: log.New(os.Stdout, fmt.Sprintf("[host=%s] ", host), log.LstdFlags),
	}
	// for gorilla/mux
	router := mux.NewRouter()
	r := router.PathPrefix("/api").Subrouter()
	//r.Methods("GET").Path("/loc").Handler(AppHandler{h: app.GetLoc})
	//r.Methods("GET").Path("/loc/staticName").Handler(AppHandler{h: app.GetLoc})
	r.Methods("GET").Path("/loc/{id}").Handler(AppHandler{h: app.GetLocWithNumber})

	if err := http.ListenAndServe(":8080", router); err != nil {
		log.Fatal(err)
	}
}

クライアント側
main.go で保存して、
>go run main.go
で起動

参考とさせて頂いたページ「Goでhttpリクエストを送信する方法

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	//url := "http://google.co.jp"
	url := "http://localhost:8080/api/loc/2"

	resp, _ := http.Get(url)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(byteArray)) // htmlをstringで取得
}
動作結果
>go run main.go
 {"id":"2","lat":"35.653758","lng":"139.794192"}

JSONで展開するにはどうしたらいいかな?

参考にさせて頂いたのは「goでjson apiを叩く

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

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

func main() {
	//url := "http://google.co.jp"
	url := "http://localhost:8080/api/loc/2"

	resp, _ := http.Get(url)
	defer resp.Body.Close()

	/*
		byteArray, _ := ioutil.ReadAll(resp.Body)
		fmt.Println(string(byteArray)) // htmlをstringで取得
	*/

	var d GetLoc

	fmt.Printf("======Body (use json.Unmarshal)======\n")
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	err = json.Unmarshal(body, &d)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%v\n", d)

	fmt.Printf("ID:%v\n", d.ID)
	fmt.Printf("Lat:%v\n", d.Lat)
	fmt.Printf("Lng:%v\n", d.Lng)
}

出力結果

>go run main.go
======Body (use json.Unmarshal)======
{2 35.653758 139.794192}
ID:2
Lat:35.653758
Lng:139.794192

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

本日は、コラムがリリースされた日なので、日記はお休みです。

Today, new my column is released, so I take a day off.

踊るバズワード ~Behind the Buzzword(6)量子コンピュータ(6):

ひねくれボッチのエンジニアも感動で震えた「量子コンピュータ至高の技術」

Dancing Buzzword-Behind the Buzzword (6) Quantum Computer (6)

Even a cynical lonely engineer was moved to tears by the "supreme technology of the quantum computer"

-----

今回、量子コンピュータの最終回です。

This is the final installment of the "Quantum Computer" series.

最終回なので、監修をして頂いた、ご自称「量子コンピュータオタクのTさん」に、コメントの寄稿をお願いしたのですが、

As this is the last episode, I asked the self-proclaimed "quantum computer geek, Mr. T", who supervised the article, to contribute a comment.

『少し考えてみましたが、いい文章が浮かばなかった』

"I gave it some thought, but I couldn't come up with a good sentence"

との理由でご辞退されました。

He declined to do so because of the reason.

まあ、突然寄稿を依頼されて、ホイホイ応じる人間は、私の知る限り、私だけです。

Well, as far as I know, I'm the only person who is suddenly asked to contribute and responds immediately.

-----

長い間、「無礼な後輩シリーズ」として、レビューをして貰っている後輩にも、コメントの寄稿について訊ねてみたのですが、

For a long time, I asked the juniors who have been reviewing it as a "rude junior series" about contributing comments. However he said to me.

「メリットがない」

"No merit"

と一蹴されました。

I was kicked off by him.

後輩:「江端さん、もう何年もコラム書き続けているのに、全然メジャーになっていないじゃないですか」

"Ebata-san, you've been writing a column for years now, but you haven't become a major player at all.

後輩:「江端さんにフリーライド(ただ乗り)しても、私にメリットがないですよ」

"There's no benefit to me in free-riding with Ebata-san"

-----

確かに ―― その通り。

Certainly -- that's right.

コラム執筆に、こんなに膨大な時間と労力を注いでいるのに、「日の当たるところにいる」という感覚が ―― "ゼロ"

I've put such an enormous amount of time and effort into writing the column, however, the sense of "being in the sun" is "zero"

何故だろう。おかしい。なぜ、私の努力はこんなにも空回りしてしまうのだろう。

I don't know why. It's funny. Why are my efforts so spinning out of control?

―― というような話を、嫁さんにしたところ、

I told my wife about the story, and she said

嫁さん:「そもそも『メジャーになりたい』と思っているの?」

Wife: "Do you want to 'be a major player' in the first place?

と、言われました。

-----

あ、どんどん、嫌なことを思い出してきた。

Oh, I'm starting to remember more and more of the bad stuff.

そういえば、大学院の入試勉強の際にも、

Come to think of it, when I was studying for my graduate school entrance exam.

―― エバちゃんってさぁ、「入試の目的(合格)」を忘れて、「勉強の為に勉強する」ようになるから、心配だよ

I'm worried about you, Eba-chan, because you're going to forget the purpose of the entrance exam (passing) and start "studying for the sake of studying".

と、下宿に閉じ籠って勉強している私に、手紙を送ってくれたゼミの友人(女性)のメッセージを思い出してしまいました。

It reminded me of a message from a seminar friend (a woman) who sent me a letter, when I was studying in the boarding house.

# 電子メールを使うだけで、オタク呼ばわりされた時代でした(BBS(nifty)経由のみ)

# It was an era when people were called nerds just by using e-mail (only via BBS (nifty))

-----

そういえば、私、"メジャー"というものも、よく分からないんですよ。

By the way, I don't really understand what the "major" is.

どのくらい分からないかと言えば、「東京に出てきて、一旗上げる」という、その「旗」の内容が分からない程度に、分からないです。

I don't know it like the contents of a "flag" of "raising in Tokyo".