* MIT License
* Copyright (c) 2021 - 2024 _VIFEXTech
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "SystemInfosView.h"
using namespace Page;
#define ITEM_HEIGHT_MIN 100
SystemInfosView::SystemInfosView(EventListener* listener, lv_obj_t* root)
: _listener(listener)
, _fontAwesome(40, "awesome")
#define BINDING_DEF(name, type) , _binding##name((Binding<type, SystemInfosModel>*)getBinding(BINDING_TYPE::name))
#include "BindingDef.inc"
#undef BINDING_DEF
{
static_assert(sizeof(SystemInfosView) >= (size_t)MSG_ID::_LAST, "Large MSG_ID");
rootInit(root);
styleInit();
itemGroupCreate(root);
onRootScroll(root);
}
SystemInfosView::~SystemInfosView()
{
}
lv_uintptr_t SystemInfosView::msgID(MSG_ID id)
{
return (lv_uintptr_t)this + (lv_uintptr_t)id;
}
void SystemInfosView::publish(MSG_ID id, const void* payload)
{
lv_msg_send(msgID(id), payload);
}
void SystemInfosView::subscribe(MSG_ID id, lv_obj_t* obj, lv_event_cb_t event_cb)
{
lv_msg_subscribe_obj(msgID(id), obj, this);
lv_obj_add_event(obj, event_cb, LV_EVENT_MSG_RECEIVED, this);
}
void* SystemInfosView::getBinding(BINDING_TYPE type)
{
Binding_Info_t info;
info.type = type;
info.binding = nullptr;
_listener->onViewEvent(EVENT_ID::GET_BINDING, &info);
return info.binding;
}
void SystemInfosView::rootInit(lv_obj_t* root)
{
lv_obj_update_layout(root);
lv_obj_set_style_pad_ver(root, ((lv_obj_get_height(root) - ITEM_HEIGHT_MIN) / 2), 0);
lv_obj_set_flex_flow(root, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(
root,
LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_START,
LV_FLEX_ALIGN_CENTER);
lv_obj_add_event_cb(
root,
[](lv_event_t* e) {
onRootScroll(lv_event_get_current_target_obj(e));
},
LV_EVENT_SCROLL,
this);
lv_obj_add_flag(root, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_set_scroll_snap_y(root, LV_SCROLL_SNAP_CENTER);
lv_obj_set_scrollbar_mode(root, LV_SCROLLBAR_MODE_OFF);
}
void SystemInfosView::onRootScroll(lv_obj_t* obj)
{
auto child_cnt = lv_obj_get_child_count(obj);
auto obj_height = lv_obj_get_height(obj);
auto obj_pad_top = lv_obj_get_style_pad_top(obj, 0);
auto scroll_y = lv_obj_get_scroll_y(obj);
for (uint32_t i = 0; i < child_cnt; i++) {
auto cont = lv_obj_get_child(obj, i);
auto icon = lv_obj_get_child(cont, 2);
auto cont_height = lv_obj_get_height(cont);
auto cont_y = lv_obj_get_y(cont);
auto offset_y = (obj_height / 2.0f + scroll_y) - (obj_pad_top + cont_y + cont_height / 2.0f);
lv_anim_t a;
lv_anim_init(&a);
a.duration = cont_height;
a.act_time = LV_ABS(offset_y);
a.start_value = 70;
a.end_value = 220;
auto width = lv_anim_path_ease_out(&a);
a.duration = cont_height / 2;
a.start_value = LV_OPA_COVER;
a.end_value = LV_OPA_TRANSP;
auto opa = lv_anim_path_ease_out(&a);
lv_obj_set_width(icon, width);
lv_obj_set_style_border_opa(icon, opa, 0);
}
}
void SystemInfosView::styleInit()
{
lv_style_set_pad_all(&_style.icon, 0);
lv_style_set_border_width(&_style.icon, 0);
lv_style_set_radius(&_style.icon, 0);
lv_style_set_width(&_style.icon, 220);
lv_style_set_border_side(&_style.icon, LV_BORDER_SIDE_RIGHT);
lv_style_set_border_width(&_style.icon, 2);
lv_style_set_border_color(&_style.icon, lv_color_hex(0xff931e));
lv_style_set_text_font(&_style.icon, lv_theme_get_font_normal(nullptr));
lv_style_set_text_color(&_style.icon, lv_color_white());
lv_style_set_text_font(&_style.info, lv_theme_get_font_small(nullptr));
}
void SystemInfosView::itemGroupCreate(lv_obj_t* par)
{
char infoBuf[128];
{
lv_snprintf(infoBuf, sizeof(infoBuf),
"%s\n%s\n%s",
_("TOTAL_TRIP"), _("TOTAL_TIME"), _("MAX_SPEED"));
lv_obj_t* label = itemCreate(
par,
_("SPORT"),
LV_SYMBOL_EXT_BICYCLE,
infoBuf);
subscribe(
MSG_ID::SPORT_STATUS,
label,
[](lv_event_t* e) {
auto obj = lv_event_get_current_target_obj(e);
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::SportStatus_Info_t*)lv_msg_get_payload(msg);
lv_label_set_text_fmt(
obj,
"%0.2fkm\n"
"%s\n"
"%0.1fkm/h",
info->totalDistance / 1000.0f,
makeTimeString(info->totalTime),
info->speedMaxKph);
});
}
{
lv_snprintf(infoBuf, sizeof(infoBuf),
"%s\n%s\n%s\n%s\n\n%s\n%s",
_("LATITUDE"),
_("LONGITUDE"),
_("ALTITUDE"),
_("UTC_TIME"),
_("COURSE"),
_("SPEED"));
lv_obj_t* label = itemCreate(
par,
_("GNSS"),
LV_SYMBOL_EXT_SATELLITE,
infoBuf);
subscribe(
MSG_ID::GNSS,
label,
[](lv_event_t* e) {
auto obj = lv_event_get_current_target_obj(e);
auto msg = lv_event_get_msg(e);
auto info = (const HAL::GNSS_Info_t*)lv_msg_get_payload(msg);
lv_label_set_text_fmt(
obj,
"%0.6f\n"
"%0.6f\n"
"%0.2fm\n"
"%s\n"
"%0.1f deg\n"
"%0.1fkm/h",
info->latitude,
info->longitude,
info->altitude,
makeTimeString(&info->clock),
info->course,
info->speed);
});
}
{
lv_snprintf(infoBuf, sizeof(infoBuf),
"%s\n%s",
_("DATE"),
_("TIME"));
lv_obj_t* label = itemCreate(
par,
_("TIME"),
LV_SYMBOL_EXT_CLOCK,
infoBuf);
subscribe(
MSG_ID::CLOCK,
label,
[](lv_event_t* e) {
auto obj = lv_event_get_current_target_obj(e);
auto msg = lv_event_get_msg(e);
auto info = (const HAL::Clock_Info_t*)lv_msg_get_payload(msg);
lv_label_set_text_fmt(
obj,
"%s",
makeTimeString(info));
});
}
{
lv_snprintf(infoBuf, sizeof(infoBuf),
"%s\n%s\n%s",
_("USAGE"),
_("VOLTAGE"),
_("STATUS"));
lv_obj_t* label = itemCreate(
par,
_("BATTERY"),
LV_SYMBOL_EXT_BATTERY_THREE_QUARTERS,
infoBuf);
subscribe(
MSG_ID::POWER,
label,
[](lv_event_t* e) {
auto obj = lv_event_get_current_target_obj(e);
auto msg = lv_event_get_msg(e);
auto info = (const DataProc::Power_Info_t*)lv_msg_get_payload(msg);
lv_label_set_text_fmt(
obj,
"%d%%\n"
"%0.2fV\n"
"%s",
info->level,
info->voltage / 1000.0f,
info->isCharging ? _("CHARGE") : _("DISCHARGE"));
});
}
{
lv_snprintf(infoBuf, sizeof(infoBuf),
"%s\n%s\n%s\n%s\n%s",
_("NAME"),
_("AUTHOR"),
_("GRAPHICS"),
_("COMPILER"),
_("BUILD_DATE"));
lv_obj_t* label = itemCreate(
par,
_("ABOUT"),
LV_SYMBOL_EXT_ADDRESS_CARD,
infoBuf);
auto info = _bindingVerison->get();
lv_label_set_text_fmt(
label,
"%s\n"
"%s\n"
"%s\n"
"%s\n"
"%s",
info.name,
info.author,
info.graphics,
info.compiler,
info.buildDate);
}
}
lv_obj_t* SystemInfosView::itemCreate(
lv_obj_t* par,
const char* name,
const char* symbol,
const char* infos)
{
lv_obj_t* cont = lv_obj_create(par);
lv_obj_enable_style_refresh(false);
lv_obj_remove_style_all(cont);
lv_obj_set_width(cont, 220);
lv_obj_clear_flag(cont, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_t* icon = lv_obj_create(cont);
lv_obj_enable_style_refresh(false);
lv_obj_clear_flag(icon, LV_OBJ_FLAG_SCROLLABLE);
lv_obj_add_flag(icon, LV_OBJ_FLAG_SCROLL_ON_FOCUS);
lv_obj_add_style(icon, &_style.icon, 0);
lv_obj_set_style_align(icon, LV_ALIGN_LEFT_MID, 0);
lv_obj_set_flex_flow(icon, LV_FLEX_FLOW_COLUMN);
lv_obj_set_flex_align(
icon,
LV_FLEX_ALIGN_SPACE_AROUND,
LV_FLEX_ALIGN_CENTER,
LV_FLEX_ALIGN_CENTER);
{
lv_obj_t* label = lv_label_create(icon);
lv_obj_enable_style_refresh(false);
lv_obj_set_style_text_font(label, _fontAwesome, 0);
lv_label_set_text_static(label, symbol);
}
{
lv_obj_t* label = lv_label_create(icon);
lv_obj_enable_style_refresh(false);
lv_label_set_text(label, name);
}
lv_obj_t* labelInfo = lv_label_create(cont);
{
lv_obj_enable_style_refresh(false);
lv_label_set_text(labelInfo, infos);
lv_obj_add_style(labelInfo, &_style.info, 0);
lv_obj_align(labelInfo, LV_ALIGN_LEFT_MID, 75, 0);
}
lv_obj_t* labelData = lv_label_create(cont);
{
lv_obj_enable_style_refresh(false);
lv_label_set_text(labelData, "-");
lv_obj_add_style(labelData, &_style.info, 0);
lv_obj_align(labelData, LV_ALIGN_CENTER, 60, 0);
}
lv_obj_move_foreground(icon);
lv_obj_enable_style_refresh(true);
lv_obj_update_layout(labelInfo);
lv_coord_t height = lv_obj_get_height(labelInfo);
height = LV_MAX(height, ITEM_HEIGHT_MIN);
lv_obj_set_height(cont, height);
lv_obj_set_height(icon, height);
return labelData;
}
const char* SystemInfosView::makeTimeString(uint64_t ms)
{
static char buf[16];
uint64_t ss = ms / 1000;
uint64_t mm = ss / 60;
uint32_t hh = (uint32_t)(mm / 60);
lv_snprintf(
buf, sizeof(buf),
"%d:%02d:%02d",
(int)hh,
(int)(mm % 60),
(int)(ss % 60));
return buf;
}
const char* SystemInfosView::makeTimeString(const HAL::Clock_Info_t* info)
{
static char buf[32];
lv_snprintf(
buf,
sizeof(buf),
"%d-%d-%d\n%02d:%02d:%02d",
info->year,
info->month,
info->day,
info->hour,
info->minute,
info->second);
return buf;
}