未分類

『Windowsの最初の認証画面が出てくるまでの時間が非常に長いマシンがあります。別のマシンは数秒から十数秒でログイン画面がでてきます。この理由と対策方法を教えて下さい』

'There are some machines where it takes a very long time for the initial Windows authentication screen to appear. The login screen appears on other machines in a few seconds to a dozen seconds. Please tell me the reason for this and how to fix it.' 

とChatGPTに質問したところ、色々対策を教えてくれました。

I asked ChatGPT this question, and it told me various measures.

Windowsの最初の認証画面(PINを入力する画面)が出てくるまでの時間が非常に長いマシンがあります(別のマシンは数秒から十数秒でログイン画面がでてきます)。この理由と対策方法を教えて下さい。

しかし、システム構成の変更にに関わる部分をいじると、高い確率で、逆に状況を悪くしたり、下手するとPCが動かなくなったりします。

However, suppose I fiddle with the parts related to changing the system configuration. In that case, there is a high probability that I will worsen the situation, or if I am not careful, my PC may stop working.

私がシステムを壊すのは、ほぼ100%、『ちょっとした改良』を施そうとした時です。

I break the system almost 100% when I try to make 'minor improvements.'

『ログイン画面の設定を変えたら、PCにアクセスできなくなった』

'I changed the login screen settings, and now I can't access my PC,'

などの他に、

and the others are

  • 『ネットワーク設定を少し最適化しようとしたら、全く通信できなくなった』
  • 『ブートオプションを少し調整したら、OSが起動しなくなった』
  • 『電源管理設定を変更したら、スリープから復帰しなくなった』
  • 『ストレージのアクセス速度を上げようとしたら、データが飛んだ』
  • 『メモリの動作クロックを少し上げたら、起動しなくなった』
  • 『BIOSの設定を最適化しようとしたら、画面が真っ黒になった』
  • 『Windowsのレジストリを少し変更したら、ログインすらできなくなった』
  • 『ドライバを最新版に更新したら、デバイスが一切認識されなくなった』
  • 『仮想メモリ設定を微調整したら、ブルースクリーンが頻発するようになった』
  • 『セキュリティ設定を強化したら、管理者権限での操作が一切できなくなった』
  • 'I tried to optimize my network settings a little, but I couldn't connect at all.
  • 'I adjusted the boot options slightly, but the OS wouldn't start.
  • 'I changed the power management settings, but it wouldn't wake up from sleep.
  • 'I tried to increase the storage access speed, but my data disappeared.
  • 'I slightly increased the memory operating clock, but it wouldn't start up.
  • 'I tried to optimize the BIOS settings, but the screen went completely black.
  • 'After making a few changes to the Windows registry, I couldn't even log in anymore.'
  • 'After updating the drivers to the latest version, my device stopped being recognized.'
  • 'After minor adjustments to the virtual memory settings, I started getting frequent blue screens.s.'
  • 'After strengthening the security settings, I couldn't do anything with administrator privileges.e.'

などがあります。

19歳の時からPCを使い続けている私ですら、このザマです。

Even I, who have been using a PC since I was 19, am like this.

『PCの設定変更が、怖くて仕方がない』という思いは、PCを使用してきた時間とは関係ありません。

The thought that 'changing the PC settings is too scary' has nothing to do with how long you have used a PC.

そしてPCには、そのPCの所有者の重要な業務情報やシステム(プログラム)が満載です。

A PC contains essential business information and systems (programs) for its owner.

私たちは、PCの中身を人質に取られながら、毎日をビクビクしながら生きているようなものです。

We live in fear daily, with our PCs holding our data hostage.

-----

で、今、あの遅いログイン画面のPCに手を加えるかどうか悩んでいます。

So now I'm wondering whether or not to modify the PC with the slow login screen.

直せるかもしれませんが、逆にもっと悪くなるかもしれません。最悪、OSが立ち上がらなくなるかもしれません。

It might be possible to fix it, but it might also worsen things. In the worst-case scenario, it might prevent the OS from starting up.

大抵の場合、私は、この結論に達します。

In most cases, I reach this conclusion.

「とりあえず、このまま使おう」

“Let's just keep using it as it is.”

と。

そして私は、いつものように、電源ボタンを押して、PCの前で祈りながら、ログイン画面が現れるまでの「永遠にも感じる時間」を待っています。

As usual, I press the power button and wait until the login screen appears, praying in front of the PC.

-----

ところが、以前、この「永遠にも感じる時間」を待ち続けた結果、数ヶ月後に、HDDがクラッシュして、業者にHDDの復旧依頼をしなければならなくなったことがあります。

However, once before, after waiting for this “time that feels like an eternity,” the HDD crashed a few months later, and I had to ask a company to recover it.

総額11万円でした。

The total amount was 110,000 yen.

PC本体(中古)が3台を購入できるお金を、自腹を切って払いました。救済できたのはHDDの中身だけで、PC本体を元に戻すことはできませんでした。

I paid out of my pocket to buy three used PCs. I was only able to rescue the contents of the HDD, and I was unable to restore the PCs to their original state.

「ただ祈っていればいい」というものでもないのです。

It's not as simple as “just praying”.

-----

ちなみに、なるべく物事を変えず、現状維持を原則とする考え方を「保守」と呼びます。

Incidentally, the idea of maintaining the status quo by not changing things as much as possible is called “conservative.”

これに対し、リスクを承知で新しい試みや考え方を積極的に取り入れ、変化を推進する考え方を「リベラル(革新)」(場合によっては「ラディカル」)と呼びます。

In contrast, the idea of promoting change by actively embracing new ideas and approaches, even if they involve risk, is called “liberal” (or, in some cases, “radical”).

人間は年齢を重ねるにつれ、「リベラル(革新)」から「保守」へと立場を変えていきます。

People change their stance from “innovation” to “conservation” as they age.

その理由は、上記のPC設定の事例だけで、十分にご理解頂けると思います。

The above example of PC settings is enough to understand the reason.

私、現状を1mmも変えないまま、安全で平和な死を迎えたいだけの、バリバリの保守です(と自負しています)が、ちょっと厳しいなぁ、と思っています、

未分類

Windowsの最初の認証画面(PINを入力する画面)が出てくるまでの時間が非常に長いマシンがあります(別のマシンは数秒から十数秒でログイン画面がでてきます)。この理由と対策方法を教えて下さい。

どちらの設定もネットワーク接続を待機する動作をする

もし「ネットワークが利用可能になるまでログオンを待機する」が見つからない場合、「コンピュータの起動およびログオンで常にネットワークを待つ」を無効化すれば同じ効果を得られます。

設定の変更方法

  1. Win + Rgpedit.msc を入力し、ローカルグループポリシーエディターを開く。
  2. コンピューターの構成管理用テンプレートシステムログオン を開く。
  3. **「コンピュータの起動およびログオンで常にネットワークを待つ」**をダブルクリック。
  4. 「無効」 に設定 → 適用OK
  5. 再起動。

今回は、この対応で上手くいく場合がでてきました(上手くいかないこともある)

ログイン画面が3分を超えても出てこないと、本当に心臓に悪いので、こういう下らない設定をデフォルトにするのは止めて欲しいんだが。

未分類

