Генератор номеров для PSI WMS

31.01.17

Разработка - Универсальные функции

Описан вариант решения с генерацией "почти последовательных" уникальных номеров для разных типов документов.

Текущее состояние.

Имеестся интерфейс обмена 1С с PSI WMS. Для обмена документами необходимо каждому документу генерировать уникальный номер, связка хранится в регистре сведений «Номера накладных» с измерением «Документ» тип: составной, состоящий из документов обмена и «НомерWMS» тип: число, содержит уникальный номер. Генератор номеров реализован в непереодическом регистре сведений «Счетчик номеров», с измерением «ИмяСчетчика» тип строка, и «Номер», тип целое число. Перед записью документа в регистр сведений «Номера накладных» пишется ссылка документа и уникальный порядоковый номер, получаемы из «Счетчик номеров», счетчик при этом плюсуется. Проблема возникает при получении нового номера. Перед чтением значения из «Счетчик номеров» регистр исключительно блокируется и находится в таком состоянии до конца транзакции, а именно до окончания записи документа. На месте блокировки возникает таймаут.

Решение.

Для реализации параллельного получения номера, был выбран следующий вариант: сделать 100 счетчиков и шаг счетчика не 1, а 100. Чтобы получить номер счетчика используестя генератор случайных чисел. Последовательный перебор исключен, так как небходимо где то хранить значение, которое при каждой генерации будет блокироваться, и возникнет опять узкое место. Данный метод расширяем если не хватает 100 то можно сделать N счетчиков с шагом N. При этом блокировать нужно не весь регистр, а только запись.

При реализации данного метода возникла  проблема с случайностью генератора на нагрузочном тесте из 10 фоновых заданий оставались только 3, остальные «падали» на таймауте. При разборе проблемы выяснилось что метод

ГСЧ = Новый ГенераторСлучайныхЧисел();
ГСЧ.СлучайноеЧисло(1, 100); 

не дает случайности в необходимом размере. В диапазоне целых чисел от 1 до 100 возникали до 8 подряд одинаковых номеров. Такая же проблема возникала при системном генераторе случайных чисел.

Rnd = COMОбъект("System.Random");

Сообщить(Rnd.Next());

Но вариант с использованием уникального идентификатора дал более приемлемые результаты, тоесть при тестах на параллельный запуск нескольких фоновых заданий (был проведен тест на 1000 фоновых заданий с генерацией в цикле 100 номеров) распределение номеров получалось равномерным.

Функция ПолучитьСлучайноеЧисло(Мин,Макс)
   Для Сч = 1 По 100 Цикл

     Уник = Новый УникальныйИдентификатор;

   КонецЦикла;
   Уник = СтрЗаменить(Уник,"-","");
   Уник = СтрЗаменить(Уник,"a","");
   Уник = СтрЗаменить(Уник,"b","");
   Уник = СтрЗаменить(Уник,"c","");
   Уник = СтрЗаменить(Уник,"d","");
   Уник = СтрЗаменить(Уник,"e","");
   Уник = СтрЗаменить(Уник,"f","");
   Уник = СтрЗаменить(Уник,Символы.НПП,"");
   Если СтрДлина(Уник) < СтрДлина(Строка(Макс)) Или СтрДлина(Уник) < 5 Тогда
     Уник = ПолучитьСлучайноеЧисло(Мин, Макс);
   КонецЕсли;
   Знаменатель = 10;
   Для н = 2 По (СтрДлина(СтрЗаменить(Уник,Символы.НПП,""))) Цикл
   Знаменатель = Знаменатель * 10;
   КонецЦикла;
   Случ = Число(Уник) / Знаменатель;
   ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс);
   Возврат ЧислоИзИнтервала;
КонецФункции

В функции есть рекурсивный вызов, это при случае если уникальный идентификатор будет состоять из букв.

Функция получения номера:

