Date/time nastavitev razpona - ideje dobrodošle

Vse o programiranju na in za PC

Moderatorji: Kroko, tilz0R

Date/time nastavitev razpona - ideje dobrodošle

OdgovorNapisal/-a tilz0R » 30 Jan 2022, 13:07

Imam en vhod, ki lahko naredi neko stvar samo ob določenih date/time intervalih, recimo:

- Vsak dan med 6:00 in 7:30
- Od petka 20:00 do ponedeljka 07:00, periodično, ne glede na leto ali mesec
- Vsako uro od 0 - 15 minut
- Vsako drugo uro med 15 in 30 minut znotraj ure (torej je veljaven range med 00:15-00:30, 02:15-02:30, etc)

Konceptualno je mix med crontab unix-like time definicijo in pa direktni time compare večje ali manjše, ker moram definirati range med dvema časoma (ampak range ni nujno absoluten, lahko je samo od-do minute-sekunde, ali mesec, ali karkoli).

Izvedba z crontab je lepa in enostavna, narediš bitno polje, ki je 1 kadar je match in 0 kadar ni. Problem je, da ni mogoče narediti range sistema.

Cron omogoča:
- Zadeva je vedno veljavna, ko je minuta med 0 in 15, ne glede na uro. Postavimo prvih 16 bitov na 1 in potem primerjamo (bitfield & (1 << trenutna_minuta))
- Zadeva je vedno veljavna, ko je minuta med 0 in 15 in ura soda
- Zadeva je veljavna vsak dan med 01:00 in 02:00

Z cron opisom ni mogoče narediti efektivnega range-a, recimo
- Zadeva je veljavna vsak dan med 07:00 in 08:30, predvsem zato, ker če damo cronu 0-30 pri minutah, bo zadeva veljavna 07:00-07:30 in 08:00-08:30), torej zgubimo 30 min :)

Jaz bi rad naredil mix obojega.
Igram se na razne načine kako bi to najbolj efektivno implementiral. Kakšna ideja?

Recimo cron bit-field implementacija je fairly simple in check deluje lepo.

Koda: Izberi vse
#include <time.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

/* Flags for valid entries in structure */
#define FLAG_VALID_SEC                          ((uint16_t)0x0001)
#define FLAG_VALID_MIN                          ((uint16_t)0x0002)
#define FLAG_VALID_HOUR                         ((uint16_t)0x0004)
#define FLAG_VALID_WDAY                         ((uint16_t)0x0008)
#define FLAG_VALID_MDAY                         ((uint16_t)0x0010)
#define FLAG_VALID_YDAY                         ((uint16_t)0x0020)
#define FLAG_VALID_MON                          ((uint16_t)0x0040)
#define FLAG_VALID_WEEK                         ((uint16_t)0x0080)
#define FLAG_VALID_YEAR                         ((uint16_t)0x0100)

static inline void
prv_set_bit_field(uint8_t* obj, uint8_t startindex, uint8_t endindex, uint8_t step) {
    if (step == 0) {
        step = 1;
    }
    for (; startindex < endindex; startindex += step) {
        obj[startindex / 8] |= 1 << (startindex % 8);
    }
}

static inline uint8_t
prv_check_bit(const uint8_t* obj, uint8_t bitpos) {
    return (obj[bitpos / 8] & (1 << (bitpos % 8))) > 0;
}

/**
 * \brief           Bit-field set when specific date is a valid cron range
 */
typedef struct {
    uint16_t flags;                             /*!< Flags for fields to be part of comparison.
                                                    If flag is not set, any value is valid (do not compare) */
    uint8_t sec[8];                             /*!< Seconds in a minute, from 0 to 59 */
    uint8_t min[8];                             /*!< Minutes past full hour, from 0 to 59 */
    uint8_t hour[3];                            /*!< Minutes past full day, from 0 to 23 */
    uint8_t wday[1];                            /*!< Day in a week, from 0 (Sunday) to 6 (Saturday) */
    uint8_t mday[4];                            /*!< Day in month, from 1 to 31 */
    uint8_t yday[46];                           /*!< Day in year since January 1st, from 0 to 365 */
    uint8_t mon[2];                             /*!< Month since January, from 0 to 11 */
    uint8_t week[7];                            /*!< Week in a year, from 1 to 53 */
    uint8_t year[13];                           /*!< Year since 2000, up to 2100 is allowed (0 - 100) */
} time_cron_t;

#define DT_RESET(dt)                                    memset((dt), 0x00, sizeof(*dt))

/*
 * Setters
 *
 * dt: Date time object
 * startindex: Range start
 * stopindex: Range stop
 * step: Step in range
 *
 * For example, to set valid cron each even hour, use
 * DT_SET_HOUR(&dt, 0, 24, 2)
 */
