2021/01,江端さんの技術メモ

QGIS3でosmファイルを地図表示する方法

いつも、osmファイルをドラッグする場所を忘れるので、メモ

すると、こうなる。

以上

2021/01,江端さんの技術メモ

0.追記(江端が、今、必要な情報をトップに)

### STLって面倒(strcpyでええやん)

// added by Ebata 2021/01/04
#include "simple_udp.h"

// Ebata added simple_udp 2021/01/05                                                                                    
simple_udp udp0("192.168.0.8",12345);

// added by Ebata 2021/01/04
std::cout << "Ebata:User:  " << user.id() << "," << user.current_xy().x() << "," << user.current_xy().y() << std::endl;
std::stringstream ss;
ss << "Ebata:User:  " << user.id() << "," << user.current_xy().x() << "," << user.current_xy().y() << std::endl;

// simple_udp udp0("192.168.0.8",12345);
udp0.udp_send("hello!\n");

//udp0.udp_send(ss);  エラー
//udp0.udp_send(ss.c_str()); エラー
udp0.udp_send(ss.str());

1.背景

LinuxのDockerコンテナの中から、一方的に、ホストOSにデータを放り投げる為のUDPプログラムです。

1行程度のデータをコンテナの外に出したいだけなのに、なかなか、良い方法が見つからず、結局UDP socketを使ってしまいました(websocket用にサーバを作るのも大袈裟で面倒でした(3~4行程度で記述したかった))。

ちなみに、UDPは、相手がいようがいまいが、一方的に送信し、一方的に受信待ちができる、とても便利な通信方式です。当然、送達保証はありません。

2. 環境

私のホストOSのIPアドレスは、192.168.0.8、ポートは、12345 と適当に選びました。

普通DockerにはUDPのポートオープンの設定が必要だと思いますが、今回は、テストプログラムでたまたまpingとUDPが通ってしまったので、何もやっていません(運が良かっただけかもしれません)。

テスト用のUDPプログラムは、こちら「UDP送受信プログラム」を使いました(何でも残しておくものです)

3.ソースコード

まるまる、こちらをコピペさせて頂きました → 「メカトロ講座03 c++でudp通信をする方法」

一行でUDP送信ができるようにしたかったもので。

3.1 共通のヘッダファイル(simple_udp.h)

// g++ -std=c++11 としないと動かない(ことがある)

// simple_udp.h

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>  // "string.h"だと通らないことがあるので、注意のこと(私は、ここで2時間ほど嵌った)

class simple_udp{
  int sock;
  struct sockaddr_in addr;
public:
  simple_udp(std::string address, int port){
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr(address.c_str());
    addr.sin_port = htons(port);
  }
  void udp_send(std::string word){
    sendto(sock, word.c_str(), word.length(), 0, (struct sockaddr *)&addr, sizeof(addr));
  }

  void udp_bind(){
    bind(sock, (const struct sockaddr *)&addr, sizeof(addr));

  }
  std::string udp_recv(){
            #define BUFFER_MAX 400
    char buf[BUFFER_MAX];
    memset(buf, 0, sizeof(buf));
    recv(sock, buf, sizeof(buf), 0);
    return std::string(buf);
  }
  void udp_recv(char *buf, int size){
    memset(buf, 0, size);
    recv(sock, buf, size, 0);
  }

  ~simple_udp(){
    close(sock);
  }
};

3.2 送信プログラム(udp_sendto.cpp)

// g++ udp_sendto.cpp -o udp_sendto で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)

#include <stdio.h>
#include <string.h>
#include "simple_udp.h"

simple_udp udp0("192.168.0.8",12345); // ホストOSのUDPの受信側

int main(int argc, char **argv){
  udp0.udp_send("hello!");
  return 0;
}

3.3 受信プログラム(udp_recvfrom.cpp)

// g++ udp_recvfrom.cpp -o udp_recvfrom で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
#include <stdio.h>
#include <string.h>
#include "simple_udp.h"
simple_udp udp0("0.0.0.0",12345);

int main(int argc, char **argv){
  udp0.udp_bind();
  while (1){
    std::string rdata=udp0.udp_recv();
    printf("recv:%s\n", rdata.c_str());
  }
  return 0;
}

4.Windows10でMinGWのgccを使ってる私の場合のケース

以下のように変更した

4.1 共通のヘッダファイル(simple_udp_win.h)

// simple_udp_win.h

/* Windows版 */
#include <stdio.h>
#include <sys/types.h>
#include <winsock2.h>
#include <ws2tcpip.h>
//#include <sys/socket.h>
//#include <netinet/in.h>
//#include <arpa/inet.h>
//#include <netdb.h>
#include <string>  // "string.h"だと通らないことがあるので、注意のこと(私は、ここで2時間ほど嵌った)
#include <unistd.h> // error: 'close' was not declared in this scope; did you mean 'fclose'?


class simple_udp{
  int sock;
  struct sockaddr_in addr;

