Как была "побеждена" проблема считывания ID карт доступа в 1С для управления СКУД "Parsec"

25.11.21

Интеграция - Периферийные устройства

Описание подхода решения проблемы взаимодействия со штатным оборудованием системы СКУД "Parsec".

Данная статья является освещением одного из возможных подходов по обходу ограничений штатного оборудования СКУД.

Исходная задача – интеграция со СКУД «Parsec» с 1С для возможности создания рабочих мест управления доступами с гибкой настройкой возможных действий пользователей. В ходе решения задачи, возникла проблема выдачи карт и определения пользователя карты. Стандартные считыватели турникетов воспринимают 2 стандарта карт: NFC (Mifare) и EMarin. В то же время настольные считыватели, которые были в наличии, могли считывать, либо карты стандарта NFC, либо карты EMarine. Решение с двумя считывателями у оператора, конечно, было сделано. Под считыватели были реализованы свои внешние компоненты 1C, удовлетворяющие стандартам подключаемого оборудования. Но, возникла неприятная ситуация: если на рабочем месте запускается управляющая программа СКУД «Parsec», то считыватели для 1С блокируются. Более того, считыватель NFC подключался к родной оболочке Parsec довольно «экстравагантным способом» - через специальное ПО, которое передавало ID считанной карты через буфер обмена. Это «решения» порядком раздражало операторов пропускной системы. При этом в этом ПО был зашит один алгоритм, а с течением времени, понадобилось расширить кодировку ID, для расширения количество карт. Во внешней компоненте, обслуживающей оборудование NFC, была сделана доработка, и ID в 1С передавался корректно, но… проблемы блокировок, настройки разрешений и два считывателя (не дешевых) на столе продолжали «портить кровь».

В результате долгих и безуспешных поисков решения два в одном, без блокировок и за относительно не большие $$$, был сделан вывод: надо паять!

В качестве источника идей, конечно же выступила платформа Arduino, а поставщиком «проверенных решений» Aliexpress)

Главным критерии выполняемого решения:

  • Исключить написание каких-либо драйверов подключаемого оборудования для 1С. Воспользоваться уже имеющимися «из коробки» драйверами библиотеки подключаемого оборудования.
  • Один считыватель передает ID для обоих видов карт NFC и EMarine
  • Считыватель должен состоять из доступных компонентов и быть легко повторяемым (что бы по каким-то причинам, вышедшие из строя устройства, можно было их быстро заменить)
  • Можно удаленно или несложно обновлять прошивку, в случае каких-то изменений

В результате проработки задачи был получен следующий прототип решения:

  • На борту Arduino имеется виртуальный COM-порт, по данному порту осуществляется как прошивка, так и передача данных. В составе библиотеки подключаемого оборудования есть драйвер считывателя магнитных карт, который, как раз и является очень примитивным читателем COM-порта. Остается только написать скетч для Arduino выдачи в COM-порт строк нужного формата.

Выбор железок:

Основываясь на собственном опыте, в качестве «сердца» устройства был выбран модуль Arduino Pro Micro. У меня, почему-то, к нему сложилось очень доверительное отношение, т.к. в устройствах «малой домашней автоматизации», он ни разу меня не подводил, в отличие от однополчан рода Atmega328:

 

Для считывания ID EMarine остановился на модуле RDM6300. Довольно стабильная и неприхотливая железка:

 

А вот со считыванием NFC-идентификаторов пришлось повозиться. Изначально выбор был сделан в пользу широко известного модуля RC-522. Но по каким-то причинам, от модуля к модулю возникали проблемы с дальностью считывания. После некоторого промежутка работы, модули начинали зависать или вести себя «неадекватно».

 

 

Проведя ряд тестовых операций различных модулей из поднебесной, остановился на модуле PN-532. Немного дороже, но зато имеет хорошую дальность действия – 2-4 см и стабильность работы.

 

Сборка:

Для сборки конструкции выбрал самый обычный корпус 142х82х38мм:

 

 

Разработал несложную схему и оттрасировал печатную плату, которую в дальнейшем заказал опять же в том-же городе-герое Китай. Убрал ужасы схемотехники под кат

 

 

 

Спустя пару недель, труженики Китая, не покладая рук, изготовили и прислали законченные платы

 

 

Собрал, обработал напильником и получил следующий девайс

 

 

Написал скетч для Arduino и залил его в устройство.

 

 


