OmniAICreator/Qiita-1.07M huggingface dataset:
Amazon Athenaってデータ基盤として活用するのはどんな感じ？

#はじめに
これは[datatech-jpのアドベントカレンダー](https://qiita.com/advent-calendar/2021/datatech-jp)の17日目の記事です。

２年弱Athenaを触ってきたので、運用してて注意していたところなどを共有できればと思います。

#記事の対象者
Amazon Athenaを使ってみようと思っている人

#Athenaの概要
##Amazon Athenaって何？
AWSのS3に保存されているファイルに対してSQLでデータを操作できるサービスです。
S3にcsvやparquetファイルなどを配置するとSQLで操作できるのでとっても便利で簡単です。

https://aws.amazon.com/jp/athena/features/

##料金について
料金はクエリーに対する課金です。
5USD/TBなので0.5TBスキャンしたらなら
0.5*5 = 2.5USD となります。

https://aws.amazon.com/jp/athena/pricing/

あとはGlueのデータカタログやS3の料金などは別途かかりますのでご注意を。

#Athenaの運用して思ったこと3選

* クエリーのスキャン量を抑えること
* S3に保存するファイルはcsvではなくparquetにしよう
* さまざまなデータソースと統合してデータ基盤として作るのはちょっとしんどい

##クエリーのスキャン量を抑えること
これまず基本中の基本かと思いますが、スキャン量でお金がかかってくるのでみんなコストは下げたいですよね。
そうなるとスキャン量を抑えるための工夫が必要になります。工夫を書こうと思ったんですが....

流石のAWSさん。公式で書いてくれているんですよね。もうこれを読んだらバッチリだと思います。

https://aws.amazon.com/jp/blogs/news/top-10-performance-tuning-tips-for-amazon-athena/

##S3に保存するファイルはcsvではなくparquetにしよう
例えばETL処理で、Pythonで前処理したデータをcsvでS3にアップロードしたとします。そのあとGlue Crawlerでテーブル化すると、csvだとうまく型の予測がされない時があります。実際にどういうことが起きたかというと、毎月csvを吐き出すスクリプトを書いたとして、2020-01.csvというcsvの中にIDという列があったとします。このID列はたまたま2020-01のデータにはない状態でした。その際Glueでクローリングするとnull扱いにするのですがデータ型はstringと判断します。2020-02になって新しくcsv吐かれてID列に整数が入ってきた場合にstringで認識されたままなので思っていた変換ができなかったりします。



ということがあり、もしS3に定期的にファイルを出力するようなことがあればparquetにして型を整えたのちに出力するのがおすすめです。

##さまざまなデータソースと統合してデータ基盤として作るのはちょっとしんどい
データ基盤を構築する上で重要な点として、簡単に色々なデータソースと繋げられてデータを投入できることだと思うのですが、巷のdata ingestion toolなど見るとAthenaに綺麗に対応しているツールってあんまり見かけないんですよね。S3に入れればなんとかできるんですが、JSONファイルを加工しないと厳しかったり、データレイクに入れる前に前処理が必要だったりして、そこがつらみだったりします。

BQが裏やま案件になるやつっすね。

# Athenaをデータ基盤として利用するケース
個人的な意見ではありますが以下の点でAthenaを採用するのは良いと思います。

* AWS上に会社のデータを保有している
* Redshiftほど大きなデータではなくコストをあまりかけたくない
* 運用コストも下げたいのでマネージドサービスを利用したい
* お試しでデータ基盤をサクッと作りたい

#感想
Amazon Athenaについてすごく簡単に書いた記事でした。実際他にも色々とあったような気がしたんですがちょっとど忘れしてしまいましたw。やっぱり定期的にブログ等でアウトプットせねばダメっすね。
Athenaはとても扱いやすく素早くかつ低コストでデータ基盤を実現できます。その一方でさまざまなSaaSとの連携がしづらいというのが現時点でのデメリットかと思います。データ量も増えてくると実行結果が返ってくるまでに時間がかかったりとパフォーマンスチューニングを考えなきゃいけない点も気をつけないといけないです。

個人的な意見ですが、AWS上にサービス展開している会社でデータ基盤をちょっと構築してみたいというフェーズでは素早く作れるので良いと思います。AWSが出している[Quicksight](https://aws.amazon.com/jp/quicksight/)をつかってデータの可視化も容易なので合わせて使うとなお良いと思います。またデータガバナンスに必要なデータアクセス管理については[AWS Lake Formation](https://aws.amazon.com/jp/lake-formation/)を使って、Athenaの管理をするのがおすすめです。

話は変わりますが、AthenaのようにRedshiftが使えそうなRedshift Serverlessが発表されましたね。Athenaよりもっとデータ量が増える場合はRedshift Serverlessを利用するイメージなんですかねぇ。今後が楽しみです。（以下クラスメソッド様の記事）

https://dev.classmethod.jp/articles/20211201-amazon-redshift-serverless-release/
FlutterのTextFieldのデザインを自由自在に変更する

# はじめに
Flutterでテキスト入力をさせる場合、`TextField`（もしくは`TextFormField`）を使用しますが、`OutlineInputBorder`を使用するとPaddingがかなり大きめに取られてしまい、`contentPadding`に`EdgeInsets.zero`を指定してもPaddingは消えてくれません。

そこで、**`InputDecoration.collapsed`**を使うことでPaddingを無くし、`Container`でラップしてデザインする方法を考えました。
`InputDecoration.collapsed`はサイズが入力フィールドと同じになり（Paddingが0）、出来る装飾が限られます。

# 元になる`TextField`

```dart
TextField(
  controller: _textController,
  decoration: InputDecoration(
    filled: true,
    fillColor: Colors.grey,
    border: OutlineInputBorder(
      borderRadius: BorderRadius.circular(4),
      borderSide: const BorderSide(
        width: 0,
        style: BorderStyle.none,
      ),
    ),
    prefixIcon: const Icon(Icons.search),
    hintText: '検索',
  ),
)
```

今回は、枠線なしで縁は丸くします。

# `Container`でデザインする

```dart
Container(
  padding: const EdgeInsets.all(10),
  decoration: BoxDecoration(
    color: Colors.grey,
    borderRadius: BorderRadius.circular(4),
  ),
  child: Row(
    children: <Widget>[
      const Icon(Icons.search),
      const Padding(padding: EdgeInsets.only(right: 8)),
      Expanded(
        child:TextField(
          decoration: const InputDecoration.collapsed(
            hintText: '検索',
            border: OutlineInputBorder(
              borderSide: BorderSide(
                width: 0,
                style: BorderStyle.none,
              ),
            ),
          ),
        ),
      ),
    ],
  ),
)
```

filledやfillColorは`Container`で行うので消します。
`InputDecoration.collapsed`ではprefixIconを付けることができないので、`Row`で`Icon`と`TextField`を横に並べます。また、アイコンが近すぎるので`Padding`で調整します。
`TextField`は `Expanded`でラップしないとダメです。

とりあえずこれで好きなデザインに出来るようにはなりましたが、`Container`部分や`Icon`部分をタップしても`TextField`にフォーカスが移らないため、めちゃくちゃ押しにくくなってしまいました。
なので、さらに`GestureDetector`でラップしてonTapでフォーカスを渡すようにします。

#最終的なコード

```dart
GestureDetector(
  onTap: () => _focusNode.requestFocus(),
  child: Container(
    padding: const EdgeInsets.all(10),
    decoration: BoxDecoration(
      color: Colors.grey,
      borderRadius: BorderRadius.circular(4),
    ),
    child: Row(
      children: <Widget>[
        const Icon(Icons.search),
        const Padding(padding: EdgeInsets.only(right: 8)),
        Expanded(
          child:TextField(
            focusNode: _focusNode,
            decoration: const InputDecoration.collapsed(
              hintText: '検索',
              border: OutlineInputBorder(
                borderSide: BorderSide(
                  width: 0,
                  style: BorderStyle.none,
                ),
              ),
            ),
          ),
        ),
      ],
    ),
  ),
);
```

だいぶコードが長くなってしまったので、StatelessWidgetなどにして使いまわしやすいようにしたほうが良いかもしれないですね。
演算子いろいろ

論理積	&&	
x && y	：左辺も右辺もtrueの場合にtrue（左辺 かつ 右辺）
論理和	||	
x || y	：左辺か右辺のどちらかがtrueの場合にtrue（左辺 または 右辺）
論理否定	!	
!x　	：xがtrueの場合にfalse、xがfalseの場合にtrue
【C#】CSVを1行ずつ読み込む方法

# 概要
C#でCSVファイルを1行ずつ処理したい時に少しハマったので、備忘録的にを残しておきます。

# StreamReaderを利用する方法
これはググって最初に出てきた方法です。
C#でファイルを読み込む際は[StreamReader](https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamreader?view=net-5.0)を使うのが普通のようです。
1行ずつ文字列として読み込んでカンマでsplitすれば要素を取得できます。
Program.CSと同じ階層に以下のようなCSVファイルがあるとします。

```text:input.csv
A1,A2,A2
B1,B2,B3
```

```cs:Program.CS
using System;
using System.IO;

namespace TestReadingCsv
{
    class Program
    {
        static void Main(string[] args)
        {
            using (StreamReader reader = new StreamReader(@"..\..\..\input.csv"))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    string[] values = line.Split(',');
                    foreach (string value in values)
                        Console.WriteLine(value);
                }
            }
        }
    }
}
```

```text:結果
A1
A2
A2
B1
B2
B3
```

## 問題点
しかし、この方法だと要素内にカンマや改行が含まれている時に期待する結果にはなりません。
また、CSVがダブルクォートで囲まれている形式だった場合にいちいち取り除く必要があり不便です。
ましてや、行によってダブルクォートが有ったり無かったりしたら面倒極まりないでしょう。

```text:input.csv
A1,"カンマ,が含まれた行",A2
"B1","改行
が含まれた行","B3"
```

```cs:Program.CS
using System;
using System.IO;

namespace TestReadingCsv
{
    class Program
    {
        static void Main(string[] args)
        {
            using (StreamReader reader = new StreamReader(@"..\..\..\input.csv"))
            {
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    string[] values = line.Split(',');
                    int i = 1;
                    foreach (string value in values)
                        Console.WriteLine(string.Format("{0}: {1}", i++, value));
                }
            }
        }
    }
}
```

```text:結果
1: A1
2: "カンマ
3: が含まれた行"
4: A2
1: "B1"
2: "改行
1: が含まれた行"
2: "B3"
```


# TextFieldParserを利用する方法
このようなケースにぴったりのクラスがありました。それが[TextFieldParser](https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.visualbasic.fileio.textfieldparser?view=net-5.0)です。
要素内のカンマや改行に対応しています。また、ダブルクォートの有無が混在していても問題ありません。
（こっちを先に見つけていれば困ることもなかったのに・・・）

```cs:Program.CS
using System;
using Microsoft.VisualBasic.FileIO;

namespace TestReadingCsv
{
    class Program
    {
        static void Main(string[] args)
        {
            using (TextFieldParser txtParser = new TextFieldParser(@"..\..\..\input.csv"))
            {
                txtParser.SetDelimiters(",");
                while (!txtParser.EndOfData)
                {
                    string[] values = txtParser.ReadFields();
                    int i = 1;
                    foreach (string value in values)
                        Console.WriteLine(string.Format("{0}: {1}", i++, value));
                }
            }
        }
    }
}
```

```text:結果
1: A1
2: カンマ,が含まれた行
3: A2
1: B1
2: 改行
が含まれた行
3: B3
```

まさにこれって感じですね。
区切り文字をタブ文字にすればTSVにも対応できます。
その他にもいくつか設定値があるので、うまく使いこなせばCSV以外のフォーマットでも使えそうです。
bladeからreactにデータを渡す

この記事は
難解なやり方から簡単なやり方に大きくリファクタリングできたため、
2021-12-5に丸ごと書き換えました。

#　サンプルの概要
Reactにカレンダーコンポーネントがあって、そこにlaravelからbladeを通してデータをpropさせるとする。

#controller

普通にモデルから全件取得してviewへ渡す。

```MeetingController.php
use App\Models\Meeting;

        $meetings = Meeting::all();

        return view('web.top.index')->with(compact(
            'meetings',
        ));
```

#blade側

idはReact.rendar()で使うもの。
data-hoge-huga の形の属性にしておくと jsからdataset.hogeFuga の形(HTMLのケバブケースがjsで勝手にキャメルケースに変換される)でアクセスできるので、ここに通常のblade記法でモデルのデータを流し込む。

```meeting.blade.php
    <div id="calendar" data-calendar-meetings="{{ $meetings }}"></div>
```

#React側

rendarメソッドに直接 getElementByIdを書かずに、ソースコードの上の方に先に定義しておく。
それで取得したDOMからさっきbladeで書いたdatasetが取れる。 

取得しただけだと、シリアライズされた文字列型のjsonなので、JSON.parseメソッドでオブジェクト型に変換する。
あとはmeetings.title みたいな感じでjsとして使える。

```calendar.tsx
import React from 'react';
import { render } from "react-dom";

const targetDom = document.getElementById('calendar');
const str_meetings = targetDom?.dataset.calendarMeetings;
const meetings = JSON.parse(str_meetings ?? '');

export default function Calendar() {
  return (
    <>
      ~ 生成したいDOMのjsx  ~
    </>
}

render(<Calendar/>,targetDom);

```
Ubuntu 20.04 LTSでログイン画面が表示されなかったのを直した

# 事の始まり

CentOS 8で運用していたPCのOSを，サポートが終了することに伴って，Ubuntuに入れ替えることになった．
CentOSとマシンの相性か分からないが，フリーズしてしまうことも多く，電源を落とさざるを得ない場面もしばしばあった．

## 環境
・AMD EPYC CPU 16core 7302 3.0GHz x 2
・Ubuntu 20.04 LTS

(2023/01/20 追記) 2022年12月にUbuntu 22.04 LTSでも、同様の症状が出た際に、この方法で解決した。


## 起こったこと

UbuntuのLiveUSB(ブータブルUSB)を作成し，それをマシンに差してUbuntuをインストールした．

その後，再起動すると画面が真っ暗なままで，ログイン画面が出てこなかった．

## とりあえず色々見てみた

そのPCでは```Ctrl + Alt + F1```でGUI，```Ctrl + Alt + F2~F6```でCUIが表示された．
```Ctrl + Alt + F1```を押すとGUIは出てくるが，ログインしようとして```Enter```を押したり，マウスをユーザー名に重ねるとGUIが落ちてしまった．何度もやっているとフリーズしてどうにもならなくなる．

CUIは問題なく起動し，ログインもできた．コマンドも実行できた．

## 先に結論

結論から言うと，以下の2点が原因であった可能性が高い．

1. ログインの試行錯誤の過程で，電源ボタンで強制シャットダウンをしたことにより，GUI関連の重要なファイルである```xorg.conf```というファイルが壊れてしまったこと．

2. これまで用いられていたX-windowというウインドウドライバから変更された，Waylandという新しいウインドウドライバが悪さをしていたこと．

全く知識がない状態から調べまくって色々試したので，どれが効いたのか分からない．
なので，以下では試した過程の全てを記載する．

## 1への対処法

真っ暗な画面で，```Ctrl + Alt + F2~F6```を押し，CUIでログイン．

以下で，gdm(gnome display manager)というパッケージを再インストール．

```
$ sudo apt install ubuntu-desktop
$ sudo apt install --reinstall gdm3 ubuntu-desktop gnome-shell
```

多分これでは直っていない．

次に```xorg.conf```を修正する．

まずはrootログイン．

```
$ sudo su -
```

その後，```xorg.conf```が存在するかを確認する．

```
# ls /etc/X11/xorg.conf
```

これを入力してパスが出れば```xorg.conf```が存在することになる．
自分は存在しなかったので```xorg.conf```を再生成し，元の場所においてあげる必要があった．

rootユーザーのまま

```
# Xorg -configure
# mv -f /root/xorg.conf.new /etc/X11/xorg.conf
```

と入力．一行目で```xorg.conf.new```というファイルを生成し，二行目でそれの名前を```xorg.conf```に変更しながら元の場所へ戻す．

原因1のせいだった人はこれで直ると思う．

## 2への対処法

1で直らなかった人は2の可能性がある．CUIで編集ができるエディタ(自分はviを使用した)を起動し，gdm3というパッケージの```custom.conf```というファイルを編集する．

```
# vi /etc/gdm3/custom.conf
```

この中に，

```custom.conf
#WaylandEnable=false
```

というコメントアウトされた行がある．この先頭の```#```を外し，Waylandを使用しないようにする．

保存し終了したら，

```
# sync
# sync
# reboot
```

で同期，再起動を実行する．

自分はこれで直った！

## 参考

https://falconblog.org/ubuntu18-04-lts-login-display/

他にもあったが，全部リンク切れ．
一つのレコードから複数の列を新たに追加

#概要#

データの処理の際にレコード内に４つの情報が入っていてそれを取り出す必要があったので備忘録として記録しておきます。（前回はJSONフォーマットでしたが今回は半角スペース区切りでデータが入力されていることが前提）


| id | Jan | Feb|
|:-----------|------------:|:------------:|
| 1     | 1位 大阪府 12,345 67% | 1位 大阪府 11,111 22%|
| 2     | 2位 京都府 98,765 43%| 2位 京都府 22,222 33% |

こんな感じで空白区切りでレコード内にデータがいくつか入っているものを

| id | 1月順位| 都道府県|数| 割合|2月順位|都道府県|数|割合|
|:-----------|------------|:------------:|:------------:|:------------:|:------------:|:------------:|:------------:|:------------:|
| 1     | 1位 |大阪府|12,345|67%| 1位 | 大阪府|11,111|22%|
| 2     | 2位 | 京都府|98,765|43%|2位 |京都府 |22,222|33%|

こんな感じにするのがゴール

#コード#
データフレームの作成

```
import pandas as pd 

dict1=dict(Jan=["1位 大阪府 12,345 67%","2位 京都府 98,765 43%"],Feb=["1位 大阪府　 11,111 22%","2位 京都府 22,222 33%"])
list1=pd.DataFrame(data=dict1)
list1
```

splitを利用して元のデータフレームに列を追加

```
cols_name=['Jan','Feb']

list_new =[]
list_split=[]

for i in cols_name:
  list_split=list1[i].str.split(' ',expand=True)
  list_spilit_1=list_split.values.tolist()
  list_new.append(list_spilit_1)

df_list=[]

for i in range(len(list1.columns)):
  list_new_new=list_new[1-i]
  new_new=pd.DataFrame(list_new_new)
  df_list.append(new_new)
  new_df=pd.concat(df_list,axis=1)

new_df
```

列名が「０」「１」「２」「３」みたいに数字になってしまい、それも列追加の際に「順位」「都道府県」みたいにできれば良かったんですが実力不足でできませんでした。。。
なのでかっこ悪いですがこの処理の後にrenameで列名を変えました。
neosでlogix

# 概要

neosでlogixやってみる。
まずは、インストール。

# インストール。

NeosPublicSetup.exeでインストール。

# 環境

windows 10 home 64bit
celeron 1.1Ghz
memory 4Gb
GPU intel graphics 500
HMD なし

# デスクトップモードの操作方法

|キー、マウス操作	|動作
|:--|:--|
|Esc	|ダッシュメニューを開く/閉じる。テキストフィールドからフォーカスを外す。
|右クリック	|物を持つ。オブジェクトをグラブする。
|左クリック	|クリック、プライマリ(三人称の時レーザーアクティブ)
|DeleteまたはCtrl + Shift + DまたはCtrl + Shift + S	|グラブしているオブジェクトを破棄。
|マウスを移動	|見回す。視点移動。
|W,A,S,D	|移動(前後左右)、歩く。
|Shift + W,A,S,D	|走る。
|W,A,S,Dをダブルタップ|	走る。
|Z	|移動速度切り替え。
|C	|しゃがむ(短く押すとトグル100ミリ秒以下)、下に移動。
|スペース	|ジャンプ、上に移動、アンカーから出る
|Ctrl + マウスホイール	|自身をスケールする(コンテキストメニューからスケーリング有効にしている場合)
|M	|ミュートのオン・オフ切り替え
|F5	|一人称・三人称視点切り替え、カメラ
|Ctrl + マウスの右ボタン長押し	|カメラのオフセット（水平と距離）を調整
|Ctrl + マウスの右ボタンをダブルクリック	|カメラのオフセットをリセット
|Rまたはマウスボタン4(あれば)	|セカンダリ
|Ctrl + D	|グラブしているオブジェクトをコピー。
|Ctrl + S	|グラブしているアイテムをインベントリに保存。
|Ctrl + Z	|もとに戻す(undo)
|Ctrl + Y	|やり直す(redo)
|Ctrl + F	|フォーカス
|E + マウス移動	|グラブしているアイテムをX軸Y軸を中心に回転。
|Shift + E + マウス移動	|グラブしているアイテムを垂直軸を中心に回転。
|Shift + マウスホイール	|グラブしているアイテムをスケール(拡大縮小)。
|Tまたはマウスホイールクリック	|コンテキストメニューを開く。
|PageUp,PageDown	|ロコモーション(移動方法)の変更
|Ctrl + I	|旧インベントリを開く
|マウスホイール	|メニューをスクロール
|Shift + PまたはShift + PrintScreen	|写真を撮る
|Ctrl + PまたはCtrl + PrintScreen	|タイマーで写真を撮る
|Ctrl + Tab	|アクティブなワールドを切り替える
|Shift + Escを2回押す	|現在のワールドを終了する/セッションを切断する
|F2	|エディットモードの切り替え
|F6	|自由視点
|Ctrl + マウスの右ボタン + w,a,s,d,c,space	|視点移動
|マウスの左ボタン + w,a,s,d,c,space	|移動
| 1-9 |ツールチップ切り替え 


以上。
Ruby の Array.pack('p') で 'can't modify frozen String' が出たら、バージョンを上げるか、解凍が必要

Ruby で pack をしたら、 `can't modify frozen String` が出たので、記録する。

```rb
[47] pry(main)> str = 'abc'
=> "abc"
[48] pry(main)> str.freeze
=> "abc"
[49] pry(main)> [str].pack('p*')
RuntimeError: can't modify frozen String
from (pry):48:in `pack'
```

`pack` は変更(`modify`)ではない、エラーが出るのが不思議だった。
そこで思いついたのは、「ポインタを渡すと、破壊できるから」という理由。

本当にそうなのかを調べようと思ったが、 2.6.3 まではエラーだが、 2.7.3 からはエラーにならなくなっていることに気づいた。

下記のコードを `tmp.rb` として保存し、手元にある version を切り替えながらテストした。

```rb
# tmp.rb
# frozen_string_literal: true

puts RUBY_VERSION

['abc'].pack('p*')
```

```rb
$ rbenv shell 2.3.3
$ ruby tmp.rb
2.3.3
tmp.rb:5:in `pack': can't modify frozen String (RuntimeError)
        from tmp.rb:5:in `<main>'
```
```rb
$ rbenv shell 2.5.0
$ ruby tmp.rb
2.5.0
Traceback (most recent call last):
        1: from tmp.rb:5:in `<main>'
tmp.rb:5:in `pack': can't modify frozen String (FrozenError)
```
```rb
$ rbenv shell 2.6.3
$ ruby tmp.rb
2.6.3
Traceback (most recent call last):
        1: from tmp.rb:5:in `<main>'
tmp.rb:5:in `pack': can't modify frozen String (FrozenError)
```
```rb
$ rbenv shell 2.7.3
$ ruby tmp.rb
2.7.3
```
```rb
$ rbenv shell 3.0.1
$ ruby tmp.rb
3.0.1
```

ポインタが関係なかったのは残念。
バージョンを上げれば解決する。

バージョンを上げずに対応する方法は、解凍すること。

```rb
[56] pry(main)> [str].map(&:+@).pack('p*')
=> "\xD8\xC6\b\x04\x00\x00\x00\x00"
```
GitHub ActionsでFirebaseにデプロイできない

## 課題
`git push` をしたらFirebaseにデプロイされるはずが、
以下のようなエラーメールが届いてしまう。
![スクリーンショット 2021-11-24 10.41.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223280/03b8f678-efcc-e390-cc8d-8f9e49172f9d.png)

## 原因
npm周りでエラーが出ている
![スクリーンショット 2021-11-24 10.49.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223280/f793e9e7-572a-203b-f5ff-85283c55f572.png)

## 解決策
`.gitignore` に `package.json` たちが追加されていることが原因でした。

```:.gitignore
package.json → 削除する
package-lock.json → 削除する
```

削除したらデプロイに成功しました！！
![スクリーンショット 2021-11-24 10.55.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223280/d6fc4ab7-896e-1e22-bcf0-9f86b0a95dd0.png)

## 要点
:::note info
GitHub Actionsの実行は githubにあげたpackage.jsonたちが使われる
もしエラーが出たら .gitignoreを確認してみよう
:::
seleniumでreadonlyを解除する

# seleniumでreadonlyを解除する

seleniumでの操作中に操作したいテキストボックスがDatePickerかつreadonlyで通常の操作が
できなかったため、本記事のコードを実行した。


```Python:コード
from selenium import webdriver

driver = webdriver.Chrome("./chromedriver")
...

param = "document.getElementsByClassName('readonly解除場所')[1].removeAttribute('readonly');"
driver.execute_script(param)
```
ソフトウェア開発とSDGs

##　はじめに

本記事はMDC Advent Calendar 2021の5日目の記事です。
私自身、環境保全活動などに精を出しているわけではありませんが、タイトルとして何が当てはまるか考えてみると壮大な響きになってしまいました。

##　SDGsとは何か

解説しているサイトはたくさんあるのでここでは割愛します。
17あるゴールのうち、ここで取り上げたかったのは「12.つくる責任 つかう責任」に該当しそうです。

https://imacocollabo.or.jp/about-sdgs/#:~:text=SDGs%E3%81%A8%E3%81%AF%E3%80%8CSustainable%20Development,%E7%9B%AE%E6%A8%99%EF%BC%89%E3%80%8D%E3%81%AE%E7%95%A5%E7%A7%B0%E3%81%A7%E3%81%99%E3%80%82&text=SDGs%E3%81%AF2015%E5%B9%B49,%E3%81%AB%E6%8E%B2%E3%81%92%E3%81%9F%E7%9B%AE%E6%A8%99%E3%81%A7%E3%81%99%E3%80%82

##　気になること

SDGsといえばエコとか地球環境的な話が注目されがちですが、

- プロダクトが成功するには、開発自体にサステナブルな思想が必要ではないか

ということについて考えてみました。
いくつか例を挙げてみたいと思います

###　チームは走り続けられるのか？

１つのプロダクトを長く運用し続けていると、既存サービスの維持保守に追われて、新たな機能開発のペースが落ちてくることがあります。
技術的負債と呼ばれるものの蓄積だったり、「運用でカバー」みたいな諦めだったり、色々な要因があると思います。
ソフトウェアは作って終わりではなく、ビジネスが続く限り運用され続けるものであって、自身が担当を離れた後も使われる、使ってもらえるように作らないといけません。

最初の技術選定から、質の高いコード、運用の中でのスキルトランスファーなど、アウトプットの中で常に意識し続ける必要があるはずです。
とある会社の方は、「エンジニアのアウトプットを評価するのに、特定の案件のみでは判断できない」と言っていました。
エンジニアの価値は、数年後にビジネスの成功に寄与できているかに現れるもので、さらにはビジネスの成否は外部環境にも左右されることから、個人を評価すること自体を諦めたそうです。

###　仕事を守ることが正義か？

開発するサービスによって、これまで人が行っていた仕事がITに奪われるのは許せないという反発が組織内で起きることがあります。
開発をやめれば、その時はいいかもしれませんが、長期的に見てどうなのか。
組織内で変わることができなければ、他の組織からコスト面で優れるサービスが出てきて、組織ごと駆逐されてしまいます。

###　そのプロダクトは人を幸せにするか？

消費者金融で無理な借金をして自己破産に至るというのは、サステナブルでないビジネスの一例だと思います。
そんな不幸を招く社会問題から総量規制（年収の１/3までの貸出上限）が設定されましたが、銀行系カードローンについては規制対象外となってきました。
今も規制はかかっていないままですが、最近は銀行系カードローンも総量規制に準じた自主規制ルールを設けているところが多くなっているそうです。
最近はCMでも銀行系カードローンを見ることは無くなりました。
ブランドに対する意識からそう変化したとすれば、いい流れだと思います。
一方で最近登場した山口FGとアイフルの提携のニュース、「低所得者の死亡保険を返済に充てる」というビジネスについては、大きな物議を醸しています。

https://biz-journal.jp/2021/12/post_266888_2.html/amp

##　さいごに

エンジニアの仕事は「総合格闘技」みたいなものだと表現する方がいました。
これからのシステム開発はただのものづくりではなく、長期的なビジネスへのコミットメントが求められています。
ITをビジネスの源泉として成長を続ける企業も増えてきていますが、成功にはITの負の側面も押さえながらコントロールしていくことが大切です。
最近炎上していた「オートリザーブ」という飲食店の予約サービスを思い出しました。AI電話予約という技術の着眼点は良かったにせよ、重要な登場人物である飲食店の思いを蔑ろにしたら成功は難しいだろうと思います。

https://news.yahoo.co.jp/byline/toryu/20211123-00269446

チームさえ良ければ、会社さえ良ければ、利益が出れば、法令遵守していれば、ではダメで、社会にどう貢献するか。
これは難しい反面、やりがいに直結する話でもあって、突き詰めると素晴らしいDeveloper Experienceが得られると思います。
私も高い倫理観で、「いい仕事」をしたいと思います。

##　参考文献

自分の言いたかったことは、この辺りの記事にも詰まっていました。

https://xtech.nikkei.com/atcl/nxt/column/18/00148/090200189/

https://xtech.nikkei.com/atcl/nxt/column/18/00148/092100191/
Couchbase Mobile開発：データ同期パターン〜データ同期範囲の制御

## はじめに

Couchbase Mobileは、オフラインファーストアプリケーションの幅広いユースケースに対応することが可能です。

ここでは、Couchbase LiteとSync Gatewayとのデータ同期に関して、データ同期範囲を制御するパターンについて説明します。

## クライアントローカルデータとサーバーと同期するデータの分離

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/cdacc9ca-eb6c-8337-f068-cfbc4bc1b74d.png)

### はじめに：Couchbase Liteデータベースインスタンスについて
アプリでは、Couchbase Liteデータベースの複数のインスタンスを使うことができます。データベースインスタンスの数に厳しい制限はありませんが、実際的な制限があります。たとえば、データベースを跨いだデータの結合(JOIN)クエリを行うことはできません。

### ユースケース
アプリ内に複数のデータベースを持つ目的の一つとして、クライアントに対してのみローカルなデータを、残りのデータとは別のデータベースインスタンスから分離することが挙げられます。データベースを分離することで、開発者はデータベースレベルでデータ同期設定を有効化するかどうかを選択することができるようになります。

### アプローチ
ローカルのみのデータ用にCouchbase Liteデータベースのインスタンスを作成します。
ローカル専用データベースには、データ同期のためのレプリケーターを構成しません。

## いつどのドキュメントをサーバーに同期するかを制御する


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/e993a063-d664-d45b-24e2-d60019195610.png)

### はじめに：Couchbase Liteのデータ同期フィルター機能について

Couchbase Liteは、アプリがレプリケーターにきめ細かいフィルターを設定して、サーバーにプッシュされるドキュメントを決定する機能をサポートしています。この機能を利用して、いつどのドキュメントをサーバーに同期するかを制御できます。

### ユースケース

先に紹介した方法は、厳密なデータ分離が必要な場合や、データベース間の結合クエリが必要ない場合に機能しますが、ローカルの変更を同期するタイミングをきめ細かく制御したい場合があります。ドキュメントがローカルのみに存在する（つまり、同期されない）ことを強制するために、このパターンを使用することができます。

### アプローチ
ドキュメントをモデル化する際に、ワークフローを制御するために、ユーザー定義の「ステータス」プロパティを含めます。たとえば、「進行中」の値を使用して、変更を同期する準備ができていないことを示し、変更をプッシュアップする必要がある場合は「準備完了」のステータス値を使用します。
あるいは、ステータスに関わらず、ドキュメントをローカルのみに持てば十分な場合は、ユーザー定義の「スコープ」プロパティを設け、値を「ローカル」に設定し、ドキュメントを同期しないことを示すことも考えられます（この場合、インスタンスレベルで分割する先のパターンと異なり、同期の有無に関わらずドキュメント間の結合を行うことができます）。
レプリケーターで適切なレプリケーションフィルター機能を設定します。フィルターは、上で定義したドキュメントのプロパティを検査して、ドキュメントを同期する必要があるかどうかを判断します。
ドキュメントの変更があるたびに、レプリケーターによってフィルター機能が適用され、フィルターの基準が満たされた場合にのみドキュメントが同期されます。


## 参考情報

https://blog.couchbase.com/best-practices-common-patterns-couchbase-mobile-part1/

https://blog.couchbase.com/best-practices-couchbase-mobile-database-sync-part2/


https://docs.couchbase.com/userprofile-couchbase-mobile/current/userprofile/userprofile_sync.html
Pythonでマイクロサービスの作成

#準備
Python環境の用意→インストールは[こちら](https://www.python.org/downloads/)から
Flaskのインストール

```
$ pip install flask
```
sqlachemyのインストール

```
$ pip install sqlachemy
```
requestsのインストール

```
$ pip install requests
```

DBの用意(今回はSQLite3)

#実行環境
MacOS 11.4
Python 3.9.0
SQLite 3.32.3
requests 2.24.0
sqlachemy 1.4.26
Flask 2.0.2


#webサーバーを立てる
今回は, ブラウザ上で選択したアイテムの値段を計算するwebサイトをマイクロサービスアーキテクチャもどきで作成する. 
今回の機能としては以下の２つ
①dbに登録されている商品と値段を表示する
②選択した商品のみの表示

```main.py
import json
import os

from flask import Flask, render_template, request
import requests

from orders_models import Order, new_Order

app = Flask(__name__, template_folder='templates')


@app.route("/")
def main():
    all_item = Order.query.all() //Order.dbの全テーブルの全アイテムを取得
    return render_template("index.html", all_item=all_item) //index.htmlにOrder.dbの中身を送る

@app.route("/add", methods=["post"])
def show():
    url = 'http://loaclhost/order' //app.pyのURL
    id_list = request.form.getlist("add") //htmlからaddメソッドを受け取る
    for id in id_list: 
        payload = int(id) 
        requests.post(url=url, json=json.dumps(payload)) //データ部にidを格納してapp.pyに送る
    all_item = new_Order.query.all() //new_Orderテーブル内の全アイテムを取得
    return render_template("index.html", all_item=all_item)


if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=80)
```

```app.py
import json
import os

from flask import Flask, render_template, request
from sqlalchemy.ext.asyncio import session
from sqlalchemy.orm import scoped_session, sessionmaker

from database import db_session
from orders_models import Order, new_Order

app = Flask(__name__, template_folder='templates')

@app.route("/order", methods=["post"])
def item():
    data = json.loads(request.json) 
    content = Order.query.filter_by(id=data).first() //idが一致するデータを取り出す
    i = new_Order(name=content.name, price=content.price) //ｄｂの更新
    db_session.add(i)
    db_session.commit()

if __name__ == "__main__":
    app.run(debug=True, host='0.0.0.0', port=81)
```

```index.html
<!DOCTYPE html>
<html>
    <head>
        <title>order.net</title>
    </head>
         <h1>メニュー表</h1>
        {% for item in all_item %}
        <div>
            <input type="checkbox" name="add" form="add" value={{item.id}}>
            {{item.name}}：{{item.price}}
        </div>
        {% endfor %}
    <form action="/add" method="post" id="add">
        <input type="submit"  value="買い物かごに追加する">
    </form>
    </body>
</html>
```

#実際に動かしてみた
![demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/690609/8b7f214d-22d9-fcb1-e8b0-6cbe0e9f5c2d.gif)
【Swift】LaunchScreenの待機時間を設定する方法

SwiftでLaunchScreenの待機時間を設定する方法。

## 解決コード

`AppDelegate`の関数の中身にコードを加えるだけ。

```swift
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        sleep(UInt32(5)) //この部分を追加 この場合は5秒待機。
        return true
    }
}

