OmniAICreator/Qiita-1.07M huggingface dataset:
「オブジェクト指向による簡易版ブラックジャックゲームの実装（PHP）」のクラス図修正案

https://qiita.com/daitennn/items/19798b5a0613589ecaa8

上記記事のクラス図の誤記をコメントしようと思ったのですが、以前の指摘でブロックされてしまったようなので、記事にて修正案を書いておきます。

Qiitaのガイドラインにも書いてありますが、間違いなどをみんなで改善していきたいですね。

https://help.qiita.com/ja/articles/qiita-community-guideline

> ☝ 記事の価値はみんなで高めよう
> 読んでみて「この記事が役に立った！」と思ったら、LGTMやコメントなどでぜひその気持を伝えてください。 記事の中に間違いや追記すべき内容などがあれば、非難するのではなくみんなで改善をしていきましょう。Qiitaには編集リクエストなどの、記事をみんなで改善しあえる機能が備わっています。みんなでいまある記事の価値を高めましょう。

## クラス図修正案

* クラスの継承は親クラス側に三角形
* Card と BlackJackGame の依存の向きが逆
* 引数で渡すオブジェクトはコンポジションにしない
* Hand も Card を持つ

なお、クラス図は plantuml テキスト にて記述していますので簡単に修正できます。
「Markdownで本文を見る」を選んでいただくと書き方が分かります。
コード内容は自由に使っていただいて構いません。

ちなみに、GitHub では mermaid コードで書くと図に変換してくれます。

```plantuml
class Main {
  呼び出し側
}

class Deck {
  drawCard()
  shuffles()
}

class Card {
  getRank()
  getRankA()
}

Class BlackJackGame {
  start()
  actionSelect(object user)
  showWinner(string winner)
}

class Hand {
  handScoreCalculate()
  getHandScore()
}

abstract class User {
  {abstract} action()
  drawCard(Deck deck, int drawNum)
}

class Player {
  action()
}

class Dealer {
  action()
  drawCard(Deck deck, int drawNum)
}

class PlayerComputer {
  action()
}

Main --> BlackJackGame
BlackJackGame "1" *--> "1" Player
BlackJackGame "1" *--> "1" Dealer
BlackJackGame "1" *--> "1" PlayerComputer
BlackJackGame "1" *-> "1" Deck
Deck "0..1" o--> "*" Card

User <|-up- Player
User <|-up- Dealer
User <|-up- PlayerComputer

User "1" *-> "1" Hand
Hand "0..1" o-up-> "*" Card
```
InDesign JavaScript XML 選択オブジェクトを兄弟関係の構造に

選択オブジェクトを兄弟関係の構造にするスクリプトは、これで良いのかな・・・？

~~~JavaScript
/*
このスクリプトを利用して起こった不具合の責任は取れません。
ご了承下さい。

更新 2022/06/17
*/

// アプリ指定
#target "indesign";

// スクリプト名
var scriptName = "選択オブジェクトを兄弟関係の構造に";

// メモ：doScriptをすると元に戻したときおかしくなる事がある。

// 参照要素配列
var associatedXMLElementArray = [];
    
// タグ付きプリセットオプションの画像のタグがImageで無い場合
if(app.activeDocument.xmlPreferences.defaultImageTagName != "Image"){

    // Imageに
    app.activeDocument.xmlPreferences.defaultImageTagName = "Image";
}

// タグ付きプリセットオプションのテキストフレームのタグがStoryで無い場合
if(app.activeDocument.xmlPreferences.defaultStoryTagName != "Story"){

    // Storyに
    app.activeDocument.xmlPreferences.defaultStoryTagName = "Story";
}
 
// 選択の数だけ繰り返す
for(var i = 0; i < app.activeDocument.selection.length; i++){

    // autoTagメソッドが有る場合
    if(app.activeDocument.selection[i].hasOwnProperty("autoTag")){

        // 参照する要素が無い場合
        if(app.activeDocument.selection[i].associatedXMLElement == null){

            // 自動タグ
            app.activeDocument.selection[i].autoTag();
        }   
    }
    
    // 追加
    associatedXMLElementArray.push(app.activeDocument.selection[i].associatedXMLElement);
}

// ルートに要素を追加
var parentElement = app.activeDocument.xmlElements.firstItem().xmlElements.add("Information");

// 参照要素の数だけ繰り返す
for(var i = 0; i < associatedXMLElementArray.length; i++){

    // 参照要素の名前がImageの場合
    if(associatedXMLElementArray[i].markupTag.name == "Image"){

        // 「href」名の属性が有る場合
        if(associatedXMLElementArray[i].xmlAttributes.itemByName("href").isValid == true){

            // 画像がファイル名を検索
            associatedXMLElementArray[i].xmlAttributes.itemByName("href").value.match(/([^\/]+)(\.[a-zA-Z]+$)/g);

            // 一つ目の検索結果が有る場合
            if(RegExp.$1 != null){

                // 「fileName」名の属性が有る場合
                if(associatedXMLElementArray[i].xmlAttributes.itemByName("fileName").isValid == true){

                    // 一つ目の検索結果を属性の値に入れる
                    associatedXMLElementArray[i].xmlAttributes.itemByName("fileName").value = RegExp.$1;

                // 無い場合
                }else{

                    // 「fileName」名の属性と値を追加
                    associatedXMLElementArray[i].xmlAttributes.add("fileName", RegExp.$1);
                }
            }
        }

        // 追加した要素の子（終わり）に移動
        associatedXMLElementArray[i].move(LocationOptions.AT_END, parentElement);

    // 以外の場合
    }else{

        // 追加した要素の子（初め）に移動
        associatedXMLElementArray[i].move(LocationOptions.AT_BEGINNING, parentElement);
    }
}
~~~
ディスクが足りないサーバーやPCでOneDriveを活用した無料または安価なTBサイズのドライブを追加する

# はじめに
サーバーやPCで追加のストレージが必要になった際に通常のディスクをマウントすると高額になることがある。例としてAzure Managed Diskの場合、1TBで月額2万円弱になってしまう。安価に回避する方法としてOneDriveやSharePointドライブマッピングがある。（ブラウザだけでは不便なんですよね・・・）

# 制約条件
■共有フォルダのように排他制御の厳密さは期待できない
■読み取りに時間がかかる、Web通信が発生するので読み書き速度は遅い（一部のツールではキャッシュ対応しているが、キャッシュにも限界があるのでサーバー自体のディスクと比較すれば断然遅い）
　
# メリット
■バージョン履歴管理、ランサムウェア対策として期待できる
■ドライブをマッピングするかブラウザ経由でどこのPC、サーバー、スマホからも同じデータを閲覧できる
■ドライブマッピングをしてデータの保存をしてもPCやサーバー自体のディスクスペースを取らない（直接Microsoftのサイトに書き込みにいきますからね・・・）

# ドライブ容量や必要ライセンス（すでにO365使っていれば追加ライセンスは要らないかも）