/////////////////////////////////////////
// sketch.cpp

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_PN532.h>
#include <string.h>
#include "rdm6300_1.h"
#include "RFIDData.h"

#define PIN_RED 4
#define PIN_GREEN 2
#define PIN_BLUE 3
#define PIN_BUZZER 5

#define PN532_SCK  (15)
#define PN532_MOSI (16)
#define PN532_SS   (10)
#define PN532_MISO (14)


// Init array that will store new NUID 
byte nuidPICC[4];

Rdm6300 rdm6300;
Adafruit_PN532 nfc(PN532_SCK, PN532_MISO, PN532_MOSI, PN532_SS);

uint8_t prev_uid[] = { 0, 0, 0, 0, 0, 0, 0 };

char serial[8] = { "" };

RFIDData currentData;

void setup()
{
	
	Serial.begin(115200);
	
	pinMode(PIN_RED, OUTPUT);
	pinMode(PIN_GREEN, OUTPUT);
	pinMode(PIN_BLUE, OUTPUT);
	pinMode(PIN_BUZZER, OUTPUT);
	
	

	nfc.begin();

	uint32_t versiondata = nfc.getFirmwareVersion();
	if (!versiondata) {
		Serial.print("Didn't find PN53x board");
		for (int i = 0; i < 3; i++)
		{
			digitalWrite(PIN_RED, HIGH);
			digitalWrite(PIN_BUZZER, HIGH);
			delay(100);
			digitalWrite(PIN_RED, LOW);
			digitalWrite(PIN_BUZZER, LOW);
			delay(100);
		}
		while (1); // halt
	}

	// configure board to read RFID tags
	nfc.SAMConfig();

	delay(100);
	rdm6300.begin();
	

	digitalWrite(PIN_RED, HIGH);
	digitalWrite(PIN_BUZZER, HIGH);
	delay(100);
	digitalWrite(PIN_RED, LOW);
	digitalWrite(PIN_BUZZER, LOW);
	digitalWrite(PIN_GREEN, HIGH);
	delay(100);
	digitalWrite(PIN_GREEN, LOW);
	digitalWrite(PIN_BUZZER, HIGH);
	digitalWrite(PIN_BLUE, HIGH);
	delay(100);
	digitalWrite(PIN_BLUE, LOW);
	digitalWrite(PIN_BUZZER, LOW);
	digitalWrite(PIN_RED, HIGH);
}

void loop()
{
	//mifare
	readMF();

	//EM Marin
	readEM();

	if (currentData.hasNewSerial()) {


		Serial.print(currentData.getType());
		Serial.print("#");
		Serial.println(currentData.getSerial());


	}
	
	delay(100);
}

void readEM() {

	/* if non-zero tag_id, update() returns true- a new tag is near! */
	if (rdm6300.update()) {

		digitalWrite(PIN_RED, LOW);
		digitalWrite(PIN_BLUE, HIGH);
		digitalWrite(PIN_BUZZER, HIGH);
		delay(200);
		digitalWrite(PIN_BLUE, LOW);
		digitalWrite(PIN_BUZZER, LOW);
		digitalWrite(PIN_RED, HIGH);

		currentData.setCurrentSerial(rdm6300.get_tag_id());
	}

}

void readMF() {


	uint8_t success;
	uint8_t uid[] = { 0, 0, 0, 0, 0, 0, 0 };  // Buffer to store the returned UID
	uint8_t uidLength;                        // Length of the UID (4 or 7 bytes depending on ISO14443A card type)

	// Wait for an ISO14443A type cards (Mifare, etc.).  When one is found
	// 'uid' will be populated with the UID, and uidLength will indicate
	// if the uid is 4 bytes (Mifare Classic) or 7 bytes (Mifare Ultralight)
	success = nfc.readPassiveTargetID(PN532_MIFARE_ISO14443A, uid, &uidLength, 100);



	if (success) {

		int i;

		if (memcmp(prev_uid, uid, 7) == 0) return;

		memcpy(prev_uid, uid, 7);

		//Info
		digitalWrite(PIN_RED, LOW);
		digitalWrite(PIN_GREEN, HIGH);
		digitalWrite(PIN_BUZZER, HIGH);
		delay(200);
		digitalWrite(PIN_GREEN, LOW);
		digitalWrite(PIN_BUZZER, LOW);
		digitalWrite(PIN_RED, HIGH);


		currentData.setCurrentSerial(uid, uidLength);

	}
	else {
		
		memset(prev_uid, 0, 7);
	
	}

}