データベースを仕込んだDockerの移行は、ことごとく失敗したので、今日は終日、手作業でDBのエクスポートとインポートをやっていました。

1台はDockerをやめて、PostgreSQLを立てて、そこに片っぱしからデータベースを放り込んでおきました (ポート15432がコンフリクトして、解決に2~3時間かかりました)。

データベースが死んだら、私(の研究)も死ぬ。

とりあえず、これでデータベースの3重バックアップの形になったかな、と思います。

以下、今日の作業メモ

pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_pt2018 > tomioka_pt2018.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_pt2018_org > tomioka_pt2018_org.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 pj20005 > pj20005.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 pt2008 > pt2008.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 pt2018 > pt2018.sql

create database tomioka_pt2018;
create database tomioka_pt2018_org;
create database pj20005;
create database pt2008;
create database pt2018;

psql -U postgres -h 192.168.0.8 -p 15432 tomioka_pt2018 < tomioka_pt2018.sql
psql -U postgres -h 192.168.0.8 -p 15432 tomioka_pt2018_org < tomioka_pt2018_org.sql
psql -U postgres -h 192.168.0.8 -p 15432 pj20005 < pj20005.sql
psql -U postgres -h 192.168.0.8 -p 15432 pt2008 < pt2008.sql
psql -U postgres -h 192.168.0.8 -p 15432 pt2018 < pt2018.sql

psql -U postgres -h 192.168.0.8 -p 15432 tomioka_db_d.sql < tomioka_db_d.sql
psql -U postgres -h 192.168.0.8 -p 15432 tomioka_db_d_org < tomioka_db_d_org.sql
psql -U postgres -h 192.168.0.8 -p 15432 tomioka_db_e < tomioka_db_e.sql
psql -U postgres -h 192.168.0.8 -p 15432 tomioka_db_e_org < tomioka_db_e_org.sql
psql -U postgres -h 192.168.0.8 -p 15432 tomioka_db_f < tomioka_db_f.sql
psql -U postgres -h 192.168.0.8 -p 15432 tomioka_db_f_org < tomioka_db_f_org.sql

create extension postgis;
create extension pgrouting;

========

pg_dump -U postgres -h 192.168.0.23 -p 15432 agent_db > agent_db.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 moove > moove.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db > tomioka_db.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db2 > tomioka_db2.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db4 > tomioka_db4.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db_a > tomioka_db_a.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db_b > tomioka_db_b.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db_c > tomioka_db_c.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db_c_cost > tomioka_db_c_cost.sql
pg_dump -U postgres -h 192.168.0.23 -p 15432 tomioka_db_c_trial > tomioka_db_c_trial.sql

create database agent_db;
\c agent_db
create extension postgis;
create extension pgrouting;

create database moove;
\c moove
create extension postgis;
create extension pgrouting;

create database tomioka_db;
\c tomioka_db;
create extension postgis;
create extension pgrouting;

create database tomioka_db2;
\c tomioka_db2;
create extension postgis;
create extension pgrouting;

===

create database tomioka_db4;
\c tomioka_db4;
create extension postgis;
create extension pgrouting;

===

create database tomioka_db_a;
\c tomioka_db_a;
create extension postgis;
create extension pgrouting;

create database tomioka_db_b;
\c tomioka_db_b;
create extension postgis;
create extension pgrouting;

create database tomioka_db_c;
\c tomioka_db_c;
create extension postgis;
create extension pgrouting;

create database tomioka_db_c_cost;
\c tomioka_db_c_cost;
create extension postgis;
create extension pgrouting;

create database tomioka_db_c_trial;
\c tomioka_db_c_trial;
create extension postgis;
create extension pgrouting;

psql -U postgres -h 192.168.0.8 -p 15432 -d agent_db -f agent_db.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d moove -f moove.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db -f tomioka_db.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db2 -f tomioka_db2.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db4 -f tomioka_db4.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db_a -f tomioka_db_a.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db_b -f tomioka_db_b.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db_c -f tomioka_db_c.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db_c_cost -f tomioka_db_c_cost.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d tomioka_db_c_trial -f tomioka_db_c_trial.sql

 

=====

pg_dump -U postgres -h 192.168.0.3 -p 15432 hakata_db > hakata_db.sql
pg_dump -U postgres -h 192.168.0.3 -p 15432 hakata_ex_db > hakata_ex_db.sql

create database hakata_db;
\c hakata_db;
create extension postgis;
create extension pgrouting;

create database hakata_ex_db;
\c hakata_ex_db;
create extension postgis;
create extension pgrouting;

psql -U postgres -h 192.168.0.8 -p 15432 -d hakata_db -f hakata_db.sql
psql -U postgres -h 192.168.0.8 -p 15432 -d hakata_ex_db -f hakata_ex_db.sql

 

未分類

+88366870110 という不審な番号で電話がかかってきました。当然に電話には出ないで、着信拒否にしましたが、この番号をWebで調べても出てきませんでした。この電話の発信元を推定して下さい

具体的には、+883はITU(国際電気通信連合)などによって国や地域に限定されない国際通信サービス向けに割り当てられている番号帯のひとつです。

そのため、+88366870110という番号は、以下のような可能性が考えられます:

  • 非地理的サービス番号:
    +883の番号は、通常の国内通話や国際通話の枠組みに当てはまらず、特定の国に紐づかない国際的な通信サービス(例えば、VoIP、衛星電話、その他のインターネット電話サービス)で利用されることが多いです。

  • 悪質な用途の可能性:
    こうした非地理的な番号は、実際に正当なサービスで使われる場合もありますが、詐欺や迷惑電話、SMS詐欺など悪質な行為に利用されるケースも報告されています。特に、不審な番号からの着信は、番号を隠して発信元を特定しにくくしているため、注意が必要です。

  • 正確な発信元の特定の難しさ:
    電話番号の国際的な割り当てから、大まかな番号帯の用途は推測できますが、具体的にどのサービスプロバイダや発信者が利用しているかを特定するのは、一般利用者にとっては困難です。詳しい情報が必要な場合は、通信事業者や関係当局に問い合わせるのが良いでしょう。

まとめると、+88366870110は国際的な非地理的番号帯に属しており、正規の通信サービスで使用される可能性はあるものの、詐欺や迷惑電話などのリスクが伴うことが多いため、発信元の詳細な特定は難しく、不審な場合は応答を避けるのが安全です。

2025,江端さんの忘備録

私は、謙虚であることは重要だと思っています。

I think it's important to be humble.

ただ、それは謙虚であることが、人間として徳のある行為だから、とは1mmも思っていません。

However, I don't think for one second that humility is a virtuous act for a human being.

謙虚でないと「恥をかく」ことが多いからです。

This is because you will often “disgrace yourself” if you are not humble.

特に、私の勤務している会社のように、技術者が万単位で集っている組織においては、特にそうです。

This is especially true in organizations like the one I work for, which employs tens of thousands of engineers.

-----

私のような「老獪(ろうかい)」の域にいる人間から見ると、自分の技術を自慢している風に語る若者は『かわいい』です。

From the perspective of someone like me who is “old cunning,” young people who talk about their skills as if they were proud of them are “cute.”

で、そう思っているシニアが、その辺にゴロゴロいます(苦笑しながら)。

