Как была "побеждена" проблема считывания 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 руб. Причем, проблема драйверов и блокировок для фирменного считывателя по прежнему решена не будет.

См. также

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

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

10 стартмани

14.11.2024    511    1    aximo    0    

3

Периферийные устройства Системный администратор Пользователь Платформа 1С v8.3 1С:Бухгалтерия 3.0 Абонемент ($m)

Простая в использовании обработка https://infostart.ru/1c/tools/1001819/ в целом решает поставленную задачу, но имеет явный недостаток - взаимодействует только с принтерами, подключенными к серверу. Доработанная версия позволяет работать как с принтерами на клиенте, так и на сервере

1 стартмани

30.08.2024    386    3    Sergey1CSpb    0    

4

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

Расширение для вывода информации для покупателя из РМК на второй монитор. Также реализован вывод картинок из каталога в левую часть экрана покупателя. Разработано для УТ 11.5, УНФ 3.0.6 и 3.0.9.

1 стартмани

12.08.2024    1060    21    Rastopchinss    15    

3

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

Вывод для клиента на второй монитор (информационное табло) номера заказа в браузер (по умолчанию, например, Яндекс). Внешняя обработка может быть легко переделана под другие конфигурации (нужды) даже начинающим программистом.

1 стартмани

02.08.2024    686    0    AlOkt    0    

5

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

Пример работы с эмулятором дисплея покупателя через библиотеку БПО.

6 стартмани

10.07.2024    704    5    aximo    0    

3

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

Речь пойдет не о сканерах штрихкодов 2D/3D/RF/распознавании лиц и прочего, а о старом добром аппарате, позволяющем считать страницу и отправить ее образ в ПК через COM/USB порт. Рассмотрим, как можно подключить и использовать сканер в типовых 1С-конфигурациях. И что делать, если никак не найти команду настройки сканера.

1 стартмани

28.05.2024    1026    Sergey1CSpb    2    

6

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

Расширение для 1С: Розница 2.3 версий 2.3.15.ХХХ и выше. Удобный способ изменения принтера по умолчанию во встроенной обработке печати ценников и этикеток. Только для операционной системы Windows.

1 стартмани

13.05.2024    1006    5    independ    0    

5

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

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

1 стартмани

15.04.2024    8311    madonov    60    

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