C言語関係掲示板

過去ログ

No.284.目覚まし機能のプログラム

[戻る] [ホームページ]

No.1693

目覚まし機能
投稿者---あさみ(2002/06/08 23:01:21)


C言語で目覚まし機能のプログラムを作ろうとしてます。
概要としては7件の目覚ましが設定でき、曜日指定か毎日起動するか
設定できる目覚ましです。そこで設定値を以下の構造体に持つことを考えてみました。
struct ALARMINF
{
uchar HOUR;    //設定時刻(時)
uchar MIN;    //設定時刻(分)
uchar ALARM;   //アラームのON,OFF
uchar REPEAT; //曜日指定か毎日
uchar WEEK[7];  //曜日
};

struct ALARM_T
{
struct ALARMINF_T ALARMINF[7];
};

struct ALARM_T ALARM;

毎日指定時は0,曜日指定時は1がはいります。
曜日指定で月曜日指定時はWEEK[0]に0,以下月曜日の場合は
WEEK[1]に1がはいります。曜日は複数指定できます。
もっといい考えがある方いらしゃいますか?
アドバイスお願いします。


No.1696

Re:目覚まし機能
投稿者---B.Smith(2002/06/09 15:37:01)


こんにちは。
struct ALARMINF
{
    unsigned char HOUR;     /* 設定時刻(時) */
    unsigned char MIN;      /* 設定時刻(分) */
    unsigned char ALARM;    /* アラームのON,OFF */
    unsigned char REPEAT;   /* 曜日指定か毎日 */
    unsigned char WEEK[7];  /* 曜日 */
};

struct ALARM_T
{
    struct ALARMINF_T ALARMINF[7];  /* 7件分 */
};

struct ALARM_T ALARM;

これはアプリケーションの仕様が絡んでくることですが、構造体だけ見れば特に問題無いと思います。

データの持ち方に関しては、ビットに曜日を対応させると、もう少しだけサイズを小さくできます。

>曜日指定で月曜日指定時はWEEK[0]に0,以下月曜日の場合は
>WEEK[1]に1がはいります。曜日は複数指定できます。

WEEK[0]〜WEEK[6]がそれぞれ日曜〜土曜に対応していて、1がセットされている曜日に目覚ましが機能する、と解釈します。

#define     WEEK_SUN        0x1    /* 日曜日 */
#define     WEEK_MON        0x2    /* 月曜日 */
#define     WEEK_TUE        0x4    /* 火曜日 */
#define     WEEK_WED        0x8    /* 水曜日 */
#define     WEEK_THU        0x10   /* 木曜日 */
#define     WEEK_FRI        0x20   /* 金曜日 */
#define     WEEK_SAT        0x40   /* 土曜日 */

#define     WEEK_ENABLE     0x80   /* 曜日設定有効 */

定数を上記のように定めておき、構造体の方は
struct ALARMINF
{
    unsigned char Hour;     /* 設定時刻(時) */
    unsigned char Min;      /* 設定時刻(分) */
    unsigned char Alarm;    /* アラームのON,OFF */
    unsigned char Week;     /* 毎日/曜日指定 */
};

このようにREPEATとWEEKを融合してしまいます。メンバ変数Weekの値が持つ意味を、
bit0 bit1 bit2 bit3 bit4 bit5 bit6       bit7
 日   月   火   水   木   金   土  (曜日設定有効)

と定めます。

曜日指定しない場合は、最上位ビットにゼロをセットします。
    struct ALARMINF     alarm;

    alarm.Week = 0;       /* 設定を残す必要が無い場合 */

    alarm.Week &= 0x7f;   /* 設定されている曜日を残しておく場合 */

曜日を指定する場合は、最上位ビットに1を立て、メンバ変数Weekと、先ほど定義した定数のORを取ります。設定から外したい場合はEXORです。
    alarm.Week = WEEK_ENABLE; /* 曜日指定を有効にする */

    alarm.Week |= WEEK_MON;                       /* 月曜日のみ */

    alarm.Week |= WEEK_SUN | WEEK_WED | WEEK_SAT; /* 日、水、土曜日 */

特定の曜日であるかを判断する場合は、メンバ変数Weekと曜日に対応する値のANDを取ります。
    /* 曜日設定が有効であるか? */
    if (alarm.Week & WEEK_ENABLE){

        if (alarm.Week & WEEK_SUN){           /* 日曜日に設定されているか? */
            (設定が日曜日の場合の処理)

        }else if (alarm.Week & WEEK_MON){     /* 月曜日に設定されているか? */
            (設定が月曜日の場合の処理)
        }

                ・
                ・
                ・
    }


その他の改良点は特に見受けられません。

この構造体は設計者である貴方が、機能に必要と思ったものを盛り込んだものですから、もっと自信を持っても良いと思いますよ?
(貴方本人かもしれませんが)アプリケーションの実際の使用者や設計者のように、仕様を良く知っている人が「これでいい」というのであれば、外野がなんと言おうとそれはそれで正しいものです。後は完成目指して作るのみです。