And many seniors think this way (while smiling wryly).

が、彼らは、それを指摘して若者に恥をかかすようなことはしません。

However, they do neither point this out nor humiliate the young people.

―― それは、かつての自分の姿を思い起こさせるから

"That's because it reminds me of who I used to be."

か、どうかは分かりませんが(私はそうです)。

I don't know if it's true or not (it is true for me).

すでに、老獪(ろうかい)なシニアである私は、そういう若者を"よいしょ"して、上手く方向を誘導して、仕事の成果に落しこむ方に画策します(もちろん、私の仕事の為にです)。

As a crafty senior, I plan to “lift” such young people up, guide them in the right direction, and then incorporate their work into my own (of course, for my work).

-----

私が観察した範囲では、問題は、若者よりもシニアの方に多いように思えます。

I have observed that the problem seems more common among seniors than young people.

たとえば、新人や2~3年目の研究員に、まともな特許明細書が書けるわけがありません。

For example, a new researcher or a researcher in their second or third year cannot write a decent patent specification.

「新規性」と「進歩性」の違いなんて、私は特許法のゼミで、特許庁の実施要項を暗記できるくらい叩き込まれて、ようやく理解できるようになったくらいです。

I only really understood the difference between “novelty” and “inventive step” after being drilled in a patent law seminar to the extent that I could recite the JPO's implementation guidelines by heart.

そんでもって、これまで100本以上の特許明細書を出願してきた(但し、分割出願を含む)私ですら、毎回、酷く苦労している状態です。

Even I, who have applied for over 100 patent specifications (including divisional applications), have always struggled.

-----

「それではダメだ」というだけでは、ダメです。

Anyone who says “that won't do” is a no-good person.

そんなの、誰でも言えますし、私が見るに「アイデアが出せないシニアほど、このセリフを使いがち」です。

Anyone can say that, and in my experience, seniors who cannot generate ideas tend to use that line.

『それを、こう変えてみようか』と言い、『うん、随分よくなっているよ』と言い、『まとめの方は、私が直してしまっていいかな?』と言い続けるのが、シニアの仕事 ―― と私は思っています。

I think a senior staff member's job is to keep saying things like, “How about changing that to this?”, “Yes, it's much better now,” and “Can I fix the summary for you?”

しかし、『若者の指導』という体裁で、『自分の無能』を喧伝しているシニアを、度々みかけます。

However, I often see seniors promoting their incompetence under the guise of “guiding the young.”

私は、『自分がラクをするため』に誰かにアドバイスをすることはありますが、その人間を『育てる』ためにアドバイスした記憶は、一度もありません。

I may advise someone 'for my benefit,' but I don't remember advising someone 'for their benefit.'

本人から頼まれれば、アドバイスすることがありますが、頼まれないのにアドバイスしにいくほど、私は、暇でも親切でもありません。

If the person asks me for advice, I may give it, but I am neither idle nor kind enough to go and give advice even if I am not asked to.

------

先日、長女と電話で話をしていたとき、

The other day, when I was talking on the phone with my eldest daughter, she said,

『次女(今年の4月から社会人)に、社会の厳しさを教えなくてもいいの?』

'Don't you need to teach your second daughter (who will start working this April) about the harshness of society?

と言われました。

『"社会の厳しさ"なるものは、"社会"が教えてくれるのに、なんでわざわざ私が、嫌われ役をかって出てまで、教えてやらなければならないんだ?』

'Why should I go out of my way to play the role of the hated one to teach her about the “harshness of society” when society itself is supposed to teach her about it?

と返しました。

I answered the above to the eldest daughter.

だいたい、そういう、下らない苦言を子どもたちにするバカな大人が多いから、子どもたちが、必要以上の社会に出るのを恐れるようになるのですよ。

Many adults make such stupid complaints to children, so children become afraid of going out into society more than they need to.

大人は、むしろ、大人のメリットの方を、子どもに発信しているべきでしょう。

Adults should be communicating the benefits of being an adult to children.

ともあれ、私は、"社会の厳しさ"なるものを、子どもに偉そうに語っている大人って『真正のバカ』と思っています。

Anyway, I think that adults who talk to children about the “harshness of society” in a big-headed way are “genuine idiots.”

―― 大人は自由だ。大人は楽しい。大人はパラダイスだ

2025,江端さんの忘備録

Web画面に『ここにボタンを一つ追加して』というセリフを言う平気で言う人間 ―― これだけで、その人間のプログラムやシステムに関する見識の無さが露呈しています。

A person who says, “Add a button here,” on a web page without batting an eyelid, reveals the person's lack of insight into programming and systems.

こういう人を責める訳ではないのですが、

I don't blame people like this, but I think many people are unaware that

この追加されるボタン一つが、

This additional button is

■ユーザインターフェース(UI)の追加だけでなく、

■ in addition to adding user interface (UI) elements,

■プログラムコードに大変更を与えて、

■ making major changes to program code,

■それがデータベースの項目に影響し、

■ and having that affect database items,

■最悪の場合、それがトランザクションや排他処理に及ぶまでの、

■ in the worst case, it can extend to transactions and exclusive processing,

膨大な設計変更に及ぶ ―― ということを知らない人は多いと思います。

leading to a huge amount of design changes.

-----

多分、『ここにボタンを一つ追加して』と安易に言う人は、立食い蕎麦で『あ、それと、天麩羅と生卵を追加して』というノリなのでしょう。

Perhaps the people who say “Add a button here” so quickly are the same people who say “Oh, and add tempura and raw egg” when eating at a standing-only soba noodle bar.

しかし、ITシステムにおける、『ここにボタンを一つ追加して』とは、その蕎麦屋の『店舗全体を設計しなおす規模になる』というレベルの大作業になることは、そんなに珍しいことではありません。

However, in IT systems, it is not that unusual for a request like “add a button here” to be a primary task on the scale of “re-designing the entire store” for that soba shop

だから、『開発開始後の仕様変更』は、原則、許されることではないのです。

Therefore, 'specification changes after development has started' are not permitted in principle.

-----

しかし、そういうことを平気でやる発注者って、結構多いです ―― 金も出さずに、納期も変えずに。

However, quite a few clients do this without a second thought - without paying or changing the delivery date.

でも、システム開発者の担当者が、

However, it is essential to remember that the system developer in charge is probably thinking

―― このシステム音痴の無能野郎!

"“You incompetent system-illiterate bastard!”"

と、心の中で罵っていることは、覚えておいた方が良いでしょう。

in their heart.

受注側は、お金を貰う弱みがあり、最終的に、それに応じることにはなります(または、裁判になります)が  ―― しかし、そこには『開発者が、発注者に対して"憎悪"が発生している』ということは覚えておいた方がいいです。

The weakness is that the recipient will be paid, and ultimately there is no choice but to obey (or take it to court), but what must not be forgotten is that the recipient is “hated” by the developer.

義務教育のプログラミング教育に意義があるとすれば、プログラミングできるようになることではなく、この「システム音痴の無能野郎」が、一人でも減ること、と、私は思っています。

There is no significance to programming education as part of compulsory education; it is not so much that people should be able to program, but I think the number of “system-illiterate incompetents” should be reduced.

-----

