2024,江端さんの技術メモ

UDPは一斉送信できるのは知っていましたが、いままで一度も使ったことがありませんでした。

で、今回試してみたら、メチャクチャ便利だなぁ、と実感しましたので、自分の為にコードを残しておきます。

受信側の「SO_REUSEADDRオプションを設定」がキモです。"bind: Address already in use"の呪いを受けなくてもすみます。

udp_multicast_send に対して、複数個のudp_multicast_recv でメッセージ受信できます。

(送信側) udp_multicast_send.c

#include <stdio.h>
/*
gcc -o udp_multicast_send  udp_multicast_send.c
*/

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MULTICAST_GROUP "239.0.0.1"
#define MULTICAST_PORT 12345

void receive_process_count() {
    int sock;
    struct sockaddr_in addr;
    struct ip_mreq mreq;
    char message[256];

    // ソケットの作成
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        exit(1);
    }

    // 受信するアドレスとポートを設定
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(MULTICAST_PORT);

    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(1);
    }

    // マルチキャストグループに参加
    mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt");
        exit(1);
    }

    // メッセージの受信
    while (1) {
        int nbytes = recv(sock, message, sizeof(message), 0);
        if (nbytes < 0) {
            perror("recv");
            exit(1);
        }

        message[nbytes] = '\0';
        printf("Received process count: %s\n", message);
    }
}

int main() {
    printf("abc_vtp_0.2 process started. Waiting for process count...\n");
    receive_process_count();
    return 0;
}

(受信側) udp_multicast_recv.c

