Ex01_TickerP

em.examples.combo/Ex01_TickerP.em
import em from '@$$emscript'
export const $U = em.$declare('MODULE')

import * as BoardC from '@$distro/BoardC.em'
import * as Common from '@em.mcu/Common.em'
import * as FiberMgr from '@em.utils/FiberMgr.em'
import * as TickerMgr from '@em.utils/TickerMgr.em'
import * as TimeTypes from '@em.utils/TimeTypes.em'

// app settings
const sys_led_period_ms = 1500
const app_led_period_ms = 2000
const print_period_ms = 5000
const min_press_time_ms = 10
const max_press_time_ms = 2000
const max_divided_by = 8

// app resources
const AppLed = $delegate(BoardC.AppLed)
const AppBut = $delegate(BoardC.AppBut)
const SysLed = $delegate(BoardC.SysLed)
const AppTicker = $config<TickerMgr.Obj>()
const SysTicker = $config<TickerMgr.Obj>()
const PrintTicker = $config<TickerMgr.Obj>()

// initial state vector
var divided_by: u32 = 1
var sys_count: u32 = 0
var app_count: u32 = 0
var last_app_count: u32 = 0
var last_sys_count: u32 = 0
var print_count: u32 = 0
var expected_app_count: u32
var expected_sys_count: u32
var total_errors: u32 = 0
var prints_after_rate_change: u32 = 0

export namespace em$meta {
    export function em$construct() {
        AppTicker.$$ = TickerMgr.em$meta.create()
        SysTicker.$$ = TickerMgr.em$meta.create()
        PrintTicker.$$ = TickerMgr.em$meta.create()
    }
}

export function em$run() {
    printf`\nEx01_TickerP program startup\n\n`()
    startLedTickers()
    startPrintTicker()
    startButton()
    printStatus()
    FiberMgr.run()
}

function appTickCb() {
    app_count += 1
    em.$['%%c']
    AppLed.$$.wink(10)
}

function countError(count: u32, expectedCount: u32): bool_t {
    return (count < expectedCount || count > expectedCount + 1)
}

function onButtonPressed() {
    if (AppBut.$$.isPressed()) {
        // a long press (press time > max_press_time_ms)
        printf`Long button press: Stopping app/sys tickers\n`()
        divided_by = 0
        stopLedTickers()
        last_app_count = 0
        last_sys_count = 0
    } else {
        // a short press (min_press_time_ms < press time < max_press_time_ms)
        divided_by = (divided_by >= max_divided_by || divided_by < 1) ? 1 : divided_by * 2
        printf`Short button press: Setting rate to %dx\n`(divided_by)
        startLedTickers()
        printStatus()
    }
    prints_after_rate_change = 0
}

function printStatus() {
    printf`Button effects:\n... short press (>%d ms): cycle through rates (1,2,4,8x)\n... long press (>%d s): stop led tickers\n`(
        min_press_time_ms,
        max_press_time_ms / TimeTypes.millisecondsPerSecond
    )
    printf`Current rate %dx\n`(divided_by)
    printf`... should print every ~%ds\n`(print_period_ms / TimeTypes.millisecondsPerSecond)
    printf`... app ticks should be %d..%d\n`(expected_app_count, expected_app_count + 1)
    printf`... sys ticks should be %d..%d\n`(expected_sys_count, expected_sys_count + 1)
}

function printTickCb() {
    print_count += 1
    prints_after_rate_change++
    const this_app_count = app_count - last_app_count
    const this_sys_count = sys_count - last_sys_count
    const this_app_error = countError(this_app_count, expected_app_count)
    const this_sys_error = countError(this_sys_count, expected_sys_count)
    if (prints_after_rate_change > 2 && (this_app_error || this_sys_error)) {
        this_app_error && total_errors++
        this_sys_error && total_errors++
    }
    printTime(Common.Uptimer.$$.read())
    printf` Print tick {rate: %dx, ticks: {app: %d%s, sys: %d%s}, errors: %d}\n`(
        divided_by,
        this_app_count,
        this_app_error ? t$`*` : t$``,
        this_sys_count,
        this_sys_error ? t$`*` : t$``,
        total_errors
    )
    if (divided_by > 0 && last_sys_count > 0 && last_sys_count == sys_count) {
        printf`No sys ticks detected since last print\n`()
        em.halt#f()
    }
    if (divided_by > 0 && last_app_count > 0 && last_app_count == app_count) {
        printf`No app ticks detected since last print\n`()
        em.halt#f()
    }
    last_app_count = app_count
    last_sys_count = sys_count
}

function printTime(rawTime: TimeTypes.RawTime) {
    const timeParts = TimeTypes.RawTimeToTimeParts(rawTime)
    printf`%dT%02d:%02d:%02d.%03d`(
        timeParts.days,
        timeParts.hours,
        timeParts.minutes,
        timeParts.seconds,
        timeParts.milliseconds
    )
}

function startButton() {
    AppBut.$$.onPressed($cb(onButtonPressed), min_press_time_ms, max_press_time_ms)
}

function startLedTickers() {
    AppTicker.$$.$$.start(TimeTypes.Secs24p8_initMsecs(app_led_period_ms) / divided_by, $cb(appTickCb))
    SysTicker.$$.$$.start(TimeTypes.Secs24p8_initMsecs(sys_led_period_ms) / divided_by, $cb(sysTickCb))
    expected_app_count = divided_by * print_period_ms / app_led_period_ms
    expected_sys_count = divided_by * print_period_ms / sys_led_period_ms
}

function startPrintTicker() {
    PrintTicker.$$.$$.start(TimeTypes.Secs24p8_initMsecs(print_period_ms), $cb(printTickCb))
}

function stopLedTickers() {
    AppTicker.$$.$$.stop()
    SysTicker.$$.$$.stop()
    expected_app_count = 0
    expected_sys_count = 0
}

function sysTickCb() {
    sys_count += 1
    em.$['%%d']
    SysLed.$$.wink(10)
}