SharePoint Online Plan2 相当が使えるサブスクリプションが5本以上あるテナントはSharePoint Online Plan2相当のライセンスを割り当てたユーザーにはデフォルトで5TB, 無い場合はOneDriveが使えるライセンスが割り当たっているユーザーであれば1TBが使える。Office E3などのユーザーも5本あればシステム側で設定すればデフォルトで5TBまで使える。5TB使い切る頃にはサポートに依頼すると段階的に25TBまで増やしてくれて、以後、別のSharePointサイトとして25TBを増やしてくれる仕組み。OneDrive for Business Plan2 とSharePoint Online Plan2は価格は同じだがSharePoint Online Plan2はOneDriveをカバーしているため、OneDriveを単品で買うならSharePoint Online Plan2が良いであろう。
(https://www.microsoft.com/ja-jp/microsoft-365/sharepoint/compare-sharepoint-plans)

# ドライブマッピングのツール
■Air Live Drive（キャッシュ機能がある、自動起動対応、無料版あり）
　　・・・類似製品も多数見つかるがキャッシュ機能があり無料で一定程度使える、日本に代理店がある、世界で使われている実績などを鑑みると一番無難にみえた

■PowerShellでマッピング(OneDriveMapperというツールがあり複数ドライブSharePointなども対応 https://www.lieben.nu/liebensraum/onedrivemapper/)

■DOSでマッピング（SSOやInternet Explorer問題の回避が無い模様、）

実際にAzure上にあるVMのサーバー（世界どこへも持ち運び可能なマイPC環境です）にOneDriveをマッピングしたイメージ：
![無料または安価なストレージ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/518389/3ff417b8-97dc-410b-ee9e-7921dc63c4d6.png)

# 応用編
同時アクセスが無いか極めて少ないサイトの場合で在宅ワーク中心、データ量が大でない場合、ファイルサーバーとして使うことも実現できています（問題ないですよね？）。SharePoint Online Plan2で共有アカウントとして一つ作成し、OneDriveのフォルダ階層を定め、フォルダ毎（人事、総務、ITとか）に共有アクセス権設定（可能であればO365グループを作成し）を事前にする（これ重要、ファイル数が多いとOneDrive/SharePointでは後から権限変更できません・・・）。PowerShellやDOSバッチプログラムを配布しドライブマッピングさせてしまいTBクラスの爆速共有フォルダの出来上がり。ドライブマッピングが上手く動かなくても最悪ブラウザで何とかなる。適切な共有権限を設定し各O365ユーザーが利用可能としておくことでそれぞれのO365利用者は自身のログインでアクセスができますし更新者情報もそれぞれの利用者情報が残ります（Teamsライクです）。フォルダごと読み書き両方、読み取り専用とか設定ができます。
curlでCORSリクエストの再現方法

curlでpreflightリクエストを再現するときは、HTTPヘッダに次の３つが最低限必要。（MDN:『[Preflight request (プリフライトリクエスト)](https://developer.mozilla.org/ja/docs/Glossary/Preflight_request)』より）

- Origin
- Access-Control-Request-Method
- Access-Control-Request-Headers

例えばローカルにMockAPIなどを建ててCORSの動作確認する場合、次のようなコマンドを実行するとよい。
 
```shell-session
curl -v -X OPTIONS \
    -H 'Origin:*' \
    -H 'Access-Control-Request-Method: GET' \
    -H 'Access-Control-Request-Headers: content-type' \
    http://localhost/目的のMockAPIのURLなど
```

なお `-v` とするのは受送信状況を確認するため。preflightリクエストが成功すると 204 No Content 応答なので何も表示されないため。


## このメモの背景

[PRISM(stoplightio/prism)](https://github.com/stoplightio/prism) の[Dockerコンテナ](https://hub.docker.com/r/stoplight/prism/)を使ってリバプロの後ろにMockAPIを建てた時、CORSの動作確認をしようとしたら、次のエラーが出てしまった。

```:(読みやすいように改行済み)
{
  "type":"https://stoplight.io/prism/errors#NO_METHOD_MATCHED_ERROR",
  "title":"Route resolved, but no method matched",
  "status":405,
  "detail":"The route リクエストしたパス has been matched, but it does not have \"options\" method defined"
}
```

「あれ？ PRISMは[CORSをうまくしてくれる](https://meta.stoplight.io/docs/prism/ZG9jOjE2MDY1NjY4-cors)んじゃねーんだが？」 と思ったのが調査の始まり。
なお、原因はヘッダ`Origin`がない状態でリクエストしたから。

## 参考資料
- MDN:[『オリジン間リソース共有 (CORS)』](https://developer.mozilla.org/ja/docs/Web/HTTP/CORS)
AWSアカウントを作成後にやっておいた方がいいこと

# AWSアカウント作成

これまで業務用アカウントしか持っていなかったので、個人用アカウントを作成しました。
個人用アカウントは自分のクレジットカードが登録されているので、アカウントが乗っ取られるとマズイ。
セキュアな初期設定を調べて、設定することにしました。

# 設定したこと

## ルートアカウントにMFAを設定

ルートアカウントとは、AWSアカウント作成時に生成されるアカウントです。
メールアドレスでログインできるやつです。
ルートアカウントは管理者権限を持ち、なんでもできてしまうのでMFAを設定することが推奨されます。

MFAとは多要素認証のことで、ワンタイムパスワードの入力を必須にします。
私のスマホにはAuthenticator(マイクロソフト)がインストールされていたので、こちらを利用しました。
QRコードをスマホで読むと、アプリ上にワンタイムパスワードが表示されました。
２回連続で表示されるワンタイムパスワードを入力すると、設定が完了します。簡単ですね。

## IAMユーザー作成

開発用のユーザーを作成しました。
個人利用なので、グループは作らずにユーザーに直接ポリシーをアタッチしました。

## IAMパスワードポリシーの強化

パスワードは90日ごとに変更を強制しました。

![スクリーンショット 2022-06-17 12.13.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528549/6430ff11-fbe0-e209-215c-d360e72c9943.png)

## 使わないリージョンのSTSを無効にする

STSとはAWSサービスへアクセスするための一時トークンを発行するサービスです。
アカウントが乗っ取られて普段見ないリージョンで好き勝手されていたっていうことがないように、使わないリージョンのSTSは無効化しておきます。
グローバルエンドポイントとバージニアは無効化できないので、そのままで、
とりあえず東京と大阪以外は使う予定がないので、無効化しました。

![スクリーンショット 2022-06-17 12.28.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528549/c48a9713-72a6-db04-8db1-1f3d9c25120e.png)

## 請求情報へのアクセス権付与

IAMユーザーが請求情報を確認できるように設定しました。

## Budgetsの設定

利用料金が設定した予算を超えるとアラートメールを送信して、気づかせてくれます。
DBの消し忘れ等で無断にリソースを使ってることがしばしばあるので、低めの予算設定ですぐに対応できるようにしておきます。

貧乏性なので$1.00の予算を超えたらメール通知が来るように設定しました。

# 参考にした記事

https://kacfg.com/aws-first-config_1/

https://kacfg.com/aws-first-config_2/
【小技】Excelで行の高さや列の幅が0の箇所をまとめて見える高さに変更する

# 結論
シートを全選択して行の高さや列の幅が0の行番号や列名の位置をダブルクリックする

# 概要
以下のような、高さが0の行(行番号2, 5, 6, 10, 11, 12)をまとめて見える高さに変更する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692730/71977f83-c1ba-8f12-19e7-1fac6b77d9e2.png)

# 手順(2 Step)
1. シートを全選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692730/46c5d230-b8c4-327a-c2b4-adec3e7b3c3a.png)

2. マウスで絶妙な位置をダブルクリックする
マウスカーソルのアイコンは以下のどちらでもよい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692730/0d2b0ebc-f274-bc99-5169-65eb45acb062.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692730/e99145c4-d5a6-bec5-983a-5a3625c1b87b.png)

見ていない！オレは見てないぞ なあーんにも見てないッ！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2692730/372378ee-d2ea-d679-f3f4-a70af4959772.png)
cscの作法 その196

# 概要
cscの作法、調べてみた。
hyperlinkやってみた。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/19374d33-7cb8-12fb-daf8-ce221a47364f.png)


# サンプルコード

```
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;

namespace app
{
	public partial class Form1 : Form {
		private Button button1;
		private Button button2;
		private Button button3;
		public Form1() {
			this.Text = "hyperlink";
			this.Size = new Size(300, 300);
			this.button1 = new Button();
			this.button2 = new Button();
			this.button3 = new Button();
			this.button1.Location = new System.Drawing.Point(30, 30);
			this.button1.TabIndex = 0;
			this.button1.Text = "1";
			this.button1.Click += new System.EventHandler(this.button1_Click);
			this.button2.Location = new System.Drawing.Point(30, 80);
			this.button2.TabIndex = 0;
			this.button2.Text = "2";
			this.button2.Click += new System.EventHandler(this.button2_Click);
			this.button3.Location = new System.Drawing.Point(30, 130);
			this.button3.TabIndex = 0;
			this.button3.Text = "3";
			this.button3.Click += new System.EventHandler(this.button3_Click);
			Controls.AddRange(new Control[] {
				button1,
				button2,
				button3
			});
		}
		private void button1_Click(object sender, EventArgs e) {
			string url = "https://www.google.co.jp";
			OpenUrl(url);
		}
		private void button2_Click(object sender, EventArgs e) {
			string url = "https://www.yahoo.co.jp";
			OpenUrl(url);
		}
		private void button3_Click(object sender, EventArgs e) {
			string url = "https://qiita.com";
			OpenUrl(url);
		}
		private Process OpenUrl(string url) {
			ProcessStartInfo psi = new ProcessStartInfo();
			psi.FileName = url;
			psi.UseShellExecute = true;
			return Process.Start(psi);
		}
		[STAThread]
		static void Main() {
			Application.Run(new Form1());
		}
	}
}





```


以上。
cscの作法 その195

# 概要
cscの作法、調べてみた。
練習問題やってみた。

# 練習問題
マウスイベントを使え。

# 参考にしたページ

https://ameblo.jp/hirokifukuzaki/entry-12541452533.html

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/ab7c6d60-3ca2-9ceb-d20b-7a9d9a813647.png)

