#include "hg_scanner.h" #include "scanner_manager.h" #include #include #include #include "./scanner/scanner_handler.h" #include "user-opt/user.h" #include ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // hg_scanner static std::string device_opt_json[] = { "{\"tx-prog\":{\"cat\":\"none\",\"group\":\"\\u9ad8\\u7ea7\\u8bbe\\u7f6e\",\"title\":\"\\u6587\\u4ef6\\u4f20\\u8f93\\u8fdb\\u5ea6\",\"desc\":\"\\u4f20\\u8f93\\u6587\\u4ef6\\u5b8c\\u6210\\u767e\\u5206\\u6bd4\",\"type\":\"float\",\"ui-pos\":0,\"auth\":0,\"visible\":1,\"size\":8,\"ownread\":true,\"cur\":0.000000,\"default\":0.000000},\"dump-path\":{\"cat\":\"base\",\"group\":\"\\u9ad8\\u7ea7\\u8bbe\\u7f6e\",\"title\":\"\\u6d4b\\u8bd5\\u56fe\\u50cf\\u4fdd\\u5b58\\u8def\\u5f84 \",\"desc\":\"\\u6d4b\\u8bd5\\u65f6\\u4e2d\\u95f4\\u56fe\\u50cf\\u7684\\u4fdd\\u5b58\\u8def\\u5f84\",\"type\":\"string\",\"ui-pos\":20,\"auth\":0,\"enabled\":false,\"size\":256,\"ownread\":true,\"cur\":\"\",\"default\":\"\",\"depend\":\"dump-img==true\"}}" }; hg_scanner::hg_scanner(ONLNSCANNER* dev, imgproc_mgr* imgproc, hguser* user, std::vector* constopts) : dev_(*dev), status_(SCANNER_ERR_OPENED_BY_OTHER_PROCESS) , msg_(from_default_language("\350\256\276\345\244\207\345\267\262\347\273\217\350\242\253\350\277\233\347\250\213 '%s' \345\215\240\347\224\250")) , raw_imgs_("img-usb"), final_imgs_("img-final") { raw_imgs_.enable_wait_log(false); final_imgs_.enable_wait_log(false); singleton_ = hg_scanner::create_device_singleton(dev_.vid, dev_.pid, dev_.addr); if (!singleton_->is_first()) { std::string pre(singleton_->read()); size_t pos = msg_.find("%s"); if (pos == std::string::npos) msg_ += ": " + pre; else msg_.replace(pos, 2, pre); singleton_->release(); } else { set_where(dev->display_name.c_str()); user_ = user; init(); if (status() == SCANNER_ERR_OK) { //imgproc_ = imgproc; //imgproc_->add_ref(); //dev_opts_->add(imgproc_); if (constopts) { for(auto& v: *constopts) dev_opts_->add(v); } std::string alg(dev_opts_->get_option_value(nullptr, SANE_ACTION_GET_ENTIRE_JSON)); gb_json *root = new gb_json(), *child = nullptr; if (root->attach_text(&alg[0])) { alg = ""; child = root->first_child(); while (child) { child->get_value("cat", alg); if (alg == "imgp") { int pos = 0; if (child->get_value("pos", pos)) { img_prc_name_[pos] = child->key(); } } child->release(); child = root->next_child(); } } root->release(); #ifdef USE_SAFE_THREAD auto tf = [this](void) -> void { thread_image_processor(); }; imgpr_thread_.start(tf, 0, "hg_scanner::thread_image_processor"); #else imgpr_thread_.reset(new std::thread(&hg_scanner::thread_image_processor, this)); #endif } } } hg_scanner::~hg_scanner() { close(); } shared_memory* hg_scanner::create_device_singleton(int vid, int pid, int addr) { unsigned long long key = vid; key <<= 16; key |= pid; key <<= 16; key |= addr; return new shared_memory(key); } image_holder_ptr hg_scanner::make_finished_image_holder(int err) { PACKIMAGE over; image_holder_ptr ihp = nullptr; memset(&over, 0, sizeof(over)); over.info_size = err; ihp = new image_holder(&over); return ihp; } bool hg_scanner::is_finished_image_holder(image_holder_ptr ptr, int* err) { PACKIMAGE over; bool finish = false; memset(&over, 0, sizeof(over)); over.info_size = ptr->get_info()->info_size; finish = memcmp(&over, ptr->get_info(), sizeof(over)) == 0; if (finish && err) *err = over.info_size; return finish; } void hg_scanner::init(void) { std::string opts(""); int ret = SCANNER_ERR_OK; auto on_status = [this](uint32_t statu) ->void { //status_ = statu; }; auto on_image_received = [this](LPPACKIMAGE pimg, uint64_t size) ->data_holder_ptr { data_holder_ptr pdh = nullptr; if (pimg == IMG_RECEIVER_PAPER_CNT) { } else if (pimg == IMG_RECEIVER_FINISHED) { image_holder_ptr ihp = hg_scanner::make_finished_image_holder(size); raw_imgs_.save(ihp, true); utils::to_log(LOG_LEVEL_DEBUG, "Scan finished with error: %s\n", scanner_error_name(size).c_str()); } else { auto over = [this](uint64_t total, uint64_t cur_size, uint32_t err, void* user) -> int { image_holder_ptr pimg = (image_holder_ptr)user; tx_prg_ = cur_size; tx_prg_ /= total; if (total != cur_size || total == FINAL_NOTIFY) return 0; pimg->add_ref(); raw_imgs_.save(pimg, true); return 0; }; image_holder_ptr ihp = new image_holder(pimg); ihp->set_progress_notify(over, ihp); pdh = dynamic_cast(ihp); } return pdh; }; auto privilege = [this](int priv) -> bool { return user_->has_privilege(priv); }; auto privilege_empty = [this](int priv) -> bool { return true; }; auto logmsg = [&](const char* msg) -> void { utils::to_log(LOG_LEVEL_DEBUG, msg); }; scanner_ = new scanner_handler(); scanner_->set_image_receiver(on_image_received); scanner_->set_status_notifyer(on_status); ret = scanner_->open_usb_scanner(dev_.dev); img_prc_name_[0] = "raw"; img_prc_name_[10] = "rebuild"; if (ret == SCANNER_ERR_OK) { std::string pc(""); for (auto& v : device_opt_json) pc += v; ret = scanner_->option_get_all(opts); if (ret != SCANNER_ERR_OK) { int times = 0, err = ret; do { std::this_thread::sleep_for(std::chrono::milliseconds(5)); ret = scanner_->option_get_all(opts); } while (ret != SCANNER_ERR_OK && times++ < 20); utils::to_log(LOG_LEVEL_WARNING, "Failed to get options from device with err %d. wait %d times(%ums) and result = %d\n" , err, times, times * 5, ret); } if (ret == SCANNER_ERR_OK) { // merge two JSON texts ... size_t pos = pc.rfind('}'); if (pos != std::string::npos) { pc[pos] = ','; pos = opts.find("{"); if (pos++ != std::string::npos) { opts.erase(0, pos); opts.insert(0, pc); } } } set_opt_json_text(&opts[0]); if(user_) dev_opts_ = new device_option(false, privilege, logmsg); else dev_opts_ = new device_option(false, privilege_empty, logmsg); dev_opts_->add(this); pc = dev_opts_->get_option_value(SANE_STD_OPT_NAME_DEVICE_MODEL, SANE_ACTION_GET_VALUE); if (pc.length()) { opts = dev_opts_->get_option_value(SANE_STD_OPT_NAME_DEVICE_SERIAL_NO, SANE_ACTION_GET_VALUE); if (opts.length()) pc += " - " + opts; set_where(pc.c_str()); } status_ = SCANNER_ERR_OK; msg_ = "OK"; } else { status_ = ret; } } void hg_scanner::thread_image_processor(void) { imgproc_mgr * processor = imgproc_; image_holder* raw = nullptr; //processor->add_ref(); while (raw_imgs_.take(raw, true)) { int err = 0; if (hg_scanner::is_finished_image_holder(raw, &err)) { status_ = err; if (scan_over_notify_) scan_over_notify_(status_); utils::to_log(LOG_LEVEL_DEBUG, "Finished scanning %d picture(s) in %ums.\n", final_cnt_, scan_time_.elapse_ms()); } else { process_image(raw); } raw->release(); } //processor->release(); } void hg_scanner::process_image(image_holder_ptr img) { bool addref = true; int stage = img->get_info()->prc_stage; if (!dump_path_.empty()) { char alg[128] = { 0 }; if (img_prc_name_.count(stage)) sprintf(alg, "%s", img_prc_name_[stage].c_str()); else sprintf(alg, "%s", "Unk"); img->save_2_file(dump_path_.c_str(), stage, alg); } else { std::string alg(img_prc_name_.count(stage) ? img_prc_name_[stage] : std::to_string(stage)); if(alg == SANE_OPT_NAME(OUT_FORMAT)) utils::to_log(LOG_LEVEL_ALL, "Image-Process '%s' of picture '%04d-%d%d%d' is %ums, total = %ums.\n", alg.c_str() , (int)img->get_info()->pos.paper_ind, (int)img->get_info()->pos.paper_side, (int)img->get_info()->pos.split_ind, (int)img->get_info()->pos.multiout_ind , img->get_info()->prc_time, (int)img->get_info()->life); else utils::to_log(LOG_LEVEL_ALL, "Image-Process '%s' of picture '%04d-%d%d%d' is %ums.\n", alg.c_str() , (int)img->get_info()->pos.paper_ind, (int)img->get_info()->pos.paper_side, (int)img->get_info()->pos.split_ind, (int)img->get_info()->pos.multiout_ind, img->get_info()->prc_time); } if (img->get_info()->format != IMG_FMT_BMP) { // decode ... #ifdef TEST_HGSCANNER #else cv::ImreadModes mode = img->get_info()->channels == 1 ? cv::IMREAD_GRAYSCALE : cv::IMREAD_COLOR; std::vector buf(img->data_length()); memcpy(buf.data(), img->data(), img->data_length()); cv::Mat mat(cv::imdecode(buf, mode)); PACKIMAGE head(*img->get_info()); image_holder *ptr = nullptr; uint32_t size = mat.total() * mat.channels(); head.data_size = size; head.format = IMG_FMT_BMP; ptr = new image_holder(&head); ptr->put_data(mat.ptr(), &size); img = ptr; addref = false; #endif } if(addref) img->add_ref(); final_cnt_++; final_imgs_.save(img, true); } image_holder_ptr hg_scanner::wait_image(void) { image_holder_ptr ptr = nullptr; if (final_imgs_.size() == 0) { while (status_ == SCANNER_ERR_DEVICE_BUSY) { std::this_thread::sleep_for(std::chrono::milliseconds(3)); if (final_imgs_.size()) break; } } if (final_imgs_.size()) ptr = final_imgs_.front(); return ptr; } char* hg_scanner::get_value(const char* name, void* value, size_t* size, int* err) { char* ret = nullptr; if (strcmp(name, "tx-prog") == 0) { ret = (char*)malloc(sizeof(double)); *(double*)ret = tx_prg_; if (size) *size = sizeof(double); if (err) *err = status_; } else if (strcmp(name, SANE_OPT_NAME(DUMP_IMG_PATH)) == 0) { ret = (char*)malloc(dump_path_.length() + 1); strcpy(ret, dump_path_.c_str()); if (size) *size = dump_path_.length(); if (err) *err = SCANNER_ERR_OK; } else if (scanner_) { uint32_t len = 0; int result = 0; size_t l = 0; if (!size) size = &l; dev_opts_->get_option_value_type(name, size); len = *size; ret = (char*)malloc(len); result = scanner_->option_value_get(name, ret, &len); *size = len; if (result) { free(ret); ret = nullptr; } if (err) *err = result; } else if (err) *err = SCANNER_ERR_NOT_OPEN; return ret; } int hg_scanner::set_value(const char* name, void* val) { int type = DATA_TYPE_BOOL, val_size = 0, ret = SCANNER_ERR_OK; size_t size = 0; std::string t(dev_opts_->get_option_value_type(name, &size)); uint16_t after = 0; if (strcmp(SANE_OPT_NAME(RESOLUTION), name) == 0) dpi_ = *(int*)val; if (strcmp(name, SANE_OPT_NAME(DUMP_IMG)) == 0) { if (*(bool*)val && dump_path_.empty()) { dump_path_ = utils::get_local_data_path() + PATH_SEPARATOR + "imgs"; utils::create_folder(dump_path_.c_str()); dump_path_ += std::string(PATH_SEPARATOR); } } else if (strcmp(name, SANE_OPT_NAME(DUMP_IMG_PATH)) == 0) { dump_path_ = (char*)val; return ret; } val_size = size; if (t == JSON_SANE_TYPE_BOOL) { type = DATA_TYPE_BOOL; } else if (t == JSON_SANE_TYPE_INT) { type = DATA_TYPE_INT4; } else if (t == JSON_SANE_TYPE_FIXED) { type = DATA_TYPE_FLOAT; } else if (t == JSON_SANE_TYPE_STRING) { type = DATA_TYPE_STRING; val_size = strlen((char*)val); } ret = scanner_->option_value_set(name, type, val, size, val_size, &after); if (ret == SCANNER_ERR_OK) ret = after; if (ret == SCANNER_ERR_RELOAD_OPT_PARAM || ret == SCANNER_ERR_CONFIGURATION_CHANGED) { // re-set json text int e = scanner_->option_get_all(t); if (e == SCANNER_ERR_OK) set_opt_json_text(&t[0]); } return ret; } int hg_scanner::start(std::string* devcfg, std::function over_cb) { int ret = SCANNER_ERR_OK; cancelled_ = false; clear_images(); if (scanner_) { scan_over_notify_ = over_cb; ret = scanner_->scan_start(devcfg); if (ret == SCANNER_ERR_OK) { scan_time_.reset(); status_ = SCANNER_ERR_DEVICE_BUSY; } } return ret; } int hg_scanner::stop(void) { int ret = SCANNER_ERR_OK; cancelled_ = true; if (scanner_) ret = scanner_->scan_stop(); return ret; } int hg_scanner::close(void) { int ret = SCANNER_ERR_OK; if (scanner_) { ret = scanner_->close(); if (ret) return ret; scanner_->release(); scanner_ = nullptr; } if (dev_opts_) { dev_opts_->clear(); dev_opts_->release(); dev_opts_ = nullptr; } if (singleton_) { singleton_->release(); singleton_ = nullptr; } // wait image thread ... raw_imgs_.trigger(); #ifndef USE_SAFE_THREAD if (imgpr_thread_.get() && imgpr_thread_->joinable()) imgpr_thread_->join(); #endif if (imgproc_) { imgproc_->release(); imgproc_ = nullptr; } user_ = nullptr; return ret; } int hg_scanner::re_connect(void) { return SCANNER_ERR_OK; } int hg_scanner::get_image_info(SANE_Parameters* pii) { image_holder_ptr ptr = wait_image(); if (ptr) { pii->bytes_per_line = (ptr->get_info()->bpp * ptr->get_info()->width * ptr->get_info()->channels + 7) / 8; // no 4-bytes align pii->depth = ptr->get_info()->bpp; // 此处指每一个颜色分量的位深,我们的扫描仪固定为“8” pii->last_frame = SANE_TRUE; // 一幅图片如果各个分量相互分离,则最后一个分量的时候设置为true。彩色图像RGB时也只有一“帧”,所以也为true if (ptr->get_info()->format == IMG_FMT_JPEG) pii->format = (SANE_Frame)SANE_FRAME_JPEG; else if (ptr->get_info()->format == IMG_FMT_PNG) pii->format = (SANE_Frame)SANE_FRAME_PNG; else pii->format = ptr->get_info()->channels == 3 ? SANE_FRAME_RGB : SANE_FRAME_GRAY; pii->lines = ptr->get_info()->height; pii->pixels_per_line = ptr->get_info()->width; } else { // default ... double *w = (double*)get_value(SANE_OPT_NAME(PAPER_W), nullptr, nullptr), *h = (double*)get_value(SANE_OPT_NAME(PAPER_H), nullptr, nullptr); int res = dpi_; SIZE size(paper::size("A3")); if (w) { size.cx = *w; free(w); } if (h) { size.cy = *h; free(h); } pii->pixels_per_line = utils::mm_2_pixel(size.cx, res); pii->lines = utils::mm_2_pixel(size.cy, res); pii->bytes_per_line = pii->pixels_per_line * 3; // no 4-bytes align pii->depth = 8; // 此处指每一个颜色分量的位深,我们的扫描仪固定为“8” pii->last_frame = SANE_TRUE; // 一幅图片如果各个分量相互分离,则最后一个分量的时候设置为true。彩色图像RGB时也只有一“帧”,所以也为true pii->format = SANE_FRAME_RGB; } return SCANNER_ERR_OK; } int hg_scanner::read_image_data(uint8_t* buf, size_t* len) { int ret = SCANNER_ERR_INVALID_PARAMETER; if (len) { image_holder_ptr ptr = wait_image(); size_t bufl = *len; *len = 0; ret = SCANNER_ERR_NO_DATA; if (ptr) { if (cur_img_pos_ == 0) cur_img_pos_ = ptr->get_info()->info_size; if (buf) { if (bufl >= ptr->data_length() - cur_img_pos_) { *len = ptr->data_length() - cur_img_pos_; memcpy(buf, ptr->data() + cur_img_pos_, *len); cur_img_pos_ = 0; final_imgs_.pop_front(); ptr->release(); } else { memcpy(buf, ptr->data() + cur_img_pos_, bufl); *len = bufl; cur_img_pos_ += *len; ret = SCANNER_ERR_OK; } } else { *len = ptr->get_info()->data_size; ret = SCANNER_ERR_INSUFFICIENT_MEMORY; } } } return ret; } int hg_scanner::get_resolution(void) { return dpi_; } void hg_scanner::clear_images(void) { image_holder_ptr img = nullptr; while (final_imgs_.take(img)) img->release(); cur_img_pos_ = 0; final_cnt_ = 0; } int hg_scanner::status(EP0REPLYSTATUS* ds, bool en_dev_log) { if (ds) scanner_->get_scanner_status(ds, en_dev_log); return status_; } std::string hg_scanner::status_message(void) { return msg_; } bool hg_scanner::is_online(void) { return online_; } char* hg_scanner::get_option_value(const char* name, void* value, size_t* size, int* err) { std::string val(dev_opts_->get_option_value(name, SANE_ACTION_GET_VALUE, (int*)size, value)); char* ret = nullptr; if (val.empty()) { if (err) *err = SCANNER_ERR_DEVICE_NOT_FOUND; } else { ret = (char*)malloc(val.length() + 1); memcpy(ret, val.c_str(), val.length()); ret[val.length()] = 0; if (err) *err = SCANNER_ERR_OK; if (size) *size = val.length(); } return ret; } device_option* hg_scanner::get_device_opt(void) { dev_opts_->add_ref(); return dev_opts_; } int hg_scanner::get_peer_config(LPPEERCFG cfg) { return scanner_->get_peer_config(cfg); } int hg_scanner::file_transfer(const char* local, const char* remote, bool to_remote) { auto prog = [this](uint64_t total, uint64_t txed, uint32_t err, void* user) -> int { double now = txed; now /= total; tx_prg_ = now; status_ = err; if (err) utils::to_log(LOG_LEVEL_WARNING, "File transfer error: %s (at %llu/%llu)\n", scanner_error_name(err).c_str(), txed, total); else if (txed >= total) utils::to_log(LOG_LEVEL_DEBUG, "File transfer finished(%llu/%llu) with error %d\n", txed, total, scanner_error_name(err).c_str()); return 0; }; tx_prg_ = .0f; return scanner_->file_transfer(local, remote, to_remote, prog); }