私の英語力では、内容が頭の中で滑るので、https://www.matsim.org/downloads/ を翻訳してみました。
https://kobore.net/matsim/jp_www_matsim_org_downloads.html
江端智一のホームページ
私の英語力では、内容が頭の中で滑るので、https://www.matsim.org/downloads/ を翻訳してみました。
https://kobore.net/matsim/jp_www_matsim_org_downloads.html
[Control]+[Shift]でSKKが出てくる
[ALT] + [半角/全角漢字] で 日本語入力とアルファベット入力が切り変わる
モバイルPCとして購入した
を設定しているのですが、やっぱりEmacs+SKKが欲しくて、色々作業しています(やること山ほどあるけど、これをやらないと作業が捗らないので)
今回は、これまでと違って、SKKFEPというものを入れていみました(SKKのインストールで苦労するのが嫌だったからです)
今、動いているのですが、なんで動いているのか分からない状態なので、現時点で覚えていることを書き下しておきます。
参考にしたのはこちらのページ → SKK日本語入力FEP 今だ!インストールだ! (fc2.com)
Webサイト上の最新版の手動更新をブラウザでクリックして直接実行をすると、以下の画面がでてきて、設定してくれるようです。(管理者モードにしたかもしれないし、しなかったかもしれない)

で、次に、

で、これで多分、SKKFEPが、この辺に表示されるだろう・・・と思ったのですが、これが出てこない。

のように、Microsoft IMEしかでてこなくて、頭を抱えました。

手当たりしだい、メニューを叩いて上記のように「言語バーのオプション」を叩いたら、以下の画面がでてきて、「キーの詳細設定」を選ぶとやっと、"SKK"という言葉がでてきました。

「シーケンスキーの変更」を押して、

のようにして、Ctrl+Shiftを押したら、やっとこさ、SKKのモードになりました。
少しインターフェースに戸惑うことがありますが、取り敢えず、後の設定は後で考えることにします。
以上
https://www.nikkoken.or.jp/pdf/project/2021/A-854.pdf
これまで述べてきたように、MATSimは移動の均衡をモデル化するための統合シミュレーションツールキットとして設計されました。このため、MATSimは交通需要と供給の両方をモデル化できる必要があります。の部分をモデル化する必要があります。この目的のために、MATSimは図2.3に示すように、均衡に収束するための共進化反復ループを採用しています。図2.3に示すとおりである(p.19)。

