Commit 6cbb72b6 authored by Claes Sjofors's avatar Claes Sjofors

Sev deadband linear regression added

parent d9245c13
......@@ -517,8 +517,10 @@ int sev_server::mainloop()
qcom_sEvent *ep = (qcom_sEvent*) get.data;
new_event.m = ep->mask;
if (new_event.b.terminate)
if (new_event.b.terminate) {
delete m_db;
exit(0);
}
break;
}
default: ;
......
......@@ -41,8 +41,10 @@
#include "pwr.h"
#include "pwr_class.h"
#include "pwr_baseclasses.h"
#include "rt_mh_net.h"
#include "rt_sev_net.h"
#include "sev_valuecache.h"
using namespace std;
......@@ -66,6 +68,25 @@ typedef struct {
unsigned int eventstore_msg_cnt;
} sev_sStat;
typedef struct {
pwr_tTime time;
pwr_tFloat32 value;
int stored;
} sev_StoredFloat32;
typedef struct {
pwr_tTime time;
pwr_tInt32 value;
int stored;
} sev_StoredInt32;
typedef struct {
int size;
int first;
int last;
void *values;
} sev_sStoredValues;
class sev_attr {
public:
sev_attr() : type(pwr_eType_), size(0), elem(0) {
......@@ -94,17 +115,24 @@ class sev_event {
class sev_item {
public:
sev_item() : deadband_active(0), last_id(0), value_size(0), old_value(0), first_storage(1), status(0), logged_status(0),
idx(0), deleted(0)
{ /*memset( old_value, 0, sizeof(old_value));*/}
cache(0), idx(0), deleted(0) {
/*memset( old_value, 0, sizeof(old_value));*/
}
sev_item( const sev_item& x) : id(x.id), oid(x.oid), creatime(x.creatime), modtime(x.modtime),
storagetime(x.storagetime), sevid(x.sevid), scantime(x.scantime), deadband(x.deadband), options(x.options),
deadband_active(x.deadband_active), last_id(x.last_id), value_size(x.value_size), old_value(x.old_value),
first_storage(x.first_storage),
attrnum(x.attrnum), attr(x.attr), status(x.status), logged_status(x.logged_status),
attrnum(x.attrnum), attr(x.attr), status(x.status), logged_status(x.logged_status), cache(0),
idx(x.idx), deleted(x.deleted) {
strncpy( tablename, x.tablename, sizeof(tablename));
strncpy( oname, x.oname, sizeof(oname));
strncpy( description, x.description, sizeof(description));
if ( x.cache)
cache = new sev_valuecache(*x.cache);
}
~sev_item() {
if ( cache)
delete cache;
}
unsigned int id;
char tablename[256];
......@@ -128,6 +156,7 @@ class sev_item {
vector<sev_attr> attr;
pwr_tStatus status;
pwr_tStatus logged_status;
sev_valuecache *cache;
unsigned int idx;
int deleted;
};
......
......@@ -736,6 +736,8 @@ int sev_dbms::create_table( pwr_tStatus *sts, char *tablename, pwr_eType type,
if ( cnf_get_value( "sevMysqlEngine", engine, sizeof(engine)) != 0)
snprintf( enginestr, sizeof(enginestr), " engine=%s", engine);
if ( cdh_NoCaseStrcmp( engine, "innodb") == 0)
strcat( enginestr, " row_format=compressed");
if ( options & pwr_mSevOptionsMask_PosixTime) {
if ( options & pwr_mSevOptionsMask_HighTimeResolution) {
......@@ -993,10 +995,12 @@ int sev_dbms::get_items( pwr_tStatus *sts)
item.scantime = atof(row[13]);
item.deadband = atof(row[14]);
item.options = strtoul(row[15], 0, 10);
item.attrnum = 1;
m_items.push_back( item);
if ( item.options & pwr_mSevOptionsMask_DeadBandLinearRegr)
add_cache( m_items.size()-1);
}
mysql_free_result( result);
......@@ -1009,6 +1013,32 @@ int sev_dbms::get_items( pwr_tStatus *sts)
int sev_dbms::store_value( pwr_tStatus *sts, int item_idx, int attr_idx,
pwr_tTime time, void *buf, unsigned int size)
{
if ( m_items[item_idx].options & pwr_mSevOptionsMask_DeadBandLinearRegr) {
double value;
switch ( m_items[item_idx].attr[0].type) {
case pwr_eType_Float32:
value = *(pwr_tFloat32 *)buf;
break;
case pwr_eType_Float64:
value = *(pwr_tFloat64 *)buf;
break;
case pwr_eType_Int32:
value = *(pwr_tInt32 *)buf;
break;
default:
return 0;
}
m_items[item_idx].cache->add( value, &time);
m_items[item_idx].cache->evaluate();
return 1;
}
else
return write_value( sts, item_idx, attr_idx, time, buf, size);
}
int sev_dbms::write_value( pwr_tStatus *sts, int item_idx, int attr_idx,
pwr_tTime time, void *buf, unsigned int size)
{
if(size != m_items[item_idx].value_size) {
//Something is seriously wrong
......@@ -2083,6 +2113,9 @@ int sev_dbms::add_item( pwr_tStatus *sts, pwr_tOid oid, char *oname, char *aname
m_items.push_back( item);
*idx = m_items.size() - 1;
if ( item.options & pwr_mSevOptionsMask_DeadBandLinearRegr)
add_cache( *idx);
*sts = SEV__SUCCESS;
return 1;
}
......@@ -3883,6 +3916,42 @@ int sev_dbms::store_stat( sev_sStat *stat)
return 1;
}
void sev_dbms::write_db_cb( void *data, int idx, double value, pwr_tTime *time)
{
pwr_tStatus sts;
sev_dbms *dbms = (sev_dbms *)data;
switch( dbms->m_items[idx].attr[0].type) {
case pwr_eType_Float32: {
pwr_tFloat32 v = value;
dbms->write_value( &sts, idx, 0, *time, &v, sizeof(v));
break;
}
case pwr_eType_Float64: {
pwr_tFloat64 v = value;
dbms->write_value( &sts, idx, 0, *time, &v, sizeof(v));
break;
}
case pwr_eType_Int32: {
pwr_tInt32 v = value;
dbms->write_value( &sts, idx, 0, *time, &v, sizeof(v));
break;
}
default: ;
}
}
void sev_dbms::add_cache( int item_idx)
{
if ( m_items[item_idx].options & pwr_mSevOptionsMask_DeadBandMeanValue)
m_items[item_idx].cache = new sev_valuecache( sev_eCvType_Mean, m_items[item_idx].deadband,
m_items[item_idx].scantime);
else
m_items[item_idx].cache = new sev_valuecache( sev_eCvType_Point, m_items[item_idx].deadband,
m_items[item_idx].scantime);
m_items[item_idx].cache->set_write_cb( write_db_cb, this, item_idx);
}
int sev_dbms::begin_transaction()
{
char query[20];
......@@ -3917,6 +3986,9 @@ sev_dbms::~sev_dbms()
{
printf("Freeing memory\n");
for(size_t idx = 0; idx < m_items.size(); idx++) {
if ( m_items[idx].cache)
// Write last value
m_items[idx].cache->write(0);
if( m_items[idx].old_value != 0 ) {
free(m_items[idx].old_value);
m_items[idx].old_value = 0;
......
......@@ -139,6 +139,8 @@ class sev_dbms : public sev_db {
pwr_tFloat32 deadband, pwr_tMask options, unsigned int *idx);
int store_value( pwr_tStatus *sts, int item_idx, int attr_idx,
pwr_tTime time, void *buf, unsigned int size);
int write_value( pwr_tStatus *sts, int item_idx, int attr_idx,
pwr_tTime time, void *buf, unsigned int size);
int get_values( pwr_tStatus *sts, pwr_tOid oid, pwr_tMask options, float deadband, char *aname,
pwr_eType type, unsigned int size, pwr_tFloat32 scantime, pwr_tTime *creatime,
pwr_tTime *starttime,
......@@ -161,6 +163,7 @@ class sev_dbms : public sev_db {
char *dbName() { return sev_dbms_env::dbName();}
char *pwrtype_to_type( pwr_eType type, unsigned int size);
static int timestr_to_time( char *tstr, pwr_tTime *ts);
static void write_db_cb( void *data, int idx, double value, pwr_tTime *time);
int check_objectitem( pwr_tStatus *sts, char *tablename, pwr_tOid oid, char *oname, char *aname,
pwr_tDeltaTime storagetime,
char *description, pwr_tFloat32 scantime,
......@@ -200,6 +203,7 @@ class sev_dbms : public sev_db {
int alter_engine( pwr_tStatus *sts, char *tablename);
int optimize( pwr_tStatus *sts, char *tablename);
int store_stat( sev_sStat *stat);
void add_cache( int item_idx);
int begin_transaction();
int commit_transaction();
inline char* create_colName(unsigned int index, char *attributename) {
......
/*
* Proview Open Source Process Control.
* Copyright (C) 2005-2016 SSAB EMEA AB.
*
* This file is part of Proview.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Proview. If not, see <http://www.gnu.org/licenses/>
*
* Linking Proview statically or dynamically with other modules is
* making a combined work based on Proview. Thus, the terms and
* conditions of the GNU General Public License cover the whole
* combination.
*
* In addition, as a special exception, the copyright holders of
* Proview give you permission to, from the build function in the
* Proview Configurator, combine Proview with modules generated by the
* Proview PLC Editor to a PLC program, regardless of the license
* terms of these modules. You may copy and distribute the resulting
* combined work under the terms of your choice, provided that every
* copy of the combined work is accompanied by a complete copy of
* the source code of Proview (the version used to produce the
* combined work), being distributed under the terms of the GNU
* General Public License plus this exception.
**/
#include <stdio.h>
#include <string.h>
#include <float.h>
#include <math.h>
#include "pwr.h"
#include "co_time.h"
#include "rt_gdh.h"
#include "pwr_baseclasses.h"
#include "sev_valuecache.h"
const int sev_valuecache::m_size = VALUECACHE_SIZE;
int sev_valuecache::idx( int index)
{
if ( !m_length)
return 0;
if ( index >= m_length)
return 0;
int i = m_last - index;
if ( i < 0)
i += m_size;
return i;
}
sev_sCacheValue& sev_valuecache::operator[]( const int index)
{
return m_val[idx(index)];
}
void sev_valuecache::add( double val, pwr_tTime *t)
{
double time;
pwr_tDeltaTime dt;
time_Adiff_NE( &dt, t, &m_start_time);
time = time_DToFloat64( 0, &dt);
// Store optimized write index before adding
m_last_opt_write = get_optimal_write();
bool update_k = m_length < m_size;
if ( !m_length) {
m_val[0].val = val;
m_val[0].time = time;
m_length++;
}
else {
if ( ++m_last >= m_size)
m_last -= m_size;
m_val[m_last].val = val;
m_val[m_last].time = time;
m_length++;
if ( m_last == m_first) {
m_first++;
if ( m_first >= m_size)
m_first -= m_size;
m_length--;
}
}
if ( !m_inited) {
write( 0);
m_inited = true;
return;
}
if ( update_k) {
calculate_k();
// Update epsilon for all data
calculate_epsilon();
}
else
calculate_epsilon(0);
}
void sev_valuecache::evaluate()
{
int value_added = 1;
while( 1) {
if ( !check_deadband()) {
// Store optimal value
write( m_last_opt_write + value_added);
}
else
break;
calculate_k();
calculate_epsilon();
m_last_opt_write = get_optimal_write();
value_added = 0;
}
}
void sev_valuecache::calculate_k()
{
double xysum = 0;
double x2sum = 0;
for ( int i = 0; i < length(); i++) {
int ii = idx(i);
xysum += (m_val[ii].val - m_wval.val) * (m_val[ii].time - m_wval.time);
x2sum += (m_val[ii].val - m_wval.val) * (m_val[ii].val - m_wval.val);
}
if ( x2sum < DBL_EPSILON) {
m_k = 1E32;
m_m = m_wval.time;
m_deadband = m_deadband_time;
}
else {
m_k = x2sum/xysum;
m_m = m_wval.val - m_wval.time * m_k;
m_deadband = m_deadband_value +
fabs(atan(m_k)) / (M_PI/2) * ( m_deadband_time - m_deadband_value);
}
}
void sev_valuecache::write( int index)
{
int ii = idx(index);
double wval, wtime;
if ( m_type == sev_eCvType_Mean) {
if ( fabs(m_last_k) < 1) {
m_wval.val = m_wval.val + m_last_k * (m_val[ii].time - m_wval.time);
m_wval.time = m_val[ii].time;
}
else {
m_wval.time = m_wval.time + (m_val[ii].val - m_wval.val) / m_last_k;
m_wval.val = m_val[ii].val;
}
wval = m_wval.val;
wtime = m_wval.time;
}
else {
wval = m_val[ii].val;
wtime = m_val[ii].time;
m_wval = m_val[ii];
}
if ( index == 0) {
m_last = m_first = 0;
m_length = 0;
}
else {
m_first = ii + 1;
if ( m_first >= m_size)
m_first -= m_size;
m_length = m_last - m_first + 1;
if ( m_length < 0)
m_length += m_size;
}
if ( m_write_cb) {
pwr_tTime time;
time_Aadd( &time, &m_start_time, time_Float64ToD( 0, wtime));
(m_write_cb)( m_userdata, m_useridx, wval, &time);
}
}
// Calculate epsilon for all
void sev_valuecache::calculate_epsilon()
{
if ( m_length == 1) {
m_val[m_first].epsilon = 0;
return;
}
for ( int i = 0; i < m_length; i++)
calculate_epsilon(i);
}
// Calculate epsilon for one index
void sev_valuecache::calculate_epsilon( int index)
{
int ii = idx(index);
if ( m_k >= 1E32) {
m_val[ii].epsilon = fabs( m_val[ii].time - m_wval.time);
}
else {
m_val[ii].epsilon = fabs(m_val[ii].val - m_k * m_val[ii].time - m_m) / sqrt( 1 + m_k * m_k);
}
}
// Check deadband for one index
// Returns true if all values inside deadband.
bool sev_valuecache::check_deadband( int index)
{
if ( m_val[idx(index)].epsilon > m_deadband)
return false;
return true;
}
// Check deadband for all values
// Returns true if all values inside deadband.
bool sev_valuecache::check_deadband()
{
for ( int i = 0; i < m_length; i++) {
int ii = idx(i);
if ( m_val[ii].epsilon > m_deadband)
return false;
}
return true;
}
int sev_valuecache::get_optimal_write()
{
if ( m_type == sev_eCvType_Mean) {
m_last_k = m_k;
return 0;
}
double min_weight = 10E32;
int ii;
double dist;
double weight;
int min_idx = 0;
for ( int i = 0; i < m_length; i++) {
if ( m_length == m_size && i == m_length - 1)
continue;
ii = idx(i);
dist = sqrt( (m_val[ii].val - m_wval.val) * (m_val[ii].val - m_wval.val) +
(m_val[ii].time - m_wval.time) * (m_val[ii].time - m_wval.time));
weight = m_val[ii].epsilon / dist;
if ( weight < min_weight) {
min_weight = weight;
min_idx = i;
}
}
return min_idx;
}
/*
* Proview Open Source Process Control.
* Copyright (C) 2005-2016 SSAB EMEA AB.
*
* This file is part of Proview.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Proview. If not, see <http://www.gnu.org/licenses/>
*
* Linking Proview statically or dynamically with other modules is
* making a combined work based on Proview. Thus, the terms and
* conditions of the GNU General Public License cover the whole
* combination.
*
* In addition, as a special exception, the copyright holders of
* Proview give you permission to, from the build function in the
* Proview Configurator, combine Proview with modules generated by the
* Proview PLC Editor to a PLC program, regardless of the license
* terms of these modules. You may copy and distribute the resulting
* combined work under the terms of your choice, provided that every
* copy of the combined work is accompanied by a complete copy of
* the source code of Proview (the version used to produce the
* combined work), being distributed under the terms of the GNU
* General Public License plus this exception.
**/
#ifndef sev_valuecache_h
#define sev_valuecache_h
#include "pwr.h"
#include "co_time.h"
typedef enum {
sev_eCvType_Point,
sev_eCvType_Mean,
} sev_eCvType;
typedef struct {
double val;
double time;
double epsilon;
} sev_sCacheValue;
#define VALUECACHE_SIZE 20
class sev_valuecache {
static const int m_size;
sev_eCvType m_type;
void *m_userdata;
int m_useridx;
int m_length;
int m_first;
int m_last;
bool m_inited;
sev_sCacheValue m_val[VALUECACHE_SIZE];
sev_sCacheValue m_wval;
double m_k;
double m_m;
double m_deadband;
double m_deadband_value;
double m_deadband_time;
int m_last_opt_write;
pwr_tTime m_start_time;
double m_last_k;
void (*m_write_cb)( void *, int , double, pwr_tTime *);
public:
sev_valuecache( sev_eCvType type, double deadband_value, double deadband_time) :
m_type(type), m_userdata(0), m_useridx(0), m_length(0), m_first(0), m_last(0), m_inited(false),
m_deadband_value(deadband_value), m_deadband_time(deadband_time), m_write_cb(0) {
memset( &m_wval, 0, sizeof(m_wval));
time_GetTime(&m_start_time);
}
sev_valuecache( const sev_valuecache& x) : m_type(x.m_type), m_userdata(x.m_userdata), m_useridx(x.m_useridx),
m_length(x.m_length),
m_first(x.m_first), m_last(x.m_last), m_inited(x.m_inited), m_k(x.m_k), m_deadband(x.m_deadband),
m_deadband_value(x.m_deadband_value), m_deadband_time(x.m_deadband_time), m_last_opt_write(x.m_last_opt_write),
m_start_time(x.m_start_time), m_last_k(x.m_last_k), m_write_cb(x.m_write_cb) {
memcpy( m_val, x.m_val, sizeof(m_val));
memcpy( &m_wval, &x.m_wval, sizeof(m_wval));
}
~sev_valuecache() {}
int length() { return m_length;}
int idx( int index);
sev_sCacheValue& operator[]( const int index);
sev_sCacheValue& wval() { return m_wval;}
void add( double value, pwr_tTime *time);
void evaluate();
void calculate_k();
void write( int index);
void calculate_epsilon();
void calculate_epsilon( int index);
bool check_deadband( int index);
bool check_deadband();
int get_optimal_write();
double epsilon(int index) { return m_val[idx(index)].epsilon;}
double get_k() { return m_k;}
void set_write_cb( void (*write_cb)( void *, int, double, pwr_tTime *), void *userdata, int idx) {
m_write_cb = write_cb;
m_userdata = userdata;
m_useridx = idx;
}
};
#endif
......@@ -106,6 +106,26 @@ SObject pwrb:Type
EndBody
EndObject
!/**
! Deadband calculated with linear regression of last points.
!*/
Object DeadBandLinearRegr $Bit
Body SysBody
Attr PgmName = "DeadBandLinearRegr"
Attr Text = "DeadBandLinearRegr"
Attr Value = 64
EndBody
EndObject
!/**
! Store a mean value calculated with linear regression.
!*/
Object DeadBandMeanValue $Bit
Body SysBody
Attr PgmName = "DeadBandMeanValue"
Attr Text = "DeadBandMeanValue"
Attr Value = 128
EndBody
EndObject
!/**
! Not yet implemented.
!*/
Object Parameter $Bit
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment