OmniAICreator/Qiita-1.07M huggingface dataset:
wingetコマンドによるブラウザやAdobe、Teamsアプリの更新

# はじめに
こんにちは。最近ChromeやAdobeなどのアプリがCUIで更新できると知って、「あれ？　じゃあ　１つ１つアプリ開きながらバージョン確認してインストールする必要なくね？」と思いつきこの記事を書きました。

::: note warn
【注意】
検証を重ねてコード掲載に至っておりますが、ご利用は最新の公式情報を参照の上、自己責任でお願い申し上げます。
また、ある程度コマンドプロンプトや、PowerShellを実行経験済みの方が参考にされる事をお勧めします。
一部手順を省略している場合があり、お使いのOS環境によりましては上手く動作しない場合がございます。
:::

::: note warn
【注意②】
全てのアプリが対応しているわけではありません。
    `winget lsit` or `winget search アプリ名` を実行し以下の`ソース`部分が`winget`表示があればコマンドを使えます。

 `　　　` 無表示や`unnown`などは利用できません。
 
:::

```:Power Shellで'winget list'を実行した例
名前        　　　　ID     　　　　　　　　　　  バージョン   　　   利用可能 　　　ソース
---------------------------------------------------------------------------------------
Google Chrome    Google.Chrome        　　　　  124.0.6367.156      125        　 winget

iTunes　　　　　  AppleInc.iTunes_nzyj5cx40ttqa
```



## 【動作概要】
・PCを起動するごとに自動でChromeやAdobeなどのアプリをダウンロードからインストールまでやる。

## 【使用環境】
OS:Windows11　23H2
ソフト:BATファイル、PowerShell　※正確な表現ではないです。
コマンド:wingetコマンド、cmdコマンド、


## 【作業】

①メモ帳を開いて 次のコードを貼り付ける。


```:.batファイルのコード
@echo off
pushd %~dp0
rem このバッチファイル自体を管理者実行
if not "%1"=="am_admin" (powershell start -verb runas '%0' am_admin & exit /b)

set /p answer="WindowsUpdateとWingetを実行しますか？(y/n):"

if "%answer%" == "y" (
  echo 実行します。
powershell -Noprofile -ExecutionPolicy Unrestricted .\AppUpd.ps1
) else if "%answer%" == "n" (
  echo かしこまりました。実行を中止します。
) 
```

②`AppUpd.bat`のファイル名をつける。

③保存先を`C:\Users\***\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`に指定する。

※「***」にはお使いのユーザー名が入ります。


④`エンコード:`と書かれた右側部分をクリックして`ANSI`を選択し保存する。

⑤再度メモ帳を開いて以下のコードを貼り付ける。　※お使いの環境によっては`「pwsh.exe」`の場所が異なります。

```:.ps1ファイルのコード
Start-Process -FilePath "C:\Windows\System32\cmd.exe"-Verb runas "/c start ms-settings:windowsupdate"-Wait
Start-Process -FilePath "C:\Windows\System32\cmd.exe"-Verb runas "/c start ms-settings:windowsupdate-action"
Start-Process -FilePath "C:\Program Files\PowerShell\7\pwsh.exe" -argumentList "-noexit -command winget search --id Google.Chrome -e`
;winget upgrade --id Google.Chrome --force`
;winget upgrade --id Adobe.Acrobat.Reader.64-bit --force"
```

⑥`AppUpd.ps1`のファイル名をつける。


⑦保存先を`C:\Users\***\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup`に指定する。

※「***」にはお使いのユーザー名が入ります。


⑧`エンコード:`と書かれた右側部分をクリックして`ANSI`を選択し保存する。


#　以上です。最後まで閲覧いただきありがとうございました。
Source Orbit：ソース・オービット IBM i のアプリコードDevOps・可視化ツール

※一先ず覚書です。随時更新したいと思います。（※現時点では以下間違っている箇所があるかもしれません。）

## Source Orbitとは？
■公式サイト：https://ibm.github.io/sourceorbit/#/

上記URL内容を一文にまとめると、
***IBM i アプリ開発にGit(DevOps,CI/CD)を組み込もうとする際、昔のAS/400時代からのオブジェクトやライブラリー階層表現のままだと扱いにくいライブラリー構造やオブジェクト依存関係をGitなど標準的なリポジトリとシームレスに扱えるようにするための機能群。DevOps,CI/CDにRPG,COBOLなどIBM i アプリコードを乗せるための機能。基本的にはIBM i ネイティブのソースコード類そのままではGit等で扱いにくいためテキストファイル化してソースコード管理する形態に移行する想定。エディター・IDEとしてVSCodeなどを想定している***

ということになるでしょうか。
概要機能として、
・該当するすべてのソースコードをスキャンして依存関係ツリーを構築
・既存の？オブジェクトが開発しているコードにどのように影響するかを依存関係をサーチしてVSCode内で表示できる
・JSONアプリビルドスクリプトを生成する機能
・作業中のブランチに関するレポートを生成する
・IBM i 上のソース類をGit移行するためのツール群

たとえば、上：ライブラリー形式→下：Git他のリポジトリーと共通化
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/fb6470da-ced9-339e-d076-ce68e8669286.png)

長いファイル名をサポートしたりできるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/a221fc6a-71fa-2cb1-15e9-85ae14ac2166.png)

本日時間無くボチボチ調べてみたいと思います。
Lambdaを使うときのセッション管理方法

# LambdaはStatelessアーキテクチャ
Lambda関数は通常、ステートレスなアーキテクチャで実装されます。これは、各リクエストが独立しており、Lambda関数間で状態を共有しないことを意味します。この場合、セッション管理はクライアント側で行われることが一般的です。クライアントは、認証トークンやセッションIDを使用して状態を管理し、必要に応じてそれらをリクエストに含めます。

# 外部のセッション管理サービスを使用する
外部のセッション管理サービス（例えば、Amazon CognitoやAuth0など）を使用して、ユーザーの認証とセッション管理を行うことができます。これらのサービスは、ユーザーの認証情報やセッション情報を安全に管理し、Lambda関数からそれらの情報にアクセスすることができます。

# ステートフルなLambda関数
一部のユースケースでは、ステートフルなLambda関数が必要な場合があります。この場合、Lambda関数内でセッション状態を保持する必要があります。例えば、DynamoDBやElastiCacheなどのデータストアを使用して、セッション情報を保持することができます。ただし、このアプローチはコストやパフォーマンスの観点から検討する必要があります。

# API Gatewayのセッション管理
API Gatewayを使用してLambda関数にアクセスする場合、API Gatewayのセッション管理機能を使用することができます。API Gatewayは、認証やアクセス制御、リクエスト/レスポンスの変換などの機能を提供し、Lambda オーソライザー（JWT）を利用するなり、セッション管理そのものではないが、認可という形の疑似的なセッション管理を行うことができます。

# 参考サイト
https://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html
RSAを感覚で理解する

# RSA暗号とは
大きな素数を素因数とする数の素因数分解が困難であることを根拠とした暗号技術の一つ。
シンプルだが、強力である。
公開鍵で暗号化し、秘密鍵で復号する。

## RSA暗号を使ったやり取りの仕組み
### 【鍵の生成】
まず、データの受信者が公開鍵と秘密鍵を準備する。
具体的に作る鍵の種類と作り方は以下の通り。

① 非常に大きく、ランダムである任意の素数「p」
② 非常に大きく、ランダムである任意の素数「q」 ※ただし、p≠q
③ pとqの積「n」
④ (p-1)(q-1)と互いに素(=最大公約数が1となる)な任意の自然数「e」
⑤ edを(p-1)(q-1)で割った余りが1となるような任意の自然数「d」

「n」と「e」を公開鍵、「p」「q」「d」を秘密鍵となる。

### 【暗号化】
データの送信者は、受信者から受け取った公開鍵「n」「e」を使ってメッセージを暗号化する。

具体的には、
1. メッセージを数値「α」に変換する ※ただし、α<n
1. αのe乗をnで割った余りを求め、それを暗号文「y」とする

送信者は、暗号文「y」を受信者に送る。

### 【復号化】
受信者は送られてきた暗号文「y」を秘密鍵「d」を使って復号し、平文を取得する。

具体的には、
1. yのd乗をしたものを「β」とする
1. βをnで割った余りを求め、それを平文「Σ」とする

## あとがき
nは公開されている。
んで、復号するにはdが必要。
さらに、dを得るにはpとqが必要。

p×qで算出されるnは公開されているが、nからpとqを逆算するにはとんでもねー手間がかかるため、やってらんねーてこと。
【flutter】showDatePickerを使ってカレンダーから日時を選択したい。

こんにちは！
今回はshowDatePickerを使って、カレンダーから日時を選択する方法について、紹介したいと思います。


## 実装例

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/b50f8e7e-ca0e-be16-7b02-b097f9c6525e.gif)


