2024-01-16 09:51:34 +00:00
|
|
|
|
#include "imgprc_mgr.h"
|
|
|
|
|
|
|
|
|
|
#include <json/gb_json.h>
|
|
|
|
|
#include <sane_opt_json/device_opt.h>
|
|
|
|
|
#include <huagao/hgscanner_error.h>
|
|
|
|
|
#include <base/packet.h>
|
|
|
|
|
|
2024-01-19 08:54:58 +00:00
|
|
|
|
#include "./algs/rebuild.h"
|
2024-01-23 09:34:20 +00:00
|
|
|
|
#include "./algs/stretch.h"
|
2024-01-24 04:05:05 +00:00
|
|
|
|
#include "./algs/auto_crop.h"
|
2024-01-25 06:13:24 +00:00
|
|
|
|
#include "./algs/color_correct.h"
|
|
|
|
|
#include "./algs/ImageProcess_Public.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// imgproc_mgr
|
|
|
|
|
#define ADD_IMG_PROCESSOR(cls) \
|
|
|
|
|
{ \
|
|
|
|
|
cls *obj = new cls(); \
|
|
|
|
|
opts_->add(obj); \
|
|
|
|
|
processors_.push_back(obj); \
|
|
|
|
|
}
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
|
|
|
|
static std::string device_opt_json[] = {
|
2024-01-30 03:43:06 +00:00
|
|
|
|
"{\"is-multiout\":{\"cat\":\"base\",\"group\":\"base\",\"title\":\"\\u591a\\u6d41\\u8f93\\u51fa\",\"desc\":\"\\u540c\\u65f6\\u8f93\\u51fa\\u591a\\u79cd\\u989c\\u8272\\u6a21\\u5f0f\\u7684\\u56fe\\u50cf\",\"type\":\"bool\",\"fix-id\":34817,\"ui-pos\":10,\"auth\":0,\"size\":4,\"cur\":false,\"default\":false},\"multiout-type\":{\"cat\":\"base\",\"group\":\"base\",\"title\":\"\\u591a\\u6d41\\u8f93\\u51fa\\u7c7b\\u578b\",\"desc\":\"\\u9009\\u62e9\\u591a\\u6d41\\u8f93\\u51fa\\u7684\\u7c7b\\u578b\",\"type\":\"string\",\"fix-id\":34818,\"ui-pos\":11,\"auth\":0,\"enabled\":false,\"size\":66,\"cur\":\"\\u5f69\\u8272+\\u7070\\u5ea6+\\u9ed1\\u767d\",\"default\":\"\\u5f69\\u8272+\\u7070\\u5ea6+\\u9ed1\\u767d\",\"range\":[\"\\u5f69\\u8272+\\u7070\\u5ea6+\\u9ed1\\u767d\",\"\\u5f69\\u8272+\\u7070\\u5ea6\",\"\\u5f69\\u8272+\\u9ed1\\u767d\",\"\\u7070\\u5ea6+\\u9ed1\\u767d\"],\"depend\":\"is-multiout==true\"},\"mode\":{\"cat\":\"base\",\"group\":\"base\",\"title\":\"\\u989c\\u8272\\u6a21\\u5f0f\",\"desc\":\"\\u9009\\u62e9\\u8272\\u5f69\\u6a21\\u5f0f\",\"type\":\"string\",\"fix-id\":34819,\"ui-pos\":15,\"auth\":0,\"size\":24,\"cur\":\"24\\u4f4d\\u5f69\\u8272\",\"default\":\"24\\u4f4d\\u5f69\\u8272\",\"range\":[\"24\\u4f4d\\u5f69\\u8272\",\"256\\u7ea7\\u7070\\u5ea6\",\"\\u9ed1\\u767d\",\"\\u989c\\u8272\\u81ea\\u52a8\\u8bc6\\u522b\"],\"depend\":\"is-multiout!=true\"},\"dump-img\":{\"cat\":\"base\",\"group\":\"\\u9ad8\\u7ea7\\u8bbe\\u7f6e\",\"title\":\"\\u8f93\\u51fa\\u4e2d\\u95f4\\u56fe\\u50cf\",\"desc\":\"\\u8f93\\u51fa\\u5404\\u7b97\\u6cd5\\u4e2d\\u95f4\\u7ed3\\u679c\\u56fe\\u50cf\",\"type\":\"bool\",\"ui-pos\":10,\"auth\":0,\"affect\":2,\"size\":4,\"cur\":false,\"default\":false},\"cis-rebuild\":{\"cat\":\"advanced\",\"group\":\"\\u9ad8\\u7ea7\\u8bbe\\u7f6e\",\"title\":\"\\u56fe\\u50cf\\u62fc\\u63a5\",\"desc\":\"\\u4eceCIS\\u6570\\u636e\\u6d41\\u91cd\\u5efa\\u56fe\\u50cf\",\"type\":\"bool\",\"ui-pos\":20,\"auth\":0,\"affect\":2,\"size\":4,\"cur\":true,\"default\":true},\"stretch\":{\"cat\":\"advanced\",\"group\":\"\\u9ad8\\u7ea7\\u8bbe\\u7f6e\",\"title\":\"\\u56fe\\u50cf\\u62c9\\u4f38\",\"desc\":\"\\u5c06\\u786c\\u4ef6\\u5206\\u8fa8\\u7387\\u62c9\\u4f38\\u5230\\u7528\\u6237\\u6307\\u5b9a\\u7684\\u5206\\u8fa8\\u7387\",\"type\":\"bool\",\"ui-pos\":30,\"auth\":0,\"affect\":2,\"size\":4,\"cur\":true,\"default\":true}}"
|
2024-01-16 09:51:34 +00:00
|
|
|
|
};
|
|
|
|
|
|
2024-01-30 03:43:06 +00:00
|
|
|
|
|
2024-01-16 09:51:34 +00:00
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
// imgproc_mgr
|
|
|
|
|
|
|
|
|
|
imgproc_mgr::imgproc_mgr(std::function<void(data_source_ptr)> sender
|
|
|
|
|
, device_option* devopts
|
|
|
|
|
)
|
|
|
|
|
: img_sender_(sender), opts_(devopts), prc_que_("prcimg")
|
|
|
|
|
{
|
2024-01-23 09:34:20 +00:00
|
|
|
|
ADD_THIS_JSON();
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
|
|
|
|
if (opts_)
|
|
|
|
|
opts_->add_ref();
|
|
|
|
|
else
|
2024-01-19 08:54:58 +00:00
|
|
|
|
opts_ = new device_option(true);
|
|
|
|
|
load_processor(nullptr);
|
2024-01-23 07:07:17 +00:00
|
|
|
|
rebuild_.reset(new rebuild());
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
|
|
|
|
auto thrd = [&](void) -> void
|
|
|
|
|
{
|
|
|
|
|
thread_worker();
|
|
|
|
|
};
|
2024-01-24 09:40:41 +00:00
|
|
|
|
auto restart = [this](const char* thread_name) ->void
|
|
|
|
|
{
|
|
|
|
|
auto thrd = [this](void) -> void
|
|
|
|
|
{
|
|
|
|
|
thread_worker();
|
|
|
|
|
};
|
|
|
|
|
workers_.stop(thread_name);
|
|
|
|
|
add_busy_worker(-1);
|
|
|
|
|
printf("\nrestart imgproc_mgr::thread_worker\n\n");
|
|
|
|
|
workers_.start(thrd, "imgproc_mgr::thread_worker", (void*)&imgproc_mgr::thread_worker);
|
|
|
|
|
};
|
|
|
|
|
workers_.set_exception_handler(restart);
|
2024-01-16 09:51:34 +00:00
|
|
|
|
workers_.start(thrd, "imgproc_mgr::thread_worker", (void*)&imgproc_mgr::thread_worker);
|
|
|
|
|
}
|
|
|
|
|
imgproc_mgr::~imgproc_mgr()
|
|
|
|
|
{
|
|
|
|
|
clear();
|
|
|
|
|
opts_->release();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool imgproc_mgr::sort_processor_by_pos(image_processor* l, image_processor* r)
|
|
|
|
|
{
|
|
|
|
|
return l->get_position() < r->get_position();
|
|
|
|
|
}
|
|
|
|
|
bool imgproc_mgr::sort_image_packet(image_packet_ptr l, image_packet_ptr r)
|
|
|
|
|
{
|
|
|
|
|
return l->get_paper_index() < r->get_paper_index();
|
|
|
|
|
}
|
|
|
|
|
data_source_ptr imgproc_mgr::scan_finished_packet(uint32_t scanid, uint32_t err)
|
|
|
|
|
{
|
|
|
|
|
dyn_mem_ptr reply = dyn_mem::memory(sizeof(PACK_BASE));
|
|
|
|
|
BASE_PACKET_REPLY(*((LPPACK_BASE)reply->ptr()), PACK_CMD_SCAN_FINISHED_ROGER, scanid, err);
|
|
|
|
|
reply->set_len(sizeof(PACK_BASE));
|
|
|
|
|
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
image_packet_ptr imgproc_mgr::image_sent_packet(LPPACKIMAGE head, dyn_mem_ptr img, uint32_t scanid, void* info, size_t info_l)
|
|
|
|
|
{
|
|
|
|
|
image_packet_ptr pimg = new image_packet(head, img, scanid, info, info_l);
|
|
|
|
|
|
|
|
|
|
return pimg;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint32_t imgproc_mgr::add_busy_worker(int inc)
|
|
|
|
|
{
|
|
|
|
|
SIMPLE_LOCK(working_cnt_lock_);
|
|
|
|
|
working_cnt_ += inc;
|
|
|
|
|
|
|
|
|
|
return working_cnt_;
|
|
|
|
|
}
|
|
|
|
|
void imgproc_mgr::thread_worker(void)
|
|
|
|
|
{
|
|
|
|
|
RAWIMG img;
|
2024-01-19 08:54:58 +00:00
|
|
|
|
while(run_)
|
2024-01-16 09:51:34 +00:00
|
|
|
|
{
|
2024-01-19 08:54:58 +00:00
|
|
|
|
if(prc_que_.take(img, true))
|
|
|
|
|
{
|
|
|
|
|
add_busy_worker();
|
|
|
|
|
|
|
|
|
|
// try
|
|
|
|
|
// {
|
|
|
|
|
process(&img);
|
|
|
|
|
// }
|
|
|
|
|
// catch(const std::exception& e)
|
|
|
|
|
// {
|
|
|
|
|
// printf("exception occurs when process paper %d: %s!\n", img.info.pos.paper_ind, e.what());
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
add_busy_worker(-1);
|
|
|
|
|
}
|
2024-01-16 09:51:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void imgproc_mgr::process(RAWIMG* img)
|
|
|
|
|
{
|
|
|
|
|
if(img->img)
|
|
|
|
|
{
|
2024-01-27 09:43:13 +00:00
|
|
|
|
std::vector<PROCIMGINFO> in, out, *src = &in, *dst = &out, *swp = nullptr;
|
2024-01-23 07:07:17 +00:00
|
|
|
|
chronograph watch;
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
2024-01-30 03:43:06 +00:00
|
|
|
|
if(do_rebuild_)
|
|
|
|
|
{
|
|
|
|
|
rebuild_->do_rebuild(&img->info, img->data->ptr(), in);
|
|
|
|
|
utils::to_log(LOG_LEVEL_ALL, "Rebuild paper %d spend %llu milliseconds.\n", img->info.pos.paper_ind, watch.elapse_ms());
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
PROCIMGINFO i;
|
|
|
|
|
i.info = img->info;
|
|
|
|
|
i.img = cv::Mat(img->info.width, img->info.height, CV_8UC1, img->data->ptr());
|
|
|
|
|
in.push_back(i);
|
|
|
|
|
}
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
|
|
|
|
if(dump_img_)
|
|
|
|
|
{
|
2024-01-23 07:07:17 +00:00
|
|
|
|
send_image(&img->info, img->data->ptr(), img->info.width * img->info.height);
|
|
|
|
|
img->data->release();
|
2024-01-27 09:43:13 +00:00
|
|
|
|
|
2024-01-16 09:51:34 +00:00
|
|
|
|
for(auto& v: processors_)
|
|
|
|
|
{
|
2024-01-27 09:43:13 +00:00
|
|
|
|
send_image(*src, false);
|
2024-01-23 07:07:17 +00:00
|
|
|
|
if(v->is_enable())
|
|
|
|
|
{
|
2024-01-27 09:43:13 +00:00
|
|
|
|
v->process(*src, *dst);
|
|
|
|
|
src->clear();
|
|
|
|
|
swp = src;
|
|
|
|
|
src = dst;
|
|
|
|
|
dst = swp;
|
2024-01-23 07:07:17 +00:00
|
|
|
|
}
|
2024-01-16 09:51:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-01-23 07:07:17 +00:00
|
|
|
|
img->data->release();
|
2024-01-16 09:51:34 +00:00
|
|
|
|
for(auto& v: processors_)
|
|
|
|
|
{
|
2024-01-23 07:07:17 +00:00
|
|
|
|
if(v->is_enable())
|
|
|
|
|
{
|
2024-01-27 09:43:13 +00:00
|
|
|
|
v->process(*src, *dst);
|
|
|
|
|
src->clear();
|
|
|
|
|
swp = src;
|
|
|
|
|
src = dst;
|
|
|
|
|
dst = swp;
|
2024-01-23 07:07:17 +00:00
|
|
|
|
}
|
2024-01-16 09:51:34 +00:00
|
|
|
|
}
|
2024-01-23 09:34:20 +00:00
|
|
|
|
uint32_t t = watch.elapse_ms();
|
|
|
|
|
for(auto& v: in)
|
|
|
|
|
v.info.prc_time = t;
|
2024-01-16 09:51:34 +00:00
|
|
|
|
}
|
2024-01-30 03:43:06 +00:00
|
|
|
|
|
2024-01-27 09:43:13 +00:00
|
|
|
|
send_image(*src, true);
|
2024-01-16 09:51:34 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2024-01-23 07:07:17 +00:00
|
|
|
|
data_source_ptr ptr = imgproc_mgr::scan_finished_packet(scan_id_, img->info.data_size);
|
|
|
|
|
uint32_t wait = 0, que = 0;
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
|
|
|
|
ptr->set_session_id(session_id_);
|
2024-01-23 07:07:17 +00:00
|
|
|
|
while((que = add_busy_worker(0)) > 1)
|
|
|
|
|
{
|
|
|
|
|
if(wait++ == 0)
|
|
|
|
|
utils::to_log(LOG_LEVEL_DEBUG, "Received scan completed event while processing %u paper(s), wait ...\n", que - 1);
|
|
|
|
|
|
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
|
|
|
|
}
|
2024-01-16 09:51:34 +00:00
|
|
|
|
img_sender_(ptr);
|
|
|
|
|
ptr->release();
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-23 07:07:17 +00:00
|
|
|
|
void imgproc_mgr::send_image(LPPACKIMAGE head, uint8_t* data, size_t size, void* info, size_t info_l)
|
|
|
|
|
{
|
2024-01-27 09:43:13 +00:00
|
|
|
|
auto ovr = [&](uint64_t total, uint64_t cur_size, uint32_t err, void* user_data) -> int
|
|
|
|
|
{
|
|
|
|
|
if(total == FINAL_NOTIFY && cur_size == FINAL_NOTIFY)
|
|
|
|
|
printf("~%p\n", user_data);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
};
|
|
|
|
|
|
2024-01-23 07:07:17 +00:00
|
|
|
|
dyn_mem_ptr mem(dyn_mem::memory(size));
|
|
|
|
|
image_packet_ptr ptr = nullptr;
|
|
|
|
|
|
2024-01-27 09:43:13 +00:00
|
|
|
|
// mem->set_progress_notify(ovr, mem);
|
2024-01-23 07:07:17 +00:00
|
|
|
|
mem->put(data, size);
|
|
|
|
|
ptr = imgproc_mgr::image_sent_packet(head, mem, scan_id_, info, info_l);
|
2024-01-27 09:43:13 +00:00
|
|
|
|
// ptr->set_progress_notify(ovr, ptr);
|
|
|
|
|
// printf("+dyn_mem(%p)\n+image_packet(%p)\n", mem, ptr);
|
|
|
|
|
|
2024-01-23 07:07:17 +00:00
|
|
|
|
mem->release();
|
|
|
|
|
ptr->set_session_id(session_id_);
|
|
|
|
|
img_sender_(ptr);
|
|
|
|
|
ptr->release();
|
|
|
|
|
}
|
2024-01-16 09:51:34 +00:00
|
|
|
|
void imgproc_mgr::send_image(std::vector<PROCIMGINFO>& imgs, bool clear_after_send)
|
|
|
|
|
{
|
|
|
|
|
for(auto& v: imgs)
|
|
|
|
|
{
|
2024-01-27 09:43:13 +00:00
|
|
|
|
if(clear_after_send)
|
|
|
|
|
v.info.prc_stage = -1;
|
2024-01-23 07:07:17 +00:00
|
|
|
|
send_image(&v.info, v.img.ptr(), v.img.total() * v.img.channels()
|
|
|
|
|
, v.ext_info.empty() ? nullptr : &v.ext_info[0], v.ext_info.length());
|
2024-01-27 09:43:13 +00:00
|
|
|
|
// if(clear_after_send)
|
|
|
|
|
// v.img.release();
|
2024-01-16 09:51:34 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int imgproc_mgr::set_value(const char* name, void* val)
|
|
|
|
|
{
|
|
|
|
|
int ret = SCANNER_ERR_OK;
|
|
|
|
|
|
2024-01-17 08:54:06 +00:00
|
|
|
|
if(strcmp(name, SANE_FULL_NAME(DUMP_IMG)) == 0)
|
|
|
|
|
dump_img_ = *(bool*)val;
|
2024-01-30 03:43:06 +00:00
|
|
|
|
else if(strcmp(name, SANE_FULL_NAME(CIS_REBUILD)) == 0)
|
|
|
|
|
do_rebuild_ = *(bool*)val;
|
|
|
|
|
else if(strcmp(name, SANE_FULL_NAME(CIS_STRETCH)) == 0)
|
|
|
|
|
{
|
|
|
|
|
if(*(bool*)val)
|
|
|
|
|
{
|
|
|
|
|
if(stretcher_)
|
|
|
|
|
{
|
|
|
|
|
processors_.push_back(stretcher_);
|
|
|
|
|
stretcher_ = nullptr;
|
|
|
|
|
std::sort(processors_.begin(), processors_.end(), &imgproc_mgr::sort_processor_by_pos);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
for(size_t i = 0; i < processors_.size(); ++i)
|
|
|
|
|
{
|
|
|
|
|
if(strcmp(processors_[i]->from(), "stretch") == 0)
|
|
|
|
|
{
|
|
|
|
|
stretcher_ = processors_[i];
|
|
|
|
|
processors_.erase(processors_.begin() + i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-17 08:54:06 +00:00
|
|
|
|
else
|
|
|
|
|
ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int imgproc_mgr::load_processor(const char* path)
|
|
|
|
|
{
|
|
|
|
|
int ret = SCANNER_ERR_OK;
|
|
|
|
|
|
2024-01-23 07:07:17 +00:00
|
|
|
|
// ADD_IMG_PROCESSOR(rebuild);
|
2024-01-23 09:34:20 +00:00
|
|
|
|
ADD_IMG_PROCESSOR(stretch);
|
2024-01-24 04:05:05 +00:00
|
|
|
|
ADD_IMG_PROCESSOR(auto_crop);
|
2024-01-25 06:13:24 +00:00
|
|
|
|
ADD_IMG_PROCESSOR(color_correct);
|
2024-01-19 08:54:58 +00:00
|
|
|
|
|
2024-01-16 09:51:34 +00:00
|
|
|
|
std::sort(processors_.begin(), processors_.end(), &imgproc_mgr::sort_processor_by_pos);
|
|
|
|
|
|
2024-01-24 04:05:05 +00:00
|
|
|
|
|
2024-01-16 09:51:34 +00:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
int imgproc_mgr::clear(void)
|
|
|
|
|
{
|
|
|
|
|
for (auto& v : processors_)
|
|
|
|
|
v->release();
|
|
|
|
|
processors_.clear();
|
2024-01-30 03:43:06 +00:00
|
|
|
|
if(stretcher_)
|
|
|
|
|
stretcher_->release();
|
|
|
|
|
stretcher_ = nullptr;
|
2024-01-16 09:51:34 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int imgproc_mgr::process(LPPACKIMAGE info, dyn_mem_ptr data, bool img)
|
|
|
|
|
{
|
|
|
|
|
RAWIMG ri;
|
|
|
|
|
int ret = SCANNER_ERR_OK;
|
|
|
|
|
|
|
|
|
|
ri.data = data;
|
2024-01-19 08:54:58 +00:00
|
|
|
|
if(img)
|
2024-01-30 03:43:06 +00:00
|
|
|
|
return ret; // data->add_ref();
|
2024-01-16 09:51:34 +00:00
|
|
|
|
if(img)
|
|
|
|
|
ri.info = *info;
|
|
|
|
|
else
|
2024-01-23 07:07:17 +00:00
|
|
|
|
ri.info.data_size = (uint32_t)(long)info;
|
2024-01-16 09:51:34 +00:00
|
|
|
|
ri.img = img;
|
|
|
|
|
prc_que_.save(ri, true);
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void imgproc_mgr::stop(void)
|
|
|
|
|
{
|
|
|
|
|
run_ = false;
|
|
|
|
|
prc_que_.trigger();
|
|
|
|
|
|
|
|
|
|
workers_.stop(nullptr);
|
|
|
|
|
}
|
|
|
|
|
bool imgproc_mgr::is_busy(void)
|
|
|
|
|
{
|
|
|
|
|
SIMPLE_LOCK(working_cnt_lock_);
|
|
|
|
|
|
|
|
|
|
return working_cnt_;
|
|
|
|
|
}
|
|
|
|
|
void imgproc_mgr::start_new_turn(uint32_t scanid, uint32_t sessionid)
|
|
|
|
|
{
|
|
|
|
|
scan_id_ = scanid;
|
|
|
|
|
sent_ind_ = 0;
|
|
|
|
|
session_id_ = sessionid;
|
|
|
|
|
}
|