  // Windows専用おまじない(ここから)
  WSADATA wsaData;
  // Windows専用おまじない(ここまで)	  

 public:
  simple_udp(std::string address, int port){

	// Windows専用おまじない(ここから)
	WSAStartup(MAKEWORD(2,0), &wsaData);
	// Windows専用おまじない(ここまで)	  
	  
	sock = socket(AF_INET, SOCK_DGRAM, 0);
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(address.c_str());
	addr.sin_port = htons(port);
  }
  void udp_send(std::string word){
	sendto(sock, word.c_str(), word.length(), 0, (struct sockaddr *)&addr, sizeof(addr));
  }
  
  void udp_bind(){
	bind(sock, (const struct sockaddr *)&addr, sizeof(addr));
  }
  std::string udp_recv(){
#define BUFFER_MAX 400
	char buf[BUFFER_MAX];
	memset(buf, 0, sizeof(buf));
	recv(sock, buf, sizeof(buf), 0);
	return std::string(buf);
  }
  
  void udp_recv(char *buf, int size){
	memset(buf, 0, size);
	recv(sock, buf, size, 0);
  }
  
  ~simple_udp(){
	close(sock);
  }
};

4.2 送信プログラム(udp_sendto_win.cpp)

// g++ udp_sendto_win.cpp -o udp_sendto_win で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
// g++ -g udp_sendto_win.cpp -o udp_sendto_win -lwsock32 -lws2_32
#include <stdio.h>
#include <string.h>
//#include "simple_udp.h"
#include "simple_udp_win.h"

//simple_udp udp0("192.168.0.8",12345); // ホストOSのUDPの受信側
//simple_udp udp0("0.0.0.0",12345); // ホストOSのUDPの受信側
simple_udp udp0("127.0.0.1",12345); // ホストOSのUDPの受信側

int main(int argc, char **argv){
  udp0.udp_send("hello!");
  return 0;  
}

4.3 受信プログラム(udp_recvfrom_win.cpp)

// g++ udp_recvfrom_win.cpp -o udp_recvfrom_win で大丈夫だが、g++ -std=c++11としないと動かない(ことがある)
// Windows の場合は、g++ -g udp_recvfrom_win.cpp -o udp_recvfrom_win -lwsock32 -lws2_32

#include <stdio.h>
#include <string.h>
#include "simple_udp.h"

//simple_udp udp0("0.0.0.0",12345); // ホストOSのUDPの受信側 simple_udp udp0("127.0.0.1",12345); int main(int argc, char **argv){ udp0.udp_bind(); while (1){ std::string rdata=udp0.udp_recv(); printf("recv:%s\n", rdata.c_str()); } return 0; }

2021/01,江端さんの技術メモ

1.前書き
TILE38のクライアントプログラムのサンプルを見たくて、色々探していたら、「TILE38紹介」を紹介するという、非常に良いページを見つけました。
私がプログラムを良く忘れるので、このページのプログラムをそのままコピペさせて頂いて、私の備忘録とさせて頂きます(私の環境では、コメントアウト等がちょっと違ったようです)

2. 前提
Tile38のサーバを立ち上げておきます。Dockerを使って立ち上げておくと簡単です。dockerを使わない方法は、https://github.com/tidwall/tile38 に記載があります。

docker pull tile38/tile38
docker run -p 9851:9851 tile38/tile38

3. サンプルプログラム

(1つ目)

package main

import (
        //"encoding/json"
        "fmt"
        "log"

        //"reflect"

        "github.com/garyburd/redigo/redis"
)

func main() {
        // db connect
        c, err := redis.Dial("tcp", ":9851")
        if err != nil {
                log.Fatalf("Could not connect: %v\n", err)
        }
        defer c.Close()

        // SET location
        ret, err := c.Do("SET", "location", "me", "POINT", 35.6581, 139.6975)
        if err != nil {
                log.Fatalf("Could not SET: %v\n", err)
        }
        fmt.Printf("SET ret:%s\n", ret)

        // GET location
        ret, err = c.Do("GET", "location", "me")
        if err != nil {
                log.Fatalf("Could not GET: %v\n", err)
        }
        fmt.Printf("GET ret:%s\n", ret)
}

(2つめ)

package main

import (
	"encoding/json"
	"fmt"
	"log"

	"github.com/garyburd/redigo/redis"
)

type GeoJsonMember struct {
	Type           string          `json:"type"`
	CoordinatesRaw json.RawMessage `json:"coordinates,omitempty"`
	CoordinatesObj interface{}     `json:"-"`
}

type Point [2]float64

type LineString []Point

type Polygon []LineString

func (member *GeoJsonMember) String() string {
	return fmt.Sprintf("%s %v", member.Type, member.CoordinatesObj)
}