## 実装コード
```php
Future<void> _selectDate(BuildContext context) async {
    final DateTime? picked = await showDatePicker(
        context: context,
        initialDate: selectedDate,
        firstDate: DateTime(2023),
        lastDate: DateTime(2026),
        barrierColor: Colors.green);

    if (picked != null) {
      setState(() {
        selectedDate = picked;
      });
    }
  }

```
```php

  Text(
'選択したい日付${selectedDate.year}/${selectedDate.month}/${selectedDate.day}'),

  const SizedBox(height: 20),
  
  SizedBox(
    height: 60,
    width: 150,
    child: ElevatedButton(
      style: ElevatedButton.styleFrom(
          backgroundColor: Colors.green,
          side: const BorderSide(color: Colors.white)),
      onPressed: () => _selectDate(context),
      child: const Text(
        '日付選択',
        style: TextStyle(color: Colors.white),
      ),
    ),
  ),
```

参考にさせていただいたサイト

https://hiyoko-programming.com/2101/


## 終わり

今回の記事は以上になります。
今回も最後まで読んでいただきありがとうございました！
では、また次の記事で〜！
Dockerfileの中身がわからない

# 背景
Dockerまわりの勉強をしてると色々な先人の手順が簡単にヒットして「とりあえず動いたやったー」となりがちなので、ちゃんとDockerfileの中身を見てみようと思いました。
今回は

https://zenn.dev/maha17/articles/ade5fc1dbc9d02

のサイトをもとに勉強を進めていて利用したDockerfileです。

__注意点__
- 筆者は自分で使うシステム用にDockerを使う程度なので、「載っているコードは企業で使うならこうすべきではない」などあるかもしれません。
- この記事は初学者向けです。


# Dockerfileってなに？
DockerはDockerfileに記述された命令を読み込んで自動的にイメージのビルドができる。

docker buildでDockerfileに記述された命令を順番に実行してくれるので、簡単に同じ環境を作れる。　つまり勉強の過程でやり直したい時や、開発の時に複数人で環境を揃えたい時にDockerfileを共有すれば簡単にできる。えらいね。

<br>

記述の形式は以下の通り
```
命令 引数
```

<br>

今回作りたい環境のDockerfileはこちら。

```dockerfile
FROM python:3
ENV PYTHONUNBUFFERED=1
RUN mkdir /backend
WORKDIR /backend
ADD requirements.txt /backend/
RUN pip install -r requirements.txt
ADD . /backend/
```