私も昨夜、プログラムの改修作業をしていました。

I was also working on program modifications last night.

私の見積り時間は"10分間"でしたが、改修完了に掛った時間は6時間でした。予定通りの稼動を確認したのは、今朝です。

My estimate was for 10 minutes, but it took 6 hours to complete the repairs. I confirmed that it was working as planned this morning.

自作プログラムでも、このザマです。

Even with my program, it's like this.

ITシステムというのを、ファストフードのトッピングのように考えているとしたら、それは無知を超えて、社会悪と断じて良いものです。

If you think of IT systems as being like toppings on fast food, then that's not just ignorance; it's a social evil.

―― 考えすぎじゃね?

2025,江端さんの技術メモ

Go言語を使ったマルチエージェントシミュレーションが、他の言語よりもメリットがある点とデメリットがある点について論じて下さい

一つ一つが、大変納得した内容だったので、私のメモ用に記録しておきます。

 

Go言語を用いたマルチエージェントシミュレーション(MAS)の利点と欠点について、他の言語(C++, Python, Juliaなど)と比較しながら論じる。

1. Go言語を使うメリット

(1) 並行処理の容易さ

Goはgoroutineを用いた並行処理が特徴であり、エージェントごとにgoroutineを割り当てることで効率的な並列処理が可能である。他の言語ではスレッド管理(C++のstd::threadやPythonのthreading)が必要になるが、Goではシンプルな記述で実装できる。

例:

func agent(id int) {
    for {
        fmt.Printf("Agent %d is moving...\n", id)
        time.Sleep(time.Millisecond * 500)
    }
}

func main() {
    for i := 0; i < 10; i++ {
        go agent(i) // 各エージェントをgoroutineで実行
    }
    select {} // メインスレッドを終了させない
}

このようにGoの並行処理はシンプルかつ軽量であり、大量のエージェントを扱うシミュレーションに適している。


(2) シンプルなコードとメンテナンス性

Goはシンプルな文法強力な標準ライブラリを持つため、コードの可読性が高く、開発者間での共有が容易である。
C++ではテンプレートやマルチスレッド処理が複雑になりやすく、PythonではGIL(Global Interpreter Lock)の影響で並列処理が制限される。一方、GoはGILの問題がなく、コードの記述量も比較的少ないため、長期的な開発に向いている。


(3) 高速なコンパイルと実行

Goはコンパイルが非常に速く、バイナリを直接実行できるため、デバッグやテストのサイクルを短縮できる。C++のように長時間のコンパイルが不要で、Pythonのようなインタープリタ型の遅延もない。


(4) クロスプラットフォームの容易さ

Goは静的バイナリを生成できるため、Windows, Linux, macOS などの異なる環境での実行が容易である。C++ではコンパイル時にライブラリ依存の問題が生じやすく、Pythonでは環境設定(仮想環境やパッケージ管理)が面倒になりがちだが、Goでは1つのバイナリで解決できる。


(5) クラウドやWebとの親和性

GoはもともとGoogleが開発した言語であり、クラウド環境(GCP, AWS)やWebとの親和性が高い。
シミュレーション結果をWeb API経由でリアルタイム表示したり、分散処理の一部をクラウド上で実行する際に、Goの標準ライブラリだけでHTTPサーバを簡単に実装できるのは大きなメリット。


2. Go言語を使うデメリット

(1) 高速計算ライブラリが少ない

PythonやC++には、最適化された数値計算ライブラリ(NumPy, SciPy, Eigen, OpenMPなど)が豊富にあるのに対し、Goは数値計算や線形代数のライブラリが少ない
そのため、大量の行列演算や物理シミュレーションを行う場合、Go単体では計算効率が劣る可能性がある。

対策:

  • Goで数値計算を行う場合は、gonum ライブラリを使用する。
  • 必要に応じて、GoからC/C++(BLAS, LAPACK)を呼び出す。

(2) GPUサポートが弱い

C++はCUDAやOpenCLを利用してGPUによる並列計算が可能であり、PythonもTensorFlowやCuPyを通じてGPUを活用できる。しかし、Goは公式にはGPUを直接サポートしていないため、CUDAを使う場合はCGO経由でC++のライブラリを呼び出す必要がある

対策:

  • gorgonia(Go向けのニューラルネットワークライブラリ)を利用すると、一部のGPU計算が可能。
  • CUDA/OpenCLを直接利用したい場合は、GoからC++のラッパーを呼び出す。

(3) 研究事例が少ない

PythonやC++は、数多くの学術論文や研究事例があり、豊富なサンプルコードが存在する。一方で、Goは学術分野ではあまり使われておらず、論文や研究事例が少ない
特に、MAS関連のライブラリやフレームワークが未成熟なため、開発者が独自に実装する必要がある。

対策:

  • MASの一般的なアルゴリズム(Boidモデル、Cellular Automatonなど)をGoで実装する。
  • 他言語のライブラリと組み合わせて使用する。

(4) ガベージコレクションの影響

Goのガベージコレクション(GC)は自動的にメモリを管理するが、大規模なシミュレーションではGCの影響で予期しないタイミングで一時的な遅延が発生する可能性がある。
C++のように明示的なメモリ管理ができないため、リアルタイム性を求めるシミュレーションでは不利になることがある。

対策:

  • メモリ割り当てを最小限に抑える設計を行う(オブジェクトの再利用)。
  • sync.Pool を活用してメモリ管理のオーバーヘッドを減らす。

3. 結論

Go言語を使ったMASは、並行処理の容易さ・コードのシンプルさ・クラウドとの親和性に優れており、大規模エージェントシミュレーションの実装がしやすい
しかし、GPUサポートの弱さ・計算ライブラリの不足・研究事例の少なさといった課題もあり、特に高度な数値計算が必要なシミュレーションではPythonやC++の方が適している

Go言語が適しているケース:

  • 大規模なエージェント数をgoroutineで管理
  • ネットワーク・クラウド連携が必要
  • シンプルな設計を重視

PythonやC++の方が適しているケース:

  • 高度な数値計算(線形代数、最適化)
  • GPUを活用したシミュレーション
  • 既存のライブラリや研究事例を活用

最適なアプローチ:
Go単体でMASを実装するよりも、計算部分をPython/C++で処理し、Goは並列制御やネットワーク管理に使うというハイブリッドアプローチが有効となる。


昨日、

PrumeMobileを使った人流の見える化

について記載したんだけど、これ簡単に言うと『goroutineを止めて、サブルーチンにした』というものです。

300万のエージェントが、メインルーチンの情報変更に上手く連動しなくなると、"goroutineの自己破壊命令が届かない"という問題がボロボロでてきたからです。

まあ、それでも、『数十万人分、361万行の移動データを、全部表示して正常終了した』というGo言語の凄さには、相変らず感心しているんですけどね(昨夜、PC回しながら寝たのですが、今朝、全部のオブジェクトが消えて、正常終了していました)。

2025,江端さんの技術メモ

起動方法
$go run server22-1.go

http://localhost:8080

$go run pm_proxy_single_socket.go

 

// server22-1.go