このループは、各個人の1日の行動とトリップチェーンという形で、最初の旅行需要から始まります。ループが始まる。このアクティビティとトリップチェーンは、Mobsimの段階で道路ネットワークにロードされる。一日のシミュレーションが終わると を計算し,その結果をもとに,各エージェントのアクティビティ・チェーン(計画)のスコアを計算する.最後の このとき,各エージェントは,以前の計画から生成された計画の集合を所有し を持つことになり,そのスコアに基づいて次の反復で実行するものを選択しなければならない.
ネットワーク負荷の部分では、MATSimのデフォルトとしてトラフィックフローモデルQSimが採用されている。この QSimは計算効率の良い待ち行列ベースのアプローチを適用している。基本的に、車両が道路セグメントに進入する際 を計算することができる.次に、その車両は道路の待ち行列の最後尾に挿入される。第二に,車両は道路の待ち行列の最後尾に挿入され,最後に,道路の流出能力に応じて道路を離れます.ここで、流出速度は各道路に固有であり、容量属性で指定される。
スコアリングとリプランニングの部分では、各エージェントにとって「より良い」プランと「より悪い」プランを区別する基準を提供するために、一般化された効用様式でスコアが定式化される。再計画では、このスコアをもとに、過去の計画、すなわち特定のエージェントが行った計画をもとに、完全に新しい計画を「品種改良」する遺伝的アルゴリズム(または進化的アルゴリズムと呼ばれる)を適用する。一般に、突然変異と選択という二つの演算子が使われる。突然変異演算子は、以前に実行された計画中の特定のコンポーネントを変更し、この変更された(すなわち、突然変異)計画を次の反復に使用する。次の反復処理に採用する。
MATSimで使用できるセレクタは、使用する平衡状態によって複数種類あります。
る平衡状態によって異なる。ベストリプライセレクタは、前回の反復からベストプランを選択するものです。
MNLセレクタは、プラン選択のアプローチを離散的選択の方法で適用し、エージェントの行動における以下の確率を考慮する。エージェントの行動には確率がある。
MATSim OSS & フリー Google Scholar(GS) 5750
http://bin.t.u-tokyo.ac.jp/kaken/pdf/2014_wakabayashi1.pdf
インプットデータ
・1日のスケジュール
・道路ネットワーク
・モデルのパラメータ
2. 1日のスケジュール効用を計算
𝑈𝑎𝑐𝑡 : 活動の効用 𝑈𝑡𝑟𝑎𝑣𝑒𝑙 : 移動の効用 𝑖:1回の活動
1. シミュレーションの実行
3. プランの変更
・出発時刻
・交通手段
・経路
・目的地
・駐車場
1~3を効用(Score)が収束するまで繰り返す
https://progsoft.net/ja/software/matsim
MATSimは、Javaで実装されたアクティビティベースの拡張可能なマルチエージェントシミュレーションフレームワークです。
これはオープンソースであり、インターネットからダウンロードできます(MATSim、2016; GitHub、2015)。
フレームワークは大規模なシナリオ向けに設計されています。つまり、対象となる機能を効率的に処理するために、すべてのモデルの機能が取り除かれています。
並列化も非常に重要です(例:Dobler and Axhausen、2011; Charypar、2008)。 たとえば、ネットワーク負荷シミュレーションの場合、キューベースのモデルが実装され、非常に複雑で計算コストの高い自動車追従動作が省略されます(セクション1.3を参照)。
現時点では、MATSimは、アクティビティベースのモデルの分析の一般的な単位である1日をモデル化するように設計されています(たとえば、Bowmanによるレビュー、2009aを参照)。
それにもかかわらず、原則として、複数日モデルを実装することができます(Horni and Axhausen、2012年)。
SUMO OSS & フリー 交差点解析などに使われている様子 GS 35700
https://kudzuyu.github.io/SUMO-wiki-ja/SUMO_at_a_Glance/
交通シミュレーションの研究をやっていることから、MATSimの調査を行っています。


もう、昨日から本当にドタバタやっているのですが、MATSimの環境構築について、記載されているドキュメントが、非常に乏しく、日本語のものはゼロと言って良いと思います。
いろいろやって、現時点で上手くいかなかったこと
> git clone https://github.com/maptic/matsim-docker.git
> docker-compose build
> docker-compose upで、"docker-compose build" は成功したのですが、"docker-compose up" は以下ようにになってしまいました。
>docker run -v /opt/matsim/data/input -v /opt/matsim/data/output maptic/matsim:latest
「The Multi-Agent Transport Simulation MATSim」を読むことにしました。

から、C:\Users\tomoi\matsim-14.0 に展開しておきました。
つぎにここから「The Multi-Agent Transport Simulation MATSim」を落してきました。

で、

の、

のこれを出せるかどうかが、勝負だと見ました。
まず、javaをインストールしました。
私は、https://adoptopenjsk.net から、64bit版WindowsJDKのZIP版をダウンロードしました。
1. Choose a Version では、OpenJDK11 を、2. Choose a JVMではHotSpotを選択、Other platformsのボタンをクリックしました。
Windows, x64, .zip でダウンロードして、C:\Program Files\Java\jdk-11.0.17+8\bin にパスを通しておきました。
:\Users\tomoi\matsim-14.0>java -jar matsim-14.0.jar を起動すると、以下の画面が表示されます。

で、先程展開した、C:\Users\tomoi\matsim-14.0のサンプルの中にあるconfig.xmlを使うと、何か計算をしているようです。