# サンプルコード

```
using System;
using System.Drawing;
using System.Windows.Forms;

class Form1 : Form {
	private Point last = new Point(0, 0);
	private Bitmap bmp = new Bitmap(400, 400);
	private Graphics g;
	PictureBox pictureBox1;
	public Form1() {
		this.Text = "pen";
		this.Size = new Size(400, 400);
		this.Load += new EventHandler(this.Form1_Load);
		pictureBox1 = new PictureBox();
		pictureBox1.Width = 400;
		pictureBox1.Height = 400;
		Controls.AddRange(new Control[] {
			pictureBox1
		});
	}
	private void Form1_Load(object sender, EventArgs e) {
		g = Graphics.FromImage(bmp);
		pictureBox1.MouseMove += new MouseEventHandler(pictureBox1_MouseMove);
	}
	private void pictureBox1_MouseMove(object sender, MouseEventArgs e) {
		if (e.Button == MouseButtons.Left)
		{
			g.DrawLine(Pens.Blue, last.X, last.Y, e.X, e.Y);
		}
		last = e.Location;
		pictureBox1.Image = bmp;
	}
	[STAThread]
	static void Main() {
		Application.EnableVisualStyles();
		Application.Run(new Form1());
	}
}





```


以上。
【Swift】XXXXX One of the paths in DEVELOPMENT_ASSET_PATHS does not exist: XXXXXXを修正する

# 発生条件
`Preview Content`のファイルディレクトリを初期ディレクトリから変更した場合
![スクリーンショット 2022-06-17 11.56.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/f12a2304-1d0e-7a45-eb8f-60a2a3bff192.png)

# 修正方法
① プロジェクトを選択する
② ターゲットを選択する
③ 「Build Settings」を選択する
④ 「Development Assets」と検索する
⑤ 赤枠の箇所をダブルタップする
![スクリーンショット 2022-06-17 12.00.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/26b7e10e-1abe-baf7-dc3c-db57c3d6243f.png)

ファイルディレクトリ変更後のパスを入力
私の場合、
`"SampleProject/Preview Content"`
↓ ↓ ↓ ↓ ↓ ↓ ↓
`"SampleProject/Resources/Preview Content"`
![スクリーンショット 2022-06-17 12.05.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/87d9e064-b90a-005f-bfd0-89592bae7185.png)
IMUをROS2で利用する

## 環境
ros2 galactic
TR-IMU1647X

## 参考というかこれ通り
https://github.com/technoroad/ADI_IMU_TR_Driver_ROS2

## 実装
```
cd ros2/src
git clone -b galacric --recursive https://github.com/technoroad/ADI_IMU_TR_Driver_ROS2
```
ros2はあなたのros2ワークスペースに合わせる。私のワークスペースはros2
galacticはあなたのブランチに合わせる。私はros2 galacticのため、galacticを指定。

```
cd ros2
rosdep update
rosdep install --from-paths src --ignore-src --rosdistro ${ROS_DISTRO} -y
cd ros2
colcon build --symlink-install
source install/setup.bash
ros2 launch adi_imu_tr_driver_ros2 adis_rcv_csv.launch.py mode:=Register device:=/dev/ttyACM0
```
ttyACM0は実際のデバイスに合わせる

新しいターミナルを開き
```
cd ros2
source install/setup.bash
ros2 topic echo /imu/data_raw
```
取得値が表示される
【Vivado】非表示にしたのrerun out-of-date synthesis before implementationを再表示する

[Tools] > [Setting] > [Window Behavior] > [Confirmations]

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214706/53aac764-df99-beec-0ee2-4e632eca3db3.png)

その他の非表示にしたものも、ここで再表示するようにできる。
Stripe Checkoutの決済ページや請求ポータルを、任意の言語で表示するように設定する方法

Stripe Checkoutで顧客に表示する決済ページは、基本的にはブラウザの設定言語と同じ言語で表示されます。

ですが、「サイトやアプリのユーザー設定で言語が変更できる場合」など、ブラウザの設定とは異なる言語でページを表示させたい場合も存在します。

その場合は、セッション作成時に、　`locale`パラメータを追加しましょう。

## Checkout（決済ページ）のセッションで、言語を個別指定する

```js
      const sessions = await stripe.checkout.sessions.create({
          customer: customerId,
          line_items: [{
              price: 'price_xxxx',
              quantity: 2,
          }],
          success_url: 'https://example.com?success',
          cancel_url: 'https://example.com',
          mode: 'payment',
          locale: 'de',
      });
```

このサンプルでは、言語をドイツ語（`DE`）に設定しています。

作成したCheckoutセッションのURLにアクセスすると、ドイツ語表記の決済ページが表示されます。
![スクリーンショット 2022-06-13 21.11.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/33e78be0-1822-719d-699d-23311857c35c.png)

## Customer Portal(請求ポータル)で、言語を個別指定する

Customer Portalについても、セッション作成時に`locale`が指定できます。

```js
      const sessions = await stripe.billingPortal.sessions.create({
        customer: customerId,
        locale: 'de'
      })
```

こちらも`locale`を`de`に設定したため、ポータルの言語がドイツ語に切り替わりました。

![スクリーンショット 2022-06-13 21.14.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366300/fcdb2047-f65b-990e-3ae1-08923a6270ea.png)

## `locale`のデフォルトは`auto`（ブラウザの設定に依存）

なお、`locale`を設定しない場合は、`auto`がデフォルト値で設定されます。

`auto`の場合は、基本的にブラウザの設定言語で決済ページが表示されます。

## 関連ドキュメント

- https://stripe.com/docs/api/checkout/sessions/create#create_checkout_session-locale
- https://stripe.com/docs/api/customer_portal/sessions/create#create_portal_session-locale

## [PR] Stripe開発者向け情報をQiitaにて配信中！

- [Stripe Updates]：開発者向けStripeアップデート紹介・解説
- ユースケース別のStripe製品や実装サンプルの紹介
- Stripeと外部サービス・OSSとの連携方法やTipsの紹介
- 初心者向けのチュートリアル(予定)

など、Stripeを利用してオンラインビジネスを始める方法について週に２〜３本ペースで更新中です。

