diff --git a/include/colors.h b/include/colors.h index d7dd172..44f9dbb 100644 --- a/include/colors.h +++ b/include/colors.h @@ -5,11 +5,11 @@ namespace BigMoney { enum Color { - kRedBlack = 1, - kGreenBlack = 2, - kWhiteBlue = 3 + kRedBlack = 1, + kGreenBlack = 2, + kWhiteBlue = 3 }; int GetColorPair(Color color); -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/include/command_bar.h b/include/command_bar.h index f27310d..da043df 100644 --- a/include/command_bar.h +++ b/include/command_bar.h @@ -9,15 +9,15 @@ namespace BigMoney { class CommandBar : public Window{ public: - CommandBar(int x, int y, int startx, int starty); - void GetCommand(); - ~CommandBar(); + CommandBar(int x, int y, int startx, int starty); + void GetCommand(); + ~CommandBar(); private: - void ParseCommand(const std::string &cmd); + void ParseCommand(const std::string &cmd); private: - std::thread *listen_thread_{nullptr}; - bool is_listen_{false}; + std::thread *listen_thread_{nullptr}; + bool is_listen_{false}; }; -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/include/earn.h b/include/earn.h index d5149aa..d30b46e 100644 --- a/include/earn.h +++ b/include/earn.h @@ -9,13 +9,13 @@ namespace BigMoney { class Earn { public: - Earn(); - ~Earn(); + Earn(); + ~Earn(); private: - FundBoard *fund_board_{nullptr}; - HelpWindow* help_windows_{nullptr}; - StatusBar *status_bar_{nullptr}; - CommandBar *command_bar_{nullptr}; + FundBoard *fund_board_{nullptr}; + HelpWindow* help_windows_{nullptr}; + StatusBar *status_bar_{nullptr}; + CommandBar *command_bar_{nullptr}; }; } // namespace BigMoney diff --git a/include/fund.h b/include/fund.h index 4a09142..b00af27 100644 --- a/include/fund.h +++ b/include/fund.h @@ -5,14 +5,20 @@ namespace BigMoney { struct Fund { - std::string fund_code; - float fund_worth{0.0f}; - float valuation{0.0f}; - float fluctuations{0.0f}; - float share{0.0f}; - float income{0.0f}; - float sum{0.0f}; - std::string fund_name; - std::string last_update_time; + std::string fund_code; + float fund_worth{0.0f}; + float valuation{0.0f}; + float fluctuations{0.0f}; + float share{0.0f}; + float income{0.0f}; + float real_income{0.0f}; + float sum{0.0f}; + std::string fund_name; + std::string last_update_time; }; -} // namespace BigMoney \ No newline at end of file +struct FundIncome { + float income{0.0f}; + float read_income{0.0f}; + float sum{0.0f}; +}; +} // namespace BigMoney diff --git a/include/fund_board.h b/include/fund_board.h index 64ec906..62a2ab7 100644 --- a/include/fund_board.h +++ b/include/fund_board.h @@ -13,22 +13,28 @@ namespace BigMoney { class FundBoard : public Window { public: - FundBoard(int x, int y, int startx, int starty); - ~FundBoard(); - void Paint() override; - virtual bool MessageProc(const Msg &msg) override; + FundBoard(int x, int y, int startx, int starty); + ~FundBoard(); + void Paint() override; + virtual bool MessageProc(const Msg &msg) override; private: - void GetFundData(); - void LoadFundFromFile(); - static size_t WriteFunction(void *data, size_t size, size_t bytes, void *user_data); - bool UpdateFund(const Fund& fund); - bool DeleteFund(const std::string &fund_code); - bool Serialize(); + void GetFundData(); + void LoadFundFromFile(); + static size_t WriteFunction(void *data, size_t size, size_t bytes, void *user_data); + bool UpdateFund(const Fund& fund); + bool DeleteFund(const std::string &fund_code); + bool Serialize(); private: - const static std::array, 9> FIELD_WIDTH_MAP; - std::vector funds_; - CURL *curl_{nullptr}; - std::mutex fund_mutex_; - Timer *timer{nullptr}; + const static std::array, 10> FIELD_WIDTH_MAP; + std::vector funds_; + CURL *curl_{nullptr}; + std::mutex fund_mutex_; + Timer *timer{nullptr}; + uint32_t page_{0}; + uint32_t per_page_{0}; + uint32_t max_page_{0}; + std::atomic request_flag_{false}; + std::atomic running_{true}; + std::thread *request_thread_{nullptr}; }; -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/include/help.h b/include/help.h index f55ed80..43963a0 100644 --- a/include/help.h +++ b/include/help.h @@ -6,11 +6,11 @@ namespace BigMoney { class HelpWindow : public Window { public: - HelpWindow(int x, int y, int startx, int starty); - bool MessageProc(const Msg& msg) override; - void Paint() override; + HelpWindow(int x, int y, int startx, int starty); + bool MessageProc(const Msg& msg) override; + void Paint() override; private: - bool show_{ false }; + bool show_{ false }; }; -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/include/msg.h b/include/msg.h index 87c97a9..ec4c386 100644 --- a/include/msg.h +++ b/include/msg.h @@ -10,59 +10,61 @@ namespace BigMoney { enum MsgType { - kUpdateFund, - kDeleteFund, - kUpdateIncome, - kUpdateStatus, - kPaint, - kShowHelp, - kHiddenPop, - kReloadFile, - kQuit, + kUpdateFund, + kDeleteFund, + kUpdateIncome, + kUpdateStatus, + kPrePage, + kNextPage, + kPaint, + kShowHelp, + kHiddenPop, + kReloadFile, + kQuit, }; struct Msg { - MsgType msg_type; - void *lparam; - void *rparam; + MsgType msg_type; + void *lparam; + void *rparam; }; class MsgQueue { public: - bool Enqueue(const Msg& msg); - bool Dequeue(Msg *msg); - bool Empty(); + bool Enqueue(const Msg& msg); + bool Dequeue(Msg *msg); + bool Empty(); private: - std::queue msg_queue_; - std::mutex mutex_; + std::queue msg_queue_; + std::mutex mutex_; }; class MsgReactor { public: - MsgReactor(); - virtual ~MsgReactor(); - virtual bool MessageProc(const Msg &msg) = 0; + MsgReactor(); + virtual ~MsgReactor(); + virtual bool MessageProc(const Msg &msg) = 0; }; class MsgManager : public sy::Singleton { public: - inline bool Enqueue(const Msg& msg) { - return msg_queue_.Enqueue(msg); - } - inline bool Dequeue(Msg *msg) { - return msg_queue_.Dequeue(msg); - } - inline bool QueueEmpty() { - return msg_queue_.Empty(); - } - void AddReactor(MsgReactor *reactor); - void RemoveReactor(MsgReactor *reactor); - void StartMainLoop(); + inline bool Enqueue(const Msg& msg) { + return msg_queue_.Enqueue(msg); + } + inline bool Dequeue(Msg *msg) { + return msg_queue_.Dequeue(msg); + } + inline bool QueueEmpty() { + return msg_queue_.Empty(); + } + void AddReactor(MsgReactor *reactor); + void RemoveReactor(MsgReactor *reactor); + void StartMainLoop(); private: - MsgQueue msg_queue_; - std::unordered_set reactors_; - std::mutex reactors_mutex_; + MsgQueue msg_queue_; + std::unordered_set reactors_; + std::mutex reactors_mutex_; }; bool GetMsg(Msg *msg); diff --git a/include/status_bar.h b/include/status_bar.h index 0d578d7..aa41367 100644 --- a/include/status_bar.h +++ b/include/status_bar.h @@ -3,21 +3,21 @@ #include "timer.h" #include #include +#include "fund.h" namespace BigMoney { class StatusBar : Window{ public: - StatusBar(int x, int y, int startx, int starty); - ~StatusBar(); - void Paint() override; - bool MessageProc(const Msg &msg) override; + StatusBar(int x, int y, int startx, int starty); + ~StatusBar(); + void Paint() override; + bool MessageProc(const Msg &msg) override; private: - std::string show_msg_; - float income_{0.0f}; - float sum_{0.0f}; - Timer *timer_{nullptr}; - std::atomic update_{false}; - std::mutex msg_mutex_; + std::string show_msg_; + FundIncome fund_income_{0.0f, 0.0f, 0.0f}; + Timer *timer_{nullptr}; + std::atomic update_{false}; + std::mutex msg_mutex_; }; -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/include/timer.h b/include/timer.h index 97218fc..9462ea0 100644 --- a/include/timer.h +++ b/include/timer.h @@ -14,33 +14,33 @@ namespace BigMoney { class TimerManager; class Timer { -friend class TimerManager; -typedef std::function TimeoutCallback; + friend class TimerManager; + typedef std::function TimeoutCallback; public: - Timer(const TimeoutCallback &callback); - ~Timer(); - void Start(uint32_t milliseconds); - void Stop(); + Timer(const TimeoutCallback &callback); + ~Timer(); + void Start(uint32_t milliseconds); + void Stop(); private: - std::atomic running_{false}; - TimeoutCallback callback_; - std::chrono::time_point start_time_; - int timeout_{0}; + std::atomic running_{false}; + TimeoutCallback callback_; + std::chrono::time_point start_time_; + int timeout_{0}; }; class TimerManager : public sy::Singleton, public MsgReactor { public: - void AddTimer(Timer *timer); - void RemoveTimer(Timer *timer); - bool MessageProc(const Msg &msg) override; - void StartWork(); - ~TimerManager(); + void AddTimer(Timer *timer); + void RemoveTimer(Timer *timer); + bool MessageProc(const Msg &msg) override; + void StartWork(); + ~TimerManager(); private: - std::unordered_set timers_; - std::mutex timers_mutex_; - std::atomic running_{false}; - std::thread work_thread_; + std::unordered_set timers_; + std::mutex timers_mutex_; + std::atomic running_{false}; + std::thread work_thread_; }; void StartTimerLoop(); diff --git a/include/util.h b/include/util.h index 465b08f..3ef7bee 100644 --- a/include/util.h +++ b/include/util.h @@ -4,26 +4,26 @@ #include #define UPDATE_STATUS(format, ...) \ - do {\ - auto *buf = new std::vector(255);\ - snprintf(buf->data(), buf->size(), (format), ##__VA_ARGS__);\ - PostMsg({MsgType::kUpdateStatus, buf});\ - }while(0) + do {\ + auto *buf = new std::vector(255);\ + snprintf(buf->data(), buf->size(), (format), ##__VA_ARGS__);\ + PostMsg({MsgType::kUpdateStatus, buf, nullptr});\ + }while(0) #define JSON_GET(type, key, v, json)\ - do {\ - auto itr = (json).FindMember(key);\ - if (itr != (json).MemberEnd()) {\ - if (itr->value.Is##type()) {\ - (v) = itr->value.Get##type();\ - } else {\ - assert(0 && "Type mismatch, get json value fail: "#key);\ - }\ - } else {\ - assert(0 && "Cannot find key, get json value fail: "#key);\ - }\ - }while (0) + do {\ + auto itr = (json).FindMember(key);\ + if (itr != (json).MemberEnd()) {\ + if (itr->value.Is##type()) {\ + (v) = itr->value.Get##type();\ + } else {\ + assert(0 && "Type mismatch, get json value fail: "#key);\ + }\ + } else {\ + assert(0 && "Cannot find key, get json value fail: "#key);\ + }\ + }while (0) #include @@ -33,23 +33,35 @@ std::string UTF8ToANSI(const std::string &str); #define FUND_DATA_URL "http://fundgz.1234567.com.cn/js/" inline std::string GenerateFundUrl(const std::string& code) { - std::ostringstream os; - os << FUND_DATA_URL << code << ".js"; - return os.str(); + std::ostringstream os; + os << FUND_DATA_URL << code << ".js"; + return os.str(); +} + +#include "fund.h" + +#define FUND_REAL_INCOME "http://hq.sinajs.cn/list=" + +inline std::string GenerateRealIncomeUrl(const std::vector &fund_list) { + std::string url = FUND_REAL_INCOME; + for (auto &fund : fund_list) { + url.append("f_").append(fund.fund_code).append(","); + } + return url; } int StringWidth(const std::string &str) ; -int FloatWidth(float f); +int FloatWidth(float f, const std::string &format); inline bool IsNumber(const std::string &str) { - if (str.empty()) { - return false; - } - for (auto ch : str) { - if (ch > '9' || ch < '0') { - return false; - } + if (str.empty()) { + return false; + } + for (auto ch : str) { + if (ch > '9' || ch < '0') { + return false; } - return true; -} \ No newline at end of file + } + return true; +} diff --git a/include/win.h b/include/win.h index 4be5126..fae294b 100644 --- a/include/win.h +++ b/include/win.h @@ -5,45 +5,45 @@ namespace BigMoney { class Window : public MsgReactor{ public: - Window(int x = 0, int y = 0, int startx = 0, int starty = 0) - : x_(x), - y_(y), - startx_(startx), - starty_(starty) { - win_ = newwin(y_, x_, starty_, startx_); - } - bool MessageProc(const Msg &msg) override { - if (msg.msg_type == kPaint) { - if (static_cast(msg.lparam) == this) { - Paint(); - return true; - } - } - else if (msg.msg_type == kHiddenPop) { - Update(); - return false; - } - return false; - } - void UpdateNow() { + Window(int x = 0, int y = 0, int startx = 0, int starty = 0) + : x_(x), + y_(y), + startx_(startx), + starty_(starty) { + win_ = newwin(y_, x_, starty_, startx_); + } + bool MessageProc(const Msg &msg) override { + if (msg.msg_type == kPaint) { + if (static_cast(msg.lparam) == this) { Paint(); + return true; + } } - void Update() { - Msg msg{kPaint, static_cast(this), 0}; - PostMsg(msg); + else if (msg.msg_type == kHiddenPop) { + Update(); + return false; } - virtual void Paint() { }; - virtual ~Window() { - if (win_) { - delwin(win_); - } + return false; + } + void UpdateNow() { + Paint(); + } + void Update() { + Msg msg{kPaint, static_cast(this), 0}; + PostMsg(msg); + } + virtual void Paint() { }; + virtual ~Window() { + if (win_) { + delwin(win_); } + } protected: - WINDOW *win_{nullptr}; - int x_; - int y_; - int startx_; - int starty_; + WINDOW *win_{nullptr}; + int x_; + int y_; + int startx_; + int starty_; }; -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/src/colors.cc b/src/colors.cc index 422b517..74bc684 100644 --- a/src/colors.cc +++ b/src/colors.cc @@ -4,19 +4,19 @@ namespace BigMoney { void Init() { - static bool is_init = false; - if (!is_init) { - start_color(); - init_pair(kGreenBlack, COLOR_GREEN, COLOR_BLACK) ; - init_pair(kRedBlack, COLOR_RED, COLOR_BLACK); - init_pair(kWhiteBlue, COLOR_WHITE, COLOR_BLUE); - is_init = true; - } + static bool is_init = false; + if (!is_init) { + start_color(); + init_pair(kGreenBlack, COLOR_GREEN, COLOR_BLACK) ; + init_pair(kRedBlack, COLOR_RED, COLOR_BLACK); + init_pair(kWhiteBlue, COLOR_WHITE, COLOR_BLUE); + is_init = true; + } } int GetColorPair(Color color) { - Init(); - return COLOR_PAIR(color); + Init(); + return COLOR_PAIR(color); } -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/src/command_bar.cc b/src/command_bar.cc index 93881c7..02f4acc 100644 --- a/src/command_bar.cc +++ b/src/command_bar.cc @@ -4,66 +4,71 @@ #include "fund.h" #include + namespace BigMoney { CommandBar::CommandBar(int x, int y, int startx, int starty) - :Window(x, y, startx, starty){ + :Window(x, y, startx, starty){ } CommandBar::~CommandBar() { - if (listen_thread_ && listen_thread_->joinable()) { - listen_thread_->join(); - } + if (listen_thread_ && listen_thread_->joinable()) { + listen_thread_->join(); + } } void CommandBar::GetCommand() { - is_listen_ = true; - listen_thread_ = new std::thread([this]{ - std::array input_buf; - while(is_listen_) { - wclear(win_); - wgetnstr(win_, input_buf.data(), input_buf.size()); - ParseCommand(input_buf.data()); - } - }); + is_listen_ = true; + listen_thread_ = new std::thread([this]{ + std::array input_buf; + while(is_listen_) { + wclear(win_); + wgetnstr(win_, input_buf.data(), input_buf.size()); + ParseCommand(input_buf.data()); + } + }); } void CommandBar::ParseCommand(const std::string &cmd) { - if (cmd.empty()) { - return; + if (cmd.empty()) { + return; + } + std::istringstream is(cmd); + std::string action; + is >> action; + if (action == "add" || action == "update") { + std::string fund_id; + float fund_share = 0; + is >> fund_id >> fund_share; + if (IsNumber(fund_id)) { + Fund *fund = new Fund(); + fund->fund_code = fund_id; + fund->share = fund_share; + Msg msg = {kUpdateFund, fund, nullptr}; + PostMsg(msg); } - std::istringstream is(cmd); - std::string action; - is >> action; - if (action == "add" || action == "update") { - std::string fund_id; - float fund_share = 0; - is >> fund_id >> fund_share; - if (IsNumber(fund_id)) { - Fund *fund = new Fund(); - fund->fund_code = fund_id; - fund->share = fund_share; - Msg msg = {kUpdateFund, fund}; - PostMsg(msg); - } - } else if (action == "delete") { - std::string *fund_id = new std::string(); - is >> *fund_id; - if (IsNumber(fund_id->c_str()) || *fund_id == "all") { - Msg msg{kDeleteFund, fund_id}; - PostMsg(msg); - } - } else if (action == "reload") { - PostMsg({kReloadFile}); - } else if (action == "help") { - PostMsg({ kShowHelp }); - } else if (action == "q") { - PostMsg({ kHiddenPop }); - } else if (action == "quit") { - Msg msg = {kQuit, nullptr}; - is_listen_ = false; - PostMsg(msg); - } else { - UPDATE_STATUS("无效命令: %s", cmd.c_str()); + } else if (action == "delete") { + std::string *fund_id = new std::string(); + is >> *fund_id; + if (IsNumber(fund_id->c_str()) || *fund_id == "all") { + Msg msg{kDeleteFund, fund_id, nullptr}; + PostMsg(msg); } + } else if (action == "reload" || action == "r") { + PostMsg({kReloadFile, nullptr, nullptr}); + } else if (action == "help" || action == "h") { + PostMsg({kShowHelp, nullptr, nullptr}); + } else if (action == "q") { + PostMsg({kHiddenPop, nullptr, nullptr}); + } else if (action == "quit") { + Msg msg = {kQuit, nullptr, nullptr}; + is_listen_ = false; + PostMsg(msg); + } else if (action == "pre" || action == "p") { + PostMsg({kPrePage, nullptr, nullptr}); + } else if (action == "next" || action == "n") { + PostMsg({kNextPage, nullptr, nullptr}); + } else { + UPDATE_STATUS("无效命令: %s", cmd.c_str()); + } } -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/src/earn.cc b/src/earn.cc index 315b910..d6bab26 100644 --- a/src/earn.cc +++ b/src/earn.cc @@ -4,45 +4,45 @@ namespace BigMoney { Earn::Earn() { - setlocale(LC_ALL, ""); + setlocale(LC_ALL, ""); #ifdef __APPLE__ - setenv("TERM", "xterm-basic", 1); - setenv("TERMINFO", "/usr/share/terminfo", 1); + setenv("TERM", "xterm-basic", 1); + setenv("TERMINFO", "/usr/share/terminfo", 1); #endif - initscr(); - raw(); - keypad(stdscr, TRUE); - curs_set(0); - cbreak(); - int max_row = 0, max_col = 0; - getmaxyx(stdscr, max_col, max_row); + initscr(); + raw(); + keypad(stdscr, TRUE); + curs_set(0); + cbreak(); + int max_row = 0, max_col = 0; + getmaxyx(stdscr, max_col, max_row); - fund_board_ = new FundBoard(max_row, max_col - 2, 0, 0); - int help_win_width = max_row * 0.6; - int help_win_height = (max_col -2) * 0.6; - int help_win_left = (max_row - help_win_width) / 2; - int help_win_top = (max_col - help_win_height - 2) / 2; - help_windows_ = new HelpWindow(help_win_width, - help_win_height, help_win_left, help_win_top); - status_bar_ = new StatusBar(max_row, 1, 0, max_col - 2); - command_bar_ = new CommandBar(max_row, 1, 0, max_col - 1); - command_bar_->GetCommand(); + fund_board_ = new FundBoard(max_row, max_col - 2, 0, 0); + int help_win_width = max_row * 0.6; + int help_win_height = (max_col -2) * 0.6; + int help_win_left = (max_row - help_win_width) / 2; + int help_win_top = (max_col - help_win_height - 2) / 2; + help_windows_ = new HelpWindow(help_win_width, + help_win_height, help_win_left, help_win_top); + status_bar_ = new StatusBar(max_row, 1, 0, max_col - 2); + command_bar_ = new CommandBar(max_row, 1, 0, max_col - 1); + command_bar_->GetCommand(); } Earn::~Earn() { - endwin(); - if (fund_board_) { - delete fund_board_; - } - if (help_windows_) { - delete help_windows_; - } - if (status_bar_) { - delete status_bar_; - } - if (command_bar_) { - delete command_bar_; - } + endwin(); + if (fund_board_) { + delete fund_board_; + } + if (help_windows_) { + delete help_windows_; + } + if (status_bar_) { + delete status_bar_; + } + if (command_bar_) { + delete command_bar_; + } } -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/src/fund_board.cc b/src/fund_board.cc index 7859937..97c4df6 100644 --- a/src/fund_board.cc +++ b/src/fund_board.cc @@ -9,354 +9,460 @@ #include "timer.h" #include "colors.h" #include +#include +#include +#include using namespace rapidjson; namespace BigMoney { // compute content -const std::array, 9> FundBoard::FIELD_WIDTH_MAP = { - std::make_pair("编号", StringWidth("编号")), - std::make_pair("名称", StringWidth("名称")), - std::make_pair("净值", StringWidth("净值")), - std::make_pair("估值", StringWidth("估值")), - std::make_pair("持有份额", StringWidth("持有份额")), - std::make_pair("估算总值", StringWidth("估算总值")), - std::make_pair("增长率", StringWidth("增长率")), - std::make_pair("预计收益", StringWidth("预计收益")), - std::make_pair("更新时间", StringWidth("更新时间")) - }; +const std::array, 10> FundBoard::FIELD_WIDTH_MAP = { + std::make_pair("编号", StringWidth("编号")), + std::make_pair("名称", StringWidth("名称")), + std::make_pair("净值", StringWidth("净值")), + std::make_pair("估值", StringWidth("估值")), + std::make_pair("持有份额", StringWidth("持有份额")), + std::make_pair("估算总值", StringWidth("估算总值")), + std::make_pair("增长率", StringWidth("增长率")), + std::make_pair("预计收益", StringWidth("预计收益")), + std::make_pair("实际收益", StringWidth("实际收益")), + std::make_pair("更新时间", StringWidth("更新时间")) +}; size_t FundBoard::WriteFunction(void *data, size_t size, size_t bytes, void *user_data) { - size_t all_bytes = size * bytes; - std::string *str = reinterpret_cast(user_data); - if (str->max_size() > str->size() + all_bytes) { - str->append(static_cast(data), all_bytes); - return all_bytes; - } else { - return 0; - } + size_t all_bytes = size * bytes; + std::string *str = reinterpret_cast(user_data); + if (str->max_size() > str->size() + all_bytes) { + str->append(static_cast(data), all_bytes); + return all_bytes; + } else { + return 0; + } } FundBoard::FundBoard(int x, int y, int startx, int starty) - : Window(x, y, startx, starty) { - curl_global_init(CURL_GLOBAL_DEFAULT); - curl_ = curl_easy_init(); - LoadFundFromFile(); - timer = new Timer(std::bind(&FundBoard::GetFundData, this)); - timer->Start(60000); + : Window(x, y, startx, starty) { + // per page display item size + per_page_ = y - 3; + curl_global_init(CURL_GLOBAL_DEFAULT); + curl_ = curl_easy_init(); + request_thread_ = new std::thread([this]{ + while (running_) { + if (request_flag_) { + GetFundData(); + request_flag_ = false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(500)); + } + }); + timer = new Timer([this]{ + request_flag_ = true; + }); + timer->Start(60000); + LoadFundFromFile(); } void FundBoard::LoadFundFromFile() { - FILE *fp = fopen("fund.json", "rb"); - if (fp == nullptr) { - return; - } - Document doc; - std::array read_buffer; - FileReadStream is(fp, read_buffer.data(), read_buffer.size()); + FILE *fp = fopen("fund.json", "rb"); + if (fp == nullptr) { + return; + } + Document doc; + std::array read_buffer; + FileReadStream is(fp, read_buffer.data(), read_buffer.size()); - // load fund json file fail - if(doc.ParseStream(is).HasParseError() || !doc.IsArray()) { - UPDATE_STATUS("解析基金配置文件失败"); - return; - }; - fund_mutex_.lock(); - funds_.clear(); - for(auto fund_itr = doc.Begin(); fund_itr != doc.End(); fund_itr ++) { - Fund fund; - JSON_GET(String, "fund_code", fund.fund_code, fund_itr->GetObject()); - JSON_GET(String, "fund_name", fund.fund_name, fund_itr->GetObject()); - JSON_GET(String, "fund_last_update", fund.last_update_time, fund_itr->GetObject()); - JSON_GET(Double, "fund_share", fund.share, fund_itr->GetObject()); - funds_.push_back(fund); - } - fund_mutex_.unlock(); + // load fund json file fail + if(doc.ParseStream(is).HasParseError() || !doc.IsArray()) { fclose(fp); - GetFundData(); + UPDATE_STATUS("解析基金配置文件失败"); + return; + }; + fund_mutex_.lock(); + funds_.clear(); + for(auto fund_itr = doc.Begin(); fund_itr != doc.End(); fund_itr ++) { + Fund fund; + JSON_GET(String, "fund_code", fund.fund_code, fund_itr->GetObject()); + JSON_GET(String, "fund_name", fund.fund_name, fund_itr->GetObject()); + JSON_GET(String, "fund_last_update", fund.last_update_time, fund_itr->GetObject()); + JSON_GET(Double, "fund_share", fund.share, fund_itr->GetObject()); + funds_.push_back(fund); + } + fund_mutex_.unlock(); + request_flag_ = true; + fclose(fp); } FundBoard::~FundBoard() { - if (timer) { - timer->Stop(); - delete timer; - } - if (curl_) { - curl_easy_cleanup(curl_); - } + if(request_thread_) { + request_thread_->join(); + delete request_thread_; + } + if (timer) { + timer->Stop(); + delete timer; + } + if (curl_) { + curl_easy_cleanup(curl_); + } - curl_global_cleanup(); + curl_global_cleanup(); } void FundBoard::GetFundData() { - std::lock_guard lock(fund_mutex_); - for (auto &fund : funds_) { - UPDATE_STATUS("请求基金数据: %s", fund.fund_code.c_str()); - auto resp_buf = new std::string(); - auto http_response = std::unique_ptr(resp_buf); - std::string url = GenerateFundUrl(fund.fund_code); - curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); - curl_easy_setopt(curl_, - CURLOPT_WRITEFUNCTION, FundBoard::WriteFunction); - curl_easy_setopt(curl_, - CURLOPT_WRITEDATA, - static_cast(http_response.get())); - auto curl_code = curl_easy_perform(curl_); - if (curl_code == CURLE_OK) { - if (http_response->empty()) { - // empty resposne - UPDATE_STATUS("请求失败, 基金: %s", fund.fund_code.c_str()); - continue; - } else { - Document doc; - // remove garbage char - size_t begin_offset = http_response->find("{"); - size_t end_offset = http_response->find_last_of(")"); - if (begin_offset == http_response->npos || - end_offset == http_response->npos) { - UPDATE_STATUS("基金数据格式错误, 基金: %s", fund.fund_code.c_str()); - continue; - } - http_response->at(end_offset)= 0; - if(doc.Parse(http_response->c_str() + begin_offset).HasParseError()) { - UPDATE_STATUS("解析数据失败, 基金: %s", fund.fund_code.c_str()); - continue; - } - if (!doc.IsObject()) { - UPDATE_STATUS("数据格式无效, 基金: %s", fund.fund_code.c_str()); - } - std::string fund_code; - JSON_GET(String, "fundcode", fund_code, doc); - if (fund_code != fund.fund_code) { - UPDATE_STATUS("基金编码不匹配\n"); - } - std::string valuation; - JSON_GET(String, "gsz", valuation, doc); - fund.valuation = std::atof(valuation.c_str()); + std::lock_guard lock(fund_mutex_); + for (auto &fund : funds_) { + UPDATE_STATUS("请求基金数据: %s", fund.fund_code.c_str()); + auto resp_buf = new std::string(); + auto http_response = std::unique_ptr(resp_buf); + std::string url = GenerateFundUrl(fund.fund_code); + curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl_, + CURLOPT_WRITEFUNCTION, FundBoard::WriteFunction); + curl_easy_setopt(curl_, + CURLOPT_WRITEDATA, + static_cast(http_response.get())); + auto curl_code = curl_easy_perform(curl_); + if (curl_code == CURLE_OK) { + if (http_response->empty()) { + // empty resposne + UPDATE_STATUS("请求失败, 基金: %s", fund.fund_code.c_str()); + continue; + } else { + Document doc; + // remove garbage char + size_t begin_offset = http_response->find("{"); + size_t end_offset = http_response->find_last_of(")"); + if (begin_offset == http_response->npos || + end_offset == http_response->npos) { + UPDATE_STATUS("基金数据格式错误, 基金: %s", fund.fund_code.c_str()); + continue; + } + http_response->at(end_offset)= 0; + if(doc.Parse(http_response->c_str() + begin_offset).HasParseError()) { + UPDATE_STATUS("解析数据失败, 基金: %s", fund.fund_code.c_str()); + continue; + } + if (!doc.IsObject()) { + UPDATE_STATUS("数据格式无效, 基金: %s", fund.fund_code.c_str()); + } + std::string fund_code; + JSON_GET(String, "fundcode", fund_code, doc); + if (fund_code != fund.fund_code) { + UPDATE_STATUS("基金编码不匹配\n"); + } + std::string valuation; + JSON_GET(String, "gsz", valuation, doc); + fund.valuation = static_cast(std::atof(valuation.c_str())); - std::string fluctuations; - JSON_GET(String, "gszzl", fluctuations, doc); - fund.fluctuations = std::atof(fluctuations.c_str()); + std::string fluctuations; + JSON_GET(String, "gszzl", fluctuations, doc); + fund.fluctuations = static_cast(std::atof(fluctuations.c_str())); + + std::string dwjz; + JSON_GET(String, "dwjz", dwjz, doc); + fund.fund_worth = std::atof(dwjz.c_str()); + if (fund.share > 0) { + fund.income = fund.share * (fund.valuation - fund.fund_worth); + fund.sum = fund.share * fund.valuation; + } - std::string dwjz; - JSON_GET(String, "dwjz", dwjz, doc); - fund.fund_worth = std::atof(dwjz.c_str()); - if (fund.share > 0) { - fund.income = fund.share * (fund.valuation - fund.fund_worth); - fund.sum = fund.share * fund.valuation; - } + JSON_GET(String, "name", fund.fund_name, doc); + JSON_GET(String, "gztime", fund.last_update_time, doc); + auto date_offset = fund.last_update_time.find_first_of("-"); + if (date_offset != std::string::npos) { + fund.last_update_time = fund.last_update_time.substr(date_offset + 1); + } - JSON_GET(String, "name", fund.fund_name, doc); - JSON_GET(String, "gztime", fund.last_update_time, doc); - auto date_offset = fund.last_update_time.find_first_of("-"); - if (date_offset != std::string::npos) { - fund.last_update_time = fund.last_update_time.substr(date_offset + 1); - } + } + } else { + UPDATE_STATUS("网络请求出现错误"); + } + } - } - } else { - UPDATE_STATUS("网络请求出现错误"); + FundIncome *fund_income = new FundIncome(); + auto now = std::time(nullptr); + auto hour = std::localtime(&now)->tm_hour; + if (hour > 14 && !funds_.empty()) { + UPDATE_STATUS("计算实际收益..."); + std::string real_income_url = GenerateRealIncomeUrl(funds_); + auto resp_buf = new std::string(); + auto http_response = std::unique_ptr(resp_buf); + curl_easy_setopt(curl_, CURLOPT_URL, real_income_url.c_str()); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, http_response.get()); + if(curl_easy_perform(curl_) == CURLE_OK) { + std::istringstream is(http_response->c_str()); + std::string line; + std::unordered_map fund_price_map; + while (std::getline(is, line)) { + if (!line.empty()) { + std::array fund_code; + float price = 0.0f; + if (sscanf(line.c_str(), + "%*[^0-9]%[0-9]=\"%*[^,],%f", + fund_code.data(), &price) == 2) { + fund_price_map[fund_code.data()] = price; + } + } + } + for (auto &fund : funds_) { + auto itr = fund_price_map.find(fund.fund_code); + if (itr != fund_price_map.end()) { + if (fund.fund_worth != itr->second) { + fund.real_income = fund.share * (itr->second - fund.fund_worth); + } } + fund_income->income += fund.income; + fund_income->read_income += fund.real_income; + fund_income->sum += fund.share * fund.valuation; + } } - // network request finished, send msg for update ui - Update(); - float income = 0.0f; - float sum = 0.0f; + } else { for (auto &fund : funds_) { - income += fund.income; - sum += fund.share * fund.valuation; + fund_income->income += fund.income; + fund_income->sum += fund.share * fund.valuation; } - Msg msg; - msg.msg_type = kUpdateIncome; - msg.lparam = reinterpret_cast((*reinterpret_cast(&income))); - msg.rparam = reinterpret_cast((*reinterpret_cast(&sum))); - PostMsg(msg); + } + // network request finished, send msg for update ui + Update(); + PostMsg({kUpdateIncome, static_cast(fund_income), nullptr}); } bool FundBoard::UpdateFund(const Fund& fund) { - bool changed = false; - if (fund.fund_code.empty()) { - return changed; - } - fund_mutex_.lock(); - auto itr = funds_.begin(); - while(itr != funds_.end()) { - if (itr->fund_code == fund.fund_code) { - *itr = fund; - changed = true; - break; - } - itr ++; - } - // new fund - if (itr == funds_.end()) { - changed = true; - funds_.push_back(fund); - } - fund_mutex_.unlock(); - if (changed) { - // write fund info to file - Serialize(); - // update local fund info from network - GetFundData(); - } + bool changed = false; + if (fund.fund_code.empty()) { return changed; + } + fund_mutex_.lock(); + auto itr = funds_.begin(); + while(itr != funds_.end()) { + if (itr->fund_code == fund.fund_code) { + *itr = fund; + changed = true; + break; + } + itr ++; + } + // new fund + if (itr == funds_.end()) { + changed = true; + funds_.push_back(fund); + } + fund_mutex_.unlock(); + if (changed) { + // write fund info to file + Serialize(); + // update local fund info from network + request_flag_ = true; + } + return changed; } bool FundBoard::DeleteFund(const std::string& fund_code) { - bool changed = false; - fund_mutex_.lock(); - if (fund_code == "all") { - funds_.clear(); - changed = true; - } else { - for (auto itr = funds_.begin(); itr != funds_.end(); itr++) { - if (itr->fund_code == fund_code) { - funds_.erase(itr); - changed = true; - break; - } - } - } - fund_mutex_.unlock(); - if (changed) { - Update(); - Serialize(); + bool changed = false; + fund_mutex_.lock(); + if (fund_code == "all") { + funds_.clear(); + changed = true; + } else { + for (auto itr = funds_.begin(); itr != funds_.end(); itr++) { + if (itr->fund_code == fund_code) { + funds_.erase(itr); + changed = true; + break; + } } - return changed; + } + fund_mutex_.unlock(); + if (changed) { + Update(); + Serialize(); + } + return changed; } void FundBoard::Paint() { - wclear(win_); - int x_offset = 0, y_offset = 0; - auto field_width_map = FIELD_WIDTH_MAP; - std::lock_guard lock(fund_mutex_); - for(auto &fund: funds_) { - int width = StringWidth(fund.fund_code); - field_width_map[0].second = std::max(field_width_map[0].second, width); - width = StringWidth(fund.fund_name); - field_width_map[1].second = std::max(field_width_map[1].second, width); - width = FloatWidth(fund.fund_worth); - field_width_map[2].second = std::max(field_width_map[2].second, width); - width = FloatWidth(fund.valuation); - field_width_map[3].second = std::max(field_width_map[3].second, width); - width = FloatWidth(fund.share); - field_width_map[4].second = std::max(field_width_map[4].second, width); - width = FloatWidth(fund.sum); - field_width_map[5].second = std::max(field_width_map[5].second, width); - width = FloatWidth(fund.fluctuations); - field_width_map[6].second = std::max(field_width_map[6].second, width); - width = FloatWidth(fund.income); - field_width_map[7].second = std::max(field_width_map[7].second, width); - width = StringWidth(fund.last_update_time); - field_width_map[8].second = std::max(field_width_map[8].second, width); - } - for (auto &field : field_width_map) { - mvwprintw(win_, 0, x_offset, _TEXT(field.first.c_str())); - x_offset += field.second + 2; - } - mvwhline(win_, ++y_offset, 0, '-', x_); + wclear(win_); + int x_offset = 0, y_offset = 0; + auto field_width_map = FIELD_WIDTH_MAP; + + std::lock_guard lock(fund_mutex_); + max_page_ = static_cast(ceil(1.0f * funds_.size() / per_page_)) - 1; + + uint32_t start_offset = page_ * per_page_; + uint32_t end_offset = 0; + if (page_ < max_page_) { + end_offset = (page_ + 1) * per_page_; + } else if (page_ == max_page_) { + end_offset = funds_.size(); + } + + for(auto index = start_offset; index < end_offset; index ++){ + auto &fund = funds_[index]; + int width = StringWidth(fund.fund_code); + field_width_map[0].second = std::max(field_width_map[0].second, width); + width = StringWidth(fund.fund_name); + field_width_map[1].second = std::max(field_width_map[1].second, width); + width = FloatWidth(fund.fund_worth, "%.3f"); + field_width_map[2].second = std::max(field_width_map[2].second, width); + width = FloatWidth(fund.valuation, "%.3f"); + field_width_map[3].second = std::max(field_width_map[3].second, width); + width = FloatWidth(fund.share, "%.3f"); + field_width_map[4].second = std::max(field_width_map[4].second, width); + width = FloatWidth(fund.sum, "%.3f"); + field_width_map[5].second = std::max(field_width_map[5].second, width); + width = FloatWidth(fund.fluctuations, "%.3f"); + field_width_map[6].second = std::max(field_width_map[6].second, width); + width = FloatWidth(fund.income, "%.3f"); + field_width_map[7].second = std::max(field_width_map[7].second, width); + width = FloatWidth(fund.real_income, "%.3f"); + field_width_map[8].second = std::max(field_width_map[8].second, width); + width = StringWidth(fund.last_update_time); + field_width_map[9].second = std::max(field_width_map[9].second, width); + } + for (auto &field : field_width_map) { + mvwprintw(win_, 0, x_offset, _TEXT(field.first.c_str())); + x_offset += field.second + 2; + } + mvwhline(win_, ++y_offset, 0, '-', x_); #ifdef _WIN32 - // refresh on windows for pdcurses bug(workaround) - wrefresh(win_); + // refresh on windows for pdcurses bug(workaround) + wrefresh(win_); #endif // _WIN32 - // compute float value format - std::array format_table; - std::array format_buffer; - for (size_t i = 2, j = 0; i < 8; i ++, j ++) { - memset(format_buffer.data(), 0, format_buffer.size()); - snprintf(format_buffer.data(), format_buffer.size(), "%%%d.3f", field_width_map[i].second); - format_table[j] = std::string(format_buffer.data()); + // compute float value format + std::array format_table; + std::array format_buffer; + for (size_t i = 2, j = 0; i < 9; i ++, j ++) { + memset(format_buffer.data(), 0, format_buffer.size()); + snprintf(format_buffer.data(), format_buffer.size(), "%%%d.3f", field_width_map[i].second); + format_table[j] = std::string(format_buffer.data()); + } + + for(auto index = start_offset; index < end_offset; index ++){ + auto &fund = funds_[index]; + x_offset = 0; + y_offset ++; + mvwprintw(win_, y_offset, x_offset, fund.fund_code.c_str()); + x_offset += field_width_map[0].second + 2; + mvwprintw(win_, y_offset, x_offset, _TEXT(fund.fund_name.c_str())); + x_offset += field_width_map[1].second + 2; + mvwprintw(win_, y_offset, x_offset, format_table[0].c_str(), fund.fund_worth); + x_offset += field_width_map[2].second + 2; + mvwprintw(win_, y_offset, x_offset, format_table[1].c_str(), fund.valuation); + x_offset += field_width_map[3].second + 2; + mvwprintw(win_, y_offset, x_offset, format_table[2].c_str(), fund.share); + x_offset += field_width_map[4].second + 2; + if (fund.fluctuations > 0) { + wattron(win_, GetColorPair(kRedBlack)); + } else if (fund.fluctuations < 0){ + wattron(win_, GetColorPair(kGreenBlack)); + } + mvwprintw(win_, y_offset, x_offset, format_table[3].c_str(), fund.sum); + x_offset += field_width_map[5].second + 2; + mvwprintw(win_, y_offset, x_offset, format_table[4].c_str(), fund.fluctuations); + x_offset += field_width_map[6].second + 2; + mvwprintw(win_, y_offset, x_offset, format_table[5].c_str(), fund.income); + x_offset += field_width_map[7].second + 2; + if (fund.fluctuations > 0) { + wattroff(win_, GetColorPair(kRedBlack)); + } else if(fund.fluctuations < 0){ + wattroff(win_, GetColorPair(kGreenBlack)); } - for(auto &fund: funds_) { - x_offset = 0; - y_offset ++; - mvwprintw(win_, y_offset, x_offset, fund.fund_code.c_str()); - x_offset += field_width_map[0].second + 2; - mvwprintw(win_, y_offset, x_offset, _TEXT(fund.fund_name.c_str())); - x_offset += field_width_map[1].second + 2; - mvwprintw(win_, y_offset, x_offset, format_table[0].c_str(), fund.fund_worth); - x_offset += field_width_map[2].second + 2; - mvwprintw(win_, y_offset, x_offset, format_table[1].c_str(), fund.valuation); - x_offset += field_width_map[3].second + 2; - mvwprintw(win_, y_offset, x_offset, format_table[2].c_str(), fund.share); - x_offset += field_width_map[4].second + 2; - if (fund.fluctuations > 0) { - wattron(win_, GetColorPair(kRedBlack)); - } else if (fund.fluctuations < 0){ - wattron(win_, GetColorPair(kGreenBlack)); - } - mvwprintw(win_, y_offset, x_offset, format_table[3].c_str(), fund.sum); - x_offset += field_width_map[5].second + 2; - mvwprintw(win_, y_offset, x_offset, format_table[4].c_str(), fund.fluctuations); - x_offset += field_width_map[6].second + 2; - mvwprintw(win_, y_offset, x_offset, format_table[5].c_str(), fund.income); - x_offset += field_width_map[7].second + 2; - if (fund.fluctuations > 0) { - wattroff(win_, GetColorPair(kRedBlack)); - } else if(fund.fluctuations < 0){ - wattroff(win_, GetColorPair(kGreenBlack)); - } - mvwprintw(win_, y_offset, x_offset, fund.last_update_time.c_str()); + if (fund.real_income > 0) { + wattron(win_, GetColorPair(kRedBlack)); + } else if (fund.real_income< 0){ + wattron(win_, GetColorPair(kGreenBlack)); } - wrefresh(win_); + mvwprintw(win_, y_offset, x_offset, format_table[6].c_str(), fund.real_income); + x_offset += field_width_map[8].second + 2; + if (fund.real_income > 0) { + wattroff(win_, GetColorPair(kRedBlack)); + } else if(fund.real_income < 0){ + wattroff(win_, GetColorPair(kGreenBlack)); + } + mvwprintw(win_, y_offset, x_offset, fund.last_update_time.c_str()); + } + wrefresh(win_); } + bool FundBoard::Serialize() { - FILE *fp = fopen("fund.json", "wb"); - std::array write_buffer; - FileWriteStream ws(fp, write_buffer.data(), write_buffer.size()); - PrettyWriter writer(ws); - std::lock_guard lock(fund_mutex_); - writer.StartArray(); - for (auto &fund : funds_) { - writer.StartObject(); - writer.Key("fund_code"); - writer.String(fund.fund_code.c_str()); - writer.Key("fund_name"); - writer.String(fund.fund_name.c_str()); - writer.Key("fund_last_update"); - writer.String(fund.last_update_time.c_str()); - writer.Key("fund_share"); - writer.Double(fund.share); - writer.EndObject(); - } - writer.EndArray(); - fclose(fp); - UPDATE_STATUS("保存配置文件完成"); - return true; + FILE *fp = fopen("fund.json", "wb"); + std::array write_buffer; + FileWriteStream ws(fp, write_buffer.data(), write_buffer.size()); + PrettyWriter writer(ws); + std::lock_guard lock(fund_mutex_); + writer.StartArray(); + for (auto &fund : funds_) { + writer.StartObject(); + writer.Key("fund_code"); + writer.String(fund.fund_code.c_str()); + writer.Key("fund_name"); + writer.String(fund.fund_name.c_str()); + writer.Key("fund_last_update"); + writer.String(fund.last_update_time.c_str()); + writer.Key("fund_share"); + writer.Double(fund.share); + writer.EndObject(); + } + writer.EndArray(); + fclose(fp); + UPDATE_STATUS("保存配置文件完成"); + return true; } bool FundBoard::MessageProc(const Msg &msg) { - bool processed = false; - switch(msg.msg_type) { - case kUpdateFund: { - Fund *fund = reinterpret_cast(msg.lparam); - // update fund fail - UPDATE_STATUS("添加基金: %s", fund->fund_code.c_str()); - UpdateFund(*fund); - delete fund; - processed = true; - break; - } - case kDeleteFund: { - std::string *fund_code = reinterpret_cast(msg.lparam); - UPDATE_STATUS("删除基金: %s", fund_code->c_str()); - DeleteFund(*fund_code); - delete fund_code; - processed = true; - break; - } - case kReloadFile: { - UPDATE_STATUS("重新加载配置文件"); - LoadFundFromFile(); - processed = true; - break; - } - default: { - processed = Window::MessageProc(msg); - break; - } + bool processed = false; + switch(msg.msg_type) { + case kUpdateFund: { + Fund *fund = reinterpret_cast(msg.lparam); + // update fund fail + UPDATE_STATUS("添加基金: %s", fund->fund_code.c_str()); + UpdateFund(*fund); + delete fund; + processed = true; + break; + } + case kDeleteFund: { + std::string *fund_code = reinterpret_cast(msg.lparam); + UPDATE_STATUS("删除基金: %s", fund_code->c_str()); + DeleteFund(*fund_code); + delete fund_code; + processed = true; + break; + } + case kReloadFile: { + UPDATE_STATUS("重新加载配置文件"); + LoadFundFromFile(); + processed = true; + break; + } + case kPrePage: { + if(page_ > 0) { + page_ --; + Update(); + } + UPDATE_STATUS("[%u, %u]", page_ + 1, max_page_ + 1); + processed = true; + break; + } + case kNextPage: { + if(page_ < max_page_) { + page_ ++; + Update(); + } + UPDATE_STATUS("[%u, %u]", page_ + 1, max_page_ + 1); + processed = true; + break; + } + case kQuit: { + running_ = false; + } + default: { + processed = Window::MessageProc(msg); + break; } - return processed; + } + return processed; } -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/src/help.cc b/src/help.cc index 6111d31..f312142 100644 --- a/src/help.cc +++ b/src/help.cc @@ -4,48 +4,51 @@ namespace BigMoney { -static const std::array HELP_INFO = { - "使用方法:", - "\t添加/修改基金: \t\tupdate <基金编号> <持有份额>", - "\t删除基金: \t\tdelete <基金编号>", - "\t删除全部: \t\tdelete all", - "\t重新加载: \t\treload", - "\t关闭: \t\t\tq", - "\t退出程序: \t\tquit", - "\t帮助: \t\t\thelp" +static const std::array HELP_INFO = { + "使用方法:", + "\t添加/修改基金: \t\tupdate <基金编号> <持有份额>", + "\t删除基金: \t\tdelete <基金编号>", + "\t删除全部: \t\tdelete all", + "\t重新加载: \t\treload", + "\t上一页: \t\tpre", + "\t下一页: \t\tnext", + "\t关闭: \t\t\tq", + "\t退出程序: \t\tquit", + "\t帮助: \t\t\thelp" }; + static const char* GITHUB = "GitHub:\thttps://github.com/hanhan-GKD/BigMoney"; HelpWindow::HelpWindow(int x, int y, int startx, int starty) - :Window(x, y, startx, starty){ + :Window(x, y, startx, starty){ } bool HelpWindow::MessageProc(const Msg& msg) { - bool processed = false; - if (msg.msg_type == kHiddenPop) { - show_ = false; - UpdateNow(); - } - else if (msg.msg_type == kShowHelp) { - show_ = true; - Update(); - processed = true; - } else { - processed = Window::MessageProc(msg); - } - return processed; + bool processed = false; + if (msg.msg_type == kHiddenPop) { + show_ = false; + UpdateNow(); + } + else if (msg.msg_type == kShowHelp) { + show_ = true; + Update(); + processed = true; + } else { + processed = Window::MessageProc(msg); + } + return processed; } void HelpWindow::Paint() { - wclear(win_); - if (show_) { - wborder(win_, '|', '|', '-', '-', '+', '+', '+', '+'); - int y_offset = 1; - for (auto item : HELP_INFO) { - mvwprintw(win_, y_offset, 1, _TEXT(item)); - y_offset++; - } - mvwprintw(win_, y_ - 2, 1, _TEXT(GITHUB)); - } - wrefresh(win_); + wclear(win_); + if (show_) { + wborder(win_, '|', '|', '-', '-', '+', '+', '+', '+'); + int y_offset = 1; + for (auto item : HELP_INFO) { + mvwprintw(win_, y_offset, 1, _TEXT(item)); + y_offset++; + } + mvwprintw(win_, y_ - 2, 1, _TEXT(GITHUB)); + } + wrefresh(win_); } -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/src/main.cc b/src/main.cc index 6792e99..9da8368 100644 --- a/src/main.cc +++ b/src/main.cc @@ -6,7 +6,7 @@ using namespace BigMoney; int main() { - Earn earn; - StartMainLoop(); - return 0; -} \ No newline at end of file + Earn earn; + StartMainLoop(); + return 0; +} diff --git a/src/msg.cc b/src/msg.cc index bcf35e3..266e593 100644 --- a/src/msg.cc +++ b/src/msg.cc @@ -8,77 +8,77 @@ namespace BigMoney { bool MsgQueue::Enqueue(const Msg &msg) { - std::lock_guard lock(mutex_); - msg_queue_.push(msg); - return true; + std::lock_guard lock(mutex_); + msg_queue_.push(msg); + return true; } bool MsgQueue::Dequeue(Msg *msg) { - std::lock_guard lock(mutex_); - if (msg_queue_.empty()) { - return false; - } - *msg = msg_queue_.front(); - msg_queue_.pop(); - return true; + std::lock_guard lock(mutex_); + if (msg_queue_.empty()) { + return false; + } + *msg = msg_queue_.front(); + msg_queue_.pop(); + return true; } bool MsgQueue::Empty() { - std::lock_guard lock(mutex_); - return msg_queue_.empty(); + std::lock_guard lock(mutex_); + return msg_queue_.empty(); } MsgReactor::MsgReactor() { - MsgManager::instance().AddReactor(this); + MsgManager::instance().AddReactor(this); } MsgReactor::~MsgReactor() { - MsgManager::instance().RemoveReactor(this); + MsgManager::instance().RemoveReactor(this); } void MsgManager::AddReactor(MsgReactor *reactor) { - std::lock_guard lock(reactors_mutex_); - reactors_.insert(reactor); + std::lock_guard lock(reactors_mutex_); + reactors_.insert(reactor); } void MsgManager::RemoveReactor(MsgReactor *reactor) { - std::lock_guard lock(reactors_mutex_); - reactors_.erase(reactor); + std::lock_guard lock(reactors_mutex_); + reactors_.erase(reactor); } bool GetMsg(Msg *msg) { - if (MsgManager::instance().QueueEmpty()) { - return false; - } - return MsgManager::instance().Dequeue(msg); + if (MsgManager::instance().QueueEmpty()) { + return false; + } + return MsgManager::instance().Dequeue(msg); } bool PostMsg(const Msg &msg) { - return MsgManager::instance().Enqueue(msg); + return MsgManager::instance().Enqueue(msg); } void MsgManager::StartMainLoop() { - for(;;) { - if(QueueEmpty()) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - continue; - } - Msg msg; - if (Dequeue(&msg)) { - for (auto reactor : reactors_) { - if (reactor->MessageProc(msg)) { - break; - } - } - if (msg.msg_type == kQuit) { - break; - } + for(;;) { + if(QueueEmpty()) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + Msg msg; + if (Dequeue(&msg)) { + for (auto reactor : reactors_) { + if (reactor->MessageProc(msg)) { + break; } - + } + if (msg.msg_type == kQuit) { + break; + } } + + } } void StartMainLoop() { - StartTimerLoop(); - MsgManager msn; - MsgManager::instance().StartMainLoop(); + StartTimerLoop(); + MsgManager msn; + MsgManager::instance().StartMainLoop(); } } // namespace BigMoney diff --git a/src/status_bar.cc b/src/status_bar.cc index a09e176..e92053e 100644 --- a/src/status_bar.cc +++ b/src/status_bar.cc @@ -6,58 +6,61 @@ namespace BigMoney { StatusBar::StatusBar(int x, int y, int startx, int starty) - :Window(x, y, startx, starty){ - wbkgd(win_, GetColorPair(kWhiteBlue)); - Update(); - timer_ = new Timer([this]{ - if (!update_) { - std::lock_guard lock(msg_mutex_); - show_msg_.clear(); - Update(); - } - update_ = false; - }); - timer_->Start(3000); + :Window(x, y, startx, starty){ + wbkgd(win_, GetColorPair(kWhiteBlue)); + Update(); + timer_ = new Timer([this]{ + if (!update_) { + std::lock_guard lock(msg_mutex_); + show_msg_.clear(); + Update(); + } + update_ = false; + }); + timer_->Start(3000); } StatusBar::~StatusBar() { - if (timer_) { - timer_->Stop(); - } - delete timer_; + if (timer_) { + timer_->Stop(); + } + delete timer_; } void StatusBar::Paint() { - update_ = true; - wclear(win_); - std::vector output_buf(x_ + 2); - msg_mutex_.lock(); - wprintw(win_, _TEXT(show_msg_.c_str())); - msg_mutex_.unlock(); - snprintf(output_buf.data(), output_buf.size(), " 今日收益: %.2f, 估算总值: %.2f ", income_, sum_); - int width = StringWidth(output_buf.data()); - mvwprintw(win_, 0, x_ - width, _TEXT(output_buf.data())); - wrefresh(win_); + update_ = true; + wclear(win_); + std::vector output_buf(x_ + 2); + msg_mutex_.lock(); + wprintw(win_, _TEXT(show_msg_.c_str())); + msg_mutex_.unlock(); + snprintf(output_buf.data(), output_buf.size(), + " 估算收益: %.2f 实际收益: %.2f 估算总值: %.2f ", + fund_income_.income, fund_income_.read_income, fund_income_.sum); + int width = StringWidth(output_buf.data()); + mvwprintw(win_, 0, x_ - width, _TEXT(output_buf.data())); + wrefresh(win_); } bool StatusBar::MessageProc(const Msg &msg) { - bool processed = false; - if (msg.msg_type == kUpdateIncome) { - income_ = *reinterpret_cast(const_cast(&msg.lparam)); - sum_ = *reinterpret_cast(const_cast(&msg.rparam)); - Update(); - processed = true; - } else if (msg.msg_type == kUpdateStatus){ - auto *str = reinterpret_cast*>(msg.lparam); - msg_mutex_.lock(); - show_msg_ = str->data(); - msg_mutex_.unlock(); - delete str; - Update(); - processed = true; - } else { - processed = Window::MessageProc(msg); - } - return processed; + bool processed = false; + if (msg.msg_type == kUpdateIncome) { + auto fund_income = static_cast(msg.lparam); + fund_income_ = *fund_income; + delete fund_income; + Update(); + processed = true; + } else if (msg.msg_type == kUpdateStatus){ + auto *str = reinterpret_cast*>(msg.lparam); + msg_mutex_.lock(); + show_msg_ = str->data(); + msg_mutex_.unlock(); + delete str; + Update(); + processed = true; + } else { + processed = Window::MessageProc(msg); + } + return processed; } -} // namespace BigMoney \ No newline at end of file +} // namespace BigMoney diff --git a/src/timer.cc b/src/timer.cc index 45fb243..5e493f6 100644 --- a/src/timer.cc +++ b/src/timer.cc @@ -8,67 +8,67 @@ using namespace std::chrono; namespace BigMoney { Timer::Timer(const TimeoutCallback &callback) - : callback_(callback){ - TimerManager::instance().AddTimer(this); + : callback_(callback){ + TimerManager::instance().AddTimer(this); } void Timer::Start(uint32_t milliseconds) { - running_ = true; - timeout_ = milliseconds; - start_time_ = system_clock::now(); + running_ = true; + timeout_ = milliseconds; + start_time_ = system_clock::now(); } void Timer::Stop() { - running_ = false; + running_ = false; } Timer::~Timer() { - TimerManager::instance().RemoveTimer(this); + TimerManager::instance().RemoveTimer(this); } void TimerManager::AddTimer(Timer *timer) { - std::lock_guard lock(timers_mutex_); - timers_.insert(timer); + std::lock_guard lock(timers_mutex_); + timers_.insert(timer); } void TimerManager::RemoveTimer(Timer *timer) { - std::lock_guard lock(timers_mutex_); - timers_.erase(timer); + std::lock_guard lock(timers_mutex_); + timers_.erase(timer); } void TimerManager::StartWork() { - running_ = true; - work_thread_ = std::thread([this] { - while (running_) { - timers_mutex_.lock(); - auto now = system_clock::now(); - for (auto timer : timers_) { - if (timer->running_) { - auto elapsed = duration_cast(now - timer->start_time_).count(); - if (elapsed > timer->timeout_) { - timer->callback_(); - timer->start_time_ = now; - } - } - } - timers_mutex_.unlock(); - std::this_thread::sleep_for(milliseconds(200)); + running_ = true; + work_thread_ = std::thread([this] { + while (running_) { + timers_mutex_.lock(); + auto now = system_clock::now(); + for (auto timer : timers_) { + if (timer->running_) { + auto elapsed = duration_cast(now - timer->start_time_).count(); + if (elapsed > timer->timeout_) { + timer->callback_(); + timer->start_time_ = now; + } } - }); + } + timers_mutex_.unlock(); + std::this_thread::sleep_for(milliseconds(200)); + } + }); } TimerManager::~TimerManager() { - if (work_thread_.joinable()) { - work_thread_.join(); - } + if (work_thread_.joinable()) { + work_thread_.join(); + } } bool TimerManager::MessageProc(const Msg &msg) { - if (msg.msg_type == kQuit) { - running_ = false; - } - return false; + if (msg.msg_type == kQuit) { + running_ = false; + } + return false; } void StartTimerLoop() { - TimerManager::instance().StartWork(); + TimerManager::instance().StartWork(); } - -} // namespace BigMoney \ No newline at end of file + +} // namespace BigMoney diff --git a/src/util.cc b/src/util.cc index 153eb0d..95b30a8 100644 --- a/src/util.cc +++ b/src/util.cc @@ -5,57 +5,57 @@ int StringWidth(const std::string &str) { - int width = 0; - for (size_t i = 0; i < str.size();){ - char ch = str[i]; - if (!(ch & 0x80)) { - width += 1; - i ++; - } else { - width += 2; - if ((ch & 0xE0) == 0xC0) { - i += 2; - } else if ((ch & 0xF0) == 0xE0) { - i += 3; - } else if ((ch & 0xF8) == 0xF0) { - i += 4; - } else { - return -1; - } - } + int width = 0; + for (size_t i = 0; i < str.size();){ + char ch = str[i]; + if (!(ch & 0x80)) { + width += 1; + i ++; + } else { + width += 2; + if ((ch & 0xE0) == 0xC0) { + i += 2; + } else if ((ch & 0xF0) == 0xE0) { + i += 3; + } else if ((ch & 0xF8) == 0xF0) { + i += 4; + } else { + return -1; + } } - return width; + } + return width; } -int FloatWidth(float f) { - std::array format_buf; - snprintf(format_buf.data(), format_buf.size(), "%.3f", f); - return strlen(format_buf.data()); +int FloatWidth(float f, const std::string &format) { + std::array format_buf; + snprintf(format_buf.data(), format_buf.size(), format.c_str(), f); + return strlen(format_buf.data()); } #ifdef _WIN32 #include std::string UTF8ToANSI(const std::string &str) { - BSTR bstr; - char *psz = nullptr; - int length; - char * utf8_str = nullptr; + BSTR bstr; + char *psz = nullptr; + int length; + char * utf8_str = nullptr; - length = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), nullptr, 0); - bstr = SysAllocStringLen(nullptr, length); + length = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), nullptr, 0); + bstr = SysAllocStringLen(nullptr, length); - MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), bstr, length); + MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.size(), bstr, length); - length = WideCharToMultiByte(CP_ACP, 0, bstr, -1, nullptr, 0, nullptr, nullptr); - utf8_str = new char[length]; + length = WideCharToMultiByte(CP_ACP, 0, bstr, -1, nullptr, 0, nullptr, nullptr); + utf8_str = new char[length]; - WideCharToMultiByte(CP_ACP, 0, bstr, -1, utf8_str, length, nullptr, nullptr); - SysFreeString(bstr); + WideCharToMultiByte(CP_ACP, 0, bstr, -1, utf8_str, length, nullptr, nullptr); + SysFreeString(bstr); - std::string ret(utf8_str); - delete[] utf8_str; - return ret; + std::string ret(utf8_str); + delete[] utf8_str; + return ret; } -#endif \ No newline at end of file +#endif