2023-01-01T00:33:27,301 INFO IOUtils:215 Resolved C:\Users\tomoi\matsim-14.0\examples\berlin\config.xml to C:\Users\tomoi\matsim-14.0\examples\berlin\config.xml
ただ、今のところ、エラー終了してしまうようです。
上記の問題については、https://github.com/matsim-org/matsim-code-examples/issues/736 の方に記載されていました。
内容はこんな感じでした。
残念ながら、これはまだ修正されていないバグです。GUI クラスを実行する代わりに、RunMatsim クラスを直接実行することもできます。
java -cp matsim-example-project-0.0.1-SNAPSHOT.jar org.matsim.project.RunMatsim
デフォルトの設定ファイルとは別の設定ファイルを使用したい場合は、コマンドが変わります。
java -cp matsim-example-project-0.0.1-SNAPSHOT.jar org.matsim.project.RunMatsim /path/to/your/config.xml となります。
こんにちは、私は同じエラーに遭遇し、それはWindowsにリンクされているように見えた。このバグが修正されるかどうか、またいつ修正されるか知っていますか?
Windows の MATSimGUI で AccessDeniedException が発生する matsim-org/matsim-libs#2052
matsim-org/matsim-libs#2052 にissueを作成しました。ご指摘ありがとうございました。
JDKのバージョンを確認してください。この問題は、jdk 17 以降で発生するようです。JDK11でmatsim-14.0.jarを動かしてみてください。がんばってください。
でも、私、jdk11を使っているんだけどなぁ?
続きがありました。
私もJDK11を使用していますが、同じ問題に遭遇しました。なぜなら、私が知る限り、LegHistogramChartクラスはpngを保存するために適切なローカルパスを送っていますが、jfreechartはC:³³に一時ファイルを作成しようとし、パーミッション問題に遭遇しています。簡単な回避策は、<param name="createGraphs" value="false" />を追加して、グラフをオフにすることです。をconfig.xmlのcontrollerモジュールに追加することです。そうすれば問題なく実行できます。もちろん、グラフは表示されませんが、その他の出力や解析はすべて可能です。
ということのようです。
で、C:\Users\tomoi\matsim-14.0\examples\berlin\config.xml の中をちょっくら弄ってみました。
<module name="controler">
<param name="outputDirectory" value="./output/berlin" />
<param name="firstIteration" value="0" />
<param name="lastIteration" value="0" />
<param name="createGraphs" value="false" />
</module>
の赤字の一行を追加したら、エラーで止まることはなくなりました。

さて、多分、ここ(C:\Users\tomoi\matsim-14.0\examples\berlin\output\berlin)にできているファイルを使って、シミュレーションの見える化をするのだろうと思います。
https://www.matsim.org/downloads/の
シミュレーションが実行されると、その出力ディレクトリに多くのファイルが作成されます。GUIには出力ディレクトリに到達するためのボタンがあることに注意してください。そのうちの1つは、いわゆるイベントファイルで、通常10回目のイテレーションごとに生成されます。0回目の繰り返しのイベントファイルは .../ITERS/it.0/...0.events.xml.gz に格納されています。これには可視化できる情報がたくさん含まれています。
MATSimの出力を視覚化する最も簡単な方法は、VIAを使うことです。エージェントの数に制限のある無料版がダウンロード可能です。VIAを起動すると、大きな黒い領域が表示されるはずです。ここがトラフィックを可視化する場所です。このエリアの左側に、上部に4つのアイコンがある小さなエリアがあります(「コントロール」)。最初のアイコン(Data Sources)をクリックします。このセクションにファイルをドラッグ&ドロップするか(例:network.xml、events.xml.gz)、下部の「+」をクリックして追加するファイルを選択できます。いずれかの方法で、まずnetwork.xmlを利用可能なデータのリストに追加し、次にevents.xml.gzを追加します。これでビジュアライザーがデータを認識し、どのようにビジュアル化するかを指示することができます。
次に、コントロールセクションの 2 番目のアイコン ("レイヤー") をクリックします。初期状態では、背景レイヤーのみが表示されています。をクリックして、表示させたいデータを選択します。読み込んだnetwork.xmlでネットワークを視覚化するよう既に提案されているはずなので、「Add」をクリックします。しばらくすると、ネットワークが可視化エリアに表示されるはずです。をもう一度クリックし、今度はレイヤーの種類として「ビークル」を選択します。event.xml.gzファイルはすでに選択されています。追加]をクリックします。イベントに依存するレイヤと同様に、レイヤタグの下部に「データを読み込む」ボタンが表示されます。これをクリックすると、イベントから車両の位置が抽出されます。
VIAというビューアについて調査中
https://simunto.com/via/download
出典を忘れてしまいましたが、どなたかが作られたコードを丸パクリさせて頂きました。→たぶん"これ"
ノード1 → "A", ノード2 → "B", ノード3 → "C", ノード4 → "D", ノード5 → "E" として取り扱います。

