VEU.WathBody/3-app/5-watch/FenixDataField/source/FenixDataField.mc

192 lines
7.1 KiB
MonkeyC
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Поле данных «Феникс» для профиля «Силовая» (Ф-2, m13).
// Экран: текущий пульс (крупно) + плашка зоны цветом с подписью, строка
// «ПОДХОД мм:сс - ср N» / «ОТДЫХ мм:сс». Lap — граница подхода и старт
// автономного отдыха (errata Ф-2.3/Ф-2.4). Кнопки не перехватываются,
// запись .fit ведёт платформа Garmin (Ф-2.5–Ф-2.7).
import Toybox.Activity;
import Toybox.Graphics;
import Toybox.WatchUi;
class FenixDataField extends WatchUi.DataField {
var _settings;
var _zones;
var _set;
var _rest;
var _vibe;
var _fmt;
var _hr; // кэш пульса (Number или null)
var _maxBpm10; // макс. ЧСС ×10
var _zone; // текущая зона (идентификатор ZoneCalc)
function initialize() {
DataField.initialize();
_settings = new FieldSettings();
_zones = new ZoneCalc();
_set = new SetTracker();
_rest = new RestTimer();
_vibe = new Vibe();
_fmt = new Format();
_hr = null;
_maxBpm10 = _zones.maxBpm10For(_settings.hrMaxBpm, _settings.age);
_zone = _zones.ZONE_NONE;
}
function timerNow() {
var info = Activity.getActivityInfo();
if (info != null && info.timerTime != null) {
return info.timerTime;
}
return 0;
}
// Lap (штатно — конец подхода): метрики обнуляются, отдых стартует
function onTimerLap() {
var t = timerNow();
_set.onLap(t);
_rest.start(t, _settings.restSec);
}
function onTimerStart() {
resetState();
}
function onTimerReset() {
resetState();
}
function resetState() {
_set.onLap(0);
_rest.initialize();
}
// Раз в секунду: пульс → метрики, зоны, сигналы вибраций
function compute(info) {
_hr = info.currentHeartRate;
var t = info.timerTime;
if (t == null) {
return;
}
_set.onSample(_hr);
_zone = _zones.zoneFor(_hr, _maxBpm10);
if (_rest.onEnd(t)) {
_vibe.vibrateRestEnd();
}
_vibe.onZone(_zone == _zones.ZONE_RED, t);
}
function colorFor(zoneId) {
if (zoneId == _zones.ZONE_BLUE) {
return Graphics.COLOR_BLUE;
}
if (zoneId == _zones.ZONE_GREEN) {
return Graphics.COLOR_GREEN;
}
if (zoneId == _zones.ZONE_YELLOW) {
return Graphics.COLOR_YELLOW;
}
if (zoneId == _zones.ZONE_RED) {
return Graphics.COLOR_RED;
}
return Graphics.COLOR_DK_GRAY;
}
function labelFor(zoneId) {
if (zoneId == _zones.ZONE_BLUE) {
return WatchUi.loadResource(Rez.Strings.ZoneBlue);
}
if (zoneId == _zones.ZONE_GREEN) {
return WatchUi.loadResource(Rez.Strings.ZoneGreen);
}
if (zoneId == _zones.ZONE_YELLOW) {
return WatchUi.loadResource(Rez.Strings.ZoneYellow);
}
if (zoneId == _zones.ZONE_RED) {
return WatchUi.loadResource(Rez.Strings.ZoneRed);
}
return WatchUi.loadResource(Rez.Strings.ZoneNone);
}
// Параметрическая отрисовка по размерам области (dc): ни одной
// фиксированной координаты — один код на всё семейство экранов (Ф-2.8)
function onUpdate(dc) {
var w = dc.getWidth();
var h = dc.getHeight();
dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);
dc.clear();
// 1) плашка зоны сверху: цвет зоны + подпись текстом (цвет —
// не единственный носитель, ui-ux)
var tiny = Graphics.FONT_TINY;
var zoneLabel = labelFor(_zone);
var tw = dc.getTextWidthInPixels(zoneLabel, tiny);
var th = dc.getFontHeight(tiny);
var pad = 4;
var plateW = tw + pad * 2;
var plateH = th + pad;
var plateX = (w - plateW) / 2;
var plateY = 2;
if (_zone == _zones.ZONE_NONE) {
dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_DK_GRAY);
dc.fillRectangle(plateX, plateY, plateW, plateH);
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_DK_GRAY);
} else {
var zc = colorFor(_zone);
dc.setColor(zc, zc);
dc.fillRectangle(plateX, plateY, plateW, plateH);
dc.setColor(Graphics.COLOR_BLACK, zc);
}
dc.drawText(plateX + pad, plateY + 1, tiny, zoneLabel, Graphics.TEXT_JUSTIFY_LEFT);
// 2) пульс — крупное число (адаптивный шрифт: THAI_HOT, если не
// влезает — MEDIUM; поле может делить страницу с другими полями)
var bigFont = Graphics.FONT_NUMBER_THAI_HOT;
var bigH = dc.getFontHeight(bigFont);
if (bigH > h * 5 / 10) {
bigFont = Graphics.FONT_NUMBER_MEDIUM;
bigH = dc.getFontHeight(bigFont);
}
var hrText;
if (_hr == null || _hr <= 0) {
hrText = "-";
} else {
hrText = "" + _hr;
}
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
var hw = dc.getTextWidthInPixels(hrText, bigFont);
var hrY = plateY + plateH + (h - plateH - plateY - bigH - th - 8) / 2;
if (hrY < plateY + plateH + 2) {
hrY = plateY + plateH + 2;
}
dc.drawText((w - hw) / 2, hrY, bigFont, hrText, Graphics.TEXT_JUSTIFY_LEFT);
// 3) подпись единиц под пульсом
var unit = WatchUi.loadResource(Rez.Strings.Unit);
var uw = dc.getTextWidthInPixels(unit, tiny);
dc.drawText((w - uw) / 2, hrY + bigH + 1, tiny, unit, Graphics.TEXT_JUSTIFY_LEFT);
// 4) строка состояния: ОТДЫХ (остаток) или ПОДХОД (время с границы
// Lap - средний пульс интервала)
var line;
var t = timerNow();
var small = Graphics.FONT_SMALL;
var lh = dc.getFontHeight(small);
if (_rest.isResting(t)) {
line = WatchUi.loadResource(Rez.Strings.Rest) + " " + _fmt.mmss(_rest.remainingMs(t));
} else {
var avg = _set.avgBpm();
var avgText;
if (avg == null) {
avgText = "-";
} else {
avgText = "" + avg;
}
line = WatchUi.loadResource(Rez.Strings.Set) + " " + _fmt.mmss(_set.elapsedMs(t))
+ " - " + WatchUi.loadResource(Rez.Strings.Avg) + " " + avgText;
}
var lw = dc.getTextWidthInPixels(line, small);
dc.drawText((w - lw) / 2, h - lh - 2, small, line, Graphics.TEXT_JUSTIFY_LEFT);
}
}