m_shige1979のささやかな抵抗と欲望の日々

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

golangのpanic発生しても処理を続行したい

golangでのエラーとかって

基本的にはエラーオブジェクトのチェックしかしていない

panic発生したら

落ちます

package main

import (
    "log"
)

func main() {
    log.Println("start")


    // 意図的にpanicを起こす
    panic("aaaa")
    //var a *interface{}
    //*a = 0
    //x := 10
    //y := 0
    //b := x / y
    //log.Println(b)

    log.Println("end")
}

2019/08/14 10:04:05 start
panic: aaaa

goroutine 1 [running]:
main.main()
	/sample2-b.go:12 +0x79
exit status 2

そうならないようにerrオブジェクトや変数のチェックで抑制していますが
まれになんらかの理由でオブジェクトにnullが挿入される自体が発生してpanicがおきたらマズい

javaのtry-catchとかあればよいがそういうのはないため、
deferとrecoverを併用することで対応できるとのこと

ドキュメントより以下、引用(http://golang.jp/effective_go#recover

panicが呼び出されたとき(これには、配列のインデックスが範囲外であるときや、型アサーションに失敗したような暗黙的なものも含む)は、すぐさま、カレントの関数を停止し、ゴルーチンのスタックの巻き戻しを開始します。その途中、遅延指定されている関数をすべて実行します。この巻き戻しがゴルーチンのスタックの先頭にたどり着くと、プログラムは終了します。ただし、組み込み関数recoverを使うことで、ゴルーチンの制御を取り戻し、通常の実行を再開させることが可能です。

書き直し

package main

import (
	"log"
	"fmt"
	"runtime"
)

func main() {
	log.Println("start")

	// 今回は即時関数にしているがerrorの戻り値を取得できるならなんでもよい
	err := func() (errRes error) {
		// panic発生時に補足するやつ
		defer func() {
			if err := recover(); err != nil {
				// エラー情報を戻り値のerrResへ設定
				errRes = fmt.Errorf("error: %s", err)

				// stack trace
				for i:=0;;i++ {
					_, file, line, ok := runtime.Caller(i)
					if ok == false {
						break
					}
					log.Printf("depth: %d, line: %4d, file:%v\n", i, line, file)
				}
			}
		}()

		// 意図的にpanicを起こす
		//panic("aaaa")
		//var a *interface{}
		//*a = 0
		x := 10
		y := 0
		b := x / y
		log.Println(b)

		// ここまできたら正常
		return nil
	}()

	// errorチェック
	if err != nil {
		log.Println(err)
	}

	log.Println("end")
}

2019/08/14 10:13:38 start
2019/08/14 10:13:38 depth: 0, line:   22, file: /work/golang/sample2.go
2019/08/14 10:13:38 depth: 1, line:  522, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go
2019/08/14 10:13:38 depth: 2, line:   61, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/panic.go
2019/08/14 10:13:38 depth: 3, line:   37, file:/work/golang/sample2.go
2019/08/14 10:13:38 depth: 4, line:   42, file:/work/golang/sample2.go
2019/08/14 10:13:38 depth: 5, line:  200, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/proc.go
2019/08/14 10:13:38 depth: 6, line: 1337, file:/usr/local/Cellar/go/1.12.7/libexec/src/runtime/asm_amd64.s
2019/08/14 10:13:38 error: runtime error: integer divide by zero
2019/08/14 10:13:38 end

Webフレームワークとかは

API配下のpanicは拾ってくれるが、内部でgoroutineなどを任意で定義した場合にpanicを起こすと
Webフレームワークのアプリ自体が停止する可能性がある・・・

所感

なんかスマートじゃない感じ…
現状、これしか見つからないからこの方法で対応を試みます。

golangでUUIDの生成をライブラリでお試し

仕事でコード書くより

設計の仕事ばっかり、スケジュール管理とかめんどくさいので
他の人やってくんないかな…

UUIDって?

UUID(Universally Unique Identifier)とは、ソフトウェア上でオブジェクトを一意に識別するための識別子である。

*1

ざっくりいうとユニークIDみたいやつ
基本的にはsha1とかmd5とか乱数で生成していたけど
そういったものにフォーマットがついた感じ

例:3cc807ab-8e31-3071-aee4-f8f03781cb91

バージョン

現在1から5までの方法がある

確認用ライブラリ

https://github.com/google/uuid
今度改めて自身でアルゴリズムは検討するが今回はちょっとお試しでやってみる

ソース

package main

import (
    "github.com/google/uuid"
    "fmt"
)

func main() {
    fmt.Println("version1 NewUUID --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        fmt.Println("  ", uuidObj.String())
    }

    fmt.Println("version2 NewDCESecurity --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        domain := uuidObj.Domain()
        id := uuidObj.ID()

        uuidObj2, _ := uuid.NewDCESecurity(domain, id)
        fmt.Println("  ", uuidObj2.String())
    }

    fmt.Println("version3 NewMD5 --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        data := []byte("wnw8olzvmjp0x6j7ur8vafs4jltjabi0")
        uuidObj2 := uuid.NewMD5(uuidObj, data)
        fmt.Println("  ", uuidObj2.String())
    }

    fmt.Println("version5 NewSHA1 --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewUUID()
        data := []byte("wnw8olzvmjp0x6j7ur8vafs4jltjabi0")
        uuidObj2 := uuid.NewSHA1(uuidObj, data)
        fmt.Println("  ", uuidObj2.String())
    }

    fmt.Println("version4 NewRandom --")
    for i:=0;i<10;i++ {
        uuidObj, _ := uuid.NewRandom()
        fmt.Println("  ", uuidObj.String())
    }

}

結果

mshige1979MBA:golang matsumotoshigeharu$ go run sample1.go
version1 NewUUID --
   ef45b54c-9f70-11e9-a730-6476baad914e
   ef45c294-9f70-11e9-a730-6476baad914e
   ef45c2b2-9f70-11e9-a730-6476baad914e
   ef45c2c6-9f70-11e9-a730-6476baad914e
   ef45c2e4-9f70-11e9-a730-6476baad914e
   ef45c2f8-9f70-11e9-a730-6476baad914e
   ef45c316-9f70-11e9-a730-6476baad914e
   ef45c334-9f70-11e9-a730-6476baad914e
   ef45c352-9f70-11e9-a730-6476baad914e
   ef45c366-9f70-11e9-a730-6476baad914e
version2 NewDCESecurity --
   ef45c38e-9f70-21e9-a730-6476baad914e
   ef45c3ac-9f70-21e9-a730-6476baad914e
   ef45c3ca-9f70-21e9-a731-6476baad914e
   ef45c3e8-9f70-21e9-a732-6476baad914e
   ef45c410-9f70-21e9-a732-6476baad914e
   ef45c442-9f70-21e9-a732-6476baad914e
   ef45c46a-9f70-21e9-a733-6476baad914e
   ef45c49c-9f70-21e9-a733-6476baad914e
   ef45c4c4-9f70-21e9-a734-6476baad914e
   ef45c4ec-9f70-21e9-a735-6476baad914e
version3 NewMD5 --
   3cc807ab-8e31-3071-aee4-f8f03781cb91
   7e7e001e-6957-38d5-b035-41926dd035dd
   91d52425-9470-3e2b-8e5f-649b7b4ec795
   157ffb9e-d6b2-3b99-9fca-1d347f60b3e3
   33b0480d-6d5f-358a-a37a-893e5850fa62
   4b41d96b-dd72-3314-9cc2-1ab241eacb30
   9337ce23-6909-3137-98d2-a9372bf57692
   1e15f3d0-a040-3eca-a8bc-c677bc8bc619
   b90f3b5d-059c-3c0e-b3d8-85c577763bf6
   f741b68d-a9d2-3396-b499-745920a718ff
version5 NewSHA1 --
   13f575fa-02d7-5c38-8e66-48524dd233a4
   a56079b2-5373-5656-911a-483e3a92f19c
   18c2d54b-1a94-5928-8a4a-2f3dd5fc3e57
   8ac4f8a2-89b8-5ea3-81eb-d1654deaab51
   bb6bff8b-615f-5791-a9fb-e3543fd9d02e
   8209f28f-ccc9-5a90-beaf-d7e8e9927183
   387ab566-8b80-5c6c-a156-8e49a5c9c3fe
   75a4de2d-8049-5bb1-80c9-7250959af3d2
   5991cc2e-2bac-5f83-9ae0-b4a45882a11a
   b8e33f96-449d-5972-8c9c-98b0487549dd
version4 NewRandom --
   01a2c8b4-e3ec-4df5-a027-b7ffe5c6ed5d
   eb2aa249-3614-4833-b979-99c87c4189a8
   4c8bda02-4b27-460a-b46f-d65b4e4f5e48
   23df6b4c-e10f-4bcd-b935-0224f3216680
   2ab1a839-08b2-4069-9bd7-a772c5934e66
   dab1ade8-a446-4609-844b-257a7ab1ba0b
   eb770f37-a59a-4888-9789-d585f514ae93
   b9999fdb-cd20-4bdb-b671-6228667a6746
   8160d492-1ead-4d33-b4e8-d242cf2df332
   63b96ec6-e9d7-4fa4-abb7-31b9e12fca0d

*1:出典: フリー百科事典『ウィキペディアWikipedia)』