Функция ПолучитьСледующийНомерСчетчика(ИмяСчетчика)
   НачатьТранзакцию();
   Попытка
     Если ИмяСчетчика="WMS" Тогда
        НомерНакладнойМакс = 7000000;
     Иначе    
        НомерНакладнойМакс = 0;
     КонецЕсли;
     ШагСчетчика = 1;
     Если ИмяСчетчика = "WMS" Тогда
        НомерСчетчика = ПолучитьСлучайноеЧисло(1, 100);
        ИмяСчетчика = ИмяСчетчика + НомерСчетчика;
        ШагСчетчика = 100;
        НомерНакладнойМакс = НомерНакладнойМакс + НомерСчетчика;
      КонецЕсли;
        Блокировка = Новый БлокировкаДанных;
        ЭлементБлокировки = Блокировка.Добавить();
        ЭлементБлокировки.Область = "РегистрСведений.СчетчикиНомеровНакладных";
        ЭлементБлокировки.УстановитьЗначение("ИмяСчетчика", ИмяСчетчика);
        ЭлементБлокировки.Режим = РежимБлокировкиДанных.Исключительный;
        Блокировка.Заблокировать();
        Запрос = Новый Запрос;
        Запрос.Текст = "ВЫБРАТЬ
       |  ЕСТЬNULL(МАКСИМУМ(СчетчикиНомеровНакладных.НомерНакладной), 0) КАК НомерНакладнойМакс
       |ИЗ
       |  РегистрСведений.СчетчикиНомеровНакладных КАК СчетчикиНомеровНакладных
       |ГДЕ
       |  СчетчикиНомеровНакладных.ИмяСчетчика = &ИмяСчетчика";
       Запрос.УстановитьПараметр("ИмяСчетчика", ИмяСчетчика);
       РезультатЗапроса = Запрос.Выполнить();
       Если НЕ РезультатЗапроса.Пустой() Тогда
          Выборка = РезультатЗапроса.Выбрать();
          Если Выборка.Следующий() Тогда
             Если Выборка.НомерНакладнойМакс >= НомерНакладнойМакс Тогда
                НомерНакладнойМакс = Выборка.НомерНакладнойМакс + ШагСчетчика;
             КонецЕсли;
          КонецЕсли;
       КонецЕсли;
       МенеджерЗаписи = РегистрыСведений.СчетчикиНомеровНакладных.СоздатьМенеджерЗаписи();
       МенеджерЗаписи.ИмяСчетчика = ИмяСчетчика;
       МенеджерЗаписи.НомерНакладной = НомерНакладнойМакс;
       МенеджерЗаписи.Записать();           
       ЗафиксироватьТранзакцию();
       Возврат НомерНакладнойМакс;
       Исключение
       ЗаписьЖурналаРегистрации("СчетчикиНомеровНакладных", УровеньЖурналаРегистрации.Ошибка,,,"Ошибка номера " + ИмяСчетчика + " " + ОписаниеОшибки());
       ТекстСообщения = НСтр("ru='Невозможно создать номер документа для выгрузки в WMS!'");
       ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения,,,,);
       ОтменитьТранзакцию();
       ВызватьИсключение;
       КонецПопытки;
КонецФункции

PSI WMS обмен уникальный код

См. также

Вставляем картинку из буфера обмена (платформа 1С 8.3.24)

Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    2664    0    John_d    8    

53

GUID в 1С 8.3 - как с ними быть

Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    4595    atdonya    22    

45

Переоткрытие внешних обработок

Универсальные функции Платформа 1С v8.3 Бесплатно (free)

На заключительных этапах, когда идет отладка или доработка интерфейса, необходимо много раз переоткрыть внешний объект. Вот один из способов автоматизации этого.

30.11.2023    3956    ke.92@mail.ru    16    

61

Валидация JSON через XDTO (включая массивы)

WEB-интеграция Универсальные функции Механизмы платформы 1С Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    8802    YA_418728146    6    

141

Печать непроведенных документов для УТ, КА, ERP. Настройка печати по пользователям, документам и печатным формам

Пакетная печать Печатные формы Адаптация типовых решений Универсальные функции Платформа 1С v8.3 1С:ERP Управление предприятием 2 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х Россия Абонемент ($m)

Расширение для программ 1С:Управление торговлей, 1С:Комплексная автоматизация, 1С:ERP, которое позволяет распечатывать печатные формы для непроведенных документов. Можно настроить, каким пользователям, какие конкретные формы документов разрешено печатать без проведения документа.

2 стартмани

22.08.2023    2071    21    progmaster    7    

3

Расширение: Быстрые отборы через буфер [Alt+C] Копировать список, [Alt+V] Вставить список, [Ctrl+C] Копировать из файлов

Инструментарий разработчика Универсальные функции Платформа 1С v8.3 Конфигурации 1cv8 1С:Розница 2 1С:ERP Управление предприятием 2 1С:Бухгалтерия 3.0 1С:Управление торговлей 11 1С:Комплексная автоматизация 2.х 1С:Зарплата и Управление Персоналом 3.x Абонемент ($m)

