こみなのメモ帳 / 趣味と実益のネタ帳 Sun, 28 Dec 2025 09:56:16 +0000 ja hourly 1 https://wordpress.org/?v=6.1.1 WordPressを静的サイトとして公開する(2) /archives/1833/ Sun, 28 Dec 2025 09:54:10 +0000 /?p=1833 AppEngineでサイトを公開してから1か月近く経過したころ、10円くらい料金が発生しているのに気づきました。無料枠にすべて収まる想定だったので完全に想定外です。

明細を見ると、Artifact Registry Storage/Artifact Registry で料金が発生していました。

どうゆこと?

App Engine コンテナ イメージを Artifact Registry に移行する  |  App Engine migration center  |  Google Cloud Documentation

App Engineのコンテナイメージのデプロイに Artifact Registry が使用されるとのこと。そして料金は、

料金  |  Artifact Registry  |  Google Cloud

Artifact Registry料金

0.5GBを超えると、1GBごとに約$0.10/月≒15円/月ほど発生します。

初めのころは特にデプロイを試行錯誤していたので、古いコンテナイメージが消されずに残ることで無料枠を超えて課金となったようです

対策

Artifact Registry にはクリーンアップポリシーという条件に合ったイメージを自動で削除してくれる機能がありますので、これを利用することにしました。

稼働中のアプリケーションのイメージは再起動やスケーリングの際に必要になるので削除してはダメなので、最新イメージを保持、それ以外は消していくルールにします。

Artifact Registry > レポジトリ > gae-standard でレポジトリの編集をクリック、そして設定一覧の中に「クリーンアップポリシーを追加」というボタンを押下します。

保持ルール作成

最新イメージには latestタグが付けられているので保持ルールの条件にします。

削除ルール作成

それ以外のイメージを削除するルールを登録します。今回は1日経過したものを対象としてみました。(入力時は 1d としましたが開きなおすと 86400s になっていました)

クリーンアップポリシーは設定してから実際に実行されるまで、数時間から1日くらいかかるとのことです。しばらく経ってから意図通りイメージが削除されているか確認しましょう。

]]>
[play1]evolutions機能 /archives/1820/ Tue, 16 Dec 2025 16:04:28 +0000 /?p=1820 概要

playframework1に当初から内包されているDBスキーマを管理する機能の名前。普段、DB周りはJPAに任せてしまうことが多いのですが、先日初めて evolutionを使ってみたのでそのとき得た知見をまとめておこうと思います。

何ができるのか

JPA(Hibernate)は自動でデータベースのスキーマ更新を行うことができます。小規模開発ならJPAの自動更新(インデックスの削除など不可なケースもある)に任せてもいいのですが、ある程度のプロジェクトであればスキーマの変更を追跡、管理する必要が出てきます。

スキーマの変更をメンバー全員に周知したり、本番サーバのスキーマ変更を明示的に行ったり、いくつかの開発環境DBを同期させたり、などの場面で役立ちます。

準備

  1. Evolution機能を利用する際はまず、ディレクトリ db/evolutions を作成する。
  2. そのディレクトリの中に、1.sql, 2.sql, とDDLを作成する。
    スキーマ変更を行うたびに新しい番号でDDLを記述したファイルを配置する。