PlantUMLをVisualStudioCodeで書く

PlantUMLって

なんかUMLをテキストで書いて絵にするツールらしい

環境

今回はMacでやります

インストール

graphvizをインストール
brew install graphviz

デモ

画面

f:id:m_shige1979:20181103220003p:plain

動画


plantuml

所感

Excelファイルによるドキュメント資料からの脱却を目指したい
まあ、まだまだ無理でしょうけど少しづつやればなんとか…

curlを最新版へソースインストール

ruby関連の勉強を始めてみようとしたけど

なんか出る

/tmp/ruby-build.20181006035131.5059 ~/.rbenv/plugins/ruby-build
curl: (35) Peer reports incompatible or unsupported protocol version.
~

環境

vangrant上のcentos7(64bit)

なんかcurlが古いらしい

$ curl -V
curl 7.29.0 (x86_64-redhat-linux-gnu) libcurl/7.29.0 NSS/3.15.4 zlib/1.2.7 libidn/1.28 libssh2/1.4.3
Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtsp scp sftp smtp smtps telnet tftp
Features: AsynchDNS GSS-Negotiate IDN IPv6 Largefile NTLM NTLM_WB SSL libz
$

証明書関連で動かなくなった感じ?

最新をソースコードよりインストールする

yumでツールをインストール
sudo yum install -y openssl-devel libmetalink-devel libssh2-devel c-ares-devel lbzip2
ダウンロード&インストール
wget https://curl.haxx.se/download/curl-7.61.1.tar.gz
tar zxf curl-7.61.1.tar.gz
cd curl-7.61.1
./configure --enable-libcurl-option
make
sudo make install
インストールパスを間違えたので古いやつを削除してリンクにする
sudo rm -f /usr/bin/curl
sudo ln -s /usr/local/bin/curl /usr/bin/curl
確認
$ curl -V
curl 7.61.1 (x86_64-pc-linux-gnu) libcurl/7.61.1 OpenSSL/1.0.2k zlib/1.2.7
Release-Date: 2018-09-05
Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp smb smbs smtp smtps telnet tftp
Features: AsynchDNS IPv6 Largefile NTLM NTLM_WB SSL libz UnixSockets HTTPS-proxy
$

