Add persistent history of per-constraint item counts in workflow.

This will be needed for properly merging or integrating the status
screen by falconne. The history is maintained as a circular buffer
of up to 28 entries, and persists in save files.
develop
Alexander Gavrilov 2012-11-28 19:25:01 +04:00
parent 593dc4f554
commit bfc11cf946
2 changed files with 143 additions and 7 deletions

@ -81,6 +81,55 @@ namespace DFHack
int &ival(int i) { return int_values[i]; }
int ival(int i) const { return int_values[i]; }
// Pack binary data into string field.
// Since DF serialization chokes on NUL bytes,
// use bit magic to ensure none of the bytes is 0.
// Choose the lowest bit for padding so that
// sign-extend can be used normally.
size_t data_size() const { return str_value->size(); }
bool check_data(size_t off, size_t sz = 1) {
return (str_value->size() >= off+sz);
}
void ensure_data(size_t off, size_t sz = 0) {
if (str_value->size() < off+sz) str_value->resize(off+sz, '\x01');
}
uint8_t *pdata(size_t off) { return (uint8_t*)&(*str_value)[off]; }
static const size_t int7_size = 1;
uint8_t get_uint7(size_t off) {
uint8_t *p = pdata(off);
return p[0]>>1;
}
int8_t get_int7(size_t off) {
uint8_t *p = pdata(off);
return int8_t(p[0])>>1;
}
void set_uint7(size_t off, uint8_t val) {
uint8_t *p = pdata(off);
p[0] = uint8_t((val<<1) | 1);
}
void set_int7(size_t off, int8_t val) { set_uint7(off, val); }
static const size_t int28_size = 4;
uint32_t get_uint28(size_t off) {
uint8_t *p = pdata(off);
return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((p[3]&~1U)<<20);
}
int32_t get_int28(size_t off) {
uint8_t *p = pdata(off);
return (p[0]>>1) | ((p[1]&~1U)<<6) | ((p[2]&~1U)<<13) | ((int8_t(p[3])&~1)<<20);
}
void set_uint28(size_t off, uint32_t val) {
uint8_t *p = pdata(off);
p[0] = uint8_t((val<<1) | 1);
p[1] = uint8_t((val>>6) | 1);
p[2] = uint8_t((val>>13) | 1);
p[3] = uint8_t((val>>20) | 1);
}
void set_int28(size_t off, int32_t val) { set_uint28(off, val); }
PersistentDataItem() : id(0), str_value(0), int_values(0) {}
PersistentDataItem(int id, const std::string &key, std::string *sv, int *iv)
: id(id), key_value(key), str_value(sv), int_values(iv) {}

