LoginSignup
7
3

More than 5 years have passed since last update.

組込みに近いものをTDDで開発してみる〜問題提起編〜

Last updated at Posted at 2018-02-12

はじめに

下記の続きです。

組込みに近いものをTDDで開発してみる〜準備編〜

リアルタイムで内容を考えているため、ご意見・ご要望があれば、ぜひご連絡下さい。
コードはgithubで公開中

前回からの変更点と決定事項

ビルドツール

ビルドツールをAutotoolsからCMakeに変更しました。
CMake version 2.8以降が必須環境となります。

プロダクトコード

プロダクトコードはC言語にします。
あまり複雑なコードを書く予定がないため、生C言語で続ける予定です。コードが複雑になれば、glibの利用を検討します。

作るもの

キーボードの「A」ボタンを押すと、caps lockのLEDがON/OFFする。

とりあえずここから始めて、その場のノリと思いつきとフィードバックで仕様拡張していきます。
今回掲載するコードを除き、TDDで作っていきます。
最終的には、ラズパイなどの別ハードで、キーボード以外のハードウェアを使用し、ちゃんと移植できたよね、めでたしめでたし、としたいですね!
現時点で本当に具体的なハードウェアを選定していないです。ほら、その方が実際の開発っぽいでしょ?

問題提起

さて、作るものに戻って、キーボードの「A」ボタンを押すと、caps lockのLEDがON/OFFすると言われてプログラムを作るとき、どのような実装になるでしょうか?

愚直に作ると、概ね次のような疑似コードになるのではないでしょうか。

static void mainloop() {
  do {
    キーイベントを取得
    if (Aボタンが押された) {
      write(LEDをトグル);
    }
  } while (何らかの条件を満たすまで);
}

int main() {
  mainloop();
  return 0;
}

上の疑似コードを実際に作ると次のようなコードになりました。
libevdevの動作は、前回のライブラリ理解のためのテストで確認しましたね。

#define KEYBOARD_DEVICE "/dev/input/event2"
#define LED_DEVICE      "/sys/class/leds/input2::capslock/brightness"

#define KEY_RELEASED 0
#define KEY_PRESSED 1

static void mainloop() {
  struct libevdev *dev = NULL;
  int key_fd = open(KEYBOARD_DEVICE, O_RDONLY|O_NONBLOCK);
  int rc = libevdev_new_from_fd(key_fd, &dev);

  if (rc < 0) {
    fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc));
    exit(1);
  }

  int led_fd = open(LED_DEVICE, O_WRONLY|O_NONBLOCK);
  if (led_fd < 0) {
    fprintf(stderr, "Failed to init LED device.\n");
    exit(1);
  }

  bool led_on = false;
  do {
    struct input_event ev;
    rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev);
    if (rc == 0) {
      if (ev.type == EV_KEY && ev.code == KEY_A && ev.value == KEY_PRESSED) {
        led_on = !led_on;
        char buf[2];
        snprintf(buf, 2, "%d", led_on ? 1 : 0);
        write(led_fd, buf, 2);
      }
    }
  } while (rc == 1 || rc == 0 || rc == -EAGAIN);

  libevdev_free(dev);
  close(key_fd);
  close(led_fd);
}

int main() {
  mainloop();
  return 0;
}

Linux環境がある方は実際に動かすことができます。
前回記事を参考に、キーボードデバイスとLEDデバイスを確認し、2つのマクロ(KEYBOARD_DEVICE, LED_DEVICE)のパスを修正して下さい。
ビルドし、build/bad_example/bad_exampleを実行すると、「A」キーを押すたびにLEDがONされたりOFFされたります(環境によりroot権限が必要です)。

さて、このコードは一見、問題がないように見えますが、多くの問題を内包しています。

  • 全てのコードが意図通りに動作していることが明確でない。(エラー処理、終了時の動作確認ができない)
  • ロジックとハードウェアが密結合しており、ハードウェア変更、追加がロジックに影響する(オープン・クローズドの原則違反)。
  • mainloop関数を変更する理由が複数存在する(単一責務の原則違反)。例えば、キーイベント追加、LEDデバイス追加、双方ともにmainloopへの修正が発生する。

これらの理由のため、機能拡張を繰り返していくと、機能追加が次第に困難になり、デグレを起こさずに機能追加することが難しくなります。

次回からは、今回のコードをTDDで開発するとどうなるか、を実際にやってみて、順に機能を追加していきます。

次:
組込みに近いものをTDDで開発してみる〜file open編〜

7
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
3