rbenvでインストールができるようになった\(^o^)/

[vagrant@localhost ~]$ rbenv install 2.3.7
Downloading ruby-2.3.7.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.3/ruby-2.3.7.tar.bz2
Installing ruby-2.3.7...
Installed ruby-2.3.7 to /home/vagrant/.rbenv/versions/2.3.7

[vagrant@localhost ~]$

go言語の学習:websocketで画像を送信し、webで表示

できたの


なにこれ?

websocketサーバより1秒単位で画像をバイナリ形式で送信したもの

ソース(サーバ側)

package main

import (
	"log"
	"net/http"
	"golang.org/x/net/websocket"
	"time"
	"io/ioutil"
	"image"
	"gocv.io/x/gocv"
	"image/color"
	"bytes"
	"image/jpeg"
	"os/user"
)

var (
	black = color.RGBA{0, 0, 0, 0}
)

func main() {
	name := "server"
	log.Println(name + " start")

	http.Handle("/ws", websocket.Handler(WsHandler))
	err := http.ListenAndServe(":8001", nil)
	if err != nil {
		panic("ListenAndServe: " + err.Error())
	}

	log.Println(name + " end")
}

func WsHandler(ws *websocket.Conn) {
	log.Println("connect")

	// 自動送信
	go autoSend(ws)

	// 受信処理監視
	for {
		var b []byte
		err := websocket.Message.Receive(ws, &b)
		if err != nil {
			log.Println(err)
			break
		}
	}
}