設計時に大切なのは、コーディング中に仕様を変更しなければならない事態に陥らないようにする事です。特に留意するべき点ですね。
作成途中に仕様を変更する事は、料理を作っている途中で突然別のメニューに変更するようなものです。例えばひじきの煮物を作っている最中にカレーに変更するような…
完成後の正しい結果が予測困難になるだけでなく、あらゆる面で問題を発生する要因になります。



No.1698

Re:目覚まし機能
投稿者---あさみ(2002/06/09 18:12:25)


アドバイスありがとうございます。
B.Smithさんの考えに感心しました。
そこでもうひとつ質問してもよろしいでしょうか?
実際に設定時刻になった場合、現在の曜日と設定した曜日が一致するか
判定する場合どのようにすればいいのでしょうか?
年月日から曜日を求める関数の戻り値(0〜6)
とalarm.weekの設定値を比較する方法はありますでしょうか?



No.1706

Re:目覚まし機能
投稿者---B.Smith(2002/06/09 20:37:04)


こんばんは。

>実際に設定時刻になった場合、現在の曜日と設定した曜日が一致するか
>判定する場合どのようにすればいいのでしょうか?
>年月日から曜日を求める関数の戻り値(0〜6)
>とalarm.weekの設定値を比較する方法はありますでしょうか?

ifを使う方法、switchを使う方法等いろいろ考えられますが、ここでは2つほどご紹介します。
数値の範囲から、恐らくstruct tmを使って返される値だと思いますので、ここではstruct tmを使うことを前提としています。

比較対照の数値を作る
曜日は0〜6の数値がそれぞれ日、月、火…と順番に対応しています。先ほどの定義では1ビット目、2ビット目、3ビット目…がそれぞれ日、月、火…と定めましたので、1を曜日の数値でシフトすれば良いことになります。この操作で、構造体のメンバWeekとそのまま比較できる数値になります。
#define COMPWEEK(WEEK,TODAY)    ((WEEK) == (1 << (TODAY)))

このマクロで比較する場合(一致の確認をする場合)、
    struct tm    Today;
           ・
           ・
           ・
    /* 先ほど、最上位ビットを「曜日設定有効」と定めましたので、*/
    /* 1〜7ビットだけを0x7fとのANDで抽出して比較しています     */
    if (COMPWEEK(alarm.Week & 0x7f,Today.tm_wday)){
        (現在の曜日と指定の曜日が一致)
    }

この方法は、多くのコードを記述する必要がないので比較的簡単です。

配列を使用する
,任魯咼奪髪藥察淵轡侫函砲砲茲蝓比較対照の数値を求めましたが、比較の度に演算を行わなければならず、コードは簡単であっても非効率的です。曜日は0〜6と決まっていますので、0〜6の配列に、あらかじめシフト演算の結果を代入しておけば、tm_wdayの値をそのままインデックスとして配列を参照するだけで、比較対照の数値を得ることが出来ます。
#define     WEEK_SUN        0x1    /* 日曜日 */
#define     WEEK_MON        0x2    /* 月曜日 */
#define     WEEK_TUE        0x4    /* 火曜日 */
#define     WEEK_WED        0x8    /* 水曜日 */
#define     WEEK_THU        0x10   /* 木曜日 */
#define     WEEK_FRI        0x20   /* 金曜日 */
#define     WEEK_SAT        0x40   /* 土曜日 */

static unsigned char    Num[] = {
    WEEK_SUN,WEEK_MON,WEEK_TUE,WEEK_WED,WEEK_THU,WEEK_FRI,WEEK_SAT
};

比較は以下のようになります。
    struct tm    Today;
           ・
           ・
           ・
    if (Num[Today.tm_wday] == (alarm.Week & 0x7f)){
        (現在の曜日と指定の曜日が一致)
    }

,犯罎戮襪半々面倒と思われるかもしれませんが、処理中の演算が少ない分こちらの方が効率的です。

この方法は、前計算として知られるアルゴリズム最適化の一種で、身近な所では標準ライブラリのisalpha、isspace、isdigit等のis系関数が使用しています。これらの関数は、内部で256要素の配列を持っており、引数の文字コードをインデックスとして種別情報を取得していますので、大変効率が良くなっています(ライブラリリファレンス等で「ifを組み合わせた比較よりも効率が良いです」と言っているのは、このためです)。



No.1710

Re:目覚まし機能
投稿者---あさみ(2002/06/10 01:07:07)


ありがとうございます。
ひとつ疑問があります。
曜日を複数指定した場合、現在の曜日と比較するとき