/**
 * Helper routine to dump a byte array as hex values to Serial.
 */
void printHex(byte* buffer, byte bufferSize) {
	for (byte i = 0; i < bufferSize; i++) {
		Serial.print(buffer[i] < 0x10 ? " 0" : " ");
		Serial.print(buffer[i], HEX);
		if (i < 4) {

			//serial[]

		}
	}
}

/**
 * Helper routine to dump a byte array as dec values to Serial.
 */
void printDec(byte* buffer, byte bufferSize) {
	for (byte i = 0; i < bufferSize; i++) {
		Serial.print(buffer[i] < 0x10 ? " 0" : " ");
		Serial.print(buffer[i], DEC);
	}
}

void dump_byte_array(byte* buffer, byte bufferSize) {
	for (byte i = 0; i < bufferSize; i++) {
		Serial.print(buffer[i] < 0x10 ? " 0" : " ");
		Serial.print(buffer[i], HEX);
	}
}

/////////////////////////////////////////
// RFFIDData.h

#ifndef _RFFIDData_h
#define _RFFIDData_h

#if defined(ARDUINO) && ARDUINO >= 100
	#include "arduino.h"
#else
	#include "WProgram.h"
#endif

#define USE_NEW_ALG
//#define ALG_TEST
//#define DBG

const static uint8_t CRCTBL[256] = {
  0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,
  157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,
  35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98,
  190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,
  70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7,
  219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154,
  101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,
  248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,
  140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,
  17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,
  175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238,
  50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,
  202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,
  87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,
  233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,
  116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};

class RFIDData
{
public:

	void setCurrentSerial(uint32_t serial);
	void setCurrentSerial(byte* buffer, byte bufferSize);
	int getType() {

		return type;
	}

	char* getSerial() {

		_hasNewSerial = false;
		return serial;

	}
	bool hasNewSerial() {
		return _hasNewSerial;
	};

private:

	int type = 0;
	char serial[9] = { '0' };
	bool _hasNewSerial = false;

	void clearSerial() {

		for (int i = 0; i < 9; i++) {
			serial[i] = '0';
		}

		serial[8] = 0;

	}

};


#endif

/////////////////////////////////////////
// RFFIDData.cpp

#include "RFIDData.h"

void RFIDData::setCurrentSerial(uint32_t serial_num) {

	char tmp_buf[16] = { 0, };
	type = 1;

	clearSerial();
	int cnt = 0;
	ultoa(serial_num, tmp_buf, 16);

#ifdef DBG

	Serial.println("TMP_Buffer:");
	for (int i = 0; i < 16; i++)
	{

		if (tmp_buf[i] < 20) {
			Serial.print("_");
			continue;
		}
		Serial.print(tmp_buf[i]);


	}
	Serial.println();

	Serial.print("len: ");
	Serial.println(strlen(tmp_buf));

#endif // DBG


	int startpos = 8 - strlen(tmp_buf);
	while (tmp_buf[cnt]) {
		serial[cnt + startpos] = toUpperCase(tmp_buf[cnt]);
		cnt++;
		if (cnt > 6) break;
	}

	_hasNewSerial = true;

}

