アジャイル開発の学習について学んだこと
1 はじめに
アジャイル開発について習得するためUdemyのオンライン講座を受講しました。
学習したことを自分のものにするため、記事化することとしました。
2 学習の目的
ソフトウェア開発ではスタンダードになりつつあるアジャイル開発について学び、仕事に活かせるか結論を出す。
■知りたいこと
- 従来のプロセス(ウォータフォールモデル)と比較したときのメリット、デメリット
- アジャイル開発の特性
- アジャイル開発で注意すべき点
3 従来の開発(ウォータフォールモデル)とアジャイル開発の比較
3.1 従来の開発(ウォータフォールモデル)
概要
要求の分析、設計、実装、テストと段階を経てプロセスを進めていく。
部品ごとに作って、完成品ができるのは最後の段階。
顧客は数ヶ月、完成品ができるまで待つ必要がある。特徴
- 開発の進め方
要求の分析の段階で数ヶ月かかり、さらにそのあとデザインの段階で数ヶ月かかる。
その次に、実装の工程、テストの工程と一つずつ完了させてから、次の工程へ進む。最初に計画した目標を達成することを強く求められる。
目標を変更すると遅延が発生するため、目標や見通しは厳格に守られることが求められる。
最初から完成品を目指す。
-役割分担
基本的には役割は固定。プランニングを立てる役割、実装をする役割、テストをする役割等あり、その役割は固定のことが多い。顧客との関係
一般的に顧客は発注の段階で要求仕様を確定する。一度確定した仕様に関して、途中で変更を加えることは難しい。
完成品ができるまでは、仕様の確認など以外は基本的に関わらない。向いているプロジェクト
数年にわたるプロダクトデリバリーが必要となる大規模なシステムは従来の開発が向いている。
- 開発の進め方
3.2 アジャイル開発
概要
最初の段階で、要求の一部を満たす製品を顧客にリリースする。そして、顧客とコミュニケーションをとって、徐々に改善していく。
スプリント(1サイクルの開発期間)は通常2週間。3~4週間の場合もある。
顧客の最低限の要求を満たすソリューションを作る。(MVP)特徴
開発の進め方
アジャイル開発は最初の段階からテストを行う。そしてスプリントごとに分析、プランニングを都度行う。
顧客へのデリバリーを素早く行い、継続的に行う。必要かつ妥当であればスコープの変更にも積極的に応じる。
プランニングも途中で変更可能。
要求も変更可能。変更による影響も顧客と調整し進めていく(機能変更によって、〇〇の機能は次のスプリントでは入らないなど調整する)。
ドキュメントよりも成果物が優先される。ただし、アジャイルでもドキュメントは用いられる。しかし、ドキュメントかすることを目的とせず、文章を短く、シンプルにすることで成果物を作ることに集中する。役割分担
自由な役割分担。テストには誰でも参加可能で、テスターやテストリーダーはいない(テストしないメンバーもいる)。
アジャイルでは各段階でプランニング、テストを行うので全員が開発に加わる。
→重要としているのはマルチロールで協力しながら進め方 成功を確実にするためにチームワークを重視する。向いているプロジェクト 顧客や事業主が進捗の把握やプロジェクトへの関与を求める場合に有効。
4 アジャイルの原則
アジャイルの原則について、重要なものについてまとめる。
- 動くソフトを頻繁にリリースする(1ヶ月未満) リリースに数ヶ月かかるものはアジャイルではない。
- シンプルに 基本的でシンプルな解決策によって最も重要な要求を満たす。 一度に全てを終わられる必要はない。向上させ、改善しリリースすることを繰り返す。
アジャイルはすべての課題を解決するわけではない。 アジャイルの以下の良さを活かし、より良いプロダクトを作ることを目指す。 - シンプルさ - 継続的に実施すること - チームのメンバーや事業主、ステークホルダーとの協調が可能
5 アジャイルで使用される用語
ユーザーストーリ
顧客に価値を提供するもの、決まった書き方がある。
アジャイル開発では最初にユーザーストーリを作成する。
ユーザーストーリーは途中で変更を加えても良い。
例)
顧客として私はSNSのログイン認証情報を使いたい、アカウントを作成せずにECサイトを利用するためだ
→このストーリーは顧客に価値を提供している。アカウント作成の手間を省くことで顧客の時間を節約できている。プロジェクトバックログ
ユーザーストーリーをまとめたもの。ストーリーポイント
ユーザーストーリの複雑さの程度を示すもの。
複雑度の見積もりに時間をかけないことが重要。自分の直感を信じる。
例) 1(低),3(中),5(高)スプリント
ユーザーストーリに取り組む期間。2週間程度。長くても4週間。ベロシティ
1スプリントで完了するストーリポイントの数。ベロシティは見積もりではなく、実測値で測る必要がある。
繰り返すうちにベロシティの平均を見出すことができる。MVP(Minimum viable product)
実用最小限の機能。顧客の要望を満たすための最小限の機能。リリース
ユーザーストーリーを文字通り本番環境に移すこと。実動すること。
6 アジャイルの開発チーム
開発チームで各担当が担う役割について説明する。
プロダクトオーナー
顧客または顧客を代表する担当者
プロジェクトの優先順位を決めたり、デモの際に確認したい内容をチームに提示したりする。スクラムマスター
プロジェクトリーダー(アジャイルに詳しい人なら誰でも良い、またメンバー全員が納得しており、本人にやる気があることが重要)
開発者、テスターなど誰でも担当することができる。デリバリーチーム
開発者やテスター、ビジネスアナリストなどで構成される。
デリバリーすることが役割。
7 アジャイルの4つの慣習
アジャイル開発では4つの慣習がある。
スプリントプランニング
この会議では、スプリントで取り組むユーザーストーリを決める。
チーム全体で作業範囲や優先事項、次のスプリント目標を確認する。
全員でバックログを確認し、次のスプリントで取り組むユーザーストーリを選ぶ。
参加者:デリバリーチームと進行役のスクラムマスターデイリースタンドアップ
立ちながら行う会議。要点を絞り、短時間で済ます会議(5~15分程度)。
前日の作業状況と、今日の作業予定を発表する。障害や問題があればサポートを依頼する(スクラムマスターが進行役をし、調整する)。スプリントレビュー
前回のスプリントの成果物をレビューする。
このミーティングは各スプリントの終了直後に行い、デリバリーしたユーザーストーリーと未着手のストーリーがあれば確認をする。
誰かを責めたりはせず、作業にチームの意見を反映するために行う。
参加者;デリバリーチーム
※スプリントレビューをプロダクトオーナーと行うものをデモと呼ぶこともある。スプリントレビューとは別に設定する。振り返り スプリントレビュー終了直後に、教訓や改善点について確認する。
デリバリーや各作業をより良くするために次のスプリントへ反映させる。
以下の3つの質問をする。- うまくいったことは
- 失敗したことは
- 次のスプリントで工夫すべきことは
8 アジャイルに対する誤解
ドキュメント化しない
ユーザーストーリーなどドキュメント化するものはある。アジャイルで大切にしているのは、ドキュメントは簡潔にし、プロダクトに拘るということである。計画を立てない
アジャイルには計画が欠かせない。スプリントプランニングで簡潔に要点を絞った会議を実施し、計画を立てる。アジャイルはすべて解決できる
アジャイルはフレームワークに過ぎない。全てを解決することはできない。仲間と協力して問題を解決することが重要。アジャイルはITプロジェクトにしか使えない
結婚式の計画などでも使われている。色々な活用法があるため詳細は検索する。
9 アジャイル開発の始め方
アジャイルを始める前にプランニングをする。
Google Edge TPUをVirtual Box上のUbuntuで動かす
1 はじめに
Google Edge TPUのUSB Acceleratorを購入しました。Virtual Box上のUbuntuで動かすのに一苦労ありましたので、ここに書き留めておきます。
※こちらの記事はQiitaで投稿した記事を移行したものとなります
2 環境
Host OS: Windows 10
Guest OS: Ubuntu 18.04 LTS
仮想化マシン:Virtual Box ver.6.0.6
3 手順
- Virtual Boxのインストール
- VirtualBoxにOracle VM VirtualBox Extension Packの追加
ダウンロード先:https://www.virtualbox.org/wiki/Downloads - Virtual BoxにUbuntuのインストール
参考:https://qiita.com/ykawakami/items/4bae371932110b2e25e3 - Virtual BoxのUSBフィルタを編集
4.1. 設定をクリック