SQLスクリプトファイル

  • db/evolutions に配置するSQLスクリプトは、UpsとDownsの2つのパートから構成される。
  • Upsは変更するためのスクリプト。(例えば、 create table XXX...
  • Downsは元に戻すためのスクリプト。(例えば、drop table XXX...

マルチDB対応

マルチDBにも対応しています。マルチDBでは各DBに識別子を設定しますが、その識別子をスクリプトファイルの先頭に記述します。

1.sql, 2.sql, ... となるところが、DB-1.1.sql, DB-1.2.sql, ...,DB-2.1.sql, DB-2.2.sql,... といった形になります。

設定

application.conf による動作の制御方法

evolutions.enabled=false

Evolutions機能を利用しないときに false を設定する。

modules.evolutions.enabled=false

Evolutionsモジュールを利用しないときに設定する。

dbname.evolutions.enabled=false

マルチDBでDB指定してEvolutionsモジュールを利用しないときに設定する。

evolutions.autocommit=false

autocommitをしないときに指定する。

evolution.PLAY_EVOLUTIONS.textType=varchar(32768)

Evolutions機能で作成する管理テーブル play_evolutionsapply_script , revert_script , last_problem 列の型を指定する。未指定の時、oracleなら clob型、他は text型 となる。
デフォルトの hd2b を使う場合は varchar(32768) を指定する必要があった。

db/evolutions ディレクトリの存在でモジュールはONとなるので、上記設定で個別にOFFにしていく感じになります。

動作

DEVモード

DEVモードの時はリクエストが発生するたびにスキーマ状態の確認が割り込みます。そこで未適用のスクリプトが見つかった場合は適用を要求するページが表示されます。(例外オブジェクトの getMoreHTML にて適用するための <form/> を含む html を返すことで適用要求ページを実現している)

詳しくは調べていないのですが、UnexpectedException にラップされて <form /> が表示されない事象が発生したので以下のように views/errors/500.html を修正して対応しました。

適用要求ページが出ないで UnexpectedException が表示される
      <body>
        #{if play.mode.name() == 'DEV'}
          #{if exception?.cause instanceof play.exceptions.PlayException }
            #{500 exception.cause /}
          #{/if}#{else}
            #{500 exception /}
          #{/else}
        #{/if}
        #{else}
            <h1>Oops, an error occurred</h1>

db=mem で中身が空の場合は問い合わせなく勝手にスクリプトが適用される。

PRODモード

本番モードなら最初の起動時にスキーマ状態の検査が行われます。未適用のスクリプトが見つかった場合はエラーとなって起動中断となります。

アプリケーションが要求するDBスキーマとなっていないと起動しないようになっています。

コマンドライン

コマンドラインよりスクリプトの適用を制御します。

$ play evolutions

未適用のスクリプトを表示する。

$ play evolutions:apply

未適用のスクリプトを適用する。

$ play evolutions:markApplied

未適用と認識されているスクリプトを Evolutions機能を経由しないで適用したときに使用する。(未適用スクリプトを強制的に適用済ステータスにする)

$ play evolutions:resolve

スクリプトの実行中にエラーが発生すると問題が発生したスクリプトとして記録され、以降の適用が保留となる。そのときこのコマンドで問題が解決済とする。(問題発生ステータスをクリアする)

play evolutionsplay ev と省略することが可能です。

Tips

DDL作成

自分で一からSQLスクリプトファイルを書くのは大変なのでJPA(Hibernate)を利用すると楽ができます。

jpa.ddl=update
jpa.ddl=create

この設定にしておけば起動時にDDL変更を自動で行ってくれる。

jpa.debugSQL=true
hibernate.show_sql=true

DDL変更のSQLがログに出力されるのでこれをスクリプトファイルへ転記すればよい。

戻しについてはJPAからの出力は期待できないので自分でスクリプトを用意する必要があります。

module_key カラムについて

管理テーブル play_evolutions には、module_key というカラムが有、application.name の値が格納されていて、抽出条件にもなっています。

複数の playframeworkアプリが同一DBに対して Evolutionsを利用することを想定しているようです。

なので、application.name の扱いには注意しましょう。途中で変更したり、開発時だけ名前を変えていたりすると、予期しない挙動を招く可能性があります。

途中からEvolutionを利用するとき

既に開発が進んでいるプロジェクトで Evolutionsを利用するときの手順を示します。

  1. アプリケーション停止する。
  2. db/evolutions ディレクトリを作成する。
  3. 1.sql」 を設置(現時点のDBのDDLを取得して作成)
  4. play ev」コマンドを実行する。(管理テーブルが作成されて、1.sql が未適用として表示される)
  5. play ev:markedApplied」コマンドを実行して適用済とマークする。
  6. アプリケーション起動する。

以上になります。以降のDDL変更は、#.sql へ記述します。

h2dbで現時点のスキーマを出力する方法

sql> script nodata to 'C:\work\ddl.sql

データやスキーマをSQLとして書き出すことができるコマンド。今回はデータなしなので nodata を指定。

参考)https://h2database.com/html/commands.html#script

本番リリースするとき

新しいバージョンのアプリケーションに入れ替えるときは、停止中にDDL適用を行います。

  1. play ev:apply」コマンドを実行し、未適用のスクリプトを適用する。
  2. apply中にエラーが出てしまったらステータスがエラーになってしまうので、「play ev:resolve」でエラー状態を解消する。
    問題を解決して再びスクリプト適用を行う。

evolution.PLAY_EVOLUTIONS.textTypeの必要性

h2dbでは下図のような管理テーブル play_evolutions が作られます。

デフォルト設定の場合

ここで apply_scriptrevert_script 列のデータ型が clob型となっている点に注目してください。Evolutionsではこれらを resultSetからgetString(n) で取得しているのですが、Clob型のtoStringではテキストの中身ではなく `java.sql.Clob@123′ のようなオブジェクトID文字列になってしまいます。

そのオブジェクトID文字列とスクリプトファイルの中身を比較することになるために常にNGとなってしまうのです。

そこでDBデータ型を明示的に指定することでこの問題を回避することができるのです。

evolution.PLAY_EVOLUTIONS.textType=varchar(32768)

※ この設定だけ evolutionが単数形なので注意。

org.h2.jdbc.JdbcSQLSyntaxErrorException: 列名 “MODULE_KEY” が重複しています

想像ですが。昔の Evolutionsでは module_key による管理がなく、ある時から管理できるようになった。そのため昔に作られた管理テーブル play_evolutions に自動でカラムを追加してくれるマイグレーション機能が存在するみたいです。

で、module_keyが存在しているのにこのマイグレーション機能が誤作動してしまうと題名のようなエラーが発生することがあります。

具体的例としては、h2dbの DATABASE_TO_UPPERtrueになっていると大文字でテーブルやカラムが作成されてしまって存在チェックで誤検知が発生してしまうことになります。

play-1.5.3 + h2db@1.4.196 では小文字で play_evolutions が作成
play-1.7.2 + h2db@1.4.200 では大文字で作成

組み合わせでデフォルト動作が異なるので JDBC_URL で明示的に指定した方が良いです。

]]>
WordPressを静的サイトとして公開する /archives/1793/ Sat, 29 Nov 2025 16:25:17 +0000 /?p=1793 このサイトはWordPressで作っています。CMSとして有名で情報も多く、プラグインも豊富。設置も簡単だったので使い始めたのですけど、いかんせん重い。

とくにコメント機能が盛り上がっているわけでもないので静的サイトで十分じゃないか、という結論に達しまして。編集作業はローカルで行い、静的サイトとしてエクスポートして公開することにしました。

ローカルでWordPressを稼働させるにあたり、SynologyのNASを活用させることにしました。というわけでそれなりに苦労もしたので手順を残しておきたいと思います。

移行の手順

WordPress

我が家にある SynologyのNASは DS216j です。家庭用の低価格マシンです。

パッケージセンターから WordPressを探してインストールすると、依存関係にある Apache HTTP Server PHP MariaDB も自動でインストールされます。これは楽ちんです。やり方を紹介しているサイトもたくさん見つかります。

最新版のWordPressではなく Synology が動作確認したバージョンで固定されたものになります。(私がインストールしたときは、WordPress 6.1.1Apache HTTP Server 2.4PHP 8.0MariaDB 10.3.32 でした。)

インストールが完了したらサイトの移行を行います。WordPress間の移行はエクスポート機能を利用することができるので省略します。

GitHub

まず、GitHub に公開資材を格納するレポジトリ(komina-info-publish)をPrivateで作成しました。任意のディレクトリで git clone して初期資材を作成していきます。

$ git clone https://github.com/komina77/komina-info-publish.git
komina-info-publish
 ├── app.yaml
 └── www/
      └── index.html

直下に app.yamlを作成。
wwwフォルダを作成しテスト用のindex.htmlを作成。

runtime: python313       # ランタイム指定(静的でも必須)
instance_class: F1       # 無料枠を使うならF1を指定
automatic_scaling:
  min_instances: 0
  max_instances: 1

handlers:
  # ルートURLの場合 index.html を返す
  - url: /
    static_files: www/index.html
    upload: www/index.html

  # その他のパスは www/ 以下のファイルにマッピング
  - url: /(.*)
    static_files: www/\1
    upload: www/(.*)
<h1>Hello, World!</h1>

App Engine

サイト公開用のプロジェクトの準備ができたら、Google Cloud にログインしプロジェクト(komina-info-publish)を作成、App Engineでアプリ作成します。

  • 近場の東京リージョン(asia-northeast1)を選択
  • サービスアカウントはデフォルト設定
  • 言語は PythonStandard環境を選択。

AppEngine側の準備ができたらプロジェクトを手動でデプロイして動作確認してみます。

$ gcloud app deploy
$ gcloud app browse

ブラウザにテスト用のindex.htmlの内容が表示されていればOKです。

GitHub → AppEngine

そうしたら、GitHub のコミット契機で AppEngine へデプロイされる仕組みを作ります。

Google Cloud Console で IAM サービスアカウント作成します。

  1. 権限付与
    • App Engine Deployer(App Engine デプロイ担当者)
    • Storage Admin(Storage オブジェクト管理者)
    • App Engine Admin(App Engine管理者)
    • Cloud Build Service Account(Cloud Build サービスアカウント)
    • Service Account User(サービスアカウントユーザ)
  2. 鍵を作成、json形式で保存

GitHub→Settings→Secrets and variables→Actions を開き、新しいSecret作成します(名前は GCP_CREDDENTIALS、内容はjson形式の鍵ファイルの中身を転記)。

GitHub→Settings→Secrets and variables→Actions を開き、新しいRepositoryVariable作成します(名前はGCP_PROJECT_ID、値は komina-info-publish)。

AppEngineへのデプロイ準備が整ったら、mainブランチへのpushをトリガにして AppEngineへデプロイするジョブを作成します。

name: Deploy to App Engine

on:
  push:
    branches: [ main ]   # mainブランチへのpushをトリガー

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout source
        uses: actions/checkout@v3

      - name: Authenticate to Google Cloud
        uses: google-github-actions/auth@v2
        with:
          credentials_json: ${{ secrets.GCP_CREDENTIALS }}

      - name: Deploy to App Engine
        uses: google-github-actions/deploy-appengine@v1
        with:
          project_id: ${{ vars.GCP_PROJECT_ID }}   # リポジトリ変数から取得
          deliverables: app.yaml            # デプロイ対象

      - name: Clean up old versions
        run: |
          # トラフィック割当0%の全バージョンを削除
          gcloud app versions list \
            --project=${{ vars.GCP_PROJECT_ID }} \
            --format="value(version.id)" \
            --filter="traffic_split=0.0" | \
          xargs -r gcloud app versions delete \
            --project=${{ vars.GCP_PROJECT_ID }} --quiet

App Engine Admin APIを有効にします。

main/www 配下に任意のweb資材を置いてpush。App Engine へリリースされていることを確認できればOKです。

WordPress-Staaticプラグイン

静的サイトを出力するプラグインとしては Simply Static というプラグインが有名なようです。残念ながら WordPress 6.1.1 では動作しないということでインストールできなかったので、次点でヒットした Staatic – Static Site Generator をインストールしました。

  • GitHub の プロフィールアイコンから、Settings → Developer settings → Personal access tokens→Fine-grained personal access tokens
  • 適当な期限を入力。
  • レポジトリを指定: komina-info-publish
  • 権限の付与: Content: read and write
  • 生成されたトークンをGitHubトークン欄へコピペ。
  • レポジトリ: komina77/komina-info-publish
  • ブランチ: main
  • 接頭辞: www (/を付けてはいけない)

WordPress-パーマリンク

WordPressサイトを静的サイトに変換するにあたってパーマリンク設定に注意が必要です。GETパラメータを含むURLや、/ (スラッシュ)で終わらないURLは、htmlのみで再現できないためです。

変換元サイトでは /archives/123 のような数字ベース形式を採用していたのですが、このままでは変換できないので最後に / (スラッシュ)を加えた形式をカスタム構造に設定しました。

リダイレクト設定

変換元サイトで検索エンジンに登録されていた /archives/123 のようなURLへの参照があったときに 404 が返されてしまうと訪問者に不便が生じます。新しい /archives/123/ へ 301リダイレクトするように設定を追加します。

といっても app.yamlhandlers設定ではそこまでできないようなので、Pythonにやってもらうことにしました。

from flask import Flask, redirect

app = Flask(__name__)

@app.route("/archives/<int:num>")
def redirect_archives(num):
    return redirect(f"/archives/{num}/", code=301)
Flask==3.0.0
# handlers以下を抜粋
handlers:
  # アーカイブURL(数値のみ)をスラッシュ付きにリダイレクト(ルート除く)
  - url: /archives/([0-9]+)$
    script: auto  # アプリケーションコードに処理を委ねる

  # スラッシュで終わるURL(任意の深さ)の場合、index.htmlを返す
  - url: /(.*)/$
    static_files: www/\1/index.html
    upload: www/(.*)/index.html

  # ルートURLの場合 index.html を返す
  - url: /
    static_files: www/index.html
    upload: www/index.html

  # その他のパスは www/ 以下のファイルにマッピング
  - url: /(.*)
    static_files: www/\1
    upload: www/(.*)

一連の動作確認

これまでの設定がうまく行われていれば、Staaticプラグインで公開することで、静的サイト資材の作成、GitHubへのPush、AppEngineへのデプロイまで自動で行われるようになります。

私の環境、サイトでは資材の作成の10分強、GitHubへのPushに10分弱、AppEngineへのデプロイに3分弱、と言ったところでしょうか。

カスタムドメイン設定

NASで稼働する WordPressサイトを AppEngineで公開できることが確認できたら、ドメイン設定を行って正式に移行を完了させます。

AppEngineの設定からカスタムドメインを選択。

自分のドメインと対応付けていき、最後に表示されるキーをDNSのレコードに登録します。私の場合は muumuuドメインを利用しているので管理画面からムームーDNSの komina.info の変更へ進みます。

DNS設定が反映されるまでに少し時間がかかります。めでたく https://komina.info/ で静的サイトへアクセスできなっていれば移行は成功です。

考察など

App Engineに静的資材を公開する機能があるのは知っていはいたのですが、ほぼ100%静的サイトとして利用するのは初めての試みでした。

非常にレスポンスも速く、今のところ満足しています。

今回犠牲になったコメント機能ですが、AppEngineということなのでPythonで実装するのもアリかな、とか思ったりしています。手が空いたら考えてみたいと思います。

]]>
AppEngineへ移行 /archives/1779/ Wed, 19 Nov 2025 14:36:59 +0000 /?p=1779 本サイトのホスティングを Azure AppService(F1) + AzureCDNの構成から、Google AppEngineへと移行することにしました。

それに伴い、WordPressをホスト上で動かすのではなくローカルのSynology DS216にインストールして動かすことにしました。投稿するたびに htmlを出力してデプロイするようにセッティングしました。

詳しい方法や構成などは別のポストで紹介できればと思います。

]]>
H2DBのLinked Tablesについて調べてみた /archives/1670/ /archives/1670/#respond Tue, 05 Aug 2025 14:40:20 +0000 https://www.komina.info/?p=1670 H2DBにはLinked Tablesという機能があります。外部テーブル(ほかのDB)へのテーブルリンクを作成して、あたかも H2DB 内に存在するテーブルかのように扱う機能です。

JDBCドライバで接続できるDBであれば、以下のようなコマンドで利用可能です。

CREATE LINKED TABLE LINK('org.h2.Driver', 'jdbc:h2:./test2', 'sa', 'sa', 'TEST');

社内に MySQL PostgreSQL などいろんなDBサーバが乱立しているようなときに使ったら便利そう、ということで調べてみることにしました。

環境構築

docker-composeを使ってお試し環境を作ってみました。h2db + mysql + postgresql な環境です。githubに一式を公開してあります。

https://github.com/komina77/h2db

git clone https://github.com/komina77/h2db.git
cd LinkedTables
docker-compose up -d

サンプルデータは MySQL の公式サイトにあるサンプルデータworld)を利用させてもらうことにします。これを mysqlpostgresql の両方の初期データとしてインストールすることにします。(postgresql へはそのままの文法では食わせることはできなかったので少し書き換えています)

city, country, countryLanguage の3つのテーブルから構成されるデータベースになります。

MySQLを参照してみる

実はH2DBのサーバに内蔵されているWeb管理画面はH2DB以外のDBにも接続できますので、これを利用してみたいと思います。

ホストOS上のブラウザで、http://localhost:8082/ を開き、保存済設定の中から「Generic MySQL」を選びます。ドライバクラスなどのデフォルト設定が表示されるので、JDBC URLやユーザ名、パスワード(mysql/mysql)を入力します。接続テストが通れば成功です。

PostgreSQLを参照してみる

同様に PostgreSQL のデータベースも参照することができます。

オートコンプリート機能も有効で、ちょっとしたSQLならすぐに試してみることができます。PostgreSQLの大文字小文字混在のカラムはダブルコーテーションで囲わないといけないので、逆に不便でした。

Linked Tables を定義してみる

mysqlpostgresql の準備ができたところで H2DB に接続して Linked Tables を作成してみたいと思います。

まずは mysqlworldデータベースの中の countryテーブルを my_country という名前で参照できるようにしてみます。

CREATE LINKED TABLE IF NOT EXISTS MY_COUNTRY(
'com.mysql.jdbc.Driver', 'jdbc:mysql://mysql-1:3306/world', 'mysql', 'mysql', 'country'
);

これだけで MY_COUNTRY というテーブルをローカルにあるテーブルのように参照できるようになります。

同様に、PG_CITYPG_COUNTRYLANGUAGE についてもテーブルを定義します。

CREATE LINKED TABLE IF NOT EXISTS PG_CITY(
'org.postgresql.Driver', 'jdbc:postgresql://postgres-1/world', 'postgres', 'postgres', 'city'
);

CREATE LINKED TABLE IF NOT EXISTS PG_COUNTRYLAUNGUAGE(
'org.postgresql.Driver', 'jdbc:postgresql://postgres-1/world', 'postgres', 'postgres', 'countrylanguage'
);

どんなSQLが発行されているか(MySQL

まずは条件なしで検索。

SELECT * FROM MY_COUNTRY 
SELECT * FROM country T

次は簡単な条件を付けてみます。

SELECT * FROM MY_COUNTRY where CODE ='JPN'
SELECT * FROM country T 
WHERE CODE>='JPN' AND CODE<='JPN'

単純な文字列の等号による一致条件のつもりだったのですが不等号の範囲検索に変換されてしまいました。なにか意図があるのでしょうがとりあえずヨシとします。

どんなSQLが発行されているか(PostgreSQL

SELECT * FROM PG_CITY 
SELECT * FROM public.city T
SELECT * FROM PG_CITY 
WHERE ID =10
SELECT * FROM public.city T 
WHERE ID>=$1 AND ID<=$2
ERROR:  column "id" does not exist at character 35

おっと。エラーが出てしまいました。どうやらDDLの段階でカラム名をダブルコーテーションで囲って大文字小文字を厳密に定義していたことが原因と思われる。。

DDLを修正してもう一度やり直し。PG_CITYPG_COUNTRYLANGUAGE についてもテーブル定義をやり直します。

SELECT * FROM PG_CITY 
WHERE ID =10
SELECT * FROM public.city T 
WHERE ID>=$1 AND ID<=$2
DETAIL:  parameters: $1 = '10', $2 = '10'

無事にクエリを発行することができました。

結合したらどうなるか(異DB同士

テーブル一つに対するクエリであれば、ほぼ等価の条件式が渡されるようなので、リンクされた側のDBで適切な実行計画が適用されることになりそうです。

では、2つのDBにまたがる結合をしたらどうなるのか。やってみたいと思います。

SELECT T1.NAME, T2.LANGUAGE  FROM MY_COUNTRY  T1
INNER JOIN PG_COUNTRYLAUNGUAGE  T2
ON T2.COUNTRYCODE = T1.CODE
AND  T2.ISOFFICIAL ='T'
WHERE T1.REGION  = 'North America'
;

北アメリカの国における公用語は?、という感じの意味合いになります。まず MY_COUNTRY を北アメリカで絞り込んだのち、国コードで PG_COUNTRYLANGUAGE から公用語を得る、という結合になることを想定してみました。

MySQL

SELECT * FROM country T WHERE CODE>='ABW' AND CODE<='ABW'
SELECT * FROM country T WHERE CODE>='AFG' AND CODE<='AFG'
SELECT * FROM country T WHERE CODE>='AFG' AND CODE<='AFG'
SELECT * FROM country T WHERE CODE>='AIA' AND CODE<='AIA'
SELECT * FROM country T WHERE CODE>='ALB' AND CODE<='ALB'
SELECT * FROM country T WHERE CODE>='AND' AND CODE<='AND'
SELECT * FROM country T WHERE CODE>='ANT' AND CODE<='ANT'
SELECT * FROM country T WHERE CODE>='ANT' AND CODE<='ANT'
SELECT * FROM country T WHERE CODE>='ARE' AND CODE<='ARE'
SELECT * FROM country T WHERE CODE>='ARG' AND CODE<='ARG'
SELECT * FROM country T WHERE CODE>='ARM' AND CODE<='ARM'
:
:

PostgreSQL

SELECT * FROM public.countrylanguage T WHERE ISOFFICIAL>=$1 AND ISOFFICIAL<=$2
-- parameters: $1 = 'T', $2 = 'T'

どうやら想定と逆に、PG_COUNTRYLANGUAGE から公用語の一覧を取得したのち、対応する国の一覧を取得。クエリには北アメリカでの絞り込みは入っていないので H2DB側で一番最後に行われたようです。

実行計画を見てみる

事後になりますが H2DBでの実行計画を見てみます。

EXPLAIN
SELECT T1.NAME, T2.LANGUAGE  FROM MY_COUNTRY  T1
INNER JOIN PG_COUNTRYLAUNGUAGE  T2
ON T2.COUNTRYCODE = T1.CODE
AND  T2.ISOFFICIAL ='T'
WHERE T1.REGION  = 'North America'
;
SELECT
    "T1"."NAME",
    "T2"."LANGUAGE"
FROM "PUBLIC"."PG_COUNTRYLAUNGUAGE" "T2"
    /* PUBLIC."": ISOFFICIAL = 'T' */
    /* WHERE T2.ISOFFICIAL = 'T'
    */
INNER JOIN "PUBLIC"."MY_COUNTRY" "T1"
    /* PUBLIC."": CODE = T2.COUNTRYCODE */
    ON 1=1
WHERE ("T1"."REGION" = 'North America')
    AND (("T2"."ISOFFICIAL" = 'T')
    AND ("T2"."COUNTRYCODE" = "T1"."CODE"))

FROM句で指定していたテーブルが MY_COUNTRY から PG_COUNTRYLANGUAGE に変わっていますね。H2DB では結合順序指定するヒント句はサポートされていません。性能を高めるには希望する結合順になるまで SQLをこねくり回すしかなさそうです。

結合したらどうなるか(同DB同士

同じDBにあるテーブル同士の結合も試してみます。

EXPLAIN
SELECT T1.COUNTRYCODE ,T1.DISTRICT , T1.NAME , T2.LANGUAGE
FROM PG_CITY T1
INNER JOIN PG_COUNTRYLAUNGUAGE T2
ON T2.COUNTRYCODE =T1.COUNTRYCODE 
WHERE T1.POPULATION >7000000
AND T2.ISOFFICIAL ='T'
;
SELECT
    "T1"."COUNTRYCODE",
    "T1"."DISTRICT",
    "T1"."NAME",
    "T2"."LANGUAGE"
FROM "PUBLIC"."PG_CITY" "T1"
    /* PUBLIC."": POPULATION > 7000000 */
    /* WHERE T1.POPULATION > 7000000
    */
INNER JOIN "PUBLIC"."PG_COUNTRYLAUNGUAGE" "T2"
    /* PUBLIC."": COUNTRYCODE = T1.COUNTRYCODE */
    ON 1=1
WHERE ("T2"."COUNTRYCODE" = "T1"."COUNTRYCODE")
    AND (("T1"."POPULATION" > 7000000)
    AND ("T2"."ISOFFICIAL" = 'T'))

人口が700万人超の都市のある国の公用語の一覧を得るクエリです。PostgreSQL へは以下のクエリが送信されました。

SELECT * FROM public.city T WHERE POPULATION>=$1
-- parameters: $1 = '7000000'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'BRA', $2 = 'BRA'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'GBR', $2 = 'GBR'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'IDN', $2 = 'IDN'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'IND', $2 = 'IND'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'IND', $2 = 'IND'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'JPN', $2 = 'JPN'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'CHN', $2 = 'CHN'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'CHN', $2 = 'CHN'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'KOR', $2 = 'KOR'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'MEX', $2 = 'MEX'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'PAK', $2 = 'PAK'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'TUR', $2 = 'TUR'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'RUS', $2 = 'RUS'
SELECT * FROM public.countrylanguage T WHERE COUNTRYCODE>=$1 AND COUNTRYCODE<=$2
-- parameters: $1 = 'USA', $2 = 'USA'

700万人超の都市の一覧を取得したのち、国コードを使って1国ずつ PG_COUNTRYLANGUAGE を取得していますね。ISOFFICIAL については記載がないので H2DB側で絞り込みを行っているのでしょう。

同じDBにあるテーブルであっても、結合をそのDBに任せず H2DB内で行っていることが分かりました。

まとめ

  • H2DB には他のDBのテーブルをH2DB内のテーブルのように見せかける機能がある。複数のシステムのDBを透過的に扱うのに便利。
  • 外部DBのテーブル定義に依存する。カラム名に大文字小文字混在を許すようなテーブルを参照するときは失敗することがあるので、事前に調査してビューなどを介すなど工夫が必要。
  • テーブル1つに対するクエリであればテーブルを持つDB上で抽出が行われるので、性能的には問題なさそう。
  • リンクテーブル同士での結合はサポートされている。しかし、H2DBの判断で実行計画が決まってしまうので性能が出ないことがある。
  • 結合するリンクテーブルが同じDBのテーブルであっても、結合がリンク先のDBに任されない。H2DBの判断によって行われる。

異なるシステムで使われているDBのテーブルを一時的に参照するような用途では便利かもしれない。しかし、複雑なクエリで問い合わせるときは性能を出すために苦労することになりそうだ。

]]>
/archives/1670/feed/ 0
OneThirdCMSを動かしてみたかった話 (2) /archives/1647/ /archives/1647/#respond Thu, 12 Jun 2025 16:06:58 +0000 https://www.komina.info/?p=1647 前回、PHP8.1では動かすことが叶わず、PHP7系が必要だったことが判明したところで終わりました。あのままフェードアウトする予定だったのですが、少しモチベーションが回復してDockerイメージのベースをちょいと変えるだけということに気づいたので、もうちょっとだけ頑張ってみることにしました。

というわけで Dockerfile の一行目を以下のように書き換えてビルドしなおしました。

FROM php:7.4-apache-bullseye

エラーが出ずにインストール完了しました。さっそくURLに /login を付けてログインしてみることにします。

ん?なんだかデザインが崩れているような。開発者ツールでコンソール出力を確認してみたところ、以下のようなエラーが。

Azureで生成される長いURLのせいで難しいことが書いてあるように見えますが、よくよく読むとスタイルシートの読み込みで https を使っていないことで怒られているようです。このせいでスタイルシートが読み込まれなかったようです。

デザイン崩れはしょうがないとして、ユーザIDとパスワードを入力してみたのですが、ログイン失敗になってしまいます。どうやらログインは ajaxで処理されていてそのエンドポイントのURLがこれまた https を使っていないことでエラーになっていました。

これはおそらく Azure App Service 側の問題のような気がしてきました。プログラム側からホストのURLを取得したときに https://… ではなく http://… と認識されてしまっているのかも?

いったんサービスを停止し、/backup/data 配下を確認してみることにします。(うまくスクリプトが動いていればコンテナ停止時にコンテナ内の /var/www/html 配下のファイルが /backup/data へ同期されて見えるようにっているはずです)

そこで config.php というファイルが作成されているのを見つけ開いてみると案の定、http:// で設定されていました。オンラインインストール時にこのURLで登録されたのでしょうか。

	// path
	$config['site_path'] = '/var/www/html';
	$config['site_url'] = "http://onethirdcms-g9gtarhtafc3dte9.japaneast-01.azurewebsites.net/";
	$config['site_ssl'] = "http://onethirdcms-g9gtarhtafc3dte9.japaneast-01.azurewebsites.net/";
	$config['files_path'] = '/var/www/html/files';
	$config['files_url'] = "http://onethirdcms-g9gtarhtafc3dte9.japaneast-01.azurewebsites.net/files/";

config.php の当該箇所を修正して再アップロード。サービスを起動しなおします。

開発ツールを起動しながらページを開くと、今度は Mixed Contentエラーは出なくなりました。スタイルシートのリンクも https://… に切り替わっています。:-)

これでイケるのかも!と思って /login へアクセスしてみたのですが何とエラーも何も出ずホームページのまま。ログイン画面へ遷移しなくなってしまいました。開発者ツールにはエラーは出ない。ログイン画面が表示されない。状況は悪化してしまいました。

これ以上は内部の仕組みを調べながらでないと難しそう。今度こそあきらめることにします。ここまで読んでいただきありがとうございました。ということで OneThirdCMSを動かしたい方は実績のあるレンタルサーバを使いましょう。さよなら。

追記

ローカル環境ではログインしてCMSとして使えそうなので、ローカル環境で作成した資材を Azure App Service の公開フォルダへアップロード(config.phpを除いて)するという手が使えるかもしれません。ちょっと手間ですがローカルPC側の backup.sh で工夫すれば自動で同期をとるといった方法も使えるかもしれませんね。

追記2

少し稼働してみて気づいたこと。コンテナ起動時のスクリプトで停止シグナルはトラップするようにしており、手動でApp Serviceを停止したり再起動するときはトラップできているようですが、アクセスが途絶えて自動でシャットダウンするときには動いていないようです。

どうやら App Service on Linux はそうゆうものらしく、カスタムコンテナ以外ならスタートアップスクリプトに仕込むことで対処できるような話が見つかりました。

今回はカスタムコンテナなので初っ端に docker run が動いてしまうので対処のしようが無さそうです。

Copilotに相談したらに Azure Container Apps を使えば?みたいな回答をいただきました。

]]>
/archives/1647/feed/ 0
OneThirdCMSを動かしてみたかった話 (1) /archives/1610/ /archives/1610/#respond Mon, 09 Jun 2025 08:54:20 +0000 https://www.komina.info/?p=1610 このサイトはWordPressをAzureのAppServiceのフリープラン+CDNで細々と運用していまが、あまりお金をかけてないためWordPressが不安定なところが否めず。何かよい引っ越し先はないかと「軽量で無料なCMSを探しています」的な質問を Copilot に尋ねてみたわけです。

すると福岡発の純国産CMSであるOneThirdCMS(公式ページ https://onethird.net/)を推してくるではありませんか。SQLiteもサポートしていてDBサーバを必須としていないところもポイント高い。というわけで、さっそく試してみることにしました。

結論から言うとうまく行かなかったのですが、紆余曲折あり長くなったので記録もかねてここに記しておきます。

Dockerコンテナで動かす

ダウンロード資材は2種類あって、1つはWindowsPCでWebサーバを起動してその上でOneThirdCMSを動かすもの。もう1つはWebサーバ上で動くオンラインインストーラ。

私のゴールは Azure AppService(F1プラン)で単一コンテナで動かすことなので後者を使うことにします。Docker 公式の phpapacheコンテナを動かせばすぐに使えるかと思ったのですが・・・

  • mod_rewrite が必須(=Apacheで動かすことが前提)。これがクリア。
  • SQLiteで稼働する予定でも pdo_mysql が有効になっている必要あり。
  • オンラインインストールでzip展開するのでzipが有効になっている必要あり。
  • PHP8.2 から導入された仕様「動的プロパティの作成が非推奨」により "Deprecated: Creation of dynamic property Ut::$circle is deprecated in /var/www/html/module/utility.php on line 296 OneThird CMS" というようなエラーが発生するので PHP8.1 で動かす。

ということで php:8.1-apache-bookworm をベースに Dockerfile を作成しました。

FROM php:8.1-apache-bookworm

# Install zip & mysql
# Install required packages
RUN apt-get update && apt-get install -y libzip-dev locales

# Install PHP extensions
RUN docker-php-ext-install zip pdo_mysql

# Set up Japanese locale
RUN sed -i '/^# *ja_JP.UTF-8 UTF-8/s/^# *//g' /etc/locale.gen && \
    locale-gen && \
    update-locale LANG=ja_JP.UTF-8

ENV LC_ALL=ja_JP.UTF-8

# Set timezone to Asia/Tokyo
RUN apt-get install -y tzdata && \
    ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && \
    echo "Asia/Tokyo" > /etc/timezone

# Set cron 
RUN apt-get install -y cron

# Set rsync
RUN apt-get install -y rsync

# enable rewrite
RUN a2enmod rewrite

# custom php entrypoint
COPY custom-php-entrypoint "/usr/local/bin/"
RUN chmod +x "/usr/local/bin/custom-php-entrypoint"
ENTRYPOINT [ "/usr/local/bin/custom-php-entrypoint" ]
CMD ["apache2-foreground"]

WORKDIR /var/www/html

Docker勉強中なのでイケてないところがあるかもしれません。

githubのプライベートレポジトリにまとめていますが、GitHub Actions の勉強もかねて Docker Hubへ公開しています(https://hub.docker.com/r/komina77/onethirdcms)。使い方らしきものも書きました。

なぜ App Service の PHP8.1 を使わないか

App ServicePHP8.1で動かせばいいじゃないか、と思われるかもしれません。FTPSで公開用ディレクトリにオンラインインストーラ index.php を置くだけ済みそうです。

でもダメでした。

公開用のディレクトリ /homeAzure FilesCIFS/SMB をマウントして永続化していて、そのために POSIX のファイル操作の原子性や即時反映が期待通りに動作しなかったようです。(writableチェックとして、ファイルを複製して、元ファイルを削除、直後に再作成、でエラーになっている。ここまで念入りにチェックしていることにビックリした)

Webサーバも nginx を使っているようなので上記をクリアしてもおそらく mod_rewrite の問題で不可。

というわけで カスタムコンテナで PHP8.1 + Apache を動かす選択をするに至りました。コンテナ内のファイルシステムであればPOSIX準拠の動作になりますのでオンラインインストーラのチェックはクリアするかと。一方でファイルは一時ストレージに保持されることになるため、コンテナ停止時には /home へデータを退避は必要。

Azure App Serviceで動かす

Azure で リソースの作成>Web アプリ でリソースを作成します。公開方法を「コンテナ」、OSはLinux、価格プランを「Free F1(共通インフラストラクチャ)」を選びます。

App Serviceの設定をする

設定>環境変数

変数名設定値説明
WEBSITES_ENABLE_APP_SERVICE_STORAGEtrueApp Service機能。
コンテナ内 /home 配下が永続化されFTPでアクセスできる。
BACKUP_DIR/home/backupkomina77/onethirdcms 向け設定。
永続化対象のディレクトリを意識する。
BACKUP_CRON0 * * * *komina77/onethirdcms 向け設定。
cron書式。永続化のスケジュール。0 * * * * は毎時0分ごと。

設定>構成

  • FTPを使えるようにする。
    • SCM基本認証の発行資格情報をON
    • FTP基本認証の発行資格情報をON
    • FTPの状態をFTPSのみ
  • デプロイ>デプロイセンター>FTPS資格情報 に有効な値が表示されるのでFTPクライアントに登録する。

デプロイ>デプロイセンター>設定

項目設定値
ソースContainer Registry
コンテナの種類単一コンテナー
レジストリ ソースDocker Hub
リポジトリ アクセスパブリック
完全なイメージの名前とタグkomina77/onethirdcms:latest
スタートアップファイルまたはコマンド空欄
継続的デプロイOFF
WebhookURL空欄

準備作業

komina77/onethirdcms ではコンテナ起動時に ${BACKUP_DIR}/backup.sh 、コンテナ停止時に ${BACKUP_DIR}/restore.sh が実行されるようになっています。また、${BACKUP_CRON} が設定されていれば定期的に ${BACKUP_DIR}/restore.sh が実行されます。

komina77/onethirdcms では /var/www/html 配下がサイトとして公開されるようになっているので、${BACKUP_DIR}/data/var/www/html の間でファイルを同期するようなスクリプトを用意しておくことで公開資材を永続化することができるという仕組みです。

そこで、FTPクライアントから以下のスクリプトを保存したファイルを /${BACKUP_DIR} から /home を除いたディレクトリにアップロードしておきます。(${BACKUP_DIR}=/home/backup であれば、/backup/backup.sh, /backup/restore.sh という感じです)

#!/bin/sh

# 環境変数チェック
if [ -z "$BACKUP_DIR" ]; then
    echo "Error: BACKUP_DIR environment variable is not set"
    exit 1
fi

# ソースディレクトリ確認
SRC_DIR="/var/www/html"
if [ ! -d "$SRC_DIR" ]; then
    echo "Error: Source directory $SRC_DIR does not exist"
    exit 1
fi

# バックアップ先ディレクトリ作成
DEST_DIR="$BACKUP_DIR/data"
mkdir -p "$DEST_DIR"
if [ $? -ne 0 ]; then
    echo "Error: Failed to create backup directory $DEST_DIR"
    exit 1
fi

# バックアップ実行
echo "$(date '+%Y-%m-%d %H:%M:%S') - Starting backup: $SRC_DIR → $DEST_DIR"
rsync -a --delete "$SRC_DIR/" "$DEST_DIR/"
if [ $? -eq 0 ]; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - Backup completed successfully"
    exit 0
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - Error occurred during backup"
    exit 1
fi
#!/bin/sh

# 環境変数チェック
if [ -z "$BACKUP_DIR" ]; then
    echo "Error: BACKUP_DIR environment variable is not set"
    exit 1
fi

# バックアップソースディレクトリ確認
SRC_DIR="$BACKUP_DIR/data"
if [ ! -d "$SRC_DIR" ]; then
    echo "Error: Backup directory $SRC_DIR does not exist"
    exit 1
fi

# 復元先ディレクトリ確認/作成
DEST_DIR="/var/www/html"
mkdir -p "$DEST_DIR"
if [ $? -ne 0 ]; then
    echo "Error: Failed to create restore destination directory $DEST_DIR"
    exit 1
fi

# 復元実行
echo "$(date '+%Y-%m-%d %H:%M:%S') - Starting restore: $SRC_DIR → $DEST_DIR"
rsync -a --delete "$SRC_DIR/" "$DEST_DIR/"
if [ $? -eq 0 ]; then
    echo "$(date '+%Y-%m-%d %H:%M:%S') - Restore completed successfully"
    exit 0
else
    echo "$(date '+%Y-%m-%d %H:%M:%S') - Error occurred during restore"
    exit 1
fi

OneThirdCMSのインストール作業

OneThirdCMSを動かす環境が準備できたらインストール作業を行います。

  • OneThirdのダウンロードからオンラインインストーラをダウンロードし解凍する。
  • FTPクライアントから解凍したファイル index.php/${BACKUP_DIR}/data から /home を除いたディレクトリにアップロードする。
  • App Service の 概要 へ
    • 開始されてなかったら開始をクリック。
    • 参照をクリックすると公開されているURLが別タブで開く。このURLへのアクセスがトリガになってコンテナ起動となる。初回はコンテナのプルから始まるので時間かかります。
うまく準備ができていればブラウザに図のような画面が表示される

[Start Download OneThird CMS] ボタンをクリックするとダウンロードが走ります。

ダウンロードが終了するとインストールボタンが表示される

[Install OneThird CMS] ボタンをクリックすると資材が解凍されてインストールが開始されます。1項目ずつチェックが入り、見た目もかっこいいです。

今回はSQLiteを使うので [インストール(SQLite)] をクリック。

Admin ID や Admin password を適当に入れて、jQuery はCDN参照を選んでインストール開始します。

順調にインストールは進み・・・

成功したように思いきやなんかエラーが出てしまった。

うむ。私はPHP5までの人なのだが、どうやら PHP8 で非推奨になった書き方についてのエラーのようだ。PHP8.1からさらにPHP7系に戻すのもセキュリティ的に問題ありですし(PHP8.1もセキュリティサポート切れてますが)、ソースを直してまで稼働するところまで持っていく義理もなく。。

残念だがここで中断することにした。おそらくこういった非推奨エラーになるところを一つ一つ潰していけば、この興味深いCMSは動くようになると思われる。

(よくよく見るとローカルPCで動かす版のPHPもバージョン7のようだ :-))

中途半端になってしまったが、無駄にAzure App Serviceの知識が増えたところで今回はあきらめることにした。長々と読んでいただいた方、結果がパッとせず申し訳ない。

]]>
/archives/1610/feed/ 0
FreeCAD v1.0 でなんか書く (4) /archives/1570/ /archives/1570/#respond Tue, 08 Apr 2025 15:19:32 +0000 https://www.komina.info/?p=1570 (1)で1×6材、2×6材は、Fillet 処理を省略してきました。組立工程が一段落したのでFillet 処理を施してみます。

側板のFillet 処理

  • 側板の Body > Pad を選択して、Filletボタンを押下する。

簡単ですが2×4材っぽくなりました。

棚板の Fillet処理

  • 同様に Pad を選択して、Filletボタンを押下する。

組立図を見る

部品の方の変更はそのまま組立図の方に反映されます。Fillet処理がされると急にリアル感が増しますね。

着色してみる

さらにリアルにするために部品に色を付けていきます。

  • 側板のボディを右クリックする。
  • メニューから「外観…」をクリックする。
  • 「外観をカスタマイズ」をクリック。
  • マテリアル・プロパティのダイアログが開くので「散乱光の色」をクリック。
  • 色を選択のダイアログが開くので色を選択する。
  • 同じように、棚板や取付金具の色を変更していく。
  • 色を選択する方法以外に、プリセットされた素材を選択する方法もある。便利。

塗装を考えているときは、配色をシミュレーションできますね。

次はアセンブリワークベンチの機能を使ってみたいと思います。

]]>
/archives/1570/feed/ 0
FreeCAD v1.0 でなんか書く (3) /archives/1485/ /archives/1485/#respond Fri, 04 Apr 2025 15:54:16 +0000 https://www.komina.info/?p=1485 構成する部品が一通り準備できたのでFreeCADのV1.0で標準で同梱されるようになった Assemblyワークベンチを使って組み立ててみたいと思います。

  • 新規作成、「ディスプレイシェルフ.FCStd」でファイルを保存。
  • Assemblyワークベンチに切り替える。
  • アセンブリ作成ボタン押下、さらにコンポーネント挿入ボタン押下する。
  • ファイルを開き、先ほど作成した部品コンポーネントを開く。
  • そしてまず、基準となる部品のボディを挿入する。
  • 最初の部品を挿入すると基準パーツにするかを問うダイアログが出てくるので、Yesボタン押下する。
  • 引き続き、棚板を1枚挿入する。
  • 側板は基準となるパーツなのでカギマークが中心に描かれている。(非表示も可能)
  • 棚板は側板に埋まった状態になる。
  • ツリー状には挿入したパーツのラベル名が表示されるので、必要に応じて分かりやすく変更する。
  • ツリーから棚板を選ぶと移動・回転するためのXYZ軸の矢印が出てくる。(ギズモ、と呼ぶらしい?)
  • まずはこの矢印を使っておおよその位置関係に移動する。
  • 組立は様々なジョイントを使用する。
  • まず側板に対して棚板が上下にスライドして高さ調整するイメージで、接する辺をそれぞれ選択した状態とする。
  • スライダージョイントを作成ボタンを押下する。
  • 2つの辺とスライダーという情報だけでジョイントが作成される。
  • タスクにて、回転角度やオフセット値を変更して希望の位置関係に修正する。
    ここでは回転=-90°とする。
  • OKを押してジョイント完了。
  • 棚板をドラッグして上下に動かせる状態となる。
  • 最小長さ=最大長さ、にすると固定状態となる。
  • 棚の高さは組み立て後に調整することにして、同じように棚板を追加していく。
  • 反対側の側板を追加する。
  • 側板のジョイント方法1
    • 側板_右と側板_左を固定ジョイント、オフセットを棚板の長さ700mmで固定する。
  • 側板のジョイント方法2
    • 棚板とスライダージョイント。距離ジョイントで側板の左右で高さが一致するようにする。
  • 後者であれば側板の長さが変わったときに対応できそう。前者であっても変数を使っておけば対応できそう。
  • ここでは前者の方法(固定ジョイント)を採用。
  • 側板の内側の面をそれぞれ選択して、固定ジョイントを押下する。
  • 選択した面同士がピタっとくっつくので、オフセット値を調整する。

棚の全貌が見えてきました。次はL字取付金具を付けていきます。

取付金具は木材の面に接することを条件に固定していきます。

  • らくらく取付L字金具を1つ追加する。
  • 組み立て方の画像のように向きと位置を合わせる。
  • 代替の位置が決まったら、作業しやすいように「側板_左」を非表示にする。
  • 「側板_左」と接する棚板の側面と、取付金具の面を選択する。
  • 距離ジョイント(距離0mm)を押下する。
  • だいたい望んだとおりに配置されないので、落ち着いて向きを調整する。
  • 取付金具の方のオフセットを調整するので、Offset2のボタンを押下する。
  • 取付金具に表示されているギズモを見て調整していく。
  • 今回の場合は向きは問題ないので位置調整を行う。
    • 赤のX軸の方向で手前に移動。
    • 緑のY軸の方向で下へ移動。
  • 次は棚板の底面と取付金具の面を選択する。
  • 同じように距離ジョイント(距離0mm)。
  • 側板との距離ジョイントを保持しつつ、棚板の底面とピタリとくっついた。
  • 思惑通りになったので今回は調整は不要。
  • さらに棚板の前面と取付金具の面を選択し、同様に距離ジョイント(0mm)。

こちらも棚板の前面にピッタリくっつきました。ここで「側板_左」を表示状態に戻してみると、いい感じに組みあがっていることが分かります。

取付金具は棚板に固定されているので、棚板をドラッグして上下させると、取付金具も一緒に動くことが確認できますよ。

  • あとは側板の左右、棚板の分だけ取付金具を挿入&固定していく。
  • 完成形に近づいてきた。

上部にアジャスターを装着します。

  • 2×4材用アジャスターを追加。
  • 移動して「側板_右」の上部に配置する。
  • 側板の上面と、アジャスターの面を選択して固定ジョイント。
  • 2×4材用アジャスター、側板、それぞれ作図したときに原点=中心になるようにしたので固定ジョイントだけでいい感じの場所に固定された。
  • ハイグリップアジャスターを追加。
  • 移動して「2×4材用アジャスター_右」の上部に配置する。
  • ハイグリップアジャスターの軸と、2×4材用アジャスターのナット穴の円筒を選択する。
  • 円筒ジョイントを押下する。
  • ネジ穴にハイグリップアジャスターがスポッと固定されればOK。
  • ハイグリップアジャスターをドラッグして上下すると、ネジが回転しながら上下する。すごい!
  • 左側の側板についても同じようにアジャスターを固定する。

これで一通り組み上げることができました。ツリーはこんな感じになりました。

私なりに調べて試して何とか形になりましたが、正直これが正解なのか分かりません。。参考になりましたら幸いです。

]]>
/archives/1485/feed/ 0
FreeCAD v1.0 でなんか書く (2) /archives/1423/ /archives/1423/#respond Fri, 04 Apr 2025 15:53:55 +0000 https://www.komina.info/?p=1423 2×4材用アジャスター

続けて2バイ用のアジャスターを書いていきます。早速サイズが分かる画像を検索してみたのですが・・・。(実際に購入する際はネジや滑り止めゴムシートが一緒になったセットが販売されています)

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

木材用アジャスター [8個]
価格:1,514円~(税込、送料別) (2025/4/3時点)


簡単な図だけでネジ穴の位置が詳細にわかる画像が見つかりません。そこで正攻法でメーカーHPをあたってみたとこr、製品の電子カタログ(PDF)を発見しました。商品ページに書かれているJANコードで検索すると詳細な図面にたどり着けました。これに従って書いていきます。

  • 新規作成。ファイル名は「2×4材用アジャスター.FCStd」。
  • ボディ作成、スケッチをXY平面で作成。
  • 中央の部分から板を生成して、Sheet Metalで耳を伸ばしていく方針。

さて耳はどうしようか、と思ったら図面には曲げRが記載されておらず困りました。門外漢なのでどんな値が適当なのかわかりません。先ほど書いた金具では、板厚1.2mmでR0.5mmでした。今回は板厚1.6mmということなので分かりやすくR1.0mmと仮定して進めます。耳の角丸もR4mmと仮定します。

  • D=39mm、曲げ(R1.0)を考慮して 39-1.0-1.0=37mm。
  • スケッチを閉じて Padで逆方向に押し出す。
  • Sheet Metal ワークベンチに切り替える。
  • 耳を作成する辺を選択して、Make Wall ボタンをクリック。
  • 曲げRは1.0mmのまま。壁の高さは板厚1.6mmと曲げ1.0mmを含んで25mmなので、25mm-1.6mm-1.0mm=22.4mm。
  • 片耳ずつスケッチを作成して抜き(ネジ穴、角丸)を書く。
  • スケッチを閉じて Pocket押下してタイプ「最初まで」で抜く。
  • 次はひっくり返して鉄製高ナットをくっつける。
  • スケッチを作成。
  • M10の高ナット(32mm)は Fasteners ワークベンチではやり方が分からなかったので自作することにする。
  • 正六角形を書いて、辺の並行拘束、サイズを13mmに設定してスケッチを閉じる。
  • Padで32mm押し出す。
  • それっぽく見えればいいので、押し出した面でスケッチを作成。
  • M10めねじ内径8.376mmを直径とする円を書いて Pocket で 32mm 掘る。

省略しましたけど雰囲気は再現できたように思います。

ハイグリップアジャスター

最後にハイグリップアジャスター M10×43.5という部品を書きます。


例によって電子カタログから数値データを見つけてきました。

  • ツマミの部分から書く。
  • 円から円柱の流れで書いていきたいところだけど、鏡餅みたいな断面になっているので断面を書いて回転させることにする。
  • ツマミのトータル高さは表から9.5mm。ギザギザのある部分の高さを仮に4.5mmとし、モリモリの円弧半径を2mm2mmとした。
  • この断面図のスケッチを閉じて、Revolution 押下。設定はデフォルト。
  • それっぽい形になってれば成功。
  • 次はツマミにギザギザを付けていく。(ローレット加工というらしい)
    • ツマミの円周に食い込むように三角柱をぐるっと配置して減算する流れ。
  • 新しいボディを作成し、XY平面でスケッチを作成する。
  • チビっとだけ円周に食い込む正三角形を書く。(サイズや位置は適当でOK、現物合わせ)
  • スケッチを閉じ、Pad 押下。
  • ボディを選択した状態で Part Designワークベンチの PolarPatternを押下する。
    円状パターンパラメータの回数を
  • 真上からの角度にするとわかりやすい。
  • 回数=64回にしてみると、ツマミの写真と近い感じになったので確定。
  • Part ワークベンチに切り替える。
  • ツマミ → 三角柱 の順にボディーを選択し、Cut(切り取り)をクリック。
  • ツマミから三角柱が切り取られた図形が作成される。
  • 次はネジ部分。見やすくするためにツマミの方は非表示にする。
  • 楽するため、Fasteners ワークベンチを使う。
  • まず、Add DIN ThreadedRod Metric を押下する。
    • DiameterはM10
    • Pich Customは1.5mm、ThreadはTrueに。
    • Lengthはツマミに少し埋めるつもりで長めに45mmとしておく。
  • ネジ部分を非表示にする。
  • Add ISO 4035 Hexagon thin nuts 押下。
    • DeiameterはM10
    • ThreadはTrue
  • ツマミ、ネジ部、ナットをすべて非表示→表示へ。
  • Part ワークベンチに切り替える。
  • まずツマミとナットが完全に重なっているので、ナットを移動させる。
  • ツリーからナットを選択して右クリック>変換。
    するとXYZの矢印が表示されるので、Z軸(青矢印)に沿って動かしてツマミ内部から外へ出す。
    動かす量は好みでOK。
  • 次はネジ部分が43.5mmとなるように、ツマミの中に埋め込む。
  • ネジ部分は45mmで作成したので、1.5mm上へ動かせばよい。
  • 最後にツマミ、ネジ部分、ナットを一つの立体へ統合する。
  • ツリーでツマミ、ネジ部、ナットをすべて選択し、Fuse(結合)を押下する。
  • Fusionという一つのオブジェクトに統合されて完成。

部品がすべて用意できたので組み立ててみたいですね。

]]>
/archives/1423/feed/ 0