buto > /dev/null

buto > /dev/null

だいたい急に挑戦してゴールにたどり着かずに飽きる日々です

電子工作〜まずはモーターを動かしてみる

電子工作始めてみた

猫との暮らしをより良くできるものが作れたら〜と思い電子工作を始めた

まずは安全ないたずら防止としてキッチンに猫が上がったら霧吹きで水をかける装置を作ってみたい

ということで、、

じゃーーーん!秋月電子ECで色々購入しました〜(注文翌日に届くという爆速感)

これらの材料を使ってまずはモーターを動かすところまでが今日の目標!

モーター動かすぞお

モーターはタミヤのギアボックスを買い、組み立て始めたけど...

部品多っ!小さい部品多っ!!猫が誤飲したらどうするんじゃ^-θωθ-^

開始10分ほどでギアボックスの組み立ては諦め、DCモーターのみを使うことにした

DCモーターを動かす記事を見てこれらの材料を調達したが、使い方がイマイチわからぬ。。

なんかいい動画ないかなーと探すと電子工作入門Udemy講座があった!!!

会社でUdemy学び放題入っていて良かった〜〜〜〜

動画を見ながらブレッドボードに線を差し込んで、、、はい!モーター動いた!!

てゆうか、モーター動きすぎなんですよねー。。こんな激しく動くと思わなかった

あとモーター動くと焦げ臭い?感じがしてなんか嫌

さあここからが本題だ!

自動で霧吹きが出る装置を作りたいのでモーターを動力に霧吹きのレバーを引けるようにしたい

ということで、このモーターに紐を結んでその紐を霧吹きのレバーにも結んで電源ON!!

はい。モーターが激しく回りすぎて紐が吹っ飛びました〜〜

紐の結び目ををセロテープで補強したところで全然ムダ!!

モーターの回転する軸に割り箸を挟んでみても割り箸が吹っ飛ぶだけ

見つけた課題

やってみないと分からないのが電子工作!次はこんな改善をしたい

  • モーターの回転速度を遅くするorギアをつけるなどして回転速度を遅くする?
  • 霧吹きのレバーに結びつけた紐を巻き取れるようにする
    • ただ回転するだけだと紐は巻き取られないってことも今日分かった笑

Puppeteerをtypescriptで使う

前回はseleniumを使ってみたので今回は別のブラウザ操作ライブラリPuppeteerを使ってみます!

Seleniumをtypescriptで使ってみる - buto > /dev/null

Puppeteerインストール

npm install puppeteer

ブラウザ操作コードを書きます!

今回もこちらのログイン画面を操作します

puppeteerはchroniumという軽量chromeが一緒にインストールされているので

特に指定しなければchromeが動いてくれます

const puppeteer = require('puppeteer');
const assert = require('assert');

/**
 * ログイン成功
 */
(async () => {
    const browser = await puppeteer.launch({ headless: false });
    const page = await browser.newPage();
    await page.goto('http://localhost:3000');
    // IDパスワードを入力
    const userId = await page.$('#loginId');
    await userId.type('xxxxx');
    const password = await page.$('#password');
    await password.type('xxxxxx');
    // ログイン
    const loginButton = await page.$('#loginButton');
    await loginButton.click();
    await page.waitForTimeout(3000);

    // 支払い一覧ページへ遷移していること
    assert.strictEqual(await page.url(), "http://localhost:3000/payment");
    await browser.close();
})();

seleniumと比較するとコード量が少ないです!

あらかじめ npm run serve で開発サーバにアプリをホスティングしておき

node loginTest.ts で上記テストコードを実行しましょう!

ブルーのchroniumアイコンが起動します

動きはseleniumと同じです

Seleniumをtypescriptで使ってみる

画面開発でコーディングと動作確認を交互にしていると 毎回フル桁を入力して、ボタン押下して、結果確認して…の流れが結構辛い。

ということで、javascriptコードでブラウザ操作を自動実行してくれるseleniumを始めよう!(今更感)

seleniumインストール

何事もまずはnpm installから!!

npm install selenium-webdriver

次にseleniumが使うブラウザドライバーをダウンロードします

www.selenium.dev

ダウンロードしたドライバーはプロジェクトのルートディレクトリに配置しておきます

TypeScriptでブラウザ操作コードを書く

シンプルなログイン画面にID、パスワードを入力してログインボタンを押下してみます

まずはtsファイルを作成してお作法コードを書きます インストールしたseleniumを使うよ!宣言とchromeの設定です

const webdriver = require('selenium-webdriver');
const { Builder, By, until } = webdriver;
const assert = require('assert');

const capabilities = webdriver.Capabilities.chrome();
capabilities.set('chromeOptions', {
    args: [
        '--headless',
        '--no-sandbox',
        '--disable-gpu',
        `--window-size=1980,1200`
    ]
});

次にテキストボックスへの入力、ボタン押下、結果のassertを行います

/**
 * ログイン成功
 */