4.2. サイドバーのUSBを選択し「USBコントローラーを有効化」にチェックを入れる
4.3. USB Acceleratorを挿入しているUSBポートに合わせ、USBコントローラーのバージョンを指定する
4.4. USBフィルタの追加をクリックし、Global Unichip Corp.[0100]を追加する

4.5. 4.4と同様の操作をし、Global Unichip Corp.[0100]を再度追加する
4.6. 2つのうち、1つを編集し以下のように設定する
ベンダーID:18d1
プロダクトID:9302

- Ubuntuを起動
- 以下のサイトをもとにUSB Acceleratorの動作確認をする
https://coral.withgoogle.com/docs/accelerator/get-started/
4 結果
以下の通り、Virtual Box上でGoogle Edge TPUのUSB Acceleratorを動作させることができた。

5 経緯
Virtual Box上のUbuntuで動作させるに当たり、2つの問題にぶつかった。
5.1 "No Edge TPU Device detected!"エラー
これは、Virtual Box上でUSBデバイスを認識していないのが原因である。よって、USBフィルタにUSB Acceleratorを追加することで解決できた。
5.2 "RuntimeError: Failed to allocate tensors."エラー
この原因はさっぱりわからなかったが、githubのissueを見ているとサンプルプラグラム動作後にUSB AcceleratorのベンダーID、プロダクトIDが変わっているとの記載があった。また、サンプルプログラムを実行するとUSB Acceleratorがアンマウントされてしまうという現象が発生していた。このことから、試しにUSBフィルタに変更後のUSB AcceleratorのベンダーID、プロダクトIDを追加すると動作することができた。
6 補足事項
- 3で作成した仮想環境をMacBookAirのVirtual Boxにインポートしたが、USB Acceleratorを動作させることができた
- debianでも同様のことを実施したが、5.2のエラーが表示され動作させることができなかった
ローカルネットワークからjson-serverにアクセスする
1 はじめに
json-serverを以下のようにデフォルトで立ち上げるとローカルホストのみ通信できる状態になる。
json-server db.json
ローカルネットーワーク内にjson-serverを立ち上げ、立ち上げているPCとは別の端末からアクセスしたい場合は、以下2点を設定する必要がある。
・ホスト名の指定
・ファイアーウォールの通過設定
※こちらの記事はQiitaで投稿した記事を移行したものとなります
2 ホスト名の指定
ホスト名を指定する場合は、以下のオプションをつける。このオプションを付けないとローカルホストで立ち上がってしまい、外部からアクセスできない。
--host <IPアドレス or ドメイン名>
ドメイン名の場合は、合わせてDNSサーバーにドメイン名を登録しておく。これにより、ローカルネットワーク内の端末からサーバーを見つけられるようになる。
3 ファイアーウォールの通過設定
まず、デフォルトではポート番号が3000となっているが、任意のポートに指定したい場合は以下のオプションをつける。
--p <port番号>
そのポートで通信できるようにファイアーウォールの受信規則に上記ポートを受信できるようにする。ファイヤーウォールに上記ポートを通過設定しておかないと、サーバーが立ち上がっていても、外部からの通信をファイヤーウォールが遮断してしまうため外部からアクセスできない。
4 Node.jsで動かす場合
Node.jsで動かす場合は、以下のようにポートとIPアドレス or ドメイン名を指定する。
server.listen(<port>, '<IPアドレス or ドメイン名>');
const jsonServer = require('json-server');
const server = jsonServer.create();
const router = jsonServer.router('db.json');
const middlewares = jsonServer.defaults();
server.use(middlewares);
server.use(router);
server.listen(80, '192.168.11.20', () => {
console.log('run');
});
4 結果
以下のような環境で実行した結果を示す。結果の通り、ローカルネットーワーク内のjson-serverに対し、json-serverを立ち上げているPCとは別の端末からアクセスできた。
MacPC(clinet)---router--WindowsPC(json-server)
・WindowsPCがjson-serverを立ち上げた結果