__FROM__
これから命令するベース・イメージを指定している。今回はpythonのイメージで、バージョン3を使いますよーということ。(公式のURLはhttps://hub.docker.com/_/python)

<br>

__ENV__
ここでは環境変数PYTHONUNBUFFEREDに1を設定するよということ。

→PYTHONUNBUFFEREDは空文字に設定されない場合には標準出力・標準エラーのストリームのバッファリングを行わない設定になる<br>
参考元のサイトでは
```dockerfile
ENV PYTHONUNBUFFERED 1
```
と書いてあったが公式のリファレンスを見てると、やんわり非推奨と書かれていたので変更してます。
<br>
__RUN__
引数に書かれてるコマンドを実行する。今回はbackendディレクトリを作るコマンドを設定してます。

<br>

__WORKDIR__
以降で実行するコマンドを実行するディレクトリを指定するコマンド。

今回の場合には、「一つ上のRUNコマンドで作成したbackendディレクトリで以降のコマンドを実行してね」と指定してます。
<br>
__ADD__
追加元　追加先で指定して追加元のファイルを追加先に指定したディレクトリに作成できます。

今回の場合にはrequirements.txt(あとで使う)をbackendディレクトリに追加しています。

# まとめ
ハンズオンで学ぶ時になんとなく放置して進んでいたDockerfileを再確認できてよかった。
もっと深く理解すれば手作業がもっと減るはず、なのでどんどん活用していきたい。


# 参考文献
- [Python Tips: Docker で Python を使うときに有用な環境変数まとめ
](https://www.lifewithpython.com/2021/05/python-docker-env-vars.html)


- [1. Command line and environment
](https://docs.python.org/3/using/cmdline.html#cmdoption-u)
Unity Catalogのアセットが最近使用したアイテムに表示されるようになりました！

Databricksのホームページで気づきました。

![Screenshot 2024-05-09 at 14.23.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/8e282cb2-01e0-7153-f2dc-179a4fec7d77.png)

> 最近使用したアイテムにすべてのUnity Catalogのアセットが表示されるようになりました。これには、カタログ、スキーマ、テーブル、モデル、ボリューム、関数が含まれます。

アクセスしてみると確かに。
![Screenshot 2024-05-09 at 14.27.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/dce50838-8d12-770a-8339-5d2b54a6053d.png)

作業している際にアクセスするテーブルやスキーマ(データベース)、モデル、ファイルなどは固定される傾向があると思いますので、便利な機能だと思います。是非ご活用ください！

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
Eclipseでbuildごとに派生チェックが外れるのを防ぐ

# 事象
- ビルドするたびにtargetフォルダの派生チェックが外れて検索結果に紛れ込んでくる

# 解決方法
1. AutoDeriv 1.4を Eclipse マーケットプレースからインストールする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287745/875becea-5f86-911e-0f27-90f8c9e9be50.png)
1. インストールしたらEclipseは再起動が必要
1. プロジェクト直下に「.drived」ファイルを作成。ファイルは置くだけでOK
   （内容は [公式サイト](https://nodj.github.io/AutoDeriv/) から）
   ```
   # そのまま書くと派生
   target
   ext
   
   # but don't affect the 'keep' sub-folder
   # keepのサブフォルダを派生から除外したい場合はビックリマークつける
   !target/keep
   
   # all files with a '.dep' extension are generated
   # .depと付くものは全て派生にしたい場合：
   *.dep
   
   src/include/version.h
   # this specific file is also generated.
   # こういう書き方でも派生となる。


# 環境
- Eclipse 2024-03
- Javaの開発
- mavenビルド
修正したファイルにのみ prettier をかけたいワンライナー

# 概要

```zsh
$ npx prettier --write $(git diff --name-only $(git branch --contains | cut -d ' ' -f 2))
```

# 解説

```zsh
$ git branch --contains

　　　　feature/hoge
* feature/fuga
　　　　feature/piyo
```

```zsh
$ git branch --contains | cut -d ' ' -f 2

feature/fuga
```

```zsh
$ git diff --name-only $(git branch --contains | cut -d ' ' -f 2)

src/pages/Hoge/index.tsx
src/components/Fuga/style.scss
```
【visionOS】WorldTrackingProviderでApple Vision Proの位置と向きを取得する

本記事ではApple Vision Proの位置と向きを取得する方法を紹介します。

## デモ

![画面収録 2024-05-09 13.23.00.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1042576/075baec7-cb7f-a94d-27c2-f9524e11cf8e.gif)

## 方法

ARKitSessionを`WorldTrackingProvider`で開始し、その`queryDeviceAnchor(atTimestamp:)`メソッドを呼び出すことで、`DeviceAnchor`を作成します。
`DeviceAnchor`にはApple Vision Proのデバイスの位置と向きの情報が含まれています。
`originFromAnchorTransform`を使用することで、デバイスから見た空間における平面の位置と方向を取得することができます。

## 実装

### ImmersiveViewModel

```swift
@MainActor
final class ImmersiveViewModel: ObservableObject {

    let session = ARKitSession()
    let worldInfo = WorldTrackingProvider()

    func run() async {
        do {
            try await session.run([worldInfo])
            await handleAnchorUpdates()
        } catch {
            assertionFailure("Failed to run session: \(error)")
        }
    }

    func handleAnchorUpdates() async {
        for await update in worldInfo.anchorUpdates {
            switch update.event {
            case .added, .updated:
                print("Anchor position updated.")
            case .removed:
                print("Anchor position now unknown.")
            }
        }
    }
    
    func getTransform() async -> simd_float4x4? {
        if let anchor = worldInfo.queryDeviceAnchor(atTimestamp: CACurrentMediaTime()) {
            return anchor.originFromAnchorTransform
        }
        return nil
    }
}
```

WorldTracking用に設定されたARKitSessionを使用し、DeviceAnchorのUpdate情報の受信を開始します。

### ImmersiveView

```swift
struct ImmersiveView: View {
    @StateObject var vm: ImmersiveViewModel = ImmersiveViewModel()
    @State var timer = Timer.publish(every: 0.02, on: .main, in: .common).autoconnect()
    
    var body: some View {
        RealityView { content in
            // Add the initial RealityKit content
            if let scene = try? await Entity(named: "Immersive", in: realityKitContentBundle) {
                content.add(scene)
            }
        }
        .task() {
            await vm.run()
        }
        .onReceive(timer) { _ in
            Task {
                let transform = await vm.getTransform()
                let position = SIMD3<Float>(transform!.columns.3.x, transform!.columns.3.y, transform!.columns.3.z)
                print("x: \(position.x), y: \(position.y), z: \(position.z)")
            }
        }
    }
}
```

Apple Vision Proデバイスの位置と向きが0.02秒ごとに取得され、コンソールに表示されます。これにより、ARコンテンツをデバイスの動きに合わせて更新することが可能になります。

(※技適特例届け出済み)

## 参考資料
https://developer.apple.com/documentation/visionos/tracking-points-in-world-space

https://developer.apple.com/documentation/arkit/worldtrackingprovider

https://developer.apple.com/documentation/arkit/worldtrackingprovider/4293525-querydeviceanchor

https://developer.apple.com/documentation/arkit/deviceanchor

https://developer.apple.com/documentation/arkit/planeanchor/4293522-originfromanchortransform
UnityECSでGameObjectリストからEntityリストを作成する

Unity 6000.0.0f1
Entities 1.2.1

Unity EntityComponentSystem(ECS)勉強中の者です。
間違っていたらすみません。
IComponentDataはそもそもリストや配列は使用できません。
```NativeArray<Entity>```なら！と思いましたがこれも

:::note alert
ArgumentException: srcEntity is not a valid entity
:::

というエラーでダメでした。
結論、リストや配列のGameObjectをECSで使用するにはDynamicBufferを使用するといいみたいです。

```c#:SampleComponentData.cs
//コンポーネントデータ
public struct SampleComponentData : IComponentData { }
//バッファ
public struct SampleElement : IBufferElementData
{
    public Entity sampleEntity;
}
```

```c#:SampleAuth.cs
//オーサリング
public class SampleAuthoring : MonoBehaviour
{
    public List<GameObject> prefabs;
}
public class SampleAuthoringBaker : Baker<SampleAuthoring>
{
    public override void Bake(SampleAuthoring authoring)
    {
        var entity = GetEntity(TransformUsageFlags.Dynamic);
        var sample = new SampleComponentData();
        var buffer = AddBuffer<SampleElement>(entity);
        for (int i = 0; i < authoring.prefabs.Count; i++)
        {
            var prefab = authoring.prefabs[i];
            buffer.Add(new SampleElement
            {
                sampleEntity = GetEntity(prefab, TransformUsageFlags.Dynamic)
            });
        }
        AddComponent(entity, sample);
    }
}
```
使用例
```c#:SampleSystem.cs
//システム
public partial struct SampleSystem : ISystem
{
    [BurstCompile]
    public void OnUpdate(ref SystemState state)
    {
        foreach (var buffer in SystemAPI.Query<DynamicBuffer<SampleElement>>().WithAll<SampleComponentData>())
        {
            for (int i = 0; i < buffer.Length; i++)
            {
                var entity = buffer[i].sampleEntity;
                state.EntityManager.Instantiate(entity);
            }
        }
        state.Enabled = false;
    }
}
```
手軽にできる！RailsとNext.jsでURL上のIDを暗号化する方法

## はじめに
Rails APIとNext.jsで開発しているときにURL上でIDを平文のまま載せたくない場合の対策になります。


## 実現させたいこと
具体的には、Rails APIをバックエンドに、Next.jsをフロントエンドとして利用し、
ユーザーがブラウザ上でproduct/1などといったIDを直接露出する形ではなく、暗号化したIDを使ったURL（例: product/暗号化されたID）を用いて、データへのアクセスを行います。
```mermaid
sequenceDiagram
  participant Frontend as Next.js
  participant Backend as Rails API

  Frontend ->>+ Backend: 暗号化したIDでデータにアクセス要求
  Backend ->>+  Backend: 暗号化したIDを復号化し、該当するデータをデータベースから検索
  Backend ->>+ Frontend: 応答として必要なデータを返送
```

## やること
### Rails API側
やることとしては共通鍵を設定し、暗号/復号化時にそれを参照するようにしています。
```app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  before_action :set_encryptor

  private

# 共通鍵生成関数
  def set_encryptor
    # Rails.application.credentials.saltには32文字の適当な英数字を設定
    secret_key = Rails.application.credentials.salt
    # secret_keyを使用して暗号/復号化のための共通鍵を作成
    @encryptor = ActiveSupport::MessageEncryptor.new(secret_key[0, 32])
  end

# 暗号化用関数
  def encrypt(data)
    encrypted_data = @encryptor.encrypt_and_sign(data)
    Base64.urlsafe_encode64(encrypted_data)
  end

# 復号化用関数
  def decrypt(data)
    decoded_encrypted_data = Base64.urlsafe_decode64(data)
    @encryptor.decrypt_and_verify(decoded_encrypted_data)
  end
end
```

### Next.js側
フロント側はもらったものを表示するのみのため実装例は不要と思いますが記載します。
```pages/product/[encryptedId].js
import axios from 'axios';
import { useRouter } from 'next/router';
import { useState, useEffect } from 'react';

function ProductPage() {
  const { encryptedId } = useRouter().query;
  const [product, setProduct] = useState({});
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    if (!encryptedId) return;
    axios.get(`/api/products/${encryptedId}`)
      .then(response => {
        setProduct(response.data);
        setLoading(false);
      })
      .catch(error => {
        setError(error);
        setLoading(false);
      });
  }, [encryptedId]);

  if (loading) return <div>Loading...</div>;
  return (
    <div>
      <h1>{product.name}</h1>
      <p>{product.description}</p>
      <p>${product.price}</p>
    </div>
  );
}

export default ProductPage;

```

### 最後に
今回はRailsとNext.jsでActiveSupport::MessageEncryptorを使用する方法を紹介しましたが、
別のアプローチはたくさんあるのでケースによって検討いただければと思います。
GitHub Actions Hadolint でDockerfileを静的解析する

## 概要

Hadolint(Haskell Dockerfile Linter)
https://github.com/hadolint/hadolint

HadolintはHaskellで実装されているDockerfileのLinterツールです。
ベストプラクティスなDockerイメージの構築を手伝ってくれる便利なツールです。

## Gitシリーズ記事まとめ

https://qiita.com/ucan-lab/items/33c63a402f533aa92f3e

## 利用するアクション

https://github.com/hadolint/hadolint-action

Hadolintのアクションが用意されているのでこちらを使います。

## 実装

- `.github/workflows/hadolint-testing.yaml`

```yaml:.github/workflows/hadolint-testing.yaml
name: Hadolint Testing
on:
  pull_request:
    types: [synchronize, opened, reopened]
jobs:
  hadolint-testing:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: hadolint/hadolint-action@v3.1.0
        with:
          recursive: true
```

`recursive` オプションを有効にすると `Dockerfile` を再帰的に検索してくれます。
本当は複数のDockerfileをパス指定したかったのですが、オプションとしては用意されてなさそうでした。

https://github.com/hadolint/hadolint-action?tab=readme-ov-file#inputs

Dockerfileの配置場所としては下記の3箇所に置きましたが問題なく検査してくれました。

```terminal
infra
└── docker
   ├── mysql
   │  └── Dockerfile
   ├── nginx
   │  └── Dockerfile
   └── php
      └── Dockerfile
```

## 実行結果

![ScreenShot 2024-05-09 13.32.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/52879/53497119-32c5-4b42-da5d-f7076fe6209d.png)

実行自体は秒で終わってます。

```
Error: infra/docker/php/Dockerfile:19 DL3008 warning: Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
Error: infra/docker/php/Dockerfile:19 DL3009 info: Delete the apt-get lists after installing something
```

エラーについてはHadolintのWikiに直し方も合わせて記載があります。

https://github.com/hadolint/hadolint/wiki

`DL3009 info: Delete the apt-get lists after installing something` に関してはこれです。

https://github.com/hadolint/hadolint/wiki/DL3009

## ルール変更したい場合

プロジェクトルートに `.hadolint.yaml` を配置するか `config` オプションで設定ファイルを参照してくれます。

詳細はこちらです。
https://github.com/hadolint/hadolint#configure

今回は特にルールは変更しませんでした。

## 業務改善度: ★★★★★

これを設定しておけば綺麗なDockerfileを書かざるを得なくなるのでおすすめです。
もっと早く設定しておけばよかった！
Mac で NAS (SMB) のファイルが見えない問題を Unicode 正規化方式を変えて解決

先日，NAS (SAMBA) にあるファイル（フォルダ）が Mac から見えないという事象に遭遇しました。どうやら，Unicode の正規化形式に起因する問題であったようです。文字コードの正規化形式を変換して解決しました。

## 症状と原因

まず，今回の障害の症状と原因を整理しておきます。

##### 症状

- NAS (SAMBA) に保存されている一部のファイルが，Mac PC から見えない 
- 同ファイルは，Windows/Linux からは見えている

##### 原因

どうやら，このファイルのファイル名に使われている文字コード（正確には正規化方式）が NFD 形式であったためのようです。所謂，**NFC/NFD問題**（Unicode正規化形式の違いに起因する問題）と呼ばれるもので，MacOS で作成したファイルで起こりがちなようです。

https://zenn.dev/hacobell_dev/articles/68ccc92bffd6cc


## NFD → NFC 変換

問題解決のために，ファイルの名前を UTF-8 (NFC) 形式に変換する Python のプログラムを作成しました。[奥村晴彦さんのページ](https://okumuralab.org/~okumura/python/encoding.html)を参考にしています。当プログラムを起動すると，起動プログラムと同じ階層とそれ以下の階層にあるファイル（フォルダ）の名称を UTF-8 (NFC) 形式に変換します。

```python:rename_to_nfc.py
import os
import pathlib
import unicodedata

files = pathlib.Path('.').glob('**/*')
for file in files:
    filename = str(file)
    nfc_filename = unicodedata.normalize('NFC', filename)
    if filename != nfc_filename:
        print('Renaming ', filename.encode('utf-8'), 'to ', nfc_filename.encode('utf-8'))
        os.rename(filename, nfc_filename)
```

テストしてみます。
まず，テストに使用する NFC/NFD 形式のファイルを作成します。次のプログラムで作成できます。

```python:gen_nfc_files.py
import os
import unicodedata

s = 'ヴィジブル'
s_nfc = unicodedata.normalize('NFC', s).encode('utf-8')

os.makedirs('nfc_files', exist_ok=True)
os.chdir('nfc_files')
with open(s_nfc, 'w') as f:
    pass
```

```python:gen_nfd_files.py
import os
import unicodedata

s = 'インヴィジブル'
s_nfd = unicodedata.normalize('NFD', s).encode('utf-8')

os.makedirs('nfd_files', exist_ok=True)
os.chdir('nfd_files')
with open(s_nfd, 'w') as f:
    pass
```

NFC 形式の「ヴィジブル」と NFD 形式の「インヴィジブル」という名称のファイルが生成されます。この状態で Mac PC から NAS (SAMBA) にアクセスしても「ヴィジブル」しか見えません（今回の障害の主題です）。

先ほどの変換プログラムを実行してみます。

```sh
python rename_to_nfc.py
```

これですべてのファイルがNFC 形式に統一されたので「インヴィジブル」も見えるようになりました。

以上，**Mac PC から NAS (SAMBA) のファイルが見えない問題を，文字コードの正規化形式を変換して解決**しました。

## NAS のマウント

話が前後しますが，そもそも NAS (SMB) にアクセスして上記の変換プログラムを起動するためには，ローカル PC にマウントできていなければならないはずです。NAS のマウントは以下のようにして行います。

##### MacOS の場合：
```sh
mount -t smbfs //user@smbserver/share ~/mnt/smb
```

##### Linux の場合：
```sh
sudo mount -t cifs -o uid=$(id -u),gid=$(id -g),username=user //smbserver/share ~/mnt/smb
```

## 感想

Unicode 正規化方式として NFD を採用しているのは Mac なのに，SMB (NAS) を介してみると当の Mac だけがそういったファイルを認識できない（ことがある）というのはなんとも皮肉な結果ですね...。
SAP S/4HANA Cloudの製品とシステムバージョン確認方法

# Build-in supportボタンクリック

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821335/b0b334e4-ae1f-47c7-bd4d-e6c994a28e14.png)

# accountボタン
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821335/dd0bc376-5e8c-e774-d70d-358080a1ad83.png)

# System Context Information
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821335/bb8dcc1c-b238-3ced-b88d-05da05426874.png)

# Product and Version:SAP S/4HANA Cloud 2402
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821335/8047dd7e-f274-9bfc-8072-629a4e129176.png)

## 余談:ACH componentとは
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2821335/3eae522f-0f9f-0553-a5ce-1e2263c6f32c.png)

>Application component hierarchy (SAP's component names for bug reports); attribute is mandatory for SAP apps, but is not used so far for apps developed outside SAP

>アプリケーションコンポーネント階層（バグ報告用のSAPのコンポーネント名）。この属性はSAPアプリには必須ですが、SAP以外で開発されたアプリでは今のところ使用されていません。

とのことです。インシデント登録やnote検索の時に使えそうですね。
学習日記（エンジニアへの道）

CSSの基礎を学ぼう②〜文字・寸法の装飾〜
①エリアを作る要素
②一般的な装飾の設定（背景色・テキスト・グラデーション・透明度）
③余白の設定（外側・内側の余白・枠線の設定）
④要素のサイズ変更（高さ・幅）
⑤セレクタの複数指定

CSSの基礎を学ぼう③〜エリア配置の装飾〜
　今回の学習では、要素の配置について学習。
前回学習したpadding、marginとは違い、一定間隔で要素を配置できるように学習を進める。
ブロック要素とインライン要素の理解度によって学習の進み具合が変わってくるので、心配な方はブロック要素とインライン要素について再度見直し！

学習の内容
①ブロック要素の性質
②要素の配置
③displayの使い方

特に③displayプロパティの使い方は整理が必要かな
ブロック要素、インライン要素を変更できるプロパティ
『flex:横並び』display: flex ;
・水平方向の配置調整には<justify-content>プロパティ
・垂直方向の配置調整には<align>プロパティ
を使用しますので、<display：flex；>とセットで覚える！！
・flex-start　行頭寄せ、通常は左揃え（初期値）
・flex-end　行末寄せ、通常は右揃え
・center　センター揃え
・space-between　アイテムの両端にスペースを均等に割り付け
・space-around　アイテムの間にスペースを均等に割り付け
垂直方向の配置調整も同様！
align-items: center　センター揃えなど

一発でブラウザに反映されないし、自分で何がダメなのか見つけることがまだまだできていない。ただ、完成させていること自体は評価して継続して精度を高めていくことを意識してやっていく。
本当の新人プログラマーが『新人プログラマーのためのエラーが怖くなくなる本』を読んでみて

## 今回読んだ本
新人プログラマー向けの研修カリキュラムとして、『[コードが動かないので帰れません！ 新人プログラマーのためのエラーが怖くなくなる本](https://www.amazon.co.jp/%E3%80%90Amazon-co-jp-%E9%99%90%E5%AE%9A%E3%80%91%E3%82%B3%E3%83%BC%E3%83%89%E3%81%8C%E5%8B%95%E3%81%8B%E3%81%AA%E3%81%84%E3%81%AE%E3%81%A7%E5%B8%B0%E3%82%8C%E3%81%BE%E3%81%9B%E3%82%93%EF%BC%81-%E6%96%B0%E4%BA%BA%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E6%80%96%E3%81%8F%E3%81%AA%E3%81%8F%E3%81%AA%E3%82%8B%E6%9C%AC-%E7%89%B9%E5%85%B8-%E3%83%90%E3%82%B0%E3%81%AE%E3%80%8C%E5%9B%B0%E3%81%A3%E3%81%9F%EF%BC%81%E3%80%8D%E8%A7%A3%E6%B6%88%E3%83%95%E3%83%AD%E3%83%BC%E3%83%81%E3%83%A3%E3%83%BC%E3%83%88/dp/479818067X)』を読んでみました。

そもそもまだ実務でエラーと向き合っていない状態のため「エラーが怖い、怖くないもわからない」状態ですが、このレベル感で読んでみても参考になるところがありました。

## 読む前と読んだ後の変化
（読む前）　
エラーが怖いことはないけど、Google先生にコピペでなんとかやるか→２日経過
（読んだ後）
明日エラーが出ることが少しだけ楽しみになりました


## 新人なりに刺さったところ
- エラーメッセージを読み解く手段
独学のころから、「エラーメッセージって大体いつも似たような出だし（invaild、unexpectedとか）だな〜」とは感じていましたが、そのあたりがわかりやすく解説されていた
- ダブルクォーテーションで検索キーワードを囲む
- 関数の実行時、終了時にプリントデバックを仕込む 
- 今日から使えるテクニック
    - 「command + G」エラ〜メッセージに表示されている行:列を入れることで、エラーが発生したコードまで移動
    - Google Chromeのデベロッパーツールを活用したデバッグ（「source」タブでステップイン、ステップアウト、ステップオーバー）


## 総評
以前まで、エラーと戦うための武器は「Google」の剣だけでしたが、「プリントデバッグ」「二分探索」など、斧もハンマーも手に入れることができました。

ですが、「選べる武器が多すぎてどれを選んで良いかわからないし、そもそもレベルが足りなくてもまだ使えない」というのが今の正直な感想です。

内容をバッチリ頭に入れる、というより辞書的な立ち位置として、懐に忍ばせておいていざという時に見返すのがよさそうです。
今後のエンジニア人生で、エラーに苦しめられる前にこの本に触れることができたのは、とても幸いだったと思います。


## 参考文献
- [コードが動かないので帰れません！ 新人プログラマーのためのエラーが怖くなくなる本](https://www.amazon.co.jp/%E3%80%90Amazon-co-jp-%E9%99%90%E5%AE%9A%E3%80%91%E3%82%B3%E3%83%BC%E3%83%89%E3%81%8C%E5%8B%95%E3%81%8B%E3%81%AA%E3%81%84%E3%81%AE%E3%81%A7%E5%B8%B0%E3%82%8C%E3%81%BE%E3%81%9B%E3%82%93%EF%BC%81-%E6%96%B0%E4%BA%BA%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9E%E3%83%BC%E3%81%AE%E3%81%9F%E3%82%81%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E3%81%8C%E6%80%96%E3%81%8F%E3%81%AA%E3%81%8F%E3%81%AA%E3%82%8B%E6%9C%AC-%E7%89%B9%E5%85%B8-%E3%83%90%E3%82%B0%E3%81%AE%E3%80%8C%E5%9B%B0%E3%81%A3%E3%81%9F%EF%BC%81%E3%80%8D%E8%A7%A3%E6%B6%88%E3%83%95%E3%83%AD%E3%83%BC%E3%83%81%E3%83%A3%E3%83%BC%E3%83%88/dp/479818067X)
- [The Developer Coefcient](https://stripe.com/files/reports/the-developer-coefficient.pdf)
packaging.resources.excludesに複数セットする

# 目的
備忘です。packaging.resources.excludesで複数セットするやり方がわからなかったので。

# 環境
- Android Studio Hedgehog | 2023.1.1 Patch 2
- Windows11 Pro

# コード
Android Studioのテンプレートからポチポチしていき、Kotlin DSLを選ぶとデフォルトで次のように記載されています。
```kotlin
packaging {
    resources {
        excludes += "/META-INF/{AL2.0,LGPL2.1}"
    }
}
```

javamailを使う関係で、JavaMailライセンスファイルをピックしろって書いてあるので、なんとかぶち込まなきゃいけないのですが、色々調べてもpackaging.resources.excludesに複数セットする方法が見つかりませんでした。合ってるかわかないけど、次のとおりやってみたら、大丈夫そうでした。（ちゃんと理解できてないけど、エラー吐かないから良しとします・・・。）

```kotlin
packaging {
    resources {
        excludes += "META-INF/LICENSE.txt"
        excludes += "META-INF/NOTICE.md"
        excludes += "META-INF/LICENSE.md"
        excludes += "/META-INF/{AL2.0,LGPL2.1}"
    }
}
```

もしくは、

```kotlin
packaging {
    resources {
        excludes += listOf(
            "META-INF/LICENSE.txt",
            "META-INF/NOTICE.md",
            "META-INF/LICENSE.md",
            "/META-INF/{AL2.0,LGPL2.1}"
        )
    }
}
```

これでもいけた。
```kotlin
packaging {
    resources {
        excludes += "/META-INF/{AL2.0,LGPL2.1,LICENSE.txt,NOTICE.md,LICENSE.md}"
    }
}
```

# REFERENCEs
https://stackoverflow.com/questions/64026598/javamail-api-on-android

https://developer.android.com/reference/tools/gradle-api/8.4/com/android/build/api/dsl/Packaging#resources(kotlin.Function1)

https://javaee.github.io/javamail/Android

https://stackoverflow.com/questions/75172062/how-to-replace-deprecated-packagingoptions-in-android-gradle-build-files

https://zenn.dev/investorx/articles/c36a245fd5e568

# さいごに
お疲れ様でした！
PHPの多次元配列から特定のキーの値を取り出す

普通のPHPerなら知ってて当然かもしれないので自分用の覚え書き。
ユーザー情報等のデータベースを扱うことがよくあって、`FETCH_ASSOC|FETCH_UNIQUE`なんかでデータを取り出すと
```php
print_r($alldata);
/*
Array
(
    [1] => Array
        (
            [id] => 1
            [account] => suzuki
            [sei] => 鈴木
            [mei] => 一郎
            [seikana] => すずき
            [meikana] => いちろう
        )
    [3] => Array
        (
            [id] = 3
            [account] => tanaka
            [sei] => 田中
            [mei] => 次郎
            [seikana] => たなか
            [meikana] => じろう
        )
    [10] => Array
        (
            [id] => 10
            [account] => sato
            [sei] => 佐藤
            [mei] => 花子
            [seikana] => さとう
            [meikana] => はなこ
        )
)
*/
```
こんなふうになってて、この中から`'account'`のリストを取り出したい、というとき。
```php
//BEFORE array_column
foreach (array_values($alldata) as $v) {
    $accounts[] = $v['account'];
}
print_r($accounts);

/*
Array
(
    [0] => suzuki
    [1] => tanaka
    [2] => sato
)
*/

// $accountsのキーをidにしたいとき
foreach (array_values($alldata) as $v) {
    $accounts[$v['id']] = $v['account'];
}
print_r($accounts);

/*
Array
(
    [1] => suzuki
    [3] => tanaka
    [10] => sato
)
*/
```
といった感じだったのが、`array_column`を使うと
```php
//AFTER array_column
$accounts = array_column($alldata, 'account');
print_r($accounts);
//略

// $accountsのキーをidにしたいとき
$accounts = array_column($alldata, 'account', 'id');
print_r($accounts);
//略
```
と簡単。

3次元配列の場合は追って調べて書く。
【ChatGPT＋DALL-E3】gen_idの利点。


　どうも、カーブミラーです。

　今回は、gen_idの利点について、書きます。

:::note info
本記事は、ChatGPT（有料版）
＋DALL-E3で行なっております。
画像はあります。　あしからず。
:::

　とりあえず、ニュース。

　昨日（2024/05/08）から？
　　X-Twitterで
　　　文章生成AIである
　　　　Grokが開放されました。
　　　　　あっ、課金会員だけね😅
***
　さて、本題です。

　一昨日（2024/05/07）の
　　KEITOさんのショート動画で
　　　作成した記事。
[【ChatGPT＋DALL-E3】画像の中にひとりのキャラを複数生成する方法。](https://qiita.com/kabumira/items/b70304c93eb5087945bb)

　この中で、
　　gen_idを使っていました。

　このgen_idとは
　　なんでしょうか？

　ChatGPTさんに
　　尋ねたところ、
　　　このような答えが
　　　　返ってきました。
>　この「gen_id」とは、「generation identifier」の略で、生成されたデータやオブジェクトに一意の識別子を割り当てるために使用されるものです。このIDは、データの追跡や識別を容易にするために使われ、特にデータベース管理やソフトウェア開発でよく見られます。たとえば、写真や画像を生成するシステムでは、それぞれの生成物に固有のgen_idが割り当てられ、後からその具体的な生成物を特定や参照するのに役立ちます。

　このgen_idですが、
　　私は、画像だけに
　　　割り振られている、とばかり
　　　　思っていましたが、
　　　　　生成された文章にも
　　　　　　割り振られていて、
　　　　　　　ビックリしました😅
***
　さて、
　　そんなgen_idの利点を
　　　説明しましょう。

　私がいつも使っている
　　“リトライ”は、
　　　繰り返していると
　　　　少しずつ変化していきます。

　ところが
　　gen_idを使うと、
　　　セッションのあいだだけですが、
　　　　その画像を生成できます。

　これを利用して
　　元画像への変更を
　　　行なうことにより、
　　　　“リトライ”のような変化を
　　　　　起こさずに進められます。

　一例として
　　そのやり取りを
　　　お見せしましょう。
***
***user***
白い鎧兜を着た若い男性。描画

***ChatGPT***
![A_young_man_wearing_a_white_armor_and_helmet,_depi.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/e3056ed8-4eb9-34c6-4101-60ac6092494a.jpeg)

***user***
gen_idを教えてください

***ChatGPT***
生成された画像のgen_idは「re6ED7IQNRdquFVT」です。このIDを使用して、特定の生成物を参照することができます。

***user***
gen_id「re6ED7IQNRdquFVT」の男性を猫にして

***ChatGPT***
![A_young_cat_wearing_a_white_armor_and_helmet,_depi.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/357aa2e4-84a6-1f6b-22cd-3571ad24415f.jpeg)

***user***
gen_id「re6ED7IQNRdquFVT」の男性を犬にして。鎧を黒くして

***ChatGPT***
![A_young_dog_wearing_a_black_armor_and_helmet,_depi.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/1f5ac146-f6fb-e727-78de-98278b41ea8f.jpeg)

***user***
gen_id「re6ED7IQNRdquFVT」の男性をドラゴニュートにして

***ChatGPT***
![Dragonborn_Warrior.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/423432f8-981d-3d20-9ec7-ec562722dc5f.jpeg)
***
　ごらんのように
　　元画像から生成した画像は
　　　みな、同じ姿勢です。

　また、
　　犬の画像では、
　　　黒くしたのに、

　　ドラゴニュートでは、
　　　白に戻っています。

　　元画像を
　　　参照しているためですね。

　“リトライ”では
　　元画像
　　　→新しい画像
　　　　→さらに新しい画像、となる。

　gen_idでは
　　元画像
　　　→新しい画像❶
　　元画像
　　　→新しい画像❷
　　元画像
　　　→新しい画像❸、という感じ。

　これは
　　指示出しを
　　　編集して描画する
　　　　のと似ています。
　　　　　というか、そっくりです😅

　しかし、
　　違いもあります。

　それは
　　gen_idを使うことで
　　　セッションの流れに
　　　　分岐が発生しない、と
　　　　　いうことです。

　意味が
　　わからないかと
　　　思いますが、
　　　　履歴をExportして
　　　　　検索される方は
　　　　　　気付かれると
　　　　　　　思います。

　ふつう、
　　Exportでは、
　　　分岐の最後だけが
　　　　残る仕組みです。

　分岐しないのであれば、
　　履歴にも残るわけで
　　　検索にも引っ掛かる確率が
　　　　増します。

　このことは
　　この記事を書いている
　　　途中で気が付きました😅

　もちろん、
　　“リトライ”も
　　　編集して描画も
　　　　gen_idも
　　　　　それぞれに
　　　　　　利点がありますので、
　　　　　　　使い分ければ
　　　　　　　　いいだけです。

　ここでは
　　“**手段が増えた**”と
　　　思うべきでしょうね。

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
【Unity】StreamingAssets の初回読み込みが遅い問題を解決する手段

皆さんは StreamingAssets を利用していますか？

私も昔は利用していました。
しかしながら、何故か初回読み込みに関して 1 秒程度かかる問題が
発生して困っていました。

また AssetBundle も結局は
StreamingAssets から読み込むため
遅くなるハズと考えました。

そこで、アプリを起動する度に
StreamingAssets にアクセスしないで済む手段を
考える事にしました。

***

パターンA : 初回のみ StreamingAssets から読み込んで persistentDataPath に保存しておく手段について

この方式がよく使われていると
昔の記事で読んだことがあります。

確かに persistentDataPath から読み込んだ場合は
私の環境では初回読み込み時に遅くなる現象は
発生しませんでした。

しかしながら
StreamingAssets にあるデータを消せないため
2 重にファイルが存在する事になり
アプリの容量が増えるため
少しだけナンセンスだと思いました。

そのため別の方法を考察する事にしました。

***

パターンB : 可能な限り初回読み込み時のデータを Google Drive に保存する手段について

ネット環境が必要になりますが
パターンAで StreamingAssets に保存するデータに関して
可能な限り GoogleDrive に保存する方法が
パターンBです。

ユーザーがトータルでダウンロードする容量は変わらないですが
StreamingAssets と違って端末に残らない点が
パターンAより優れています。

また、アプリ自体をダウンロードする際に
容量が少なく見えるためダウンロードの敷居を下げる効果もあります。

初回読み込みのタイミングのみ動作が遅くなる点と
ネット環境が必要な点が良くない点でしょうか。

基本的にメリットの方が上回るため
この方式にしない理由がないと思っています。

***

初回読み込みの判定について

初回読み込みの判定に関しては
私は VersionCode を起動時に保存するようにしています。

ver1.2.0 の VersionCode は 6 で
ver3.3.2 の VersionCode は 32 などと分かるため
VersionCode が 6 の場合は Google Drive から読み込みが必要で
このデータとこのデータを読み込めば良いと分かります。

VersionCode の key が存在しないならば
全てのリソースを読み込めば OK です。

面倒ならば VersionCode が現在の物より小さい場合は
全てのリソースを読み込み直しても OK です。

***

以上私なりの StreamingAssets の初回読み込みが遅い問題を
解決する手段について紹介しました。

もしかしたら常識なのかもしれませんが
同じことを言っている記事を見かけないため
記事にしてみました。

もっと良い方法がある場合は
是非ともコメント欄で教えて頂けると幸いです。

ネットで探しても良い解決策が見つからなかったため
「この方法が一般的」というコメントは大変助かります。

是非あなたが持つ情報を皆さんに共有して下さい。
皆さんのコメントが
この問題を皆が解決するきっかけになるかもしれません。

皆さんがこの問題を解決できることを祈っています。
閲覧ありがとうございました。
Proxmox8 を試してみる　その１９　古い Kernel を削除する

# 古い Kernel を削除したい
Proxmox のバージョンアップやら日々のアップデートで Kernel が更新されるのはよしとしても古い Kernel が残ったままだと DISK 容量も気になるところ。しかも Proxmox は、Debian 系の OS であるので少し消す手間と手順がありどれが正解かわかない始末でしたが、いいツールを見つけたのでご紹介します。

# PVE Kernel Cleaner
jordan hillis さんが作成した ”PVE Kernel Cleaner” がよさげ。
最新の PVE Kernel を最小数保持するように指定することができるのもありますが、なんだか色々と機能満載ｗ

とりあえずインストール

```bash
# git clone https://github.com/jordanhillis/pvekclean.git
# cd pvekclean
# chmod +x pvekclean.sh
# ./pvekclean.sh
```

Kernel は、２つ残すようにして・・・

```bash
# ./pvekclean.sh -k 2
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/44540/ebe42a7f-a113-0174-a470-a8861a79d3d8.png)