// 任意の画像を送信する
func autoSend(ws *websocket.Conn) {
	t := time.NewTicker(1 * time.Second)
	for {
		select {
			case <-t.C:
				// ユーザディレクトリ取得
				user, err := user.Current()
				if err != nil {
					log.Println(err)
					return
				}

				// 画像ファイル読み込み
				b, err := ioutil.ReadFile(user.HomeDir + "/Desktop/" + "pug3_pic1.jpg")
				if err != nil {
					break
				}

				// 画像変換
				atom, err := gocv.IMDecode(b, gocv.IMReadUnchanged)
				if err != nil {
					log.Println(err)
					return
				}
				defer atom.Close()

				// 日時取得
				now := time.Now()

				// 偶数
				if int(now.Second()) % 2 == 0 {
					gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, 2)
				} else {
					gocv.Rectangle(&atom, image.Rect(400, 400, 900, 700), black, -1)
				}

				// 文字列を出力
				timeStr := now.Format("2006/01/02 15:04:05")
				gocv.PutText(&atom, timeStr, image.Pt(20, atom.Rows() - 40), gocv.FontHersheyComplex, 1, black, 1)

				// 画像に変換
				image, err := atom.ToImage()
				if err != nil {
					log.Println(err)
					return
				}
				var jpegBytes []byte
				buf := bytes.NewBuffer(jpegBytes)
				if err := jpeg.Encode(buf, image, nil); err != nil {
					log.Println(err)
				}
				b = buf.Bytes()

				websocket.Message.Send(ws, b)
				log.Println("send")
		}
	}
}

ソース(クライアント側)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <img id="img1" src="">

<script>
    var uri = "ws://localhost:8001" + "/ws";
    webSocket = new WebSocket(uri);
    webSocket.binaryType = 'blob';
    webSocket.onopen = function(e) {
        console.log("open");
    };
    webSocket.onmessage = function(e) {
        console.log("message");
        var data = e.data;
        if (data.constructor === Blob) {
            document.getElementById('img1').src = URL.createObjectURL(data);
        }
    };
    webSocket.onclose = function(e) {
        console.log("close");
    };
</script>
</body>
</html>

ソース(クライアント版2)

package main

import (
	"log"
	"golang.org/x/net/websocket"
	"strconv"
)

var (
	doneCh chan bool
	origin = "http://localhost:8001/"
	url    = "ws://localhost:8001/ws"
)

func main() {
	name := "client"
	log.Println(name + " start")

	doneCh = make(chan bool)

	ws, err := websocket.Dial(url, "", origin)
	if err != nil {
		log.Fatal(err)
		return
	}

	go receiveMsg(ws)

	<-doneCh
	log.Println(name + " end")
}