void RFIDData::setCurrentSerial(byte* buffer, byte bufferSize) {

	type = 0;

#ifdef USE_NEW_ALG

	//Translate by alg
	uint8_t code[4];
	uint8_t crc = 0x5A;
	uint8_t reverseBuffer[7];

	memset(reverseBuffer, 0, sizeof(uint8_t) * 7);
	for (int i = 0; i < bufferSize; i++)
	{
		if (i >= 7) break;
		//reverseBuffer[i] = buffer[bufferSize - 1 - i];
		reverseBuffer[i] = buffer[i];

	}

#ifdef ALG_TEST

	////IN: 00901B74028905
	////OUT: 6F92896A

	//IN: 00013EB95A8D05
	//OUT: 875B8D1F

	reverseBuffer[6] = 0x00;
	reverseBuffer[5] = 0x01;
	reverseBuffer[4] = 0x3E;
	reverseBuffer[3] = 0xB9;
	reverseBuffer[2] = 0x5A;
	reverseBuffer[1] = 0x8D;
	reverseBuffer[0] = 0x05;

#endif // ALG_TEST

	if (bufferSize == 7) {

		memset(serial, 0, 8);

		for (int i = 0; i < 7; i++) {
			crc = CRCTBL[crc ^ reverseBuffer[i]];
#ifdef ALG_TEST
			Serial.print("crc: ");
			Serial.print(crc);
			Serial.print("; index: ");
			Serial.print(crc ^ buffer[i]);
			Serial.print("; table val: ");
			Serial.print(CRCTBL[crc ^ buffer[i]]);
			Serial.print("; i: ");
			Serial.print(i);
			Serial.print("; buf value: ");
			Serial.println(buffer[i], HEX);
#endif // ALG_TEST

		}
		code[0] = reverseBuffer[0] ^ crc;
		code[1] = reverseBuffer[1] ^ reverseBuffer[6];
		code[2] = reverseBuffer[2] ^ reverseBuffer[5];
		code[3] = reverseBuffer[3] ^ reverseBuffer[4];
		for (int i = 4; i > 0; i--) {
			sprintf(serial, "%s%02X", serial, code[i-1]);
		}

		type = 2;
		_hasNewSerial = true;
		return;
	}

#endif // USE_NEW_ALG



	if (bufferSize > 3) {

		type = 2;
		memset(serial, 0, 8);
		for (int idx = 3; idx >= 0; idx--) {
			sprintf(serial, "%s%02X", serial, buffer[idx]);
		}

		_hasNewSerial = true;
	}

}

Далее запустил терминал для проверки считывания карт:

 

 

Здесь цифра впереди показывает, с какого устройства (какого типа карта) произведено чтение карты (NFC, EMarine)

А дальше дело оставалось за малым, написать код обработчика 1С для внешнего события стандартного считывателя магнитных карт и подключить устройство:

 

 

В качестве прошивальщика выступила оболочка XLoader, которая без проблем «заливает» hex-файл в ардуинку.

 

 

Вот таким незатейливым способом удалось «победить» нестыковки родного оборудования СКУД «Parsec».

Итоговая стоимость 1 считывателя вышла не больше 1 800 руб. В отличие от фирменного считывателя за 11 000 руб. Причем, проблема драйверов и блокировок для фирменного считывателя по прежнему решена не будет.

См. также

Распознавание номеров автомашин с ip - камер, видео, фото

Распознавание документов и образов Периферийные устройства Автомобили, автосервисы Россия Платные (руб)

Программа считывает кадры с ip-камер (http - запрос к камере), видео, фото (источники кадров (нет ограничения на их количество) настраивается в конфигурационном файле), находит и распознает номера автомашин и сохраняет в базу db, с сохранением фото номера и автомашины, а также времени детекции.

20400 руб.

31.05.2023    4000    3    2    

5

Тернистый путь к физической клавиатуре для программиста 1С

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

Долгое время я активно пользовался различными хоткеерами, шаблонами текста, специальными раскладками клавиатуры. Я из тех, кто очень долго верил в Снегопат... Так сложилось, что на моем текущем месте работы довольно строгая политика информационной безопасности, а потому со всем привычным софтом, который делал жизнь проще, мне пришлось расстаться. И даже шаблоны кода при каждом терминальном подключении требуется настраивать в конфигураторе заново. Как известно, если какую-то задачу нельзя решить на стороне софта, то стоит поискать железное решение.

1 стартмани

15.04.2024    5891    madonov    52    

33

[История разработки] Управляем промышленным принтером EBS-1500 из 1С

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

«У нас было два контроллера Huidu, семьдесят две китайские монохромные панели на светоизлучающих диодах, они же LED, четыре мегабайта flash памяти, 1С и целое море поддерживаемых форматов вывода информации - текстов, картинок, анимаций, а так же литр промывочной жидкости, литр разбавителя, ящик черных чернил, и 12 патч-кордов и различных удлинителей. Не то, чтобы всё это было категорически необходимо в маркировке, но если уж начал собирать маркиратор на 1С, то к делу надо подходить серьёзно.» - Страх и ненависть в Маркировке, 2019 г.

01.04.2024    1512    Interrupted    14    

34

Получение изображения с веб-камеры с возможностью его кадрирования по произвольной области

Инструментарий разработчика Периферийные устройства Платформа 1С v8.3 Конфигурации 1cv8 Россия Абонемент ($m)

Внешняя обработка получения изображения с веб-камеры с возможностью его кадрирования по произвольно выделенной области. Использует собственную внешнюю компоненту на основе библиотеки AForge. Содержит примеры интерактивного взаимодействия с полем HTML.

20.03.2024    429    2    AndSem    1    