結構よいかも。

# 参考にさせて頂いたサイト様

https://github.com/jordanhillis/pvekclean

https://www.linuxadictos.com/ja/Debian%E3%81%A7%E5%8F%A4%E3%81%84%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95.html

http://bam-system.ddo.jp/wordpress/ura/2020/06/10/debian%E3%81%A7%E5%8F%A4%E3%81%84kernel%E3%81%8C%E6%BA%9C%E3%81%BE%E3%81%A3%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F%E5%A0%B4%E5%90%88%E3%81%AE%E5%AF%BE%E5%87%A6/
【セキュリティ入門】ハッカーの「ハット」の由来

# 背景

ハッカーのカテゴリーとして「**ブラックハットハッカー**」、「**ホワイトハットハッカー**」、「**グレーハットハッカー**」があります。

「**ブラックハッカー**」など「**ハット**」が省略される場合もありますが、この「**ハット**」はなぜ名前につくのか気になったので調べてみました。

# ハッカーのカテゴリー

ハッカーのカテゴリーは主に「**ブラックハットハッカー**」、「**ホワイトハットハッカー**」、「**グレーハットハッカー**」の3つに分けられます。

## ブラックハットハッカー

「**クラッカー**」という場合もあります。
ブラックハットハッカーは、自身の知識やスキルを**不正行為**のために使用します。