#define DT_SET_SEC(dt, startindex, stopindex, step)     prv_set_bit_field((dt)->sec, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_SEC
#define DT_SET_MIN(dt, startindex, stopindex, step)     prv_set_bit_field((dt)->min, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_MIN
#define DT_SET_HOUR(dt, startindex, stopindex, step)    prv_set_bit_field((dt)->hour, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_HOUR
#define DT_SET_WDAY(dt, startindex, stopindex, step)    prv_set_bit_field((dt)->wday, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_WDAY
#define DT_SET_MDAY(dt, startindex, stopindex, step)    prv_set_bit_field((dt)->mday, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_MDAY
#define DT_SET_YDAY(dt, startindex, stopindex, step)    prv_set_bit_field((dt)->yday, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_YDAY
#define DT_SET_MON(dt, startindex, stopindex, step)     prv_set_bit_field((dt)->mon, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_MON
#define DT_SET_WEEK(dt, startindex, stopindex, step)    prv_set_bit_field((dt)->week, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_WEEK
#define DT_SET_YEAR(dt, startindex, stopindex, step)    prv_set_bit_field((dt)->year, (startindex), (stopindex), (step)); (dt)->flags |= FLAG_VALID_YEAR

#define ERR(message)                printf("[LINE %d] %s\r\n", (int)__LINE__, (message))

static uint8_t
prv_check_inside_range(const struct tm* curr_dt, const time_cron_t* range) {
    /*
     * Comparion starts with flag check.
     *
     * If flag is NOT set, check doesn't need to be done
     * as any value for particular field is valid
     */
    if ((range->flags & FLAG_VALID_SEC) && !prv_check_bit(range->sec, curr_dt->tm_sec)) {
        ERR("Seconds failed");
        return 0;
    }
    if ((range->flags & FLAG_VALID_MIN) && !prv_check_bit(range->min, curr_dt->tm_min)) {
        ERR("Minutes failed");
        return 0;
    }
    if ((range->flags & FLAG_VALID_HOUR) && !prv_check_bit(range->hour, curr_dt->tm_hour)) {
        ERR("Hours failed");
        return 0;
    }
    if ((range->flags & FLAG_VALID_WDAY) && !prv_check_bit(range->wday, curr_dt->tm_wday)) {
        ERR("Week day failed");
        return 0;
    }
    if ((range->flags & FLAG_VALID_MDAY) && !prv_check_bit(range->mday, curr_dt->tm_mday)) {
        ERR("Month day failed");
        return 0;
    }
    if ((range->flags & FLAG_VALID_YDAY) && !prv_check_bit(range->yday, curr_dt->tm_yday)) {
        ERR("Year day failed");
        return 0;
    }
    if ((range->flags & FLAG_VALID_MON) && !prv_check_bit(range->mon, curr_dt->tm_mon)) {
        ERR("Month failed");
        return 0;
    }
    if ((range->flags & FLAG_VALID_YEAR) && !prv_check_bit(range->year, curr_dt->tm_year)) {
        ERR("Year failed");
        return 0;
    }
    ERR("All good, current time is inside range");
    return 1;
}

int
cron_demo_single_var(void) {
    time_cron_t dt_range;
    time_t curr_time;
    struct tm *tm;

    /* Set range start datetime */
    DT_RESET(&dt_range);
    DT_SET_HOUR(&dt_range, 10, 12, 0);      /* Range is valid any day between 10:00 to 12:00 */

    /* Get current system time */
    time(&curr_time);
    tm = localtime(&curr_time);
    tm->tm_year -= 100;                     /* Start from 2000 for particular test */

    printf("Time is in the range: %u\r\n", prv_check_inside_range(tm, &dt_range));

    return 0;
}

Kakšna pametna ideja kako najbolj pametno implementirat?
Trenutno razmišljam o setu flagov, ki bi določal kako določeno spremenljivko primerjam, večje/manjše, ali bitfield.
To dodatno poveča velikost strukture, ker poleg bit-field rabiš še 2x time spremenljivki za range

Možgani so mi tokrat ultra zaribali
Knowledge sharing is people' caring., T. MAJERLE
Uporabniški avatar
tilz0R
 
Prispevkov: 2119
Pridružen: 17 Jan 2015, 23:12
Kraj: Črnomelj
Zahvalil se je: 241 krat
Prejel zahvalo: 654 krat
Uporabnika povabil: s56rga
Število neizkoriščenih povabil: 210

Re: Date/time nastavitev razpona - ideje dobrodošle

OdgovorNapisal/-a zanka » 30 Jan 2022, 14:21