-> [Stripe Organizationsをフォローして最新情報をQiitaで受け取る](https://qiita.com/organizations/stripe)
xcopyとmicrosoft365(one drive)とrobocopy　まったく駄目でした

MS365のクラウドにファイルをアップロードしているのですが、これが遅い遅い。
google workspaceも、最初は遅いと思っていましたが、ms365に比べると圧倒的に早いですが
ms365のクラウドは、遅い遅すぎる。
１万ファイルで１日くらいのスピードでしょうか？
はぁぁ・・・
100歩譲って、それは我慢しよう。


コピーってどうやってますか？
私は、せっかくだからコマンドでと思って
xcopy local MS365のcrowd
なんて、コマンドうってやってます。
コマンドうって、１日かけてファイルを同期させて、で、もう一回同じコマンドを何となく打ってみた。

ガ・・・・ｍｍｍｍ

またコピーが始まった。
俺なにもしてないぞ。こらぁ！

同じファイルだからコピーしないはずだけど、何でコピーしてるねん？
おかしい！というわけで、調べてみたら、excelとかwordとかだけをコピーしてる・・・
うん？
うん？
意味わからん。
なになに、バージョン履歴を管理しているから、そのせいか？

で、考え抜いた結果xcopyじゃなくってrobocopy使ってみた。
そしたら、うまくいった。
と思ったけど、やっぱりこちらもダメ・・・

robocopy local MS365のcrowd /E /DCOPY:DAT /NP /TEE /r:3 /w:3

いったい、どーすればいいんだ？
これｗ


＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝
追記
簡単な解決方法がありました。
xcopyも、robocopyも使わない。
そのかわり、BunBackupを使うんです。
https://forest.watch.impress.co.jp/library/software/bunbackup/

ちょー便利です。
ms365ってやつは、excelファイルをクラウドにコピー―すると
それをクラウドにアップします。
クラウドにアップすると、そのファイルに少しの情報を追加して、
再度、ローカルにダウンロードしてくるので、少し容量が変わるんです。
その容量の変化がxcopyやrobocopyをめちゃくちゃにしていた。
だから、bunBackupを使って、かつ、詳細設定で異なるサイズは無視するようにしてあげれば
綺麗に動作します＾＾

これで、この問題は、解決。

ちなみに
googleの場合も、あたまにくる事象があって
それは、これで解決されます。
https://www.itmedia.co.jp/pcuser/articles/2107/21/news068_3.html
Autonomous Database の資格証明（Credential）を更新する方法

2022/6/15にOracle Cloud Infrastructure（以下、OCI）から「Rotate Credentials for Tenant」という通知を受け取りました。
セキュリティ脆弱性に対応するためのパッチを適用したため、各種クレデンシャル情報の更新が求められています。

自分の環境では、Autonomous Database（以下、ADB）の資格証明（Credential）で利用する認証トークンが該当していたため、実施した作業を記載しておきます。

### 1.ADBの資格証明（Credential）の確認

まずは、現在の資格証明を確認します。

```
SQL> select * from user_credentials;

CREDENTIAL_NAME
--------------------------------------------------------------------------------
USERNAME
--------------------------------------------------------------------------------
WINDOWS_DOMAIN
------------------------------------------------------------
COMMENTS
--------------------------------------------------------------------------------
ENABLED
----------
ADW_CRED
ayamakkk

{"comments":"Created via DBMS_CLOUD.create_credential"}
TRUE
```

「ADW_CRED」という資格証明（Credential）が登録されており、OCIのユーザー名は「ayamakkk」であることがわかります。

### 2.新しい認証トークンを作成

OCIコンソールにログインし、左上三本線メニューから、アイデンティティとセキュリティ＞ユーザーとメニューを選択します。

ユーザー一覧より、該当ユーザー（今回は「ayamakkk」）をクリックします。

ユーザー詳細画面左下のリソースメニュから認証トークンを選択し、「トークンの生成」ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694576/509a2bea-17fc-51b9-6797-305dfbab2320.png)
説明を入力し「トークンの生成」ボタンを押します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694576/2ed790ec-3413-4c40-c699-99d384847c51.png)

生成されたトークンをコピーして、メモ帳などに控えておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694576/ab9e4f2f-4c98-e334-caf5-294ce64536ca.png)

### 3.資格証明（Credential）のパスワード変更

DBMS_CLOUD.UPDATE_CREDENTIALプロシージャーを利用して、資格証明（Credential）のパスワードを変更します。

credential_name：変更したいクレデンシャル名（今回は「ADW_CRED」）
attribute：'PASSWORD'
value：手順2.でメモした認証トークン

```
SQL> begin
  2    DBMS_CLOUD.UPDATE_CREDENTIAL (
  3      credential_name => 'ADW_CRED',
  4      attribute => 'PASSWORD',
  5      value => 'Ni-92XfG9Lxxxxxxxxxx'
  6    );
  7  end;
  8  /

PL/SQLプロシージャが正常に完了しました。
```

ここで、実際にObject Storageからのデータロードなどを試してみます。
うまく行けば「手順4.不要になった認証トークンを削除」に進みます。

### 4.不要になった認証トークンを削除

削除したい認証トークンの右端の3つの点をクリックし、削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/694576/379517f4-38c2-91ac-5cd5-a2ce8235b3eb.png)

参考：DBMS_CLOUD.UPDATE_CREDENTIALプロシージャー
https://docs.oracle.com/cd/E83857_01/paas/autonomous-database/adbsa/dbms-cloud-subprograms.html#GUID-5104A4E5-DCD8-4899-96EC-A9758BA03FC5
RDKitで構造式描画

# SMIELSから構造式を描画する簡単な例を備忘録として書いておく

3つ描画する例

```python3:make_png.py
from rdkit import rdBase, Chem
from rdkit.Chem import AllChem, Draw
from rdkit.Chem.Draw import rdMolDraw2D
from rdkit import DataStructs

smiles = ["<SMILES1>",　"<SMILES2>", "<SMILES3>"]
mols = [Chem.MolFromSmiles(smile) for smile in smiles]
# molsPerRowで1行に何個の分子を書くかの設定
# subImgSizeで一つの分子の描画サイズを設定（この場合は横400ピクセル，縦300ピクセル）
# legendsでラベルを設定（この場合はSMILES文字列が分子の下に表記される）
img = Draw.MolsToGridImage(mols, molsPerRow=3, subImgSize=(400,300), legends=smiles)

img.save('hoge.png')

```

出来上がるイメージはこんな感じ
![hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1789756/38870272-d24c-fd3e-6ae5-5e885b6bb4c2.png)


#　コマンドラインからSMILESを読み込めるようにして汎用性を上げたバージョン

```
from rdkit import rdBase, Chem
from rdkit.Chem import AllChem, Draw
from rdkit.Chem.Draw import rdMolDraw2D
from rdkit import DataStructs
import sys

smiles = sys.argv[1:]
mols = [Chem.MolFromSmiles(smile) for smile in smiles]
# molsPerRowで1行に何個の分子を書くかの設定
# subImgSizeで一つの分子の描画サイズを設定（この場合は横400ピクセル，縦300ピクセル）
# legendsでラベルを設定（この場合はSMILES文字列が分子の下に表記される）
img = Draw.MolsToGridImage(
    mols, molsPerRow=len(smiles), subImgSize=(400, 300), legends=smiles
)

img.save("smiles.png")

```

## 使い方
```
python draw_smiles.py 'Cc1cc(C)c(CCl)c(OCc2ccccc2Br)n1' 'Cc1cc(C)c(CCN)c(OCc2ccccc2Br)n1'
```
【Reray】ngrokっぽいサービスを作ってみた

# Reray
ngrokっぽいサービスを作ってみたので紹介したいと思います。

# なんで作ったの？
ngrokはフリープランだとサブドメインがランダムに割り当てられる（固定化できない）ため、毎回URLを打ち直したり共有し直す必要があり若干めんどい。あとサブドメインがそこそこ長い。
Rerayは**好きなサブドメインを1つ取得できる**ため、上記のような煩わしさから解放されます。