・MacPC(clinet)がサーバへリクエストした結果

openFrameworksのアドオンofxQuadWarpを使ってプロジェクションマッピング
1 はじめに
openFrameworksで簡単なプロジェクションマッピングする方法を共有します。
※こちらの記事はQiitaで投稿した記事を移行したものとなります
2 環境
Visual Studio Community 2017
openFrameworks 0.10.1
3 Visual Studio 2017のセットアップ
以下のサイトを参考に、Visual Studio 2017のセットアップを行う。ただし、「Visual Studio向けopenFrameworksプラグイン」は「前提条件が不足しているため、次の製品に拡張機能をインストールできません」とエラーが出てインストールできなかったため省略した。
https://openframeworks.cc/ja/setup/vs/
4 openFrameworksのダウンロード
openFrameworksのサイトからopenFrameworksをダウンロードする。また、Visual Studio 2017で開発する場合はopenFrameworks for visual studio (2017)をダウンロードする。
https://openframeworks.cc/ja/download/

ダウンロードしたものを解凍し、任意のフォルダに置く
5 プロジェクトの作成
ダウンロードしたopenFrameworksのフォルダにある、projectGenerator-vs\projectGenerator.exeを実行する
ウィンドウが表示されるため、自分の環境に合わせて設定する
- Projection name:任意のプロジェクト名を設定する
- Projection path:プロジェクトを作成するパスを設定する。デフォルトで入っているため、特に問題なければそのままにする
Addons:「ofxPoco、ofxOpenCv、ofxXmlSetting」を設定する
Generateをクリックする

