From b2edcfa4c31ba1dc087a1bfab86c3cc0f20cbb9a Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:08:54 +0900 Subject: [PATCH 01/10] =?UTF-8?q?=E5=BC=95=E6=95=B0=E3=81=ABCallHandle?= =?UTF-8?q?=E3=82=92=E3=81=A8=E3=82=8B=E9=96=A2=E6=95=B0=E3=81=AE=E3=82=BB?= =?UTF-8?q?=E3=83=83=E3=83=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/include/webcface/func.h | 75 +++++++++++++++++++ .../include/webcface/internal/func_internal.h | 8 +- client/src/func.cc | 38 +++++++--- client/src/func_info.cc | 22 +++--- 4 files changed, 122 insertions(+), 21 deletions(-) diff --git a/client/include/webcface/func.h b/client/include/webcface/func.h index afb6026213..6c3d98d832 100644 --- a/client/include/webcface/func.h +++ b/client/include/webcface/func.h @@ -160,6 +160,12 @@ class WEBCFACE_DLL Func : protected Field { */ const Func &setImpl(ValType return_type, std::vector &&args, std::function &&func_impl) const; + /*! + * 引数の個数不定バージョン + * + */ + const Func &setImpl(ValType return_type, std::nullopt_t, + std::function &&func_impl) const; const Func & setImpl(const std::shared_ptr &func_info) const; @@ -365,6 +371,66 @@ class WEBCFACE_DLL Func : protected Field { } }); } + /*! + * \brief 引数にCallHandleを取る関数を登録する + * \since ver2.4 + * + * * 自動でrespondされることはないので、 + * 関数が受け取ったhandleを別スレッドに渡すなどして、 + * ここでセットした関数の終了後にrespond()やreject()することも可能。 + * * setArgs(), setReturnType() で引数の個数や型と戻り値の型を指定する。 + * 指定しない場合、引数なし戻り値なしとみなされる。 + * + * \param func セットする関数または関数オブジェクト。 + * 引数としてCallHandleを1つ取り、戻り値はvoidで、 + * CallHandle::respond() や reject() を通して値を返す + * + */ + template , void>, + std::nullptr_t> = nullptr> + const Func &set(T callback) const { + return setImpl(ValType::none_, std::nullopt, + [base = *this, callback = std::move(callback)]( + const CallHandle &handle) { + if (handle.assertArgsNum(base.args().size())) { + callback(handle); + } + }); + } + /*! + * \brief 引数にFuncCallHandleを取り非同期に実行される関数を登録する + * \since ver2.4 + * + * * 自動でrespondされることはないので、 + * 関数が受け取ったhandleを別スレッドに渡すなどして、 + * ここでセットした関数の終了後にrespond()やreject()することも可能。 + * * setArgs(), setReturnType() で引数の個数や型と戻り値の型を指定する。 + * 指定しない場合、引数なし戻り値なしとみなされる。 + * + * \param func セットする関数または関数オブジェクト。 + * 引数としてCallHandleを1つ取り、戻り値はvoidで、 + * CallHandle::respond() や reject() を通して値を返す + * + */ + template , void>, + std::nullptr_t> = nullptr> + const Func &setAsync(T callback) const { + return setImpl( + ValType::none_, std::nullopt, + [base = *this, + callback = std::make_shared>( + std::move(callback))](const CallHandle &handle) { + if (handle.assertArgsNum(base.args().size())) { + std::thread([callback, handle] { + callback->operator()(handle); + }).detach(); + } + }); + } /*! * \brief 関数を関数リストで非表示にする @@ -481,6 +547,15 @@ class WEBCFACE_DLL Func : protected Field { * */ const Func &setArgs(const std::vector &args) const; + /*! + * \brief 戻り値の型の情報を更新する + * \since ver2.4 + * + * set()やsetAsync()で通常の関数をセットした場合戻り値の型は自動的に取得されるので + * setReturnType() を呼ぶ必要はない。 + * + */ + const Func &setReturnType(ValType return_type) const; /*! * \brief Funcの参照先を比較 diff --git a/client/include/webcface/internal/func_internal.h b/client/include/webcface/internal/func_internal.h index 19ac5b9443..c4c4e8e879 100644 --- a/client/include/webcface/internal/func_internal.h +++ b/client/include/webcface/internal/func_internal.h @@ -134,10 +134,14 @@ class FuncResultStore { /*! * \brief 関数1つの情報を表す。関数の実体も持つ * + * ver2.4〜: args が空vectorではなくstd::nulloptの場合、 + * 引数の個数が確定していないことを表し、 + * あとから setArgs() で個数を変更することができる + * */ struct FuncInfo : public Field { ValType return_type; - std::vector args; + std::optional> args; std::function func_impl; /*! @@ -162,7 +166,7 @@ struct FuncInfo : public Field { void run(webcface::message::Call &&call); FuncInfo() : Field(), return_type(ValType::none_), args(), func_impl() {} - FuncInfo(const Field &base, ValType return_type, std::vector &&args, + FuncInfo(const Field &base, ValType return_type, std::optional> &&args, std::function &&func_impl) : Field(base), return_type(return_type), args(std::move(args)), func_impl(std::move(func_impl)) {} diff --git a/client/src/func.cc b/client/src/func.cc index c1807d943d..f4b173dd90 100644 --- a/client/src/func.cc +++ b/client/src/func.cc @@ -1,5 +1,4 @@ #include "webcface/func.h" -#include #include #include "webcface/internal/func_internal.h" #include "webcface/message/message.h" @@ -19,6 +18,12 @@ const Func &Func::setImpl(ValType return_type, std::vector &&args, static_cast(*this), return_type, std::move(args), std::move(func_impl))); } +const Func &Func::setImpl(ValType return_type, std::nullopt_t, + std::function &&func_impl) const { + return setImpl(std::make_shared( + static_cast(*this), return_type, std::nullopt, + std::move(func_impl))); +} const Func &Func::setImpl(const std::shared_ptr &v) const { setCheck()->func_store.setSend(*this, v); return *this; @@ -93,7 +98,7 @@ ValType Func::returnType() const { std::vector Func::args() const { auto func_info = dataLock()->func_store.getRecv(*this); if (func_info) { - return (*func_info)->args; + return (*func_info)->args.value_or(std::vector{}); } return std::vector{}; } @@ -106,18 +111,31 @@ const Func &Func::setArgs(const std::vector &args) const { if (!func_info) { throw std::invalid_argument("setArgs failed: Func not set"); } else { - if ((*func_info)->args.size() != args.size()) { - throw std::invalid_argument( - "setArgs failed: Number of args does not match, size: " + - std::to_string(args.size()) + - " actual: " + std::to_string((*func_info)->args.size())); - } - for (std::size_t i = 0; i < args.size(); i++) { - (*func_info)->args[i].mergeConfig(args[i]); + if ((*func_info)->args.has_value()) { + if ((*func_info)->args->size() != args.size()) { + throw std::invalid_argument( + "setArgs failed: Number of args does not match, size: " + + std::to_string(args.size()) + + " actual: " + std::to_string((*func_info)->args->size())); + } + for (std::size_t i = 0; i < args.size(); i++) { + (*func_info)->args->at(i).mergeConfig(args[i]); + } + } else { + (*func_info)->args.emplace(args); } return *this; } } +const Func &Func::setReturnType(ValType return_type) const { + auto func_info = setCheck()->func_store.getRecv(*this); + if (!func_info) { + throw std::invalid_argument("setReturnType failed: Func not set"); + } else { + (*func_info)->return_type = return_type; + return *this; + } +} SharedString AnonymousFunc::fieldNameTmp() { static int id = 0; diff --git a/client/src/func_info.cc b/client/src/func_info.cc index 265d75193f..45e9a7fb7f 100644 --- a/client/src/func_info.cc +++ b/client/src/func_info.cc @@ -47,10 +47,12 @@ void Arg::mergeConfig(const Arg &other) { } const std::string &Arg::name() const { - return this->msg_data ? this->msg_data->name_.decode() : SharedString::emptyStr(); + return this->msg_data ? this->msg_data->name_.decode() + : SharedString::emptyStr(); } const std::wstring &Arg::nameW() const { - return this->msg_data ? this->msg_data->name_.decodeW() : SharedString::emptyStrW(); + return this->msg_data ? this->msg_data->name_.decodeW() + : SharedString::emptyStrW(); } ValType Arg::type() const { return this->type_; } Arg &Arg::type(ValType type) { @@ -117,17 +119,19 @@ std::ostream &operator<<(std::ostream &os, const Arg &arg) { internal::FuncInfo::FuncInfo(const message::FuncInfo &m) : return_type(m.return_type), args(), func_impl(nullptr) { - args.reserve(m.args.size()); + args.emplace(); + args->reserve(m.args.size()); for (const auto &a : m.args) { - args.emplace_back(a); + args->emplace_back(a); } } -message::FuncInfo -internal::FuncInfo::toMessage(const SharedString &field) { +message::FuncInfo internal::FuncInfo::toMessage(const SharedString &field) { message::FuncInfo m{0, field, return_type, {}}; - m.args.reserve(args.size()); - for (auto &a : args) { - m.args.emplace_back(a.initMsg()); + if (args.has_value()) { + m.args.reserve(args->size()); + for (auto &a : *args) { + m.args.emplace_back(a.initMsg()); + } } return m; } From 1e3d30fef294cfbd6812f5fff42b17799ecdaa11 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Mon, 23 Sep 2024 14:28:44 +0900 Subject: [PATCH 02/10] =?UTF-8?q?=E6=96=B0log=E3=83=A1=E3=83=83=E3=82=BB?= =?UTF-8?q?=E3=83=BC=E3=82=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../webcface/internal/client_internal.h | 10 +- .../include/webcface/internal/data_store1.h | 2 - .../include/webcface/internal/data_store2.h | 18 +++ client/src/client_msg_recv.cc | 34 +++--- client/src/client_msg_sync.cc | 31 +++-- client/src/data_store1.cc | 1 - client/src/data_store2.cc | 1 + message/include/webcface/message/message.h | 115 +++++++++++++----- message/src/message.cc | 7 +- 9 files changed, 145 insertions(+), 74 deletions(-) diff --git a/client/include/webcface/internal/client_internal.h b/client/include/webcface/internal/client_internal.h index 819a930ee3..294f8213fe 100644 --- a/client/include/webcface/internal/client_internal.h +++ b/client/include/webcface/internal/client_internal.h @@ -81,15 +81,14 @@ struct ClientData : std::enable_shared_from_this { StrMap1 canvas3d_prev, canvas3d_data; StrMap1 canvas2d_prev, canvas2d_data; StrMap1 image_data; - std::vector log_data; + StrMap1> log_data; StrMap1 func_data; }; struct SyncDataFirst { StrMap2 value_req, text_req, robot_model_req, view_req, - canvas3d_req, canvas2d_req, image_req; + canvas3d_req, canvas2d_req, image_req, log_req; StrMap2 image_req_info; - StrMap1 log_req; bool ping_status_req; SyncDataSnapshot sync_data; }; @@ -351,10 +350,9 @@ struct ClientData : std::enable_shared_from_this { SyncDataStore2 robot_model_store; SyncDataStore2 canvas3d_store; SyncDataStore2 canvas2d_store; - SyncDataStore1>> log_store; + SyncDataStore2> log_store; SyncDataStore1 sync_time_store; FuncResultStore func_result_store; - std::size_t log_sent_lines = 0; /*! * \brief listenerがfetchするの待ちの関数呼び出しをためておく @@ -411,7 +409,7 @@ struct ClientData : std::enable_shared_from_this { canvas3d_change_event; StrMap2>> canvas2d_change_event; - StrMap1>> log_append_event; + StrMap2>> log_append_event; StrMap1>> sync_event, ping_event; StrMap1>> value_entry_event; diff --git a/client/include/webcface/internal/data_store1.h b/client/include/webcface/internal/data_store1.h index 5735edb895..9f66eaa5a1 100644 --- a/client/include/webcface/internal/data_store1.h +++ b/client/include/webcface/internal/data_store1.h @@ -2,7 +2,6 @@ #include #include #include -#include "webcface/log.h" #include "webcface/encoding/encoding.h" WEBCFACE_NS_BEGIN @@ -64,7 +63,6 @@ class SyncDataStore1 { #if WEBCFACE_SYSTEM_DLLEXPORT extern template class SyncDataStore1; // test用 -extern template class SyncDataStore1>>; extern template class SyncDataStore1; #endif diff --git a/client/include/webcface/internal/data_store2.h b/client/include/webcface/internal/data_store2.h index 3376aa7e8a..6599b55ef0 100644 --- a/client/include/webcface/internal/data_store2.h +++ b/client/include/webcface/internal/data_store2.h @@ -14,6 +14,7 @@ #include "webcface/message/message.h" #include "webcface/internal/component_internal.h" #include "webcface/internal/robot_link_internal.h" +#include "webcface/log.h" WEBCFACE_NS_BEGIN namespace internal { @@ -209,6 +210,22 @@ using Canvas3DData = std::shared_ptr< using Canvas2DData = std::shared_ptr; using ImageData = ImageFrame; +struct LogData { + std::deque data; + std::size_t sent_lines; + + std::vector getDiff() { + auto begin = data.cbegin() + static_cast(sent_lines); + auto end = data.cend(); + sent_lines = data.size(); + return std::vector(begin, end); + } + std::vector getAll() { + sent_lines = data.size(); + return std::vector(data.cbegin(), data.cend()); + } +}; + #if WEBCFACE_SYSTEM_DLLEXPORT extern template class SyncDataStore2; // test用 extern template class SyncDataStore2; @@ -219,6 +236,7 @@ extern template class SyncDataStore2; extern template class SyncDataStore2; extern template class SyncDataStore2; extern template class SyncDataStore2; +extern template class SyncDataStore2, int>; #endif } // namespace internal diff --git a/client/src/client_msg_recv.cc b/client/src/client_msg_recv.cc index dc45ffaf4e..d0bda0275b 100644 --- a/client/src/client_msg_recv.cc +++ b/client/src/client_msg_recv.cc @@ -270,14 +270,15 @@ void internal::ClientData::onRecv( onRecvRes(this, r, r, this->image_store, this->image_change_event); break; } - case MessageKind::log: { - auto &r = *static_cast(obj.get()); - auto member = this->getMemberNameFromId(r.member_id); + case MessageKind::log + MessageKind::res: { + auto &r = *static_cast *>(obj.get()); std::lock_guard lock_s(this->log_store.mtx); - auto log_s = this->log_store.getRecv(member); + auto [member, field] = + this->view_store.getReq(r.req_id, r.sub_field); + auto log_s = this->log_store.getRecv(member, field); if (!log_s) { - log_s = std::make_shared>(); - this->log_store.setRecv(member, *log_s); + log_s = std::make_shared(); + this->log_store.setRecv(member, field, *log_s); } int log_keep_lines_local = log_keep_lines.load(); auto r_begin = r.log->begin(); @@ -287,22 +288,22 @@ void internal::ClientData::onRecv( static_cast(log_keep_lines_local)) { r_begin = r_end - log_keep_lines_local; } - while ((*log_s)->size() + (r_end - r_begin) > + while ((*log_s)->data.size() + (r_end - r_begin) > static_cast(log_keep_lines_local)) { - (*log_s)->pop_front(); + (*log_s)->data.pop_front(); } } for (auto lit = r_begin; lit != r_end; lit++) { - (*log_s)->emplace_back(*lit); + (*log_s)->data.emplace_back(*lit); } std::shared_ptr> cl; { std::lock_guard lock(event_m); - cl = findFromMap1(this->log_append_event, member) + cl = findFromMap2(this->log_append_event, member, field) .value_or(nullptr); } if (cl && *cl) { - cl->operator()(Field{shared_from_this(), member}); + cl->operator()(Field{shared_from_this(), member, field}); } break; } @@ -447,10 +448,10 @@ void internal::ClientData::onRecv( onRecvEntry(this, r, this->image_store, this->image_entry_event); break; } - case MessageKind::log_entry: { - auto &r = *static_cast(obj.get()); + case MessageKind::entry + MessageKind::log: { + auto &r = *static_cast *>(obj.get()); auto member = this->getMemberNameFromId(r.member_id); - this->log_store.setEntry(member); + this->log_store.setEntry(member, r.field); // std::decay_tlog_entry_event.at(member))> cl; // { // std::lock_guard lock(this->event_m); @@ -494,12 +495,15 @@ void internal::ClientData::onRecv( case MessageKind::robot_model + MessageKind::req: case MessageKind::image + MessageKind::req: case MessageKind::ping_status_req: - case MessageKind::log_req: + case MessageKind::log + MessageKind::req: + case MessageKind::log_default: + case MessageKind::log_req_default: if (!message_kind_warned[kind]) { logger_internal->warn("Invalid message Kind {}", kind); message_kind_warned[kind] = true; } break; + case MessageKind::log_entry_default: case MessageKind::unknown: break; default: diff --git a/client/src/client_msg_sync.cc b/client/src/client_msg_sync.cc index 048d85d348..097d8507f4 100644 --- a/client/src/client_msg_sync.cc +++ b/client/src/client_msg_sync.cc @@ -98,7 +98,11 @@ std::string internal::ClientData::packSyncDataFirst(const SyncDataFirst &data) { } } for (const auto &v : data.log_req) { - message::pack(buffer, len, message::LogReq{{}, v.first}); + for (const auto &v2 : v.second) { + message::pack( + buffer, len, + message::Req{{}, v.first, v2.first, v2.second}); + } } if (data.ping_status_req) { @@ -138,21 +142,14 @@ internal::ClientData::SyncMutexedData::syncData(internal::ClientData *this_, // std::lock_guard image_lock(this_->image_store.mtx); data.image_data = this_->image_store.transferSend(is_first); - // std::lock_guard log_lock(this_->log_store.mtx); - auto log_self_opt = this_->log_store.getRecv(this_->self_member_name); - if (!log_self_opt) { - throw std::runtime_error("self log data is null"); - } else { - auto &log_s = *log_self_opt; - if ((log_s->size() > 0 && is_first) || - log_s->size() > this_->log_sent_lines) { - auto begin = log_s->begin(); - auto end = log_s->end(); - if (!is_first) { - begin += static_cast(this_->log_sent_lines); + { + std::lock_guard log_lock(this_->log_store.mtx); + for (const auto &ld : this_->log_store.transferSend(is_first)) { + if (is_first) { + data.log_data[ld.first] = ld.second->getAll(); + } else { + data.log_data[ld.first] = ld.second->getDiff(); } - this_->log_sent_lines = log_s->size(); - data.log_data = std::vector(begin, end); } } // std::lock_guard func_lock(this_->func_store.mtx); @@ -234,9 +231,9 @@ std::string internal::ClientData::packSyncData(std::stringstream &buffer, message::Image{v.first, v.second.toMessage()}); } - if (!data.log_data.empty()) { + for (const auto &v : data.log_data) { message::pack(buffer, len, - message::Log{data.log_data.begin(), data.log_data.end()}); + message::Log{v.first, v.second.begin(), v.second.end()}); } for (const auto &v : data.func_data) { if (!v.first.startsWith(field_separator)) { diff --git a/client/src/data_store1.cc b/client/src/data_store1.cc index 9d465d21ae..70a9b4f1af 100644 --- a/client/src/data_store1.cc +++ b/client/src/data_store1.cc @@ -78,7 +78,6 @@ StrMap1 SyncDataStore1::transferReq() { } template class SyncDataStore1; // test用 -template class SyncDataStore1>>; template class SyncDataStore1; } // namespace internal diff --git a/client/src/data_store2.cc b/client/src/data_store2.cc index fdc2905fd7..582733cf62 100644 --- a/client/src/data_store2.cc +++ b/client/src/data_store2.cc @@ -259,5 +259,6 @@ template class SyncDataStore2; template class SyncDataStore2; template class SyncDataStore2; template class SyncDataStore2; +template class SyncDataStore2>, int>; } // namespace internal WEBCFACE_NS_END diff --git a/message/include/webcface/message/message.h b/message/include/webcface/message/message.h index edb6ca77f2..2f211ec415 100644 --- a/message/include/webcface/message/message.h +++ b/message/include/webcface/message/message.h @@ -42,6 +42,7 @@ enum MessageKindEnum { image = 5, robot_model = 6, canvas3d = 7, + log = 8, entry = 20, req = 40, res = 60, @@ -50,15 +51,15 @@ enum MessageKindEnum { call_response = 82, call_result = 83, func_info = 84, - log = 85, - log_req = 86, + log_default = 85, + log_req_default = 86, sync = 87, sync_init_end = 88, // svr_version = 88, ping = 89, ping_status = 90, ping_status_req = 91, - log_entry = 92, + log_entry_default = 92, }; } @@ -366,8 +367,7 @@ struct View : public MessageBase { } } View(const SharedString &field, - const std::map> - &data_diff, + const std::map> &data_diff, std::size_t length) : field(field), data_diff(data_diff), length(length) {} MSGPACK_DEFINE_MAP(MSGPACK_NVP("f", field), MSGPACK_NVP("d", data_diff), @@ -391,8 +391,7 @@ struct Canvas3DComponent { }; struct Canvas3D : public MessageBase { SharedString field; - std::map> - data_diff; + std::map> data_diff; std::size_t length = 0; Canvas3D() = default; Canvas3D(const SharedString &field, @@ -405,8 +404,8 @@ struct Canvas3D : public MessageBase { } } Canvas3D(const SharedString &field, - const std::map< - std::string, std::shared_ptr> &data_diff, + const std::map> + &data_diff, std::size_t length) : field(field), data_diff(data_diff), length(length) {} MSGPACK_DEFINE_MAP(MSGPACK_NVP("f", field), MSGPACK_NVP("d", data_diff), @@ -438,8 +437,7 @@ struct Canvas2DData { struct Canvas2D : public MessageBase { SharedString field; double width, height; - std::map> - data_diff; + std::map> data_diff; std::size_t length; Canvas2D() = default; Canvas2D(const SharedString &field, double width, double height, @@ -453,8 +451,8 @@ struct Canvas2D : public MessageBase { } } Canvas2D(const SharedString &field, double width, double height, - const std::map< - std::string, std::shared_ptr> &data_diff, + const std::map> + &data_diff, std::size_t length) : field(field), width(width), height(height), data_diff(data_diff), length(length) {} @@ -501,14 +499,15 @@ struct LogLine { * client->server時はmemberは無視 * */ -struct Log : public MessageBase { +struct LogDefault : public MessageBase { unsigned int member_id = 0; std::shared_ptr> log; - Log() = default; - Log(unsigned int member_id, const std::shared_ptr> &log) + LogDefault() = default; + LogDefault(unsigned int member_id, + const std::shared_ptr> &log) : member_id(member_id), log(log) {} template - Log(const It &begin, const It &end) : member_id(0) { + LogDefault(const It &begin, const It &end) : member_id(0) { this->log = std::make_shared>(); for (auto it = begin; it < end; it++) { if constexpr (std::is_same_v) { @@ -518,17 +517,47 @@ struct Log : public MessageBase { } } } - explicit Log(const LogLine &ll) : member_id(0) { + explicit LogDefault(const LogLine &ll) : member_id(0) { this->log = std::make_shared>(1); this->log->front() = ll; } MSGPACK_DEFINE_MAP(MSGPACK_NVP("m", member_id), MSGPACK_NVP("l", log)) }; -struct LogReq : public MessageBase { +/*! + * \brief client(member)->server->client logを追加 + * \since ver2.4 + * + */ +struct Log : public MessageBase { + SharedString field; + std::shared_ptr> log; + Log() = default; + Log(const SharedString &field, + const std::shared_ptr> &log) + : field(field), log(log) {} + template + Log(const SharedString &field, const It &begin, const It &end) + : field(field) { + this->log = std::make_shared>(); + for (auto it = begin; it < end; it++) { + if constexpr (std::is_same_v) { + this->log->push_back(*it); + } else { + this->log->emplace_back(it->toMessage()); + } + } + } + explicit Log(const SharedString &field, const LogLine &ll) : field(field) { + this->log = std::make_shared>(1); + this->log->front() = ll; + } + MSGPACK_DEFINE_MAP(MSGPACK_NVP("f", field), MSGPACK_NVP("l", log)) +}; +struct LogReqDefault : public MessageBase { SharedString member; MSGPACK_DEFINE_MAP(MSGPACK_NVP("M", member)) }; -struct LogEntry : public MessageBase { +struct LogEntryDefault : public MessageBase { unsigned int member_id = 0; MSGPACK_DEFINE_MAP(MSGPACK_NVP("m", member_id)) }; @@ -679,8 +708,7 @@ struct Res : public MessageBase { std::size_t length = 0; Res() = default; Res(unsigned int req_id, const SharedString &sub_field, - const std::map> - &data_diff, + const std::map> &data_diff, std::size_t length) : req_id(req_id), sub_field(sub_field), data_diff(data_diff), length(length) {} @@ -692,13 +720,12 @@ struct Res : public MessageBase { unsigned int req_id = 0; SharedString sub_field; - std::map> - data_diff; + std::map> data_diff; std::size_t length = 0; Res() = default; Res(unsigned int req_id, const SharedString &sub_field, - const std::map> &data_diff, + const std::map> + &data_diff, std::size_t length) : req_id(req_id), sub_field(sub_field), data_diff(data_diff), length(length) {} @@ -711,14 +738,13 @@ struct Res unsigned int req_id = 0; SharedString sub_field; double width = 0, height = 0; - std::map> - data_diff; + std::map> data_diff; std::size_t length; Res() = default; Res(unsigned int req_id, const SharedString &sub_field, double width, double height, - const std::map> &data_diff, + const std::map> + &data_diff, std::size_t length) : req_id(req_id), sub_field(sub_field), width(width), height(height), data_diff(data_diff), length(length) {} @@ -743,6 +769,35 @@ struct Res : public MessageBase, MSGPACK_NVP("p", cmp_mode_)) }; +template <> +struct Res : public MessageBase { + unsigned int req_id = 0; + SharedString sub_field; + std::shared_ptr> log; + Res() = default; + Res(unsigned int req_id, const SharedString &sub_field, + const std::shared_ptr> &log) + : MessageBase(), req_id(req_id), + sub_field(sub_field), log(log) {} + template + Res(unsigned int req_id, const SharedString &sub_field, const It &begin, + const It &end) + : MessageBase(), req_id(req_id), + sub_field(sub_field) { + this->log = std::make_shared>(); + for (auto it = begin; it < end; it++) { + if constexpr (std::is_same_v) { + this->log->push_back(*it); + } else { + this->log->emplace_back(it->toMessage()); + } + } + } + + MSGPACK_DEFINE_MAP(MSGPACK_NVP("i", req_id), MSGPACK_NVP("f", sub_field), + MSGPACK_NVP("l", log)) +}; + /*! * \brief msgpackのメッセージをパースし返す * diff --git a/message/src/message.cc b/message/src/message.cc index 1402597b40..9858ad14b5 100644 --- a/message/src/message.cc +++ b/message/src/message.cc @@ -71,9 +71,10 @@ unpack(const std::string &message, MSG_PARSE_DATA(RobotModel) MSG_PARSE_DATA(Canvas3D) MSG_PARSE_DATA(Canvas2D) - MSG_PARSE(Log) - MSG_PARSE(LogReq) - MSG_PARSE(LogEntry) + MSG_PARSE_DATA(Log) + MSG_PARSE(LogDefault) + MSG_PARSE(LogReqDefault) + MSG_PARSE(LogEntryDefault) MSG_PARSE(FuncInfo) MSG_PARSE(Sync) MSG_PARSE(SyncInitEnd) From 0fd04dd512f93c65a8cd9a70f1e3536b8838251c Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Thu, 26 Sep 2024 16:46:28 +0900 Subject: [PATCH 03/10] =?UTF-8?q?=E3=82=AF=E3=83=A9=E3=82=A4=E3=82=A2?= =?UTF-8?q?=E3=83=B3=E3=83=88=E5=81=B4=E3=81=A7log=E3=81=AE=E5=90=8D?= =?UTF-8?q?=E5=89=8D=E3=82=92=E6=8C=87=E5=AE=9A=E5=8F=AF=E8=83=BD=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/include/webcface/client.h | 34 ++++++++- client/include/webcface/field.h | 9 +++ .../webcface/internal/client_internal.h | 9 +-- .../include/webcface/internal/data_store1.h | 2 +- client/include/webcface/internal/logger.h | 3 +- client/include/webcface/log.h | 8 ++- client/include/webcface/member.h | 13 ++++ client/src/client_msg_sync.cc | 71 +++++++++++++++++-- client/src/client_threading.cc | 5 -- client/src/field.cc | 3 + client/src/log.cc | 56 ++++++++------- client/src/member.cc | 3 +- message/include/webcface/message/message.h | 2 + message/src/message.cc | 6 ++ 14 files changed, 179 insertions(+), 45 deletions(-) diff --git a/client/include/webcface/client.h b/client/include/webcface/client.h index 1a1deff5c3..986f35837b 100644 --- a/client/include/webcface/client.h +++ b/client/include/webcface/client.h @@ -240,7 +240,7 @@ class WEBCFACE_DLL Client : public Member { /*! * \brief サーバーに接続されている他のmemberのリストを得る。 * \since ver2.0.2 (constつけ忘れ) - * + * * 自分自身と、無名のmemberを除く。 * \sa member(), onMemberEntry() */ @@ -271,6 +271,14 @@ class WEBCFACE_DLL Client : public Member { * \sa loggerOStream() */ std::streambuf *loggerStreamBuf() const; + /*! + * \brief webcfaceに出力するstreambuf + * \since ver2.4 + * + * * nameを省略した場合 "default" になる。 + * + */ + std::streambuf *loggerStreamBuf(std::string_view name) const; /*! * \brief webcfaceに出力するostream * @@ -284,18 +292,42 @@ class WEBCFACE_DLL Client : public Member { * \sa loggerStreamBuf() */ std::ostream &loggerOStream() const; + /*! + * \brief webcfaceに出力するostream + * \since ver2.4 + * + * * nameを省略した場合 "default" になる。 + * + */ + std::ostream &loggerOStream(std::string_view name) const; /*! * \brief webcfaceに出力するwstreambuf * \since ver2.0 * \sa loggerStreamBuf */ std::wstreambuf *loggerWStreamBuf() const; + /*! + * \brief webcfaceに出力するwstreambuf + * \since ver2.4 + * + * * nameを省略した場合 "default" になる。 + * + */ + std::wstreambuf *loggerWStreamBuf(std::wstring_view name) const; /*! * \brief webcfaceに出力するwostream * \since ver2.0 * \sa loggerOStream */ std::wostream &loggerWOStream() const; + /*! + * \brief webcfaceに出力するwostream + * \since ver2.4 + * + * * nameを省略した場合 "default" になる。 + * + */ + std::wostream &loggerWOStream(std::wstring_view name) const; /*! * \brief WebCFaceサーバーのバージョン情報 diff --git a/client/include/webcface/field.h b/client/include/webcface/field.h index 9c3c2f4e92..2f0f587e0f 100644 --- a/client/include/webcface/field.h +++ b/client/include/webcface/field.h @@ -26,6 +26,7 @@ class FuncListener; class RobotModel; class Canvas2D; class Canvas3D; +class Log; constexpr char field_separator = '.'; @@ -187,6 +188,14 @@ struct WEBCFACE_DLL Field : public FieldBase { Canvas3D canvas3D(std::wstring_view field) const; Canvas2D canvas2D(std::string_view field = "") const; Canvas2D canvas2D(std::wstring_view field) const; + /*! + * \since ver2.4 + */ + Log log(std::string_view field = "") const; + /*! + * \since ver2.4 + */ + Log log(std::wstring_view field) const; std::vector valueEntries() const; diff --git a/client/include/webcface/internal/client_internal.h b/client/include/webcface/internal/client_internal.h index 294f8213fe..ac82208a4b 100644 --- a/client/include/webcface/internal/client_internal.h +++ b/client/include/webcface/internal/client_internal.h @@ -425,10 +425,11 @@ struct ClientData : std::enable_shared_from_this { canvas2d_entry_event; std::shared_ptr logger_internal; - std::unique_ptr logger_buf; - std::unique_ptr logger_os; - std::unique_ptr logger_buf_w; - std::unique_ptr logger_os_w; + std::mutex logger_m; + StrMap1> logger_buf; + StrMap1> logger_os; + StrMap1> logger_buf_w; + StrMap1> logger_os_w; std::string svr_name, svr_version, svr_hostname; diff --git a/client/include/webcface/internal/data_store1.h b/client/include/webcface/internal/data_store1.h index 9f66eaa5a1..d012f02193 100644 --- a/client/include/webcface/internal/data_store1.h +++ b/client/include/webcface/internal/data_store1.h @@ -1,8 +1,8 @@ #pragma once #include #include -#include #include "webcface/encoding/encoding.h" +#include "webcface/field.h" WEBCFACE_NS_BEGIN namespace internal { diff --git a/client/include/webcface/internal/logger.h b/client/include/webcface/internal/logger.h index 21123423c5..e2d184215d 100644 --- a/client/include/webcface/internal/logger.h +++ b/client/include/webcface/internal/logger.h @@ -19,12 +19,13 @@ class BasicLoggerBuf final : public std::basic_streambuf { // どうせclientが消える時にはLoggerBufも消える internal::ClientData *data_p; + SharedString field; int sync() override; int_type overflow(int_type c) override; public: - explicit BasicLoggerBuf(internal::ClientData *data_p); + explicit BasicLoggerBuf(internal::ClientData *data_p, const SharedString &field); ~BasicLoggerBuf() = default; }; #if WEBCFACE_SYSTEM_DLLEXPORT diff --git a/client/include/webcface/log.h b/client/include/webcface/log.h index f66466ed5e..2b22ef377b 100644 --- a/client/include/webcface/log.h +++ b/client/include/webcface/log.h @@ -58,13 +58,19 @@ class LogLineW : private LogLineData { /*! * \brief ログの送受信データを表すクラス * - * fieldを継承しているがfield名は使用していない + * * fieldを継承しているがfield名は使用していない + * * ver2.4〜 他のデータ型と同じようにフィールド名を指定できるようになった * */ class WEBCFACE_DLL Log : protected Field { public: Log() = default; Log(const Field &base); + /*! + * \since ver2.4 + */ + Log(const Field &base, const SharedString &field) + : Log(Field{base, field}) {} using Field::member; diff --git a/client/include/webcface/member.h b/client/include/webcface/member.h index 89a2190707..bbc2deda65 100644 --- a/client/include/webcface/member.h +++ b/client/include/webcface/member.h @@ -53,6 +53,19 @@ class WEBCFACE_DLL Member : protected Field { using Field::text; using Field::value; using Field::view; + + /*! + * \since ver2.4 + */ + Log log(std::string_view name) const { return this->Field::log(name); } + /*! + * \since ver2.4 + */ + Log log(std::wstring_view name) const { return this->Field::log(name); } + /*! + * ver2.4〜: nameを省略した場合 "default" として送信される。 + * + */ Log log() const; /*! * \brief AnonymousFuncオブジェクトを作成しfuncをsetする diff --git a/client/src/client_msg_sync.cc b/client/src/client_msg_sync.cc index 097d8507f4..d2afc5f451 100644 --- a/client/src/client_msg_sync.cc +++ b/client/src/client_msg_sync.cc @@ -1,4 +1,5 @@ #include "webcface/client.h" +#include "webcface/internal/logger.h" #include "webcface/message/message.h" #include "webcface/internal/client_internal.h" #include "webcface/internal/robot_link_internal.h" @@ -263,15 +264,77 @@ Client::onMemberEntry(std::function callback) const { std::make_shared>(std::move(callback)); return *this; } +// \private +static std::streambuf * +getLoggerBuf(const std::shared_ptr &data, + const SharedString &field) { + if (!data->logger_buf.count(field)) { + data->logger_buf.emplace( + field, std::make_unique(data.get(), field)); + } + return data->logger_buf.at(field).get(); +} std::streambuf *Client::loggerStreamBuf() const { - return data->logger_buf.get(); + std::lock_guard lock(data->logger_m); + return getLoggerBuf(data, message::Log::defaultLogName()); +} +std::streambuf *Client::loggerStreamBuf(std::string_view name) const { + std::lock_guard lock(data->logger_m); + return getLoggerBuf(data, SharedString::encode(name)); +} +// \private +static std::wstreambuf * +getLoggerBufW(const std::shared_ptr &data, + const SharedString &field) { + if (!data->logger_buf_w.count(field)) { + data->logger_buf_w.emplace( + field, std::make_unique(data.get(), field)); + } + return data->logger_buf_w.at(field).get(); } -std::ostream &Client::loggerOStream() const { return *data->logger_os.get(); } std::wstreambuf *Client::loggerWStreamBuf() const { - return data->logger_buf_w.get(); + std::lock_guard lock(data->logger_m); + return getLoggerBufW(data, message::Log::defaultLogName()); +} +std::wstreambuf *Client::loggerWStreamBuf(std::wstring_view name) const { + std::lock_guard lock(data->logger_m); + return getLoggerBufW(data, SharedString::encode(name)); +} +// \private +static std::ostream & +getLoggerOS(const std::shared_ptr &data, + const SharedString &field) { + if (!data->logger_os.count(field)) { + data->logger_os.emplace( + field, std::make_unique(getLoggerBuf(data, field))); + } + return *data->logger_os.at(field); +} +std::ostream &Client::loggerOStream() const { + std::lock_guard lock(data->logger_m); + return getLoggerOS(data, message::Log::defaultLogName()); +} +std::ostream &Client::loggerOStream(std::string_view name) const { + std::lock_guard lock(data->logger_m); + return getLoggerOS(data, SharedString::encode(name)); +} +// \private +static std::wostream & +getLoggerWOS(const std::shared_ptr &data, + const SharedString &field) { + if (!data->logger_os_w.count(field)) { + data->logger_os_w.emplace( + field, std::make_unique(getLoggerBufW(data, field))); + } + return *data->logger_os_w.at(field); } std::wostream &Client::loggerWOStream() const { - return *data->logger_os_w.get(); + std::lock_guard lock(data->logger_m); + return getLoggerWOS(data, message::Log::defaultLogName()); +} +std::wostream &Client::loggerWOStream(std::wstring_view name) const { + std::lock_guard lock(data->logger_m); + return getLoggerWOS(data, SharedString::encode(name)); } const std::string &Client::serverVersion() const { return data->svr_version; } const std::string &Client::serverName() const { return data->svr_name; } diff --git a/client/src/client_threading.cc b/client/src/client_threading.cc index ea8d930cfc..ba04d15913 100644 --- a/client/src/client_threading.cc +++ b/client/src/client_threading.cc @@ -36,11 +36,6 @@ internal::ClientData::ClientData(const SharedString &name, } else { logger_internal->set_level(spdlog::level::off); } - logger_buf = std::make_unique(this); - logger_os = std::make_unique(logger_buf.get()); - logger_buf_w = std::make_unique(this); - logger_os_w = std::make_unique(logger_buf_w.get()); - log_store.setRecv(name, std::make_shared>()); } Client::~Client() { diff --git a/client/src/field.cc b/client/src/field.cc index b9130a20f4..e7c0b6549f 100644 --- a/client/src/field.cc +++ b/client/src/field.cc @@ -8,6 +8,7 @@ #include "webcface/func.h" #include "webcface/canvas2d.h" #include "webcface/canvas3d.h" +#include "webcface/log.h" #include "webcface/internal/client_internal.h" #include #ifdef WEBCFACE_MESON @@ -76,6 +77,8 @@ Canvas3D Field::canvas3D(std::string_view field) const { return child(field); } Canvas3D Field::canvas3D(std::wstring_view field) const { return child(field); } Canvas2D Field::canvas2D(std::string_view field) const { return child(field); } Canvas2D Field::canvas2D(std::wstring_view field) const { return child(field); } +Log Field::log(std::string_view field) const { return child(field); } +Log Field::log(std::wstring_view field) const { return child(field); } /// \private template diff --git a/client/src/log.cc b/client/src/log.cc index b342c05697..ebe4bfa552 100644 --- a/client/src/log.cc +++ b/client/src/log.cc @@ -7,9 +7,7 @@ WEBCFACE_NS_BEGIN std::atomic internal::log_keep_lines = 1000; -void Log::keepLines(int n){ - internal::log_keep_lines.store(n); -} +void Log::keepLines(int n) { internal::log_keep_lines.store(n); } LogLineData::LogLineData(int level, std::chrono::system_clock::time_point time, const SharedString &message) @@ -31,20 +29,21 @@ message::LogLine LogLineData::toMessage() const { } /// \private -static void writeLog(internal::ClientData *data_p, LogLineData &&ll) { +static void writeLog(internal::ClientData *data_p, const SharedString &field, + LogLineData &&ll) { std::lock_guard lock(data_p->log_store.mtx); - auto v = data_p->log_store.getRecv(data_p->self_member_name); + auto v = data_p->log_store.getRecv(data_p->self_member_name, field); if (!v) { - throw std::runtime_error("self log data is null"); - } else { - // log_storeにはClientDataのコンストラクタで空vectorを入れてある - (*v)->emplace_back(std::move(ll)); + v.emplace(std::make_shared()); } + (*v)->data.emplace_back(std::move(ll)); + data_p->log_store.setSend(field, *v); } template -BasicLoggerBuf::BasicLoggerBuf(internal::ClientData *data_p) - : std::basic_streambuf(), data_p(data_p) { +BasicLoggerBuf::BasicLoggerBuf(internal::ClientData *data_p, + const SharedString &field) + : std::basic_streambuf(), data_p(data_p), field(field) { this->setp(buf, buf + sizeof(buf)); } template @@ -69,8 +68,9 @@ int BasicLoggerBuf::sync() { if (message.size() > 0 && message.back() == '\r') { message.pop_back(); } - writeLog(data_p, {2, std::chrono::system_clock::now(), - SharedString::encode(message)}); + writeLog(data_p, field, + {2, std::chrono::system_clock::now(), + SharedString::encode(message)}); if constexpr (std::is_same_v) { std::fputs(message.c_str(), stderr); } else if constexpr (std::is_same_v) { @@ -90,28 +90,28 @@ Log::Log(const Field &base) : Field(base) {} const Log &Log::onChange(std::function callback) const { this->request(); std::lock_guard lock(this->dataLock()->event_m); - this->dataLock()->log_append_event[this->member_] = + this->dataLock()->log_append_event[this->member_][this->field_] = std::make_shared>(std::move(callback)); return *this; } const Log &Log::request() const { auto data = dataLock(); - auto req = data->log_store.addReq(member_); + auto req = data->log_store.addReq(member_, field_); if (req) { - data->messagePushReq( - message::packSingle(message::LogReq{{}, member_})); + data->messagePushReq(message::packSingle( + message::Req{{}, member_, field_, req})); } return *this; } std::optional> Log::tryGet() const { request(); - auto v = dataLock()->log_store.getRecv(member_); + auto v = dataLock()->log_store.getRecv(member_, field_); if (v) { std::vector log_s; - log_s.reserve((*v)->size()); - for (const auto &l : **v) { + log_s.reserve((*v)->data.size()); + for (const auto &l : (*v)->data) { log_s.emplace_back(l); } return log_s; @@ -121,11 +121,11 @@ std::optional> Log::tryGet() const { } std::optional> Log::tryGetW() const { request(); - auto v = dataLock()->log_store.getRecv(member_); + auto v = dataLock()->log_store.getRecv(member_, field_); if (v) { std::vector log_s; - log_s.reserve((*v)->size()); - for (const auto &l : **v) { + log_s.reserve((*v)->data.size()); + for (const auto &l : (*v)->data) { log_s.emplace_back(l); } return log_s; @@ -134,16 +134,18 @@ std::optional> Log::tryGetW() const { } } -bool Log::exists() const { return dataLock()->log_store.getEntry(member_); } +bool Log::exists() const { + return dataLock()->log_store.getEntry(member_).count(field_); +} const Log &Log::clear() const { - dataLock()->log_store.setRecv(member_, - std::make_shared>()); + dataLock()->log_store.setRecv(member_, field_, + std::make_shared()); return *this; } const Log &Log::append(LogLineData &&ll) const { - writeLog(setCheck().get(), std::move(ll)); + writeLog(setCheck().get(), field_, std::move(ll)); return *this; } diff --git a/client/src/member.cc b/client/src/member.cc index 4c40ad9200..f2478d22a3 100644 --- a/client/src/member.cc +++ b/client/src/member.cc @@ -11,7 +11,8 @@ WEBCFACE_NS_BEGIN -Log Member::log() const { return Log{*this}; } +// ver2.4〜: nameを省略した場合 "default" として送信される。 +Log Member::log() const { return Log{*this, message::Log::defaultLogName()}; } const Member &Member::onValueEntry(std::function callback) const { std::lock_guard lock(dataLock()->event_m); diff --git a/message/include/webcface/message/message.h b/message/include/webcface/message/message.h index 2f211ec415..629aafbbea 100644 --- a/message/include/webcface/message/message.h +++ b/message/include/webcface/message/message.h @@ -529,6 +529,8 @@ struct LogDefault : public MessageBase { * */ struct Log : public MessageBase { + static SharedString defaultLogName(); + SharedString field; std::shared_ptr> log; Log() = default; diff --git a/message/src/message.cc b/message/src/message.cc index 9858ad14b5..ce73f9f938 100644 --- a/message/src/message.cc +++ b/message/src/message.cc @@ -98,5 +98,11 @@ unpack(const std::string &message, return std::vector>>{}; } } + +SharedString Log::defaultLogName() { + static SharedString default_name = SharedString::fromU8String("default"); + return default_name; +} + } // namespace message WEBCFACE_NS_END From 2d1b38a2c238c62cdc98d453b2f2cdc689cf6d36 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:13:55 +0900 Subject: [PATCH 04/10] =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC?= =?UTF-8?q?=E5=81=B4=E3=81=AE=E6=96=B0=E6=97=A7log=E3=83=A1=E3=83=83?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=82=B8=E3=81=AE=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E6=9B=B8=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/src/data_store2.cc | 3 +- .../include/webcface/server/member_data.h | 10 +- server-store/src/member_data.cc | 167 ++++++++++++++---- 3 files changed, 134 insertions(+), 46 deletions(-) diff --git a/client/src/data_store2.cc b/client/src/data_store2.cc index 582733cf62..e85ec8ad7c 100644 --- a/client/src/data_store2.cc +++ b/client/src/data_store2.cc @@ -249,7 +249,6 @@ StrMap2 SyncDataStore2::transferReq() { // } } -// ライブラリ外からは参照できないが、testのためにexportしている template class SyncDataStore2; // test用 template class SyncDataStore2; template class SyncDataStore2; @@ -259,6 +258,6 @@ template class SyncDataStore2; template class SyncDataStore2; template class SyncDataStore2; template class SyncDataStore2; -template class SyncDataStore2>, int>; +template class SyncDataStore2, int>; } // namespace internal WEBCFACE_NS_END diff --git a/server-store/include/webcface/server/member_data.h b/server-store/include/webcface/server/member_data.h index a2013acdfb..5c0be7e7ba 100644 --- a/server-store/include/webcface/server/member_data.h +++ b/server-store/include/webcface/server/member_data.h @@ -87,7 +87,7 @@ struct MemberData { std::chrono::system_clock::time_point last_sync_time; //! リクエストしているmember,nameのペア StrMap2 value_req, text_req, view_req, image_req, - robot_model_req, canvas3d_req, canvas2d_req; + robot_model_req, canvas3d_req, canvas2d_req, log_req; // image_convert_thread[imageのmember][imageのfield] = // imageを変換してthisに送るスレッド @@ -96,10 +96,11 @@ struct MemberData { const SharedString &field); StrMap1>> robot_model; - StrSet1 log_req; bool hasReq(const SharedString &member); + //! 古いLogリクエスト ("default"のログを古いメッセージ形式で返す) + StrSet1 log_req_default; //! ログ全履歴 - std::shared_ptr> log; + StrMap1>> log; /*! * \brief まだ完了していない自分へのcall呼び出しのリスト * @@ -123,8 +124,7 @@ struct MemberData { const spdlog::sink_ptr &sink, spdlog::level::level_enum level) : sink(sink), logger_level(level), store(store), con(con), - remote_addr(remote_addr), - log(std::make_shared>()) { + remote_addr(remote_addr) { this->member_id = ++last_member_id; logger = std::make_shared( std::to_string(member_id) + "_(unknown client)", this->sink); diff --git a/server-store/src/member_data.cc b/server-store/src/member_data.cc index 055997df43..cdbe09bc70 100644 --- a/server-store/src/member_data.cc +++ b/server-store/src/member_data.cc @@ -263,11 +263,22 @@ void MemberData::onRecv(const std::string &message) { f.first.decode(), cd->member_id); } } - if (!cd->log->empty()) { - this->pack( - webcface::message::LogEntry{{}, cd->member_id}); - logger->trace("send log_entry of member {}", - cd->member_id); + for (const auto &f : cd->log) { + if (!f.first.startsWith(field_separator)) { + this->pack(webcface::message::Entry< + webcface::message::Log>{ + {}, cd->member_id, f.first}); + logger->trace("send log_entry {} of member {}", + f.first.decode(), cd->member_id); + // 古いクライアントのために古いLogEntryも送る + if (f.first == message::Log::defaultLogName()) { + this->pack(webcface::message::LogEntryDefault{ + {}, cd->member_id}); + logger->trace( + "send log_entry_default(obsolete) of member {}", + cd->member_id); + } + } } for (const auto &f : cd->func) { if (!f.first.startsWith(field_separator)) { @@ -607,42 +618,77 @@ void MemberData::onRecv(const std::string &message) { } break; } - case MessageKind::log: { - auto &v = *static_cast(obj.get()); - v.member_id = this->member_id; - logger->debug("log {} lines", v.log->size()); - if (store->keep_log >= 0 && - this->log->size() < - static_cast(store->keep_log) && - this->log->size() + v.log->size() >= - static_cast(store->keep_log)) { - logger->info("number of log lines reached {}, so the oldest " - "log will be romoved.", - store->keep_log); + case MessageKind::log: + case MessageKind::log_default: { + SharedString field; + std::shared_ptr> log_data; + if (kind == MessageKind::log) { + auto &v = *static_cast(obj.get()); + logger->debug("log {}: {} lines", v.field.decode(), + v.log->size()); + field = v.field; + log_data = v.log; + } else { + auto &v = + *static_cast(obj.get()); + logger->debug("log_default(obsolete) {} lines", v.log->size()); + field = message::Log::defaultLogName(); + log_data = v.log; } - if (this->log->empty()) { + if (!this->log.count(field)) { + this->log.emplace(field, log_data); store->forEach([&](auto cd) { if (cd->name != this->name) { - cd->pack( - webcface::message::LogEntry{{}, this->member_id}); - cd->logger->trace("send log_entry of member {}", - this->member_id); + cd->pack(webcface::message::Entry{ + {}, this->member_id, field}); + cd->logger->trace("send log_entry {} of member {}", + field.decode(), this->member_id); + // 古いクライアントのために古いLogEntryも送る + if (field == message::Log::defaultLogName()) { + cd->pack(webcface::message::LogEntryDefault{ + {}, this->member_id}); + cd->logger->trace( + "send log_entry_default(obsolete) of member {}", + this->member_id); + } } }); - } - for (auto &ll : *v.log) { - this->log->push_back(ll); - } - while (store->keep_log >= 0 && - this->log->size() > - static_cast(store->keep_log)) { - this->log->pop_front(); + } else { + auto this_log = this->log.at(field); + if (store->keep_log >= 0 && + this_log->size() < + static_cast(store->keep_log) && + this_log->size() + log_data->size() >= + static_cast(store->keep_log)) { + logger->info( + "number of log lines reached {}, so the oldest " + "log will be romoved.", + store->keep_log); + } + for (auto &ll : *log_data) { + this_log->push_back(ll); + } + while (store->keep_log >= 0 && + this_log->size() > + static_cast(store->keep_log)) { + this_log->pop_front(); + } } // このlogをsubscribeしてるところに送り返す store->forEach([&](auto cd) { - if (cd->log_req.count(this->name)) { - cd->pack(v); - cd->logger->trace("send log {} lines", v.log->size()); + auto req_field = findReqField(cd->log_req, this->name, field); + auto &req_id = req_field.first; + auto &sub_field = req_field.second; + if (req_id > 0) { + cd->pack(webcface::message::Res( + req_id, sub_field, log_data)); + cd->logger->trace("send log_res req_id={} + '{}'", req_id, + sub_field.decode()); + } + if (cd->log_req_default.count(this->name)) { + cd->pack(message::LogDefault{this->member_id, log_data}); + cd->logger->trace("send log_default(obsolete) {} lines", + log_data->size()); } }); break; @@ -922,14 +968,55 @@ void MemberData::onRecv(const std::string &message) { } break; } - case MessageKind::log_req: { - auto &s = *static_cast(obj.get()); + case MessageKind::req + MessageKind::log: { + auto &s = + *static_cast *>( + obj.get()); + logger->debug("request log ({}): {} from {}", s.req_id, + s.field.decode(), s.member.decode()); + // 指定した値を返す + store->findAndDo(s.member, [&](auto cd) { + // if (!this->hasReq(s.member)) { + // this->pack(webcface::message::Sync{cd->member_id, + // cd->last_sync_time}); + // logger->trace("send sync {}", this->member_id); + // } + for (const auto &it : cd->log) { + if (it.first == s.field || + it.first.startsWith(s.field.u8String() + + field_separator)) { + SharedString sub_field; + if (it.first == s.field) { + } else { + sub_field = SharedString::fromU8String( + it.first.u8String().substr( + s.field.u8String().size() + 1)); + } + this->pack( + webcface::message::Res{ + s.req_id, sub_field, it.second}); + logger->trace("send log_res {} lines, req_id={} + '{}'", + it.second->size(), s.req_id, + sub_field.decode()); + } + } + }); + log_req[s.member][s.field] = s.req_id; + break; + } + case MessageKind::log_req_default: { + auto &s = + *static_cast(obj.get()); logger->debug("request log from {}", s.member.decode()); - log_req.insert(s.member); + this->log_req_default.insert(s.member); // 指定した値を返す store->findAndDo(s.member, [&](auto cd) { - this->pack(webcface::message::Log{cd->member_id, cd->log}); - logger->trace("send log {} lines", cd->log->size()); + if (cd->log.count(message::Log::defaultLogName())) { + auto log_data = cd->log.at(message::Log::defaultLogName()); + this->pack( + webcface::message::LogDefault{cd->member_id, log_data}); + logger->trace("send log {} lines", log_data->size()); + } }); break; } @@ -947,7 +1034,9 @@ void MemberData::onRecv(const std::string &message) { case MessageKind::res + MessageKind::canvas2d: case MessageKind::entry + MessageKind::image: case MessageKind::res + MessageKind::image: - case MessageKind::log_entry: + case MessageKind::entry + MessageKind::log: + case MessageKind::res + MessageKind::log: + case MessageKind::log_entry_default: case MessageKind::sync_init_end: case MessageKind::ping_status: if (!message_kind_warned[kind]) { From 8d23e1329478cbeb72ccb5ddf477f821c996ef92 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Fri, 27 Sep 2024 13:21:54 +0900 Subject: [PATCH 05/10] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../include/webcface/internal/data_store2.h | 5 +- client/src/client_msg_recv.cc | 2 +- server-store/src/member_data.cc | 22 ++- tests/client_test.cc | 144 +++++++++------- tests/data_test.cc | 72 ++++---- tests/func_listener_test.cc | 2 +- tests/func_test.cc | 2 +- tests/logger_test.cc | 36 ++-- tests/server_test.cc | 160 +++++++++++++----- 9 files changed, 278 insertions(+), 167 deletions(-) diff --git a/client/include/webcface/internal/data_store2.h b/client/include/webcface/internal/data_store2.h index 6599b55ef0..efe735997c 100644 --- a/client/include/webcface/internal/data_store2.h +++ b/client/include/webcface/internal/data_store2.h @@ -212,7 +212,10 @@ using ImageData = ImageFrame; struct LogData { std::deque data; - std::size_t sent_lines; + std::size_t sent_lines = 0; + + LogData() = default; + explicit LogData(const std::deque &data) : data(data) {} std::vector getDiff() { auto begin = data.cbegin() + static_cast(sent_lines); diff --git a/client/src/client_msg_recv.cc b/client/src/client_msg_recv.cc index d0bda0275b..627c968c1a 100644 --- a/client/src/client_msg_recv.cc +++ b/client/src/client_msg_recv.cc @@ -274,7 +274,7 @@ void internal::ClientData::onRecv( auto &r = *static_cast *>(obj.get()); std::lock_guard lock_s(this->log_store.mtx); auto [member, field] = - this->view_store.getReq(r.req_id, r.sub_field); + this->log_store.getReq(r.req_id, r.sub_field); auto log_s = this->log_store.getRecv(member, field); if (!log_s) { log_s = std::make_shared(); diff --git a/server-store/src/member_data.cc b/server-store/src/member_data.cc index cdbe09bc70..b777562e73 100644 --- a/server-store/src/member_data.cc +++ b/server-store/src/member_data.cc @@ -275,7 +275,8 @@ void MemberData::onRecv(const std::string &message) { this->pack(webcface::message::LogEntryDefault{ {}, cd->member_id}); logger->trace( - "send log_entry_default(obsolete) of member {}", + "send log_entry_default(obsolete) of " + "member {}", cd->member_id); } } @@ -636,6 +637,19 @@ void MemberData::onRecv(const std::string &message) { log_data = v.log; } if (!this->log.count(field)) { + if (store->keep_log >= 0 && + log_data->size() > + static_cast(store->keep_log)) { + logger->info( + "number of log lines reached {}, so the oldest " + "log will be romoved.", + store->keep_log); + } + while (store->keep_log >= 0 && + log_data->size() > + static_cast(store->keep_log)) { + log_data->pop_front(); + } this->log.emplace(field, log_data); store->forEach([&](auto cd) { if (cd->name != this->name) { @@ -1007,7 +1021,8 @@ void MemberData::onRecv(const std::string &message) { case MessageKind::log_req_default: { auto &s = *static_cast(obj.get()); - logger->debug("request log from {}", s.member.decode()); + logger->debug("request log_default(obsolete) from {}", + s.member.decode()); this->log_req_default.insert(s.member); // 指定した値を返す store->findAndDo(s.member, [&](auto cd) { @@ -1015,7 +1030,8 @@ void MemberData::onRecv(const std::string &message) { auto log_data = cd->log.at(message::Log::defaultLogName()); this->pack( webcface::message::LogDefault{cd->member_id, log_data}); - logger->trace("send log {} lines", log_data->size()); + logger->trace("send log_default(obsolete) {} lines", + log_data->size()); } }); break; diff --git a/tests/client_test.cc b/tests/client_test.cc index a0f862367d..8870e48f4e 100644 --- a/tests/client_test.cc +++ b/tests/client_test.cc @@ -364,9 +364,9 @@ TEST_F(ClientTest, entry) { EXPECT_EQ(m.funcEntries()[0].nameW(), L"a"); EXPECT_FALSE(m.log().exists()); - dummy_s->send(message::LogEntry{{},10}); + dummy_s->send(message::Entry{{}, 10, "a"_ss}); wcli_->loopSyncFor(std::chrono::milliseconds(WEBCFACE_TEST_TIMEOUT)); - EXPECT_TRUE(m.log().exists()); + EXPECT_TRUE(m.log("a").exists()); m.onSync(callback()); dummy_s->send(message::Sync{10, std::chrono::system_clock::now()}); @@ -380,15 +380,15 @@ TEST_F(ClientTest, logSend) { while (!dummy_s->connected() || !wcli_->connected()) { wait(); } - auto ls = - std::make_shared>(std::deque{ - {0, std::chrono::system_clock::now(), - SharedString::fromU8String(std::string(100000, 'a'))}, - {1, std::chrono::system_clock::now(), "b"_ss}, - }); - data_->log_store.setRecv(self_name, ls); + auto ls = std::make_shared(std::deque{ + {0, std::chrono::system_clock::now(), + SharedString::fromU8String(std::string(100000, 'a'))}, + {1, std::chrono::system_clock::now(), "b"_ss}, + }); + data_->log_store.setSend("a"_ss, ls); wcli_->sync(); dummy_s->waitRecv([&](const auto &obj) { + EXPECT_EQ(obj.field, "a"_ss); EXPECT_EQ(obj.log->size(), 2u); EXPECT_EQ(obj.log->at(0).level_, 0); EXPECT_EQ(obj.log->at(0).message_.decode().size(), 100000u); @@ -397,7 +397,9 @@ TEST_F(ClientTest, logSend) { }); dummy_s->recvClear(); - ls->push_back(LogLineData{2, std::chrono::system_clock::now(), "c"_ss}); + ls->data.push_back( + LogLineData{2, std::chrono::system_clock::now(), "c"_ss}); + data_->log_store.setSend("a"_ss, ls); wcli_->sync(); dummy_s->waitRecv([&](const auto &obj) { EXPECT_EQ(obj.log->size(), 1u); @@ -411,79 +413,95 @@ TEST_F(ClientTest, logReq) { while (!dummy_s->connected() || !wcli_->connected()) { wait(); } - wcli_->member("a").log().tryGet(); - dummy_s->waitRecv( - [&](const auto &obj) { EXPECT_EQ(obj.member.u8String(), "a"); }); - wcli_->member("a").log().onChange(callback()); + wcli_->member("a").log("b").tryGet(); + dummy_s->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.member.u8String(), "a"); + EXPECT_EQ(obj.field.u8String(), "b"); + EXPECT_EQ(obj.req_id, 1u); + }); + wcli_->member("a").log("b").onChange(callback()); dummy_s->send(message::SyncInit{{}, "a"_ss, 10, "", "", ""}); wcli_->loopSyncFor(std::chrono::milliseconds(WEBCFACE_TEST_TIMEOUT)); - dummy_s->send(message::Log{ - 10, std::make_shared>( - std::deque{ - LogLineData{ - 0, std::chrono::system_clock::now(), - SharedString::fromU8String(std::string(100000, 'a'))} - .toMessage(), - LogLineData{1, std::chrono::system_clock::now(), "b"_ss} - .toMessage(), - })}); + dummy_s->send(message::Res{ + 1, ""_ss, + std::make_shared>( + std::deque{ + LogLineData{ + 0, std::chrono::system_clock::now(), + SharedString::fromU8String(std::string(100000, 'a'))} + .toMessage(), + LogLineData{1, std::chrono::system_clock::now(), "b"_ss} + .toMessage(), + })}); wcli_->loopSyncFor(std::chrono::milliseconds(WEBCFACE_TEST_TIMEOUT)); EXPECT_EQ(callback_called, 1); - EXPECT_TRUE(data_->log_store.getRecv("a"_ss).has_value()); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->size(), 2u); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->at(0).level_, 0); - EXPECT_EQ(data_->log_store.getRecv("a"_ss) + EXPECT_TRUE(data_->log_store.getRecv("a"_ss, "b"_ss).has_value()); + EXPECT_EQ(data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.size(), + 2u); + EXPECT_EQ( + data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.at(0).level_, 0); + EXPECT_EQ(data_->log_store.getRecv("a"_ss, "b"_ss) .value() - ->at(0) + ->data.at(0) .message_.u8String() .size(), 100000u); - dummy_s->send(message::Log{ - 10, std::make_shared>( - std::deque{ - LogLineData{2, std::chrono::system_clock::now(), "c"_ss} - .toMessage(), - })}); + dummy_s->send(message::Res{ + 1, ""_ss, + std::make_shared>( + std::deque{ + LogLineData{2, std::chrono::system_clock::now(), "c"_ss} + .toMessage(), + })}); wcli_->loopSyncFor(std::chrono::milliseconds(WEBCFACE_TEST_TIMEOUT)); EXPECT_EQ(callback_called, 2); - EXPECT_TRUE(data_->log_store.getRecv("a"_ss).has_value()); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->size(), 3u); + EXPECT_TRUE(data_->log_store.getRecv("a"_ss, "b"_ss).has_value()); + EXPECT_EQ(data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.size(), + 3u); // keep_lines以上のログは保存しない webcface::Log::keepLines(2); - dummy_s->send(message::Log{ - 10, std::make_shared>( - std::deque{ - LogLineData{3, std::chrono::system_clock::now(), "d"_ss} - .toMessage(), - })}); + dummy_s->send(message::Res{ + 1, ""_ss, + std::make_shared>( + std::deque{ + LogLineData{3, std::chrono::system_clock::now(), "d"_ss} + .toMessage(), + })}); wcli_->loopSyncFor(std::chrono::milliseconds(WEBCFACE_TEST_TIMEOUT)); EXPECT_EQ(callback_called, 3); - EXPECT_TRUE(data_->log_store.getRecv("a"_ss).has_value()); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->size(), 2u); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->at(0).level_, 2); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->at(1).level_, 3); + EXPECT_TRUE(data_->log_store.getRecv("a"_ss, "b"_ss).has_value()); + EXPECT_EQ(data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.size(), + 2u); + EXPECT_EQ( + data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.at(0).level_, 2); + EXPECT_EQ( + data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.at(1).level_, 3); - dummy_s->send(message::Log{ - 10, std::make_shared>( - std::deque{ - LogLineData{4, std::chrono::system_clock::now(), "d"_ss} - .toMessage(), - LogLineData{5, std::chrono::system_clock::now(), "d"_ss} - .toMessage(), - LogLineData{6, std::chrono::system_clock::now(), "d"_ss} - .toMessage(), - LogLineData{7, std::chrono::system_clock::now(), "d"_ss} - .toMessage(), - })}); + dummy_s->send(message::Res{ + 1, ""_ss, + std::make_shared>( + std::deque{ + LogLineData{4, std::chrono::system_clock::now(), "d"_ss} + .toMessage(), + LogLineData{5, std::chrono::system_clock::now(), "d"_ss} + .toMessage(), + LogLineData{6, std::chrono::system_clock::now(), "d"_ss} + .toMessage(), + LogLineData{7, std::chrono::system_clock::now(), "d"_ss} + .toMessage(), + })}); wcli_->loopSyncFor(std::chrono::milliseconds(WEBCFACE_TEST_TIMEOUT)); EXPECT_EQ(callback_called, 4); - EXPECT_TRUE(data_->log_store.getRecv("a"_ss).has_value()); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->size(), 2u); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->at(0).level_, 6); - EXPECT_EQ(data_->log_store.getRecv("a"_ss).value()->at(1).level_, 7); + EXPECT_TRUE(data_->log_store.getRecv("a"_ss, "b"_ss).has_value()); + EXPECT_EQ(data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.size(), + 2u); + EXPECT_EQ( + data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.at(0).level_, 6); + EXPECT_EQ( + data_->log_store.getRecv("a"_ss, "b"_ss).value()->data.at(1).level_, 7); webcface::Log::keepLines(1000); } diff --git a/tests/data_test.cc b/tests/data_test.cc index 916ab56632..530eba80c1 100644 --- a/tests/data_test.cc +++ b/tests/data_test.cc @@ -48,9 +48,12 @@ class DataTest : public ::testing::Test { Variant variant(const T1 &member, const T2 &name) { return Variant{field(member, name)}; } - Log log(const SharedString &member) { return Log{Field{data_, member}}; } - Log log(std::string_view member) { - return Log{Field{data_, SharedString::fromU8String(member)}}; + Log log(const SharedString &member, const SharedString &name) { + return Log{Field{data_, member, name}}; + } + Log log(std::string_view member, std::string_view name) { + return Log{Field{data_, SharedString::fromU8String(member), + SharedString::fromU8String(name)}}; } int callback_called; template @@ -75,7 +78,7 @@ TEST_F(DataTest, field) { EXPECT_EQ(text("a", "b").nameW(), L"b"); EXPECT_EQ(text("a", "b").child("c").name(), "b.c"); EXPECT_EQ(text("a", "b").child(L"c").name(), "b.c"); - EXPECT_EQ(log("a").member().name(), "a"); + EXPECT_EQ(log("a", "b").member().name(), "a"); EXPECT_THROW(Value().tryGet(), std::runtime_error); EXPECT_THROW(Text().tryGet(), std::runtime_error); @@ -97,8 +100,9 @@ TEST_F(DataTest, eventTarget) { data_->text_change_event["a"_ss]["b"_ss]->operator()(field("a", "b")); EXPECT_EQ(callback_called, 1); callback_called = 0; - log("a").onChange(callback()); - data_->log_append_event["a"_ss]->operator()(field("a")); + + log("a", "b").onChange(callback()); + data_->log_append_event["a"_ss]["b"_ss]->operator()(field("a")); EXPECT_EQ(callback_called, 1); callback_called = 0; } @@ -220,45 +224,47 @@ TEST_F(DataTest, textGet) { TEST_F(DataTest, logGet) { using namespace std::chrono; auto logs = - std::make_shared>(std::deque{ + std::make_shared(std::deque{ {1, system_clock::now(), "a"_ss}, {2, system_clock::now(), "b"_ss}, {3, system_clock::now(), "c"_ss}, }); - data_->log_store.setRecv("a"_ss, logs); - EXPECT_EQ(log("a").tryGet().value().size(), 3u); - EXPECT_EQ(log("a").tryGetW().value().size(), 3u); - ASSERT_EQ(log("a").get().size(), 3u); - ASSERT_EQ(log("a").getW().size(), 3u); - EXPECT_EQ(log("a").get()[2].level(), 3); - EXPECT_EQ(log("a").getW()[2].level(), 3); - EXPECT_EQ(log("a").get()[2].message(), "c"); - EXPECT_EQ(log("a").getW()[2].message(), L"c"); - EXPECT_EQ(log("b").tryGet(), std::nullopt); - EXPECT_EQ(log("b").tryGetW(), std::nullopt); - EXPECT_EQ(log("b").get().size(), 0u); - EXPECT_EQ(log("b").getW().size(), 0u); - EXPECT_EQ(data_->log_store.transferReq().at("a"_ss), true); - EXPECT_EQ(data_->log_store.transferReq().at("b"_ss), true); - ASSERT_TRUE(log(self_name).tryGet().has_value()); - ASSERT_TRUE(log(self_name).tryGetW().has_value()); - EXPECT_EQ(log(self_name).tryGet()->size(), 0u); - EXPECT_EQ(log(self_name).tryGetW()->size(), 0u); + data_->log_store.setRecv("a"_ss, "b"_ss, logs); + EXPECT_EQ(log("a", "b").tryGet().value().size(), 3u); + EXPECT_EQ(log("a", "b").tryGetW().value().size(), 3u); + ASSERT_EQ(log("a", "b").get().size(), 3u); + ASSERT_EQ(log("a", "b").getW().size(), 3u); + EXPECT_EQ(log("a", "b").get()[2].level(), 3); + EXPECT_EQ(log("a", "b").getW()[2].level(), 3); + EXPECT_EQ(log("a", "b").get()[2].message(), "c"); + EXPECT_EQ(log("a", "b").getW()[2].message(), L"c"); + EXPECT_EQ(log("a", "c").tryGet(), std::nullopt); + EXPECT_EQ(log("a", "c").tryGetW(), std::nullopt); + EXPECT_EQ(log("a", "c").get().size(), 0u); + EXPECT_EQ(log("a", "c").getW().size(), 0u); + EXPECT_EQ(data_->log_store.transferReq().at("a"_ss).at("b"_ss), 1u); + EXPECT_EQ(data_->log_store.transferReq().at("a"_ss).at("c"_ss), 2u); + EXPECT_FALSE(log(self_name, "default"_ss).tryGet().has_value()); + EXPECT_FALSE(log(self_name, "default"_ss).tryGetW().has_value()); + EXPECT_FALSE(log(self_name, "a"_ss).tryGet().has_value()); + EXPECT_FALSE(log(self_name, "a"_ss).tryGetW().has_value()); + EXPECT_EQ(log(self_name, "a"_ss).get().size(), 0u); + EXPECT_EQ(log(self_name, "a"_ss).getW().size(), 0u); EXPECT_EQ(data_->log_store.transferReq().count(self_name), 0u); - log("d").onChange(callback()); - EXPECT_EQ(data_->log_store.transferReq().at("d"_ss), true); + log("a", "d").onChange(callback()); + EXPECT_EQ(data_->log_store.transferReq().at("a"_ss).at("d"_ss), 3u); } TEST_F(DataTest, logClear) { using namespace std::chrono; auto logs = - std::make_shared>(std::deque{ + std::make_shared(std::deque{ {1, system_clock::now(), "a"_ss}, {2, system_clock::now(), "b"_ss}, {3, system_clock::now(), "c"_ss}, }); - data_->log_store.setRecv("a"_ss, logs); - log("a").clear(); - EXPECT_EQ(log("a").tryGet().value().size(), 0u); - EXPECT_EQ(log("a").tryGetW().value().size(), 0u); + data_->log_store.setRecv("a"_ss, "b"_ss, logs); + log("a", "b").clear(); + EXPECT_EQ(log("a", "b").tryGet().value().size(), 0u); + EXPECT_EQ(log("a", "b").tryGetW().value().size(), 0u); } // todo: hidden, free diff --git a/tests/func_listener_test.cc b/tests/func_listener_test.cc index e1547b4235..48efbea38b 100644 --- a/tests/func_listener_test.cc +++ b/tests/func_listener_test.cc @@ -63,7 +63,7 @@ TEST_F(FuncListenerTest, listen) { ValType::none_); EXPECT_EQ(f.returnType(), ValType::none_); EXPECT_EQ(func(self_name, "a").returnType(), ValType::none_); - EXPECT_EQ((*data_->func_store.getRecv(self_name, "a"_ss))->args.size(), 0u); + EXPECT_EQ((*data_->func_store.getRecv(self_name, "a"_ss))->args->size(), 0u); EXPECT_EQ(f.args().size(), 0u); EXPECT_EQ(func(self_name, "a").args().size(), 0u); diff --git a/tests/func_test.cc b/tests/func_test.cc index 2c92b754d6..85806480db 100644 --- a/tests/func_test.cc +++ b/tests/func_test.cc @@ -129,7 +129,7 @@ TEST_F(FuncTest, funcSet) { ValType::none_); EXPECT_EQ(f.returnType(), ValType::none_); EXPECT_EQ(func(self_name, "a").returnType(), ValType::none_); - EXPECT_EQ((*data_->func_store.getRecv(self_name, "a"_ss))->args.size(), 0u); + EXPECT_EQ((*data_->func_store.getRecv(self_name, "a"_ss))->args->size(), 0u); EXPECT_EQ(f.args().size(), 0u); EXPECT_EQ(func(self_name, "a").args().size(), 0u); diff --git a/tests/logger_test.cc b/tests/logger_test.cc index 0884b6bebf..7f32ec0877 100644 --- a/tests/logger_test.cc +++ b/tests/logger_test.cc @@ -28,36 +28,36 @@ class LoggerTest : public ::testing::Test { }; TEST_F(LoggerTest, loggerBuf) { - LoggerBuf b(data_.get()); + LoggerBuf b(data_.get(), "buf"_ss); std::ostream os(&b); os << "a\nb" << std::endl; auto cerr_buf = std::cerr.rdbuf(); std::cerr.rdbuf(&b); std::cerr << "c" << std::endl; - auto ls = data_->log_store.getRecv(self_name); - ASSERT_EQ((*ls)->size(), 3u); - EXPECT_EQ((**ls)[0].level_, 2); - EXPECT_EQ((**ls)[0].message_.u8String(), "a"); - EXPECT_EQ((**ls)[1].level_, 2); - EXPECT_EQ((**ls)[1].message_.u8String(), "b"); - EXPECT_EQ((**ls)[2].level_, 2); - EXPECT_EQ((**ls)[2].message_.u8String(), "c"); + auto ls = data_->log_store.getRecv(self_name, "buf"_ss); + ASSERT_EQ((*ls)->data.size(), 3u); + EXPECT_EQ((*ls)->data[0].level_, 2); + EXPECT_EQ((*ls)->data[0].message_.u8String(), "a"); + EXPECT_EQ((*ls)->data[1].level_, 2); + EXPECT_EQ((*ls)->data[1].message_.u8String(), "b"); + EXPECT_EQ((*ls)->data[2].level_, 2); + EXPECT_EQ((*ls)->data[2].message_.u8String(), "c"); std::cerr.rdbuf(cerr_buf); } TEST_F(LoggerTest, loggerBufW) { - LoggerBufW b(data_.get()); + LoggerBufW b(data_.get(), "buf"_ss); std::wostream os(&b); os << L"a\nb" << std::endl; auto wcerr_buf = std::wcerr.rdbuf(); std::wcerr.rdbuf(&b); std::wcerr << L"c" << std::endl; - auto ls = data_->log_store.getRecv(self_name); - ASSERT_EQ((*ls)->size(), 3u); - EXPECT_EQ((**ls)[0].level_, 2); - EXPECT_EQ((**ls)[0].message_.u8String(), "a"); - EXPECT_EQ((**ls)[1].level_, 2); - EXPECT_EQ((**ls)[1].message_.u8String(), "b"); - EXPECT_EQ((**ls)[2].level_, 2); - EXPECT_EQ((**ls)[2].message_.u8String(), "c"); + auto ls = data_->log_store.getRecv(self_name, "buf"_ss); + ASSERT_EQ((*ls)->data.size(), 3u); + EXPECT_EQ((*ls)->data[0].level_, 2); + EXPECT_EQ((*ls)->data[0].message_.u8String(), "a"); + EXPECT_EQ((*ls)->data[1].level_, 2); + EXPECT_EQ((*ls)->data[1].message_.u8String(), "b"); + EXPECT_EQ((*ls)->data[2].level_, 2); + EXPECT_EQ((*ls)->data[2].message_.u8String(), "c"); std::wcerr.rdbuf(wcerr_buf); } diff --git a/tests/server_test.cc b/tests/server_test.cc index 431d776c88..531dc4ebc3 100644 --- a/tests/server_test.cc +++ b/tests/server_test.cc @@ -145,11 +145,12 @@ TEST_F(ServerTest, entry) { ImageColorMode::bgr} .toMessage()}); dummy_c1->send(message::Log{ - 0, std::make_shared>( - std::deque{ - LogLineData{0, std::chrono::system_clock::now(), "0"_ss} - .toMessage(), - })}); + "default"_ss, + std::make_shared>( + std::deque{ + LogLineData{0, std::chrono::system_clock::now(), "0"_ss} + .toMessage(), + })}); dummy_c1->send(message::FuncInfo{0, "a"_ss, ValType::none_, {}}); wait(); // c2が接続したタイミングでのc1のentryが全部返る @@ -187,7 +188,11 @@ TEST_F(ServerTest, entry) { EXPECT_EQ(obj.member_id, 1u); EXPECT_EQ(obj.field.u8String(), "a"); }); - dummy_c2->waitRecv( + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.member_id, 1u); + EXPECT_EQ(obj.field.u8String(), "default"); + }); + dummy_c2->waitRecv( [&](const auto &obj) { EXPECT_EQ(obj.member_id, 1u); }); dummy_c2->waitRecv([&](const auto &obj) { EXPECT_EQ(obj.member_id, 1u); @@ -264,25 +269,83 @@ TEST_F(ServerTest, log) { dummy_c2 = std::make_shared(); dummy_c2->send(message::SyncInit{{}, ""_ss, 0, "", "", ""}); wait(); + dummy_c3 = std::make_shared(); + dummy_c3->send(message::SyncInit{{}, ""_ss, 0, "", "", ""}); + wait(); dummy_c1->send(message::Log{ - 0, std::make_shared>( - std::deque{ - LogLineData{0, std::chrono::system_clock::now(), "0"_ss} - .toMessage(), - })}); + "default"_ss, + std::make_shared>( + std::deque{ + LogLineData{0, std::chrono::system_clock::now(), "0"_ss} + .toMessage(), + })}); // 初回のみリクエストしていなくても送られる - dummy_c2->waitRecv( + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.member_id, 1u); + EXPECT_EQ(obj.field, "default"_ss); + }); + dummy_c2->waitRecv( [&](const auto &obj) { EXPECT_EQ(obj.member_id, 1u); }); - dummy_c2->send(message::LogReq{{}, "c1"_ss}); + dummy_c2->send(message::Req{{}, "c1"_ss, "default"_ss, 1}); // req時の値 - // keep_logを超えたので最後の3行だけ送られる - dummy_c2->waitRecv([&](const auto &obj) { + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.req_id, 1u); + EXPECT_EQ(obj.log->size(), 1u); + EXPECT_EQ(obj.log->at(0).level_, 0); + EXPECT_EQ(obj.log->at(0).message_, "0"_ss); + }); + + // 古いクライアントの場合 + dummy_c3->send(message::LogReqDefault{{}, "c1"_ss}); + dummy_c3->waitRecv([&](const auto &obj) { EXPECT_EQ(obj.member_id, 1u); EXPECT_EQ(obj.log->size(), 1u); EXPECT_EQ(obj.log->at(0).level_, 0); EXPECT_EQ(obj.log->at(0).message_, "0"_ss); }); } +TEST_F(ServerTest, logNamed) { + dummy_c1 = std::make_shared(); + dummy_c1->send(message::SyncInit{{}, "c1"_ss, 0, "", "", ""}); + wait(); + dummy_c2 = std::make_shared(); + dummy_c2->send(message::SyncInit{{}, ""_ss, 0, "", "", ""}); + wait(); + dummy_c3 = std::make_shared(); + dummy_c3->send(message::SyncInit{{}, ""_ss, 0, "", "", ""}); + wait(); + dummy_c1->send(message::Log{ + "aaa"_ss, + std::make_shared>( + std::deque{ + LogLineData{0, std::chrono::system_clock::now(), "0"_ss} + .toMessage(), + })}); + // 初回のみリクエストしていなくても送られる + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.member_id, 1u); + EXPECT_EQ(obj.field, "aaa"_ss); + }); + dummy_c2->recv( + [&](const auto &) { + ADD_FAILURE() << "should not receive LogEntryDefault"; + }, + [&]() {}); + dummy_c2->send(message::Req{{}, "c1"_ss, "aaa"_ss, 1}); + // req時の値 + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.req_id, 1u); + EXPECT_EQ(obj.log->size(), 1u); + EXPECT_EQ(obj.log->at(0).level_, 0); + EXPECT_EQ(obj.log->at(0).message_, "0"_ss); + }); + + // 古いクライアントの場合 + dummy_c3->send(message::LogReqDefault{{}, "c1"_ss}); + dummy_c2->recv( + [&](const auto &) { ADD_FAILURE() << "should not receive LogDefault"; }, + [&]() {}); +} TEST_F(ServerTest, logKeep) { dummy_c1 = std::make_shared(); wait(); @@ -291,27 +354,31 @@ TEST_F(ServerTest, logKeep) { server->store->keep_log = 3; dummy_c1->send(message::SyncInit{{}, "c1"_ss, 0, "", "", ""}); dummy_c1->send(message::Log{ - 0, std::make_shared>( - std::deque{ - LogLineData{0, std::chrono::system_clock::now(), "0"_ss} - .toMessage(), - LogLineData{1, std::chrono::system_clock::now(), "1"_ss} - .toMessage(), - LogLineData{2, std::chrono::system_clock::now(), "2"_ss} - .toMessage(), - LogLineData{3, std::chrono::system_clock::now(), "3"_ss} - .toMessage(), - })}); + "default"_ss, + std::make_shared>( + std::deque{ + LogLineData{0, std::chrono::system_clock::now(), "0"_ss} + .toMessage(), + LogLineData{1, std::chrono::system_clock::now(), "1"_ss} + .toMessage(), + LogLineData{2, std::chrono::system_clock::now(), "2"_ss} + .toMessage(), + LogLineData{3, std::chrono::system_clock::now(), "3"_ss} + .toMessage(), + })}); wait(); dummy_c2->send(message::SyncInit{{}, ""_ss, 0, "", "", ""}); // syncinit時に送られる - dummy_c2->waitRecv( + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.member_id, 1u); + EXPECT_EQ(obj.field, "default"_ss); + }); + dummy_c2->waitRecv( [&](const auto &obj) { EXPECT_EQ(obj.member_id, 1u); }); - dummy_c2->send(message::LogReq{{}, "c1"_ss}); + dummy_c2->send(message::Req{{}, "c1"_ss, "default"_ss, 1}); // req時の値 - // keep_logを超えたので最後の3行だけ送られる - dummy_c2->waitRecv([&](const auto &obj) { - EXPECT_EQ(obj.member_id, 1u); + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.req_id, 1u); EXPECT_EQ(obj.log->size(), 3u); EXPECT_EQ(obj.log->at(0).level_, 1); EXPECT_EQ(obj.log->at(0).message_, "1"_ss); @@ -320,21 +387,22 @@ TEST_F(ServerTest, logKeep) { // 変化後の値 dummy_c1->send(message::Log{ - 0, std::make_shared>( - std::deque{ - LogLineData{4, std::chrono::system_clock::now(), "4"_ss} - .toMessage(), - LogLineData{5, std::chrono::system_clock::now(), "5"_ss} - .toMessage(), - LogLineData{6, std::chrono::system_clock::now(), "6"_ss} - .toMessage(), - LogLineData{7, std::chrono::system_clock::now(), "7"_ss} - .toMessage(), - LogLineData{8, std::chrono::system_clock::now(), "8"_ss} - .toMessage(), - })}); - dummy_c2->waitRecv([&](const auto &obj) { - EXPECT_EQ(obj.member_id, 1u); + "default"_ss, + std::make_shared>( + std::deque{ + LogLineData{4, std::chrono::system_clock::now(), "4"_ss} + .toMessage(), + LogLineData{5, std::chrono::system_clock::now(), "5"_ss} + .toMessage(), + LogLineData{6, std::chrono::system_clock::now(), "6"_ss} + .toMessage(), + LogLineData{7, std::chrono::system_clock::now(), "7"_ss} + .toMessage(), + LogLineData{8, std::chrono::system_clock::now(), "8"_ss} + .toMessage(), + })}); + dummy_c2->waitRecv>([&](const auto &obj) { + EXPECT_EQ(obj.req_id, 1u); EXPECT_EQ(obj.log->size(), 5u); EXPECT_EQ(obj.log->at(0).level_, 4); EXPECT_EQ(obj.log->at(0).message_, "4"_ss); From 90b12078e14cf9a7630de15475cce906a7f17824 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Mon, 30 Sep 2024 12:55:38 +0900 Subject: [PATCH 06/10] =?UTF-8?q?handle=E3=82=92=E4=BD=BF=E3=81=86?= =?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=AB=E3=83=90=E3=83=83=E3=82=AF=E3=81=8C?= =?UTF-8?q?throw=E3=81=97=E3=81=9F=E5=A0=B4=E5=90=88=E3=82=82catch?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/include/webcface/func.h | 32 ++++++++++++++++++--------- tests/func_test.cc | 40 ++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/client/include/webcface/func.h b/client/include/webcface/func.h index 6c3d98d832..d4ac850610 100644 --- a/client/include/webcface/func.h +++ b/client/include/webcface/func.h @@ -162,7 +162,7 @@ class WEBCFACE_DLL Func : protected Field { std::function &&func_impl) const; /*! * 引数の個数不定バージョン - * + * */ const Func &setImpl(ValType return_type, std::nullopt_t, std::function &&func_impl) const; @@ -170,14 +170,14 @@ class WEBCFACE_DLL Func : protected Field { setImpl(const std::shared_ptr &func_info) const; /*! - * f_run()を実行し結果をCallHandleに渡す + * f_run()を実行し例外を投げた場合はrejectする * */ template - static void tryRun(F1 &&f_run, const CallHandle &handle) { + static void catchAll(F1 &&f_run, const CallHandle &handle) { ValAdaptor error; try { - handle.respond(f_run()); + f_run(); return; } catch (const std::exception &e) { error = e.what(); @@ -194,6 +194,14 @@ class WEBCFACE_DLL Func : protected Field { } handle.reject(error); } + /*! + * f_run()を実行し結果をCallHandleに渡す + * + */ + template + static void tryRun(F1 &&f_run, const CallHandle &handle) { + catchAll([&] { handle.respond(f_run()); }, handle); + } static constexpr std::nullptr_t TraitOk = nullptr; @@ -313,6 +321,7 @@ class WEBCFACE_DLL Func : protected Field { * * ver2.0〜: 自動でrespondされることはないので、 * 関数が受け取ったhandleを別スレッドに渡すなどして、 * ここでセットした関数の終了後にrespond()やreject()することも可能。 + * * ver2.4〜: 例外をthrowした場合catchしてreject()する。 * * \param args 引数の型などの情報 * \param return_type 戻り値の型 @@ -332,7 +341,7 @@ class WEBCFACE_DLL Func : protected Field { [args_size, callback = std::move(callback)]( const CallHandle &handle) { if (handle.assertArgsNum(args_size)) { - callback(handle); + catchAll([&] { callback(handle); }, handle); } }); } @@ -344,6 +353,7 @@ class WEBCFACE_DLL Func : protected Field { * * 関数がrespond()もreject()もせず終了した場合自動でrespondされることはないので、 * 関数が受け取ったhandleを別スレッドに渡すなどして、 * ここでセットした関数の終了後にrespond()やreject()することも可能。 + * * ver2.4〜: 例外をthrowした場合catchしてreject()する。 * * \param args 引数の型などの情報 * \param return_type 戻り値の型 @@ -366,7 +376,7 @@ class WEBCFACE_DLL Func : protected Field { std::move(callback))](const CallHandle &handle) { if (handle.assertArgsNum(args_size)) { std::thread([callback, handle] { - callback->operator()(handle); + catchAll([&] { callback->operator()(handle); }, handle); }).detach(); } }); @@ -380,6 +390,7 @@ class WEBCFACE_DLL Func : protected Field { * ここでセットした関数の終了後にrespond()やreject()することも可能。 * * setArgs(), setReturnType() で引数の個数や型と戻り値の型を指定する。 * 指定しない場合、引数なし戻り値なしとみなされる。 + * * 例外をthrowした場合catchしてreject()する。 * * \param func セットする関数または関数オブジェクト。 * 引数としてCallHandleを1つ取り、戻り値はvoidで、 @@ -395,7 +406,7 @@ class WEBCFACE_DLL Func : protected Field { [base = *this, callback = std::move(callback)]( const CallHandle &handle) { if (handle.assertArgsNum(base.args().size())) { - callback(handle); + catchAll([&] { callback(handle); }, handle); } }); } @@ -408,6 +419,7 @@ class WEBCFACE_DLL Func : protected Field { * ここでセットした関数の終了後にrespond()やreject()することも可能。 * * setArgs(), setReturnType() で引数の個数や型と戻り値の型を指定する。 * 指定しない場合、引数なし戻り値なしとみなされる。 + * * 例外をthrowした場合catchしてreject()する。 * * \param func セットする関数または関数オブジェクト。 * 引数としてCallHandleを1つ取り、戻り値はvoidで、 @@ -426,7 +438,7 @@ class WEBCFACE_DLL Func : protected Field { std::move(callback))](const CallHandle &handle) { if (handle.assertArgsNum(base.args().size())) { std::thread([callback, handle] { - callback->operator()(handle); + catchAll([&] { callback->operator()(handle); }, handle); }).detach(); } }); @@ -550,10 +562,10 @@ class WEBCFACE_DLL Func : protected Field { /*! * \brief 戻り値の型の情報を更新する * \since ver2.4 - * + * * set()やsetAsync()で通常の関数をセットした場合戻り値の型は自動的に取得されるので * setReturnType() を呼ぶ必要はない。 - * + * */ const Func &setReturnType(ValType return_type) const; diff --git a/tests/func_test.cc b/tests/func_test.cc index 85806480db..3bb36a15ec 100644 --- a/tests/func_test.cc +++ b/tests/func_test.cc @@ -344,6 +344,46 @@ TEST_F(FuncTest, funcAsyncRun) { EXPECT_FALSE(func(self_name, "a").runAsync().rejection().empty()); EXPECT_TRUE(func(self_name, "a").runAsync().found()); } +TEST_F(FuncTest, funcHandleRun) { + // 引数と戻り値 + auto main_id = std::this_thread::get_id(); + webcface::CallHandle handle_copy; + func(self_name, "a") + .set([&](const webcface::CallHandle &h) { + if (h.args().at(0) == 0) { + throw std::invalid_argument("a == 0"); + } + if (h.args().at(0) == 1) { + h.reject("a == 1"); + return; + } + EXPECT_EQ(h.args().at(0).asInt(), 123); + EXPECT_EQ(h.args().at(1).asDouble(), 123.45); + EXPECT_EQ(h.args().at(2).asString(), "a"); + EXPECT_TRUE(h.args().at(3).asBool()); + // setしてるのでrunAsyncしてもmainスレッドで実行される + EXPECT_EQ(std::this_thread::get_id(), main_id); + handle_copy = h; + return; + }).setArgs({Arg(), Arg(), Arg(), Arg()}); + auto ret_a = func(self_name, "a").runAsync(0, 123.45, "a", true); + EXPECT_TRUE(ret_a.isError()); + EXPECT_EQ(ret_a.rejection(), "a == 0"); + ret_a = func(self_name, "a").runAsync(1, 123.45, "a", true); + EXPECT_TRUE(ret_a.isError()); + EXPECT_EQ(ret_a.rejection(), "a == 1"); + ret_a = func(self_name, "a").runAsync(2); + EXPECT_TRUE(ret_a.isError()); + EXPECT_FALSE(ret_a.rejection().empty()); + + ret_a = func(self_name, "a").runAsync(123, 123.45, "a", true); + EXPECT_TRUE(ret_a.found()); + EXPECT_FALSE(ret_a.finished()); + handle_copy.respond(123.45); + EXPECT_TRUE(ret_a.finished()); + EXPECT_FALSE(ret_a.isError()); + EXPECT_EQ(static_cast(ret_a.response()), 123.45); +} TEST_F(FuncTest, funcRunRemote) { // 未接続 auto res = func("a", "b").runAsync(1.23, true, "abc"); From dcbbc2365803f9cae707d6de17fb38f57bfc8704 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Mon, 30 Sep 2024 23:03:39 +0900 Subject: [PATCH 07/10] =?UTF-8?q?logEntries(),=20onLogEntry()=20=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/include/webcface/field.h | 4 +++ .../webcface/internal/client_internal.h | 2 ++ client/include/webcface/member.h | 25 +++++++++++++------ client/src/client_msg_recv.cc | 21 ++++++---------- client/src/field.cc | 3 +++ client/src/member.cc | 6 +++++ tests/client_test.cc | 3 +++ 7 files changed, 42 insertions(+), 22 deletions(-) diff --git a/client/include/webcface/field.h b/client/include/webcface/field.h index 2f0f587e0f..eeaf199036 100644 --- a/client/include/webcface/field.h +++ b/client/include/webcface/field.h @@ -206,6 +206,10 @@ struct WEBCFACE_DLL Field : public FieldBase { std::vector canvas3DEntries() const; std::vector canvas2DEntries() const; std::vector imageEntries() const; + /*! + * \since ver2.4 + */ + std::vector logEntries() const; /*! * \brief memberがselfならtrue diff --git a/client/include/webcface/internal/client_internal.h b/client/include/webcface/internal/client_internal.h index ac82208a4b..ecdbf67e50 100644 --- a/client/include/webcface/internal/client_internal.h +++ b/client/include/webcface/internal/client_internal.h @@ -423,6 +423,8 @@ struct ClientData : std::enable_shared_from_this { canvas3d_entry_event; StrMap1>> canvas2d_entry_event; + StrMap1>> + log_entry_event; std::shared_ptr logger_internal; std::mutex logger_m; diff --git a/client/include/webcface/member.h b/client/include/webcface/member.h index bbc2deda65..b08ff84ff1 100644 --- a/client/include/webcface/member.h +++ b/client/include/webcface/member.h @@ -64,7 +64,7 @@ class WEBCFACE_DLL Member : protected Field { Log log(std::wstring_view name) const { return this->Field::log(name); } /*! * ver2.4〜: nameを省略した場合 "default" として送信される。 - * + * */ Log log() const; /*! @@ -83,6 +83,7 @@ class WEBCFACE_DLL Member : protected Field { using Field::canvas3DEntries; using Field::funcEntries; using Field::imageEntries; + using Field::logEntries; using Field::robotModelEntries; using Field::textEntries; using Field::valueEntries; @@ -131,7 +132,7 @@ class WEBCFACE_DLL Member : protected Field { onValueEntry(std::function callback) const; /*! * \brief textが追加された時のイベント - *\since ver2.0 + * \since ver2.0 * \param callback Text型の引数をとる関数 * */ @@ -139,7 +140,7 @@ class WEBCFACE_DLL Member : protected Field { onTextEntry(std::function callback) const; /*! * \brief robotModelが追加された時のイベント - *\since ver2.0 + * \since ver2.0 * \param callback RobotModel型の引数をとる関数 * */ @@ -147,7 +148,7 @@ class WEBCFACE_DLL Member : protected Field { std::function callback) const; /*! * \brief funcが追加された時のイベント - *\since ver2.0 + * \since ver2.0 * \param callback Func型の引数をとる関数 * */ @@ -155,7 +156,7 @@ class WEBCFACE_DLL Member : protected Field { onFuncEntry(std::function callback) const; /*! * \brief imageが追加されたときのイベント - *\since ver2.0 + * \since ver2.0 * \param callback Image型の引数をとる関数 * */ @@ -163,7 +164,7 @@ class WEBCFACE_DLL Member : protected Field { onImageEntry(std::function callback) const; /*! * \brief viewが追加されたときのイベント - *\since ver2.0 + * \since ver2.0 * \param callback View型の引数をとる関数 * */ @@ -171,7 +172,7 @@ class WEBCFACE_DLL Member : protected Field { onViewEntry(std::function callback) const; /*! * \brief canvas3dが追加されたときのイベント - *\since ver2.0 + * \since ver2.0 * \param callback Canvas3D型の引数をとる関数 * */ @@ -179,12 +180,20 @@ class WEBCFACE_DLL Member : protected Field { std::function callback) const; /*! * \brief canvas2dが追加されたときのイベント - *\since ver2.0 + * \since ver2.0 * \param callback Canvas2D型の引数をとる関数 * */ const Member &onCanvas2DEntry( std::function callback) const; + /*! + * \brief logが追加されたときのイベント + * \since ver2.4 + * \param callback Log型の引数をとる関数 + * + */ + const Member & + onLogEntry(std::function callback) const; /*! * \brief Memberがsync()したときのイベント diff --git a/client/src/client_msg_recv.cc b/client/src/client_msg_recv.cc index 627c968c1a..bab5dfea89 100644 --- a/client/src/client_msg_recv.cc +++ b/client/src/client_msg_recv.cc @@ -271,11 +271,13 @@ void internal::ClientData::onRecv( break; } case MessageKind::log + MessageKind::res: { - auto &r = *static_cast *>(obj.get()); + auto &r = + *static_cast *>( + obj.get()); std::lock_guard lock_s(this->log_store.mtx); auto [member, field] = this->log_store.getReq(r.req_id, r.sub_field); - auto log_s = this->log_store.getRecv(member, field); + auto log_s = this->log_store.getRecv(member, field); if (!log_s) { log_s = std::make_shared(); this->log_store.setRecv(member, field, *log_s); @@ -449,18 +451,9 @@ void internal::ClientData::onRecv( break; } case MessageKind::entry + MessageKind::log: { - auto &r = *static_cast *>(obj.get()); - auto member = this->getMemberNameFromId(r.member_id); - this->log_store.setEntry(member, r.field); - // std::decay_tlog_entry_event.at(member))> cl; - // { - // std::lock_guard lock(this->event_m); - // cl = findFromMap1(this->log_entry_event, - // member).value_or(nullptr); - // } - // if (cl && *cl) { - // cl->operator()(Field{this->shared_from_this(), member}); - // } + auto &r = *static_cast< + webcface::message::Entry *>(obj.get()); + onRecvEntry(this, r, this->log_store, this->log_entry_event); break; } case MessageKind::func_info: { diff --git a/client/src/field.cc b/client/src/field.cc index e7c0b6549f..b2e2edec5c 100644 --- a/client/src/field.cc +++ b/client/src/field.cc @@ -117,6 +117,9 @@ std::vector Field::canvas2DEntries() const { std::vector Field::imageEntries() const { return entries(this, dataLock()->image_store); } +std::vector Field::logEntries() const { + return entries(this, dataLock()->log_store); +} bool Field::expired() const { return data_w.expired(); } diff --git a/client/src/member.cc b/client/src/member.cc index f2478d22a3..3d871ba858 100644 --- a/client/src/member.cc +++ b/client/src/member.cc @@ -65,6 +65,12 @@ const Member &Member::onImageEntry(std::function callback) const { std::make_shared>(std::move(callback)); return *this; } +const Member &Member::onLogEntry(std::function callback) const { + std::lock_guard lock(dataLock()->event_m); + dataLock()->log_entry_event[member_] = + std::make_shared>(std::move(callback)); + return *this; +} const Member &Member::onSync(std::function callback) const { std::lock_guard lock(dataLock()->event_m); dataLock()->sync_event[member_] = diff --git a/tests/client_test.cc b/tests/client_test.cc index 8870e48f4e..9b1ee12751 100644 --- a/tests/client_test.cc +++ b/tests/client_test.cc @@ -364,8 +364,11 @@ TEST_F(ClientTest, entry) { EXPECT_EQ(m.funcEntries()[0].nameW(), L"a"); EXPECT_FALSE(m.log().exists()); + m.onLogEntry(callback()); dummy_s->send(message::Entry{{}, 10, "a"_ss}); wcli_->loopSyncFor(std::chrono::milliseconds(WEBCFACE_TEST_TIMEOUT)); + EXPECT_EQ(callback_called, 1); + callback_called = 0; EXPECT_TRUE(m.log("a").exists()); m.onSync(callback()); From ec60600f0b6eeabbd7784578347ebdb9649fc9b0 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Wed, 2 Oct 2024 17:24:36 +0900 Subject: [PATCH 08/10] =?UTF-8?q?=E3=83=89=E3=82=AD=E3=83=A5=E3=83=A1?= =?UTF-8?q?=E3=83=B3=E3=83=88=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/53_func.md | 28 +++++++++++++++++++++++ docs/55_log.md | 56 ++++++++++++++++++++++++++++++++++++++++++++-- docs/81_message.md | 47 +++++++++++++++++++++++++++++++++++--- 4 files changed, 127 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4e7699e7ee..c2bc2d91da 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ WebCFaceの通信データ形式はOSやライブラリの言語によらず共 つまり、異なるバージョンのクライアント同士でも、異なるバージョンのOSでも問題なく通信が可能です。 (C++のver1.1.6以前を除く) ただしそれぞれのクライアントのバージョンよりサーバーの方が新しいバージョンである必要があります。 +サーバーよりクライアントのほうが新しい場合の動作は保証しません。 ### WebUI diff --git a/docs/53_func.md b/docs/53_func.md index ef12a71ae0..1aca6056d9 100644 --- a/docs/53_func.md +++ b/docs/53_func.md @@ -41,6 +41,7 @@ * 2.0 代入演算子はdeprecatedになりました * 同じ名前のFuncに複数回関数をセットすると上書きされ、後に登録した関数のみが呼び出されます。 ただし引数や戻り値の型などの情報は更新されず、最初の関数のものと同じになります。 + * 関数の中で例外をthrowした場合WebCFace側でcatchされ、エラーメッセージが呼び出し元に返ります。 * 2.0 set()で登録した関数はClient::sync()時に同じスレッドで実行されます。 そのため長時間かかる関数を登録するとその間他の処理がブロックされることになります。 @@ -91,6 +92,31 @@ }); ``` + + + 2.4 + 引数に webcface::CallHandle をとる関数をセットすることもできます。 + ```cpp + wcli.func("hoge").set([](webcface::CallHandle handle){ + double arg = handle.args().at(0).asDouble(); + handle.respond(123); // return value + }).setArgs({ + webcface::Arg("a").type(webcface::ValType::double_), + }); + ``` + * 通常の関数のset()の場合と同様、関数を非同期(別スレッド)で実行したい場合は setAsync() を使用してください。 + * 関数のsetのあとに、 setArgs() で受け取りたい引数の個数分の webcface::Arg() を渡し、引数名や戻り値の型を指定します。 + * 引数名などの情報が不要な場合でも引数の個数分 Arg() を渡す必要があります。 + * callbackが呼び出されたとき、引数に渡されたhandleを介して `handle.args()` でFuncを呼び出した引数を取得できます。 + * setArgs() で指定した引数の個数と呼び出し時の個数が一致しない場合は、callbackが実行される前に呼び出し元にエラーメッセージが投げられます。 + (そのため登録した関数の側で引数の個数チェックをする必要はないです) + * 値を返すのはreturnではなくhandleを介して `handle.respond(戻り値)` を使います。 + * 戻り値が不要な場合はrespondに何も渡さず `handle.respond();` とします。 + * エラーメッセージを返すには `handle.reject(エラーメッセージ)` を使います。 + * 関数の中で例外をthrowした場合もエラーメッセージが呼び出し元に返ります。 + * respondかrejectを呼び出すまで、Funcの呼び出し元には関数呼び出しの結果は送られません。 + * そのためhandleを関数の外のスコープの変数に保存(コピー)したり、別スレッドに持ち込むことで任意のタイミングで結果を返すことも可能です。 + - C \since 1.9 @@ -158,6 +184,7 @@ wcli.func("hoge").set(() => {/* ... */}); wcli.func("hoge").set((a: number, b: string) => 3.1415); ``` + * 関数の中で例外をthrowした場合WebCFace側でcatchされ、エラーメッセージが呼び出し元に返ります。 * 同じ名前のFuncに複数回関数をセットすると上書きされ、後に登録した関数のみが呼び出されます。 ただし引数や戻り値の型などの情報は更新されず、最初の関数のものと同じになります。 @@ -170,6 +197,7 @@ wcli.func("hoge").set(hoge) wcli.func("lambda").set(lambda x: return x + 5) # ラムダ式なども可 ``` + * 関数の中で例外をraiseした場合WebCFace側でcatchされ、エラーメッセージが呼び出し元に返ります。 * 同じ名前のFuncに複数回関数をセットすると上書きされ、後に登録した関数のみが呼び出されます。 ただし引数や戻り値の型などの情報は更新されず、最初の関数のものと同じになります。 * setを明示的に呼び出す代わりにfuncオブジェクトをデコレータにすると簡単に登録できます。 diff --git a/docs/55_log.md b/docs/55_log.md index b4a38622ef..f39c07ed33 100644 --- a/docs/55_log.md +++ b/docs/55_log.md @@ -18,6 +18,14 @@ trace(0), debug(1), info(2), warning(3), error(4), critical(5) の6段階のレ \note WebCFaceはログレベルを単に数値として扱うので、-1以下や6以上のレベルも一応使用可能です。 +\warning +2.41.92.1 +でLogの送受信メッセージを仕様変更しています。 +クライアントがこれら以上のバージョンでサーバーが2.3以前の場合Logデータは送受信できません。 +(serverのログ出力に `Unknown message kind 8` というwarningが表示されます。) +逆にサーバーが2.4以上でクライアントが古い場合は問題ありません。 + + ## コマンドライン ```sh webcface-send -t log @@ -60,6 +68,15 @@ webcface-send -t log wostreamを使用したい場合は wcli.loggerWOStream(), wcli.loggerWStreamBuf() を使用するとWebCFaceに出力すると同時にstderrにも出力されます。 その際Windowsでは出力文字列は Encoding::usingUTF8() の設定に従いUTF-8またはANSIに変換されるため、出力したいコンソールのコードページに設定を合わせてください。 + 2.4 + Valueなど他のデータ型と同様名前をつけて複数のLogを送信することができます。 + 名前を省略した場合、および過去のバージョンから送信されたログデータは `"default"` という名前のLogとして扱われます。 + ```cpp + wcli.log("hoge").append(webcface::level::info, "this is info"); + wcli.loggerOStream("hoge") << "hello" << std::endl; + std::cout.rdbuf(wcli.loggerStreamBuf("hoge")); + ``` + - JavaScript 1.8 Log.append() でログを送信することができます。 @@ -69,6 +86,13 @@ webcface-send -t log wcli.log().append(3, "this is error"); ``` + 1.9 + Valueなど他のデータ型と同様名前をつけて複数のLogを送信することができます。 + 名前を省略した場合、および過去のバージョンから送信されたログデータは `"default"` という名前のLogとして扱われます。 + ```ts + wcli.log("hoge").append(2, "this is info"); + ``` + - Python 2.0 Log.append() でログを送信することができます。 @@ -102,6 +126,13 @@ webcface-send -t log print("hello") ``` + 2.1 + Valueなど他のデータ型と同様名前をつけて複数のLogを送信することができます。 + 名前を省略した場合、および過去のバージョンから送信されたログデータは `"default"` という名前のLogとして扱われます。 + ```cpp + wcli.log("hoge").append(2, "this is info"); + ``` + ### 外部ライブラリの利用 @@ -241,7 +272,7 @@ serverの起動時のオプションでこの行数は変更できます。([2-1 1.2自動的に別スレッドで送信されます。 * そのデータを受信した後([4-1. Client](./41_client.md)を参照)、再度tryGet()することで値が得られます。 * Log::get() はstd::nulloptの代わりに空のvectorを返します。 - + \warning 2.1 Clientはmemberごとに最大1000行までのログを保持しています。 @@ -272,6 +303,11 @@ serverの起動時のオプションでこの行数は変更できます。([2-1 2.0 tryGetW(), getW() ではstringの代わりにwstringを使った webcface::LogLineW のリストで返ります。 + 2.4 + Valueなど他のデータ型と同様、 + `wcli.member("foo").log("hoge")` のように受信するLogの名前を指定できます。 + 名前を省略した場合、および過去のバージョンから送信されたログデータは `"default"` という名前のLogとして扱われます。 + - JavaScript Member.log() でLogクラスのオブジェクトが得られ、 Log.tryGet() でデータのリクエストをするとともにログが得られます。 @@ -316,6 +352,11 @@ serverの起動時のオプションでこの行数は変更できます。([2-1 1.1 Log::request() で明示的にリクエストを送信することもできます。 + 1.9 + Valueなど他のデータ型と同様、 + `wcli.member("foo").log("hoge")` のように受信するLogの名前を指定できます。 + 名前を省略した場合、および過去のバージョンから送信されたログデータは `"default"` という名前のLogとして扱われます。 + - Python Member.log() でLogクラスのオブジェクトが得られ、 Log.try_get() でデータのリクエストをするとともにログが得られます。 @@ -343,11 +384,16 @@ serverの起動時のオプションでこの行数は変更できます。([2-1 Log::request() で明示的にリクエストを送信することもできます。 + 2.1 + Valueなど他のデータ型と同様、 + `wcli.member("foo").log("hoge")` のように受信するLogの名前を指定できます。 + 名前を省略した場合、および過去のバージョンから送信されたログデータは `"default"` という名前のLogとして扱われます。 + ### Entry -\since 2.11.81.8 +\since 2.11.82.0 (サーバーが2.1以降の場合のみ) @@ -357,6 +403,12 @@ Log.exists() はログが少なくとも1行存在する場合、trueを返します。 tryGet() と違い、ログデータそのものを受信するリクエストは送られません。 +2.4 +1.9 +2.1 +Valueなどの他の型と同様、 Memberが送信しているLogのリストを取得したり、 +LogEntry イベントも使えるようになりました。 + ### Event 受信したデータが変化したときにコールバックを呼び出すことができます。 diff --git a/docs/81_message.md b/docs/81_message.md index fffcbf9450..45f6e6bcf6 100644 --- a/docs/81_message.md +++ b/docs/81_message.md @@ -457,7 +457,42 @@ data = { ``` ## Log -### log (kind = 85) +### log (kind = 8) +\since 2.4 + +```js +data = { + f: string, // name + l: { + v: number, // level 0〜5 + t: number, // time + m: string, // message + }[], +} +``` +* クライアント→サーバーに新しく追加されたログ差分のみ送ります + +### log entry (kind = 28) +* value entryと同様 + +### log req (kind = 48) +* value reqと同様 + +### log res (kind = 68) +```js +data = { + i: number, // request id + f: string, // sub field name + l: {...}[], // data +} +``` +* 初回はすべてのログが送られますが、2回目以降は前回送信した log res の後に追加された差分のみが送られます + +### log_default (kind = 85) +* ver2.3まで使っていた古いメッセージ仕様です。 + * 2.4以降のサーバーは、古いlog_reqメッセージを送ってきたクライアントに対してのみこの古い仕様でログを返します。 + * またこの古いlogメッセージは新logメッセージでnameを`"default"`にしたものと同等に扱われます + ```js data = { m: number, // member id @@ -472,9 +507,12 @@ data = { * リクエストがあればサーバー→各クライアントにそのまま送り返します * リクエスト直後、サーバーが保持しているログの全履歴を1つのlogメッセージにまとめてクライアントに送ります -### log entry (kind = 92) +### log_entry_default (kind = 92) \since 2.1 +* ver2.1〜2.3まで使っていた古いメッセージ仕様です。 + * 2.4以降のサーバーは、nameが`"default"`のLogデータに関してのみ、新旧両方のクライアントに対応できるようこのentryと新しいlog entryの両方を送信します + ```js data = { m: number // member id @@ -482,7 +520,10 @@ data = { ``` * 1行以上のログデータがある場合、サーバー→各クライアントに知らせます -### log req (kind = 86) +### log_req_default (kind = 86) +* ver2.3まで使っていた古いメッセージ仕様です。 + * 2.4以降のサーバーは、nameが`"default"`のLogをリクエストしたものとして処理します + ```js data = { M: string // member name From 7fa2c4b5ee4c6fc397df901328e38f44d1184feb Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Wed, 2 Oct 2024 19:00:31 +0900 Subject: [PATCH 09/10] v2.4.0 --- CHANGELOG.md | 10 ++++++++++ CMakeLists.txt | 2 +- Doxyfile | 2 +- README.md | 36 ++++++++++++++++++------------------ docs/31_setup.md | 4 ++-- meson.build | 4 ++-- 6 files changed, 34 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35bd3c0189..6621899f22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,13 @@ +## [2.4.0] - 2024-10-02 +### Changed +* Logメッセージを仕様変更、Logに名前をつけて複数送信できるようになった (#410) + * ver2.3以前のサーバーと2.4以降のクライアントの間ではLogを送受信できなくなります +* Funcに登録した引数にCallHandleをとる関数がthrowしたときそれをcatchしてrejectする +### Added +* Func::set() で引数にCallHandleをとる関数を渡してセットし、その後からsetArgs()などオプションを使う使い方 +* Field::logEntries() +* Member::onLogEntry() + ## [2.3.0] - 2024-09-17 ### Fixed * ver2.2.1以降でserverがセグフォすることがあったバグを修正 (#407) diff --git a/CMakeLists.txt b/CMakeLists.txt index 310ed66dda..917f576419 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.5) -project(webcface VERSION 2.3.0) +project(webcface VERSION 2.4.0) if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(IS_MAIN on) diff --git a/Doxyfile b/Doxyfile index c839f66165..d2f9dedbc5 100644 --- a/Doxyfile +++ b/Doxyfile @@ -48,7 +48,7 @@ PROJECT_NAME = "WebCFace" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = "2.3.0" +PROJECT_NUMBER = "2.4.0" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/README.md b/README.md index c2bc2d91da..1d0df80cc5 100644 --- a/README.md +++ b/README.md @@ -195,9 +195,9 @@ Ubuntu20.04でビルドしているため、それより古いUbuntuでは動作
x86_64 ```sh -curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.3.0/webcface_2.3.0_linux_amd64.zip -sudo unzip webcface_2.3.0_linux_amd64.zip -d /opt/webcface -rm webcface_2.3.0_linux_amd64.zip +curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.4.0/webcface_2.4.0_linux_amd64.zip +sudo unzip webcface_2.4.0_linux_amd64.zip -d /opt/webcface +rm webcface_2.4.0_linux_amd64.zip echo 'export PATH="/opt/webcface/bin:$PATH"' >> ~/.bashrc echo 'export PKG_CONFIG_PATH="/opt/webcface/lib/x86_64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH"' >> ~/.bashrc sudo ln -sf /opt/webcface/lib/systemd/system/*.service /etc/systemd/system/ @@ -207,9 +207,9 @@ sudo ln -sf /opt/webcface/lib/systemd/system/*.service /etc/systemd/system/
arm64 ```sh -curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.3.0/webcface_2.3.0_linux_arm64.zip -sudo unzip webcface_2.3.0_linux_arm64.zip -d /opt/webcface -rm webcface_2.3.0_linux_arm64.zip +curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.4.0/webcface_2.4.0_linux_arm64.zip +sudo unzip webcface_2.4.0_linux_arm64.zip -d /opt/webcface +rm webcface_2.4.0_linux_arm64.zip echo 'export PATH="/opt/webcface/bin:$PATH"' >> ~/.bashrc echo 'export PKG_CONFIG_PATH="/opt/webcface/lib/aarch64-linux-gnu/pkgconfig:$PKG_CONFIG_PATH"' >> ~/.bashrc sudo ln -sf /opt/webcface/lib/systemd/system/*.service /etc/systemd/system/ @@ -219,9 +219,9 @@ sudo ln -sf /opt/webcface/lib/systemd/system/*.service /etc/systemd/system/
armhf ```sh -curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.3.0/webcface_2.3.0_linux_armhf.zip -sudo unzip webcface_2.3.0_linux_armhf.zip -d /opt/webcface -rm webcface_2.3.0_linux_armhf.zip +curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.4.0/webcface_2.4.0_linux_armhf.zip +sudo unzip webcface_2.4.0_linux_armhf.zip -d /opt/webcface +rm webcface_2.4.0_linux_armhf.zip echo 'export PATH="/opt/webcface/bin:$PATH"' >> ~/.bashrc echo 'export PKG_CONFIG_PATH="/opt/webcface/lib/arm-linux-gnueabihf/pkgconfig:$PKG_CONFIG_PATH"' >> ~/.bashrc sudo ln -sf /opt/webcface/lib/systemd/system/*.service /etc/systemd/system/ @@ -242,7 +242,7 @@ Ubuntu20.04でビルドしているため、それより古いUbuntuでは動作
x86_64 ```sh -curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.3.0/webcface_2.3.0_amd64.deb +curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.4.0/webcface_2.4.0_amd64.deb curl -fLO https://github.com/na-trium-144/webcface-tools/releases/download/v2.0.1/webcface-tools_2.0.1_amd64.deb curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-webui_1.8.3_all.deb curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-desktop_1.8.3_linux_amd64.deb @@ -254,7 +254,7 @@ rm ./webcface*.deb
arm64 ```sh -curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.3.0/webcface_2.3.0_arm64.deb +curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.4.0/webcface_2.4.0_arm64.deb curl -fLO https://github.com/na-trium-144/webcface-tools/releases/download/v2.0.1/webcface-tools_2.0.1_arm64.deb curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-webui_1.8.3_all.deb curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-desktop_1.8.3_linux_arm64.deb @@ -266,7 +266,7 @@ rm ./webcface*.deb
armhf ```sh -curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.3.0/webcface_2.3.0_armhf.deb +curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.4.0/webcface_2.4.0_armhf.deb curl -fLO https://github.com/na-trium-144/webcface-tools/releases/download/v2.0.1/webcface-tools_2.0.1_armhf.deb curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-webui_1.8.3_all.deb curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-desktop_1.8.3_linux_armv7l.deb @@ -288,16 +288,16 @@ Universalバイナリになっており、IntelMacもAppleシリコンも共通 macOS 12 (Monterey) でビルドしているので、それより古いMacでは動かないかもしれません。 ```sh -curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.3.0/webcface_2.3.0_macos_universal.zip -curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.3.0/webcface-desktop_2.3.0_macos_app.zip +curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.4.0/webcface_2.4.0_macos_universal.zip +curl -fLO https://github.com/na-trium-144/webcface-package/releases/download/v2.4.0/webcface-desktop_2.4.0_macos_app.zip ``` sudo権限が使用できれば以下のように webcface_universal を /opt/webcface に、 webcface-desktop_app を /Applications に展開するのがおすすめです。 ```sh -sudo unzip webcface_2.3.0_macos_universal.zip -d /opt/webcface -sudo unzip webcface-desktop_2.3.0_macos_app.zip -d /Applications -rm webcface_2.3.0_macos_universal.zip -rm webcface-desktop_2.3.0_macos_app.zip +sudo unzip webcface_2.4.0_macos_universal.zip -d /opt/webcface +sudo unzip webcface-desktop_2.4.0_macos_app.zip -d /Applications +rm webcface_2.4.0_macos_universal.zip +rm webcface-desktop_2.4.0_macos_app.zip ``` また、展開したディレクトリ内の bin/ をPATHに、 lib/pkgconfig/ をPKG_CONFIG_PATHに追加してください。 diff --git a/docs/31_setup.md b/docs/31_setup.md index d2d0502807..bfa7918908 100644 --- a/docs/31_setup.md +++ b/docs/31_setup.md @@ -32,7 +32,7 @@ MesonまたはCMakeを使用する場合は、subproject/subdirectoryとしてWe ``` [wrap-git] url = https://github.com/na-trium-144/webcface.git - revision = v2.3.0 + revision = v2.4.0 depth = 1 [provide] dependency_names = webcface @@ -66,7 +66,7 @@ MesonまたはCMakeを使用する場合は、subproject/subdirectoryとしてWe ```cmake FetchContent_Declare(webcface GIT_REPOSITORY https://github.com/na-trium-144/webcface.git - GIT_TAG v2.3.0 + GIT_TAG v2.4.0 ) FetchContent_MakeAvailable(webcface) ``` diff --git a/meson.build b/meson.build index 4146510f34..fef2f42eae 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('webcface', 'c', 'cpp', - version: '2.3.0', + version: '2.4.0', license: 'MIT', meson_version: '>=1.3.0', default_options: [ @@ -17,7 +17,7 @@ cxx = meson.get_compiler('cpp') py = find_program('python3') webcface_abi_major = '20' # ABIの破壊的変更で1増やす -webcface_abi_minor = '2' # ABIの追加で1増やす +webcface_abi_minor = '3' # ABIの追加で1増やす webcface_webui_version = '1.8.3' webcface_description = 'Web-based IPC & Dashboard-like UI' From 2d717c0b093b4014b05f889072ee97d50d771fe2 Mon Sep 17 00:00:00 2001 From: na-trium-144 <100704180+na-trium-144@users.noreply.github.com> Date: Wed, 2 Oct 2024 20:56:43 +0900 Subject: [PATCH 10/10] webui 1.9.0 --- README.md | 12 ++++++------ meson.build | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1d0df80cc5..307fc10dec 100644 --- a/README.md +++ b/README.md @@ -244,8 +244,8 @@ Ubuntu20.04でビルドしているため、それより古いUbuntuでは動作 ```sh curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.4.0/webcface_2.4.0_amd64.deb curl -fLO https://github.com/na-trium-144/webcface-tools/releases/download/v2.0.1/webcface-tools_2.0.1_amd64.deb -curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-webui_1.8.3_all.deb -curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-desktop_1.8.3_linux_amd64.deb +curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.9.0/webcface-webui_1.9.0_all.deb +curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.9.0/webcface-desktop_1.9.0_linux_amd64.deb sudo apt install ./webcface*.deb rm ./webcface*.deb ``` @@ -256,8 +256,8 @@ rm ./webcface*.deb ```sh curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.4.0/webcface_2.4.0_arm64.deb curl -fLO https://github.com/na-trium-144/webcface-tools/releases/download/v2.0.1/webcface-tools_2.0.1_arm64.deb -curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-webui_1.8.3_all.deb -curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-desktop_1.8.3_linux_arm64.deb +curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.9.0/webcface-webui_1.9.0_all.deb +curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.9.0/webcface-desktop_1.9.0_linux_arm64.deb sudo apt install ./webcface*.deb rm ./webcface*.deb ``` @@ -268,8 +268,8 @@ rm ./webcface*.deb ```sh curl -fLO https://github.com/na-trium-144/webcface/releases/download/v2.4.0/webcface_2.4.0_armhf.deb curl -fLO https://github.com/na-trium-144/webcface-tools/releases/download/v2.0.1/webcface-tools_2.0.1_armhf.deb -curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-webui_1.8.3_all.deb -curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.8.3/webcface-desktop_1.8.3_linux_armv7l.deb +curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.9.0/webcface-webui_1.9.0_all.deb +curl -fLO https://github.com/na-trium-144/webcface-webui/releases/download/v1.9.0/webcface-desktop_1.9.0_linux_armv7l.deb sudo apt install ./webcface*.deb rm ./webcface*.deb ``` diff --git a/meson.build b/meson.build index fef2f42eae..0e9fb41e22 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ py = find_program('python3') webcface_abi_major = '20' # ABIの破壊的変更で1増やす webcface_abi_minor = '3' # ABIの追加で1増やす -webcface_webui_version = '1.8.3' +webcface_webui_version = '1.9.0' webcface_description = 'Web-based IPC & Dashboard-like UI' webcface_contact = 'na-trium-144@users.noreply.github.com'