SatNOGS-COMMS  4.1.0
A COMMS subsystem for CubeSats
Loading...
Searching...
No Matches
logger.cpp
Go to the documentation of this file.
1/*
2 * SatNOGS-COMMS MCU software
3 *
4 * Copyright (C) 2023-2024, Libre Space Foundation <http://libre.space>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * SPDX-License-Identifier: GNU General Public License v3.0 or later
20 */
21
22#include "logger.hpp"
23#include "bsp/bsp.hpp"
24#include "callbacks.hpp"
25#include "scoped_lock.hpp"
26#include "storage.hpp"
27#include "version.hpp"
28#include <cstring>
29#include <etl/error_handler.h>
30#include <etl/string_stream.h>
31#include <etl/to_string.h>
33#include <zephyr/device.h>
34#include <zephyr/devicetree.h>
35#include <zephyr/drivers/uart.h>
36#include <zephyr/fs/fs.h>
37#include <zephyr/retention/retention.h>
38#include <zephyr/task_wdt/task_wdt.h>
39
40#include "time.hpp"
41
42namespace sc = satnogs::comms;
43namespace scl = satnogs::comms::lib;
44
45LOG_MODULE_REGISTER(satnogscomms, CONFIG_LOG_DEFAULT_LEVEL);
46
47namespace satnogs::comms
48{
49
50static const struct device *retention_memory_dev =
51 DEVICE_DT_GET(DT_CHOSEN(zephyr_retention_memory));
52
53etl::string<CONFIG_LOG_MAX_MSG_LEN> retentained_mem_str;
54
55#if DT_NODE_EXISTS(DT_ALIAS(uartlog))
56K_SEM_DEFINE(tx_done, 1, 1);
57static __aligned(32) uint8_t dma_buf[CONFIG_LOG_MAX_MSG_LEN];
58
59static void
60uart_callback(const struct device *dev, struct uart_event *evt, void *user_data)
61{
62 ARG_UNUSED(dev);
63 switch (evt->type) {
64 case UART_TX_DONE:
65 case UART_TX_ABORTED:
66 k_sem_give(&tx_done);
67 break;
68 case UART_RX_RDY:
69 break;
70 case UART_RX_BUF_RELEASED:
71 break;
72 case UART_RX_BUF_REQUEST:
73 break;
74 case UART_RX_DISABLED:
75 break;
76 default:
77 break;
78 }
79}
80#endif
81
82static etl::string<CONFIG_LOG_MAX_MSG_LEN> log_str;
83
92void
94{
95 etl::string<CONFIG_LOG_MAX_MSG_LEN> msg;
96 etl::string_stream stream(msg);
97 stream << "SatNOGS-COMMS FW: " << sc::version::fw_major << "."
98 << sc::version::fw_minor << "." << sc::version::fw_patch
99 << ", LIB: " << scl::version::lib_major << "."
100 << scl::version::lib_minor << "." << scl::version::lib_patch
101 << ", HW: " << scl::version::hw_major << "." << scl::version::hw_minor
102 << "." << scl::version::hw_patch << " initialized";
103 scoped_lock lock(&m_mtx);
104 log_unlocked({target::RTT, target::UART, target::RING_BUFFER}, msg.c_str());
105}
106
116void
121
131void
132logger::log(const etl::exception &e)
133{
135}
136
144void
145logger::log(const std::exception &e)
146{
148}
149
156void
161
167void
168logger::log(const etl::istring &msg)
169{
171 msg.c_str());
172}
173
183void
184logger::log(std::initializer_list<target> list, const scl::exception &e)
185{
186 scoped_lock lock(&m_mtx);
187 backup_sram_push(e);
188 log_unlocked(list, e);
189}
190
200void
201logger::log(std::initializer_list<target> list, const std::exception &e)
202{
203 scoped_lock lock(&m_mtx);
204 backup_sram_push(e);
205 log_unlocked(list, e);
206}
207
217void
218logger::log(std::initializer_list<target> list, const etl::exception &e)
219{
220 scoped_lock lock(&m_mtx);
221 backup_sram_push(e);
222 log_unlocked(list, e);
223}
224
234void
235logger::log(std::initializer_list<target> list, const char *msg)
236{
237 scoped_lock lock(&m_mtx);
238 log_unlocked(list, msg);
239}
240
250void
251logger::rtt_push(const satnogs::comms::lib::exception &e)
252{
253 log_str.clear();
254 e.to_string(log_str);
255 LOG_ERR("Exception: %s", log_str.c_str());
256}
257
266void
267logger::rtt_push(const etl::exception &e)
268{
269 LOG_ERR("Exception: %s", e.what());
270}
271
280void
281logger::rtt_push(const std::exception &e)
282{
283 LOG_ERR("Exception: %s", e.what());
284}
285
293void
294logger::rtt_push(const char *msg)
295{
296 LOG_INF("%s", msg);
297}
298
307void
308logger::ring_buffer_push(const lib::exception &e)
309{
310 auto &t = time::get_instance();
311 uint64_t t_posix;
312 time::time_src t_src = t.get(t_posix);
313 log_str.clear();
314 log_str.append("EXC: ");
315 /* Using the ETL we are safe from a possible buffer overflow */
316 e.to_string(log_str);
317 if (m_ring_buffer.full() == true) {
318 m_ring_buffer.pop();
319 }
320 m_ring_buffer.push(ring_buf_msg(t_src, t_posix, log_str));
321}
322
330void
331logger::ring_buffer_push(const std::exception &e)
332{
333 auto &t = time::get_instance();
334 uint64_t t_posix;
335 time::time_src t_src = t.get(t_posix);
336 log_str.clear();
337 log_str.append("EXC: ");
338 /* Using the ETL we are safe from a possible buffer overflow */
339 log_str.append(e.what());
340 if (m_ring_buffer.full() == true) {
341 m_ring_buffer.pop();
342 }
343 m_ring_buffer.push(ring_buf_msg(t_src, t_posix, log_str));
344}
345
353void
354logger::ring_buffer_push(const etl::exception &e)
355{
356 auto &t = time::get_instance();
357 uint64_t t_posix;
358 time::time_src t_src = t.get(t_posix);
359 log_str.clear();
360 log_str.append("EXC: ");
361 /* Using the ETL we are safe from a possible buffer overflow */
362 log_str.append(e.what());
363 if (m_ring_buffer.full() == true) {
364 m_ring_buffer.pop();
365 }
366 m_ring_buffer.push(ring_buf_msg(t_src, t_posix, log_str));
367}
368
378void
379logger::ring_buffer_push(const char *msg)
380{
381 auto &t = time::get_instance();
382 uint64_t t_posix;
383 time::time_src t_src = t.get(t_posix);
384 log_str.clear();
385 log_str.append("LOG: ");
386 /* Using the ETL we are safe from a possible buffer overflow */
387 log_str.append(msg);
388 if (m_ring_buffer.full() == true) {
389 m_ring_buffer.pop();
390 }
391 m_ring_buffer.push(ring_buf_msg(t_src, t_posix, log_str));
392}
393
408void
409logger::uart_push(const lib::exception &e)
410{
411#if DT_NODE_EXISTS(DT_ALIAS(uartlog))
412 log_str.clear();
413 log_str.append("EXC: ");
414 log_str.append(e.what());
415 log_str.append("\r\n");
416 const struct device *uart_log = DEVICE_DT_GET(DT_ALIAS(uartlog));
417 /*
418 * Wait for the previous transfer to finish. If this is not possible just
419 * abort
420 */
421 if (k_sem_take(&tx_done, K_MSEC(400))) {
422 uart_tx_abort(uart_log);
423 return;
424 }
425 memcpy(dma_buf, reinterpret_cast<const uint8_t *>(log_str.c_str()),
426 log_str.size());
427 uart_tx(uart_log, dma_buf, log_str.size(), SYS_FOREVER_US);
428#endif
429}
430
442void
443logger::uart_push(const std::exception &e)
444{
445#if DT_NODE_EXISTS(DT_ALIAS(uartlog))
446 log_str.clear();
447 log_str.append("EXC: ");
448 log_str.append(e.what());
449 const struct device *uart_log = DEVICE_DT_GET(DT_ALIAS(uartlog));
450 uart_tx(uart_log, reinterpret_cast<const uint8_t *>(log_str.c_str()),
451 log_str.size(), 100000);
452#endif
453}
454
466void
467logger::uart_push(const etl::exception &e)
468{
469#if DT_NODE_EXISTS(DT_ALIAS(uartlog))
470 log_str.clear();
471 log_str.append("EXC: ");
472 log_str.append(e.what());
473 const struct device *uart_log = DEVICE_DT_GET(DT_ALIAS(uartlog));
474 uart_tx(uart_log, reinterpret_cast<const uint8_t *>(log_str.c_str()),
475 log_str.size(), 100000);
476#endif
477}
478
490void
491logger::uart_push(const char *msg)
492{
493#if DT_NODE_EXISTS(DT_ALIAS(uartlog))
494 log_str.clear();
495 log_str.append("LOG: ");
496 log_str.append(msg);
497 log_str.append("\r\n");
498 const struct device *uart_log = DEVICE_DT_GET(DT_ALIAS(uartlog));
499
500 /*
501 * Wait for the previous transfer to finish. If this is not possible just
502 * abort
503 */
504 if (k_sem_take(&tx_done, K_MSEC(400))) {
505 uart_tx_abort(uart_log);
506 return;
507 }
508 memcpy(dma_buf, reinterpret_cast<const uint8_t *>(log_str.c_str()),
509 log_str.size());
510 uart_tx(uart_log, dma_buf, log_str.size(), SYS_FOREVER_US);
511#endif
512}
513
514void
515logger::storage_push(const std::exception &e)
516{
517 auto &storage = storage::get_instance();
518 if (storage.enabled() == false || storage.get_dir() != lib::emmc::dir::MCU) {
519 return;
520 }
521
522 try {
523 m_logs_path.clear();
524 m_logs_path += mcu_log_dir;
525 storage.mkdir(m_logs_path);
526
527 auto &t = time::get_instance();
528 uint64_t t_posix;
529 time::time_src t_src = t.get(t_posix);
530
531 log_str.clear();
532 etl::string_stream stream(log_str);
533 stream << "[" << etl::dec << (int)t_src << "," << etl::dec << t_posix
534 << "]: "
535 << "EXC: " << e.what() << "\r\n";
536
537 current_log_file(m_logs_path);
538 storage.write(m_logs_path, (const uint8_t *)log_str.c_str(),
539 log_str.size());
540 } catch (lib::exception &e) {
541 return;
542 }
543}
544
545void
546logger::storage_push(const etl::exception &e)
547{
548 auto &storage = storage::get_instance();
549 if (storage.enabled() == false || storage.get_dir() != lib::emmc::dir::MCU) {
550 return;
551 }
552
553 try {
554 m_logs_path.clear();
555 m_logs_path += mcu_log_dir;
556 storage.mkdir(m_logs_path);
557
558 auto &t = time::get_instance();
559 uint64_t t_posix;
560 time::time_src t_src = t.get(t_posix);
561
562 log_str.clear();
563 etl::string_stream stream(log_str);
564 stream << "[" << etl::dec << (int)t_src << "," << etl::dec << t_posix
565 << "]: "
566 << "EXC: " << e.what() << "\r\n";
567
568 current_log_file(m_logs_path);
569 storage.write(m_logs_path, (const uint8_t *)log_str.c_str(),
570 log_str.size());
571 } catch (lib::exception &e) {
572 return;
573 }
574}
575
576void
577logger::storage_push(const lib::exception &e)
578{
579 /* if we leave this unchecked the logger will try to write the
580 * storage_delete_dir exception to a file in a dir that does not exist -
581 * producing a kernel fault
582 */
583
584 auto &storage = storage::get_instance();
585 if (!storage.enabled() || storage.get_dir() == lib::emmc::dir::FPGA) {
586 return;
587 }
588
589 try {
590 m_logs_path.clear();
591 m_logs_path += mcu_log_dir;
592 storage.mkdir(m_logs_path);
593
594 auto &t = time::get_instance();
595 uint64_t t_posix;
596 time::time_src t_src = t.get(t_posix);
597
598 log_str.clear();
599 etl::string_stream stream(log_str);
600 stream << "[" << etl::dec << (int)t_src << "," << etl::dec << t_posix
601 << "]: EXC ";
602 e.to_string(stream);
603 stream << "\r\n";
604
605 current_log_file(m_logs_path);
606 storage.write(m_logs_path, (const uint8_t *)log_str.c_str(),
607 log_str.size());
608 } catch (lib::exception &e) {
609 return;
610 }
611}
612
613void
614logger::storage_push(const char *msg)
615{
616
617 auto &storage = storage::get_instance();
618 if (!storage.enabled() || storage.get_dir() == lib::emmc::dir::FPGA) {
619 return;
620 }
621
622 try {
623 m_logs_path.clear();
624 m_logs_path += mcu_log_dir;
625 storage.mkdir(m_logs_path);
626
627 auto &t = time::get_instance();
628 uint64_t t_posix;
629 time::time_src t_src = t.get(t_posix);
630
631 log_str.clear();
632 etl::string_stream stream(log_str);
633 stream << "[" << etl::dec << (int)t_src << "," << etl::dec << t_posix
634 << "]: "
635 << "LOG: " << msg << "\r\n";
636 current_log_file(m_logs_path);
637 storage.write(m_logs_path, (const uint8_t *)log_str.c_str(),
638 log_str.size());
639 } catch (lib::exception &e) {
640 return;
641 }
642}
643
650void
651logger::current_log_file(etl::istring &path)
652{
653 path.clear();
654 etl::string_stream stream(m_logs_path);
655
656 auto &time = time::get_instance();
657 struct tm t;
658 auto tsrc = time.get(t);
659 switch (tsrc) {
661 auto &s = sc::settings::get_instance();
662 uint32_t boot_cnt = s.get<sc::settings::param::BOOT_COUNT>();
663 uint32_t days = time.uptime() / (24 * 60 * 60 * 1000);
664
665 stream << mcu_log_dir << "/"
666 << "boot-" << boot_cnt << "-" << days << ".log";
667 } break;
670 char strt[16];
671 strftime(strt, 15, "%Y-%m-%d", &t);
672 stream << mcu_log_dir << "/" << strt << ".log";
673 break;
674 }
675 default:
676 throw scl::inval_arg_exception(scl::exception::severity::MINOR, __FILE__,
677 __LINE__);
678 }
679}
680
681void
682logger::delete_storage_logs(struct tm &start, struct tm &end)
683{
686 return;
687 }
688
689 scoped_lock lock(&m_mtx);
690
691 time_t start_posix = timeutil_timegm(&start);
692 time_t end_posix = timeutil_timegm(&end);
693 time_t day_posix = 24 * 60 * 60;
694
695 struct tm tmp;
696 for (time_t t = start_posix; t <= end_posix; t += day_posix) {
697
698 gmtime_r(&t, &tmp);
699 m_logs_path.clear();
700 etl::string_stream stream(m_logs_path);
701 stream << mcu_log_dir << "/" << tmp.tm_year << "-" << tmp.tm_mon << "-"
702 << tmp.tm_mday << ".log";
703
704 try {
705 storage.rm(m_logs_path);
706 } catch (lib::exception &e) {
707 /*Do nothing*/
708 }
709 }
710}
711
724int
725logger::backup_sram_push(const lib::exception &e)
726{
727 if (static_cast<uint8_t>(e.get_severity()) <=
728 CONFIG_BACKUP_SRAM_EXCEPTION_LOG_LEVEL) {
729 log_str.clear();
730 e.to_string(log_str);
731 retention_clear(retention_memory_dev);
732 return retention_write(retention_memory_dev, 0U,
733 reinterpret_cast<const uint8_t *>(log_str.c_str()),
734 CONFIG_LOG_MAX_MSG_LEN);
735 }
736 return -1;
737}
738
747int
748logger::backup_sram_push(const std::exception &e)
749{
750 retention_clear(retention_memory_dev);
751 return retention_write(retention_memory_dev, 0U,
752 reinterpret_cast<const uint8_t *>(e.what()),
753 CONFIG_LOG_MAX_MSG_LEN);
754}
755
764int
765logger::backup_sram_push(const etl::exception &e)
766{
767 retention_clear(retention_memory_dev);
768 return retention_write(retention_memory_dev, 0U,
769 reinterpret_cast<const uint8_t *>(e.what()),
770 CONFIG_LOG_MAX_MSG_LEN);
771}
772
773size_t
775{
776 return m_ring_buffer.size();
777}
778
779void
781{
782 data = *(m_ring_buffer.crbegin() + index);
783}
784
790etl::string<CONFIG_LOG_MAX_MSG_LEN>
792{
793 if (retention_is_valid(retention_memory_dev) != 1) {
794 return "Data in BACKUP SRAM is not valid";
795 }
796 if (retention_read(retention_memory_dev, 0U,
797 reinterpret_cast<uint8_t *>(retentained_mem_str.data()),
798 CONFIG_LOG_MAX_MSG_LEN) != 0) {
799 return "Error while reading data from BACKUP SRAM device";
800 }
801 return retentained_mem_str.data();
802}
803
817{
818 k_mutex_init(&m_mtx);
819#if DT_NODE_EXISTS(DT_ALIAS(uartlog))
820 const struct device *uart_log = DEVICE_DT_GET(DT_ALIAS(uartlog));
821 struct uart_config uart_cfg = {
822 .baudrate = CONFIG_LOG_UART_BAUDRATE,
823 .parity = UART_CFG_PARITY_NONE,
824 .stop_bits = UART_CFG_STOP_BITS_1,
825 .data_bits = UART_CFG_DATA_BITS_8,
826 .flow_ctrl = UART_CFG_FLOW_CTRL_NONE,
827 };
828 uart_rx_disable(uart_log);
829 uart_tx_abort(uart_log);
830 uart_configure(uart_log, &uart_cfg);
831 uart_callback_set(uart_log, uart_callback, nullptr);
832#endif
833}
834
835} // namespace satnogs::comms
Exception base class.
Definition exception.hpp:63
@ MINOR
Failure having minimal impact.
Definition exception.hpp:75
severity get_severity() const
Get error severity level as defined in FDIR.
void to_string(etl::string_stream &stream) const
Creates a string representation of the exception.
etl::string< CONFIG_LOG_MAX_MSG_LEN > get_latest_exception() const
Retrieves the latest exception message stored in BACKUP_SRAM.
Definition logger.cpp:791
size_t get_ring_buffer_size()
Definition logger.cpp:774
@ RING_BUFFER
In-memory ring buffer for storing recent log messages.
Definition logger.hpp:79
@ STORAGE
eMMC logging for persistent storage
Definition logger.hpp:80
@ UART
UART logging output.
Definition logger.hpp:78
@ RTT
Real-Time Transfer logging (Segger RTT).
Definition logger.hpp:77
void get_ring_buffer_log(ring_buf_msg &data, size_t index)
Definition logger.cpp:780
static constexpr const char * mcu_log_dir
Definition logger.hpp:70
void delete_storage_logs(struct tm &start, struct tm &end)
Definition logger.cpp:682
logger(logger const &)=delete
Disabled copy constructor to enforce singleton.
void boot()
Logs system initialization details to multiple targets (RTT, UART, and RING_BUFFER).
Definition logger.cpp:93
void log(std::initializer_list< target > list, const lib::exception &e)
Logs a satnogs::comms::lib::exception to a specified set of targets.
Definition logger.cpp:184
Implements a scoped lock utilizing the Zephyr mutex.
static settings & get_instance()
Get a singleton access to the settings subsystem.
Definition settings.hpp:210
bool enabled() const
Checks if the storage subsystem is enabled.
Definition storage.cpp:183
static storage & get_instance()
Definition storage.hpp:94
lib::emmc::dir get_dir() const
Get the direction of the eMMC.
Definition storage.cpp:218
void rm(const etl::istring &path)
Removes a file from the filesystem.
Definition storage.cpp:295
static time & get_instance()
Definition time.hpp:63
time_src
Source of the reported time.
Definition time.hpp:74
@ UPTIME
No RTC or GNSS time source. The uptime is used to track time.
Definition time.hpp:80
@ GNSS_ONLY
No RTC installed, but there is time information from a GNSS fix.
Definition time.hpp:78
LOG_MODULE_REGISTER(satnogscomms, CONFIG_LOG_DEFAULT_LEVEL)
etl::string< CONFIG_LOG_MAX_MSG_LEN > retentained_mem_str
Definition logger.cpp:53