```

`AppDelegate`の関数に`sleep(UInt32(秒数))`を追加するだけです。

秒数は少数でも可能です。

## 参考
https://developer.apple.com/documentation/foundation/thread/1413673-sleep

## Swiftのお役立ち情報

https://wagtechblog.com/programing/swift-iphone-application-school

https://wagtechblog.com/programing/swift-textbook.html
WAS設計その15 仮想ホスト・マッチングとアプリケーションサーバのポート設定

意外にややこしい以下の設定

対象環境
仮想ホストでホスト名を明示的に入れている環境。
かつ
IHS側のポート番号を80番以外使っている場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2291303/0ebf42fe-7817-d771-f8af-ffe8a0b53a37.png)

事前設定で
仮想ホスト設定で
ホスト名に
abcdef.com　ポート番号81を入れていること

1.以下のURLを実行
https://abcdef.com/

2.LBがIHS側のポート81番にリクエストを投げる。

3.仮想ホストマッチングしないのでNG
原因：仮想ホスト・マッチングで「ホスト・ヘッダーからのポート番号を論理的に使用」になっているため
ホストヘッダは通常httpが入っているので80になる。
ホストヘッダはリクエストヘッダのHostという項目。（F12で確認してください）
通常ポート番号の記載はないため「80」とみなされる。
そのため「要求で指定されたポートを物理的に使用」に変更する。

4.それでもNG
原因：アプリケーション・サーバーのポート設定が「ホスト・ヘッダー」のままのため
それを、Webサーバー・ポートに変更すること。
理由：日本語がちょっとおかしいけど、81番ポートでIHS側が受けているので、当然WAS側はIHSにリクエストを返す時、81番ポートに投げる必要がある。それを80番で投げる？ためにエラーが起きる。

※なんか両方１つの設定でいいような気がする。そもそも分けた理由はあるのかな。
※高度すぎてよくわからない。
Firebase Cloud Messaging(FCM)は中国では使えない

iOS/Androidアプリの開発を協業している他社担当者から、システムテストにおいて、

「プッシュ通知が届かない」
「iOS/Androidとも同じく」

という報告があり、再現性を確認したのですが弊社側では再現せず…

同アプリはプッシュ通知にFirebase Cloud Messaging(FCM)を利用しています。
バックエンド側のログなどを追うと、どうやら、SDK経由で取得できるはずのプッシュトークンが取得できていない模様。

協業他社によくよく聞いてみると、「中国でテストをしている」とのこと。

そこで調べたところ、FCMって中国（本土）では使えないんですね…（→参考リンク）
というか、中国本土では国の規制によってGoogleのサービスは全般的に使えない、と。

同アプリは日本国内での利用が前提なので、協業他社に「プッシュ通知のテストだけは国内でお願いします」と依頼するだけで済んだのですが…

もしグローバルなサービスだったら、中国本土で使えるプッシュ通知mBaaSに乗り換えるなど、大幅なアーキテクチャ変更になるところでした。:cold_sweat:

なお、FCMの公式ドキュメントではその辺りの制限事項に関する記述は見つけられませんでした。

参考リンク:

https://openback.com/blog/accessing-chinas-mobile-app-market-overcoming-the-fcm-firewall/

https://stackoverflow.com/questions/64695786/fcmios-messaging-messaging-token-gives-a-request-timed-out-for-china

https://docs.kii.com/ja/guides/cloudsdk/android/managing-push-notification/push-techinology/
Couchbase Mobile：データ同期パターン〜ローカルデータクリーンアップ

## はじめに

Couchbase Mobileは、オフラインファーストアプリケーションの幅広いユースケースに対応することが可能です。

ここでは、Couchbase LiteとSync Gatewayとのデータ同期に関して、ローカルデータクリーンアップのための、設計パターンについて説明します。



## プッシュ後にパージ


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/130bf67f-964c-8b9a-b386-eef4b28f0aa5.png)

### 概要

Couchbase Liteは、イベント機能を介してドキュメントのレプリケーションステータスについてアプリに通知する機能をサポートしています。アプリはこの機能を利用して、ドキュメントのステータスに応じて、ドキュメントに対して適切なアクションを実行することができます。

この機能を利用して、ドキュメントがCouchbase Serverへ同期された後、ローカルデータベースからクリアすることができます。

### ユースケース
ローカルデータベースの肥大化を回避するために、またはコンプライアンス上の理由から、サーバーに同期された後にローカルのデータベースからドキュメントを削除することが望ましい場合があります。

### アプローチ
レプリケーターにレプリケーションイベントオブザーバーを登録します。
ドキュメントの`onPushed`イベントを受信したら、`purge()`APIを呼び出してドキュメントを削除します。パージされたドキュメントのステータスは同期されないため、サーバーにのみドキュメントが格納されることになります。


## ドキュメントの有効期間を適用する


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/22a9889a-6e2a-5822-db2b-8d324d7ad340.png)

### 概要

Couchbase Liteは、ローカルドキュメントに有効期限を設定する機能をサポートしています。アプリはこの機能を利用して、サーバーへの接続に関係なく、Couchbase Lite上のドキュメントをローカルで期限切れにすることができます。

### ユースケース
サーバー上で作成されたドキュメントは、ドキュメントがデータベース上に存続する期間であるTTL(Time To Live)、つまり有効期限を設定されている場合があります。ネットワーク接続がある環境でタイムリーに同期が行われている場合、サーバー側で削除されたドキュメントはクライアント側に同期されるため、クライアント側でも削除されます。ただし、クライアントがオフラインになっている場合があり、そのような場合であっても、有効期限切れのドキュメントをタイムリーにクライアントから削除することが重要な場合があります。

### アプローチ

クライアントレプリケーターにレプリケーションイベントオブザーバーを登録します。
ドキュメントの`Pull`イベントを受信したら、Couchbase Lite側で、`setExpirationDate()`APIを使用してドキュメントのTTLを設定します（TTLには、サーバーとクライアントとで同じ値を使うことも、あるいは要件に応じて別の値を使うことも考えられるでしょう）。
クライアントでドキュメントの有効期限が切れると、ネットワーク接続に関係なく、ドキュメントはクライアントから自動的に削除されます。
サーバー側では、Couchbase Serverの有効期限設定に従い、ドキュメントは削除されます。



## 参考情報

https://blog.couchbase.com/best-practices-couchbase-mobile-database-sync-part2/


https://docs.couchbase.com/userprofile-couchbase-mobile/current/userprofile/userprofile_sync.html
書いた記事まとめ

投稿記事のまとめです

# PowerApps 関係

### Teams投稿アプリを作成する

https://qiita.com/zaki_001/items/648256d4df9f0eb892ac

https://qiita.com/zaki_001/items/2d0cc21e4e555e086912

### 単発もの

https://qiita.com/zaki_001/items/aef61f410b63280f2fef

https://qiita.com/zaki_001/items/6a5ada896a31d40de5e8

# Tableau 関係

### 書式設定を理解する

https://qiita.com/zaki_001/items/c83bc2881a6b54a9f997

https://qiita.com/zaki_001/items/10ce5c9155887a711f34

https://qiita.com/zaki_001/items/319efb40377dcdd11823

### 表計算を理解する

https://qiita.com/zaki_001/items/0df60fc8429f04743979

https://qiita.com/zaki_001/items/499bd733e8c227dc258b

https://qiita.com/zaki_001/items/b783a044c1fc1681db98
【JavaScript】非同期処理⑧　fetch


#はじめに

Udemyの[【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)の講座の振り返りです。

前回の記事

https://qiita.com/nao0725/items/58dd9d78a1718b716880


#目的

* 非同期処理についての理解を深める

#本題
###1.fetch

サーバー上から情報を取得したりできる関数

**前提**
main.jsファイルと同じ階層にusers.jsonファイルを準備する
(VSコードの拡張機能：Live Serverがインストールされていないとエラーになります。)

* 基本的にはJSと同じ書き方
* しかしシングルクオーテーション使用不可
* 必ずダブルクオーテーション
* 一番最後の要素にカンマを使うとエラーになる

````users.json
[
  {
    "name": "Bob",
    "age": 23
  },
  {
    "name": "Tim",
    "age": 30
  },
  {
    "name": "Sun",
    "age": 25
  }
]
`````