/*
このプログラムは、WebSocket を利用したリアルタイム位置情報サーバーである。
- クライアントから送信された位置情報を受信し、処理後にレスポンスを返す。
- 位置情報はチャネルを介して他のクライアントと共有される。
- `sync.Mutex` を使用してスレッド安全な処理を実現。
- WebSocket の接続エラー時には適切にログを出力し、安定した通信を確保。
- HTML を提供し、ブラウザ上で WebSocket 通信を確認可能。
*/
package main

import (
	"flag"
	"html/template"
	"log"
	"net/http"
	"sync"

	"github.com/gorilla/websocket"
)

// GetLoc GetLoc
type GetLoc struct {
	ID  int     `json:"id"`
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
	//Address string  `json:"address"`
}

// var addr = flag.String("addr", "localhost:8080", "http service address")
var addr = flag.String("addr", "0.0.0.0:8080", "http service address") // テスト

var upgrader = websocket.Upgrader{} // use default options

var chan2_1 = make(chan GetLoc)

var maxid = 0

var mutex sync.Mutex

func echo2(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil) // cはサーバのコネクション
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	//mutex := new(sync.Mutex)

	for {
		//mt, message, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)
		//_, _, err := c.ReadMessage() // クライアントからのメッセージの受信(mtはクライアント識別子)

		//mutex.Lock()  // ここに置くとデッドロックしてしまう

		gl := new(GetLoc)

		err := c.ReadJSON(&gl) // クライアントからのメッセージの受信

		mutex.Lock()

		// 原因不明の対処処理
		if gl.ID == 0 && gl.Lat < 0.01 && gl.Lng < 0.01 {
			mutex.Unlock()
			break
		} else if gl.ID < -1 { // 受理できないメッセージとして返信する
			//条件分岐 (変なIDが付与されているメッセージは潰す)
			//if (gl.ID > maxid) || (gl.ID < -1) { // 受理できないメッセージとして返信する

			gl.ID = -1
			gl.Lat = -999
			gl.Lng = -999
			err2 := c.WriteJSON(gl)
			if err2 != nil {
				log.Println("write1:", err2)
				mutex.Unlock()
				break
			}
		} else { // それ以外は転送する
			/*
				log.Printf("echo2 after c.WriteJSON(gl) ID:%d", gl.ID)
				log.Printf("echo2 after c.WriteJSON(gl) Lat:%f", gl.Lat)
				log.Printf("echo2 after c.WriteJSON(gl) Lng:%f", gl.Lng)
			*/
			if err != nil {
				log.Println("read:", err)
				mutex.Unlock()
				break
			}
			//fmt.Printf("echo2 before chan2_1 <- *gl\n")
			chan2_1 <- *gl
			//fmt.Printf("echo2 after chan2_1 <- *gl\n")

			//で、ここで受けとる
			//gl2 := new(GetLoc)
			//fmt.Printf("echo2 before gl2 := <-chan2_1\n")
			gl2 := <-chan2_1
			maxid = gl2.ID // ID最大値の更新
			/*
				log.Printf("echo2 after gl2 := <-chan2_1 ID:%d", gl2.ID)
				log.Printf("echo2 after gl2 := <-chan2_1 Lat:%f", gl2.Lat)
				log.Printf("echo2 after gl2 := <-chan2_1 Lng:%f", gl2.Lng)
			*/
			//fmt.Printf("echo2 before err2 := c.WriteJSON(gl2)\n")
			err2 := c.WriteJSON(gl2)
			//fmt.Printf("echo2 after err2 := c.WriteJSON(gl2)\n")
			if err2 != nil {
				log.Println("write2:", err2)
				mutex.Unlock()
				break
			}
			//fmt.Printf("end of echo2\n")

		}

		mutex.Unlock()
	}
}

func echo(w http.ResponseWriter, r *http.Request) {

	c, err := upgrader.Upgrade(w, r, nil) // cはサーバのコネクション
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	defer c.Close()

	/*	ここでロックして待つ */

	for {

		//fmt.Printf("echo before gl := <-chan2_1\n")
		gl := <-chan2_1
		//fmt.Printf("echo after gl := <-chan2_1\n")

		//fmt.Printf("echo before err = c.WriteJSON(gl) gl2.id = %d\n", gl.ID)
		//fmt.Printf("echo before err = c.WriteJSON(gl) gl2.lat = %f\n", gl.Lat)
		//fmt.Printf("echo before err = c.WriteJSON(gl) gl2.lng= %f\n", gl.Lng)
		err = c.WriteJSON(gl)
		if err != nil {
			log.Println("WriteJSON1:", err)
		}
		//fmt.Printf("echo after err = c.WriteJSON(gl)\n")

		//fmt.Printf("echo before err = c.RreadJSON(gl)\n")
		gl2 := new(GetLoc)
		err2 := c.ReadJSON(&gl2)
		//fmt.Printf("echo after err = c.ReadJSON(&gl2) gl2.id = %d\n", gl2.ID)
		//fmt.Printf("echo after err = c.ReadJSON(&gl2) gl2.lat = %f\n", gl2.Lat)
		//fmt.Printf("echo after err = c.ReadJSON(&gl2) gl2.lng= %f\n", gl2.Lng)
		if err2 != nil {
			log.Println("ReadJSON:", err2)
		}
		// ここからチャネルで返す
		//fmt.Printf("echo before chan2_1 <- *gl2 gl2.id = %d\n", gl2.ID)
		//fmt.Printf("echo before chan2_1 <- *gl2 gl2.lat = %f\n", gl2.Lat)
		//fmt.Printf("echo before chan2_1 <- *gl2 gl2.lng = %f\n", gl2.Lng)
		chan2_1 <- *gl2
		//fmt.Printf("echo after chan2_1 <- *gl2\n")
		//fmt.Printf("end of echo\n")
	}

}

func home(w http.ResponseWriter, r *http.Request) {
	homeTemplate.Execute(w, "ws://"+r.Host+"/echo")
}

func smartphone(w http.ResponseWriter, r *http.Request) {
	smartphoneTemplate.Execute(w, "ws://"+r.Host+"/echo2")
}

func main() {
	flag.Parse()
	log.SetFlags(0)

	http.HandleFunc("/echo2", echo2)           // echo関数を登録 (サーバとして必要)
	http.HandleFunc("/echo", echo)             // echo関数を登録 (サーバとして必要)
	http.HandleFunc("/", home)                 // home関数を登録
	http.HandleFunc("/smartphone", smartphone) // smartphone関数を登録
	log.Fatal(http.ListenAndServe(*addr, nil)) // localhost:8080で起動をセット
}

var smartphoneTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script> 

function obj(id, lat, lng){
	this.id = id;
	this.lat = lat;
	this.lng = lng;
}

function random(min, max){
	return  Math.random()*(max-min) + min;
}

// var personal_id;

var lat = 35.654543;
var lng = 139.795534;  