package main
import (
"errors"
"fmt"
)
// ノード
type Node struct {
name string // ノード名
edges []*Edge // 次に移動できるエッジ
done bool // 処理済みかを表すフラグ
cost int // このノードにたどり着くのに必要だったコスト
prev *Node // このノードにたどりつくのに使われたノード
}
func NewNode(name string) *Node {
node := &Node{name, []*Edge{}, false, -1, nil}
return node
}
// ノードに次の接続先を示したエッジを追加する
func (self *Node) AddEdge(edge *Edge) {
self.edges = append(self.edges, edge)
}
// エッジ
type Edge struct {
next *Node // 次に移動できるノード
cost int // 移動にかかるコスト
}
func NewEdge(next *Node, cost int) *Edge {
edge := &Edge{next, cost}
return edge
}
// 有向グラフ
type DirectedGraph struct {
nodes map[string]*Node
}
func NewDirectedGraph() *DirectedGraph {
return &DirectedGraph{
map[string]*Node{}}
}
// グラフの要素を追加する (接続元ノード名、接続先ノード名、移動にかかるコスト)
func (self *DirectedGraph) Add(src, dst string, cost int) {
// ノードが既にある場合は追加しない
srcNode, ok := self.nodes[src]
if !ok {
srcNode = NewNode(src)
self.nodes[src] = srcNode
}
dstNode, ok := self.nodes[dst]
if !ok {
dstNode = NewNode(dst)
self.nodes[dst] = dstNode
}
// ノードをエッジでつなぐ
edge := NewEdge(dstNode, cost)
srcNode.AddEdge(edge)
}
// スタートとゴールを指定して最短経路を求める
func (self *DirectedGraph) ShortestPath(start string, goal string) (ret []*Node, err error) {
// 名前からスタート地点のノードを取得する
startNode := self.nodes[start]
// スタートのコストを 0 に設定することで処理対象にする
startNode.cost = 0
for {
// 次の処理対象のノードを取得する
node, err := self.nextNode()
// 次に処理するノードが見つからなければ終了
if err != nil {
return nil, errors.New("Goal not found")
}
// ゴールまで到達した
if node.name == goal {
break
}
// 取得したノードを処理する
self.calc(node)
}
// ゴールから逆順にスタートまでノードをたどっていく
n := self.nodes[goal]
for {
ret = append(ret, n)
if n.name == start {
break
}
n = n.prev
}
return ret, nil
}
// つながっているノードのコストを計算する
func (self *DirectedGraph) calc(node *Node) {
// ノードにつながっているエッジを取得する
for _, edge := range node.edges {
nextNode := edge.next
// 既に処理済みのノードならスキップする
if nextNode.done {
continue
}
// このノードに到達するのに必要なコストを計算する
cost := node.cost + edge.cost
if nextNode.cost == -1 || cost < nextNode.cost {
// 既に見つかっている経路よりもコストが小さければ処理中のノードを遷移元として記録する
nextNode.cost = cost
nextNode.prev = node
}
}
// つながっているノードのコスト計算がおわったらこのノードは処理済みをマークする
node.done = true
}
func (self *DirectedGraph) nextNode() (next *Node, err error) {
// グラフに含まれるノードを線形探索する
for _, node := range self.nodes {
// 処理済みのノードは対象外
if node.done {
continue
}
// コストが初期値 (-1) になっているノードはまだそのノードまでの最短経路が判明していないので処理できない
if node.cost == -1 {
continue
}
// 最初に見つかったものは問答無用で次の処理対象の候補になる
if next == nil {
next = node
}
// 既に見つかったノードよりもコストの小さいものがあればそちらを先に処理しなければいけない
if next.cost > node.cost {
next = node
}
}
// 次の処理対象となるノードが見つからなかったときはエラー
if next == nil {
return nil, errors.New("Untreated node not found")
}
return
}
func main() {
// 有向グラフを作る
g := NewDirectedGraph()
// グラフを定義していく
g.Add("A", "B", 2)
g.Add("A", "C", 7)
g.Add("A", "D", 5)
g.Add("B", "D", 4)
g.Add("C", "E", 2)
g.Add("D", "C", 1)
g.Add("D", "E", 4)
g.Add("E", "C", 2)
// "s" ノードから "z" ノードへの最短経路を得る
path, err := g.ShortestPath("A", "E")
// 経路が見つからなければ終了
if err != nil {
fmt.Println("Goal not found")
return
}
// 見つかった経路からノードとコストを表示する
for _, node := range path {
fmt.Printf("ノード: %v, コスト: %v\n", node.name, node.cost)
}
}
出力結果
C:\Users\ebata> go run .\dijkstra.go
ノード: E, コスト: 8
ノード: C, コスト: 6
ノード: D, コスト: 5
ノード: A, コスト: 0
ラベル修正法の練習(だけ)