# 公式サイト
[https://reray.net](https://reray.net)

# サービス名
Reray
※relayではないです。造語です。短くてなんとなくそれっぽい名前にしてみました。

読み方：リレー

# Rerayの仕組み
![structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2711406/09fc5e6c-c583-9d93-b245-b6e55499ba0e.png)

# なにができるの？
ローカルで稼働しているサーバーや開発中のWebアプリケーションなどをインターネット上へカンタンに公開することができるサービスです。
サーバーやドメインの契約なしに開発中アプリのスマホ実機確認などがサクッとできるようになります。

# 使い方
導入は簡単です。
ただ、前提としてJava8（1.8）以上がインストールされている必要がありますので、インストールされていない場合はお手数ですが以下のサイトからダウンロード＆インストールをお願いします。
[https://www.java.com/ja/download/](https://www.java.com/ja/download/)

1. ユーザー登録
Rerayを利用するためにはユーザー登録が必要です。登録する際に固定のサブドメイン名を1つ指定してください。
ここで指定したサブドメイン名があなたのサブドメインになります。
https://{指定したサブドメイン名}.reray.net/

2. Rerayアプリのダウンロード＆インストール
ダウンロードリンクからRerayアプリをダウンロードし展開してください。
今はWindowsバージョンのみです。あしからず。

3. 初期設定
初回のみ以下のコマンドを実行して設定ファイルを生成します。
`>reray -e {メールアドレス} -t {認証トークン}`
このコマンドはユーザー登録後のマイページに記載されていますので、コピペして実行してください。

4. 実行
ローカルアプリケーションを8080ポートで実行している場合のコマンドです。（http://localhost:8080）
`>reray -p 8080`
ポート番号はアプリケーションのポートに合わせて適宜変更してください。

以上でやることは終わりです。**2回目以降は「4のコマンド」を実行するだけ**で使用可能です。

それではローカルのアプリケーションを起動してアクセスしてみましょう。
https://{指定したサブドメイン名}.reray.net/

# 停止
アプリを終了する際は「q」を入力するか、`Ctrl + c`を押してください。

# Basic認証
Basic認証を設定することもできます。
-aオプションで指定できます。
`>reray -p 8080 -a xxxx:yyyy`
xxxxがユーザー名、yyyyをパスワードに置き換えてください。

# 最後に
サービス開始して間もないですので、現在はユーザー登録数に上限を設けております。

是非よろしかったら使ってみてください。
GNSS、IMUのをROS2で利用。自己位置推定（備忘録）

## 環境
ros2 galactic
f9p
TR-IMU1647X

## 参考(上から順に一通りやると理解が深まる）
https://automaticaddison.com/how-to-create-a-simulated-mobile-robot-in-ros-2-using-urdf/
https://automaticaddison.com/set-up-the-odometry-for-a-simulated-mobile-robot-in-ros-2/
https://automaticaddison.com/sensor-fusion-using-the-robot-localization-package-ros-2/
https://automaticaddison.com/set-up-lidar-for-a-simulated-mobile-robot-in-ros-2/
https://automaticaddison.com/navigation-and-slam-using-the-ros-2-navigation-stack/
https://automaticaddison.com/how-to-use-gps-with-the-robot-localization-package-ros-2/

## 参考をやってわかったこと（あってるのかは不明）、実施したこと
かなり大まかに
basic_mobile_robotパッケージはスモールタウンワールドのシミュレーション環境を構築し、
robot_localizationパッケージのekf_nodeとnavsat_transform_nodeを使って、
/fix/gps、/imu/data、wheel/odometryのトピックを入れ込み、拡張カルマンフィルター(ekf)で自己位置推定している。
そのため、
/fix/gpsと/imu/dataを実際の機器から出力されるトピック/fix、/imu/data_rawに変えた

## 比較
シミュレーション↓↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378923/dcf4b5cf-94b0-d074-c700-cdb8e49ab1b4.png">
実際の機器↓↓
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2378923/d486da57-aa48-f731-4b37-8e04af11972b.png">

### 上図を要約すると
シミュレーションと比較して実際の機器になると
・gpsデータは、f9p(ubloxノード）から出力される/fixトピックにかわっている
・imuデータは、adiノードから出力される/imu/data_rawトピックにかわっている

(上図でしめしたいことの詳細)
〇GNSSの流れに焦点
・シミュレーション
/basix_mobile_bot_gpsノード　→　/gps/fixトピック → /navsat_transformノード　→　/odometry/gpsトピック　→　/ekf_filter_node_mapノード
・実際の機器
/ublox_gps_nodeノード　→　/fixトピック→/navsat_transformノード　→　/odometry/gpsトピック　→　/ekf_filter_node_mapノード

〇imuの流れに焦点
・シミュレーション
/imu/basic_mobile_bot_imuノード　→　/imu/dataトピック　→　/ekf_filter_node_map＆/ekf_filter_node_odomノード
・実際の機器
adi_rcv_csv_nodeノード　→　/imudata_rawトピック　→　ekf_filter_node_map＆/ekf_filter_node_odomノード


### なにしたか
### ①gps、imu、wheel_odomによる自己位置推定のシミュレーション環境の構築
 参考リンクの上から順に実施
 はしょりたい人はこちら→リンク（記事作成中）
### ②f9pの値をROS2上に。/fixトピックで出力される
 https://qiita.com/m2_labo/items/1ddf05a03a1871ff4a28
### ③TR-IMU1647Xの値をROS2上に。/imu/data_rawで出力される
 https://qiita.com/m2_labo/items/db32061bf3cbfd8f721e
### ④シミュレーション環境の設定を変更
 https://qiita.com/m2_labo/items/6eab3202289220aadd5f


リンクは随時更新します。
【備忘録】Raspberry Pi の立ち上げ時にMAVProxyを自動で起動する

## はじめに
これまで MAVProxy を介して複数の端末に情報を転送したり、RTK情報を転送する方法について試してきました。
MAVproxy経由でRTK情報を転送することでQGCでNTRIPが使えるようになり便利なツールだなと思いましたが、ターミナルからコマンドを打ち込んでいくのが手間になってきました。
もしRaspberry Pi の立ち上げ時にMAVProxyを自動で起動できれば楽なのにな。。。
という所から挑戦した次第です。

参考にしたのはこちらです。

https://github.com/mustafa-gokce/ardupilot-software-development/blob/main/mavproxy/automated-forwarding-services.md

解説もしっかりされていますので、ソフトかじったことのある人であればイメージしやすいかと思います。

## 手順
1.設定ファイルを作成する
2.スクリプトに権限を付与する
3.パスをコピーする
4.設定ファイルが動くか確認する
5.起動時に動くように設定する
6.再起動して確認する
## 1. 設定ファイルを作成する
:::note warn
権限の関係でうまく動かなかい場合がありますので注意が必要です。
私の場合はユーザー権限にして パスを`/home/pi/.local/bin/` としたら上手く動きました。
:::

`sudo nano ~/startup_mav.sh` で起動スクリプトを作成します。
記入が終わりましたら `ctrl`+`X` ⇒ `Y` で保存します

#### startup_mav.sh
```
#!/bin/bash
export LOCALAPPDATA="LOCALAPPDATA"
screen -L -Logfile vehicle_proxy.log -S vehicle_proxy -d -m bash -c "/home/pi/.local/bin/mavproxy.py --force-connected --master=/dev/ttyACM* --out=AAA.AAA.AA.A:14550 --out=BBB.BBB.BB.B:14550"
```

起動時に NTRIP サーバへアクセスしたい場合は以下を追加します。
```
--cmd='module load ntrip;ntrip set casterXXX;ntrip set mountpoint XXX;ntrip set username XXX;ntrip set password XXX;ntrip start'
```

::: note
※ 名称は`startup_mav.sh`としていますが自由につけてください
※ AAA.AAA.AA.AはPCのIPを記載します
※ BBB.BBB.BB.BはスマートフォンのIPを記載します
※ ttyACM*はautopilotと接続しているシリアルポートを記載します
　　確認方法 ⇒ https://qiita.com/m2_labo/items/7823fb26197fa5f5ff23
:::
::: note
NTRIP情報
XXXXはNTRIP配信先の情報を入力します。
MAVProxy起動時に [こちら](https://qiita.com/Fumi_Saito/items/41ecb9e1cd11544d4d00) で紹介したコマンドを行自動的に実行しています。
:::

## 2. スクリプトに権限を付与する

参考元の（rc.localで動かす）方法
`sudo chmod +x ~/startup_mav.sh`

私の場合 crontab（ユーザー権限）で動かす為、以下の様にし対応しています。
`sudo chmod 777 startup_mav.sh`

## 3. パスをコピーする
`echo $(pwd)"/startup_mav.sh"`

## 4. 設定ファイルが動くか確認する
`bash startup_mav.sh`　等を打ち込んで起動時に通信が開始されるか確認する
パスが通っているので `./startup_mav.sh` でも走ると思われます

## 5. 起動時に動くように設定する
私の場合 `rc.local` (root権限)ではうまく走らなかったので、crontab（ユーザー権限）で動かしています。
こちらを参考に入力を行っています

https://qiita.com/karaage0703/items/ed18f318a1775b28eab4

`crontab -e` と打ち込むと編集ができます。
'@reboot /home/pi/startup_mav.sh` を最下段に追加します。

#### crontab
```
# To define the time you can provide concrete values for
# minute (m), hour (h), day of month (dom), month (mon),
# and day of week (dow) or use '*' in these fields (for 'any').
#
# Notice that tasks will be started based on the cron's system
# daemon's notion of time and timezones.
#
# Output of the crontab jobs (including errors) is sent through
# email to the user the crontab file belongs to (unless redirected).
#
# For example, you can run a backup of all your user accounts
# at 5 a.m every week with:
# 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/
#
# For more information see the manual pages of crontab(5) and cron(8)
#
# m h  dom mon dow   command

@reboot /home/pi/startup_mav.sh
```

## 6. 再起動して確認する
一度電源を落としてからしばらく放置させ電源を入れます。
起動が完了したタイミングで地上局を立ち上げると、自動的に接続が行われていれば成功です。
上手くいかない場合は、時間をおいて何回か試してください。
【InternetExplorer】IEに保存されたユーザーIDとパスワードがわからなくなったときの確認方法【サポート終了】

# IEサポート終了
InternetExplorer(IE)のサポート終了が6/15に終了したことは皆さんご存じかと思います。
Edge,Chrome等の別ブラウザへの移行はお済でしょうか。

# InternetExplorerに保存したパスワードがわからない
移行後にユーザーから多く問い合わせがあったのが、
**「自動入力(オートコンプリート)されていたIDとパスワードが入らない」**
というものです。
今回はそのIDとパスワードの簡単な確認方法をご紹介します。
なお、画像の中でお見せできない部分は塗りつぶしてありますのでご了承ください。

# 確認方法
### 1. 資格情報マネージャーを開く
まず、PC画面左下の虫眼鏡のマーク🔎をクリックし、入力部に「資格情報マネージャー」と入力します。
検索結果（図の赤枠部分）をクリックすると資格情報マネージャが起動します。
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362978/2b6c1444-9de9-9d87-07e7-bccb1536ae97.png)

### 2. 確認したいサイトのURLのリストを開く
資格情報マネージャーのWeb資格情報のページ内に表示されているURLから、
IDとパスワードを確認したいページのURLを探します。
そのURLの右側の▽（図の赤枠部分）をクリックすると詳細が表示されます。

![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362978/f01ab8bb-005d-960f-1759-39056561fea8.png)

表示された情報の中にある「ユーザー名」が調べたいIDになります。
次にパスワードですが、こちらは伏字になっておりそのままでは見ることができません。
![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362978/1c5ab34e-2a02-44a5-f4de-a3e053a906b0.png)

### 3. 非表示のパスワードを見えるようにする
非表示のパスワードの右側にある「表示」(前図の赤枠部分)をクリックすると
以下のようなダイアログが表示されます。
ここにWindowsのアカウントパスワード(Windowsにログイン時に入力するパスワード)を入力し、
「OK」をクリックします。
![図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362978/e5a72201-936e-286e-3af1-92678c091563.png)
すると、伏字部分が読めるようになり、パスワードが確認できます。
![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/362978/3ea08dfd-f091-c305-b679-fb41bb9d845c.png)

# 最後に
今回はInternetExplorerに保存されたIDとパスワードの簡単な確認方法をご紹介しました。
逆に言うと簡単に確認できてしまう(Chrome等の別ブラウザでも同様)ので、
**PCへの物理的アクセスやユーザー情報の管理には十分に注意してください。**
cscの作法 その194

# 概要
cscの作法、調べてみた。
練習問題やってみた。

# 練習問題
マウスイベントを使え。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/6abc3fd0-3658-878f-8829-a29e0a660223.png)


# サンプルコード


```
using System;
using System.Drawing;
using System.Windows.Forms;

class Form1 : Form {
	PictureBox pictureBox;
	Point draggedPoint;
	bool dragged;
	public Form1() {
		pictureBox = new PictureBox();
		pictureBox.SizeMode = PictureBoxSizeMode.AutoSize;
		pictureBox.Image = new Bitmap(@"ki.png");
		pictureBox.MouseDown += new MouseEventHandler(pictureBox_MouseDown);
		pictureBox.MouseUp += new MouseEventHandler(pictureBox_MouseUp);
		pictureBox.MouseMove += new MouseEventHandler(pictureBox_MouseMove);
		this.Controls.Add(pictureBox);
		this.BackColor = SystemColors.Window;
	}
	void pictureBox_MouseDown(object sender, MouseEventArgs e) {
		base.OnMouseDown(e);
		dragged = true;
		draggedPoint = e.Location;
	}
	void pictureBox_MouseUp(object sender, MouseEventArgs e) {
		base.OnMouseUp(e);
		dragged = false;
		draggedPoint = e.Location;
	}
	void pictureBox_MouseMove(object sender, MouseEventArgs e) {
		base.OnMouseMove(e);
		if (dragged)
		{
			pictureBox.Left += e.Location.X - draggedPoint.X;
			pictureBox.Top += e.Location.Y - draggedPoint.Y;
		}
	}
	[STAThread]
	static void Main() {
		Application.EnableVisualStyles();
		Application.Run(new Form1());
	}
}




```


以上。
Firefoxアドオン(拡張機能)で、Content scriptsのエラーが何故かコンソールに表示されない問題

また5周遅れ位の話ですが、とある事からFirefoxのAdd-onを作ろうとした際、Content scripts(content.js)が例外を出して止まっているっぽいのにコンソールにはエラーメッセージが表示されない事で対応に時間を食われた件について。



# 解決法

***方法1. `CTRL+SHIFT+J`、又はメニューで「ブラウザーコンソール」を開く。***
FirefoxではContent scriptsのエラーはこの**ブラウザーコンソール**ウインドウにのみ表示されます。普通のコンソールでは見れません。

***方法2. 全部try...catchで囲む***
content.js全体をtry...catchで囲めばcatchブロック内からエラーメッセージをconsole.logで出力する事ができます。

***方法3. デバッガの「例外で停止」にチェック***
デバッガの「ブレークポイント」以下にある「例外で停止」チェックボックスを有効にすれば、Content scriptsの例外にも反応します。


>参照:
[Console doesn't log js errors from content script - Stack Overflow](https://stackoverflow.com/a/60588749)



## 「ブラウザーコンソール」とは？
F12で開く通常のコンソール(Web コンソール)とは別物です。MDNによれば……

>ブラウザーコンソール - 開発ツール | MDN
https://developer.mozilla.org/ja/docs/Tools/Browser_Console
>ブラウザーコンソールは Web コンソール に似ていますが、ひとつのコンテンツタブではなくブラウザー全体に適用されます。
> (中略)
>ひとつのコンテンツタブの情報を記録するのではなく、すべてのコンテンツタブ、アドオン、ブラウザー自体のコードの情報を記録します。

違いは、全タブとその他の情報(アドオン含む)が一括で表示されるという事でしょうか。タブではなくウインドウとして表示されます。
Visual Studio CodeでAWS構成図やシーケンス図を含めたMarkdownを作成する

# はじめに

Visual Studio CodeでMarkdownを作成するときに、内部にdraw.ioで作成したAWS構成図やシーケンス図などを簡単に埋め込む方法をメモしておきます。最終的なイメージは以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708575/79d47ef9-1bd8-d6d6-7a17-fccb9d6e3a3a.png)


# 前提条件

- Visual Studio Codeをインストールしていること。
- Javaをインストールしていること。

# 事前準備

Visual Studio Codeで以下の拡張機能をインストールします。

- Markdown All in One
- Markdown Preview Enhanced
- Draw.io Integration
- PlantUML

(2023/11/07追記)
Markdown Preview Enhancedのアップデートに伴い、追加作業が必要になりました。
以下の記事を参考に対応ください。
https://qiita.com/kaz-matsubara/items/375e4481da5b83309125

# 記載方法


まずは適当にMarkdownファイルを作成します。ここではtest.mdとします。
Markdownの基本的な記載方法については割愛します。

```markdown:test.md
# サンプル資料

## AWS構成図

## シーケンス図

```

## AWS構成図

続いてAWS構成図を作成します。test.mdと同じディレクトリにtest.drawio.svgというファイルを作成します。
ファイル名に*.drawio.svgという拡張子を付けることでsvg画像として取り扱うこともでき、draw.ioで編集することもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708575/6eca873b-0f2b-f271-e424-a74dbf481b6c.png)

test.drawio.svgをVisual Studio Code上で開くと、draw.ioの作図画面が表示されます。適当にAWS構成図を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2708575/c753a527-29cf-a325-a189-6b9e2f08018c.png)