####例1

fetchの基本的な使い方

````main.js
// 第一引数にリクエスト先のURLを入力（今回はuser.jsonファイル）
// fetch("users.json")
// どのような戻り値が帰ってくるのかコンソールで確認
// console.log(fetch(`users.json`))
// このようにするとPromiseが返ってきていることがわかるのでthenメソッドが使える
fetch(`users.json`).then(function(response){
  // サーバーから返ってきたデータを格納している
  console.log(response);
})
`````

出力結果は以下の通り

```console.
body: (...)
bodyUsed: false
headers: Headers {}
ok: true　　 #サーバーからデータが取得できたことを表している
redirected: false
status: 200  #Httpステータスが200ということは通信に問題がない状態
statusText: "OK"
type: "basic"
url: "http://127.0.0.1:5500/080_%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%87%A6%E7%90%86/110_fetch/start/users.json"
[[Prototype]]: Response
`````


この結果にあるjsonメソッドを使う（jsonファイル取得のため）


`````main.js
fetch(`users.json`).then(function(response){
  // コンソールからjsonというメソッドが使えることがわかる
  return response.json();
}).then(function(Json){
  // この引数Jsonにデータが入っているか確認する
    console.log(Json);
})
``````````


このようにすると以下のように出力される

`````console.
(3) [{…}, {…}, {…}]
0: {name: 'Bob', age: 23}
1: {name: 'Tim', age: 30}
2: {name: 'Sun', age: 25}
length: 3
[[Prototype]]: Array(0)
`````

