今回の実験のケースでは、
192.168.11.232/ 255.255.248.0 / 192.168.11.1 あたりにすれば良いのであろう(多分)。
江端智一のホームページ
今回の実験のケースでは、
192.168.11.232/ 255.255.248.0 / 192.168.11.1 あたりにすれば良いのであろう(多分)。
本当に一所懸命やっているんだけど、法律で定められている時間内では、仕事の量と質から考えても絶対に間に合わない ――
I am working hard, but I will never make it within the legal time frame, given the quantity and quality of the work I have to do.
これって、誰のせい?
Whose fault is this?
私(の無能)のせい?
Is it my (incompetence) fault?
-----
10年前、30年前、そして、有史以来、人類がずっと問い続けてきたことを、リタイアをスコープに入ってきている今でさえ、自分の問題として、自問していることに腹が立ちます。
I am angry that even now, as I am scoping my retirement, I am asking myself the same question humanity has been asking since ten years ago, 30 years ago, and since the beginning of time, as my problem.
もしかしたら、『人間って、本質的にバカなの?』と思ってしまいます。
Perhaps, 'Are humans inherently stupid?' I think.
太陽にブラックホールが突っ込んでくる日が、明日やって来たとしても、今の私は歓迎します(by さよならジュピター(故小松左京先生))。
Even if the day when a black hole crashes into the sun comes tomorrow, I would welcome it now (by Byebye Jupiter (the late Sakyo Komatsu)).
Go言語で、redisを使って2つの型の異なるデータをブロードキャストしている場合、その受信している部分を1つのswitchで受けとるにはどうしたら良いですか
を、異なるエージェントで、異なるメッセージを受信できるか試してみた件。
// C:\Users\ebata\tomioka3B\src\others\main28.go
// 2つのクロック(goroutine)を用意して、異なるエージェントで受けとれるかどうかの実験
package main
import (
"encoding/json"
"fmt"
"sync"
"time"
"github.com/gomodule/redigo/redis"
)
type Clock_Info struct {
VirtualTime time.Time
RealTime time.Time
}
type SubClockInfo_2 struct {
// 異なるデータ型のフィールドをここに追加
SomeField string
AnotherField int
}
func BaseClock() {
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
// スタート時刻を指定
startTime := time.Date(2023, 10, 1, 7, 0, 0, 0, time.UTC)
// 1秒値を保持する変数
seconds := 0
var ci Clock_Info
// ループを開始
for {
// 現在の時刻を計算
ci.VirtualTime = startTime.Add(time.Duration(seconds) * time.Second)
ci.RealTime = time.Now()
// 現在の時刻を表示
// fmt.Println("シミュレータの時刻:", ci.VirtualTime.Format("2006/01/02 15:04:05"))
// fmt.Println("現在の時刻:", ci.RealTime.Format("2006/01/02 15:04:05")) // "2006/01/02 15:04:05"はフォーマットの形を真似るもので、内容に意味なし
// パブリッシュ
json_ci, _ := json.Marshal(ci)
r, err := redis.Int(conn.Do("PUBLISH", "ClockInfo_1", json_ci))
if err != nil {
panic(err)
}
fmt.Println(r)
// 5秒待つ (実際は、0.05秒くらいだが、確認用に長くしている)
time.Sleep(5000 * time.Millisecond)
// 1秒値を増加させる
seconds++
}
}
func SubClock() { // 実験用に追加(時間ではなく、単なる文字列と数値を送り込むだけ)
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
// 1秒値を保持する変数
seconds := 0
var sci2 SubClockInfo_2
// ループを開始
for {
// 現在の時刻を計算
sci2.SomeField = "ebata is great"
sci2.AnotherField = seconds
// パブリッシュ
json_sci2, _ := json.Marshal(sci2)
r, err := redis.Int(conn.Do("PUBLISH", "SubClockInfo_2", json_sci2))
if err != nil {
panic(err)
}
fmt.Println(r)
// 7秒待つ (実際は、0.05秒くらいだが、確認用に長くしている)
time.Sleep(7000 * time.Millisecond)
// 1秒値を増加させる
seconds += 1
}
}
func person_1(person_num int, wg *sync.WaitGroup) {
defer wg.Done()
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
psc := redis.PubSubConn{Conn: conn}
psc.Subscribe("ClockInfo_1") // 2つに増やした
for {
switch v := psc.Receive().(type) { // redisのメッセージを受けとると、ここでロックが外れる
case redis.Message:
switch v.Channel{
case "ClockInfo_1": // ブロードキャスト"ClockInfo_1"のメッセージは、こっちでキャッチ
ci := new(Clock_Info)
_ = json.Unmarshal(v.Data, &ci)
fmt.Println("Person_1:", person_num, "VirtualTime (ClockInfo_1):", ci.VirtualTime)
case "SubClockInfo_2": // ブロードキャスト"SubClockInfo_2"のメッセージは、こっちでキャッチ
subClockData := new(SubClockInfo_2)
_ = json.Unmarshal(v.Data, &subClockData)
fmt.Println("Person_1:", person_num, "SomeField (SubClockInfo_2):", subClockData.SomeField)
fmt.Println("Person_1:", person_num, "AnotherField (SubClockInfo_2):", subClockData.AnotherField)
}
case redis.Subscription:
fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
return
}
}
}
func person_2(person_num int, wg *sync.WaitGroup) {
defer wg.Done()
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
psc := redis.PubSubConn{Conn: conn}
psc.Subscribe("SubClockInfo_2") // 2つに増やした
for {
switch v := psc.Receive().(type) { // redisのメッセージを受けとると、ここでロックが外れる
case redis.Message:
switch v.Channel{
case "ClockInfo_1": // ブロードキャスト"ClockInfo_1"のメッセージは、こっちでキャッチ
ci := new(Clock_Info)
_ = json.Unmarshal(v.Data, &ci)
fmt.Println("Person_2:", person_num, "VirtualTime (ClockInfo_1):", ci.VirtualTime)
case "SubClockInfo_2": // ブロードキャスト"SubClockInfo_2"のメッセージは、こっちでキャッチ
subClockData := new(SubClockInfo_2)
_ = json.Unmarshal(v.Data, &subClockData)
fmt.Println("Person_2:", person_num, "SomeField (SubClockInfo_2):", subClockData.SomeField)
fmt.Println("Person_2:", person_num, "AnotherField (SubClockInfo_2):", subClockData.AnotherField)
}
case redis.Subscription:
fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
return
}
}
}
func main() {
wg := sync.WaitGroup{}
//wg.Add(1)
//go BaseClock(&wg)
go BaseClock()
go SubClock()
for i := 0; i < 5; i++ { // 5人
wg.Add(1)
go person_1(i, &wg)
}
for i := 0; i < 5; i++ { // 5人
wg.Add(1)
go person_2(i, &wg)
}
wg.Wait()
fmt.Println("end of ... main()")
}
ちゃんと動くみたいです。
"SubClockInfo_2" チャンネルから受信したデータを処理するために、新しい構造体を定義します。異なるデータ型に合わせて構造体を定義し、Unmarshal でデータを解析します。
"SubClockInfo_2" チャンネルからのメッセージを受け取るために、person 関数内の switch ステートメントを更新します。具体的には、"SubClockInfo_2" チャンネルのデータ型に合わせて処理を分岐させます。
このようにすることで、"SubClockInfo_2" チャンネルから異なる型のデータを受信し、それに応じた処理を行うことができます。異なるデータ型ごとに適切な構造体を用意し、Unmarshal でデータを解析しましょう。
// C:\Users\ebata\tomioka3B\src\others\main27.go
// 2つのクロック(goroutine)を用意して、一つのswitchで受けとれるかどうかの実験
package main
import (
"encoding/json"
"fmt"
"sync"
"time"
"github.com/gomodule/redigo/redis"
)
type Clock_Info struct {
VirtualTime time.Time
RealTime time.Time
}
type SubClockInfo_2 struct {
// 異なるデータ型のフィールドをここに追加
SomeField string
AnotherField int
}
func BaseClock() {
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
// スタート時刻を指定
startTime := time.Date(2023, 10, 1, 7, 0, 0, 0, time.UTC)
// 1秒値を保持する変数
seconds := 0
var ci Clock_Info
// ループを開始
for {
// 現在の時刻を計算
ci.VirtualTime = startTime.Add(time.Duration(seconds) * time.Second)
ci.RealTime = time.Now()
// 現在の時刻を表示
// fmt.Println("シミュレータの時刻:", ci.VirtualTime.Format("2006/01/02 15:04:05"))
// fmt.Println("現在の時刻:", ci.RealTime.Format("2006/01/02 15:04:05")) // "2006/01/02 15:04:05"はフォーマットの形を真似るもので、内容に意味なし
// パブリッシュ
json_ci, _ := json.Marshal(ci)
r, err := redis.Int(conn.Do("PUBLISH", "ClockInfo_1", json_ci))
if err != nil {
panic(err)
}
fmt.Println(r)
// 5秒待つ (実際は、0.05秒くらいだが、確認用に長くしている)
time.Sleep(5000 * time.Millisecond)
// 1秒値を増加させる
seconds++
}
}
func SubClock() { // 実験用に追加(時間ではなく、単なる文字列と数値を送り込むだけ)
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
// 1秒値を保持する変数
seconds := 0
var sci2 SubClockInfo_2
// ループを開始
for {
// 現在の時刻を計算
sci2.SomeField = "ebata is great"
sci2.AnotherField = seconds
// パブリッシュ
json_sci2, _ := json.Marshal(sci2)
r, err := redis.Int(conn.Do("PUBLISH", "SubClockInfo_2", json_sci2))
if err != nil {
panic(err)
}
fmt.Println(r)
// 7秒待つ (実際は、0.05秒くらいだが、確認用に長くしている)
time.Sleep(7000 * time.Millisecond)
// 1秒値を増加させる
seconds += 1
}
}
func person(person_num int, wg *sync.WaitGroup) {
defer wg.Done()
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
psc := redis.PubSubConn{Conn: conn}
psc.Subscribe("ClockInfo_1", "SubClockInfo_2") // 2つに増やした
for {
switch v := psc.Receive().(type) { // redisのメッセージを受けとると、ここでロックが外れる
case redis.Message:
switch v.Channel{
case "ClockInfo_1": // ブロードキャスト"ClockInfo_1"のメッセージは、こっちでキャッチ
ci := new(Clock_Info)
_ = json.Unmarshal(v.Data, &ci)
fmt.Println("Person:", person_num, "VirtualTime (ClockInfo_1):", ci.VirtualTime)
case "SubClockInfo_2": // ブロードキャスト"SubClockInfo_2"のメッセージは、こっちでキャッチ
subClockData := new(SubClockInfo_2)
_ = json.Unmarshal(v.Data, &subClockData)
fmt.Println("Person:", person_num, "SomeField (SubClockInfo_2):", subClockData.SomeField)
fmt.Println("Person:", person_num, "AnotherField (SubClockInfo_2):", subClockData.AnotherField)
}
case redis.Subscription:
fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
return
}
}
/*
for {
ci := new(Clock_Info)
switch v := psc.Receive().(type) {
case redis.Message:
_ = json.Unmarshal(v.Data, &ci)
fmt.Println("Person:", person_num, "VirtualTime:", ci.VirtualTime)
//fmt.Println("Person:", person_num, "RealTime:", ci.RealTime)
case redis.Subscription:
fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
return
}
}
*/
}
func main() {
wg := sync.WaitGroup{}
//wg.Add(1)
//go BaseClock(&wg)
go BaseClock()
go SubClock()
for i := 0; i < 5; i++ { // 5人
wg.Add(1)
go person(i, &wg)
}
wg.Wait()
fmt.Println("end of ... main()")
}
出力結果はこんな感じになり、期待した通りの動きをしているようです。
Person: 1 VirtualTime (ClockInfo_1): 2023-10-01 07:00:03 +0000 UTC
Person: 3 VirtualTime (ClockInfo_1): 2023-10-01 07:00:03 +0000 UTC
Person: 0 VirtualTime (ClockInfo_1): 2023-10-01 07:00:03 +0000 UTC
Person: 4 VirtualTime (ClockInfo_1): 2023-10-01 07:00:03 +0000 UTC
Person: 2 VirtualTime (ClockInfo_1): 2023-10-01 07:00:03 +0000 UTC
5
Person: 1 VirtualTime (ClockInfo_1): 2023-10-01 07:00:04 +0000 UTC
Person: 0 VirtualTime (ClockInfo_1): 2023-10-01 07:00:04 +0000 UTC
Person: 3 VirtualTime (ClockInfo_1): 2023-10-01 07:00:04 +0000 UTC
Person: 2 VirtualTime (ClockInfo_1): 2023-10-01 07:00:04 +0000 UTC
Person: 4 VirtualTime (ClockInfo_1): 2023-10-01 07:00:04 +0000 UTC
5
Person: 3 SomeField (SubClockInfo_2): ebata is great
Person: 3 AnotherField (SubClockInfo_2): 3
Person: 0 SomeField (SubClockInfo_2): ebata is great
Person: 0 AnotherField (SubClockInfo_2): 3
Person: 1 SomeField (SubClockInfo_2): ebata is great
Person: 1 AnotherField (SubClockInfo_2): 3
Person: 4 SomeField (SubClockInfo_2): ebata is great
Person: 4 AnotherField (SubClockInfo_2): 3
Person: 2 SomeField (SubClockInfo_2): ebata is great
Person: 2 AnotherField (SubClockInfo_2): 3
5
Person: 3 VirtualTime (ClockInfo_1): 2023-10-01 07:00:05 +0000 UTC
Person: 4 VirtualTime (ClockInfo_1): 2023-10-01 07:00:05 +0000 UTC
Person: 1 VirtualTime (ClockInfo_1): 2023-10-01 07:00:05 +0000 UTC
Person: 0 VirtualTime (ClockInfo_1): 2023-10-01 07:00:05 +0000 UTC
Person: 2 VirtualTime (ClockInfo_1): 2023-10-01 07:00:05 +0000 UTC
5
Person: 3 SomeField (SubClockInfo_2): ebata is great
Person: 3 AnotherField (SubClockInfo_2): 4
Person: 0 SomeField (SubClockInfo_2): ebata is great
Person: 4 SomeField (SubClockInfo_2): ebata is great
Person: 4 AnotherField (SubClockInfo_2): 4
Person: 1 SomeField (SubClockInfo_2): ebata is great
Person: 1 AnotherField (SubClockInfo_2): 4
Person: 0 AnotherField (SubClockInfo_2): 4
Person: 2 SomeField (SubClockInfo_2): ebata is great
Person: 2 AnotherField (SubClockInfo_2): 4
5
以上
[サーバ側] Windows10
https://iperf.fr/iperf-download.php#windows からダウンロード
インストールとかではなくて、バイナリが直接解凍される
[クライアント側] Ubuntu22.04
$ sudo apt install iperf3
使い方
[サーバ側] iperf3.exe -s
[クライアント側]iperf3.exe -c 192.168.11.167(サーバのIPアドレス等)
構成1
サーバ側の表示はこんな感じでした。
C:\Users\maoeb\iperf-3.1.3-
win64>iperf3.exe -s
-----------------------------------------------------------
Server listening on 5201
-----------------------------------------------------------
Accepted connection from 192.168.11.125, port 33940
[ 5] local 192.168.11.167 port 5201 connected to 192.168.11.125 port 33946
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-1.00 sec 8.85 MBytes 74.2 Mbits/sec
[ 5] 1.00-2.00 sec 5.86 MBytes 49.0 Mbits/sec
[ 5] 2.00-3.00 sec 4.79 MBytes 40.2 Mbits/sec
[ 5] 3.00-4.01 sec 5.13 MBytes 42.9 Mbits/sec
[ 5] 4.01-5.00 sec 5.10 MBytes 42.8 Mbits/sec
[ 5] 5.00-6.00 sec 8.65 MBytes 72.8 Mbits/sec
[ 5] 6.00-7.01 sec 5.84 MBytes 48.7 Mbits/sec
[ 5] 7.01-8.00 sec 5.06 MBytes 42.6 Mbits/sec
[ 5] 8.00-9.01 sec 5.25 MBytes 43.9 Mbits/sec
[ 5] 9.01-10.00 sec 8.37 MBytes 70.6 Mbits/sec
[ 5] 10.00-10.02 sec 194 KBytes 84.6 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-10.02 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-10.02 sec 63.1 MBytes 52.8 Mbits/sec receiver
構成2
構成3
劣化通信環境を使う上での工夫
------------------------------
Accepted connection from 192.168.11.125, port 39260
[ 5] local 192.168.11.167 port 5201 connected to 192.168.11.125 port 39270
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-1.01 sec 4.23 MBytes 35.0 Mbits/sec
[ 5] 1.01-2.01 sec 3.69 MBytes 31.2 Mbits/sec
[ 5] 2.01-3.00 sec 4.46 MBytes 37.4 Mbits/sec
[ 5] 3.00-4.00 sec 4.41 MBytes 37.1 Mbits/sec
[ 5] 4.00-5.00 sec 3.74 MBytes 31.4 Mbits/sec
[ 5] 5.00-6.00 sec 4.13 MBytes 34.5 Mbits/sec
[ 5] 6.00-7.00 sec 4.39 MBytes 36.9 Mbits/sec
[ 5] 7.00-8.01 sec 4.14 MBytes 34.5 Mbits/sec
[ 5] 8.01-9.00 sec 4.23 MBytes 35.7 Mbits/sec
[ 5] 9.00-10.00 sec 5.12 MBytes 42.9 Mbits/sec
[ 5] 10.00-10.02 sec 153 KBytes 74.3 Mbits/sec
- - - - - - - - - - - - - - - - - - - - - - - - -
[ ID] Interval Transfer Bandwidth
[ 5] 0.00-10.02 sec 0.00 Bytes 0.00 bits/sec sender
[ 5] 0.00-10.02 sec 42.7 MBytes 35.7 Mbits/sec receiver
安定した、劣化環境を作るのは、これで、なかなか難しい。
今迄、エージェントプログラムでは、エージェントの中に内部時計(正確にはSleep())を組み込んで、エージェント間の時刻同期については、無視してきたのですが、交通シミュレーションに時刻表を入れる必要になって、無視できなくなってきました。
今や、私の十八番となったredisを使ったgolangのgoroutineで作った複数エージェントへのメッセージのブロードキャストを使って、時刻同期をするサンプルプログラムを作ってみました。
BaseClock()から、所定時間間隔で、シミュレーション共通の時刻をブロードキャストして、person()のエージェントで、その時刻情報を割り込み受信させることにしました。
ブロードキャストによる遅延や割り込み取りこぼしが心配ではありますが、これまでの経験上redisのブロードキャストは十分に高速なようなので、多分大丈夫だと思います。
// C:\Users\ebata\tomioka3B\src\others\main26.go
package main
import (
"encoding/json"
"fmt"
"sync"
"time"
"github.com/gomodule/redigo/redis"
)
var Clock1 chan interface{}
type Clock_Info struct {
VirtualTime time.Time
RealTime time.Time
}
func BaseClock(wg *sync.WaitGroup) {
defer wg.Done()
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
// スタート時刻を指定
startTime := time.Date(2023, 10, 1, 7, 0, 0, 0, time.UTC)
// 1秒値を保持する変数
seconds := 0
var ci Clock_Info
// ループを開始
for {
// 現在の時刻を計算
ci.VirtualTime = startTime.Add(time.Duration(seconds) * time.Second)
ci.RealTime = time.Now()
// 現在の時刻を表示
fmt.Println("シミュレータの時刻:", ci.VirtualTime.Format("2006/01/02 15:04:05"))
fmt.Println("現在の時刻:", ci.RealTime.Format("2006/01/02 15:04:05")) // "2006/01/02 15:04:05"はフォーマットの形を真似るもので、内容に意味なし
// パブリッシュ
json_ci, _ := json.Marshal(ci)
r, err := redis.Int(conn.Do("PUBLISH", "ClockInfo_1", json_ci))
if err != nil {
panic(err)
}
fmt.Println(r)
// 5秒待つ (実際は、0.05秒くらいだが、確認用に長くしている)
time.Sleep(5000 * time.Millisecond)
// 1秒値を増加させる
seconds++
}
}
func person(person_num int, wg *sync.WaitGroup) {
defer wg.Done()
// 接続
conn, err := redis.Dial("tcp", "localhost:6379")
if err != nil {
panic(err)
}
defer conn.Close()
psc := redis.PubSubConn{Conn: conn}
psc.Subscribe("ClockInfo_1")
for {
ci := new(Clock_Info)
switch v := psc.Receive().(type) {
case redis.Message:
_ = json.Unmarshal(v.Data, &ci)
fmt.Println("Person:", person_num, "VirtualTime:", ci.VirtualTime)
fmt.Println("Person:", person_num, "RealTime:", ci.RealTime)
case redis.Subscription:
fmt.Printf("%s: %s %d\n", v.Channel, v.Kind, v.Count)
case error:
return
}
}
}
func main() {
wg := sync.WaitGroup{}
wg.Add(1)
go BaseClock(&wg)
for i := 0; i < 10; i++ { // 10人
wg.Add(1)
go person(i, &wg)
}
wg.Wait()
fmt.Println("end of ... main()")
}
pgr_dijkstra()で、ダイクストラの順番を壊さずにルートの座標を得る方法(getDijkstraPath())
ノードの切断を発生させず、ノード10~ノード20と、ノード30~ノード40を回避するダイクストラ計算を行うPostGISのクエリー
をベースとしたGo言語のプログラムになります。
// C:\Users\ebata\tomioka3B\src\others\main23.go
package main
import (
"database/sql"
"fmt"
"log"
"m/src/ldarp"
"strconv"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres",
"user=postgres password=password host=192.168.0.23 port=15432 dbname=tomioka_db_c sslmode=disable")
if err != nil {
log.Fatal("OpenError: ", err)
}
defer db.Close()
/*
以下のldarp.LocInfoの内容は、
type LocInfo struct {
Lon float64
Lat float64
Source int
}
となっています。
このプログラムファイルでは、上記を定義して、ldarp.LocInfoを、LocInfoと変換すれば動きます
ただ、前提とするpostgresqlのpostGISが取り扱うデータベースは、
mapconfig_for_cars.xml をベースとして構築しているものになりますので、ご留意下さい
https://github.com/pgRouting/osm2pgrouting/blob/main/mapconfig_for_cars.xml
*/
var a_Point, b_Point ldarp.LocInfo
a_Point.Source = 1 // 出発ノード
b_Point.Source = 50 // 到着ノード
var del_start1, del_end1, del_start2, del_end2 ldarp.LocInfo
//var del_start3, del_end3 ldarp.LocInfo
del_start1.Source = 10 // 1つ目の無効としたいノード番号(開始番号)
del_end1.Source = 20 // 1つ目の無効としたいノード番号(終了番号)
del_start2.Source = 30 // 2つ目の無効としたいノード番号(開始番号)
del_end2.Source = 40 // 2つ目の無効としたいノード番号(開始番号)
//del_start3.Source = 30
//del_end3.Source = 35
array1 := []ldarp.LocInfo{del_start1, del_end1} // 上記の無効としたいノード番号を登録する
array2 := []ldarp.LocInfo{del_start2, del_end2} // 上記の無効としたいノード番号を登録する
//array3 := []ldarp.LocInfo{del_start3, del_end3}
//delSourceArray := [][]ldarp.LocInfo{array1, array2, array3}
delSourceArray := [][]ldarp.LocInfo{array1, array2} // 登録したノード番号をarraysの変数に纏める
//delSourceArray = [][]ldarp.LocInfo{}
// arrays変数への挿入
//for _, arr := range arrays {
// delSourceArray = append(delSourceArray, arr)
//}
// fmt.Println(delSourceArray)
path, dist := getDijkstraPath(db, a_Point, b_Point, delSourceArray)
fmt.Println("path:", path)
fmt.Println("dist:", dist)
}
// 江端再修正版(2023/09/29)
/*
出発ノードと到着ノードと、通過したくないノード(無効したいノード)を登録して、ダイクストラ計算によるルートを選ぶ
*/
func getDijkstraPath(dbMap *sql.DB, locInfoStart, locInfoGoal ldarp.LocInfo, delSourceArray [][]ldarp.LocInfo) ([]ldarp.LocInfo, float64) {
log.Println("getDijkstraPath", locInfoStart, locInfoGoal)
sql_str := "" // SQL文を作る文字列の初期値
// delSourceArray の解析
fmt.Println("delSourceArray:", delSourceArray)
if len(delSourceArray) != 0 { // delSourceArrayに要素が入っていれば
// forとrangeを使用して各要素のSource値を取得
sql_str = "WHERE "
for _, arr := range delSourceArray {
/*
for _, loc := range arr {
fmt.Printf("Sourceの値: %d\n", loc.Source)
}
*/
fmt.Println(arr[0].Source, arr[1].Source)
//str += "( source NOT BETWEEN " + arr[0].Source + " AND " + arr[1].Source + " AND target NOT BETWEEN " + arr[0].Source + " AND " + arr[1].Source + ")"
sql_str += "(source NOT BETWEEN " + strconv.Itoa(arr[0].Source) + " AND " + strconv.Itoa(arr[1].Source) + " AND target NOT BETWEEN " + strconv.Itoa(arr[0].Source) + " AND " + strconv.Itoa(arr[1].Source) + ") AND "
//fmt.Println(str)
}
strlen := len(sql_str)
sql_str = sql_str[:strlen-5] // ラストの" AND "の5文字を削除する
//fmt.Println(str)
}
sql_str = "SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, cost, reverse_cost FROM ways " + sql_str + "', $1::bigint , $2::bigint , directed:=false ) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq"
//fmt.Println(sql_str)
var path []ldarp.LocInfo // 経路 (返り値の一つ目)
var totalDistanceKm float64
rowsDijkstra, errDijkstra := dbMap.Query(
//"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost FROM ways', $1::bigint , $2::bigint , directed:=false ) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
//"SELECT seq,source, target, x1, y1, x2, y2, agg_cost FROM pgr_dijkstra('SELECT gid as id, source, target, length_m as cost FROM ways WHERE (source NOT BETWEEN 10 AND 20 AND target NOT BETWEEN 10 AND 20) AND (source NOT BETWEEN 30 AND 40 AND target NOT BETWEEN 30 AND 40)', $1::bigint , $2::bigint , directed:=false ) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq",
sql_str,
locInfoStart.Source,
locInfoGoal.Source)
if errDijkstra != nil {
log.Fatal(errDijkstra)
}
defer rowsDijkstra.Close()
var agg_cost float64
var loc ldarp.LocInfo
var x1, y1, x2, y2 float64
var seq int
var target int
var source int
isFirstCheck := true
isSourceCheck := true
for rowsDijkstra.Next() {
// まずnodeを読む
if err := rowsDijkstra.Scan(&seq, &source, &target, &x1, &y1, &x2, &y2, &agg_cost); err != nil {
fmt.Println(err)
}
// 最初の1回だけチェックのために入る これについては、https://wp.kobore.net/江端さんの技術メモ/post-7668/を参照のこと
// もし rowsDijkstra.Scanで最初のsource値を読みとり、locInfoStart.Source の値と同じであれば、x1,y1をベースとして、異なる値であれば、x2,y2をベースとする
if isFirstCheck {
if source == locInfoStart.Source {
isSourceCheck = true // x1, y1をベースとする処理になる
} else {
isSourceCheck = false // x2,y2をベースとする処理になる
}
isFirstCheck = false // 最初の1回をチェックすることで、2回目はこのループには入らなくなる
}
//var loc ldarp.LocInfo
if isSourceCheck { // x1, y1をベースとする処理
loc.Source = source
loc.Lon = x1
loc.Lat = y1
} else { // x2,y2をベースとする処理
loc.Source = target
loc.Lon = x2
loc.Lat = y2
}
path = append(path, loc)
}
// ラストノードだけはsourceとtargetを引っくり返す
if isSourceCheck { // x1, y1をベースとする処理
loc.Source = target
loc.Lon = x2
loc.Lat = y2
} else { // x2,y2をベースとする処理
loc.Source = source
loc.Lon = x1
loc.Lat = y1
}
path = append(path, loc)
totalDistanceKm = agg_cost / 1000.0
return path, totalDistanceKm
}
C:\Users\ebata\tomioka3B\src\others\main21.goでバトル中
tomioka_db_c=# SELECT seq, node, edge, source, target, b.cost FROM pgr_dijkstra('SELECT gid as id, source, target, cost, reverse_cost FROM ways WHERE (source NOT BETWEEN 10 AND 20 AND target NOT BETWEEN 10 AND 20) AND (source NOT BETWEEN 30 AND 40 AND target NOT BETWEEN 30 AND 40)',1, 50, directed := false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq;
seq | node | edge | source | target | cost
-----+------+------+--------+--------+------------------------
1 | 1 | 2 | 1 | 2 | 8.518550262944415e-05
2 | 2 | 3 | 2 | 3 | 0.0009420518456308501
3 | 3 | 4 | 3 | 4 | 0.00026662995399998605
4 | 4 | 5 | 4 | 5 | 0.00021389117484400878
5 | 5 | 6 | 5 | 6 | 4.656362881781325e-05
6 | 6 | 7 | 6 | 7 | 0.00013184236987589488
7 | 7 | 8 | 7 | 8 | 6.438782425766514e-05
8 | 8 | 1254 | 8 | 897 | 1.2290752319372595e-05
9 | 897 | 1380 | 897 | 969 | 0.00038637624148597305
10 | 969 | 1938 | 969 | 1389 | 2.250777644883257e-05
11 | 1389 | 694 | 1389 | 542 | 0.0002978010745492113
12 | 542 | 1251 | 542 | 895 | 0.00015951582993150323
13 | 895 | 1815 | 895 | 1305 | 0.0001303909506021454
14 | 1305 | 1021 | 1305 | 753 | 3.9356066874244885e-05
15 | 753 | 1381 | 753 | 971 | 0.0002850072862639029
16 | 971 | 1382 | 976 | 971 | 0.001276712172995213
17 | 976 | 1388 | 977 | 976 | 0.0004267486614026622
18 | 977 | 1389 | 986 | 977 | 0.0006757336753520132
19 | 986 | 1404 | 906 | 986 | 0.0003707891314505199
20 | 906 | 1275 | 906 | 907 | 0.0001959266189093194
21 | 907 | 1277 | 907 | 908 | 9.052734394018332e-05
22 | 908 | 1921 | 908 | 1376 | 5.400925846418281e-05
23 | 1376 | 1279 | 1376 | 909 | 1.8504323815069496e-05
24 | 909 | 1057 | 909 | 773 | 6.840328939622445e-05
25 | 773 | 1069 | 773 | 781 | 0.000535513050350553
26 | 781 | 1071 | 781 | 784 | 0.00014276168953525706
27 | 784 | 1087 | 784 | 793 | 0.0004430493335842619
28 | 793 | 1682 | 793 | 1192 | 7.9895243915049e-05
29 | 1192 | 1111 | 1192 | 808 | 0.0005876979547862221
30 | 808 | 1125 | 808 | 816 | 0.00047564125136792903
31 | 816 | 51 | 816 | 49 | 3.10328360877585e-05
32 | 49 | 52 | 49 | 50 | 0.00028187114581170033
一応、QGISで調べて、ノード切れが発生していないことは確認しました。
新規に作ったノード182~413を、ちゃんと無視できるかの実験。ベースは以下のメモの内容。
pgr_dijkstra()で、ダイクストラの順番を壊さずにルートの座標を得る方法(getDijkstraPath())
■無視前
tomioka_db_c=# SELECT seq, source, target, x1, y1, x2, y2 FROM pgr_dijkstra('SELECT gid as id, source, target, cost, reverse_cost FROM ways',1036, 1320, directed := false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq;
seq | source | target | x1 | y1 | x2 | y2
-----+--------+--------+-----------------+----------------+-----------------+----------------
1 | 1036 | 182 | 139.6295688 | 35.3660511 | 139.62956342765 | 35.36605302299
2 | 182 | 183 | 139.62956342765 | 35.36605302299 | 139.62952386387 | 35.36602637499
3 | 183 | 184 | 139.62952386387 | 35.36602637499 | 139.62949515388 | 35.36599335049
4 | 184 | 185 | 139.62949515388 | 35.36599335049 | 139.62949783814 | 35.3658805722
5 | 185 | 186 | 139.62949783814 | 35.3658805722 | 139.629505074 | 35.36583679324
6 | 186 | 187 | 139.629505074 | 35.36583679324 | 139.62955514143 | 35.36580196041
7 | 187 | 188 | 139.62955514143 | 35.36580196041 | 139.62972133265 | 35.36574533322
8 | 188 | 189 | 139.62972133265 | 35.36574533322 | 139.62969285571 | 35.3656615823
9 | 189 | 190 | 139.62969285571 | 35.3656615823 | 139.62929978548 | 35.36461811112
10 | 190 | 191 | 139.62929978548 | 35.36461811112 | 139.62923349573 | 35.3644776355
11 | 191 | 192 | 139.62923349573 | 35.3644776355 | 139.62887730502 | 35.36401128513
12 | 192 | 193 | 139.62887730502 | 35.36401128513 | 139.62881288258 | 35.36394808968
13 | 193 | 194 | 139.62881288258 | 35.36394808968 | 139.62878113819 | 35.36393552673
14 | 194 | 195 | 139.62878113819 | 35.36393552673 | 139.62874285819 | 35.36391763403
15 | 195 | 196 | 139.62874285819 | 35.36391763403 | 139.62711549003 | 35.36370406459
16 | 196 | 1320 | 139.62711549003 | 35.36370406459 | 139.6270944 | 35.3636816
(16 rows)
■無視後
tomioka_db_c=# SELECT seq, source, target, x1, y1, x2, y2 FROM pgr_dijkstra('SELECT gid as id, source, target, cost, reverse_cost FROM ways WHERE (source NOT BETWEEN 182 AND 413 AND target NOT BETWEEN 182 AND 413) AND (source NOT BETWEEN 182 AND 413 AND target NOT BETWEEN 182 AND 413)',1036, 1320, directed := false) a INNER JOIN ways b ON (a.edge = b.gid) ORDER BY seq;
seq | source | target | x1 | y1 | x2 | y2
-----+--------+--------+-------------+------------+-------------+------------
1 | 1034 | 1036 | 139.6297237 | 35.365746 | 139.6295688 | 35.3660511
2 | 1034 | 1033 | 139.6297237 | 35.365746 | 139.6297007 | 35.3656671
3 | 1033 | 1325 | 139.6297007 | 35.3656671 | 139.6287884 | 35.3639297
4 | 1325 | 1320 | 139.6287884 | 35.3639297 | 139.6270944 | 35.3636816
うん、これを見ている限りは、大丈夫そうに見える。