前述の通り、svg画像として取り扱うことができるため、test.mdに以下のように記載することで、Markdownの中にAWS構成図を埋め込むことができます。

```markdown:test.md
# サンプル資料

## AWS構成図

![test.drawio.svg](./test.drawio.svg)

## シーケンス図
```

draw.ioで作図できるものであれば、AWS構成図に限らずMarkdownの中に埋め込むことができます。

## シーケンス図

続いてシーケンス図を作成します。シーケンス図は別ファイルではなくMarkdownの中に直接記載することができます。
Markdownに以下のように記載することで、Markdownの中にシーケンス図を埋め込むことができます。

~~~markdown:test.md
# サンプル資料

## AWS構成図

![test.drawio.svg](./test.drawio.svg)

## シーケンス図

```plantuml
title サーバーレス環境でのAWSリソースアクセスフロー
actor Users
Users -> CloudFront: 1. Webサイトにアクセス
CloudFront -> S3: 2. 静的コンテンツ取得
S3 --> CloudFront: 3. HTML, Javascriptなどの静的コンテンツ
CloudFront --> Users: 4. ユーザー登録画面表示
Users -> APIGateway: 5. ユーザー登録ボタンクリック
APIGateway -> Lambda: 6. ユーザー処理呼び出し
Lambda -> DynamoDB: 7. ユーザーデータ登録
DynamoDB --> Lambda: 8. レスポンス
Lambda --> APIGateway: 9. レスポンス
APIGateway --> Users: 10. 登録完了メッセージ表示
```
~~~