「Golangでcsvファイルを読み出す」の記事は多いのですが、特定の行まで引っ張り出す情報があまりないので、メモを残しておきます。
/*
以下のcsvファイル(kai_20220522holyday18.csv)の中身を取り出す
id,age,type,departure_name,departure_number,departure_lat,departure_lng,arrival_name,arrival_number,arrival_lat,arrival_lng
0,43,resident,,,34.173408,131.470684,,,34.155862,131.501246
1,24,resident,,,34.179449,131.482543,,,34.164116,131.471791
2,42,resident,,,34.168739,131.470768,,,34.160989,131.491124
3,21,resident,,,34.169494,131.469934,,,34.173498,131.471351
4,58,resident,,,34.185295,131.47414,,,34.191481,131.49456
5,48,resident,,,34.150778,131.480747,,,34.16536,131.471872
6,56,resident,,,34.16536,131.471872,,,34.174066,131.479312
7,73,resident,,,34.155731,131.500845,,,34.16776,131.472831
8,47,resident,,,34.167237,131.471785,,,34.155775,131.476531
9,21,resident,,,34.154931,131.50468,,,34.156678,131.49581
10,37,resident,,,34.16727,131.472899,,,34.171253,131.471177
11,40,resident,,,34.147241,131.474921,,,34.150675,131.486268
12,67,resident,,,34.173683,131.476347,,,34.173643,131.471027
13,28,resident,,,34.183079,131.484303,,,34.174245,131.474592
14,46,resident,,,34.146154,131.472711,,,34.159611,131.491548
15,25,resident,,,34.162497,131.489283,,,34.147212,131.475984
*/
package main
import (
"encoding/csv"
"fmt"
"log"
"os"
"strconv"
)
func main() {
file, err := os.Open("kai_20220522holyday18.csv") // 先ほど入手した郵便番号データをos.Openで開く
if err != nil {
log.Fatal(err)
}
defer file.Close()
r := csv.NewReader(file)
rows, err := r.ReadAll() // csvを一度に全て読み込む
if err != nil {
log.Fatal(err)
}
// 行ごとに
for i, row := range rows {
if i == 0 {
continue // CSVのヘッダー行を無視
}
age := row[1]
departure_lat, err := strconv.ParseFloat(row[5], 64)
if err != nil {
log.Fatal(err)
}
departure_lng, err := strconv.ParseFloat(row[6], 64)
if err != nil {
log.Fatal(err)
}
arrival_lat, err := strconv.ParseFloat(row[9], 64)
if err != nil {
log.Fatal(err)
}
arrival_lng, err := strconv.ParseFloat(row[10], 64)
if err != nil {
log.Fatal(err)
}
fmt.Println(age, departure_lat, departure_lng, arrival_lat, arrival_lng)
}
}
出力結果
C:\Users\ebata\yamaguchi\src_try1\others> go run main5.go
43 34.173408 131.470684 34.155862 131.501246
24 34.179449 131.482543 34.164116 131.471791
42 34.168739 131.470768 34.160989 131.491124
21 34.169494 131.469934 34.173498 131.471351
58 34.185295 131.47414 34.191481 131.49456
56 34.16536 131.471872 34.174066 131.479312
73 34.155731 131.500845 34.16776 131.472831
47 34.167237 131.471785 34.155775 131.476531
21 34.154931 131.50468 34.156678 131.49581
37 34.16727 131.472899 34.171253 131.471177
40 34.147241 131.474921 34.150675 131.486268
67 34.173683 131.476347 34.173643 131.471027
28 34.183079 131.484303 34.174245 131.474592
46 34.146154 131.472711 34.159611 131.491548
25 34.162497 131.489283 34.147212 131.475984