// 受信
func receiveMsg(ws *websocket.Conn) {
	var b []byte

	for {
		err := websocket.Message.Receive(ws, &b)
		if err != nil {
			log.Println(err)
			doneCh <- true
			break
		}
		log.Printf("Receive datasize=" + strconv.Itoa(len(b)))
	}
}

所感

いろいろ悩んだけど基本は画像をバイナリで送信するだけだった感じ
動画のストリーミングという感じではないのでそこまで難しくないかも…
最初はwebsocketの送受信がうまく行かなかったけどw

go言語の学習:画像を読み込み、文字を追加

日本語ができない

f:id:m_shige1979:20180729104447j:plain
時間は気にしないでwww

前のソースを一部回収

package main

import (
	"gocv.io/x/gocv"
	"log"
	"os/user"
	"time"
	"io/ioutil"
	"image/color"
	"image"
)

var (
	// サイズ
	w = 640
	h = 480

	// 色定義
	black = color.RGBA{0, 0, 0, 0}
	blue = color.RGBA{0, 0, 255, 0}
	red = color.RGBA{255, 0, 0, 0}
	white = color.RGBA{255, 255, 255, 0}
	yellow = color.RGBA{255, 255, 0, 0}
	color2 = color.RGBA{128,128,128,0}

)

func main() {

	// ユーザディレクトリ取得して保存先を作成
	user, err := user.Current()
	if err != nil {
		log.Println(err)
		return
	}

	// img読み込み
	img_file_path := user.HomeDir + "/Desktop/" + "pug3_pic1.jpg"
	// file
	b, err := ioutil.ReadFile(img_file_path)
	if err != nil {
		log.Println(err)
		return
	}

	// 画像取得
	atom, err := gocv.IMDecode(b, gocv.IMReadUnchanged)
	if err != nil {
		log.Println(err)
		return
	}
	defer atom.Close()

	// sample1描画
	sample1(&atom)

	// 領域作成
	img2 := gocv.NewMatWithSize(atom.Rows(), atom.Cols(), atom.Type())
	// 短径(線)
	gocv.Rectangle(&img2, image.Rect(400, 20, 900, 400), blue, 3)
	// 短径(塗りつぶし)
	gocv.Rectangle(&img2, image.Rect(400, 20, 900, 400), color2, -1)
	// 重ねる
	gocv.AddWeighted(atom, 1, img2, 0.5, 0, &atom)

	// 文字列を出力
	timeStr := time.Now().Format("2006/01/02 15:04:05")
	gocv.PutText(&atom, timeStr, image.Pt(20, atom.Rows() - 40), gocv.FontHersheyComplex, 1, black, 1)

	// 出力画像パス
	file_path := user.HomeDir + "/Desktop/" + time.Now().Format("20060102150405") + ".jpg"

	// 保存
	gocv.IMWrite(file_path, atom)

}

func sample1 (img *gocv.Mat) {

	// 短径描画
	gocv.Rectangle(img,image.Rect(10, 10, 100, 100), black, 5)
	gocv.Rectangle(img,image.Rect(200, 15, 300, 350), blue, 5)
	gocv.Rectangle(img,image.Rect(10, 400, 600, 470), yellow, -1) // -1の場合は塗りつぶし
	gocv.Rectangle(img,image.Rect(40, 350, 550, 450), red, -1) // -1の場合は塗りつぶし

	// 多角形描画
	ps := make([][]image.Point, 1)
	p := make([]image.Point, 4)
	p[0] = image.Pt(int(0.1*float64(w)), int(0.1*float64(h)))
	p[1] = image.Pt(int(0.1*float64(w)), int(0.4*float64(h)))
	p[2] = image.Pt(int(0.3*float64(w)), int(0.2*float64(h)))
	p[3] = image.Pt(int(0.3*float64(w)), int(0.1*float64(h)))
	ps[0] = p
	gocv.DrawContours(img, ps, 0, red, -1) // -1の場合は塗りつぶし

}

使うようになったもの

gocv.IMDecode

→ バイナリを読み込んで変換してくれる