上記のようにコードブロックで指定するやり方と、<span>@</span>startumlと<span>@</span>endumlで囲むやり方があります。
PlantUMLで表現できるものであれば、シーケンス図に限らずMarkdownの中に埋め込むことができます。

# おわりに

Visual Studio Code上でMarkdownの資料を作成することで、様々な拡張機能を利用することができます。他にも様々な拡張機能がありそうなので、便利なものは今後も追記していきたいと思います。
FITSファイルのテーブルの列をコピーしたい。

# 概要

この記事ではNASAのHEASARCが提供している高エネルギー解析ツール`ftools`を用いて、FITSファイル内のテーブルの列を別の列にコピーする方法です。別の方法があれば教えてくだされば助かります。

# 結論

`ftcalc`を用いて行う。

```bash
ftcalc 入力ファイル 出力ファイル コピーされる列名 コピーする列名
```

# 解説

このコマンドの[説明](https://heasarc.gsfc.nasa.gov/lheasoft/ftools/headas/ftcalc.html)によると

>'ftcalc' is a general table calculator utility that computes values for a table column based on an arbitrarily complex expression, which may be a function of the value of other columns or keywords in the table.
>'ftcalc' は、任意の複雑な式に基づいてテーブルの列の値を計算する一般的なテーブル計算機ユーティリティであり、テーブル内の他の列やキーワードの値の関数であってもよい。

とある。本来は計算式を与えて列の値を計算するコマンドみたいだがそのままにすればコピーになるのではと考えたがビンゴであった。
Dart で条件を満たしたものだけが入る型を作る

アカウント名は前後に空白が入らずに, 1文字以上 50文字以内である必要があるとする.

そういうときに, このように条件を満たしていることを保証する型 (class) を作成することによって条件を満たしているかの確認し忘れが減る.

```dart:account_name.dart
import 'package:meta/meta.dart';

/// アカウント名. 文字数条件を満たしていることを保証される
@immutable
class AccountName {
  final String value;

  const AccountName._(this.value);

  /// `String` からアカウント名を作成する. 条件を満たしていないものは `null` が返される
  static AccountName? fromString(final String raw) {
    final trimmed = raw.trim();
    if (trimmed.isEmpty || trimmed.length >= 50) {
      return null;
    }
    return AccountName._(trimmed);
  }

  /// String` からアカウント名を作成する. 条件を満たしていないものが渡されたときにはエラーが発生する
  static AccountName fromStringOrThrow(final String raw) {
    final normalized = fromString(raw);
    if (normalized == null) {
      throw Exception("$raw is invalid account name");
    }
    return normalized;
  }

  @override
  bool operator ==(final Object other) {
    return other is AccountName && value == other.value;
  }

  @override
  int get hashCode => value.hashCode;

  @override
  String toString() {
    return "AccountName($value)";
  }
}
```

# 解説
```dart
const AccountName._(this.value);
```
の部分で, private constructor を作成しているため, 外部のファイルから `AccountName` を直接作成することはできなくなっている

```dart:use.dart
import 'account_name.dart';

void main() {
  final rafyaAccountName = AccountName("rafya");
}
```

![68747470733a2f2f73746f726167652e676f6f676c65617069732e636f6d2f7a656e6e2d757365722d75706c6f61642f3430613135393030306464382d32303232303631372e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117764/65d7c60c-d684-acd5-8368-c614ebd4a16a.png)


このように使う
```dart
import 'account_name.dart';

void main() {
  final rafyaAccountName = AccountName.fromString("rafya");
  if (rafyaAccountName == null) {
    print("不正なアカウント名です");
    return;
  }
  print(rafyaAccountName.value); // rafya
}
```
M1 MacのPodmanでDb2コンテナを正常に稼働できなかった（2022/6/16時点） 

# 概要
M1MacのPodmanでDb2コンテナが動くか確認してみました。
Db2コンテナイメージは、arm64版がありません。（2022/6/16時点）
Podmanがエミュレートして、amd64（x64）版を正常に動かしてくれるのか気になるところです。

# 結果
2022/6/16時点では、動きませんでした。
* amd64版イメージの取得のために、--platform linux/amd64を指定するも、エミュレートできていないのか、コンテナ作成エラー。
* さらに、Podman Machineに「qemu-user-static」を導入してやってみると、コンテナ作成を成功するけど、DBのセットアップ処理でエラー。
* IBM DataManagement Community の投稿をみると、colimaで成功したとのメッセージがありましたが、私は確認していません。

2022/8/6 追記
データベースディレクトリを永続化できないという厄介すぎる制限付きですが、colimaを使ってDb2コンテナを稼働させることができました。
[M1 MaCでcolimaを使ってDb2コンテナの稼働に厄介な制限付きで成功](https://qiita.com/shimac/items/4866bf3f756f11b705a4)

# 最後に
* M1MacのPodmanでDb2を稼働できた方いればぜひ教えてください。

# 参考
[DockerHub > ibmcom/db2](https://hub.docker.com/r/ibmcom/db2/tags)
[IBM Data Management Community > DB2 LUW. 11.5.xx - Mac M1 ready](https://community.ibm.com/community/user/hybriddatamanagement/communities/community-home/digestviewer/viewthread?GroupId=2983&MessageKey=bf84e57b-263a-4ba5-a925-f13530c70e00&CommunityKey=ea909850-39ea-4ac4-9512-8e2eb37ea09a)
[GitHub > qemu-user-static](https://github.com/multiarch/qemu-user-static")
[Running amd64 docker images with Podman on Apple Silicon (M1)](URL "https://edofic.com/posts/2021-09-12-podman-m1-amd64/)



# 詳細
## Db2コンテナイメージをlinux/amd64指定で取得 -> コンテナ作成エラー
* --platform linux/amd64を指定しても、コンテナ作成自体失敗。
```
% podman run -itd --name mydb2 --platform linux/amd64 --privileged=true -p 50001:50000 -e LICENSE=accept -e DB2INST1_PASSWORD=password -e DBNAME=testdb -v /hogehoge:/database docker.io/ibmcom/db2
4d829e7cf29cfed162b1cde8ed544c28183efa39a7ca1581c6652b225a72adb7

% podman logs -f mydb2
{"msg":"exec container process `/var/db2_setup/lib/setup_db2_instance.sh`: Exec format error","level":"error","time":"2022-06-14T10:31:29.000747820Z"}
```

## PodmanMachineにqemu-user-staticを導入してコンテナ起動 -> DB作成できない
### PodmanMachineにqemu-user-staticの導入
```
$ podman machine ssh
$ sudo -i
# rpm-ostree install qemu-user-static
# systemctl reboot
```
### Db2コンテナ起動
* コンテナは起動するけど、セットアップ処理で、db2のコマンドが、command not foundでエラー。インスタンスまではできているけど、DBができない。
```
% podman run -itd --name mydb2-2 --platform linux/amd64 --privileged=true -p 50002:50000 -e LICENSE=accept -e DB2INST1_PASSWORD=password -e DBNAME=testdb -v /hogehoge-2:/database docker.io/ibmcom/db2

$ podman logs -f mydb2-2
For more information see the DB2 installation log at "/tmp/db2icrt.log.196".
DBI1070I  Program db2icrt completed successfully.


/bin/bash: db2stop: command not found
(*) Cataloging existing databases
/usr/bin/ls: cannot access /database/data/db2inst1/NODE0000: No such file or directory
(*) Applying Db2 license ...
```
Firebase Authentication v9 メソッドの書き方

Firebase バージョン9以降では、必要なメソッドだけimportすることになり、Authenticationに関わる記述方法も変わったようです。
v9を使っていて、v8以前の記述をするとエラーが出てしまいます。

## 設定ファイル　firebase.js
設定ファイルの記述方法は以下のように変わっています。

`firebase.auth();`
↓
`getAuth();`

```js:従来
import firebase from 'firebase/app';
import 'firebase/auth';

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGE_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_SENDER_ID,
};

