Автоматический запуск автопроцессов с контролем монопольного захвата таблицЧасто бывает нужно, чтобы какой-то автомат, выполнял некоторые действия постоянно. К примеру – выгружал бы прайс-лист на web-сайт предприятия или совершал бы обмен данными между двумя разнородными базами. И бывает довольно неприятно, когда выясняется, что этот автопроцесс уже пару дней не работает просто потому, что его забыли запустить на сервере. Данная статья описывает способ, который позволит забыть про необходимость вручную запускать такого рода автопроцессы. Технически, этот способ представляет из себя сценарий для Windows Scripting Host (WSH), написанный на JScript. Скрипт предельно прост и при желании, его можно перевести на VB (было бы только это кому-то нужно). К статье прилагается пример скрипта, который (за незначительными изменениями) работает у меня и успешно запускает автопроцессы. | | Автор статьи: Лёвыч | Редакторы: Последняя редакция №10 от 26.01.07 | История URL: http://kb.mista.ru/article.php?id=402 | |
Ключевые слова: автопроцесс, монопольный, ADO, 1Cv7.lck
Введение
Под автопроцессом мы будем понимать некий сеанс 1С:Предприятия, который без вмешательства пользователя выполняет с некоторой периодичностью какую-то обработку. Казалось бы, что сложного – взять да и запустить сеанс один раз «врукопашную» и забыть про него. Забыть-то не сложно, сложно вспомнить о необходимости запуска автопроцесса после того, как, например, программист «выгнал» всех пользователей из базы для внесения каких-то изменений в метаданные или для проведения каких-либо мероприятий в ИБ, требующих монопольного захвата ее таблиц.
Постановка задачи
Итак, чтобы администратор БД мог, как и раньше, забывать о необходимости запуска автопроцессов, но чтобы запуск все-таки происходил, нам необходим некий дополнительный процесс, который будет постоянно «висеть» в памяти (в нашем случае этим процессом будет сценарий на JavaScript) и, если автопроцесс не запущен, но, при этом, к базе ни кто не подключился монопольно, то наш скрипт должен будет запустить автопроцесс.
Иными словами, задача в том, чтобы автопроцессы работали ВСЕГДА, КОГДА ЭТО ТОЛЬКО ВОЗМОЖНО
Решение
Итак, для решения, нам необходимо «программно» ответить на два вопроса:
- Запущен ли уже автопроцесс?
- Нет ли монопольного подключения к базе?
На первый вопрос получить ответ достаточно просто – для этого нужно проверить наличие файла 1Cv7.lck в каталоге пользователя, который является нашим роботом. Если файла нет или, если его можно удалить, то автопроцесс не запущен. Наличие заблокированного lck в каталоге пользователя однозначно сигнализирует о том, что пользователь в данный момент подключен к БД.
Со вторым вопросом все несколько сложнее — такого удобного однозначного семафора не существует. При монопольном соединении с ИБ 1С создает lck в каталоге базы и накладывает на него блокировку. И, вроде бы, все так же замечательно, однако, точно такой же файл создается в каталоге ИБ, когда кто-то открывает конфигуратор. Причем, если еще одна особенность: если, к примеру, сначала открыли конфигуратор, потом кто-то зашел в разделенном режиме (или запустил монитор пользователей), после чего конфигуратор был закрыт, то 1Cv7.lck в каталоге ИБ все равно останется, при этом он будет заблокирован (теми, кто вошел в базу после открытия конфигуратора), т.е. удалить его будет нельзя. Спрашивается, как узнать, заблокированы ли таблицы БД? На самом деле, ответ прост: достаточно попытаться получить из них какие-то данные. Если получить данные не удается, значит доступ закрыт. Кроме того, будем считать, что база занята монопольно, если к ней подключен ТОЛЬКО один. Для получения данных из таблиц без загрузки 1С мы воспользуемся интерфейсом ADO. Для получения информации о количестве подключившихся пользователей мы будем получать данные из таблицы _1SUSERS (эта таблица содержит всего одну запись и в поле USRSCNT содержится количество подключенных в данный момент пользователей).
Задача подключения к базе и получения данных через интерфейс ADO имеет следующую общую схему:
- создать объект ADOConnection;
- вызвать его метод Open(), указав ему строку соединения, в которой содержатся параметры подключения;
- создать объект ADORecordset и выполнить SQL-запрос.
Не секрет, что у 1С:Предприятия базы данных бывают двух видов — SQL и DBF. Приведенная схема работает одинаково для обоих видов баз, разница есть лишь в строке соединения, передаваемой методу ADOConnection::Open(). Вот эти строки:
- Для SQL-базы: "Provider=SQLOLEDB.1;User ID=<Логин в базе SQL>;Pwd=<пароль от этого логина>;Data Source=(local);Initial Catalog=<Имя базы SQL>";
- Для DBF-базы: "Driver={Microsoft dBASE Driver (*.dbf)};DriverID=277;Dbq=<путь к каталогу базы данных>;"
Все данные, которые нужно заменить конкретными значениями, указаны в угловых скобках.
Ну вот, у нас все готово для написания скрипта, запускающего наши автопроцессы.
var g_WSHShell = WScript.CreateObject("WScript.Shell");
var g_FSO = new ActiveXObject("Scripting.FileSystemObject");
// главный цикл. Скрипт работает вечно, совершенно верно
<BR>
while(true)
{
if(canRunAutomatons())
{
runAnAutomaton("ИмяПользователя-робота", "пароль");
runAnAutomaton("таких роботов может быть и несколько", "");
}
WScript.Sleep(1000*60*3); // раз в 3 минуты цикл повторяется
}
//****************************************
// FUNCTIONS
//****************************************
function canRunAutomatons(){
if(getNumberOfConnections() >= 2)
return true; // пока в базе меньше 2-х пользователей, мы считаем, что она занята монопольно
<BR>return false;
}
function getNumberOfConnections(){
var l_AdoCnn = new ActiveXObject("ADODB.Connection");
var l_sQuery = "SELECT * FROM _1SUSERS";
var l_sCnnString = "Provider=SQLOLEDB.1;User ID=пароль;Pwd=логин;Data Source=сервер;Initial Catalog=ИмяБазы";
try{
l_AdoCnn.Open(l_sCnnString);
rs = new ActiveXObject("ADODB.Recordset");
rs.ActiveConnection = l_AdoCnn;
rs.Source = l_sQuery;
rs.Open();
return rs.Fields("USRSCNT").Value;
}catch(e){}
return -1;
}
function runAnAutomaton(sName, sPassword){
var sLCK = "Путь:\\К\\Каталогам\\Пользователей\\" + sName + "\\1Cv7.LCK";
try{g_FSO.DeleteFile(sLCK);}catch(e){}
if(!g_FSO.FileExists(sLCK))
{
g_WSHShell.Run("\"C:\\Program Files\\1Cv77\\BIN\\1cv7s.exe\" enterprise /DПуть:\\К\\Каталогу\\Базы /N" + sName + " /P"+sPassword);
}
}
PS Задолбался скрипт раскрашивать... 8)
Примечание для DBF-ных баз
Просто так запросы к ДБФ файлам через Microsoft dBASE Driver (равно как и через Microsoft.Jet.OLEDB) не работают, мелкомягкие заложили подарочный набор багов в этот драйвер. Вот тут они все описаны: http://support.microsoft.com/kb/307455/EN-US/
А лечение простое - нужно в ветку HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Xbase добавить значение "BDE"=dword:00000002.
Без этого ключа попытка данные из ДБФ-ки заканчивается исключением "Непредвиденная ошибка драйвера внешней базы данных (15877)" |