Копирует в буфер значения из списков, из ячеек отчетов, таблиц, настроек списков, других отборов и вставляет в выбранную настройку отбора. Работает с Объект не найден. Работает как в одной так и между разными базами 1С. Использует комбинации [Alt+C] Копировать список, [Alt+V] Вставить список. Также для копирования данных используется стандартная [Ctrl+C] (например из открытого xls, mxl, doc и т.п. файла скопировать список наименований)

1 стартмани

13.10.2022    16140    133    sapervodichka    112    

129

Система контроля ведения учета [БСП]

Универсальные функции Механизмы типовых конфигураций БСП (Библиотека стандартных подсистем) Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

В данном материале рассмотрим типовой алгоритм подсистемы контроля учета БСП в конфигурациях на примерах.

18.07.2022    7242    quazare    8    

109
Комментарии
В избранное Подписаться на ответы Сортировка: Древо развёрнутое
Свернуть все
1. BorovikSV 1036 01.02.17 08:00 Сейчас в теме
(0)
В диапазоне целых чисел от 1 до 100 возникали до 8 подряд одинаковых номеров


А не пробовали сделать диапазон от 1 до 1 000 000, а затем получить остаток от деления от 100? по природе генераторов случайных чисел это должно вам помочь

СлучайноеЧисло = ГСЧ.СлучайноеЧисло(1, 1000000) % 100; 


7. IgorNastenko 17 01.02.17 10:23 Сейчас в теме
(1)Не помогала, даже более того, что любая функциональная зависимость, основанная на арифметических операциях, при одинаковых входных данных дает одинаковые выходные, поэтому понадобилось что то похитрее.
2. m-rv 962 01.02.17 08:22 Сейчас в теме
Молодец, давно тебе говорил: опубликуй эту идею
8. IgorNastenko 17 01.02.17 10:24 Сейчас в теме
(2)Да случайно у себя на неё наткнулся, подумал, пора)
3. BorovikSV 1036 01.02.17 08:35 Сейчас в теме
(0)
Практические испытания (10 000 полученных значений) показывают то, что штатный генератор распределяет значения куда лучше, чем ваша реализация основанная на UUID.
см. во вложении диаграмму.
Верхняя диаграмма использует:
ГСЧ.СлучайноеЧисло(0,99);

Нижняя диаграмма использует:
ПолучитьСлучайноеЧисло(0,99);

Обратите внимание на нижней диаграмме граничные значения (0 и 99). Тут ваш алгоритм вообще плохо себя ведет. Я уже не говорю о том что использование UUID получается на порядки медленней.
Прикрепленные файлы:
6. IgorNastenko 17 01.02.17 10:12 Сейчас в теме
(3) У меня дополнительные требования были к генератору, кроме нормального распределения, должно быть еще, что бы в один момент времени не было повторяющихся номеров, это выявится если ГСЧ.СлучайноеЧисло(0,99) запустить в несколько потоков.
9. BorovikSV 1036 01.02.17 11:31 Сейчас в теме
(6)
это выявится если ГСЧ.СлучайноеЧисло(0,99) запустить в несколько потоков.

так вы попробуйте при создании генератора инициализировать его случайным образом (идентификатор сеанса например)
ГСЧ = новый ГенераторСлучайныхЧисел(НомерСоединенияИнформационнойБазы());
4. TODD22 18 01.02.17 09:16 Сейчас в теме
В диапазоне целых чисел от 1 до 100 возникали до 8 подряд одинаковых номеров.

Может потому что в одну секунду их генерировал? На сколько помню генератор завязан на системное время.
5. v3rter 01.02.17 09:50 Сейчас в теме
Посмотрите тут https://habrahabr.ru/post/132217/ начиная с текста "Получение псевдослучайных чисел на основе полиномиального счетчика", возможно этот метод тоже подойдет.
10. slawa 26 01.02.17 12:01 Сейчас в теме
Зачем после возврата из рекурсивного вызова (Уник = ПолучитьСлучайноеЧисло(Мин, Макс);) опять приводится к интревалу?
Сразу его вернуть нельзя "Возврат ПолучитьСлучайноеЧисло(Мин, Макс);" ?
11. IgorNastenko 17 01.02.17 12:56 Сейчас в теме
(10)Чтобы избежать случая, где GUID будет состоять из недостаточного количества цифр (составляющие GUID будут больше 9), в данном случае это не произойдет, но если этот генератор нужен будет для использования с длинной числа > 16 то лучше сделать эту проверку.
17. slawa 26 02.02.17 09:15 Сейчас в теме
(11)
Зачем нужен рекурсивный вызов - понятно. (для перегенерации случ.числа при получении гуид с малым количеством цифр)