GithubでofxQuadWarpをダウンロードする
https://github.com/julapy/ofxQuadWarp/tree/master先程作成したプロジェクトのsrc配下にダウンロードした以下のソースコードをコピーする
- main.cpp
- ofApp.cpp
- ofApp.h
- ofxQuadWarp.cpp
ofxQuadWarp.h
プロジェクトのbinフォルダ配下に画像を置く
6 ソースコードの修正
openFrameworks 0.10.1の場合、ofxQuadWarpはビルドエラーとなるため以下の修正を行う。
※Github上で0.10.1で動作するように修正するpull requestがあったので、その修正が適用されれば以下の作業は不要です
- 以下のインクルードを追加する
ofxQuadWarp.cpp
#include "ofxXmlPoco.h" #include "calib3d.hpp"
- ofXmlをofxXmlPocoに修正する
ofxQuadWarp.cpp
// ofXml xml;
ofxXmlPoco xml;
7 動作確認結果
四隅の点をマウスでドラッグ&ドロップで動かし、プロジェクションしたい物体の四隅に合わせることができます。

実際にプロジェクターで写した結果は以下となります。画像では荒いですが、実際は綺麗に写っています。

Wiresharkを使って一定時間でパケットを自動保存するバッチファイル
1 はじめに
解析等でネットワークパケットを保存したい場合、Windows OSではWiresharkを使ってパケットキャプチャするのが一般的だと思います。また、毎日、長時間パケットキャプチャする場合は、PCが突然落ちてしまうことも考え、一定時間で自動保存してほしいです。
そのバッチファイルを作成したので共有します。
※WiresharkのGUIからでもできます
※こちらの記事はQiitaで投稿した記事を移行したものとなります
2 やりたいこと
- Wiresharkを使いたい
- 一定時間でパケットを保存したい
- 日毎にパケットをフォルダに分けたい
※毎日バッチを実行、終了する前提 - バッチファイルで実行したい
3 動作確認環境
Wiondows 10
Wireshark Version 2.0.5
4 バッチファイル
@echo off @Rem 保存するフォルダを指定する Set BASE_DIR=C:\packets\ @Rem BASE_DIRで指定したフォルダの下で日付のフォルダを作成する Set TODAY=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2% Set FILE_DIR=%BASE_DIR%%TODAY% Mkdir %FILE_DIR% @Rem ネットワークインターフェイスを表示する @echo ネットワークインターフェイス一覧 "C:\Program Files\Wireshark\tshark.exe" -D @Rem ネットワークインターフェイスを入力する set /p interface_id="パケットをキャプチャしたいネットワークインターフェイスを入力してください:" @Rem パケットの保存間隔を入力する set /p time_duration="保存間隔(分):" set /a "time_duration *= 60" @echo Wiresharkでパケットを保存します。終了させたい場合はCtrl+Cを押してください。 "C:\Program Files\Wireshark\dumpcap.exe" -i %interface_id% -b duration:%time_duration% -w %FILE_DIR%\packet.pcapng
5 解説
保存フォルダを指定します。好きなフォルダを指定してください。
Set BASE_DIR=C:\packets\
指定した保存フォルダに日付名でフォルダを作成します。
Set TODAY=%DATE:~0,4%%DATE:~5,2%%DATE:~8,2% Set FILE_DIR=%BASE_DIR%%TODAY% Mkdir %FILE_DIR%
ネットワークインターフェイス番号を表示します。Wireshrakのインストールされているフォルダにあるtshark.exeを指定します。-Dのオプションでインターフェイス番号を表示することができます。
"C:\Program Files\Wireshark\tshark.exe" -D
パケットキャプチャしたいネットワークインターフェイスの番号、パケットの保存間隔(分)を入力してもらいます。後述しますが、dumpcap.exeは秒間隔での指定となるため、分から秒に換算しています。
@Rem ネットワークインターフェイスを入力する set /p interface_id="パケットをキャプチャしたいネットワークインターフェイス番号を入力してください:" @Rem パケットの保存間隔を入力する set /p time_duration="保存間隔(分):" set /a "time_duration *= 60"
Wireshrakのインストールされているフォルダにあるdumpcap.exeを指定します。
"C:\Program Files\Wireshark\dumpcap.exe" -i %interface_id% -b duration:%time_duration% -w %FILE_DIR%\packet.pcapng
各オプションの説明は以下のとおりです。
| オプション | 説明 |
|---|---|
| -i | ネットワークインターフェイス番号を指定する |
| -b duration | 保存間隔(秒) |
| -w | キャプチャ結果をファイルに保存する |
6 実行結果
コマンドプロンプトの出力は以下の通りになります。