Če se potrebuje 1,5 ure od 7. do 8. ure in 30 minut, se lahko razbije na 2 dela:
- ko je ura 7
- ko je ura 8 in minute do 30.
Uporabniški avatar
zanka
 
Prispevkov: 3414
Pridružen: 17 Mar 2016, 00:16
Kraj: SI-8000
Zahvalil se je: 127 krat
Prejel zahvalo: 420 krat
Uporabnika povabil: DusanK
Število neizkoriščenih povabil: 83

Re: Date/time nastavitev razpona - ideje dobrodošle

OdgovorNapisal/-a tilz0R » 30 Jan 2022, 14:28

Ouu, lepa, hvala za ta namig. Nisem niti pomislil na to.
Knowledge sharing is people' caring., T. MAJERLE
Uporabniški avatar
tilz0R
 
Prispevkov: 2119
Pridružen: 17 Jan 2015, 23:12
Kraj: Črnomelj
Zahvalil se je: 241 krat
Prejel zahvalo: 654 krat
Uporabnika povabil: s56rga
Število neizkoriščenih povabil: 210

Re: Date/time nastavitev razpona - ideje dobrodošle

OdgovorNapisal/-a LiPo » 30 Jan 2022, 16:22

Jaz ponavadi timerji/urnike optimiram, da se jih lahko izvede v eni vrstici za vse situacije. To predvsem zaradi web vmesnika za nastavljanje. Potem pa to vrstico samo ponovim.

Jaz bi naredil sledece
ON/OFF MO TU WE TH FR SA SU 00:00

potem pa to ponovil npr 10x

ON/OFF preverja spremembo vhoda.
MO..SU dan v tednu. Ce so vsi dnevi na OFF potem upostevas
samo uro vklopa oz. izklopa.
Ce se zacne ura z 00 pomeni vsako uro. Ce pa z 01 pa vsako drugo uro. ( Je tam z 02: napaka?)


Seveda je zadaj ustrezna programska logika.

LpL
Uporabniški avatar
LiPo
 
Prispevkov: 1243
Pridružen: 04 Apr 2015, 16:30
Kraj: LJUBLJANA
Zahvalil se je: 57 krat
Prejel zahvalo: 178 krat
Uporabnika povabil: cimabella
Število neizkoriščenih povabil: 35

Re: Date/time nastavitev razpona - ideje dobrodošle

OdgovorNapisal/-a tilz0R » 30 Jan 2022, 16:45

LiPo, ideja zanke je dobra, in naredi to kar potrebujem. Več vnosov in OR med njimi. Podobno kot pri tebi.

Narediti cron kot takšen ni problem, ker samo bit-field primerjaš, jaz bi rad kontra.
Vedeti moram, če se lahko nek zunanji event zgodi v trenutnem času.

Nisem prepričan da vem o kakšni napaki govoriš? 02 pomeni "step == 02" pri for-loop (če misliš user code example, ki je zraven)
Knowledge sharing is people' caring., T. MAJERLE
Uporabniški avatar
tilz0R
 
Prispevkov: 2119
Pridružen: 17 Jan 2015, 23:12
Kraj: Črnomelj
Zahvalil se je: 241 krat
Prejel zahvalo: 654 krat
Uporabnika povabil: s56rga
Število neizkoriščenih povabil: 210

Re: Date/time nastavitev razpona - ideje dobrodošle

OdgovorNapisal/-a LiPo » 30 Jan 2022, 20:09

tilz0R je napisal/-a:LiPo, ideja zanke je dobra, in naredi to kar potrebujem. Več vnosov in OR med njimi. Podobno kot pri tebi.

Narediti cron kot takšen ni problem, ker samo bit-field primerjaš, jaz bi rad kontra.
Vedeti moram, če se lahko nek zunanji event zgodi v trenutnem času.

Nisem prepričan da vem o kakšni napaki govoriš? 02 pomeni "step == 02" pri for-loop (če misliš user code example, ki je zraven)


Saj logika je ista. Pac koncni rezultat primerjad z vhodom.
Sem se enkrat pogledal. Vse je ok. Pisem na telefonu pa sem na hitro preletel.
Jaz vedno izhajam s strani uporabnika. Oz. Uporabniskega vmesnika.
Uporabniški avatar
LiPo
 
Prispevkov: 1243
Pridružen: 04 Apr 2015, 16:30
Kraj: LJUBLJANA
Zahvalil se je: 57 krat
Prejel zahvalo: 178 krat
Uporabnika povabil: cimabella
Število neizkoriščenih povabil: 35


Vrni se na Programski jeziki

Kdo je na strani

Po forumu brska: 0 registriranih uporabnikov in 1 gost