BlueSound Node 2にネットラジオ局のお気に入り登録

NodeのTuneInで検索できるラジオ局は簡単に追加できますが、URLを入力して追加するのはストリーミングのURLがわからないと追加出来ない。naimとclassical radio stream(どちらも320Kbps品質)はなんとか見つけ出して追加した。Linn Radioが休止状態だから、この4局だけでもなんとか使えそう。

できれば今現在入手困難なRaspberry PI(RPi)を今Volumioで使っているけど、他の用途で使いたいから。RPiの入手性が改善させるのは早くても今年の下半期じゃないかな。

 

admin

Goでのチャネルの空読み

Goでのチャネルは使い方を誤ればpanicになりますが、これはGoの潔いところと思えばいいですが、チャネルの使い方で読み出しだけれども変数には代入しないというのがあります。

具体的には値を使わないのだから、単にダミーのリードですが実際の使い方としては終了待ちとして使うことになるんだろうと思います。

非常にシンプルな空読みの確認用のコードですが、チャネルでバッファーを2個用意して、最初の値は空読みすることで、2個目の値が変数xに代入されていることがわかります。

package main

import  "fmt"

func main() {
	ch := make(chan int, 2)
	defer 	close(ch)
	go func() {
		ch <- 100
		ch <- 200
	}()
	<- ch		// dummy read
	x := <- ch
	fmt.Println(x)
}

<実行結果>
200

 

admin

Goのsliceの型はなんでも良い

Goでは何でも型に定義できるかと思いますが(例外があるかどうかは定かではない)、例えば関数もそのまま型として使用できます。

例えば以下のように、

ここでは”func(float64) float64“を型名として使用して、二つの関数をスライスに格納して、それぞれの関数を無名関数の引数(あえて無名関数でなくても良いですが)としてfor rangeループで実行させています。

package main

import (
	"fmt"
	"math"
)


func main() {
	f1 := func(a float64) float64{
		return a*a
	}
	f2 := func(a float64) float64{
		return math.Pow(a, 3)
	}
	
	fs := []func(float64) float64{f1, f2}		// "[]func(float64) float64" is a type

	for _, fss := range fs{
		func(f func(float64) float64){
			fmt.Println(f(3))
		}(fss)
	}
}

他の言語からすると奇妙な記述方法ではありますが、Goではこのような記法も成立するのだと。

 

admin

 

Bluesound Node 用のMac版アプリ

去年まではMacで立ち上がらないから、iPadでずっと使っていたけど久々にD.LしてみたらMac(M1)でも動くようになっている。選択の余地が広がるのは良いことです。

版数は、

P.S. (2023.1.30)

いまだbuggy感、プレイの停止ができない!もちろんiPad版では問題ないけども。Intel Macでは起動もしない。

 

admin

Goのloopclosure問題、

問題というよりも、タイミングがそうだからそうなるよねという話です。

Goのgoroutineでの話で、以下のソースをVScodeで読み込ませるとopenclosureというwarningが出ます。実行結果も期待される値ではなくて、例えば全部40とかになるのですが、なぜか?

それは無名関数に明示的に引数としてvを与えてる訳ではないので、無名関数の中でvの使われる時点(v*2)のv値が使われるからです。具体的には並行処理を起動しても、すぐに各ルーチンが実行される訳でもなくて実行タイミングには遅延があるだろうから、実はvを使おうとしたタイミングではすでにfor range処理が先行したり、あるいは終了している可能性もあるから。

対応方法はv無名関数に引数として渡すのがスマートなやり方になるでしょう。

https://github.com/mushahiroyuki/lgo/blob/main/example/ch10/ex1005.go

package main

import "fmt"

func main() {
	a := []int{2, 4, 6, 8, 10, 12, 14, 16, 18, 20} //liststart
	ch := make(chan int, len(a))
	for _, v := range a {
		go func() {
			ch <- v * 2
			fmt.Println(":", v)
		}()
	}
	for i := 0; i < len(a); i++ {
		fmt.Print(<-ch, " ")
	} //listend
	fmt.Println()
}

こうすれば、無名関数が不規則なタイミングでその時点のv値を使用することなく、意図した値を引き渡すことができます。別の対応方法としてはvをシャドーイング(v := vをfor rangeの直後に挿入)しても良さそうですが。いずれにしろ並行処理の実行順序は保証されないので、結果の並びは昇順にはなりません。

		go func(v int) {
			ch <- v * 2
			fmt.Println(":", v)
		}(v)

 

admin

 

gobotを使ってTelloを制御する

gobotはrobotics/IoT用のフレームワークですが、サポートモデルとしてTelloが対象に含まれています。

https://gobot.io/blog/2018/04/20/hello-tello-hacking-drones-with-go/

でコマンド制御と動画再生を行っているので、コードを実行してみました。

動画の再生にはmplayer(これは追加インストール必要でした)を使って、goから引き渡しています。

$ brew install mplayer

でインストールします。go getでもmplayerがインストールできますが、外部アプリなのでこちらではない。

<環境構築>

Goはインストール済みの前提で、gobotの他にdji/telloのインストールも必要。これはmod内でしかインストールできません。

% go get -d -u gobot.io/x/gobot/...
% go get gobot.io/x/gobot
% go get gobot.io/x/gobot/platforms/dji/tello

<Telloから画像受け取り再生するコード>

Telloの素のコマンドは意識する必要はなくて関数でwrapされています。明示的に並行処理は使っていなくて、drone.On()はイベント待ちのように見えます。

package main

import (
	"fmt"
	"os/exec"
	"time"

	"gobot.io/x/gobot"
	"gobot.io/x/gobot/platforms/dji/tello"
)