PostgreSQLで、新規のデータならinsert, 書き換えならupdate をしようと思ったのですが、なんか面倒くさいので、初期テーブルを沢山作っておくことで対応することしました。
というか、そういうメソッドを準備しておいて欲しい→あるみたいですが、上手く動かせませんでした。
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq" // ←これを追記
)
func main() {
// user_log, bike_logへのアクセス
dbAgent, err := sql.Open("postgres", "user=postgres password=password host=192.168.0.23 port=15432 dbname=agent_db sslmode=disable")
if err != nil {
log.Fatal("dbAgent OpenError: ", err)
}
defer dbAgent.Close()
err = dbAgent.Ping()
if err != nil {
log.Fatal(err, "\nHint: dbAgent Database may not have started")
}
// user_logテーブルの内容の全削除
dbAgent.Query("delete from bike_log;")
// insertのテスト
for i := 0; i < 10; i++ {
str := fmt.Sprintf("insert into bike_log (stationid, num_bikes) values(%d, -1)", i)
fmt.Println(str)
dbAgent.Query(str)
}
// updateのテスト
for i := 0; i < 10; i = i + 2 {
str := fmt.Sprintf("update bike_log set num_bikes = %d where stationid = %d", i*2, i)
fmt.Println(str)
dbAgent.Query(str)
}
}
出力結果
C:\Users\ebata\yamaguchi\src_try1\others> go run main4.go
insert into bike_log (stationid, num_bikes) values(0, -1)
insert into bike_log (stationid, num_bikes) values(1, -1)
insert into bike_log (stationid, num_bikes) values(2, -1)
insert into bike_log (stationid, num_bikes) values(3, -1)
insert into bike_log (stationid, num_bikes) values(4, -1)
insert into bike_log (stationid, num_bikes) values(5, -1)
insert into bike_log (stationid, num_bikes) values(6, -1)
insert into bike_log (stationid, num_bikes) values(7, -1)
insert into bike_log (stationid, num_bikes) values(8, -1)
insert into bike_log (stationid, num_bikes) values(9, -1)
update bike_log set num_bikes = 0 where stationid = 0
update bike_log set num_bikes = 4 where stationid = 2
update bike_log set num_bikes = 8 where stationid = 4
update bike_log set num_bikes = 12 where stationid = 6
update bike_log set num_bikes = 16 where stationid = 8
データベースの中身
agent_db=# select * from bike_log;
stationid | num_bikes
-----------+-----------
1 | -1
3 | -1
5 | -1
7 | -1
9 | -1
0 | 0
2 | 4
4 | 8
6 | 12
8 | 16
(10 rows)
何度やっても、エージェントが湯田温泉駅の北口に出てこないので、QGISを立ち上げて、OpenStreetMap(OSM)のノードを調べみたら、原因が分かりました。