@ -293,6 +293,7 @@ typedef std::map<std::pair<int,int>, bool> TMaterialCache;
struct ItemConstraint {
PersistentDataItem config;
PersistentDataItem history;
// Fixed key parsed into fields
bool is_craft;
@ -308,7 +309,7 @@ struct ItemConstraint {
int weight;
std::vector<ProtectedJob*> jobs;
int item_amount, item_count, item_inuse;
int item_amount, item_count, item_inuse_amount, item_inuse_count;
bool request_suspend, request_resume;
bool is_active, cant_resume_reported;
@ -318,7 +319,7 @@ struct ItemConstraint {
public:
ItemConstraint()
: is_craft(false), min_quality(item_quality::Ordinary), is_local(false),
weight(0), item_amount(0), item_count(0), item_inuse(0),
weight(0), item_amount(0), item_count(0), item_inuse_amount(0), item_inuse_count(0),
is_active(false), cant_resume_reported(false)
{}
@ -352,6 +353,44 @@ public:
request_resume = (size <= goalCount()-goalGap());
request_suspend = (size >= goalCount());
}
static const size_t int28_size = PersistentDataItem::int28_size;
static const size_t hist_entry_size = PersistentDataItem::int28_size * 4;
size_t history_size() {
return history.data_size() / hist_entry_size;
}
size_t history_base(int idx) {
size_t hsize = history_size();
return ((history.ival(0)+hsize-idx) % hsize) * hist_entry_size;
}
int history_count(int idx) {
return history.get_int28(history_base(idx) + 0*int28_size);
}
int history_amount(int idx) {
return history.get_int28(history_base(idx) + 1*int28_size);
}
int history_inuse_count(int idx) {
return history.get_int28(history_base(idx) + 2*int28_size);
}
int history_inuse_amount(int idx) {
return history.get_int28(history_base(idx) + 3*int28_size);
}
void updateHistory()
{
size_t buffer_size = history_size();
if (buffer_size < 28)
history.ensure_data(hist_entry_size*buffer_size++, hist_entry_size);
history.ival(0) = (history.ival(0)+1) % buffer_size;
size_t base = history.ival(0) * hist_entry_size;
history.set_int28(base + 0*int28_size, item_count);
history.set_int28(base + 1*int28_size, item_amount);
history.set_int28(base + 2*int28_size, item_inuse_count);
history.set_int28(base + 3*int28_size, item_inuse_amount);
}
};
/******************************
@ -649,6 +688,9 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
update_job_data(out);
process_constraints(out);
for (size_t i = 0; i < constraints.size(); i++)
constraints[i]->updateHistory();
}
}
@ -659,6 +701,10 @@ DFhackCExport command_result plugin_onupdate(color_ostream &out)
* ITEM COUNT CONSTRAINT *
******************************/
static std::string history_key(PersistentDataItem &config) {
return stl_sprintf("workflow/history/%d", config.entry_id());
}
static ItemConstraint *get_constraint(color_ostream &out, const std::string &str, PersistentDataItem *cfg, bool create)
{
std::vector<std::string> tokens;
@ -776,6 +822,8 @@ static ItemConstraint *get_constraint(color_ostream &out, const std::string &str
nct->init(str);
}
nct->history = World::GetPersistentData(history_key(nct->config), NULL);
constraints.push_back(nct);
return nct;
}
@ -787,6 +835,7 @@ static void delete_constraint(ItemConstraint *cv)
vector_erase_at(constraints, idx);
World::DeletePersistentData(cv->config);
World::DeletePersistentData(cv->history);
delete cv;
}
@ -1064,7 +1113,8 @@ static void map_job_items(color_ostream &out)
{
constraints[i]->item_amount = 0;
constraints[i]->item_count = 0;
constraints[i]->item_inuse = 0;
constraints[i]->item_inuse_amount = 0;
constraints[i]->item_inuse_count = 0;
}
meltable_count = 0;
@ -1177,7 +1227,8 @@ static void map_job_items(color_ostream &out)
isAssignedSquad(item))
{
is_invalid = true;
cv->item_inuse++;
cv->item_inuse_count++;
cv->item_inuse_amount += item->getStackSize();
}
else
{
@ -1367,7 +1418,8 @@ static void push_constraint(lua_State *L, ItemConstraint *cv)
Lua::SetField(L, cv->item_amount, ctable, "cur_amount");
Lua::SetField(L, cv->item_count, ctable, "cur_count");
Lua::SetField(L, cv->item_inuse, ctable, "cur_in_use");
Lua::SetField(L, cv->item_inuse_amount, ctable, "cur_in_use_amount");
Lua::SetField(L, cv->item_inuse_count, ctable, "cur_in_use_count");
// Current state value
@ -1463,6 +1515,40 @@ static int setConstraint(lua_State *L)
return 1;
}
static int getCountHistory(lua_State *L)
{
auto token = luaL_checkstring(L, 1);
color_ostream &out = *Lua::GetOutput(L);
update_data_structures(out);
ItemConstraint *icv = get_constraint(out, token, NULL, false);
if (icv)
{
size_t hsize = icv->history_size();
lua_createtable(L, hsize, 0);
for (int i = hsize-1; i >= 0; i--)
{
lua_createtable(L, 0, 4);
Lua::SetField(L, icv->history_amount(i), -1, "cur_amount");
Lua::SetField(L, icv->history_count(i), -1, "cur_count");
Lua::SetField(L, icv->history_inuse_amount(i), -1, "cur_in_use_amount");
Lua::SetField(L, icv->history_inuse_count(i), -1, "cur_in_use_count");
lua_rawseti(L, -2, hsize-i); // reverse order
}
}
else
lua_pushnil(L);
return 1;
}
DFHACK_PLUGIN_LUA_FUNCTIONS {
DFHACK_LUA_FUNCTION(isEnabled),
DFHACK_LUA_FUNCTION(setEnabled),
@ -1474,6 +1560,7 @@ DFHACK_PLUGIN_LUA_COMMANDS {
DFHACK_LUA_COMMAND(listConstraints),
DFHACK_LUA_COMMAND(findConstraint),
DFHACK_LUA_COMMAND(setConstraint),
DFHACK_LUA_COMMAND(getCountHistory),
DFHACK_LUA_END
};
@ -1521,10 +1608,10 @@ static void print_constraint(color_ostream &out, ItemConstraint *cv, bool no_job
<< cv->goalCount() << " (gap " << cv->goalGap() << ")" << endl;
out.reset_color();
if (cv->item_count || cv->item_inuse)
if (cv->item_count || cv->item_inuse_count)
out << prefix << " items: amount " << cv->item_amount << "; "
<< cv->item_count << " stacks available, "
<< cv->item_inuse << " in use." << endl;
<< cv->item_inuse_count << " in use." << endl;
if (no_job) return;