動機や目的も多種多様です。有名なものをいくつか紹介します。

- **ハクティビスト**
    - 政治的あるいは社会的な主張・目的のためにハッキングを行う個人や集団を指します
- **スーサイドハッカー**
    - 不正行為などで被る結果(逮捕など)を恐れない人を指します
- **国営ハッカー**
    - 国の支援を受けて行動する集団などを指します
- **いたずら・自己顕示**
    - クラッキングすることで自身のスキルを見せびらかしたり、注目を集めたいだけの人も居ます
- **金銭目的**
    - 近年話題のランサムウェアグループのように、金銭を目的として活動するクラッカーもいます 

:::note info
ブラックハットハッカーの中でも、「既存のクラッキングツールを使用して不正行為を行うクラッカー」は俗称として**スクリプトキディ**とも呼ばれます。
:::

## ホワイトハットハッカー

「**Ethical hacker**」= 「**論理的なハッカー**」などとも呼ばれます。

ホワイトハッカーは、「**クラッカー**」から企業や組織を守るサポートをするために、自身の知識やスキルを活用する人材を指します。

近年サイバーセキュリティインシデントの増加により、ホワイトハッカーの需要が高まっていると言われています。

https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r02/html/nd134530.html

## グレーハットハッカー

ブラックハットハッカーとホワイトハットハッカーの中間のグレーハットハッカーが存在します。

