code_device/hgdriver/hgdev/hg_scanner.cpp

731 lines
17 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "hg_scanner.h"
#include "scanner_manager.h"
#include <sane_opt_json/device_opt.h>
#include <imgprc/imgprc_mgr.h>
#include <lang/app_language.h>
#include "./scanner/scanner_handler.h"
#include "user-opt/user.h"
#include <base/paper.h>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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<sane_opt_provider*>* 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<data_holder_ptr>(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<uchar> 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<void(int)> 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; // <20>˴<EFBFBD>ָÿһ<C3BF><D2BB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EEA3AC><EFBFBD>ǵ<EFBFBD>ɨ<EFBFBD><C9A8><EFBFBD>ǹ̶<C7B9>Ϊ<EFBFBD><CEAA>8<EFBFBD><38>
pii->last_frame = SANE_TRUE; // һ<><D2BB>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0BBA5><EFBFBD><EFBFBD><EBA3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊtrue<75><65><EFBFBD><EFBFBD>ɫͼ<C9AB><CDBC>RGBʱҲֻ<D2B2><D6BB>һ<EFBFBD><D2BB>֡<EFBFBD><D6A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҲΪ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; // <20>˴<EFBFBD>ָÿһ<C3BF><D2BB><EFBFBD><EFBFBD>ɫ<EFBFBD><C9AB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>λ<EFBFBD><EFBFBD><EEA3AC><EFBFBD>ǵ<EFBFBD>ɨ<EFBFBD><C9A8><EFBFBD>ǹ̶<C7B9>Ϊ<EFBFBD><CEAA>8<EFBFBD><38>
pii->last_frame = SANE_TRUE; // һ<><D2BB>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E0BBA5><EFBFBD><EFBFBD><EBA3AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊtrue<75><65><EFBFBD><EFBFBD>ɫͼ<C9AB><CDBC>RGBʱҲֻ<D2B2><D6BB>һ<EFBFBD><D2BB>֡<EFBFBD><D6A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ҲΪ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);
}