window.addEventListener("load", function(evt) {
    var output = document.getElementById("output");
    var input = document.getElementById("input");
    var ws;
    var print = function(message) {
        var d = document.createElement("div");
        d.textContent = message;
        output.appendChild(d);
	};

	var personal_id = 0;

	
	///// 起動時のボタン
	// disabled属性を削除
	document.getElementById("open").removeAttribute("disabled");
	document.getElementById("open").style.color = "black";

	// disabled属性を設定 (closeボタンを非活性化)
	document.getElementById("close").setAttribute("disabled", true);
	document.getElementById("close").style.color = "White";	

	// disabled属性を設定 (sendボタンを非活性化)
	document.getElementById("send").setAttribute("disabled", true);
	document.getElementById("send").style.color = "White";	


		
	document.getElementById("open").onclick = function(evt) {
		console.log("document.getElementById open");

		// disabled属性を設定 (openボタンを非活性化)
		document.getElementById("open").setAttribute("disabled", true);
		document.getElementById("open").style.color = "White";		

		// disabled属性を削除
		document.getElementById("send").removeAttribute("disabled");
		document.getElementById("send").style.color = "black";	

		// disabled属性を削除
		document.getElementById("close").removeAttribute("disabled");
		document.getElementById("close").style.color = "black";	

		////////// 削除2
		// ws = new WebSocket("{{.}}");
		////////// 削除2終り


		////////// 削除1
		//var send_obj = new obj(0, 35.654543,139.795534);  // 最初は"0"でエントリ

		//console.log("open:send_obj");	
		//console.log(send_obj.id);	
		//console.log(send_obj.lat);
		//console.log(send_obj.lng);		

		//var json_obj = JSON.stringify(send_obj);
		//ws.send(json_obj);
		/////////// 削除1終り


        if (ws) {
            return false;
        }
		
		
		////////// 追加2
		ws = new WebSocket("{{.}}");
		////////// 追加2終り

		
        ws.onopen = function(evt) {
			print("OPEN");
		
			//ws = new WebSocket("{{.}}");
			
			////////// 追加1			
			var send_obj = new obj(0, 35.654543,139.795534);  // 最初は"0"でエントリ

			console.log("open:send_obj");	
			console.log(send_obj.id);	
			console.log(send_obj.lat);
			console.log(send_obj.lng);		

			var json_obj = JSON.stringify(send_obj);
			ws.send(json_obj);
			/////////// 追加1終り

		}
		
        ws.onclose = function(evt) {

			print("CLOSE");
            ws = null;
        }

		ws.onmessage = function(evt) {  // 受信したメッセージはここに飛んでくる
			print("RESPONSE: " + evt.data);  // jsonメッセージの内容を表示
			// データをJSON形式に変更
			var obj = JSON.parse(evt.data);

			personal_id = obj.id; // IDの取得(何回も取る必要はないが)
			console.log("personal_id");			
			console.log(personal_id);

			
			if ((Math.abs(obj.lat) > 90.0) || (Math.abs(obj.lng) > 180.0)){ // 異常な座標が入った場合は、マーカーを消去する
				console.log("before ws.close()");
				ws.close();
				console.log("after ws.close()");
			}
		}
		
        ws.onerror = function(evt) {
            print("ERROR: " + evt.data);
        }
        return false;
    };
	
	document.getElementById("send").onclick = function(evt) {

		console.log("document.getElementById send");

		// disabled属性を設定 (openボタンを非活性化)
		document.getElementById("open").setAttribute("disabled", true);
		document.getElementById("open").style.color = "White";	
	
		// disabled属性を削除
		document.getElementById("send").removeAttribute("disabled");
		document.getElementById("send").style.color = "black";	

		// disabled属性を削除
		document.getElementById("close").removeAttribute("disabled");
		document.getElementById("close").style.color = "black";	
	
		if (!ws) {
			console.log("return false send");
			return false;			
		}

		lat += random(0.5, -0.5) * 0.00001 * 10 * 5;
		lng += random(0.5, -0.5) * 0.00002 * 10 * 5

		
		//var send_obj = new obj(personal_id, 35.654543,139.795534);  // idでエントリ
		var send_obj = new obj(personal_id, lat, lng);  // idでエントリ

		console.log("send:send_obj");	
		console.log(send_obj.id);	
		console.log(send_obj.lat);
		console.log(send_obj.lng);		

		var json_obj = JSON.stringify(send_obj);
		ws.send(json_obj);		

		/*
        print("SEND: " + input.value);
        ws.send(input.value);
		return false;
		*/

		return false;	
    };

	document.getElementById("close").onclick = function(evt) {
		console.log(" document.getElementById close");

		// disabled属性を削除
		document.getElementById("open").removeAttribute("disabled");
		document.getElementById("open").style.color = "black";

		// disabled属性を設定 (closeボタンを非活性化)
		document.getElementById("close").setAttribute("disabled", true);
		document.getElementById("close").style.color = "White";	

		// disabled属性を設定 (sendボタンを非活性化)
		document.getElementById("send").setAttribute("disabled", true);
		document.getElementById("send").style.color = "White";			


        if (!ws) {
            return false;
		}
	
		var send_obj = new obj(personal_id, 999.9, 999.9);  // 最初は"0"でエントリ

		console.log("close:send_obj");
		console.log(send_obj.id);		
		console.log(send_obj.lat);
		console.log(send_obj.lng);		

		var json_obj = JSON.stringify(send_obj);
		ws.send(json_obj);

        //ws.close();  // これはws.onmessageの方で実施
        return false;
    };
});
</script>
</head>
<body>
<table>
<tr><td valign="top" width="50%">
<p>Click "Open" to create a connection to the server, 
"Send" to send a message to the server and "Close" to close the connection. 
You can change the message and send multiple times.
<p>
<form>
<button id="open">Open</button>
<!-- <p><input id="input" type="text" value="Hello world!"> -->
<button id="send">Send</button>
<button id="close">Close</button>
</form>
</td><td valign="top" width="50%">
<div id="output"></div>
</td></tr></table>
</body>
</html>
`))

var homeTemplate = template.Must(template.New("").Parse(`
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>PruneMobile</title>

	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.css"/>
	<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0-beta.2.rc.2/leaflet.js"></script>

	<script src="http://kobore.net/PruneCluster.js"></script>           <!-- これ、いずれローカルホストから取れるように換える -->
	<link rel="stylesheet" href="http://kobore.net/examples.css"/>      <!-- これも、いずれローカルホストから取れるように換える -->

	<!-- goのテンプレートのローカルって、どこになるんだろう? -->

</head>
<body>
<div id="map"></div>