下記サイトから引用すると

>ハッキングスキルを善のために使うこともあれば、悪意を持って使うこともあります。 グレイハットハッカーは、セキュリティを改善したいという願望に動機づけられていますが、同時にセキュリティの脆弱性を暴きたいという願望も持っています。

ということらしいです。

https://www.keepersecurity.com/blog/ja/2023/06/26/the-three-different-types-of-hackers/

# ハットの由来

本題に入ります。

なぜ「ブラック**ハット**」、「ホワイト**ハット**」のようにそれぞれ「**ハット**」がつくのか気になりました。
今まで、「帽子に何の関係があるんだ？」と思っていました。

**ハット**が付いた由来の一説としては、**昔の西部劇では「悪役は黒い帽子」を、「ヒーロー役は白い帽子」をかぶり、悪=黒、白=正義のイメージが定着した。その慣例によるもの**ということらしいです。

# 参考サイト

https://www.kaspersky.co.jp/resource-center/definitions/hacker-hat-types

https://en.wikipedia.org/wiki/Black_and_white_hat_symbolism_in_film

https://www.keepersecurity.com/blog/ja/2023/06/26/the-three-different-types-of-hackers/
Go入門 : Go(Gin)で簡単にWebサーバーを作ってみる

# はじめに

Go言語でWebサーバーを立てる方法について、学習のメモとしてまとめていきます。

・version : go 1.22.2


# Webサーバーを実装

まず初めに、下記のコマンドでmoduleの初期化を行います。

```
go mod init モジュール名
```

go.modファイルが作成されます。


次に、下記のコマンドで`main.go`のファイルを作成します。

```
touch main.go
```

最初に、`Hello Golang!!` を出力するcodeを`main.go`に記述してみましょう。

```main.go
package main

import "fmt"

func main() {
    fmt.Println("Hello Golang!!")
}
```

記述後、下記のコマンドで `Hello Golang!!` が出力されるか確認しましょう。

```
go run main.go
```
`Hello Golang!!` が出力されればOKです。

# Ginを導入する

下記の公式Docsを参考に、Ginを導入していきます。