####例2

データの取得がうまくいっていたので、応用して出力する

````main.js
fetch(`users.json`).then(function(response){
  return response.json();
}).then(function(Json){
    console.log(Json);
    // for文を使用してループ
    for(const user of Json){
      // テンプレートリテラルを使って出力
      // nameとageがusers.jsonファイルのものを使用
      console.log(`おれは ${user.name}, 今年で${user.age}になる。`)
    }
})

// 以下のように出力されている
// おれは Bob, 今年で23になる。
// おれは Tim, 今年で30になる。
// おれは Sun, 今年で25になる。
```````

重要なのは２点

Promiseのオブジェクトを返す
jsonのオブジェクトを使用するにはjsonメソッドを使用して取得する必要がある


####例3

awaitとasyncで出力してみる

````js
async function fetchUsers(){
  const response = await fetch(`users.json`);
  const Json = await response.json();
  for(const user of Json){
    console.log(`おれは ${user.name}, 今年で${user.age}になる。`)
  }
}
// 実行すると同じ出力結果となる
fetchUsers();
`````







今日はここまで！

#参考にさせて頂いた記事

* [【JS】ガチで学びたい人のためのJavaScriptメカニズム](https://www.udemy.com/course/javascript-essence/)
* [Let'sプログラミング JavaScript入門](https://www.javadrive.jp/javascript/)
ESLintを特定ファイルで無効にする

## 課題
ある特定のファイルだけESLintから除外したい
## 問題
`.eslintignore` を追加しても直らない🥲
## 解決策
除外するファイルの冒頭に以下を追加する

```:除外するファイル
/* eslint-disable */
```
Writeup (ミニキャンプ2021 : ファイルシステム)


## Writeupについて

ミニキャンプ2021の修了試験で出題されたファイルシステムに関する問題のWriteUpです。
(途中までしか解けませんでしたが...)

## 解説

### 基本方針

基本方針としてはひたすらSDカードのダンプを解読するのみです(自分は未解凍のダンプをそのまま読もうとして終了しました())。SDカードはFAT32でフォーマットされているためそれを前提に解読を進めます。

## 第2クラスタの位置

ファイルシステムの構造を以下に示す。この中のルートディレクトリに手がかりとなるファイルのメタデータ(ディレクトリエントリという)があると考えて、まずはその位置を探す。
![スクリーンショット 2021-11-22 15.58.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1521871/96d1befd-ee0e-04e7-593e-7e5c4f664831.png)


FAT32ファイルシステムの先頭部分はあらかじめ予約された領域となっており、この中にはブートセクタやファイルシステムのメタ情報などが含まれている。その中で今回使用する情報を以下に示す。

| 項目名           | オフセット | サイズ(B) | 概要|
|:-----------     |---------:|:-----:|:--------------:|
| BPB_BytesPerSec        | 0xB      |2    |1セクタの大きさ(Byte)512, 1024, 2048, 4096のいずれか             |
| BPB_SecPerClus        | 0xD      | 1     |1クラスタあたりのセクタ数               |
| BPB_ResvdSecCnt       | 0xE      | 2     |予約領域のセクタ数
| BPB_NumFATs| 0x10      | 1     |FATテーブルの個数
| BPB_FATSz32     | 0x24      | 4     |FATテーブルのセクタ数

FATの構成の通り先頭から **予約領域の大きさ + FATテーブルの大きさ** だけ読み進めたところが第2クラスタの先頭の位置となる。

予約領域の大きさは **BPB_BytesPerSec(セクタ1つの大きさ) * BPB_ResvdSecCnt(セクタ数)**で求めることができる。

またFATテーブルの大きさは **BPB_BytesPerSec(セクタ1つの大きさ) * BPB_FATSz32(テーブルあたりのセクタ数) * BPB_NumFATs(テーブル数)**で求めることができる。
![スクリーンショット 2021-11-22 19.04.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1521871/76d3b8db-319a-d3fb-ff10-addb16c15759.png)
それぞれの値をリトルエンディアンで確認する。

BPB_BytesPerSecはオフセット0xBで、その値は0x0200、10真数で512となる。つまりセクタ1つ分の大きさは512バイトとなる。

BPB_ResvdSecCntはオフセット0xEで、その値は0x20、10真数で32となる。予約領域のセクタ数は32であることが分かる。

BPB_FATSz32はオフセット0x24で、その値は0x1ff8、10真数で8184となる。テーブル1つあたりのセクタ数は8184であることが分かる。

BPB_NumFATsはオフセット0x10で、その値は0x02、10真数で2となる。FATテーブルの数は2つとなる。

以上のことから

予約領域の大きさは 512 * 32 = 16384(byte)
FATテーブルの合計サイズは 512 * 8184 * 2 = 8380416(byte)
16834 + 8380416 = 8396800 

よって第2クラスタの位置は先頭から8396800バイト目であることが分かる。
### 第2クラスタ内のファイルのメタデータ
![スクリーンショット 2021-11-22 19.23.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1521871/d70f9d58-97af-37c8-e301-3e0fbd8b78ba.png)
実際に8296800バイト分飛ばして確認すると、気になる部分としてSECRET.ZIP、.ASSWORDTXTの2つが見つかる(ファイルのメタデータが保存されている)。.ASSWORDTXTについては先頭バイトがE5になっているため削除されていることが分かる(おそらく元の名前はPASSWORDTXT)。データ本体は別の場所に格納されているはずであり、ファイル先頭からオフセット0x14の値は0x0、0x1Aの値は0x18E8であるから、ファイルのデータ本体は0x18E8番目のクラスタから始まっている。ディレクトリエントリの見方については[こちら](https://qiita.com/mitsumushibayama/items/f6211b3e680b89185ac0)を参照。

0x18E8は10進数で6376であり、該当するデータは6376番目のクラスタから始まっている。1番目のクラスタは予約領域とFATテーブルで占められているから該当する場所は**第1クラスタの大きさ + (クラスタ番号 - 2)*クラスタ1つあたりの大きさ**で求められる。

### SECRET.ZIP

第1クラスタの大きさは 16384 + 8380416 = 8396800(byte)
第2から第6376クラスタまでの大きさは 6374 * 4096(セクタ数*セクタの大きさ) = 26107904(byte)
求める位置は 8396800 + 2610794 = 34505704(byte) となる。
![スクリーンショット 2021-11-22 20.39.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1521871/7cfad386-5e52-593f-24b4-2a69956856ba.png)
別のツールではデコードしたときのデータの中身が日本語で読める状態になっており、CLUSTER_()_LOCKの()の中身にSECRET.ZIPのパスワードは、SECRET.ZIPのデータ部分の始まりのクラスタ番号(10進数)であることが書かれている。

SECRET.ZIPのメタデータ部分のオフセット0x14の上位ビットの値は0x0、0x1Aの下位ビットの値は0x131Eとなっているためパスワードは10進数で4894となる。
SECRET.ZIPをパスワードを用いて開くことでフラグが手に入る。
Wordpressで複数のQueryを取得し、1つにまとめる

あまりやることはないし綺麗ではないけれどどうしてもやらないといけないとき用。

```php
	<?php 
        //taxonomy.phpで処理した例
        $term_id = get_queried_object_id();

        //条件1
	$post_args = array(
		'post_type' => 'post01-topics',
        'tax_query' => array(
            array(
                'taxonomy' => 'country',
                'field' => 'slug',
                'terms' => $term
            )
		),
		'posts_per_page' => 0,
	);
        //条件2
	$post_args2 = array(
		'post_type' => 'post02-topics',
		'meta_query' => array(
			'relation' => 'AND',
			array(
				'key' => 'country',
				'value' => $term_id,
				'compare' => '=',
			),
			array(
				'key' => 'association',
				'value' => 'お知らせ',
				'compare' => '=',
			),
		),
		'posts_per_page' => 0,
	);

        //条件1と2をガッチャンコ
	$myposts = array_merge( get_posts($post_args), get_posts($post_args2));

        //post_date（日付）でソート
	usort($myposts, function ($a, $b) {
        return $a->post_date> $b->post_date? -1 : 1;
    }); 

        //後はいつもどおりループ
	if ($myposts) : ?>
	<section>
		<h1>お知らせ</h1>
		<ul>
		<?php foreach($myposts as $post){
			setup_postdata($post); ?>
			<li><span class="_date"><?php the_time('Y.m.d'); ?></span>
				<a href="<?php the_permalink(); ?>">?php the_permalink(); ?><?php the_title(); ?></a>
			</li>
		<?php }
		wp_reset_postdata(); ?>
		</ul>
	</section>
	<?php endif; ?>
```
メモ〜Dockerfileとは

概要
-
Dockerイメージだけでは不足している情報をコンテナに付け足すための設定ファイル。

コンテナ上でのプロセス作成を効率化するために用いられる。

例えば「dockerを用いてCentOSベースのコンテナを作り、その上にapacheサーバーを立ててphpで`hoge`と表示する」ことを考えた場合、Dockerイメージに含まれているのはCentOSの情報のみであり、apacheとphpのインストールやサーバーの起動、phpファイルのコンテナへのコピーなどといった作業は手動で行うことになる。

しかし、これらの作業の手順を記したDockerfileを作成しておき、そこからコンテナを作成することで、上記の作業を全て自動で実行することができる。

Dockerファイルを用いたコンテナ作成の手順
-

- Dockerファイルの作成
- 必要なファイル（今回でいえば`hoge`と出力するためのphpファイル）の用意
- DockerファイルからDockerイメージを作成
- コンテナの作成

Dockerファイルの作成〜具体例
-

```shell:Dockerfile
FROM centos:centos7
  
RUN yum -y install httpd php
 
COPY test.php /var/www/html/
 
CMD ["/usr/sbin/httpsd","-DFOREGROUND"]
```

=========注意=============

- Dockerファイルのファイル名はDockerファイルとしてください。
- Dockerファイルはホームディレクトリ以外に作成してください。通っているパスの関係でdockerイメージの作成（後述）に支障が出る場合があります。

=========注意終わり=========

Dockerファイルの作成〜具体例の解説
-

```shell:1行目
FROM centos:centos7
```
`centos:centos7`というdockerイメージをベースにしてコンテナを作ることを宣言。

```shell:2行目
RUN yum -y install httpd php
```
`apache`と`php`のインストール。

FROMで指定したイメージに対して行う作業には`RUN`コマンドをつける必要がある。

```shell:３行目
COPY test.php /var/www/html/
```
test.phpというファイル（`hoge`と表示するためのファイル）を`/var/www/html/`というディレクトリにコピーする。

`/var/www/html/`はdockerコンテナにデフォルトで用意されているファイルの一つ。

apacheの初期設定ではこの`/var/www/html/`以下に置かれたファイルがWebサイト上で表示されるようになっている。

```shell:4行目
CMD ["/usr/sbin/httpsd","-DFOREGROUND"]
```
コマンドの起動。

`["実行するコマンド", "引数"]とすることで、指定した形式でコマンドが実行される。

今回は作成したapacheサーバーをバックグラウンドで実行している。

Dockerファイルの作成補足〜RUN, COPYとCMDの違い
-
RUNやCOPYなどのコマンドを直接Dockerfileに記述する場合とCMDコマンドの引数として指定する場合では、コマンドの実行タイミングに違いが出る。

直接記述したコマンドに関しては`DockerファイルからDockerイメージを作成する際`に実行されるのに対し、CMDの引数に指定したコマンドに関しては`Dockerコンテナ作成時`に実行される。

上記の例ではベースとなったdockerイメージに追加してほしい要素、すなわちapacheのインストールなどはコマンドを直接記述してイメージ作成時に実行されるようにし、サーバーの起動に関してはコンテナ作成時に実行されるようにCMDコマンドで指定している。

Dockerイメージの作成
-

Dockerファイルが存在するディレクトリにて以下のコマンドを入力する。

```shell
% docker build -t dockerイメージの名前:タグ名 .
```

末尾のコロンはDockerファイルのパス。

タグ名は省略可能（省略した場合は`latest`になる）。

Dockerファイル関連のコマンド達
-

```shell
# ベースイメージを指定
# 公式
FROM イメージ名:タグ名
# 例 
FROM centos:centos7

# ベースイメージに対するインストール
# 公式
RUN -y install xxx
# 例
RUN -y install httpd php
# -yをつけることで、ダウンロードファイル容量確認後の処理を継続するかどうかの選択をスキップすることができる
# ここを-tにしてしまうとyes/no選択の部分で処理が中断してしまうので、必ず"-y"をつける

# ファイルのコピー
# 公式
COPY ファイル名 コピー先のディレクトリ
# 例
COPY test.php /var/www/html/

# ファイルの追加
# 公式
ADD コピー元のパス コピー先のイメージのパス
# 例
ADD https://wwww.xxx.com /tmp
# COPYとは異なりURLの指定が可能。また、圧縮ファイルの展開も自動で行われる。
Raspberry Pi ZeroのUSB電源制御を試してみた

# この記事について
前回のイルミネーションでは物足りないので、USB式のイルミネーションをチカチカさせようとした結果失敗した、失敗談を書いています  

# やりたいことのイメージ
ラズパイ上のUSBの電源制御で、USBで接続した機器を点灯 / 消灯を繰り返すようにしてみたい
USB式のイルミネーションを探して、100均に行ってみたのですが、見つからなかったので、USBライトで代用しています

![iOS の画像 (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502127/0cbd4666-24b2-b374-08c6-cc0fd97f5cff.jpeg)

# hub-ctrlで制御してみる
- hub-ctrlをインストールする

```
$ sudo apt-get install libusb-dev
$ wget http://www.gniibe.org/oitoite/ac-power-control-by-USB-hub/hub-ctrl.c
$ gcc -O2 hub-ctrl.c -o hub-ctrl-armhf-static -lusb -static
$ sudo cp hub-ctrl-armhf-static /usr/local/bin/hub-ctrl
```

- （補足）wgetが通らなかったので、一時的にDNSの向き先を`8.8.8.8`にしています

```
$ cat /etc/resolv.conf
# Generated by resolvconf
nameserver 8.8.8.8
```

- USBの状態を確認する

```
$ lsusb
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

$ lsusb -t
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc_otg/1p, 480M
```

- hub-ctrlだとサポートしていない（というか認識していない？）

```
$ hub-ctrl 
Hub #0 at 001:000
 INFO: ganged switching.
 WARN: Port indicators are NOT supported.
```

# /sys/devicesを強引に書き換える
- USBの電源制御っぽいファイルがありました

```
$ cd /sys/devices/platform/soc/20980000.usb/

$ ls -al | grep power
-rw-r--r--  1 root root 4096 11月 18 20:05 buspower
drwxr-xr-x  2 root root    0 11月 18 20:05 power
```

- いかにも電源っぽい内容です

```
$ cat buspower 
Bus Power = 0x1
```

- buspowerを書き換えてみました

```
$ echo 0 | sudo tee /sys/devices/platform/soc/20980000.usb/buspower >/dev/null

$ cat buspower 
Bus Power = 0x0
```

- が、制御できていないので、元に戻します

```
$ echo 1 | sudo tee /sys/devices/platform/soc/20980000.usb/buspower >/dev/null

$ cat buspower 
Bus Power = 0x1
```

- ほかにpowerっぽいファイルも確認

```
$ ls -al power/
合計 0
drwxr-xr-x 2 root root    0 11月 18 20:05 .
drwxr-xr-x 5 root root    0 11月 18 20:01 ..
-rw-r--r-- 1 root root 4096 11月 18 20:06 autosuspend_delay_ms
-rw-r--r-- 1 root root 4096 11月 18 20:06 control
-r--r--r-- 1 root root 4096 11月 18 20:06 runtime_active_time
-r--r--r-- 1 root root 4096 11月 18 20:06 runtime_status
-r--r--r-- 1 root root 4096 11月 18 20:06 runtime_suspended_time
```

```
$ cat autosuspend_delay_ms 
cat: autosuspend_delay_ms: 入力/出力エラーです

$ cat control 
auto

$ cat runtime_active_time 
0

$ cat runtime_status 
unsupported

$ cat runtime_suspended_time 
0
```

# 所感
ラズパイ上で「USBへの通電を止める」は難しかった（zeroだとできない？）
（「USBへの通電を止める」と「カードリーダーなどの端末を切断する」でネットの記事と認識がずれている気がした。。。）

# 参考サイト
- https://kinokotimes.com/2017/03/07/usb-control-method-by-raspberry-pi/
- https://artfinex.co.jp/wp/wp-content/uploads/2019/03/afx_RaspberryPiRaspbianUSB_Reset_UHF.pdf
- https://forums.raspberrypi.com/viewtopic.php?t=242059
- https://forums.raspberrypi.com/viewtopic.php?t=134351
AWSで手っ取り早くJupter notebookを動かした話

AWSで手っ取り早くJupyter notebookを起動させてみます
認証等を考えると色々振り回されるのですが、動かすだけなら割と簡単でした
認証周りは検証が不十分なので、とりあえず動かすことに主に紹介します

# 環境
AWS環境

- Deep Learning AMI (Ubuntu 18.04) Version 35.0
- g4dn.xlarge

# Jupyter notebookの設定
今回の環境ではJupyter notebookはインストール済みなので、とりあえず設定を変えてみます
Port 8081からアクセスできるようにします

```
vi ~/.jupyter/jupyter_notebook_config.py

c = get_config()
c.NotebookApp.ip = '0.0.0.0'
c.NotebookApp.open_browser = False
c.NotebookApp.port = 8081
c.NotebookApp.token = ''
```

# Jupyter notebook起動
バックグラウンドで起動します

```
jupyter notebook &
```

# AWSのセキュリティ許可
AWSのインバウンドルールで忘れずに8081を許可します  
(「ソース」は`0.0.0.0/0`ではなく正しい値を設定した方がセキュアです)

![インバウンド.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502127/3a304ba1-c1cc-96ec-ce62-2ced15508bcf.png)

機会があれば、Jupyter notebookをよりセキュアにする話でも書いてみようと思います。
MySQL 8系のレプリケーションでAuthentication requires secure connectionになる問題をとりあえず回避する

すべてレプリカ側の記述。
ソース側の記述や操作は一切なし。

```
show slave status\G;
```
をすると、 `Authentication requires secure connection` という注意文が表示される。

これを回避するためには、

```
CHANGE MASTER TO
    -> MASTER_HOST='master_hostname',
    -> MASTER_USER='replicate',
    -> MASTER_PASSWORD='password',
    -> MASTER_SSL=1;
```

のようする。
肝は `MASTER_SSL=1` を記述すること。
あとはこれまで通り START SLAVE; した後に、SHOW SLAVE STATUS\G; して、 `Slave_IO_Running` が Yes になっていれば正常に動作しているはず。

詳しいことは公式ドキュメントを読むこと。
https://dev.mysql.com/doc/refman/8.0/ja/replication-solutions-encrypted-connections.html

※キーのやり取りをしていないのでSSL通信が動いている訳ではないとは思うので、個人情報を扱うレプリケーションを行う際はしっかり公式ドキュメントを見て検証してください。詳しいことは知りません。
PythonのPandasとMatplotlibを使用してCSVファイルをグラフ化する

#はじめに
外の気温を温度センサーで計測してCSVファイルに書き込み，PandasとMatplotlibを使用して平均を求め，グラフ化をしてみました．

# 環境
プログラミング言語 : Python 3.8.10
Pandas : 1.3.4
Matplotlib : 3.4.3


# CSVファイル
データ件数 : 1900件(8:30~17:33)

|datetime|temperture|
|:---:|:---:|
|2021/11/10-08:30:12.2|17.3125|
|2021/11/10-08:30:12.2|17.3125|
|2021/11/10-08:30:12.2|17.25|
|2021/11/10-08:30:12.2|17.25|
|2021/11/10-08:30:12.2|17.3125|
|...|...|
|2021/11/10-17:33:28.2|17.25|
|2021/11/10-17:33:28.2|17.25|
|2021/11/10-17:33:28.2|17.25|
|2021/11/10-17:33:28.2|17.25|
|2021/11/10-17:33:28.2|17.25|


#プログラム
```python
#pandasとmatplotlibをインポート
import pandas as pd
import matplotlib.pyplot as plt

#csvファイルを読み込み
df = pd.read_csv('new/20211110/soto.csv', names = ['datetime', 'temperture'])

#datetime64型に変換
df['datetime'] = pd.to_datetime(df['datetime'])

#pandas.DateFrameの列をインデックス(行名)に割り当てる
df1 = df.set_index("datetime")

#2次元の表形式のデータに変換
df_sec = pd.DataFrame(df1)

#30分ごとに平均を求める(30minを変えるとほかの間隔にできる)
ave = df_sec.resample('30min').mean()

#折れ線グラフ作成
ave.plot()
```

# 結果
### 表
![実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739848/9e5101ff-833f-7eec-875f-e31dd12cc7c1.png)
### グラフ
![実行結果グラフ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739848/79229a0a-15e2-15e2-2d52-f9d711cc8391.png)

#終わりに
今回は，外の気温を温度センサーで計測してCSVファイルに書き込み，pandasとmatplotlibを使用して平均を求め，グラフ化をしてみました．
aws-cli の list_objects_v2() 関数 の 取得上限 1000 オブジェクトの罠

S3 バケットから、数テラのデータをダウンロードしていて、ハマりました。

AWS の 関数、list_objects_v2() は、1000件までしかオブジェクトを取得できないという問題があるようでした。
https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListObjectsV2.html

これを回避するには、1000件ごとに、Continuationtoken を渡してやる必要があるようです。

結果的には、普通に以下のコマンドでダウンロードできました。内部的に、Continuationtoken を渡しているようです。

```
# aws s3 sync s3://<bucket_name> f:\<folder_name>
```

試しに、Python スクリプトを作成して今回は Windows11 で実行してみましたができていたようでした。

```python
import boto3
import os

s3_client = boto3.client('s3')

def download_dir():
    """
    params:
    - prefix: pattern to match in s3
    - local: local path to folder in which to place files
    - bucket: s3 bucket with target contents
    - client: initialized s3 client object
    """

    local='.\<folder_name>'
    bucket='<bucket_name>'
    prefix=''

    keys = []
    dirs = []
    next_token = ''
    base_kwargs = {
        'Bucket':bucket,
        'Prefix':prefix,
    }
    while next_token is not None:
        kwargs = base_kwargs.copy()
        if next_token != '':
            kwargs.update({'ContinuationToken': next_token})
        results = client.list_objects_v2(**kwargs)
        contents = results.get('Contents')
        for i in contents:
            k = i.get('Key')
            if k[-1] != '/':
                keys.append(k)
            else:
                dirs.append(k)
        next_token = results.get('NextContinuationToken')
    for d in dirs:
        dest_pathname = os.path.join(local, d)
        if not os.path.exists(os.path.dirname(dest_pathname)):
            os.makedirs(os.path.dirname(dest_pathname))
    for k in keys:
        dest_pathname = os.path.join(local, k)
        if not os.path.exists(os.path.dirname(dest_pathname)):
            os.makedirs(os.path.dirname(dest_pathname))
        client.download_file(bucket, k, dest_pathname)

if __name__ == "__main__":
    client = boto3.client('s3')
    resource = boto3.resource('s3')
    download_dir()
```
（参考）
https://newbedev.com/boto3-to-download-all-files-from-a-s3-bucket
MicroPythonにおけるBLEモジュールのTips

# はじめに
今回は前のBLEの実装作業の続きとして、BLEモジュールを使っていくつかのTipsを紹介します。本文にはubluetoothを使って送信時に必要なパケットの圧縮形式と受信で識別する時の識別子の話となります。
前回:
[ESP32でBLEの実装作業【スキャン】](https://qiita.com/C0118185/items/753ca056443eca574be4)
[ESP32でBLEの実装作業【ブロードキャスト】](https://qiita.com/C0118185/items/32da60974e1593e0587b)
[ESP32でBLEの実装作業【ペリフェラルとセントラル】](https://qiita.com/C0118185/items/7de0bf99eb315d46258a)
## BLEのパケット
BLEは送信、および受信の際に圧縮されたC言語で定義したパケットで行われています。ustruct.unpack("<h", data)[0]によって圧縮されたデータを読み取ることができます。ここに注意してほしいのは最初の"<h"部分です。これはリトルエンディアンのshortと定義されているという意味であり、データを圧縮と解凍する時に[参照する形式](https://qiita.com/pashango2/items/5075cb2d9248c7d3b5d4)の一種です。基本的に送信側が圧縮するパケットの形式と受信側の解凍する形式は一致しなければなりません(自分は異なる形式をやった結果buffer too smallというエラー文が出ってきました)。
### コード
ここに[ESP32でBLEの実装作業【ペリフェラルとセントラル】](https://qiita.com/C0118185/items/7de0bf99eb315d46258a)のスキャンプログラムを例として、注目してほしいのはdef_update_valueの三行目です。前述の通りでここに使っている解凍する形式は"<h"であるリトルエンディアンのshortとなります。



```ble_scanner.py
#パケットの解凍形式
class BLETemperatureCentral:
    def __init__(self, ble):
        self._ble = ble
        self._ble.active(True)
        self._ble.irq(self._irq)

        self._reset()
   def _update_value(self, data):
        # Data is sint16 in degrees Celsius with a resolution of 0.01 degrees Celsius.
        self._value = struct.unpack("<h", data)[0] / 100
        return self._value

```

## BLEの識別
BLEデバイスお互いに一般的にUUIDという[サービス番号](https://btprodspecificationrefs.blob.core.windows.net/assigned-values/16-bit%20UUID%20Numbers%20Document.pdf)で識別していますが、しかし同じようなサービス番号を使うことも時々起きます。その解決の一種となるのは、UUIDだけではなく、サービスの名前も合併して一緒に識別子とします。
### コード
ここに[ESP32でBLEの実装作業【ペリフェラルとセントラル】](https://qiita.com/C0118185/items/7de0bf99eb315d46258a)のスキャンプログラムを例として、注目してほしいのはdef _irqの5行目です。今はUUIDのみ識別子を使っています。この行の後ろにand decode_name(adv_data) == "好きな名前"と追加していればサービスの名前も一緒にif分に入れ、自分のデバイスしか識別できないようにすることができます。すると5行目下記の通りになります。
if adv_type in (_ADV_IND, _ADV_DIRECT_IND) and _ENV_SENSE_UUID in decode_services(adv_data) and decode_name(adv_data) == "好きな名前":
(もちろんペリフェラルの方からもnameの設定を"好きな名前"にしないとなりませんね)



```ble_scanner.py
_IRQ_SCAN_RESULT                     = const(5)
#EVENT中で識別子を書き換える
class BLETemperatureCentral:
    def __init__(self, ble):
        self._ble = ble
        self._ble.active(True)
        self._ble.irq(self._irq)

        self._reset()
    def _irq(self, event, data):
        if event == _IRQ_SCAN_RESULT:
            addr_type, addr, adv_type, rssi, adv_data = data
            print('type:{} addr:{} rssi:{} data:{}'.format(addr_type, ubinascii.hexlify(addr), rssi, ubinascii.hexlify(adv_data)))
            if adv_type in (_ADV_IND, _ADV_DIRECT_IND) and _ENV_SENSE_UUID in decode_services(adv_data) and decode_name(adv_data) == "好きな名前":
                # Found a potential device, remember it and stop scanning.
                self._addr_type = addr_type
                self._addr = bytes(
                    addr
                )  # Note: addr buffer is owned by caller so need to copy it.
                self._name = decode_name(adv_data) or "?"
                self._ble.gap_scan(None)



```