Не понял зачем полученный из рекурсии результат опять приводится к интервалу. Ведь он и так должен вернуться в нужном диапазоне.
12. bulpi 215 01.02.17 14:33 Сейчас в теме
ЭЭЭЭ.... А на фига все это ? Я тоже общаюсь с wms, и мне хватает префиксов в номерах документов, чтобы получить уникальный номер Ж)
14. IgorNastenko 17 01.02.17 14:57 Сейчас в теме
(12) Согласен что нумератор надо использовать из документа. Такое вот наследство было, и хорошо что вообще не в константе не хранили, а переделывать нельзя было, во первых, ответственная часть, во-вторых, этот элемент использовался в интеграции с другими системами. Если что то не так пойдет то много процессов бы встало, а так если отделаться малой кровью и избавиться от проблемы блокировки, использовалось такое решение.
13. v3rter 01.02.17 14:52 Сейчас в теме
А глобальная статическая переменная не выход? Пожалуй, не выход.
15. v3rter 01.02.17 15:29 Сейчас в теме
Я бы попробовал хранить счетчик(и) во внешней базе MS SQL, используя хранимую функцию, возвращающую и автоинкрементирующую нужный счетчик в момент обращения.
16. IgorNastenko 17 01.02.17 16:46 Сейчас в теме
(15)Проверял разные готовые генераторы, в том числе и виндовые, они не дают нужную последовательность случайных чисел, и скорее всего скуль тоже использует те же генераторы, если оно так, то такая автоинкрементация будет давать в один момент времени один и тот же счетчик, да и с внешней базой тоже вопрос, получается такая мелкая, но очень важная база, про которую скорее всего все забудут, и в какой нибудь момент когда что нибудь накроется или переедет, могут быть проблемы.
18. v3rter 02.02.17 11:14 Сейчас в теме
Интересно, если использовать все цифры гуида распределение ухудшается или нет?

   УникЧисло16=0;
   Для Позиция = 1 По СтрДлина(Уник) Цикл
        УникЧисло16=УникЧисло*16+Найти("123456789abcdef",Сред(Уник, Позиция ,1));
   КонецЦикла;

22. IgorNastenko 17 05.02.17 21:13 Сейчас в теме
(18)Давно конечно эксперементировал, но все эти генераторы давали достаточно хорошее "нормальное" распределение, если не брать создание в 1 момент времени, да и с 16-ричными числами в 1с работать как то не удобно.
19. slawa 26 02.02.17 17:53 Сейчас в теме
Взято тут: http://www.forum.mista.ru/topic.php?id=196865
Не проверял
В конце:

//преобразуем его в случайное число из заданного интервала, округляем до целого 

//ЧислоИзИнтервала = Мин(Макс(Окр(Мин + (Макс-Мин)*Случ),Мин),Макс);
 
// Делаем правильно вот так, т.к. по предыдущей формуле крайние значения выпадают в 2 раза реже.
 
    ЧислоИзИнтервала = Цел(0.5 + (Макс - Мин + 1) * Случ) + Мин - 1;
    Если ЧислоИзИнтервала = Мин - 1 Тогда
         ЧислоИзИнтервала = Макс;
    КонецЕсли;

Показать
20. v3rter 03.02.17 12:01 Сейчас в теме
Чисто академический интерес - Новый УникальныйИдентификатор несёт в себе дату-время как в https://helpf.pro/faq/view/1099.html ?
Как по GUID определить время и дату создания ссылки?
Код 1C v 8.
 Функция ДатаСозданияСсылки(Ссылка)
    ГУИД = Ссылка.УникальныйИдентификатор();
    Строка16 = Сред(ГУИД, 16, 3) + Сред(ГУИД, 10, 4) + Сред(ГУИД, 1, 8);
    Разрядность = СтрДлина(Строка16);
    ЧислоСек = 0;
    Для Позиция = 1 По Разрядность Цикл
        ЧислоСек = ЧислоСек + Найти("123456789abcdef",Сред(Строка16,Позиция,1))*Pow(16,Разрядность - Позиция);
    КонецЦикла;
    ЧислоСек = ЧислоСек / 10000000;
    Возврат Дата(1582, 10, 15, 04, 00, 00) + ЧислоСек;
КонецФункции   
Показать
Отсюда же я взял преобразование из 16-ричной системы, только слегка упростил формулу.
21. v3rter 03.02.17 16:36 Сейчас в теме
(20) Проверил. Нет. Не несет.
23. IgorNastenko 17 05.02.17 21:15 Сейчас в теме
(21)Вроде он должен нести в себе дату время + счетчик. Может только в какой нибудь другой зависимости.
Оставьте свое сообщение