[Gin Web Framework](https://gin-gonic.com/ja/docs/quickstart/)

`main.go`を下記のように修正します。

```main.go
package main

import "github.com/gin-gonic/gin"

func main() {
  r := gin.Default()
    r.GET("/hoge", func(c *gin.Context) {
      c.JSON(200, gin.H{
        "message": "fuga",
      })
    })
  r.Run()
}

```
その後、下記のコマンドを入力します。

```
go mod tidy
```

`go mod tidy`を実行することで、Goモジュールの依存関係を最新の状態にします。


それでは、main.goを実行します。

```
go run main.go
```

ターミナル上で、下記のように出力されればOKです。

```
$ go run main.go     
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.

[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
 - using env:   export GIN_MODE=release
 - using code:  gin.SetMode(gin.ReleaseMode)

[GIN-debug] GET    /hoge                     --> main.main.func1 (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Environment variable PORT is undefined. Using port :8080 by default
[GIN-debug] Listening and serving HTTP on :8080
```

次に、別のターミナルを開いて、下記のコマンドを入力します。

```
curl http://localhost:8080/hoge
```

下記のように出力されれば完成です。

```
{"message":"fuga"}
```
Playwright で if then else 的な条件分岐をしたい

本来であれば良くないことだと思うが、対象環境の状態に応じて、どうしてもテストケース内で条件分岐させたい場合があると思う


# コード例

以下はページに特定のテキストがなければテストをスキップする例

強制的に例外を補足して return させてしまう

```ts
test('example', async ({ page }) => {
  // SOME LINES

  try {
    await expect(page.getByText('SOME TEXT')).toBeVisible()
  } catch (exceptionVar) {
    console.log('SKIP!')
    return
  }

  // SOME LINES
}

```


# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
低コスト！TerraformでサクッとQuery Insightsを設定した！


こんにちは。
株式会社HRBrainでエンジニアをしているみつです！

新規プロダクトに配属されてから初のQiita投稿！（頑張るぞ）

そこまで長い記事ではないですが、Query Insightsという機能、良き！と思いメモ:point_up:

## 目次:lips:
- [Query Insightsとは？](#query-insightsとは)
- [Query Insightsで取得できるデータ](#query-insightsで取得できるデータ)
- [Terraformで設定する各項目](#terraformで設定する各項目)
- [データを見るのに必要なこと](#データを見るのに必要なこと)
- [Query Insights導入後の懸念](#query-insights導入後の懸念)
  - [1週間以上前のデータはどうするか](#1週間以上前のデータはどうするか)
  - [誰が実行したかを詳細に追いたい場合](#誰が実行したかを詳細に追いたい場合)
- [Query Insightsによるインシデント対応](#query-insightsによるインシデント対応)
- [参考文献](#参考文献)
- [PR](#pr)

## Query Insightsとは？

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1841912/cf6cd752-bf75-ad5f-6d84-e80026afe08a.png)

> Query Insights では、Cloud SQL データベースに対するクエリ パフォーマンスの問題を検出、診断、防止できます。直感的なモニタリングをサポートし、検出するだけでなくパフォーマンスの問題の根本原因の特定に役立つ診断情報を提供します。

- クエリのパフォーマンスとかを見ることができるもの。（取れる詳細なデータは後述。）

> Query Insights に追加料金は発生しません。Query Insights ダッシュボードから 1週間分のデータにアクセスできます。

- 追加料金の発生なし
- 1週間分のデータが取得できるらしい。（ちょっと短い...？）

## Query Insightsで取得できるデータ

- **クエリ**: 正規化されたクエリ文字列。Query Insights では、デフォルトでクエリ文字列の 1,024 文字のみが表示されます
- **データベース**: クエリが実行されたデータベース
- **負荷（合計時間別）/ 負荷（CPU 別）/ 負荷（IO 待機別）/ 負荷（ロック待機別）**: 特定のクエリをフィルタして最大の負荷を見つけるオプションです
- **平均実行時間（ミリ秒）**: クエリの平均実行時間
- **呼び出された回数**: アプリケーションがクエリを呼び出した回数
- **返された平均行数**: クエリに対して返された行数の平均
- **平均スキャン行数**: クエリでスキャンされた行数の平均

## Terraformで設定する各項目

- **`[query_insights_enabled]`**: バイト単位で保存される最大のクエリーlength。256から4500の間で指定する。デフォルトは1024。クエリーlengthが長いほど分析クエリに便利だが、より多くのメモリを必要とする。クエリ長を変更するには、インスタンスを再起動する必要がある。
- **`[record_application_tags]`**: アプリケーションタグを記録するにはTrueに設定
- **`[record_client_address]`**: クライアントアドレスを記録するにはTrueに設定
- **`[query_plans_per_minute]`**: 1分間にInsightsがキャプチャしたクエリ実行プランの数。0から20の間で指定する必要があり、デフォルトは5。

## データを見るのに必要なこと
- Query Insightsをenable trueで設定
    - **Query Insights機能を、まずenableにする。**
- `cloudtrace.traces.get` IAM権限を追加
    - **データを見るための権限設定としてIAM権限を付与する。**

## Query Insights導入後の懸念
### 1週間以上前のデータはどうするか
1週間以上前のデータを確認できないのは少し不便かもなと思う。

あくまで簡単にできるし、やらない理由ってあまりないよね、なツール。

「集計をして分析したい」「インシデント対応で必要に迫られてデータを長期間にわたり確認する必要がある」などの場合は、オブザーバビリティツールなどで設定しておく必要がありそう。

### 誰が実行したかを詳細に追いたい場合
ユーザー管理があるアプリケーションでAPIが叩かれ、SQLが発火する場合、「どのページで誰が何のSQLを実行したか」みたいなデータまでは取得することができない。

こういった場合も、必要に応じてオブザーバビリティツールのLog収集の設定が必要になりそう。

## Query Insightsによるインシデント対応
Query Insightsを設定するのは簡単で、実際にインシデントなどの対応で使えたという記事も見かけたので。

設定しておくだけで少しは安心できそう。

https://medium.com/sakajunlabs/sre-incident-response-with-cloudsql-query-insights-a122a1ed4a8c

## 参考文献
https://cloud.google.com/sql/docs/mysql/using-query-insights?hl=ja

https://cloud.google.com/blog/products/devops-sre/monitor-your-cloud-sql-query-performance-with-code/?hl=en

https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/sql_database_instance

## PR
HRBrain では一緒に働く仲間を募集しています！

一緒に高みを目指しましょ〜！

https://www.hrbrain.co.jp/recruit
prismaでViewをQueryする方法

# やりたいこと
prismaですでにMySQLデータベースに作成されたViewをQueryしたい

:::note info
prismaで直接データベースに対してビューを作成することができないので、事前にビューを作成する必要があるます
:::

## ①ビューを作成
以下の両テーブルをJOINしてViewを作成します

ユーザーのSNS投稿を管理するテーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/124f6316-4e02-5a95-7b79-aed3be9ca33d.png)

ユーザー情報テーブル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/0374a460-c884-9cd8-1cef-71815ae56ae2.png)

直接A5M2でSQLを打ってViewを作成～

すべてのpostに対してLEFT OUTER JOINする形でSQLを作りました
（postを全データがほしい、user情報がない場合はuser情報をnullにしてpost情報と合体する）

```ruby:
CREATE VIEW sns.user_post AS
SELECT ROW_NUMBER() OVER (ORDER BY sns.post.id) AS id, sns.user.name, sns.user.portrait, sns.post.post, sns.post.updatedAt
FROM sns.post
LEFT OUTER JOIN sns.user ON sns.post.authorId = sns.user.id;
```
すると、「user_post」ビューが出来上がりました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/707107fa-aef2-8774-d550-84374bef83a9.png)

## ②Prismaスキーマにビューに対応するモデルを定義
prismaはビューの存在を検知することができないので、手動でスキーマを教えてあげないといけない

schema.prismaに以下のモデルを手動で書きます～

```ruby:schema.prisma
// View
model user_post {
  id       Int     @id
  name     String
  portrait String  @default("001.png")
  post       String
  updatedAt  DateTime   @updatedAt
}
```
:::note warn
モデル名はつい「userPost」で書きがちですが、これはダメです！！
ちゃんとデータベースにあるビュー名と同じにしないと、Prismaクライアントの再生成をするとき、「does not exist in the current database.」のエラーがでて、prismaはビューの存在をちゃんと検知できなくなります。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/d3ced00b-074a-b6f2-62ad-f06ee80d6fb2.png)

## ③Prismaクライアントの再生成
```ruby:
npx prisma generate
```

ターミナルでnpx prisma generateコマンドを実行して、Prismaクライアントを再生成します。これにより、新しいモデルがクライアントに追加され、ビューに対するクエリを実行できるようになります。

## ④ビューに対するクエリの実行
Prismaクライアントの再生成までできたら、次の操作は通常のテーブル操作と一緒で、prismaの文法を書けば各種クエリ処理ができます～

こちらもちゃんと「user_post」で呼ばないとね！

```ruby:
const { PrismaClient } = require("@prisma/client");
const prisma = new PrismaClient();

class UserPostViewRepository {
  async searchMany(where) {
    const userPosts = await prisma.user_post.findMany({
      where: where,
    });
    return userPosts;
  }

  async insert(data) {
    const userPost = await prisma.user_post.create({
      data: data,
    });
    return userPost;
  }

  async update(where) {
    const userPost = await prisma.user_post.update({
      where: where,
    });
    return userPost;
  }

  async delete(where, data) {
    const userPost = await prisma.user_post.delete({
      where: where,
      data: data,
    });
    return userPost;
  }
}

module.exports = UserPostViewRepository;

```

# 終わりに
以前springで開発するとき、ビューのモデル自動生成やクエリメソッド自動生成など、フレームワークがいろいろやってくれて、あまり手動で書かなくても済むに対して、prismaちょっと不便だなあと感じました。笑
htmxとは何なのか？ 

htmxは、HTML属性を使用してAJAX、CSSトランジション、WebSockets、Server Sent Eventsを直接操作できる軽量なJavaScriptライブラリです。複雑なJavaScriptコードを書くことなく、シンプルでパワフルなハイパーテキストベースのモダンなユーザーインターフェースを構築することを可能にします。

**htmxの特徴**

* **シンプルで軽量**: htmxはわずか14KB（gzip圧縮時）の軽量ライブラリであり、依存関係もありません。
* **使いやすい**: htmxはHTML属性を使用して動作するため、JavaScriptの知識がなくても簡単に使用できます。
* **パワフル**: htmxは、AJAX、CSSトランジション、WebSockets、Server Sent Eventsなど、モダンなWeb開発に必要な機能をすべて備えています。
* **アクセシビリティ**: htmxは、JaJavaScriptが無効なユーザーでもセマンティックHTMLの利用、プログレッシブエンハンスメント、フォールバック機能等により動作します。
* **柔軟性**: htmxは、独自のカスタム属性やイベントを追加することで拡張できます。

**htmxの利点**

* **開発効率の向上**: htmxを使用すると、複雑なJavaScriptコードを書く必要がなくなり、開発効率が向上します。
* **保守性の向上**: htmxはHTML属性を使用して動作するため、コードが読みやすく、保守性が向上します。
* **パフォーマンスの向上**: htmxは、非同期通信を効率的に処理するため、パフォーマンスが向上します。
* **アクセシビリティの向上**: htmxは、JavaScriptを使用しないため、JavaScriptが無効なユーザーでも利用できます。

**htmxのユースケース**

* **シングルページアプリケーション (SPA)**: htmxは、SPAの構築に最適なライブラリです。ページ全体を再読み込みすることなく、部分的な更新を行うことができます。
* **リアルタイムアプリケーション**: htmxは、WebSocketsやServer Sent Eventsを使用して、リアルタイムアプリケーションを構築することができます。
* **動的なWebフォーム**: htmxは、AJAXを使用して、動的なWebフォームを構築することができます。
* **インタラクティブなWebページ**: htmxは、CSSトランジションを使用して、インタラクティブなWebページを構築することができます。

**htmxの始め方**

htmxは、以下の方法で簡単に導入できます。

1. htmxライブラリをダウンロードします。
2. ダウンロードしたライブラリをHTMLページにインクルードします。
3. HTML属性を使用して、htmxの機能を利用します。

htmxの詳細については、以下のリソースを参照してください。

* htmx公式ドキュメント: [https://htmx.org/](https://htmx.org/)
* htmxチュートリアル: [https://htmx.org/docs/](https://htmx.org/docs/)
* htmxサンプル: [https://htmx.org/examples/](https://htmx.org/examples/)

htmxは、モダンなWeb開発をシンプルかつパワフルにする優れたライブラリです。ぜひhtmxを試して、開発効率とパフォーマンスを向上させてください。
[入門]AI音声認識エンジン「AmiVoice」のAPIをPythonで使ってみた！

## AmiVoice概要

[公式ページ](https://acp.amivoice.com/amivoice/)を適宜抜粋しております。

### AmiVoiceの特徴

- 音声認識市場シェアNo.1の音声認識エンジンを開発者に提供
    - 特に日本語に対する高い認識精度
- ノイズに強い
    - 街中、工場でも認識可能
- 国内での開発&運用
    - お客様音声データの海外流出はない
- 専門用語に特化したエンジンを複数用意
    - 医療、金融、保険など業界特有用語の認識精度が高いエンジンも用意

### 提供タイプ

- AmiVoice API (共用サーバ利用型)
    - 従量課金制のクラウド音声認識API
    - さくっと始めるならまずはこれ(毎月60分程度の無料枠も有)
    - 本記事で取り扱うのはこれのみ
- AmiVoice API Private (専用サーバー構築型)
    - オンプレ環境で利用する音声認識API
- AmiVoice SDK (端末組み込み型)
    - オフラインで使える音声認識エンジン

提供タイプ間の詳細な違いについては[公式ページ](https://acp.amivoice.com/amivoice_tools/#diff)参照ください

### 認識精度を試してみる

[公式ページ](https://acp.amivoice.com/amivoice/)の下部の`認識精度を試す`でマイクからの入力に対する音声認識を実際に確認することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434536/1ee87731-be17-662f-4b11-6db9875570e4.png)

### APIの種類

同期、非同期、WebSocketが用意されています。
マイページに使用するエンジンとの対応表が用意されており、非常にわかりやすい印象を持ちました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/434536/d4ea5295-5e08-0717-cce6-e4316c7803ad.png)

## AmiVoice API試してみた

音声ファイルは[公式ページ](https://docs.amivoice.com/amivoice-api/manual/tutorial/short-speech-transcription#%E9%9F%B3%E5%A3%B0%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB)を参考に、`test.wav`を使用します。

以下パラメータで試します。
- エンジン：`会話_汎用`
- API：`同期API`

```python:example.py
import requests

# AmiVoiceのAPIキーを設定(マイページで確認できます)
APP_KEY = "XXXXXXXXXXXXXXXXX"

# 認識対象の音声ファイルパス
AUDIO_FILE = "test.wav"

# エンドポイントのURL
URL = "https://acp-api.amivoice.com/v1/recognize"

# 音声ファイルを開く
with open(AUDIO_FILE, "rb") as f:
    audio_data = f.read()

# リクエストのパラメータを設定
params = {
    "d": "-a-general"
}
files = {
    "u": (None, APP_KEY),
    "a": ("test.wav", audio_data)
}

# POSTリクエストを送信
response = requests.post(URL, params=params, files=files)

# レスポンスを処理
if response.status_code == 200:
    result = response.json()
    print("認識結果:", result["text"])
else:
    print("エラー:", response.status_code, response.text)
```

```shell-session
$ python3 example.py 
認識結果: アドバンスト・メディアは、人と機械との自然なコミュニケーションを実現し、豊かな未来を創造していくことを目指します。
```

非常に短いコードかつ正確な文字起こしができました。

今回はpythonでAPIを直接たたきましたが、python含めいくつかの言語ではクライアントライブラリが用意されているそうです。
https://docs.amivoice.com/amivoice-api/manual/client-library/sample-programs
こちらも今後試していきたいと思います。
ラズパイ CODESYS CNC開発プロジェクト　画面

RPi + CODESYS + EtherCAT AC Servo のCNC（いまのところ仮想サーボ軸のCNC）をGPLv3として公開しはじめました。

https://qiita.com/SnowTiger/items/e95f4b07847e5c6efaa7


現状開発中の画面です。


<H2>手動操作画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408008/b2f134b7-3de1-8e80-98ed-838d54edbeb7.png)

ユーザー機械の手動操作ボタンも入れられるようにスペースを空けています。



<H2>自動運転画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408008/38361922-31b3-ccae-afe3-dd7646e6d118.png)

リアルタイムの速度や回転数の表示、Gコード表示が出来ています。
デバッグのために色々入れています。将来的には整理する予定です。

<H2>Gコード編集画面

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408008/6af1a9ac-8b6a-8c98-0a34-1e6a2b352743.png)

直接ファイルをエディタで確認・修正できるようにしてあります。
修正については、VISUとPCのキーボード操作の対応がいまいちのままです、現場で見たい用途はあると思いますが、Gコードを直接修正する必要はないかも？

<H2>アラーム履歴

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408008/029ac572-6b2a-4576-6ca1-dfe1c37ebe3f.png)

<H2>設定-座標テーブル

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/408008/1e14bd7d-496f-8a65-72cc-c7a080ce5a68.png)
(工具補正テーブルやワーク座標系テーブルはまだ機能していません。）


<H2>開発機材募集のおしらせ
本プロジェクトでは、CODESYSで使える機材を募集しています。

最新のものでなくて良いです、余っているものありましたら是非、ご連絡下さい。
（数か月貸与もしくは譲渡）

<H3>

CODESYSで使える、EtherCAT接続の
ACサーボドライブ、ステッピングモーター、インバーター、デジタルIO、A/D, D/A。

<H3>
手パ（オープンコレクタでもラインドライバでも）

本プロジェクトに謝辞、動作確認機材として記載します。

<H2>寄付は・・

寄付は、PaypayやAmazonのギフトなどで、電線代、コーヒー代など、少額でOkです。開発速度が上がるかも。