<script>

	ws = new WebSocket("{{.}}"); // websocketの確立

	/*
	var print = function(message) {
		var d = document.createElement("div");
		d.textContent = message;
		output.appendChild(d);
	};
	*/

	// 引数にはミリ秒を指定。(例:5秒の場合は5000)
	function sleep(a){
  		var dt1 = new Date().getTime();
  		var dt2 = new Date().getTime();
  		while (dt2 < dt1 + a){
			dt2 = new Date().getTime();
		}
  		return;
	}

    var map = L.map("map", {
        attributionControl: false,
        zoomControl: false
	}).setView(new L.LatLng(35.36716428833585, 139.62724520774287), 16); // 富岡		
	//}).setView(new L.LatLng(33.58973407765046, 130.41048227121925), 16); // 中州
    //}).setView(new L.LatLng(35.654543, 139.795534), 18); 豊洲
    // }).setView(new L.LatLng(35.598563, 139.475528), 18); 広袴

    L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
        detectRetina: true,
        maxNativeZoom: 18
    }).addTo(map);

    var leafletView = new PruneClusterForLeaflet(1,1);  // (120,20)がデフォルト

	ws.onopen = function (event) {
	}

	var markers = [];

	//var helicopterIcon = L.icon({ iconUrl: 'http://sintef-9012.github.io/PruneCluster/examples/helicopter.png', iconSize: [48, 48] });
    //var airplaneIcon = L.icon({ iconUrl: 'http://sintef-9012.github.io/PruneCluster/examples/airplane.png', iconSize: [48, 48] });

	// 受信すると、勝手にここに飛んでくる
	ws.onmessage = function (event) {
		// データをJSON形式に変更
		var obj = JSON.parse(event.data);

		console.log("233");	
		console.log(obj.id);
		console.log(obj.lat);						
		console.log(obj.lng);	

		if (obj.id == 0){  // idが未登録の場合
			console.log("obj.id == 0")
			// データをマーカーとして登録
			var marker = new PruneCluster.Marker(obj.lat, obj.lng);

			// 参考資料  http://embed.plnkr.co/WmtpkEqSDJFuFeuiYP54/
			//var marker = new PruneCluster.Marker(obj.lat, obj.lng, {
			//	//popup: "Bell 206 " + i,
			//	icon: helicopterIcon
			//});


			console.log(marker.hashCode);		
			markers.push(marker);
	
			leafletView.RegisterMarker(marker);
	
			console.log(markers);
			console.log(markers.length)

			obj.id = marker.hashCode;
			//ws.send(marker.hashCode); // テキスト送信
			var json_obj = JSON.stringify(obj);
			ws.send(json_obj);			
		} else if ((Math.abs(obj.lat) > 90.0) || (Math.abs(obj.lng) > 180.0)){ // 異常な座標が入った場合は、マーカーを消去する
			console.log("Math.abs(obj.lat) > 180.0)")
			for (let i = 0; i < markers.length; ++i) {
				if (obj.id == markers[i].hashCode){
					console.log(i)
					console.log(obj.id)										
					console.log("obj.id == markers[i].hashCode")

					//leafletView.RemoveMarkers(markers[obj.id]);  // これでは消えてくれません
					// 1つのマーカーを消すのに、面倒でも以下の2行が必要
					var deleteList = markers.splice(i, 1);					
					leafletView.RemoveMarkers(deleteList);

					// 以下失敗例リスト
					//leafletView.RemoveMarkers(markers[i].hashCode);  //これはダメ
					//leafletView.RemoveMarkers(markers[obj.id],'item');
					//leafletView.ProcessView(); // 試しに入れてみる
					//leafletView.RemoveMarkers(markers[i-1]);
					//leafletView.RemoveMarkers(markers);					
					break;
				}
			}
			obj.lat = 91.0;
			obj.lng = 181.0;
			var json_obj = JSON.stringify(obj);
			ws.send(json_obj);				
		} else {
			// 位置情報更新
			console.log("else")
			for (let i = 0; i < markers.length; ++i) {
				if (obj.id == markers[i].hashCode){
					var ll = markers[i].position;
					ll.lat = obj.lat;
					ll.lng = obj.lng;
					break;
				}
			}
			var json_obj = JSON.stringify(obj);
			ws.send(json_obj);	
		}
	}

	// 位置情報の更新
    window.setInterval(function () {
        leafletView.ProcessView();  // 変更が行われたときに呼び出されれなければならない
	}, 1000);

	// サーバを止めると、ここに飛んでくる
	ws.onclose = function(event) {
		//print("CLOSE");
		ws = null;
	}


    map.addLayer(leafletView);
</script>



