|
uniset-algorithms
0.2
|
#include <SEESGroup.h>
Классы | |
| struct | ReservInfo |
Открытые типы | |
| typedef std::list< ReservInfo > | RList |
Открытые члены | |
| SEESGroup (int num, const std::string name="") | |
| int | getNumber () |
| std::string | getName () |
| long | getP () |
| long | getU () |
| long | getF () |
| bool | isUdev () |
| bool | isFdev () |
| long | getReservPower () |
| void | step () |
| void | update () |
| void | updatePowerInfo () |
| void | requiredReserv (bool set) |
| bool | isNoReserv () |
| bool | isReadyForLoading () |
| void | addGroup (SEESGroup *gr) |
| bool | add (SEES *ses) |
| bool | del (SEES *ses) |
| void | clear () |
| bool | isProtection () |
| int | acceptQG (int num) throw (UniSetTypes::NameNotFound) |
| void | releaseQG (int num) throw (UniSetTypes::NameNotFound) |
| bool | canRun (int num) throw (UniSetTypes::NameNotFound) |
| SEES * | get (int num) |
| SESList | getSESList () |
| void | setReservStatePause (int s) |
| void | setCommandTimeout (int s) |
| void | setReservTime (int s) |
| void | setMaxWorkingDG (int s) |
| int | getWorkingDG () |
| void | setFdev (int f_min_, int f_max_, int f_msec) |
| void | setUdev (int u_min_, int u_max_, int U_msec) |
Открытые статические члены | |
| static void | init_dlog (DebugStream &dlog) |
Статические открытые данные | |
| static DebugStream | dlog |
Защищенные члены | |
| void | reserv (bool set) |
| void | checkReserv () |
| bool | resetReserv () |
| void | makeReservList () |
| void | checkAloneWorking () |
| void | updateCounters () |
Защищенные статические члены | |
| static void | update (RList &rlist, ReservInfo &ri) |
Защищенные данные | |
| UniSetTypes::uniset_mutex | lstMutex |
| int | gnum |
| std::string | myname |
| int | qf_locked |
| SESList | lst |
| SEES * | leader |
| RList | rlst |
| int | maxWorkingDG |
| int | countWorkingDG |
| UniSetTypes::uniset_mutex | maxrunMutex |
| int | reservStatePause |
| int | reservCommandTimeout |
| int | checkReservTime |
| PassiveTimer | ptCheckReserv |
| int | f_min |
| int | f_max |
| int | U_min |
| int | U_max |
| SandClock | sc_Fdev |
| SandClock | sc_Udev |
| bool | U_dev |
| bool | f_dev |
Друзья | |
| std::ostream & | operator<< (std::ostream &os, SEESGroup &g) |
| std::ostream & | operator<< (std::ostream &os, SEESGroup *g) |
| std::ostream & | print_chain (std::ostream &os, SEESGroup *g) |
Реализация управления группой СЭС
| int SEESGroup::acceptQG | ( | int | num | ) | throw (UniSetTypes::NameNotFound) |
захват приоритета включения генераторного автомата
{
{ // lock
UniSetTypes::uniset_mutex_lock lock(lstMutex, 5000);
if( qf_locked != 0 )
{
if( qf_locked == num )
{
if( SEESGroup::dlog.debugging(Debug::INFO) )
SEESGroup::dlog[Debug::INFO] << myname << "(acceptQG): QG" << num << " ALREADY ACCEPTED..." << endl;
return qf_locked;
}
if( SEESGroup::dlog.debugging(Debug::INFO) )
SEESGroup::dlog[Debug::INFO] << myname << "(acceptQG): NOT ACCEPT QG" << num << " qf locked..." << endl;
return qf_locked;
}
for( SESList::iterator it=lst.begin(); it!=lst.end(); ++it )
{
SEES* s(*it);
if( s->isHaveQGLock() )
{
if( (*it)->getNumber() == num )
{
if( SEESGroup::dlog.debugging(Debug::INFO) )
SEESGroup::dlog[Debug::INFO] << myname << "(acceptQG): REPEAT ACCEPT QG" << num << endl;
qf_locked = num;
return qf_locked;
}
if( SEESGroup::dlog.debugging(Debug::INFO) )
SEESGroup::dlog[Debug::INFO] << myname << "(acceptQG): NOT ACCEPT QG" << num
<< " but blocked ses" << (*it)->getNumber() << endl;
s->setQGLock(true);
qf_locked = (*it)->getNumber();
return qf_locked;
}
}
}
ostringstream err;
err << myname << "(acceptQG): Not found ses" + num;
throw NameNotFound(err.str());
}
| void SEESGroup::checkAloneWorking | ( | ) | [protected] |
обновление информации о том "один ли ДГ на шинах"
Перекрестные ссылки lst.
{
{ // lock
UniSetTypes::uniset_mutex_lock lock(lstMutex, 4000);
int count = getWorkingDG();
if( count > 0 )
{
for( SESList::iterator it=lst.begin(); it!=lst.end(); ++it )
{
if( (*it)->isOnMode() )
{
if( count == 1 )
(*it)->setAloneWorking(true);
else
(*it)->setAloneWorking(false);
}
else
(*it)->setAloneWorking(false);
}
}
else
{
for( SESList::iterator it=lst.begin(); it!=lst.end(); ++it )
(*it)->setAloneWorking(true);
}
}// unlock
}
| void SEESGroup::checkReserv | ( | ) | [protected] |
проверка необходимости запуска/остановки резерва
Перекрестные ссылки leader, myname, reserv(), resetReserv() и rlst.
{
UniSetTypes::uniset_mutex_lock lock(lstMutex, 5000);
bool needCheck = false;
if( !leader )
{
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
SEESGroup::dlog[Debug::LEVEL4] << myname << "(checkReserv): ignore.. no leader.." << endl;
return;
}
if( leader->isReservOnNeeded() )
{
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
SEESGroup::dlog[Debug::LEVEL4] << myname << "(checkReserv): needON for "
<< leader->getName() << endl;
reserv(true);
needCheck = true;
}
else if( leader->isReservOffNeeded() )
{
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
SEESGroup::dlog[Debug::LEVEL4] << myname << "(checkReserv): needOFF for "
<< leader->getName() << endl;
reserv(false);
needCheck = true;
}
else
{
// Сбрасываем все таймеры и тригеры
// т.к. резерв никому не требуется
for( RList::iterator it=rlst.begin(); it!=rlst.end(); ++it )
{
it->ptPause.setTiming(0);
it->trChangeState.hi(true);
it->waitRunning = false;
it->waitStopping = false;
}
resetReserv();
}
if( !needCheck )
ptCheckReserv.setTiming(UniSetTimer::WaitUpTime);
}
| long SEESGroup::getF | ( | ) |
| long SEESGroup::getP | ( | ) |
| long SEESGroup::getReservPower | ( | ) |
| long SEESGroup::getU | ( | ) |
| bool SEESGroup::isFdev | ( | ) |
отклонение частоты от нормы
Перекрестные ссылки f_dev.
Используется в UnionGroups::updateFdev().
{
return f_dev;
}
| bool SEESGroup::isProtection | ( | ) |
| bool SEESGroup::isReadyForLoading | ( | ) |
готова ли группа к приёму нагрузки
Перекрестные ссылки lst.
Используется в UnionGroups::updateReadyForLoading().
| bool SEESGroup::isUdev | ( | ) |
отклонение напряжения от нормы
Перекрестные ссылки U_dev.
Используется в UnionGroups::updateUdev().
{
return U_dev;
}
| void SEESGroup::makeReservList | ( | ) | [protected] |
обновление списка резервных ДГ
Перекрестные ссылки SEES::isAutoControl(), SEES::isOnMode(), leader, lst, myname и rlst.
{
UniSetTypes::uniset_mutex_lock lock(lstMutex, 5000);
// проходим по списку СЭС в группе
// определяем главный ДГ (он должен быть один)
// и какие есть резервные ДГ
SEES* main = 0;
RList rlist;
// Т.к. самым приоритетным считается ДГ с наименьшим заданным приоритетом,
// то строим список резервных так: делаем map, сортируем по возрастанию приоритета
// и строим уже список резервных
// в map - добавляем только те ДГ, которые находятся в режиме "авто"
typedef std::map<int,SEES*> PriorityMap;
PriorityMap prmap;
for( SESList::iterator it=lst.begin(); it!=lst.end(); ++it )
{
SEES* ses(*it);
if( ses->isAutoControl() && ses->getPriority()>0 )
prmap[ses->getPriority()] = ses;
else
ses->setLeader(false); // просто на всякий сбрасываем флаг
// если ГДГ не в режиме автомат, сбрасываем команды управления резервом
if( !ses->isAutoControl() )
ses->resetReservCommand();
}
// формируем список резервных на основе "карты приоритетов"
if( !prmap.empty() )
{
PriorityMap::iterator it=prmap.begin();
// первый в списке, это самый приоритетный
main = it->second;
it++;
// остальные добавляем в список в соответсвии с приоритетом
while( it!=prmap.end() )
{
rlist.push_back(it->second);
it++;
}
}
// -------------------
// Есть особый случай:
// Допустим, что НЕ приоритетный дизель, до перевода в "автомат"
// был в ручную запущен. А переключателем "приоритета пуска"
// выставлен приоритетным другой дизель. То после перевода обоих ДГ
// в режим "автомат" будет противоречивое состояние:
// Тот дизель который должен быть основным (и по идее начать автоматически запускаться)
// не запущен, а тот который должен быть резервным (и должен быть остановлен),
// оказывается запущенным (уже на шинах).
// Для решения этого вопроса, здесь реализована следующая логика:
// Если найденный "потенциальный" лидер, не запущен, то мы смотрим,
// нет ли среди списка резервных уже запущенного. Если есть, то находим,
// среди запущенных (резервных), ДГ с наименьшим приоритетом и делаем его лидером.
// А нашего "потенциального" лидера (если был до этого найден),
// переводим в список резервных.
if( !main || (main && !main->isOnMode()) )
{
RList::iterator rit = rlist.end();
for( RList::iterator it=rlist.begin(); it!=rlist.end(); ++it )
{
if( !it->ses->isOnMode() )
continue;
long prior = it->ses->getPriority();
if( rit == rlist.end() || ( prior>0 && prior < rit->ses->getPriority() ))
rit = it;
}
// если нашли среди резервных "запущенный"
if( rit != rlist.end() )
{
if( main )
{
// сперва добавляем "бывшего" лидера в список резервных
ReservInfo ri(main);
rlist.push_back(ri);
}
// запоминаем нового лидера
main = rit->ses;
// удаляем его из списка резервных
rlist.erase(rit);
}
}
// -------------------
if( !main )
{
// Если не нашли (явного) приоритетного лидера (например у всех одинаковый приоритет)
// а список резервных не пустой
// то берём из резервных с наименьшим номером
// и делаем его лидером (удалив из списка резервных)
RList::iterator rit = rlist.end();
if( !rlist.empty() )
{
for( RList::iterator it=rlist.begin(); it!=rlist.end(); ++it )
{
long num = it->ses->getNumber();
if( rit == rlist.end() || num < rit->ses->getNumber() )
rit = it;
}
if( rit != rlist.end() )
{
main = rit->ses;
rlist.erase(rit);
}
}
}
// ------------------
if( main )
{
// чтобы сохранить работу внутренних таймеров структуры ReservInfo
// надо в новом списке обновить информацию взяв из старого
for( RList::iterator it=rlst.begin(); it!=rlst.end(); ++it )
update( rlist, (*it) );
leader = main;
rlst = rlist;
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
{
SEESGroup::dlog[Debug::LEVEL4] << myname << "(makeReservList): For leader='ses" << main->getNumber()
<< "' reserv(" << rlist.size() << "): [";
for( RList::iterator it=rlst.begin(); it!=rlst.end(); ++it )
SEESGroup::dlog(Debug::LEVEL4) << " ses" << it->ses->getNumber(); // << ")" << it->ses->getName();
SEESGroup::dlog(Debug::LEVEL4) << " ]" << endl;
}
// выставляем у всех признак, что они в резерве (не лидеры)
for( RList::iterator it=rlst.begin(); it!=rlst.end(); ++it )
{
try
{
it->ses->setLeader(false);
}
catch(...){}
}
// и выставляем режим у "ведущего"
leader->setLeader(true);
}
else
{
SEESGroup::dlog[Debug::LEVEL4] << myname << "(makeReservList): LEADER not found..." << endl;
leader = 0;
rlst.clear();
}
}
| void SEESGroup::releaseQG | ( | int | num | ) | throw (UniSetTypes::NameNotFound) |
освобождение приоритета включения генераторного автомата Проверка можно ли запускать указанный СЭС например ограничение по максимльному разрешенному количеству одновременно работющих ДГ на шинах
{
{ // lock
UniSetTypes::uniset_mutex_lock lock(lstMutex, 4000);
for( SESList::iterator it=lst.begin(); it!=lst.end(); ++it )
{
SEES* s(*it);
if( s->getNumber() == num )
{
if( s->isHaveQGLock() )
SEESGroup::dlog[Debug::INFO] << myname << "(releaseQG): RELEASE QG" << num << endl;
s->setQGLock(false);
qf_locked = 0;
return;
}
}
}
ostringstream err;
err << myname << "(acceptQG): Not found ses" + num;
throw NameNotFound(err.str());
}
| void SEESGroup::requiredReserv | ( | bool | set | ) |
| void SEESGroup::reserv | ( | bool | set | ) | [protected] |
функция запуска или остановки резерва
Перекрестные ссылки SEES::isProtection(), myname, rlst, SEESGroup::ReservInfo::waitRunning и SEESGroup::ReservInfo::waitStopping.
Используется в checkReserv().
{
// UniSetTypes::uniset_mutex_lock lock(lstMutex, 5000);
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
{
SEESGroup::dlog[Debug::LEVEL4] << myname << "(reserv):"
<< " set=" << set
<< " rlist=" << rlst.size() << endl;
}
if( rlst.empty() )
{
SEESGroup::dlog[Debug::LEVEL4] << myname
<< "(reserv): rlst=0 RESERV NOT FOUND" << endl;
ptCheckReserv.setTiming(UniSetTimer::WaitUpTime);
return;
}
// Проходим по списку резервных
// и ищем кто готов запускатся..
// Т.к. список выстроен в соответсвии с приоритетом
// то можно остановиться на первом подходящем
// Если нам нужно запускать ищем в прямом порядке,
// если останавливать, то в обратном.
typedef list<ReservInfo*> TMPList;
TMPList tmplist;
if( set )
{
for( RList::iterator it=rlst.begin(); it!=rlst.end(); ++it )
tmplist.push_back( &(*it) );
}
else
{
for( RList::iterator it=rlst.begin(); it!=rlst.end(); ++it )
tmplist.push_front( &(*it) );
}
ReservInfo* rses = 0;
for( TMPList::iterator it=tmplist.begin(); it!=tmplist.end(); ++it )
{
ReservInfo* ri(*it);
// Ловим момент, если резервный только что, запустился/остановился
// и засекаем таймер на "паузу"
// необходимую для прихода системы в устойчивое состояние
// после пуска/остановки резерва
// (например распределение нагрузки и т.п.)
// а то может начать запускатся следующий резервный
// если распределитель ещё не успел "поработать"
// и основной(leader) остался загружен
// несмотря на то, что уже запустился резервный
if( set && ri->trChangeState.hi(ri->ses->getOMode()==mON) )
{
ri->ptPause.setTiming(reservStatePause);
ri->waitRunning = false;
ri->waitStopping = false;
}
else if( !set && ri->trChangeState.hi(ri->ses->getOMode()==mOFF) )
{
ri->ptPause.setTiming(reservStatePause);
ri->waitRunning = false;
ri->waitStopping = false;
}
// ничего не делаем, пока резервный ses исполняет команду
// либо он её выполнит, либо timeout, либо у него сработает защита
if( (set && ri->waitRunning) || (!set && ri->waitStopping) )
{
if( !ri->ptPause.checkTime() && !ri->ses->isProtection() )
{
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
{
int wmsec = ri->ptPause.getInterval() - ri->ptPause.getCurrent();
SEESGroup::dlog[Debug::LEVEL4] << myname << "(reserv):"
<< " IGNORE. Wait processing command for ses"
<< ri->ses->getNumber()
<< " (sec=" << wmsec/1000 << " msec=" << wmsec%1000 << ")"
<< endl;
}
ptCheckReserv.setTiming(checkReservTime);
return;
}
ri->waitRunning = false;
ri->waitStopping = false;
ri->ptPause.setTiming(0);
}
// Если пауза ещё не прошла, ждём...
// либо признак нужности/ненужности резерва
// снимется у leader, либо мы начнём
// запускатся/останавливать следующий в списке
if( !ri->ptPause.checkTime() && !ri->waitRunning && !ri->waitStopping )
{
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
{
int wmsec = ri->ptPause.getInterval() - ri->ptPause.getCurrent();
SEESGroup::dlog[Debug::LEVEL4] << myname << "(reserv):"
<< " IGNORE. Wait state pause for ses"
<< ri->ses->getNumber()
<< " (sec=" << wmsec/1000 << " msec=" << wmsec%1000 << ")"
<< endl;
}
ptCheckReserv.setTiming(checkReservTime);
return;
}
// Эти условия должны проверятся ОБЯЗАТЕЛЬНО после ptStatePause
// т.к. canReservRunning или canReservStopping вернут false
// как только, резерв запустится/остановится.
// а нам ведь надо ещё и паузу выждать,
// прежде чем пытатся запускать/останавливать
// следующий резервный в списке..
if( set && !ri->ses->canReservRunning() ) // требуется запуск, а не может
continue;
if( !set && !ri->ses->canReservStopping() ) // требуется остановка, а не может
continue;
rses = ri;
break;
}
if( !rses )
{
SEESGroup::dlog[Debug::LEVEL4] << myname << "(reserv):"
<< " FREE RESERV NOT FOUND" << endl;
ptCheckReserv.setTiming(UniSetTimer::WaitUpTime);
return;
}
if( set )
{
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
{
SEESGroup::dlog[Debug::LEVEL4] << myname << "(reserv):"
<< " SELECT RUN ses" << rses->ses->getNumber() << endl;
}
rses->ses->onReservCommand();
rses->waitRunning = true;
rses->waitStopping = false;
}
else
{
if( SEESGroup::dlog.debugging(Debug::LEVEL4) )
{
SEESGroup::dlog[Debug::LEVEL4] << myname << "(reserv):"
<< " SELECT STOP ses" << rses->ses->getNumber() << endl;
}
rses->ses->offReservCommand();
rses->waitRunning = false;
rses->waitStopping = true;
}
ptCheckReserv.setTiming(checkReservTime);
rses->trChangeState.hi(false);
rses->ptPause.setTiming(reservCommandTimeout);
}
| bool SEESGroup::resetReserv | ( | ) | [protected] |
сброс команд управления резерва
Перекрестные ссылки lst, myname и rlst.
Используется в checkReserv().
{
// SEESGroup::dlog[Debug::LEVEL4] << myname << "(resetReserv): ..." << endl;
for( SESList::iterator it=lst.begin(); it!=lst.end(); ++it )
{
// если есть хоть один СЭС требущий резерв,
// то сбрасывать команды нельзя...
if( (*it)->isReservOnNeeded() || (*it)->isReservOffNeeded() )
return false;
}
// сбрасываем команды
for( RList::iterator it=rlst.begin(); it!=rlst.end(); ++it )
{
SEESGroup::dlog[Debug::LEVEL4] << myname << "(resetReserv): сбрасываем команды резерва для ses" << it->ses->getNumber() << endl;
it->ses->resetReservCommand();
}
return true;
}
| void SEESGroup::update | ( | RList & | rlist, |
| ReservInfo & | ri | ||
| ) | [static, protected] |
функция копирующая в список rlst, информацию из ri, если там есть такой ДГ
{
//UniSetTypes::uniset_mutex_lock lock(lstMutex, 5000);
for( RList::iterator it=rlist.begin(); it!=rlist.end(); ++it )
{
if( it->ses->getNumber() == ri.ses->getNumber() )
{
(*it) = ri;
return;
}
}
}
| void SEESGroup::updateCounters | ( | ) | [protected] |
Обновление различных счётчиков
Перекрестные ссылки countWorkingDG и lst.
{
int count = 0;
{ // lock
UniSetTypes::uniset_mutex_lock lock(lstMutex, 4000);
for( SESList::iterator it=lst.begin(); it!=lst.end(); ++it )
{
if( (*it)->isOnMode() )
count++;
}
}
// Обновляем информацию о текущем количестве работающих ДГ
{
uniset_mutex_lock l(maxrunMutex,200);
countWorkingDG = count;
}
}
| void SEESGroup::updatePowerInfo | ( | ) |
Обновление расчётных параметров сети
Перекрестные ссылки getF(), getU(), sc_Fdev и sc_Udev.
{
bool u_res = false;
// Фомирование сигнала по отклонению напряжения
if( U_min > 0 )
{
long u = getU();
if( u > 0 )
{
if( u < U_min || u > U_max )
u_res = true;
}
}
// устанавливаем (или отключаем) таймер
sc_Udev.rotate(u_res);
bool f_res = false;
// Фомирование сигнала по отклонению напряжения
if( f_min > 0 )
{
long f = getF();
if( f > 0 )
{
if( f < f_min || f > f_max )
f_res = true;
}
}
// устанавливаем (или отключаем) таймер
sc_Fdev.rotate(f_res);
}
int SEESGroup::countWorkingDG [protected] |
текущее количество работающих ДГ
Используется в updateCounters().
bool SEESGroup::f_dev [protected] |
флаг отклонение частоты от нормы
Используется в isFdev().
int SEESGroup::gnum [protected] |
номер данной группы
SEES* SEESGroup::leader [protected] |
указатель на "ведущий" ДГ в группе
Используется в checkReserv() и makeReservList().
SESList SEESGroup::lst [protected] |
номер СЭС заблокировавшей включение автомата список СЭС в группе
Используется в checkAloneWorking(), getF(), getP(), getReservPower(), getU(), isProtection(), isReadyForLoading(), makeReservList(), requiredReserv(), resetReserv() и updateCounters().
int SEESGroup::maxWorkingDG [protected] |
максимальное разрешённое количество одновременно работающих ДГ
std::string SEESGroup::myname [protected] |
исключительно для логов
Используется в checkReserv(), makeReservList(), reserv() и resetReserv().
RList SEESGroup::rlst [protected] |
список доступных резервных ДГ
Используется в checkReserv(), makeReservList(), reserv() и resetReserv().
SandClock SEESGroup::sc_Fdev [protected] |
специальная задержка на срабатывание по сигналу "отклонение частоты"
Используется в updatePowerInfo().
SandClock SEESGroup::sc_Udev [protected] |
специальная задержка на срабатывание по сигналу "отклонение напряжения"
Используется в updatePowerInfo().
bool SEESGroup::U_dev [protected] |
флаг отклонение напряжения от нормы
Используется в isUdev().
1.7.6.1