firebase.initializeApp(firebaseConfig);
export const auth = firebase.auth();
```

```js:v9
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth'

const firebaseConfig = {
  apiKey: process.env.REACT_APP_FIREBASE_API_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_MESSAGE_SENDER_ID,
  appId: process.env.REACT_APP_FIREBASE_SENDER_ID,
};

initializeApp(firebaseConfig);
export const auth = getAuth();
```

## ユーザー登録（サインアップ）
以下のように記述方法が変わっています。エラーの受け取りは従来通り。
`auth.createUserWithEmailAndPassword(email, password)`
↓
`createUserWithEmailAndPassword(auth, email, password)`

```js:従来
auth.createUserWithEmailAndPassword(email, password)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    // ..
  });
```

```js:v9
import { createUserWithEmailAndPassword } from "firebase/auth";
// 略
createUserWithEmailAndPassword(auth, email, password)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
    // ..
  });
```

## ログイン（サインイン）

`auth.signInWithEmailAndPassword(email, password)`
↓
`signInWithEmailAndPassword(auth, email, password)`

```js:従来
auth.signInWithEmailAndPassword(email, password)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
  });
```
```js:v9
import { signInWithEmailAndPassword } from 'firebase/auth'
// 略
signInWithEmailAndPassword(auth, email, password)
  .then((userCredential) => {
    // Signed in
    const user = userCredential.user;
    // ...
  })
  .catch((error) => {
    const errorCode = error.code;
    const errorMessage = error.message;
  });
```

## ログアウト（サインアウト）
```js:従来
auth.signOut();
```
```js:v9
import { signOut } from 'firebase/auth'
// 略
signOut(auth);
```

## 参考
[公式ドキュメント](https://firebase.google.com/docs/auth/web/start)
PHP 列挙型（enum）と値に依存した列挙型（backed enum）

## 概要

- PHP8.1から追加された列挙型（enum）について定義方法や使い方をまとめてみた。

## 定義方法（backed enum）

- クラスのように定義する。
- 例えば「aaa」と「bbb」の2つの値をstr1、str2としてそれぞれ列挙したいとき下記のように定義する。
    - 列挙型名: StrEnum

    ```php
    enum StrEnum: string
    {
        case str1 = 'aaa';
        case str2 = 'bbb';
    }
    ```

## 列挙した値を呼び出す（backed enum）

- 下記のように記載することで値を呼び出す事ができる。

    ```php
    列挙型名::キー名->value
    ```

- 今回のStrEnumの「aaa」を取得したい場合下記のように記載する。

    ```php
    enum StrEnum: string
    {
        case str1 = 'aaa';
        case str2 = 'bbb';
    }

    echo StrEnum::str1->value;
    ```

## 列挙下キー名を呼び出す（enum, backed enum）

- 下記のように記載することでキー名を呼び出す事ができる。

    ```php
    列挙型名::キー名->name
    ```

- 今回のStrEnumの「str1」を取得したい場合下記のように記載する。

    ```php
    enum StrEnum: string
    {
        case str1 = 'aaa';
        case str2 = 'bbb';
    }

    echo StrEnum::str1->name;
    ```

## 注意点

- 列挙型は列挙した値（キーやそれに紐づく値）の名前がついたオブジェクト（シングルトンオブジェクト）
    - オブジェクトなので`<`や`>`では比較できない。これを行うと必ずfalseが返る。
    - 公式によると下記の様になるらしい。

        ```php
        $a = StrEnum::str1;

        $a instanceof StrEnum; // true
        ```

## 参考文献

- [https://www.php.net/manual/ja/language.enumerations.php](https://www.php.net/manual/ja/language.enumerations.php)
- [https://www.php.net/manual/ja/language.enumerations.backed.php](https://www.php.net/manual/ja/language.enumerations.backed.php)
- [https://blog.capilano-fw.com/?p=9829](https://blog.capilano-fw.com/?p=9829)
- [https://qiita.com/ucan-lab/items/e9f53aa024ca3cc5ea1b](https://qiita.com/ucan-lab/items/e9f53aa024ca3cc5ea1b)
【Swift】イベント通知のデリゲートパターンについて  



# はじめに
複数のオブジェクトが互いにイベント通知を行う場合、やみくもにオブジェクト同士を参照させ合えば良いわけではありません。なぜなら、依存関係が複雑になりすぎてメンテナンスが不可能になってしまったり、メモリリークが発生してしまったりする可能性があるからです。
そこでイベント通知を行う方法として、**デリゲートパターン、クロージャ、オブザーバパターン**の３つの方法が主に扱われます。今回はそのうちのデリゲートパターンについて解説したいと思います。

# デリゲートパターンとは
デリゲートパターンはデリゲート(委譲)という名前の通り、**あるオブジェクトの処理を別のオブジェクトに代替させるパターン**です。デリゲート元のオブジェクトは適切なタイミングで、デリゲート元のオブジェクトにメッセージを送ります。デリゲート先のオブジェクトはメッセージを受けて、自分自身や別のオブジェクトの状態を変更したり、何かしらの結果をデリゲート元のオブジェクトに返したりします。

### メリット
デリゲートパターンを用いると、デリゲート先のオブジェクトを切り替えることでデリゲート元の振る舞いを柔軟に変更できます。
### デメリット
一方で、必要な処理をプロトコルとして事前に宣言されている必要があり、記述するコードが多くなりがちです。

# 実装方法
デリゲートパターンでは、委譲する処理をプロトコルのメソッドとして宣言します。デリゲート先のオブジェクトはそのプロトコルに準拠し、デリゲート元のオブジェクトからの処理の委譲に応えられるようにします。**デリゲート元のオブジェクトはデリゲート先のオブジェクトをプロパティとしてもち**、デリゲート先のメソッドを実行して処理を委譲します。

例として、以下のようなサンプルアプリを紹介します。
内容としては、NextButtonを押すと次の画面に遷移し、textFieldに文字を記入してSaveボタンを押すと、元の画面のLabelに文字を表示するようになっています。
![demo](https://gyazo.com/ae78a0d66cb6f91a163db02421a1a817/raw)

以下がコード内容です。
```swift: ViewController.swift
protocol NextViewControllerDelegate: AnyObject {
    func didSave(text: String)
    func didCancel()
}

class ViewController: UIViewController {

    @IBOutlet private weak var textLabel: UILabel!

    private func presentNextViewController() {
        let nextVC = UIStoryboard(name: "Next", bundle: nil).instantiateInitialViewController()
        as! NextViewController

        nextVC.delegate = self

        let nav = UINavigationController(rootViewController: nextVC)
        present(nav, animated: true)
    }
    
    @IBAction func nextButton(_ sender: Any) {
        presentNextViewController()
    }
}

extension ViewController: NextViewControllerDelegate {
    func didSave(text: String) {
        textLabel.text = text
        dismiss(animated: true)
    }

    func didCancel() {
        dismiss(animated: true)
    }
}
```
```swift: NextViewController.swift
class NextViewController: UIViewController {

    weak var delegate: NextViewControllerDelegate?
    
    @IBOutlet private weak var textField: UITextField!
    
    @IBAction func saveButton(_ sender: Any) {
        guard let text = textField.text else { return }
        delegate?.didSave(text: text)
    }

    @IBAction func cancelButton(_ sender: Any) {
        delegate?.didCancel()
    }
}
```

# 利用するべき時
### ⑴２つのオブジェクト間で多くの種類のイベント通知を行う
２つのオブジェクト間で多くの種類のイベント通知を実現したい場合、デリゲートパターンはたいへん有効です。非同期処理中に発生するイベントに応じて実行する関数を切り替えたい場合などはデリゲートパターンを利用するのが良いでしょう。

### ⑵外部からのカスタマイズを前提としたオブジェクトを設計する
オブジェクトの中には外部からのカスタマイズを前提とした設計が適しているものがあり、そのようなケースではデリゲートパターンを採用するのが良いでしょう。
デリゲートパターンでは、カスタマイズ可能な処理をプロトコルとして定義するため、オブジェクトのどの振る舞いがカスタマイズ可能かは明らかです。例えばUITableViewクラスの場合、画面によって異なるセルの選択時の動作をデリゲート先に委譲できるようにしています。