</body>
</html>
`))
// pm_proxy5_single_socket.go

/*
	このプログラムは、CSVファイルに記載された順番にエージェントの位置情報を読み取り、
	WebSocket を介してサーバーへ送信・更新する。

	サーバへ転送するプログラムは、server22-1.go

	- WebSocket 切断時に自動で再接続(最大5回リトライ)
	- エージェントが移動した場合のみサーバーへ通知
	- `sync.Mutex` を使用してスレッド安全な処理を実現
	- 位置情報が 999.0 以上の場合、エージェントを削除


	csv情報のサンプル (IDと緯度経度のみ使っている

	50044035447,139.629538,35.365357,00:00:00,WALK
	50044035447,139.629430,35.365151,00:00:30,WALK
	50044035447,139.629321,35.364945,00:01:00,WALK
	50044035447,139.629213,35.364738,00:01:30,WALK
	50044035447,139.629104,35.364532,00:02:00,WALK
	50044035447,139.628996,35.364325,00:02:30,WALK
	50044035447,139.628888,35.364119,00:03:00,WALK
	50044035447,139.628787,35.363937,00:03:30,WALK
	50044035447,139.628742,35.364159,00:04:00,WALK
	50044035447,139.628699,35.364375,00:04:30,WALK
	50044035447,139.628654,35.364592,00:05:00,WALK
	50044035447,139.628533,35.364724,00:05:30,WALK
	50044035447,139.628261,35.364691,00:06:00,WALK
	50044035447,139.627989,35.364658,00:06:30,WALK
	50044035447,139.627716,35.364625,00:07:00,WALK
	50044035447,139.627680,35.364620,00:07:30,WALK
	50044035447,999.9,999.9,00:08:00,WALK

	(50044035447, 999.9, 999.9, 00:08:00, WALK) でエージェントの移動が終了し、サーバからアイコンが消滅する

*/

package main

import (
	"encoding/csv"
	"flag"
	"fmt"
	"log"
	"math"
	"net/url"
	"os"
	"strconv"
	"sync"
	"time"

	"github.com/gorilla/websocket"
)

const (
	TopLeftLat     = 35.37574882601201
	TopLeftLon     = 139.61403393574466
	BottomRightLat = 35.36163108058289
	BottomRightLon = 139.6297897196359
)

type GetLoc struct {
	ID  int     `json:"id"`
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}

type unmTbl struct {
	uniName string
	objType string
	simNum  int
	pmNum   int
	lon     float64
	lat     float64
}

var list = make([]unmTbl, 0)
var addr = flag.String("addr", "0.0.0.0:8080", "http service address")

var mutex sync.Mutex

func connectWebSocket() (*websocket.Conn, error) {
	u := url.URL{Scheme: "ws", Host: *addr, Path: "/echo2"}

	for i := 0; i < 5; i++ {
		c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
		if err == nil {
			log.Println("Reconnected to WebSocket successfully.")
			return c, nil
		}
		log.Println("WebSocket reconnection attempt failed:", err)
		time.Sleep(2 * time.Second)
	}
	return nil, fmt.Errorf("failed to reconnect WebSocket after multiple attempts")
}

func main() {
	file, err := os.Open("agent_track_2018_101_end_and_order.csv")
	if err != nil {
		panic(err)
	}
	defer file.Close()

	fmt.Println("Opened testtest.csv")
	reader := csv.NewReader(file)
	var line []string

	flag.Parse()
	log.SetFlags(0)

	c, err := connectWebSocket()
	if err != nil {
		log.Fatal("WebSocket connection failed:", err)
	}
	defer c.Close()

	for {
		time.Sleep(time.Millisecond * 10)

		line, err = reader.Read()
		if err != nil {
			break
		}

		uniName := line[0]
		objType := line[4]
		lon, _ := strconv.ParseFloat(line[1], 64)
		lat, _ := strconv.ParseFloat(line[2], 64)

		flag := 0

		for i := range list {
			if list[i].uniName == uniName {
				old_lat := list[i].lat
				old_lon := list[i].lon

				list[i].lon = lon
				list[i].lat = lat

				if math.Abs(list[i].lat-old_lat) > 0.000000001 || math.Abs(list[i].lon-old_lon) > 0.000000001 {
					gl := GetLoc{ID: list[i].pmNum, Lat: list[i].lat, Lng: list[i].lon}

					mutex.Lock()
					err = c.WriteJSON(gl)
					if err != nil {
						log.Println("write2 error:", err)
						mutex.Unlock() // **ロック解除**
						c.Close()
						c, err = connectWebSocket()
						if err != nil {
							log.Println("WebSocket reconnection failed:", err)
							return
						}
						continue
					}

					gl3 := new(GetLoc)
					err = c.ReadJSON(gl3)
					if err != nil {
						log.Println("read error:", err)
						mutex.Unlock() // **ロック解除**
						c.Close()
						c, err = connectWebSocket()
						if err != nil {
							log.Println("WebSocket reconnection failed:", err)
							return
						}
						continue
					}
					mutex.Unlock()

					println("move Object!")

					if list[i].lat > 999.0 || list[i].lon > 999.0 {
						list = append(list[:i], list[i+1:]...)
						println("-----------------------------------------------------------------> delete Object!")
					}
				}
				flag = 1
				break
			}
		}

		if flag == 0 {
			uniNum := len(list)
			ut := unmTbl{uniName: uniName, objType: objType, simNum: uniNum, lat: lat, lon: lon}

			gl := new(GetLoc)
			gl.ID = 0
			gl.Lat = ut.lat
			gl.Lng = ut.lon

			mutex.Lock()
			err := c.WriteJSON(gl)
			if err != nil {
				log.Println("write1 error:", err)
				mutex.Unlock() // **ロック解除**
				c.Close()
				c, err = connectWebSocket()
				if err != nil {
					log.Println("WebSocket reconnection failed:", err)
					return
				}
				continue
			}

			gl2 := new(GetLoc)
			err = c.ReadJSON(gl2)
			if err != nil {
				log.Println("read error:", err)
				mutex.Unlock() // **ロック解除**
				c.Close()
				c, err = connectWebSocket()
				if err != nil {
					log.Println("WebSocket reconnection failed:", err)
					return
				}
				continue
			}
			mutex.Unlock()

			ut.pmNum = gl2.ID
			list = append(list, ut)
			println("------------>generate Object!")
		}
	}
}

 

2025,江端さんの忘備録

VSCode(Visual Studio Code)は、Microsoftが開発した無料のオープンソースのコードエディタです。軽量でありながら高機能で、多くのプログラミング言語に対応しています。

VSCode (Visual Studio Code) is a free, open-source code editor developed by Microsoft. It is lightweight yet highly functional and supports many programming languages.

先日、VSCodeの拡張機能をアドインしたら、私のGo言語プログラムが全く動かなくなりました。

The other day, my Go program stopped working when I added an extension to VSCode.

このPCは、私の業務に関わるプログラムのほぼ全部を担当しているマシンなので、大袈裟ではなく「私の半分が殺された」といっても良い状態です。

This PC is the machine that is in charge of almost all of the programs related to my work, so it's not an exaggeration to say that “half of me has been killed.”

# 「人生の半分を損している」というレベルとは違います。

# This is not the same as “losing half of your life.”

ただ、経験的に、こういう時に慌てて色々なことを試みると、プログラム開発環境どころか、PCシステムそのものを回復不可能なまでに破壊する、ということを、私は良く知っています(というか、これまで壊したシステムの数は、3桁いっていると思います)。

However, from experience, I know that if you try various things in a panic in a situation like this, you can destroy the PC system itself beyond repair, not to mention the program development environment (or rather, I think the number of systems I've destroyed is in the triple digits).

-----

という訳で、動かなくなったGo言語環境を復旧する為にChatGPTの支援を受けながら、作業をしていました。

So, I was working to restore the Go language environment that had stopped working with the help of ChatGPT.

Go言語を再インストールして実行したら、『「go1.23.4.windows-amd64」ではダメだ。「go1.23.2.windows-amd64」をインストールしろ』、と指示されて、リビジョン番号の違うバイナリをダウンロードして、対応しました。

When I reinstalled Go and ran it, I was told to download a binary with a different revision number, which I did.

結果として、現在、プログラム環境は回復したように見えましたので、それはそれで良かったのですが、

As a result, the program environment now appeared to have recovered, so that was good, but

―― バージョンの枝番まで細かく命令されて、それに従っている私って・・・

"I'm being ordered to follow the version branch number in detail..."

という気持ちになりました。

I felt that way.

-----

まあ、私は、おそらくは300以上(1000を超えるかもしれない)自作プログラムを人質に取られているようなものですので、環境の命じる通りに作業するしかありません。

Well, I'm probably a hostage with over 300 (maybe over 1000) self-made programs, so I have no choice but to work as the environment commands.

しかし「VSCodeに隷属している」という気持ちは拭えません。

However, I can't shake the feeling that I am “subordinate to VSCode.”

まあ、しかし、今の私は、すでに「生成AIに隷属」している身の上です。

Well, but now I am already “enslaved to generative AI.”

ですから、『今さら何を』という気持ちもあります。

So, I also feel like, 'What is the point now?

-----

とは言え、VSCodeを使っていて、マルクスの『資本論』における「労働の疎外(Entfremdete Arbeit)(*)」を実感することになるとは思いませんでした。

However, I never thought that using VSCode would make me realize the “alienation of labor” in Marx's “Das Kapital” (*).

(*)本来人間が支配し利用するはずの道具(生産手段)が、資本主義社会では逆に人間を支配するようになるという逆転現象のこと

(*) A reversal in which the tools (means of production) humans should control and use end up controlling them in a capitalist society.

停滞主義とは(2)

2025,江端さんの技術メモ

vscodeでデバッグをする時に、同じディレクトリにmain()関数を持つGo言語プログラムが複数あると、デバッグができません。ファイル名を指定してデバッグすることはできないでしょうか。ただし、launch.json にファイル名を書き込むと、実行ファイルが固定されてしまうので、それは避けたいのですが。

launch.json を使うと、特定のGoファイルが環境として固定されるため、別のファイルをデバッグしたいときに launch.json を都度編集する必要があり、やや不便です。

ですが、以下の方法を使えば 環境を固定せずに、デバッグするファイルを柔軟に切り替えられます。


1. launch.json${file} を使う

環境を固定せずに、現在開いているGoファイルをデバッグする方法です。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug Current File",
            "type": "go",
            "request": "launch",
            "mode": "debug",
            "program": "${file}"
        }
    ]
}

  • ${file} は、現在アクティブな(開いている)ファイルをデバッグ対象にする特殊変数です。
  • この設定にすると、エディタで開いているGoファイルがどれでも、F5 でデバッグが開始できます。

開いているGoのプログラムでデバッガーが動いた。これから作業がラクになるかな。