(async () => {
    const driver = await new Builder().withCapabilities(capabilities).build();
    await driver.get("http://localhost:3000");
    // IDパスワードを入力
    let userId = await driver.findElement(By.id('loginId'));
    userId.sendKeys("xxxxx");
    let password = await driver.findElement(By.id('password'));
    password.sendKeys("xxxxx");
    // ログイン
    let loginButton = await driver.findElement(By.id('loginButton'));
    loginButton.click();

    await driver.wait(until.elementLocated(By.id('paymentList')), 10000);
    // 支払い一覧ページへ遷移していること
    assert.strictEqual(await driver.getCurrentUrl(), "http://localhost:3000/payment");
    driver.quit();
})();

あらかじめ npm run serve で開発サーバにアプリをホスティングしておき

node loginTest.ts で上記コードを実行しましょう

自動でchromeが起動してログインを再現してくれます!!

Flutter インストールからメニュー表示まで

久しぶりにモバイルアプリを作ってみたくなったのでflutter始めました

iOSAndroidを同じコードで開発できるので効率的です

Flutterインストール

まずはhomebrewでFlutterをインストールしてdoctorコマンドでFlutterの利用開始チェックをする

XcodeとAndroidStudioが入っていればチェックOKなはず)

brew install flutter
flutter doctor

Flutterプロジェクト作成

インストール完了したらFlutterプロジェクトを作成します

--org オプションでパッケージ名を指定できます(指定なしだと com.example となる)

flutter create --org package project_name

プロジェクトが作成できたら標準出力に従ってアプリを起動!

cd project_name
flutter run

プロジェクト作成直後の画面 カウントアップボタンのみがあるシンプルアプリ

f:id:butorisa:20220327165430p:plain

画面をカスタマイズ

↑画面コードは project_name/lib/main.dart に書かれているのでこちらを修正していく

まずはこんな感じで画面を修正しました

  • アプリタイトルを変更
  • AppBarの表示文字・カラーを変更
  • サイドメニュー(Drawer)を追加
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'framboise nail', // ブラウザタブ表示名
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const MyHomePage(title: 'my page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
        backgroundColor: Colors.pinkAccent.shade700,
      ),
      drawer: Drawer( // サイドメニュー
        child: ListView(
          children: <Widget>[
            DrawerHeader(
              child: const Text(
                'risa',
                style: TextStyle(
                  fontSize: 24,
                  color: Colors.white
                ),
              ),
              decoration: BoxDecoration(color: Colors.pinkAccent.shade700),
            ),
            const ListTile(
              title: Text('カタチから探す'),
            ),
            const ListTile(
              title: Text('スクエア'),
              trailing: Icon(Icons.square),
            ),
            const ListTile(
              title: Text('ラウンド'),
              trailing: Icon(Icons.circle),
            ),
            const ListTile(
              title: Text('オーバル'),
              trailing: Icon(Icons.egg),
            ),
            const ListTile(
              title: Text('ポイント'),
              trailing: Icon(Icons.water_drop),
            )
          ],
        ),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: const <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
          ],
        ),
      ),
    );
  }
}

このようにプロジェクト作成直後から画面をカスタマイズできました!

サイドメニューは利用頻度が高そうなのでサラッとコード書けるようにしておきたい

f:id:butorisa:20220327215000p:plain

f:id:butorisa:20220327215027p:plain

ちょっとしたカスタマイズですが、何時間も掛かってしまいました…

webpack フロントエンドでも環境変数を扱う

React+SpringBootアプリでローカル、ステージング、本番環境用に環境変数ファイルがあって、それぞれに接続するAPIアドレスなどが定義されているのはよくあるケース

SpringBootで環境変数を読み込むのは何度も行ってきましたが、Reactで環境変数にアクセスしたことなかった!

今回フロントエンドはwebpack+Reactだったのでwebpackを使って環境変数を読み込む

そもそもwebpackの話

web資材(js、css、画像などのファイル)をpacking(まとめる)するツール

この記事のようにindex.jsなどのエントリポイントから参照される各モジュール(画面コンポーネントなど)をひとまとめにできる!

まとめる設定をwebpack.config.jsファイルに記述する

goworkship.com

DefinePluginで環境変数を読み込む

あらかじめlocal.envなどの環境変数ファイルをサーバ内に環境変数として読み込まれるようにしておく

今回はdockerだったのでdocker-compose.ymlにビルド実行するコンテナのenv-filesにlocal.envを指定した

webpack.config.jsにDefinePluginを記述して環境変数をフロントエンドのグローバル変数に置き換える

local.envに定義したAPI_GATEWAYはprocess.env.でアクセスしグローバル変数API_HOSTに格納

const webpack = require('webpack');

module.exports = {
  mode: 'development',
  plugins: [
    new webpack.DefinePlugin({
      'API_HOST': JSON.stringify(process.env.API_GATEWAY || "")
    }),
  ]
};