gocv.AddWeighted

→ 既存の短径領域に別の短径領域を重ねる

gocv.PutText

→ 指定の場所にテキストを追加

所感

IDEってやっぱり良いです\(^o^)/
viなどでもなんかできそうですけど、commandキーや入力している間に引数やメソッドの候補がでるのは嬉しい

go言語の学習:gocvで画像を作成して出力

gocv

Home :: GoCV - Golang Computer Vision Using OpenCV 3
opencvでなんかいろいろやってくれるライブラリ

インストールするまでくっそ面倒

macの場合はbrew installで簡単そうですけど
なんかいろいろエラーでて大変でした
pythonいれたり、クリーンアップしたり、brew docterして問題解決したり色々やったなあ…
・多分、最新バージョンならwindowsでもできると思います

ソース

package main

import (
	"image"
	"image/color"

	"gocv.io/x/gocv"
	"log"
	"os/user"
	"time"
	"os"
	"image/jpeg"
)

var w = 640
var h = 480

func main() {
	// 描画領域作成
	atom := gocv.NewMatWithSize(h, w, gocv.MatTypeCV8UC3)
	defer atom.Close()

	// 色定義
	black := color.RGBA{0, 0, 0, 0}
	blue := color.RGBA{0, 0, 255, 0}
	red := color.RGBA{255, 0, 0, 0}
	white := color.RGBA{255, 255, 255, 0}
	yellow := color.RGBA{255, 255, 0, 0}

	// 短径描画
	gocv.Rectangle(&atom,image.Rect(0, 0, 640, 480), white, -1)
	gocv.Rectangle(&atom,image.Rect(10, 10, 100, 100), black, 5)
	gocv.Rectangle(&atom,image.Rect(200, 15, 300, 350), blue, 5)
	gocv.Rectangle(&atom,image.Rect(10, 400, 600, 470), yellow, -1) // -1の場合は塗りつぶし
	gocv.Rectangle(&atom,image.Rect(40, 350, 550, 450), red, -1) // -1の場合は塗りつぶし

	// 多角形描画
	ps := make([][]image.Point, 1)
	p := make([]image.Point, 4)
	p[0] = image.Pt(int(0.1*float64(w)), int(0.1*float64(h)))
	p[1] = image.Pt(int(0.1*float64(w)), int(0.4*float64(h)))
	p[2] = image.Pt(int(0.3*float64(w)), int(0.2*float64(h)))
	p[3] = image.Pt(int(0.3*float64(w)), int(0.1*float64(h)))
	ps[0] = p
	gocv.DrawContours(&atom, ps, 0, red, -1) // -1の場合は塗りつぶし

	// ユーザディレクトリ取得して保存先を作成
	user, err := user.Current()
	if err != nil {
		log.Println(err)
		return
	}
	file_path := user.HomeDir + "/Desktop/" + time.Now().Format("20060102150405") + ".jpg"

	// 保存
	//gocv.IMWrite(file_path, atom)

	// 画像に変換
	image, err := atom.ToImage()
	if err != nil {
		log.Println(err)
		return
	}

	// file出力
	file, errOs := os.Create(file_path)
	if errOs != nil {
		log.Println(err)
		return
	}
	defer file.Close()

	// jpeg形式で出力
	if err := jpeg.Encode(file, image, &jpeg.Options{100}); err != nil {
		log.Println(err)
		return
	}

	/*
	// bytes変換
	var jpegBytes []byte
	buf := bytes.NewBuffer(jpegBytes)
	if err := jpeg.Encode(buf, image, nil); err != nil {
		log.Println(err)
	}
	jpegBytes = buf.Bytes()
	*/

}

できたもの(適当)
f:id:m_shige1979:20180728200639j:plain

本来は

webカメラの情報をキャプチャして取得するとかもできますけど
持っているwebカメラではだけでした(´・ω・`)

所感

複雑な画像編集することが容易にできるような感じとのこと
文字入れたり、グレースケールとかできたらやってみたい