例)現在の曜日が水曜日で指定曜日が日曜日、月曜日、水曜日の場合
 if (Num[Today.tm_wday] == (alarm.Week & 0x7f))
 {
(現在の曜日と指定の曜日が一致)

  この処理ですと、Num[Today.tm_wday]には0x8が入り
  (alarm.Week & 0x7f)は0xBとなってしまいます。
  どのようにすればよいのでしょうか?
  


No.1711

Re:目覚まし機能
投稿者---B.Smith(2002/06/10 02:43:05)


>曜日を複数指定した場合、現在の曜日と比較するとき
>
>例)現在の曜日が水曜日で指定曜日が日曜日、月曜日、水曜日の場合
> if (Num[Today.tm_wday] == (alarm.Week & 0x7f))
> {
>(現在の曜日と指定の曜日が一致)
>
>  この処理ですと、Num[Today.tm_wday]には0x8が入り
>  (alarm.Week & 0x7f)は0xBとなってしまいます。
>  どのようにすればよいのでしょうか?

あ…ごめんなさい。うっかりしていました。この比較はANDの結果で行わないとだめですね。

すでに気づかれているかもしれませんが、一応説明入れておきます。
日曜日、月曜日、水曜日の場合、alarm.Weekは
alarm.Week == WEEK_SUN | WEEK_MON | WEEK_WED | WEEK_ENABLE == 0x1 | 0x2 | 0x8 | 0x80 == 0x8b

現在が水曜日ですとインデックスは3になりますので、Num[3]の内容はWEEK_WED(0x8)になります。現在の曜日が指定曜日に設定されているかを調べるには、現在の曜日のビット位置と同じ位置が1であるかを調べれば良く、
0x8b & 0x8 == 0x8

    10001011(0x8b)    ← 指定曜日
&   00001000(0x8)     ← 現在の曜日
--------------------
    00001000(0x8)

指定位置のビットが1か0かを調べれば良いので、条件としては「現在の曜日のビットマスクとANDを取り、その結果がゼロでなければ、現在の曜日は指定曜日である」ということになります。つまり、
    if (Num[Today.tm_wday] & alarm.Week){
        (現在の曜日と指定の曜日が一致)
    }

alarm.Weekの「曜日指定の有無」(最上位ビット)を無視するように0x7fのマスクをかけていましたが、今回ANDの比較を行うため不要です。

ちなみに、マクロを使う場合も、同じくAND結果による比較となります。
#define COMPWEEK(WEEK,TODAY)    ((WEEK) & (1 << (TODAY)))

    if (COMPWEEK(alarm.Week,Today.tm_wday)){
        (現在の曜日と指定の曜日が一致)
    }




No.1715

Re:目覚まし機能
投稿者---あさみ(2002/06/10 22:03:52)


ありがとうございました。
B.Smithさんのアドバイスのおかげでなんとか
目覚ましを設定するところまで完成しました。
次は実際に目覚ましを起動する処理でどの目覚ましを起動するか判定する
処理で悩んでいます。
起動条件として以下が挙げられます。

1.現在の時刻と比較し未来のもの
2.目覚ましがONのもの
3.曜日指定の場合、現在の曜日と一致するもの

例えば、以下のように目覚ましを設定します。
同時刻のものは番号の若いものが優先されます。

現在時刻 09:00
目覚まし1 :10:00
目覚まし2 :09:30  <-この目覚ましを起動
目覚まし3 :09:40
目覚まし4 :11:00
目覚まし5 :01:00
目覚まし6 :09:30
目覚まし7 :10:30

目覚まし2を起動させたいのですがどのような判定処理をすれば
よいのでしょうか?現在の時刻から一番近い時刻を判定する処理等
あるのでしょうか?
わたしはmemcmpを使って判定する処理しか思い浮かばないのですか・・・




No.1716

Re:目覚まし機能
投稿者---B.Smith(2002/06/11 12:50:22)


こんにちは。

>目覚まし2を起動させたいのですがどのような判定処理をすれば
>よいのでしょうか?現在の時刻から一番近い時刻を判定する処理等
>あるのでしょうか?

「現在の時刻に最も近いものを探す」、ということは、「現在の時刻と設定時刻の差が最も小さいものを探す」ということです。

>1.現在の時刻と比較し未来のもの
>2.目覚ましがONのもの
>3.曜日指定の場合、現在の曜日と一致するもの

>同時刻のものは番号の若いものが優先されます。

1〜3の条件に当てはまる設定の時刻と、現在の時刻との差を求め、その最小値を検索する処理を構成すればOKです。
最小値検索の条件を、minimum値 >= 時刻差、ではなく、minimum値 > 時刻差にして、構造体配列を先頭から順に検索していけば、同時刻のものがあっても必ず最初のものが優先されます。



No.1717

Re:目覚まし機能
投稿者---あさみ(2002/06/11 23:20:30)


こんばんは。
>1〜3の条件に当てはまる設定の時刻と、現在の時刻との差を求め、その最小値を検索する処理を構成すればOKです。
>最小値検索の条件を、minimum値 >= 時刻差、ではなく、minimum値 > 時刻差にして、構造体配列を先頭から順に検索していけば、同時刻のものがあっても必ず最初のものが優先されます。
>
現在の時刻との差を求める方法はどのようにすればいいのでしょうか?
また、最小値を検索する処理も教えていただけないでしょうか?