パケットは指定したフォルダ以下に自動で保存されます。パケットのファイル名には自動で連番が振られます。

7 参考サイト
C++でフォルダ以下にあるファイル一覧を取得し、同じグループの画像(ファイル名で判別)を配列にまとめる
1 はじめに
学生時代はOpenCVを使って画像処理系の研究をしており、そのときに作成したC++の関数を共有します。
また、C++でフォルダ以下にあるファイル一覧を取得する方法は以下のQiitaに書いていますので、そちらを参照ください。
technotes.hatenablog.jp
環境:Visual Studio 2017
※こちらの記事はQiitaで投稿した記事を移行したものとなります
2 やりたいこと
フォルダ以下にあるファイル一覧を取得し、同じグループの画像(ファイル名で判別)を配列にまとめたい。

3 ソースコード
#define SAME_GROUP 0 /* 同じグループ(memcmpで文字列一致した) */ /** * @brief フォルダ以下の同じグループの画像をまとめる関数 * 画像の命名規則: xx.yy.拡張子(xxを同じグループの場合は同一にする) * @param[in] folderPath フォルダパス * @param[out] fileGroups 同じグループの画像をまとめた配列 * @return true:成功, false:失敗 * @detail */ bool getImageGroups(std::string folderPath, std::vector< std::vector<std::string> >& imageGroups) { bool result = false; /* 処理結果 */ std::vector<std::string> fileNames; /* フォルダ内にあるファイル一覧 */ /* フォルダパス以下にあるファイル一覧を取得する */ result = getFileNames(folderPath, fileNames); /* ファイル一覧の取得に失敗した場合はfalseを返す */ if (result == false) { return false; } /* ファイル一覧から同じグループのファイルを動的配列にまとめる * 画像の命名規則: xx.yy.拡張子とし、xxを判別用パスとする * 判別用パスが同一なら同じグループの画像である */ const std::string distinction = "."; /* 判別用文字 */ std::vector<std::string>::iterator it = fileNames.begin(); while (it != fileNames.end()){ std::vector<std::string> sameImageGroup; /* 同じ画像のグループ */ std::string searchStr = *it; /* 判別する画像のパス */ size_t searchStrPos = 0; /* 判別用文字の位置 */ /*配列の先頭の画像をグループに追加し、追加した画像はファイル一覧から削除する */ sameImageGroup.push_back(*it); it = fileNames.erase(it); /* 判別用文字の位置を取得する */ searchStrPos = searchStr.find(distinction); /* ファイル一覧から同じグループのものを取得する */ std::vector<std::string>::iterator tempIt = it; while (tempIt != fileNames.end()){ int result = std::memcmp(searchStr.c_str(), tempIt->c_str(), searchStrPos); if (result == SAME_GROUP){ /* 同じグループの画像を配列に追加し、ファイル一覧から削除する */ sameImageGroup.push_back(*tempIt); tempIt = fileNames.erase(tempIt); /* すでに削除しているイテレータを参照しているため更新する */ it = tempIt; } else{ /* 同じグループでない場合はイテレータを進める */ tempIt++; } } /* グループを追加する */ imageGroups.push_back(sameImageGroup); } return true; } /** * @brief フォルダ以下のファイル一覧を取得する関数 * @param[in] folderPath フォルダパス * @param[out] fileNames ファイル名一覧 * return true:成功, false:失敗 */ bool getFileNames(std::string folderPath, std::vector<std::string> &fileNames) { using namespace std::filesystem; directory_iterator iter(folderPath), end; std::error_code err; for (; iter != end && !err; iter.increment(err)){ const directory_entry entry = *iter; fileNames.push_back( entry.path().string() ); printf("%s\n", fileNames.back().c_str()); } /* エラー処理 */ if (err){ std::cout << err.value() << std::endl; std::cout << err.message() << std::endl; return false; } return true; }
4 出力結果
同じグループの画像のパスを一つの配列にまとめています。そして、そのグループを配列に格納した2次元配列で出力します。