func main() {
	drone := tello.NewDriver("8890")

	work := func() {
		mplayer := exec.Command("mplayer", "-fps", "25", "-")
		mplayerIn, _ := mplayer.StdinPipe()
		if err := mplayer.Start(); err != nil {
			fmt.Println(err)
			return
		}

		drone.On(tello.ConnectedEvent, func(data interface{}) {
			fmt.Println("Connected")
			drone.StartVideo()
			drone.SetVideoEncoderRate(4)
			gobot.Every(100*time.Millisecond, func() {
				drone.StartVideo()
			})
		})

		drone.On(tello.VideoFrameEvent, func(data interface{}) {
			pkt := data.([]byte)
			if _, err := mplayerIn.Write(pkt); err != nil {
				fmt.Println(err)
			}
		})
	}

	robot := gobot.NewRobot("tello",
		[]gobot.Connection{},
		[]gobot.Device{drone},
		work,
	)

	robot.Start()
}

 

動画は外部のアプリを使っているせいか、遅延が大きいのと画素が飛んでいます。gobotの本質とはそれほど関係ないですが、

gobotはこれからも成長していくだろうから、Robotics/IoTアプリ開発はかなり楽になりそうです。

 

admin

 

interfaceの基本(Go言語)

interfaceはGo言語における唯一の抽象型だそうで、抽象型というのは実装の自由度がある訳だから使いこなせば便利でしょう。

ここでは使いこなしではなくて、基本機能の説明です。構造体のデータの操作を行う二つのメソッドを定義しています。

package main

import (
	"fmt"
)

type person struct {
	FirstName	string
	FamilyName	string
	Age			int8
}

type inf interface{
	print()
	exchange()
}

func (p *person) print(){
	fmt.Println(p.FirstName, p.FamilyName, p.Age)
}

func (p *person) exchange(){
	fn := p.FirstName
	p.FirstName = p.FamilyName
	p.FamilyName = fn
	p.print()
}

func main(){
	var i1 inf = &person{
		FirstName: "Mary",
		FamilyName: "Bloody",
		Age: 36,
	}

	i1.print()

	i1.exchange()
}

実行結果は、

Mary Bloody 36
Bloody Mary 36

Goの場合にはJavaやC#のように明示的なimplementは不要です、つまり緩い関係になってます。interfaceで定義したメソッドが全て実装されていればimplementと等価です。

じゃ『interfaceは何が良いの?』というとそれは抽象型だからというところに行き着くでしょう、何故なら実装をどう記述しようがinterfaceに規定したメソッドと同じ形(名称、引数と戻り値)であれば入れ替えできるわけで、実は明示的では無いですが継承や多態性が実現できることになります。それゆえGoにおける唯一の抽象型というのは使い方に多様性があることになります。

例えば、上の例で異なる型を扱う同名のメソッドを定義すれば、それは正しく多態性になる訳だから。

このように型によって緩やかな結合を取ることがGoの一つの大きな特徴であるように思います

P.S. (2023/1/25)

interfaceの型名は~~er(~~するもの)とするのが通例のようですから、命名規則からは外れています。

 

admin

 

Apple SiliconのFusion 360 native対応

https://isehara-3lv.sakura.ne.jp/blog/2022/07/18/m1-macbook-air到着/

昨年夏にM1 MacBok Airも立ち上げましたが、Intel Macが継続して必要だった大きな理由のFusion 360が動かない問題は今年の夏頃にはnative対応できるらしい。

以下、Autodeskのサイトからの引用、

唯一の残りはレーザープリンタードライバーが対応しないから使えないだけ、トナー変えてそれほど使っていないから勿体無いし、そもそもプリンタ使う頻度も少ないからどうするか?

P.S. 2023/1/23

canonのサポート見てみたらLBP6230のVentura対応ドライバーが登録されてたのでインストール。特にApple Siliconの記述はないからユニバーサルか。

 

admin

値を変更するならポインター渡し(Go言語)

https://isehara-3lv.sakura.ne.jp/blog/2023/01/17/goは値渡し言語、/

で、Goは値渡しなので、渡し先で値を変更するならポインター渡しと言っていますがその例です。

以下のソースで関数printPersonにポインターを渡した時はjohnの値が変更されていますが、値渡しにすると元の値は変更されません。

package main

import (
	"fmt"
)

type Person struct{
	LastName string
	FirstName string
	Age int
}

func (p Person) printPerson(p1 *Person) Person{
	p1.Age = 72
	fmt.Println("John : ", p1.FirstName, p1.LastName, p1.Age)
	return *p1
}

func main() {
	var john Person
	john.FirstName = "John"
	john.LastName = "Wein"
	john .Age = 70
	john.printPerson(&john)
	fmt.Println("returned age : ", john.Age)
}

以下はポインタ渡しと値渡しでの実行結果です、確かに値渡しではオリジナルの情報は更新されません。

<ポインター渡し>
John :  John Wein 72
returned age :  72

<値渡し>
John : John Wein 72
returned age : 70

 

admin

 

 

SSDとHDDでTimeMachineの振る舞いが違う

昨年、NASの一台のディスクを容量拡大のためにSSDからHDDに入れ替えましたが、MacのバックアップがSSD(timemachine)では制限値80%で古いファイルは削除されて追加されているようですが、HDD(Timemachine_2)では80%の壁は乗り越えて増殖中。最後にはおそらく削除は発生すると思うけれども、特に設定は無いように思うけれどもなんでだろう?

P.S. (2023/2/5)

結局どちらの領域も制限地では留まらず、

 

admin