実際にAPI接続するモジュールで declear const API_HOST: string; と記述すると環境変数の値が利用できる

webpack.js.org

環境変数を扱う方法にはdotenv、dotenv-webpackもあるがライブラリを追加したくない場合はこの方法が使える

AWSメッセージキュー SQS

昨日はSNSがモバイル通知、分析用Firehorseなど複数の宛先へデータを流すことができると学んだので

今日はSQSの特徴を学んでいく!!(SESはEメール通知専用っていうのは知ってるからスキップ)

Amazon SQS

f:id:butorisa:20220215202733p:plain

SNSのアイコンと比べると宛先は1つになる?中央の四角がキューを表している??

ya6mablog.com

貯まったメッセージは受信側(Consumer)がキューに問い合わせを行うと、キューからメッセージが配信されて、受信側はメッセージを取得することが出来ます。

SNSはメッセージ発行元(publisher)から一方通行でデータが流れたけど

SQSはpublisherがキューにメッセージを入れて、consumerがポーリングでキューからメッセージを取り出すようだ

SQSを試してみる

f:id:butorisa:20220215204800p:plain

マネジメントコンソールでSQS作成をしてみると、スタンダードとFIFOが選べる

配信順序を気にしなければスタンダードで良さそう(FIFOの方が割高)

f:id:butorisa:20220215205045p:plain

スタンダードキューを作成してみたのでメッセージの送受信をクリックしてみた

メッセージ本文を入力してメッセージ送信をクリック!

f:id:butorisa:20220215205146p:plain

f:id:butorisa:20220215205253p:plain

メッセージがキューに送られたので利用可能なメッセージが1となった

f:id:butorisa:20220215205451p:plain

自動でポーリングされないようだったのでメッセージをポーリングをクリックするとポーリングが開始された

f:id:butorisa:20220215205537p:plain

メッセージが届きました!

SNSと連携

SQSのメニューにSNSサブスクライブとあったので昨日作成したSNSトピックを紐付けてみた

SNSで「わああああああ〜!」とメッセージを発行してみると

f:id:butorisa:20220215210707p:plain

昨日作成したSMS通知がちゃんと届いたのと同時にSQSにもメッセージが渡ってきた!

f:id:butorisa:20220215210931p:plain

f:id:butorisa:20220215211256p:plain

SNSからSQSにメッセージを渡せる機能なのか!

EC2上のアプリからSNSで通知+SQSにデータを流してSQSから他のEC2アプリにデータを連携する時使えそう!

SQSはアプリ間連携に使えるメッセージキューということが分かった

AWSメッセージキュー SNS

AWS資格試験の模擬テストにメッセージキューが頻出するけど違いが分からない、覚えられないのでまとめておく。

AWS複数種類のメッセージキュー(MQ)が提供されているから1つずつ覚えていきます!

メッセージキューってなに??

メールやSMSメッセージ通知をする時にメッセージ作成アプリ→配信アプリに橋渡しをするキュー

メッセージキューって名前だけどメッセージ配信以外のアプリ間データ連携に利用されている

直接アプリ同士で連携してもいいけど、大量データだとアプリサーバに負荷が掛かっちゃうのでMQはよく使われるらしい

キューなので渡されたデータから処理されていく(先入先出)

Amazon SNSとは

f:id:butorisa:20220214232645p:plain

サービスアイコンは左側から入ったデータがフィルタされて複数に流れるような

Amazon Simple Notification Serviceって名前の通り通知(SMS・プッシュ通知)によく使われる

SNSスマホにメッセージ送信

f:id:butorisa:20220214235359p:plain

トピック(後述)のタイプをスタンダードにするとSMS・プッシュ通知などのメッセージ通知ができ

FIFOにするとSQSのみ選択できた(こちらはアプリ間データ連携に使う時に選択するっぽい)

トピックを作成しなくても左メニューSMSを選択して電話番号を登録

ワンタイムパスワードで認証後、テストSMSが送信できる

f:id:butorisa:20220214235047p:plain

今度はスタンダードタイプのトピックから送ってみる

トピックを選択してSMS送信をサブスクリプションに設定すると、こんなメッセージが届いた(一番下)

f:id:butorisa:20220215000402p:plain

トピックから送信するとメッセージの先頭に「トピック名>」って付くみたい

アプリからSMS通知するには

携帯電話に発行する - Amazon Simple Notification Service

公式ドキュメントにコードから電話番号を動的に渡してAmazon SNSからSMS通知できるみたい

あらかじめトピックを作っておかなくてもSMSが送れるように見える

Amazon SNS トピックを作成する - Amazon Simple Notification Service

トピックは配信先のグループなので「メッセージ配信+配信内容を分析するRedshiftにデータ登録」を実現したい時に

トピックのサブスクリプションにSMS通知、kinesisFirehorseを登録してあげればトピックでメッセージ発行をすると

SMS通知が届き、一方ではFirehorse経由でRedshiftにデータが登録されました〜となる