C++でフォルダ以下のファイル一覧を取得する
1 はじめに
大量の画像をOpenCVで画像処理するときに、フォルダ以下にあるファイル一覧を取得したかったので、その関数を作成しました。
※こちらの記事はQiitaで投稿した記事を移行したものとなります
2 WinAPIを使う
参考サイト[1]を参照して作成しました。C++17以降が使用できる環境であれば、後述しているfilesystemを使う方法がおすすめです。
2.1 ソースコード
/** * @brief フォルダ以下のファイル一覧を取得する関数 * @param[in] folderPath フォルダパス * @param[out] file_names ファイル名一覧 * return true:成功, false:失敗 **/ bool getFileNames(std::string folderPath, std::vector<std::string> &file_names) { HANDLE hFind; WIN32_FIND_DATA win32fd; std::string search_name = folderPath + "\\*"; hFind = FindFirstFile(search_name.c_str(), &win32fd); if (hFind == INVALID_HANDLE_VALUE) { throw std::runtime_error("file not found"); return false; } /* 指定のディレクトリ以下のファイル名をファイルがなくなるまで取得する */ do { if (win32fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { /* ディレクトリの場合は何もしない */ printf("directory\n"); } else { /* ファイルが見つかったらVector配列に保存する */ file_names.push_back(win32fd.cFileName); printf("%s\n", file_names.back().c_str()); } } while (FindNextFile(hFind, &win32fd)); FindClose(hFind); return true; }
2.2 出力結果
関数の実行結果は下図の通りです。

3 filesystemを使う
C++17で標準入りしたfilesystemを使う方法を書きました。こちらの方法では絶対パスが得られます。 Visual Studio 2017でC++17を使う方法は参考サイト[2]を参照してください。
3.1 ソースコード
/** * @brief フォルダ以下のファイル一覧を取得する関数 * @param[in] folderPath フォルダパス * @param[out] file_names ファイル名一覧 * return true:成功, false:失敗 */ bool getFileNames(std::string folderPath, std::vector<std::string> &file_names) { using namespace std::filesystem; directory_iterator iter(folderPath), end; std::error_code err; for (; iter != end && !err; iter.increment(err)) { const directory_entry entry = *iter; file_names.push_back( entry.path().string() ); printf("%s\n", file_names.back().c_str()); } /* エラー処理 */ if (err) { std::cout << err.value() << std::endl; std::cout << err.message() << std::endl; return false; } return true; }
3.2 出力結果
vector配列にフォルダ以下にあるファイルの絶対パスが格納されます。サブフォルダは格納されません。

4 参考サイト
[1] http://d.hatena.ne.jp/s-kita/20100129/1264776052
[2] https://codezine.jp/article/detail/10890