/* gcc -o udp_multicast_recv udp_multicast_recv.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define MULTICAST_GROUP "239.0.0.1"
#define MULTICAST_PORT 12345

void receive_process_count() {
    int sock;
    struct sockaddr_in addr;
    struct ip_mreq mreq;
    char message[256];

    // ソケットの作成
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        perror("socket");
        exit(1);
    }

    // SO_REUSEADDRオプションを設定
    int reuse = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
        perror("Setting SO_REUSEADDR error");
        close(sock);
        exit(1);
    }

    // 受信するアドレスとポートを設定
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port = htons(MULTICAST_PORT);

    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        perror("bind");
        exit(1);
    }

    // マルチキャストグループに参加
    mreq.imr_multiaddr.s_addr = inet_addr(MULTICAST_GROUP);
    mreq.imr_interface.s_addr = htonl(INADDR_ANY);
    if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
        perror("setsockopt");
        exit(1);
    }

    // メッセージの受信
    while (1) {
        int nbytes = recv(sock, message, sizeof(message), 0);
        if (nbytes < 0) {
            perror("recv");
            exit(1);
        }

        message[nbytes] = '\0';
        printf("Received process count: %s\n", message);
    }
}

int main() {
    printf("abc_vtp_0.2 process started. Waiting for process count...\n");
    receive_process_count();
    return 0;
}

2024,江端さんの忘備録

私は、大学のころ山岳サークルに入っていましたが、1年目で早々に退会しました。

I was in a college mountain club but left early after my first year.

理由は色々あったのですが、登山が本当に好きであれば、止めなかったとは思うので、私はそんなに好きではなかったのでしょう。

There were many reasons, but I don't think I would have stopped if I liked climbing, so I guess I didn't like it that much.

それ以上に、登山というのは本当に苦しかった。普通に登るだけでも辛いのに、そこにあの荷物です。

But more than that, climbing the mountain was a real pain. It was usually hard enough to climb, but then there was all that luggage.

荷物が重すぎて(20kgくらい)、正しい手順を正確に行わないと、立ち上がることすらできない。

The load was so heavy (about 20 kg) that I could not stand up if I didn't strictly follow the correct procedure.

ようやく立ち上がって、5歩進めば『もう嫌』と思えるようなものが、楽しいわけがない。

It's no fun to finally get up, take five steps forward, and think, 'I don't want to do this anymore.

あと、絶望的な「非日常感」ですね。「ここで滑落すれば死ぬ」は当然として、「ここで骨を折るだけで死ぬ」「ここで腹痛おこせば死ぬ」というのは、日常ではありえない状況です。

Another thing is the hopeless sense of the extraordinary. It is natural that “if you slip and fall here, you will die,” but “if you just break a bone here, you will die,” or “if you have a stomachache here, you will die” are situations that would not be possible in everyday life.

山には楽しいこともあったと思うのですが ―― けど、それ以上に、私にとって、山は辛く、怖いものだったのです。

I think there were fun things about the mountains -- but more than that, for me, the mountains were rugged and scary.

今となっては良く分からいのですが、あの敗北感が、その後の、私の海外放浪に繋ったと思うこともあります。

I am unsure now, but I sometimes think that sense of defeat led to my subsequent wandering abroad.

まあ、全く無関係だった、という気もしますが。

Well, I also feel that it was utterly irrelevant.

-----

私は、登山に関するドキュメンタリーとか映画とかは、好きです。

I like documentaries and movies about mountaineering.

故井上靖先生の『氷壁』(1956年)や『岩壁』、ちょっと方向は違いますが、高村薫先生の『マークスの山』(1993年)も好きです。

I also like “Ice Wall” (1956) and “Rock Wall” by the late Yasushi Inoue and, although in a slightly different direction, “Mark's Mountain” (1993) by Kaoru Takamura.

新田次郎先生の『剱岳 点の記』(2009年)、古いもので言えば、『八甲田山』(1977年)など、小説も映画もなんども読み直しました。

I have reread many novels and movies, including Jiro Nitta's “Tsurugidake: Ten no Ki” (2009) and an older one, “Hakkoudasan” (1977).

『剱岳 点の記』は、Making of  の方も何度も見直しています。

I have reviewed the Making of Edition of “Tsurugidake: Ten no Ki” many times.

神々の山嶺(アニメの方)」は、もう3回以上も見ています。

I've watched “Ridge of the Gods (the animated one)” more than three times now.

(夢枕獏さんの小説を谷口ジローさんが漫画化し、『フランスのアニメ制作陣によって手がけられ』、フランスで最も有名なアニメ作品の一つである、というのも結構ビックリなのですが)

(It is also quite surprising that Taniguchi Jiro's manga adaptation of Yumemakura Baku's novel was “created by a French animation production team” and is one of the most famous French animation works.)

山を愛する人の狂気を描いた作品として、ズバ抜けています。

It is an outstanding work about the madness of a mountain lover.

私も『狂気だけが才能を越える』と主張してきた一人ですが、この山を愛する人の狂気には遠く及ばない気がします。

I have insisted that 'only Madne's surpasses talent,' but I feel that I am not far from the madness of this mountain lover.

課題で「主観的幸福(SWB)」を調べているのですが、なかなか興味深いです

登山家の死亡率は、恐しく高いです。

The mortality rate for climbers is frighteningly high.

ChatGPTに推定をお願いしたところ、『ベレストや他の難関山岳に挑戦する登山家の死亡率が約1~30%であるのに対し、一般人の年間死亡率は約0.1%以下』いうことでした。

ChatGPT estimated that “the annual mortality rate for the general public is about 0.1% or less, compared to about 1-30% for climbers who attempt Mount Everest and other rugged mountains.

登山家の死亡率はもっとも楽観的な数値で10倍、最悪値で300倍以上ということになります。

The mortality rate for climbers is ten times higher at the most optimistic value and more than 300 times higher at the worst value.

-----

そういえば、私、ティーンエイジャのころ、母親から『冬山をやったら、親子の縁を切る』とまで言われていましたが、母の心配はまったくの杞憂でした。

Come to think of it, when I was a teenager, my mother even told me that if I did winter mountaineering, she would cut me off from my parents, but her fears were completely unfounded.

―― あなたの息子は、冬山どころか、夏の北アルプスの1週間の縦走くらいで簡単に根を上げる、軟弱者でしたよ

Your son was feeble-minded and gave up quickly climbing just for a weeklong traverse of the Northern Alps in summer, let alone winter mountains.

と、空に向かって呟いています。

I muttered to the sky.

------

愛の中でも、ロジックを越えた狂気の(ように見える)愛は、代償が大きいです。

Among love, crazy (seemingly) love that transcends logic has a high price to pay.

「命を賭けた愛」と呼ぶに値するものは、『ここ』にしかない、という気すらします。

I even feel that the only thing worthy of being called “love at the risk of one's life” can be found in “Mountain Climbing.

2024,江端さんの忘備録

ドキュメント72時間SP フランス・パリ 街角のマンガ喫茶で』を視聴しました。

I watched " Document 72 Hours SP: Paris, France - At a Manga Cafe on a Street Corner ".

楽しかったです。

It was fun.

私の知っているアニメやコミックが、海外でも人気があるのは嬉しいです。

I am glad that anime and comics I know are also popular overseas.

二次創作物も好きです。

I also like derivative works.

―― ゴーダマシッダールタさん没後、2500年オーダの時間をかけて創作され続けた膨大な物量の「同人誌」

コンテンツを作成して頂いている原作者の方は言うに及ばず、その編集、翻訳や拡販に努力されている皆様の努力のお陰があってのことかと思います。

I am grateful for the efforts of all those who are editing, translating, and promoting the content, not to mention the original authors who are creating them.

その成果を誇ってもいいのは「上記の方々」であって、「日本」ではありませんが。

It is "the above people" who can be proud of their achievements, though not "Japan.

それはさておき。

Aside from that.

------

ChatGPTに「日本国内のマンガとアニメの市場規模の合計、または、日本を除く海外における日本のマンガやアニメの市場規模の合計をフェルミ推定で概算して下さい」と頼んでみたところ、国内の市場規模は6兆円、海外では4.6兆円と出してきました。

When we asked ChatGPT to estimate the total market size of manga and anime in Japan, or the total market size of Japanese manga and anime overseas excluding Japan, using Fermi estimation, they came up with 6 trillion yen for the domestic market and 4.6 trillion yen for the overseas market.

一方、フェルミ推定ではなく、過去データからの推定を頼んでみたら、日本国内市場規模は3兆6937億円、海外では約1兆4600億円と出てきました。

On the other hand, when we asked for an estimate based on historical data rather than Fermi's estimation, we came up with a market size of 3.6937 trillion yen in Japan and about 1.46 trillion yen overseas.

「韓国国内の韓国ドラマの市場規模の合計、または、韓国を除く海外における韓国ドラマの市場規模の合計をフェルミ推定ではなく、過去のデータに基づいて概算して下さい」とお願いしたところ、こちらは、それぞれ、9兆5715億円(の大部分)、1兆6740億円となりました。

When I asked for a rough estimate of the total market size of Korean dramas in Korea or the total market size of Korean dramas overseas, excluding Korea, based on historical data rather than Fermi estimates, I got 9,571.5 billion yen (most of it) and 1,674.0 billion yen, respectively.

日本国内のマンガとアニメの市場規模と韓国のドラマの市場規模は、拮抗している(やや日本の方が強い?)という値が出てきました。

The market size of the Japanese domestic manga and anime market and the Korean drama market are close (slightly more substantial in Japan?).

------

昔、私は『韓国では、ドラマのコンテンツに国家予算が投入されている』という話を、聞いたような気がします。

I think I heard long ago that the government invested in the budget for drama content in Korea.

比して、日本では、アニメ制作者を、『安価なカカオやコーヒー豆を買い叩き、あるいは、格安の衣服を作らせる第三世界の労働者と同程度の低い地位のままにしている』との話も聞いたことがあります。

In comparison, I have heard that in Japan, animation producers are "left with the same low status as third-world workers who make them forced to buy cheap cacao and coffee beans or make cheap clothes.

上記の話はウラを取っていませんので、私が、誤解していたら大変申し訳ありませんが ――

I have not checked the above story as a fact check, and I am very sorry if I have misinterpreted it, but--

『韓国のドラマと同程度の規模の市場を持つマンガやアニメのコンテンツを、日本国は全く保護していない』

"The Japanese state does not protect manga and anime content, which have a market as large as Korean dramas."

という理解で良いでしょうか?

Am I correct in my understanding that this is the case?

なにしろ、私は、アニメのエンドロールに「提供: 日本国文化庁」とか入っているのを見たことありませんし。

After all, I have never seen "courtesy of the Agency for Cultural Affairs of Japan" in the end roll of an anime.

(この辺について、私に教えてくれる方のご連絡をお待ちしております。謙虚にご講義を受けるつもりです。)

(I look forward to hearing from anyone who can enlighten me on this area. I will humbly accept your lecture.)

------

とは言え、『コンテンツ文化に、国家が関わってくるとロクなことにならない』というのも、また事実ですので ―― 『国策として、何もしない』というのは、それはそれで悪くない、とも思うこともあります。

However, it is also a fact that "when the state gets involved in content culture, it can lead to bad things," - so I sometimes think that "doing nothing as a matter of national policy" is not such a bad idea.

伝統芸能などとは異なり、サブカルについては『国に借りを作っておかない』というのも、ありかなぁ、とも思います。

Unlike traditional arts, I think it is important to "not owe a debt to the government" when it comes to subculture.

ただ、このまま放置しておいて、日本のサブカルが衰退していくようなことになれば、多分、私はとても悲しいです。

However, I will probably be despondent if this is left unchecked and the Japanese subculture is left to decline.

しかしながら、アニメ制作者の酷い現場の現状が続けば、"それ"はいつかは起こるのだろう、とも思います。

However, I believe that "it" will happen someday if the terrible situation of animation producers in the field continues.

------

では、日本の次に、マンガとアニメの覇権を握るとすれば、それはどの国か?

So, after Japan, which country will be most dominant in manga and anime, if any?

中国(大陸)が最右翼ですが、共産党一党独裁をしている内は大丈夫でしょう。権力が、サブカルの表現の自由に敵対するでしょうから。

China (mainland) is the most right-wing, but as long as they have a one-party communist dictatorship, they will be fine. The power will be hostile to the freedom of expression of the subculture.

北米(アメリカ合衆国)は、"自国正義ファースト"の国ですので、こっちも心配ないです。

North America (United States of America) is a "justice first" country, so there is no need to worry here either.

韓国は、ドラマで成功していますので、この国は強いのではないかと。

Korea has been prosperous in dramas, so this country must be strong.

あと台湾なども強そうです(半導体は強いですね)。

Also, Taiwan seems to be strong (semiconductors are strong).

イスラム圏は除外して良さそうです。日本のサブカル持ち込んだら鞭打ちになるかもしれません(酷い偏見です。私、イスラム圏の学友が何人かいます)。

The Muslim world seems to be an excellent place to exclude. If you bring in a Japanese subculture, you may get flogged (I'm prejudiced. I have several Muslim schoolmates).

根拠はないのですが、『欧州の小国のどこかが、突然立ち上がっているんじゃないかな』という予感があります。

I don't have any evidence, but I have a feeling that 'some of the smaller European countries will suddenly be standing up.

-----

ちなみに、「日本のマンガやコンテンツ」の部分を、「自動車産業」に置き換えてみたら、ChatGPTは、それぞれ、57兆円、33兆円と回答しました。

Incidentally, if we replace the "Japanese manga and content" part with the "automobile industry," ChatGPT responded 57 trillion yen and 33 trillion yen, respectively.

「ハードとソフトを比較するのはナンセンス」とは思いつつも、市場規模のケタが違うなぁ、と実感しました。

Although I think it is nonsense to compare hardware and software, I realized that the market scale is very different.

2024,江端さんの技術メモ

これからGO言語で構造体を使い倒さねばならない私(江端)が、これから何度でもハマるような気がするので、わざわざ簡単なプログラムを作って、今後の自分の為にメモを残す。

agentsスライスの要素を変更しても、その変更が反映されない理由として、agent変数が値渡しである可能性があります。Goでは、スライスやマップなどのリファレンスタイプを使わない限り、変数は値渡しされます。

具体的には、以下のようなコードでは、agent変数はスライスagentsの要素をコピーしたものであり、変更はagentsスライス自体には反映されません。

 

重要なポイントは、

"for i, agent := range agents {" は表示するだけなら問題はないが、書き込みはできず、

" for i := range agents {
          agent := &agents[i] // この1行が滅茶苦茶重要"
としないと、書き込みが反映されない、ということであり、変更点は、以下のようにforループ内でagentをポインタとして扱うことである。

 

また表示の際にも、例えば1000番目の構造体のエージェントだけを表示したいのであれば、

i := 1000
	if i < len(agents) {  // これは構造体の数を越えていないかをチェックする
		agent := &agents[i]
この1行が滅茶苦茶重要"
		fmt.Printf("hh_id:%d idv_id:%d\n", agent.hh_id, agent.idv_id)
		fmt.Printf("Agent %d: q1=%d, q2t=%d, q31=%d, q32=%d, q33=%d, q34=%d, q41a=%d, q42a=%d, q43a=%d, q44a=%d, q45a=%d, q46a=%d, q47a=%d, q41e=%d, q42e=%d, q43e=%d, q44e=%d, q45e=%d, q46e=%d, q47e=%d, q10=%d, q13=%d, q14=%d, no_answer=%d\n",
			i, agent.q1, agent.q2t, agent.q31, agent.q32, agent.q33, agent.q34,
			agent.q41a, agent.q42a, agent.q43a, agent.q44a, agent.q45a, agent.q46a, agent.q47a,
			agent.q41e, agent.q42e, agent.q43e, agent.q44e, agent.q45e, agent.q46e, agent.q47e,
			agent.q10, agent.q13, agent.q14, agent.no_answer)

		for _, trip := range agent.trip {
			fmt.Printf("trip: {id:%d act_id:%d trip_id:%d day:%d origin:%s destination:%s ox:%.6f oy:%.6f dx:%.6f dy:%.6f m_ox:%.6f m_oy:%.6f m_dx:%.6f m_dy:%.6f dep_area:%s arr_area:%s dep_time:%s m_dep_time:%s arr_time:%s m_arr_time:%s stay_time:%.2f traffic:%s next_id:%d category:%d}\n",
				trip.id, trip.act_id, trip.trip_id, trip.day, trip.origin, trip.destination, trip.ox, trip.oy, trip.dx, trip.dy, trip.m_ox, trip.m_oy, trip.m_dx, trip.m_dy,
				trip.dep_area, trip.arr_area, trip.dep_time.Format("15:04:05"), trip.m_dep_time.Format("15:04:05"), trip.arr_time.Format("15:04:05"), trip.m_arr_time.Format("15:04:05"), trip.stay_time.Hours(), trip.traffic, trip.next_id, trip.category)
		}

以下は、その考え方を簡易に表現したもの

 

普通の構造体の取扱は、こんな感じでいい。

// G:\home\ebata\tomioka3B\src\others\main85.go

package main

import (
	"errors"
	"fmt"
)

// Trip構造体の定義
type Trip struct {
	id      int
	act_id  int
	trip_id int
	day     int
}

// Agent構造体の定義
type Agent struct {
	hh_id     int
	idv_id    int
	trip      []Trip
	sex       int
	no_answer int
}

// getGenderメソッドの定義
func (a Agent) getGender() (string, error) {
	switch a.sex {
	case 1:
		return "女性", nil
	case 2:
		return "男性", nil
	case 3:
		return "その他(未回答)", nil
	default:
		return "", errors.New("invalid sex value")
	}
}

func main() {
	// エージェントを5個生成
	agents := make([]Agent, 5)
	for i := 0; i < 5; i++ {
		agents[i] = Agent{
			hh_id:  i + 1,
			idv_id: i + 1,
			sex:    (i % 3) + 1, // 1, 2, 3をループ
			trip: []Trip{
				{id: 1, act_id: 10, trip_id: 100 + i, day: 1},
				{id: 2, act_id: 20, trip_id: 200 + i, day: 2},
			},
		}
	}

	// 各エージェントの性別とTrip情報を表示
	for i, agent := range agents {
		gender, err := agent.getGender()
		if err != nil {
			fmt.Printf("Agent %d: Error: %v\n", i+1, err)
		} else {
			fmt.Printf("Agent %d: Gender: %s\n", i+1, gender)
		}
		for _, trip := range agent.trip {
			fmt.Printf("\tTrip ID: %d, Act ID: %d, Trip ID: %d, Day: %d\n", trip.id, trip.act_id, trip.trip_id, trip.day)
		}
	}
}

出力結果
$ go run main85.go
Agent 1: Gender: 女性
Trip ID: 1, Act ID: 10, Trip ID: 100, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 200, Day: 2
Agent 2: Gender: 男性
Trip ID: 1, Act ID: 10, Trip ID: 101, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 201, Day: 2
Agent 3: Gender: その他(未回答)
Trip ID: 1, Act ID: 10, Trip ID: 102, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 202, Day: 2
Agent 4: Gender: 女性
Trip ID: 1, Act ID: 10, Trip ID: 103, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 203, Day: 2
Agent 5: Gender: 男性
Trip ID: 1, Act ID: 10, Trip ID: 104, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 204, Day: 2

このプログラムの後ろに、Agent 4を変更するプログラムを追加したい、とする。

Agent 4は
(1)hh_idを1000, idv_idを1001, sexを2、no_answerを1
(2)
Trip ID: 123, Act ID: 456, Trip ID: 789, Day: 1
Trip ID: 321, Act ID: 654, Trip ID: 987, Day: 2
とするプログラムは以下のようになる。

 

package main

import (
	"errors"
	"fmt"
)

// Trip構造体の定義
type Trip struct {
	id      int
	act_id  int
	trip_id int
	day     int
}

// Agent構造体の定義
type Agent struct {
	hh_id     int
	idv_id    int
	trip      []Trip
	sex       int
	no_answer int
}

// getGenderメソッドの定義
func (a Agent) getGender() (string, error) {
	switch a.sex {
	case 1:
		return "女性", nil
	case 2:
		return "男性", nil
	case 3:
		return "その他(未回答)", nil
	default:
		return "", errors.New("invalid sex value")
	}
}

func main() {
	// エージェントを5個生成
	agents := make([]Agent, 5)
	for i := 0; i < 5; i++ {
		agents[i] = Agent{
			hh_id:  i + 1,
			idv_id: i + 1,
			sex:    (i % 3) + 1, // 1, 2, 3をループ
			trip: []Trip{
				{id: 1, act_id: 10, trip_id: 100 + i, day: 1},
				{id: 2, act_id: 20, trip_id: 200 + i, day: 2},
			},
		}
	}

	// 各エージェントの性別とTrip情報を表示
	for i, agent := range agents {
		gender, err := agent.getGender()
		if err != nil {
			fmt.Printf("Agent %d: Error: %v\n", i+1, err)
		} else {
			fmt.Printf("Agent %d: Gender: %s\n", i+1, gender)
		}
		for _, trip := range agent.trip {
			fmt.Printf("\tTrip ID: %d, Act ID: %d, Trip ID: %d, Day: %d\n", trip.id, trip.act_id, trip.trip_id, trip.day)
		}
	}

	// Agent 4を変更
	agentIndex := 3
	if agentIndex < len(agents) {
		agents[agentIndex] = Agent{ // for i, agent := range agents のagentに上書きしても反映されない
			hh_id:     1000,
			idv_id:    1001,
			sex:       2,
			no_answer: 1,
			trip: []Trip{
				{id: 123, act_id: 456, trip_id: 789, day: 1},
				{id: 321, act_id: 654, trip_id: 987, day: 2},
			},
		}
	}

	// 変更後のエージェント情報を表示
	fmt.Println("\nAfter modification:")
	for i, agent := range agents {
		gender, err := agent.getGender()
		if err != nil {
			fmt.Printf("Agent %d: Error: %v\n", i+1, err)
		} else {
			fmt.Printf("Agent %d: Gender: %s\n", i+1, gender)
		}
		fmt.Printf("Agent %d: hh_id:%d, idv_id:%d, no_answer:%d\n", i+1, agent.hh_id, agent.idv_id, agent.no_answer)
		for _, trip := range agent.trip {
			fmt.Printf("\tTrip ID: %d, Act ID: %d, Trip ID: %d, Day: %d\n", trip.id, trip.act_id, trip.trip_id, trip.day)
		}
	}
}

出力結果
tomoi@DESKTOP-1QS7OI7 MSYS /g/home/ebata/tomioka3B/src/others
$ go run main85.go
Agent 1: Gender: 女性
Trip ID: 1, Act ID: 10, Trip ID: 100, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 200, Day: 2
Agent 2: Gender: 男性
Trip ID: 1, Act ID: 10, Trip ID: 101, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 201, Day: 2
Agent 3: Gender: その他(未回答)
Trip ID: 1, Act ID: 10, Trip ID: 102, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 202, Day: 2
Agent 4: Gender: 女性
Trip ID: 1, Act ID: 10, Trip ID: 103, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 203, Day: 2
Agent 5: Gender: 男性
Trip ID: 1, Act ID: 10, Trip ID: 104, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 204, Day: 2

After modification:
Agent 1: Gender: 女性
Agent 1: hh_id:1, idv_id:1, no_answer:0
Trip ID: 1, Act ID: 10, Trip ID: 100, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 200, Day: 2
Agent 2: Gender: 男性
Agent 2: hh_id:2, idv_id:2, no_answer:0
Trip ID: 1, Act ID: 10, Trip ID: 101, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 201, Day: 2
Agent 3: Gender: その他(未回答)
Agent 3: hh_id:3, idv_id:3, no_answer:0
Trip ID: 1, Act ID: 10, Trip ID: 102, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 202, Day: 2
Agent 4: Gender: 男性
Agent 4: hh_id:1000, idv_id:1001, no_answer:1
Trip ID: 123, Act ID: 456, Trip ID: 789, Day: 1
Trip ID: 321, Act ID: 654, Trip ID: 987, Day: 2
Agent 5: Gender: 男性
Agent 5: hh_id:5, idv_id:5, no_answer:0
Trip ID: 1, Act ID: 10, Trip ID: 104, Day: 1
Trip ID: 2, Act ID: 20, Trip ID: 204, Day: 2

2024,江端さんの技術メモ

TC(Traffic Controller)で、帯域5M、パケットロス10%、遅延1000msを セットするコマンドを教えて下さい

# qdiscの設定をクリア
sudo tc qdisc del dev eth0 root

# トークンバケットフィルター (TBF) を使用して帯域幅を制限
sudo tc qdisc add dev eth0 root tbf rate 5mbit burst 10kb latency 50ms

# ネットエミュレーション(netem)を使用して遅延とパケットロスを追加 sudo tc qdisc add dev eth0 parent 1:1 handle 10: netem delay 1000ms loss 10%

TC(Traffic Controller)のコマンドの一つとして使われている"latency"と"delay"の違いについて教えて下さい。

latency

"latency" は、トークンバケットフィルター (TBF) などのキューイングディシプリン(qdisc)で使用されます。ここでの "latency" は、バッファ内のパケットが許可される最大遅延時間を示します。つまり、指定されたバッファサイズ(burst)が満たされている場合に、パケットがそのバッファ内にどれだけ長く滞留できるかを示します。

delay

"delay" は、ネットエミュレーション(netem)で使用され、パケットに対して指定された遅延を追加します。この遅延は、ネットワークの遅延やラグをシミュレートするために使用されます。

まとめ

  • latency: TBF などのキューイングディシプリンにおいて、バッファ内での最大滞留時間を指定します。パケットの滞留時間が指定された latency を超えると、そのパケットはドロップされる可能性があります。バッファ内の滞留時間であり、ネットワーク全体の遅延を反映するものではありません。
  • delay: netem などで使用され、パケットに対して指定された遅延を直接追加します。ネットワーク全体の遅延やラグをシミュレートするために使用されます。

以上

 

2024,江端さんの技術メモ

type agent_od_2018_harugakkai struct {
	id      int
	hh_id   int
	idv_id  int
	act_id  int
	trip_id int
	day     int

	testtest float64

	origin      string // 自宅
	destination string // 病院

	ox float64
	oy float64
	dx float64
	dy float64

	m_ox float64 // ポリゴン境界補正後のox
	m_oy float64 // ポリゴン境界補正後のoy
	m_dx float64 // ポリゴン境界補正後のdx
	m_dy float64 // ポリゴン境界補正後のdy

追加のm_ox、m_oy、m_dx、m_dyが反映されず(というか、vscodeがエラーといってくる)ので、頭を抱えていましたが、どうやら、同じディレクトリにある別プログラムの定義をひっぱってきているようです。

同様に、メソッドも、無関係なプログラムをひっぱってこられて、VSCode上では警告の嵐になります。

ですので、短かいテストプログラムと言えども、面倒くさいですが、別のディレクトリを切って、そこでテストしなければならないようです。(VScodeは文句言うけど、たぶん、コンパイル/ビルドは通ると思います)。

# 実にもったいない時間の消費をしてしまいました。

 

 

2024,江端さんの技術メモ

ポインタと値の操作: item がリスト内の要素をポインタ経由で取得している場合、item を変更しても元のリストの要素が変更されることがありますが、それ以外の場合はそのような自動的な反映は行われません。必要に応じて、&item を使用してポインタ経由でアクセスし、リスト内の要素を直接変更します。

 

スライスと範囲ループの挙動: Go言語では、for range ループで取得した item は元のスライスの要素のコピーです。これにより、item を直接変更しても元のリストの要素に反映されません。そのため、ポインタ経由でアクセスして変更する必要があります。

失敗例

var estimatedStartTime time.Time
			var estimatedMovingTime time.Duration
			var estimatedStayTime time.Duration

			for i, item := range list {

				if i == 0 {
					estimatedStartTime = estimated_Start_Time(data, item.destination) // 出発時刻(リアル時間)
					fmt.Println("i=0のitem.destination:", item.destination)
					fmt.Println("i=0のestimatedStartTime:", estimatedStartTime)
					item.dep_time = estimatedStartTime // 時刻 (記録用)
					fmt.Println("in i=0  item.dep_time:", item.dep_time)
				} else {
					item.dep_time = estimatedStartTime.Add(estimatedMovingTime + estimatedStayTime)
				}

				fmt.Println("===================>", item.dep_time)

				estimatedMovingTime = estimated_Moving_Time(item.ox, item.oy, item.dx, item.dy) // 移動時間(間隔)
				fmt.Println("estimatedMovingTime:", estimatedMovingTime)

				estimatedStayTime = estimated_Stay_Time(data, item.destination) // 滞在時間(間隔)
				fmt.Println("estimatedStayTime:", estimatedStayTime)

				// item.dep_time = estimatedStartTime // 時刻 (記録用)
				item.stay_time = estimatedStayTime // 滞在時間(間隔) (記録用)


					if i+1 < len(list) {
						fmt.Println("in 558", estimatedStartTime, item.dep_time)
						nextItem := &list[i+1]
						nextItem.dep_time = estimatedStartTime.Add(estimatedMovingTime + estimatedStayTime) // 次の出発時刻 = 現在の出発時刻 + 移動時間
						fmt.Println("After nextItem.dep_time:", nextItem.dep_time)
						fmt.Println("in 562", estimatedStartTime, item.dep_time)
					}


				//fmt.Println("in 565", estimatedStartTime, item.dep_time)

			}

			for i, item := range list {
				fmt.Println(i, "item.dep_time:", item.dep_time)
				fmt.Println(i, "item.stay_time:", item.stay_time)
				fmt.Println(i, "item:", item)
				//fmt.Println(i, "item.dep_time:", item.dep_time.Format("2006-01-02 15:04:05"))
			}

成功例

var estimatedStartTime time.Time
		var estimatedMovingTime time.Duration
		var estimatedStayTime time.Duration

		for i := range list {
			if i == 0 {
				estimatedStartTime = estimated_Start_Time(data, list[i].destination)
				fmt.Println("i=0のitem.destination:", list[i].destination)
				fmt.Println("i=0のestimatedStartTime:", estimatedStartTime)
				list[i].dep_time = estimatedStartTime
				fmt.Println("in i=0  item.dep_time:", list[i].dep_time)
			} else {
				// 前のアイテムの出発時刻に移動時間と滞在時間を加えて計算
				list[i].dep_time = list[i-1].dep_time.Add(estimatedMovingTime + estimatedStayTime)
			}

			// 移動時間と滞在時間を計算する
			estimatedMovingTime = estimated_Moving_Time(list[i].ox, list[i].oy, list[i].dx, list[i].dy)
			fmt.Println("estimatedMovingTime:", estimatedMovingTime)

			estimatedStayTime = estimated_Stay_Time(data, list[i].destination)
			fmt.Println("estimatedStayTime:", estimatedStayTime)

			// 滞在時間を記録用に設定する
			list[i].stay_time = estimatedStayTime

			fmt.Println("===================>", list[i].dep_time)
		}

		for i := range list {
			fmt.Println(i, "item.dep_time:", list[i].dep_time)
			fmt.Println(i, "item.stay_time:", list[i].stay_time)
			fmt.Println(i, "item:", list[i])
		}
	}

そんな仕様、知るかーーーー

2024,江端さんの技術メモ

gst-launch-1.0 rtspsrc location=rtsp://cam:Cam12345@192.168.0.10/Src/MediaInput/stream_1 latency=0 ! rtph264depay ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw,width=640,height=360 ! videorate ! video/x-raw,framerate=5/1 ! x264enc bitrate=1700 speed-preset=ultrafast tune=zerolatency key-int-max=1 ! mpegtsmux ! srtserversink uri = \"srt://192.168.101.10:38089\" latency=500
を実施するためだけのプログラムをGO言語で作成してください

package main

import (
	"fmt"
	"os"

	"github.com/go-gst/go-glib/glib"
	"github.com/go-gst/go-gst/gst"
)

func main() {
	fmt.Println("starting.....")

	// gstreamer初期化処理
	gst.Init(&os.Args)

	// メインループ生成
	mainLoop := glib.NewMainLoop(glib.MainContextDefault(), false)

	// パイプラインの要素を生成
	source, _ := gst.NewElement("rtspsrc")
	depay, _ := gst.NewElement("rtph264depay")
	decoder, _ := gst.NewElement("avdec_h264")
	convert, _ := gst.NewElement("videoconvert")
	scale, _ := gst.NewElement("videoscale")
	rate, _ := gst.NewElement("videorate")
	filter1, _ := gst.NewElement("capsfilter")
	filter2, _ := gst.NewElement("capsfilter")
	encorder, _ := gst.NewElement("x264enc")
	mux, _ := gst.NewElement("mpegtsmux")
	sink, _ := gst.NewElement("srtsink")

	// 各要素のプロパティを設定
	source.SetProperty("location", "rtsp://cam:Cam12345@192.168.0.10/Src/MediaInput/stream_1")
	source.SetProperty("latency", 0)
	filter1.SetProperty("caps", gst.NewCapsFromString("video/x-raw,width=640,height=360"))
	filter2.SetProperty("caps", gst.NewCapsFromString("video/x-raw,framerate=5/1"))
	encorder.SetProperty("bitrate", 1700)
	encorder.SetProperty("speed-preset", "ultrafast")
	encorder.SetProperty("tune", "zerolatency")
	encorder.SetProperty("key-int-max", 1)
	sink.SetProperty("uri", "srt://192.168.101.10:38089")
	sink.SetProperty("latency", 500)

	// パイプラインを生成して要素を追加
	pipeline, _ := gst.NewPipeline("video_player")
	pipeline.AddMany(source, depay, decoder, convert, scale, rate, filter1, filter2, encorder, mux, sink)
	gst.ElementLinkMany(depay, decoder, convert, scale, rate, filter1, filter2, encorder, mux, sink)

	// 動的パッドのリンク
	source.Connect("pad-added", func(element *gst.Element, pad *gst.Pad) {
		pad.Link(depay.GetStaticPad("sink"))
	})

	// パイプラインメッセージ読取
	pipeline.GetPipelineBus().AddWatch(func(msg *gst.Message) bool {
		switch msg.Type() {
		case gst.MessageEOS: // end-of-streamを受信したらパイプラインを停止
			pipeline.BlockSetState(gst.StateNull)
			mainLoop.Quit()
		case gst.MessageError: // エラーメッセージはFatalにする
			err := msg.ParseError()
			fmt.Println("ERROR:", err.Error())
			if debug := err.DebugString(); debug != "" {
				fmt.Println("DEBUG:", debug)
			}
			mainLoop.Quit()
		default:
			// 受信メッセージの表示
			fmt.Println(msg)
		}

		return true
	})

	// パイプライン開始
	pipeline.SetState(gst.StatePlaying)

	// メインループ開始
	mainLoop.Run()
}

2024,江端さんの技術メモ

C言語を使えて、GStreamerが使えて、GO言語を使えて、CGOが使える環境を作っています。

(1)Windowsの方のmsys2とGStreamerのパッケージを削除
Windowsの方のC言語、GStreamerはコンフリクトするので削除しますが、GO言語は、別のタスク(大学の方のシミュレータ)があるので、削除できませんでした。
という訳で、MSYS2の環境を、Windowsの方と切り離して運用するという覚悟ができました。

(2)C:の容量が小さいので、G:\の方にパッケージを分離していたのですが、これをやると、全く動かなくなるので、C:\msys2の配下に勝手に作成される、/c/msys64/home/tomoi/の直下でプログラムの作成をすることにしました。

これ、/usr/local/binの配下に/home/ebataを作るような気持ち悪さがあるのですが、今回は稼動が最優先です。

MSYS2のインストール

  1. MSYS2公式サイトからインストーラをダウンロードします。
  2. インストーラを実行し、画面の指示に従ってMSYS2をインストールします。

2. MSYS2の初期設定

  1. MSYS2ターミナルを起動: MSYS2 MSYSショートカットを使用してMSYS2ターミナルを起動します。
  2. パッケージデータベースと基本パッケージの更新: 以下のコマンドを順番に実行して、パッケージデータベースと基本パッケージを更新します。

    sh

    pacman -Syu

    初回のアップデートが完了したら、ターミナルを一度閉じて再起動します。

  3. 再度更新を実行: ターミナルを再起動後、再度以下のコマンドを実行します。

    sh

    pacman -Syu

3. MinGW-w64のインストール

  1. MinGW-w64ツールチェインのインストール: MSYS2 MinGW 64-bitショートカットを使用してMSYS2ターミナルを起動し、以下のコマンドを実行します。

    sh

    pacman -S mingw-w64-x86_64-toolchain

    これにより、GCC、G++、およびその他の開発ツールがインストールされます。

4. 環境変数の設定

MinGW-w64のバイナリにアクセスするために、環境変数PATHを設定します。

  1. ~/.bashrcファイルの編集: MSYS2ターミナルで~/.bashrcファイルを開きます。

    sh

    nano ~/.bashrc
  2. PATHの設定: 以下の行を追加して保存します。

    sh

    export PATH="/mingw64/bin:$PATH"
  3. 設定の反映: 設定を反映させるために、ターミナルを再起動するか、以下のコマンドを実行します。

    sh

    source ~/.bashrc

5. GStreamerのインストール

  1. GStreamerのパッケージのインストール: MSYS2 MinGW 64-bitショートカットを使用してMSYS2ターミナルを起動し、以下のコマンドを実行します。

    sh

    pacman -S mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad mingw-w64-x86_64-gst-plugins-ugly

    pacman -S mingw-w64-x86_64-gstreamer mingw-w64-x86_64-gst-plugins-base mingw-w64-x86_64-gst-plugins-good mingw-w64-x86_64-gst-plugins-bad mingw-w64-x86_64-gst-libav

6. 確認とテスト

  1. GCCの動作確認: GCCが正しくインストールされているか確認します。

    sh

    gcc --version
  2. 簡単なCプログラムの作成とコンパイル: 簡単なCプログラムを作成してコンパイルします。

    sh

    nano test.c

    以下の内容を入力します。

    c

    #include <stdio.h>

    int main() {
    printf("Hello, World!\n");
    return 0;
    }

    ファイルを保存してNanoエディタを閉じた後、コンパイルします。

    sh

    gcc test.c -o test.exe

    コンパイルが成功したら、実行します。

    sh

    ./test.exe
  3. GStreamerの動作確認: GStreamerが正しくインストールされているか確認します。

    sh

    gst-launch-1.0 --version

    簡単なGStreamerパイプラインを実行して動作確認します。

    sh

    gst-launch-1.0 videotestsrc ! autovideosink

とうぜん 、ここまですんなりできた訳ではありませんが、取り敢えず私が思い出せる程度の情報を載せてあります。

-----

https://github.com/go-gst/go-gst

から、/home/tomoi/go/srcで、 git clone https://github.com/go-gst/go-gst をして、/home/tomoi/go/src/go-gst を作って、

に張ってあったプログラムを、main.goでセーブしたのち、

go run main.go videotestsrc ! glimagesink

で、

と、

が、表われることを確認。

なにがどうなっているのかは、サッパリ分からないが、とりあえず、GoからGstreamerが動いたことは確認できた。

以上

なんか、サラっと書くと自分でも腹たつなぁ。10時間以上はかかったんだけどなぁ

2024,江端さんの技術メモ

Windows10に、GStreamerの1.24をインストール(フルインストールを選ぶこと)をしたのですが、定番の、

$ gst-launch-1.0.exe videotestsrc ! autovideosink
Use Windows high-resolution clock, precision: 1 ms
Setting pipeline to PAUSED ...
Pipeline is PREROLLING ...
Got context from element 'autovideosink0': gst.d3d11.device.handle=context, device=(GstD3D11Device)"
\(GstD3D11Device\)\ d3d11device2", adapter=(uint)0, adapter-luid=(gint64)41376, device-id=(uint)1042
, vendor-id=(uint)32902, hardware=(boolean)true, description=(string)"Intel\(R\)\ HD\ Graphics\ 4600
";
ERROR: from element /GstPipeline:pipeline0/GstAutoVideoSink:autovideosink0/GstD3D11VideoSink:autovid
eosink0-actual-sink-d3d11video: Cannot create converter
Additional debug info:
Failed to prepare d3d11window
ERROR: pipeline doesn't want to preroll.
Setting pipeline to NULL ...
ERROR: from element /GstPipeline:pipeline0/GstVideoTestSrc:videotestsrc0: Internal data stream error
.
Additional debug info:
../libs/gst/base/gstbasesrc.c(3177): gst_base_src_loop (): /GstPipeline:pipeline0/GstVideoTestSrc:vi
deotestsrc0:
streaming stopped, reason error (-5)
ERROR: pipeline doesn't want to preroll.
Freeing pipeline ...

となります。別のWindows BOXでも、先日も経験して、青ざめていました。

結果として、

$ gst-launch-1.0 videotestsrc ! glimagesink

とすれば動きます。

autovideosinkが動かん、っていうのは、結局分かっていませんが、これは困りものです。

以下自分用メモ

-UDP通信の実験

-受信側

-gst-launch-1.0 videotestsrc ! x264enc ! rtph264pay ! udpsink host=192.168.101.10 port=38089
(ローカルの場合、host=127.0.0.1とする.  host=localhost は動かない)

-送信側

-gst-launch-1.0 udpsrc port=38089 caps="application/x-rtp,media=video,encoding-name=H264,payload=96" ! rtph264depay ! h264parse ! avdec_h264 ! videoconvert ! glimagesink

-

-SRT通信の実験

-受信側

-gst-launch-1.0 srtsrc uri="srt://0.0.0.0:38089?mode=listener" keep-listening=true ! tsdemux ! h264parse ! avdec_h264 ! videoconvert ! glimagesink sync=false

-送信側

-gst-launch-1.0 rtspsrc location=rtsp://cam:Cam12345@192.168.0.10/Src/MediaInput/stream_1 latency=0 ! rtph264depay ! avdec_h264 ! videoconvert ! videoscale ! video/x-raw,width=640,height=360 ! videorate ! video/x-raw,framerate=5/1 ! x264enc speed-preset=ultrafast tune=zerolatency key-int-max=1 ! mpegtsmux ! srtserversink uri = \"srt://192.168.101.10:38089\" latency=500