func (member *GeoJsonMember) setCoordinates() error {
	var coordinates interface{}
	switch member.Type {
	case "Point":
		coordinates = new(Point)
	case "LineString":
		coordinates = new(LineString)
	case "Polygon":
		coordinates = new(Polygon)
	default:
		return fmt.Errorf("Unknown type: %v", member.Type)
	}
	err := json.Unmarshal(member.CoordinatesRaw, &coordinates)
	if err != nil {
		return fmt.Errorf("json.Unmarshal error: %v", err)
	}
	member.CoordinatesObj = coordinates
	return nil
}

func unmarshalMultiResults(shapes []byte) ([]*GeoJsonMember, error) {
	var members []*GeoJsonMember
	err := json.Unmarshal(shapes, &members)
	if err != nil {
		return nil, fmt.Errorf("Unmarshal error: %v", err)
	}

	for i, member := range members {
		err := member.setCoordinates()
		if err != nil {
			return nil, fmt.Errorf("member[%v]:type:%v coordinates:%v err:%v\n", i, member.Type, member.CoordinatesRaw, err)
		}
	}
	return members, nil
}

func unmarshalSingleResult(shapes []byte) (*GeoJsonMember, error) {
	var member GeoJsonMember
	err := json.Unmarshal(shapes, &member)
	if err != nil {
		return nil, fmt.Errorf("Unmarshal error: %v", err)
	}

	err = member.setCoordinates()
	if err != nil {
		return nil, fmt.Errorf("type:%v coordinates:%v err:%v\n", member.Type, member.CoordinatesRaw, err)
	}
	return &member, nil
}

func main() {
	// db connect
	c, err := redis.Dial("tcp", ":9851")
	if err != nil {
		log.Fatalf("Could not connect: %v\n", err)
	}
	defer c.Close()

	// SET fleet
	for _, data := range [][]interface{}{
		{"fleet", "id1", "FIELD", "start", "123456", "FIELD", "end", "789012", "POINT", 35.6581, 139.6975},
		{"fleet", "id2", "OBJECT", `{"type":"Point","coordinates":[139.6975,35.6581]}`},
		{"fleet", "id3", "OBJECT", `{"type":"LineString","coordinates":[[139.6975,35.6581],[1,1],[2,2]]}`},
		{"fleet", "id4", "POINT", 35.6581, 139.6975},
	} {
		ret, err := c.Do("SET", data...)
		if err != nil {
			log.Fatalf("Could not SET: %v\n", err)
		}
		fmt.Printf("SET ret:%#s\n", ret)
	}

	// SCAN fleet
	results, err := redis.Values(c.Do("SCAN", "fleet"))
	if err != nil {
		log.Fatalf("Could not SCAN: %v\n", err)
	}

	var cursor int
	var members []interface{}
	_, err = redis.Scan(results, &cursor, &members)
	if err != nil {
		fmt.Printf("scan result error: %v", err)
		return
	}

	for len(members) > 0 {
		// pick up one record from results as []interface{}
		var object []interface{}
		members, err = redis.Scan(members, &object)
		if err != nil {
			fmt.Printf("scan record error: %v", err)
			return
		}
		// scan columns from one record -> [id,json],fields
		var id []byte
		var json []byte
		others, err := redis.Scan(object, &id, &json)
		if err != nil {
			fmt.Printf("scan columns error: %v", err)
			return
		}

		// unmarshal geojson string to struct
		gjm, err := unmarshalSingleResult(json)
		if err != nil {
			fmt.Printf("unmarshal json error: %v", err)
			return
		}
		fmt.Printf("id:%s  json:%s  others:%s\n", id, gjm, others)
	}
}

4. PruneMobileと組み合わせてみる
ランダムウォークさせている歩行者が、一定のエリアに入った時に、メッセージを送付する。

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

XXXXXXXXXXXXXXXXXXXXXXXXXXX

 

 

2021/01,江端さんの技術メモ

  1. 参考文献
    https://r9y9.github.io/blog/2014/03/22/cgo-tips/
    https://hawksnowlog.blogspot.com/2018/12/getting-started-with-cgo.html
  2. 取り敢えず、Goのプログラムの中からCの関数を使う方法
    // go run main.go
    
    package main
    
    /*
    #include <stdio.h>
    #include <stdlib.h>
    
    void myprint(char* s) {
    	printf("%s\n", s);
    }
    */
    import "C"
    
    import "unsafe"
    
    func main() {
    	cs := C.CString("Hello from stdio\n")
    	C.myprint(cs)
    	C.free(unsafe.Pointer(cs))
    }
    
    // output
    // Hello from stdio

    とか

    package main
    
    /*
    #include <math.h>
    double MyPow(double x, double y) {
      double ret = pow(x, y);
      return ret;
    }
    */
    import "C"
    import "fmt"
    
    func main() {
    	ret := C.MyPow(10, 2)
    	fmt.Println(ret)
    }
  3. 結構面倒くさい決まりごと
    (1)import "C" の前には空行を入れない

    // #include <stdio.h>
    // #include <errno.h>
    import "C"

    (2)C / C++ のコンパイラに渡すオプション(flag)を記述する

    // #cgo CFLAGS: -I/usr/local/lib
    import C