RE-CONSTRUCT: optimize option logical algrithm, support condition value; move option logical calculation to driver, only to SANE_Option_Descriptor keeped in sane

This commit is contained in:
gb 2023-09-08 17:42:40 +08:00
parent 8fd0ea023a
commit 06b4caf64b
9 changed files with 2494 additions and 23053 deletions

File diff suppressed because it is too large Load Diff

256
hgdriver/hgdev/device_opt.h Normal file
View File

@ -0,0 +1,256 @@
//
// device_opt: option manager of device
//
// Created: 2023-09-07
//
#pragma once
#include <sane/sane_ex.h>
#include <string>
#include <vector>
#include <map>
#include <functional>
#include "simple_logic.h"
#include <json/gb_json.h>
class device_option
{
gb_json* origin_;
gb_json* now_;
std::vector<std::string> master_opts_; // options that value changed will affect others
std::map<std::string, simple_logic*> slaver_;
typedef struct _expr_calc
{
std::string name;
std::string val1;
std::string val2;
bool not_op;
bool(*compare)(gb_json*, void* val, void* v1, void* v2);
}EXPRCALC;
std::map<std::string, EXPRCALC> compare_; // simple condition compare
class condition_value
{
typedef struct _cond_val
{
simple_logic *logic;
std::string value;
}CONDVAL;
std::vector<CONDVAL> vals_;
void clear(void)
{
for (auto& v : vals_)
{
if (v.logic)
delete v.logic;
}
vals_.clear();
}
public:
condition_value()
{}
~condition_value()
{
clear();
}
public:
bool set_value(gb_json* jsn, const char* type, device_option* parent); // jsn contains only ONE value or its object
std::string value(bool(*compare)(const char*, void*), void* param)
{
for (auto& v : vals_)
{
if (!v.logic)
return v.value;
else if (v.logic->value(compare, param))
return v.value;
}
return "";
}
};
class range_value
{
bool is_range_; // true - range; false - list
int val_ind_;
std::vector<condition_value*> vals_;
void clear(void)
{
for (auto& v : vals_)
delete v;
vals_.clear();
}
public:
range_value() : is_range_(false), val_ind_(0)
{}
~range_value()
{
clear();
}
public:
bool set_value(gb_json* jsn, const char* type, device_option *parent); // jsn contains all range object
int count(void)
{
return vals_.size();
}
bool is_range(void)
{
return is_range_;
}
// return first element in list-value or min-value of range
std::string first_value(bool(*compare)(const char*, void*), void* param)
{
val_ind_ = 0;
if (val_ind_ < count())
return vals_[val_ind_]->value(compare, param);
else
return "";
}
// return next element in list-value or max-value of range
std::string next_value(bool(*compare)(const char*, void*), void* param)
{
if (++val_ind_ < count())
return vals_[val_ind_]->value(compare, param);
else
return "";
}
};
std::map<std::string, range_value*> range_value_;
static bool is_equal_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_equal_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_equal_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_equal_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_less_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_great_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_b(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_i(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_f(gb_json* opt, void* val, void* v1, void* v2);
static bool is_between_s(gb_json* opt, void* val, void* v1, void* v2);
static bool is_opt_enabled(gb_json* opt, void* val, void* v1, void* v2);
static bool get_equal(const char* type, bool(**f)(gb_json*, void*, void*, void*));
static bool get_less(const char* type, bool(**f)(gb_json*, void*, void*, void*));
static bool get_great(const char* type, bool(**f)(gb_json*, void*, void*, void*));
static bool get_between(const char* type, bool(**f)(gb_json*, void*, void*, void*));
static std::string from_text_value(const char* type, const char* text_val);
static bool parse_simple_logic_expression(gb_json* root, const char* expr, std::string* name, EXPRCALC& calc);
static void init_condition(const char* expr, void* param);
static bool calc_simple_logic_expression(const char* expr, void* param);
void clear(void);
void init_depends(gb_json* opt);
gb_json* copy_opt(gb_json* from);
bool to_now(bool init);
public:
device_option();
~device_option();
static std::string option_value(gb_json* jsn, bool def_val);
template<class T>
static condition_value* to_condition_value(gb_json* jsn, const char* key, const char* type, device_option* parent)
{
condition_value* ret = nullptr;
gb_json* child = nullptr;
if (!jsn->get_value(key, child))
{
T v;
jsn->get_value(key, v);
child = new gb_json("", v);
}
ret = new condition_value();
if (!ret->set_value(child, type, parent))
{
delete ret;
ret = nullptr;
}
child->release();
return ret;
}
public:
bool init(const char* opt_json);
bool refine_data(const char* name, void* value); // return true if the 'value' is out of range and refined it in the range
int update_data(const char* name, void* value); // return scanner_err. name and value would be null if invoked for language changed
std::string get_option_value_type(const char* name);
std::string get_option_value(const char* name, int type); // return whole json-text if name was null
};
//{
// "noise-size": {
// "cat": "base",
// "group" : "base",
// "title" : " 噪点优化尺寸",
// "desc" : "设置需要去除的黑",
// "ver" : 0,
// "pos" : 0,
// "fix-id" : 0,
// "type" : "int",
// "unit" : "none",
// "affect" : 0,
// "readonly" : false,
// "visible" : true,
// "enabled" : false,
// "size" : 4,
// "cur" : 10,
// "default" : 10,
// "range" : {
// "min": 1,
// "max" : {
// "paper==A3": 50, // condition value
// "default": 45} ,
// "step" : 1
// },
// "depend_or": ["is-noise-optimize==true"]
// },
//
// "paper": {
// "cat": "base",
// "group" : "base",
// "title" : "纸张尺寸",
// "desc" : "设置出图大小",
// "ver" : 0,
// "pos" : 0,
// "fix-id" : 0,
// "type" : "string",
// "unit" : "none",
// "affect" : 0,
// "readonly" : false,
// "visible" : true,
// "enabled" : false,
// "size" : 96,
// "cur" : "匹配原始尺寸",
// "default" : "匹配原始尺寸",
// "range" : ["A3", "8开", {
// "mode==24位彩色": "A4" // condition value
// }, "A4横向", "16开", "16开横向", "A5", "A5横向", "A6", "A6横向", "B4", "B5", "B5横向", "B6", "B6横向", "Letter", "Letter横向", "Double Letter", "LEGAL", "匹配原始尺寸", "最大扫描尺寸自动裁切", "最大扫描尺寸", "三联试卷"]
// }
//}

File diff suppressed because it is too large Load Diff

View File

@ -1,138 +0,0 @@
#include "jsonconfig.h"
#include <streambuf>
jsonconfig::jsonconfig()
{
}
jsonconfig::jsonconfig(int devnum):DEVICEJSONPATH("")
{
switch (devnum)
{
case 100:
DEVICEJSONPATH=G100JSONPATH;
break;
case 200:
DEVICEJSONPATH=G200JSONPATH;
break;
case 300:
DEVICEJSONPATH=G300JSONPATH;
break;
case 400:
DEVICEJSONPATH=G400JSONPATH;
break;
default:
break;
}
}
json jsonconfig::loadjson(std::string path)
{
json js="";
if(path.empty())
return js;
std::ifstream f;
f.open(path,std::ios::in | std::ios::binary);
if(f.is_open()){
//std::string text = (std::stringstream() << f.rdbuf()).str();
std::string text ((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
js = json::parse(text);
f.close();
}
return js;
}
json jsonconfig::loadjson()
{
json js="";
std::string path=DEVICEJSONPATH;
if(path.empty())
return js;
std::ifstream f;
f.open(path,std::ios::in | std::ios::binary);
if(f.is_open()){
std::string text ((std::istreambuf_iterator<char>(f)),
std::istreambuf_iterator<char>());
js = json::parse(text);
f.close();
}
return js;
}
json jsonconfig::load_json_from_text(const char* json_txt, std::vector<std::string>* children)
{
return json::parse(json_txt, children);
}
std::string jsonconfig::getdeviceparam(unsigned param_no)
{
json js=loadjson();
if (js.empty())
return NULL;
js[std::to_string(param_no)]["cur"]=0;
std::string str=js[std::to_string(param_no)].dump();
if (str.empty())
return NULL;
return str;
}
bool jsonconfig::savejson(std::string path,json js)
{
if(path.empty())
return false;
std::ofstream of;
of.open(path);
if(of.is_open())
{
of.write(js.dump().data(),js.dump().size());
of.close();
return true;
}
return false;
}
json jsonconfig::defaultjson()
{
std::string jsstr=R"({
"info":[{
"type":"GScanO200",
"name":"HUAGO G100 Scanner",
"vid":"3072",
"pid":"100"
},
{
"type":"GScanO200",
"name":"HUAGO G200 Scanner",
"vid":"3072",
"pid":"200"
},
{
"type":"GScanO400",
"name":"HUAGO G300 Scanner",
"vid":"3072",
"pid":"300"
},
{
"type":"GScanO400",
"name":"HUAGO G400 Scanner",
"vid":"3072",
"pid":"400"
},
{
"type":"GScanO1003399",
"name":"HUAGO G139 Scanner",
"vid":"3072",
"pid":"139"
},
{
"type":"GScanO1003399",
"name":"HUAGO G239 Scanner",
"vid":"3072",
"pid":"239"
}]
})";
return json::parse(jsstr);
}

View File

@ -1,35 +0,0 @@
#pragma once
#include <stdio.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <stdlib.h>
#include "json.hpp"
using json = nlohmann::json;
const std::string JSONPATH ="/opt/hgscannersdk.json";
static const std::string G100JSONPATH ="/opt/G100.json";
static const std::string G200JSONPATH ="/opt/G200.json";
static const std::string G300JSONPATH ="/opt/G300.json";
static const std::string G400JSONPATH ="/opt/G400.json";
class jsonconfig
{
public:
static json loadjson(std::string path);
static json load_json_from_text(const char* json_txt, std::vector<std::string>* children = nullptr);
static bool savejson(std::string path,json js);
static json defaultjson();
private:
json loadjson();
std::string DEVICEJSONPATH;
public:
jsonconfig();
jsonconfig(int devnum);
~jsonconfig();
std::string getdeviceparam(unsigned param_no);
};

View File

@ -0,0 +1,549 @@
#include "simple_logic.h"
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// logic_expression
namespace string_util
{
// Function: find the ending position in str
//
// Parameter: str - the string beginning after the first letter 'head'
//
// head - the leading letter, can be emblaced
//
// tail - the ending letter
//
// Return: position at the ending letter, or '\0' in the string
int find_end_of_pair(const char* str, char head, char tail)
{
int end = 0, banlance = 1;
while (str[end])
{
if (str[end] == '\\')
{
end++;
if (!str[end])
break;
// skip this translating-letter
end++;
continue;
}
if (str[end] == head)
{
banlance++;
}
else if (str[end] == tail)
{
if (--banlance == 0)
break;
}
end++;
}
return end;
}
void skip_space(const char*& ptr, const char* space = " \t")
{
char mark[2] = { 0 };
while (*ptr)
{
mark[0] = *ptr;
if (!strstr(space, mark))
break;
ptr++;
}
}
};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// simple_logic
simple_logic::simple_logic() : l_(nullptr), r_(nullptr), oper_(LOGIC_OPER_NONE), expr_(""), not_(false)
{}
simple_logic::simple_logic(const simple_logic& r) : l_(nullptr), r_(nullptr)
{
copy(r);
}
simple_logic::~simple_logic()
{
clear();
}
void simple_logic::clear(void)
{
if (l_)
delete l_;
if (r_)
delete r_;
l_ = r_ = nullptr;
oper_ = LOGIC_OPER_NONE;
expr_ = "";
not_ = false;
}
void simple_logic::copy(const simple_logic& r)
{
clear();
oper_ = r.oper_;
not_ = r.not_;
expr_ = r.expr_;
if (r.l_)
{
l_ = new simple_logic();
l_->copy(*r.l_);
}
if (r.r_)
{
r_ = new simple_logic();
r_->copy(*r.r_);
}
}
void simple_logic::set_not(bool notv)
{
not_ = notv;
}
bool simple_logic::parse_internal(const char* expr, int* end, void(*leaf)(const char*, void*), void* leaf_param)
{
const char* ptr = expr, * first = nullptr;
std::vector<simple_logic*> ele;
std::vector<int> oper;
bool need_oper = false, good = true;
string_util::skip_space(ptr);
if (*ptr == 0)
return false;
while (*ptr)
{
if (*ptr == '(')
{
if (need_oper)
{
good = false;
*end = ptr - expr;
break;
}
int len = string_util::find_end_of_pair(ptr + 1, '(', ')');
if (ptr[++len] != ')')
{
*end = ptr - expr + len;
good = false;
break;
}
std::string sub(ptr + 1, len - 1);
simple_logic *e = new simple_logic();
int over = 0;
bool not_v = false;
if (first)
{
while (first < ptr)
{
if (*first == '!')
not_v ^= true;
else
break;
first++;
}
if (first < ptr)
{
*end = first - expr;
good = false;
delete e;
break;
}
first = nullptr;
}
if (e->parse(sub.c_str(), &over))
{
e->set_not(not_v);
ele.push_back(e);
ptr += len;
need_oper = true;
}
else
{
*end = ptr - expr + 1 + over;
good = false;
delete e;
break;
}
}
else if (*ptr == '|')
{
if (*(ptr + 1) == '|')
{
if (need_oper || first)
{
if (first)
{
simple_logic* e = new simple_logic();
e->expr_ = std::string(first, ptr - first);
e->oper_ = LOGIC_OPER_LEAF;
ele.push_back(e);
first = nullptr;
if (leaf)
leaf(e->expr_.c_str(), leaf_param);
}
ptr++;
oper.push_back(LOGIC_OPER_OR);
need_oper = false;
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else if (*ptr == '&')
{
if (*(ptr + 1) == '&')
{
if (need_oper || first)
{
if (first)
{
simple_logic* e = new simple_logic();
e->expr_ = std::string(first, ptr - first);
e->oper_ = LOGIC_OPER_LEAF;
ele.push_back(e);
first = nullptr;
if (leaf)
leaf(e->expr_.c_str(), leaf_param);
}
ptr++;
oper.push_back(LOGIC_OPER_AND);
need_oper = false;
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else if (*ptr == '^')
{
if (need_oper || first)
{
if (first)
{
simple_logic* e = new simple_logic();
e->expr_ = std::string(first, ptr - first);
e->oper_ = LOGIC_OPER_LEAF;
ele.push_back(e);
first = nullptr;
if (leaf)
leaf(e->expr_.c_str(), leaf_param);
}
oper.push_back(LOGIC_OPER_XOR);
need_oper = false;
}
else
{
good = false;
*end = ptr - expr;
break;
}
}
else
{
// expression ...
if (need_oper)
{
good = false;
*end = ptr - expr;
break;
}
if (!first)
first = ptr;
}
ptr++;
string_util::skip_space(ptr);
}
if (first)
{
if (need_oper)
{
*end = first - expr;
good = false;
}
else
{
simple_logic* e = new simple_logic();
int over = 0;
if (e->parse(first, &over))
ele.push_back(e);
else
{
good = false;
*end = first - expr + over;
delete e;
}
}
}
if (good && oper.size() == ele.size() - 1)
{
simple_logic* root = make_binary(ele, oper);
l_ = root->l_;
r_ = root->r_;
oper_ = root->oper_;
expr_ = root->expr_;
root->l_ = root->r_ = nullptr;
delete root;
*end = ptr - expr;
}
else
{
for (auto& v : ele)
delete v;
}
return good;
}
simple_logic* simple_logic::make_binary(const std::vector<simple_logic*>& eles, const std::vector<int>& opers)
{
if (eles.size() == 0 && opers.size() == 0)
return nullptr;
else if (eles.size() == 1)
return eles[0];
int or_pos = -1;
for (int i = 0; i < opers.size(); ++i)
{
if (opers[i] == LOGIC_OPER_OR)
{
or_pos = i;
break;
}
}
simple_logic* node = new simple_logic();
if (or_pos == -1)
{
//node->l_ = eles[0];
//node->oper_ = opers[0];
//
//std::vector<simple_logic*> re;
//std::vector<int> ro;
//for (int i = 1; i < eles.size(); ++i)
// re.push_back(eles[i]);
//for (int i = 1; i < opers.size(); ++i)
// ro.push_back(opers[i]);
//node->r_ = make_binary(re, ro);
node->r_ = eles[eles.size() - 1];
node->l_ = eles[eles.size() - 2];
node->oper_ = opers[opers.size() - 1];
simple_logic** left = &node->l_;
int cnt = 2;
while (++cnt <= eles.size())
{
simple_logic* n = new simple_logic();
n->oper_ = opers[opers.size() - cnt + 1];
n->l_ = eles[eles.size() - cnt];
n->r_ = *left;
*left = n;
left = &n->l_;
}
}
else
{
std::vector<simple_logic*> re;
std::vector<int> ro;
node->oper_ = LOGIC_OPER_OR;
for (int i = 0; i < or_pos + 1; ++i)
re.push_back(eles[i]);
for (int i = 0; i < or_pos; ++i)
ro.push_back(opers[i]);
node->l_ = make_binary(re, ro);
re.clear();
ro.clear();
for (int i = or_pos + 1; i < eles.size(); ++i)
re.push_back(eles[i]);
for (int i = or_pos + 1; i < opers.size(); ++i)
ro.push_back(opers[i]);
node->r_ = make_binary(re, ro);
}
return node;
}
std::string simple_logic::to_string_internal(bool& single)
{
single = true;
if (oper_ == LOGIC_OPER_LEAF)
return expr_;
else if (oper_ == LOGIC_OPER_AND || oper_ == LOGIC_OPER_OR || oper_ == LOGIC_OPER_XOR)
{
std::string exp("");
if (l_)
{
exp = l_->to_string_internal(single);
if (!single)
{
exp += ")";
exp.insert(0, "(");
}
}
if (oper_ == LOGIC_OPER_AND)
exp += " && ";
else if (oper_ == LOGIC_OPER_OR)
exp += " || ";
else
exp += " ^ ";
if (r_)
{
bool s = false;
std::string r(r_->to_string_internal(s));
if (!s)
{
r.insert(0, "(");
r += ")";
}
exp += r;
}
if (not_)
{
exp.insert(0, "!(");
exp += ")";
single = true;
}
else
single = false;
return std::move(exp);
}
else
return "";
}
simple_logic& simple_logic::operator=(const simple_logic& r)
{
copy(r);
return *this;
}
bool simple_logic::parse(const char* expr, int* end_pos, void(*leaf)(const char*, void*), void* leaf_param)
{
bool ret = false;
clear();
if (strstr(expr, "||") == nullptr &&
strstr(expr, "&&") == nullptr &&
strstr(expr, "^") == nullptr)
{
oper_ = LOGIC_OPER_LEAF;
expr_ = expr;
ret = true;
if (leaf)
leaf(expr_.c_str(), leaf_param);
}
else
{
int end = 0;
ret = parse_internal(expr, &end, leaf, leaf_param);
if (end_pos)
*end_pos = end;
}
return ret;
}
bool simple_logic::value(bool(*simple_expr_value)(const char*, void*), void* param)
{
if (oper_ == LOGIC_OPER_LEAF)
return simple_expr_value(expr_.c_str(), param);
else if (oper_ == LOGIC_OPER_AND)
{
bool ret = true;
if (l_)
ret = l_->value(simple_expr_value, param);
if (ret && r_)
ret &= r_->value(simple_expr_value, param);
return ret ^ not_;
}
else if (oper_ == LOGIC_OPER_OR)
{
bool ret = false;
if (l_)
ret = l_->value(simple_expr_value, param);
if (!ret && r_)
ret = r_->value(simple_expr_value, param);
return ret ^ not_;
}
else if (oper_ == LOGIC_OPER_XOR)
{
bool ret = false;
if (l_)
ret = l_->value(simple_expr_value, param);
if (r_)
ret ^= r_->value(simple_expr_value, param);
return ret ^ not_;
}
else
{
return false;
}
}
std::string simple_logic::to_expression(void)
{
bool single = false;
return std::move(to_string_internal(single));
}

View File

@ -0,0 +1,56 @@
// simple_logic.h : include file for simple logical operation
//
// Purpose: to parse and execute a logical expression like 'b1 && !(b2 || b3) ^ b4 && b5'
//
// Date: 2023-09-03
//
// Author: gb
//
// Supporting opers: &&, ||, ^
//
// Priority: (&&, ^, ), (||)
#pragma once
#include <string>
#include <vector>
class simple_logic
{
int oper_;
bool not_;
std::string expr_;
simple_logic* l_;
simple_logic* r_;
void clear(void);
void copy(const simple_logic& r);
void set_not(bool notv);
bool parse_internal(const char* expr, int* end, void(*leaf)(const char*, void*), void* leaf_param);
simple_logic* make_binary(const std::vector<simple_logic*>& eles, const std::vector<int>& opers);
std::string to_string_internal(bool& single);
public:
simple_logic();
simple_logic(const simple_logic& r);
~simple_logic();
// && == ^ > ||
enum
{
LOGIC_OPER_NONE = 0,
LOGIC_OPER_LEAF,
LOGIC_OPER_OR,
LOGIC_OPER_AND,
LOGIC_OPER_XOR,
};
simple_logic& operator=(const simple_logic& r);
public:
bool parse(const char* expr, int *end_pos = nullptr, void(*leaf)(const char*, void*) = nullptr, void* leaf_param = nullptr); // leaf: callback for leaf notifying
bool value(bool(*simple_expr_value)(const char*, void*), void* param);
std::string to_expression(void);
};

View File

@ -0,0 +1,429 @@
#include "sane_opts.h"
#include <json/gb_json.h>
#include <algorithm>
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// sane_opt
sane_opt::sane_opt() : fix_id_(-1), sn_(0), enabled_(true)
{
memset(&opt_desc_, 0, sizeof(opt_desc_));
}
sane_opt::~sane_opt()
{
clear();
}
void sane_opt::clear()
{
if (opt_desc_.desc)
delete[] opt_desc_.desc;
if (opt_desc_.name)
delete[] opt_desc_.name;
if (opt_desc_.title)
delete[] opt_desc_.title;
if (opt_desc_.type == SANE_TYPE_STRING && opt_desc_.constraint_type == SANE_CONSTRAINT_STRING_LIST && opt_desc_.constraint.string_list)
delete[] opt_desc_.constraint.string_list;
else if (opt_desc_.type == SANE_TYPE_INT || opt_desc_.type == SANE_TYPE_FIXED)
{
if (opt_desc_.constraint_type == SANE_CONSTRAINT_RANGE && opt_desc_.constraint.range)
delete[] opt_desc_.constraint.range;
else if (opt_desc_.constraint_type == SANE_CONSTRAINT_WORD_LIST && opt_desc_.constraint.word_list)
delete[] opt_desc_.constraint.word_list;
}
memset(&opt_desc_, 0, sizeof(opt_desc_));
fix_id_ = -1;
sn_ = 0;
enabled_ = true;
}
void sane_opt::set_opt_desc_string_value(char** buf, const char* val)
{
int l = strlen(val) + 4;
l += 4;
l /= 4;
l *= 4;
*buf = new char[l];
if (*buf)
{
memset(*buf, 0, l);
strcpy(*buf, val);
}
}
void sane_opt::init_cap(gb_json* jsn)
{
bool bv = false;
opt_desc_.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC;
if (jsn->get_value("readonly", bv) && bv)
SET_CAP_READONLY(opt_desc_.cap);
if (jsn->get_value("enabled", bv) && !bv)
SET_CAP_ACTIVE(opt_desc_.cap, bv);
if (jsn->get_value("auto", bv) && !bv)
opt_desc_.cap &= ~SANE_CAP_AUTOMATIC;
}
void sane_opt::init_range(gb_json* jsn)
{
gb_json* child = nullptr;
if (jsn->get_value("range", child) && child)
{
if (opt_desc_.type == SANE_TYPE_STRING)
{
gb_json* val = child->first_child();
std::vector<std::string> vals;
int len = 0;
char *buf = nullptr, *oper = nullptr;
while (val)
{
std::string v("");
if (val->value(v))
{
vals.push_back(v);
len += ALIGN_TO(v.length() + 1, 4);
}
val->release();
val = child->next_child();
}
len += (vals.size() + 1) * sizeof(char*);
opt_desc_.constraint_type = SANE_CONSTRAINT_STRING_LIST;
buf = new char[len];
opt_desc_.constraint.string_list = (SANE_String_Const*)buf;
memset(buf, 0, len);
oper = buf + (vals.size() + 1) * sizeof(char*);
len = 0;
for (auto& v : vals)
{
((char**)buf)[len++] = oper;
strcpy(oper, v.c_str());
oper += ALIGN_TO(v.length() + 1, 4);
}
}
else if (opt_desc_.type == SANE_TYPE_INT)
{
int v = 0;
if (child->get_value("min", v))
{
SANE_Range* range = new SANE_Range;
opt_desc_.constraint_type = SANE_CONSTRAINT_RANGE;
opt_desc_.constraint.range = range;
range->min = v;
child->get_value("max", range->max);
child->get_value("step", range->quant);
}
else
{
std::vector<int> vals;
gb_json* val = child->first_child();
while (val)
{
if (val->value(v))
vals.push_back(v);
val->release();
val = child->next_child();
}
SANE_Word* lst = new SANE_Word[vals.size() + 1];
int ind = 0;
lst[ind++] = vals.size();
for (auto& e : vals)
lst[ind++] = e;
opt_desc_.constraint_type = SANE_CONSTRAINT_WORD_LIST;
opt_desc_.constraint.word_list = lst;
}
}
else if (opt_desc_.type == SANE_TYPE_FIXED)
{
double v = 0;
if (child->get_value("min", v))
{
SANE_Range* range = new SANE_Range;
opt_desc_.constraint_type = SANE_CONSTRAINT_RANGE;
opt_desc_.constraint.range = range;
range->min = SANE_FIX(v);
if(child->get_value("max", v))
range->max = SANE_FIX(v);
if(child->get_value("step", v))
range->quant = SANE_FIX(v);
}
else
{
std::vector<double> vals;
gb_json* val = child->first_child();
while (val)
{
if (val->value(v))
vals.push_back(v);
val->release();
val = child->next_child();
}
SANE_Word* lst = new SANE_Word[vals.size() + 1];
int ind = 0;
lst[ind++] = vals.size();
for (auto& e : vals)
lst[ind++] = SANE_FIX(e);
opt_desc_.constraint_type = SANE_CONSTRAINT_WORD_LIST;
opt_desc_.constraint.word_list = lst;
}
}
child->release();
}
}
int sane_opt::get_fix_id(void)
{
return fix_id_;
}
int sane_opt::get_sn(void)
{
return sn_;
}
SANE_Option_Descriptor* sane_opt::get_descriptor(void)
{
return &opt_desc_;
}
const char* sane_opt::name(void)
{
return opt_desc_.name;
}
bool sane_opt::is_enabled(void)
{
return enabled_;
}
bool sane_opt::from_json_text(int sn, const char* key, const char* json, void(*err_msg)(const char*))
{
bool ret = false;
std::string text(json);
gb_json *jsn = new gb_json();
char *buf = nullptr;
clear();
sn_ = sn;
while (jsn->attach_text(&text[0]))
{
jsn->get_value("fix-id", fix_id_);
if(!jsn->get_value("enabled", enabled_))
enabled_ = true;
set_opt_desc_string_value((char**)&opt_desc_.name, key);
if (jsn->get_value("title", text))
{
set_opt_desc_string_value((char**)&opt_desc_.title, text.c_str());
}
if (jsn->get_value("desc", text))
{
set_opt_desc_string_value((char**)&opt_desc_.desc, text.c_str());
}
if (!jsn->get_value("type", text))
break;
if (text == "bool")
{
opt_desc_.type = SANE_TYPE_BOOL;
}
else if (text == "int")
{
opt_desc_.type = SANE_TYPE_INT;
}
else if (text == "float")
{
opt_desc_.type = SANE_TYPE_FIXED;
}
else if (text == "string")
{
opt_desc_.type = SANE_TYPE_STRING;
}
else if (text == "group")
opt_desc_.type = SANE_TYPE_GROUP;
else if (text == "button")
opt_desc_.type = SANE_TYPE_BUTTON;
else
break;
opt_desc_.unit = SANE_UNIT_NONE;
if (jsn->get_value("unit", text))
{
if (text == "pixel")
opt_desc_.unit = SANE_UNIT_PIXEL;
else if (text == "bit")
opt_desc_.unit = SANE_UNIT_BIT;
else if (text == "mm")
opt_desc_.unit = SANE_UNIT_MM;
else if (text == "DPI")
opt_desc_.unit = SANE_UNIT_DPI;
else if (text == "%")
opt_desc_.unit = SANE_UNIT_PERCENT;
else if (text == "microsec")
opt_desc_.unit = SANE_UNIT_MICROSECOND;
else
opt_desc_.unit = SANE_UNIT_NONE;
}
jsn->get_value("size", opt_desc_.size);
// cap ...
init_cap(jsn);
// range ...
init_range(jsn);
ret = true;
break;
}
jsn->release();
return ret;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// sane_opt
device_opts::device_opts() : opt_cnt_(0)
{
memset(&opt_0_, 0, sizeof(opt_0_));
opt_0_.cap = SANE_CAP_SOFT_DETECT;
opt_0_.name = "option-count";
opt_0_.desc = "option count of this device";
opt_0_.title = "option count";
opt_0_.type = SANE_TYPE_INT;
opt_0_.size = sizeof(SANE_Int);
}
device_opts::~device_opts()
{
clear();
}
void device_opts::clear(void)
{
for (auto& v : opts_)
{
if(v.first == v.second->get_sn()) // fix-id has the same object, we delete object at it's option number
delete v.second;
}
opts_.clear();
opt_cnt_ = 0;
}
bool device_opts::init_from(const char* jsn_text/*all options*/, void(*err_msg)(const char*))
{
bool ret = false;
gb_json* jsn = new gb_json();
std::string str(jsn_text);
int sn = 1;
clear();
if (jsn->attach_text(&str[0]))
{
gb_json* child = jsn->first_child();
str = "";
while (child)
{
str = std::move(child->to_string());
ret = add_or_replace_opt(sn++, child->key().c_str(), str.c_str(), err_msg);
if (!ret)
{
child->release();
clear();
break;
}
child->release();
child = jsn->next_child();
}
}
jsn->release();
return ret;
}
bool device_opts::add_or_replace_opt(int sn, const char* name, const char* jsn_text, void(*err_msg)(const char*))
{
if (opts_.count(sn))
{
bool en = opts_[sn]->is_enabled(),
ret = opts_[sn]->from_json_text(sn, name, jsn_text, err_msg);
return ret;
}
sane_opt* opt = new sane_opt();
if (opt->from_json_text(sn, name, jsn_text, err_msg))
{
opts_[sn] = opt;
if (opt->get_fix_id() != -1)
{
opts_[opt->get_fix_id()] = opt;
}
opt_cnt_++;
return true;
}
else
{
delete opt;
return false;
}
}
SANE_Option_Descriptor* device_opts::get_opt_descriptor(const void* opt, int* fix_id)
{
if (opt == nullptr)
{
if (fix_id)
*fix_id = -1;
return &opt_0_;
}
if (IS_PTR_NUMBER(opt))
{
int sn = (int)opt;
if (opts_.count(sn))
{
if (fix_id)
*fix_id = opts_[sn]->get_fix_id();
return opts_[sn]->get_descriptor();
}
}
else
{
const char* name = (const char*)opt;
for (auto& v : opts_)
{
if (v.second->name() == name)
{
if (fix_id)
*fix_id = v.second->get_fix_id();
return v.second->get_descriptor();
}
}
}
return nullptr;
}
int device_opts::get_option_count(void)
{
return opt_cnt_;
}

View File

@ -0,0 +1,66 @@
// sane_opts.h : include file for options of SANE
#pragma once
#include <string>
#include <vector>
#include <map>
#include <sane/sane_ex.h>
#define FIXED_ID_BASE 0x8000
class gb_json;
// For: option json to SANE_Option_Descriptor*, and depends logic
//
// NOTE: re-call method 'from_json_text' if language was changed
class sane_opt
{
int fix_id_;
int sn_;
bool enabled_;
SANE_Option_Descriptor opt_desc_;
void clear();
void set_opt_desc_string_value(char** buf, const char* val);
void init_cap(gb_json* jsn);
void init_range(gb_json* jsn);
public:
sane_opt();
~sane_opt();
public:
int get_fix_id(void);
int get_sn(void);
SANE_Option_Descriptor* get_descriptor(void);
const char* name(void);
bool is_enabled(void);
bool from_json_text(int sn, const char* key, const char* json, void(*err_msg)(const char*) = nullptr);
};
class device_opts
{
std::map<int, sane_opt*> opts_;
int opt_cnt_;
SANE_Option_Descriptor opt_0_;
void clear(void);
bool add_or_replace_opt(int sn, const char* name, const char* jsn_text, void(*err_msg)(const char*));
public:
device_opts();
~device_opts();
public:
bool init_from(const char* jsn_text/*all options*/, void(*err_msg)(const char*));
SANE_Option_Descriptor* get_opt_descriptor(const void* opt, int* fix_id = nullptr);
int get_option_count(void);
};