湯田温泉駅のサイクルステーションにもっとも近いノードが、駅の南口側にあるので、ここにキャッチされて、エージェントが南口側に集合してしまっていました。
# 湯田温泉駅周辺のノードが少なすぎる (山口駅は、比較的沢山のノードがあるのに)
OSMデータの改竄(https://booth.pm/ja/items/3943484)という手もあるが、ちょっと手間がかかりすぎるので、取り敢えず、目的地を上の図の206号線の終端に移動することで対応しようと思っています。
2023年1月9日追記
他のマシンで作った鍵は、動きません。"http: TLS handshake error from 127.0.0.1:51901: remote error: tls: unknown certificate"が出てきます→ 証明書の中にPCのホスト名称が記載されているようです。ですので、面倒ですが、別のマシンで動かす鍵は、別のマシンで再度作り直す必要があるようです。
2023年1月10日追記
クライアントとサーバを同じWindowBOXで実施している場合でも、"http: TLS handshake error"がでてきますので、下記の「5.4. Windowsの場合」を参考にして、「証明書のインストール」を実施して下さい。
ローカルな実験環境で、Go言語で作ったサーバを動かし、Websocket用の鍵を作成し、TLS通信(https://)を実現します。
最近、ブラウザの"https"縛りがきつくて、ローカルのindex.htmlを叩くだけでは、画面が出てこなくなりました。特にスマホで顕著です。セキュリティ的には良いことなのかもしれませんが、研究用の開発をする人間(私)にとっては、『正直、面倒くさいなぁ』と思っています。
あくまでインターナルな環境での動作テストで使えれば足り、インターネットに出るつもりはありません。つまりIPアドレス、直接指定で実験できれば足りるのです(e.g. https://192.168.0.8:8080)
ところが問題があります。当然、インターネットに出ないことが必要なのです(というかインターネットに出たらアウト(始末書もの))。
という訳で、今回は、mkcert (https://qiita.com/k_kind/items/b87777efa3d29dcc4467) を使うことにしました。
このメモでは、mkcertを使って、インターネットに繋っていないローカルネットワークで、TLS通信を実現することを目的とするものです。
https://github.com/FiloSottile/mkcert を覗いて、バイナリがダウンロードできそうことが分かりました。
もって、ここから、Windows10で使えそうなバイナリをダウンロードして下さい。
ダウンロードしたところから、直接叩いてみたら、最初に、
# mkcert -install
しろ、と言われます。
本当はmkcertにリネームした方が良いのでしょうが、面倒なので、そのまま
# mkcert-v1.4.1-windows-amd64.exe -install
を強行しました。
その後、"localhost","127.0.0.1","192.168.0.8"の3つのアドレスを追加することにしましたので、
# mkcert-v1.4.1-windows-amd64.exe localhost 127.0.0.1 192.168.0.8
としました。
この結果は以下の通りです。
>mkcert-v1.4.1-windows-amd64.exe localhost 127.0.0.1 192.168.0.8
Using the local CA at "C:\Users\ebata\AppData\Local\mkcert"
Created a new certificate valid for the following names
- "localhost"
- "127.0.0.1"
- "192.168.0.8"
The certificate is at "./localhost+2.pem" and the key at "./localhost+2-key.pem"
と入力すると、鍵が、カレントディレクトリにできるようです。
で、
鍵を使うプログラム(main.go, server24.go)のあるディレクトリの全部に放り込んで下さい。
さらに、main.go, server24.goのhttp.ListenAndServe()を、以下のようなhttp.ListenAndServeTLS()に置き換えて下さい
var addr = flag.String("addr", ":8080", "http service address")
/*
log.Fatal(http.ListenAndServe(*addr, nil)) // localhost:8080で起動をセット
*/
var httpErr error
if _, err := os.Stat("./cert.pem"); err == nil {
fmt.Println("file ", "cert.crt found switching to https")
if httpErr = http.ListenAndServeTLS(*addr, "./cert.pem", "./key.pem", 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())
}
}
スマホ側は、当然に、"192.168.0.8"などというあやしげなサーバを信じる訳がないので、それを信じさせる処理を行う必要があります。
これをスマホに信じさせるには、ルート証明書の作成が必要となりますが、これはすでに出来ています。
# mkcert-v1.4.1-windows-amd64.exe -CAROOT
で、rootCA-key.pem rootCA.pem の場所が分かります。
("key.pem"、"cert.pem"は、使わないので注意して下さい(それに気がつかずにエラい目に遭いました))
C:\Users\ebata\Downloads>mkcert-v1.4.1-windows-amd64.exe -CAROOT
C:\Users\ebata\AppData\Local\mkcert
C:\Users\ebata\Downloads>cd C:\Users\ebata\AppData\Local\mkcert
C:\Users\ebata\AppData\Local\mkcert>ls
rootCA-key.pem rootCA.pem
あとは、インストールボタンを押し続けて、インストールが完了して下さい。
あるいは、
最後に、
Windowsがスマホになることはないと思いますが、リモートの端末になることはあるので、記載しておきます。
上記の処理を行うことで、iPhone,iPad,Android,WindowsBoxからのアクセスを行っても、サーバのコンソールから、
http: TLS handshake error from 192.168.0.22:59914: remote error: tls: unknown certificate
http: TLS handshake error from 192.168.0.22:59920: remote error: tls: unknown certificate
http: TLS handshake error from 192.168.0.22:59922: remote error: tls: unknown certificate
が出てこなくなり、ローカルネットでのセキュアなWebSocket通信が実現できる目処が立ちました。
以上
Dockerを使っていて、このメッセージにどれだけ苦しめられたことか分からない。
で、世界中の人も困っているようである。
コードにトレースログを仕込んでも、どうもコードではない部分で発生しているらしくて、手がつけられなかったが、調べている内に、こんなことが分かってきた。
db:
image: postgis-pgrouting:latest
build:
context: docker/postgis-pgrouting
expose:
- "5432"
ports:
- "15432:5432"
volumes:
- data
- ./:/hitachi_ride_hailing_go
environment:
POSTGRES_HOST_AUTH_METHOD: 'trust'
POSTGRES_PASSWORD: 'postgres'
そうしたら、いきなり、"dial tcp: lookup db on 127.0.0.11:53: no such host exit" が出てこなくなった。
理由は不明ですし、たまたまかもしれないけど、直ったので、それで良しとします。