4

Прямая печать на принтерах TSC из 1С

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

Прямая печать на принтерах TSC из 1С при вызове с сервера позволяет печатать из тонкого клиента, мобильного клиента и веб-клиента. Потребуется установка драйвера и библиотеки для отправки команд на этот драйвер.

1 стартмани

19.02.2024    704    6    realslavyan    2    

5

HotKeys на Python для 1С

Периферийные устройства Конфигурации 1cv8 Бесплатно (free)

Недавно прочитал статью на Инфостарте о KeyPad и вначале захотел себе такой, но через какое-то время я понял, что дополнительная даже маленькая клавиатура занимает место, и нужно все время держать ее под рукой. Это все создает дополнительные неудобства.

20.12.2023    5570    John_d    26    

75

Очистка очереди печати конкретного принтера

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

Очистка очереди печати выбранного принтера, перезапуск службы печати, вывод списка системных принтеров, открытие свойств принтера из 1С.

1 стартмани

07.11.2023    1330    2    vsnazarov    1    

26

Печать этикеток на DYMO LabelManager 280 через DYMO Connect Web Service

Периферийные устройства WEB-интеграция Этикетки, ценники Платформа 1С v8.3 Конфигурации 1cv8 Россия Бесплатно (free)

На сайте производителя принтера DYMO LabelManager 280 вместе с драйвером поставляется пакет DYMO Connect, который предлагает функционал вёрстки этикеток в фирменном формате и отправки их напрямую на принтер. Так же в этом пакете есть веб-сервис для взаимодействия с принтером через HTTP-запросы, о котором и пойдет речь в этой статье.

29.08.2023    1569    GeraltSnow    0    

3
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. EliasShy 48 25.11.21 12:20 Сейчас в теме
Статья интересная, большое спасибо!
Хотелось бы немного подробнее по 1сную часть - "организация рабочих мест управления доступами с гибкой настройкой возможных действий пользователей."
2. eugenevk 205 25.11.21 13:23 Сейчас в теме
(1) Отдельная конфигурация, которая взаимодействует с БД (через SQL запросы) и интерфейсом WSDL Parsec. В самой конфигурации реализованы различные АРМы операторов, с настройкой доступов, для выдачи, блокировки пропусков и прочих операций.
3. tka4enk0 144 26.11.21 08:27 Сейчас в теме
Ардуино рулит. У нас так считыватели на меге для iButton и rfid карт garveks. Но для БПО писал отдельную ВК и соответственно добавлял в БПО новый тип оборудования
4. eugenevk 205 26.11.21 08:49 Сейчас в теме
(3) Отдельная компонента действительно удобна. В одной строчке для МК действительно, много не передашь. Но компонента - это дополнительные затраты времени. А тут как всегда: "надо вчера" (первая версия была собрана на макетке за пол-дня)
5. smit1c 106 29.11.21 08:42 Сейчас в теме
Отлично! Главное теперь не увольняться с этого места работы ))
6. eugenevk 205 29.11.21 10:11 Сейчас в теме
(5) Спасибо. А в чем загвоздка? Исходники есть, схема есть... тут мне кажется как раз на специалиста не завязано. Это как раз лучше, чем написать ВК для БПО и вот, там уже точно разработка сильно привязана к специалисту.
7. smit1c 106 30.11.21 09:04 Сейчас в теме
(6) а много вы знаете 1Сников, которые хорошо разбираются в схемотехнике и смогут повторить ваш пример (даже при наличии исходников, схем и работающих устройств)? Я вот не знаю ни одного )))
8. eugenevk 205 30.11.21 15:26 Сейчас в теме
(7) Знаю, довольно много программистов, и не только 1С, которые увлекаются автоматизацией, разными железками в качестве хобби. Более того, ардуино сейчас изучают школьники с 8 класса (тема модная, материалов много). Думаю, прочитать пару-тройку статей по быстрому старту работы с Ардуино сможет любой разработчик) Тема в статье много проще, чем выучить, например, язык запросов 1С. А схемотехнику вовсе не надо создавать, если устройств нужно не более 1-2, продаются макетные платы для пайки, на которой можно все соединить при помощи проводов. Я создавал схему и заказывал плату, из-за того что мне нужно было приличное количество устройств, а собирать их на макетке очень нудно и долго) Хотя, прототип устройства был таким:
Прикрепленные файлы:
Kolunya; smit1c; +2 Ответить
Оставьте свое сообщение