15 октября, 2024

Подключение MG-T707TS к 1С 7.7

Отличительной особенностью кассового аппарата MG-T707TS является его способность работать без использования специального OLE драйвера, как это обычно делается в 1С. Вместо драйвера можно использовать протокол Http — внутри кассового аппарата работает Web-сервер, к которому можно обращаться через локальную сеть с помощью Get и Post запросов. В 1С версий 8.х можно все операции провести встроенными средствами, а вот в «семерке» встроенных средств не хватает, но это легко обходится компонентами, доступными в Windows-среде.

Наверное, самая сложная часть процесса привязки аппарата к конфигурации это авторизация. Сервер поддерживает дайджест-авторизацию, отправка запроса происходит в два этапа: сначала делается отправка запроса к нужному ресурсу без заголовков, в ответ приходит код ошибки 401, в заголовке «WWW-Authenticate» ответа приходит дайджест, это строка примерно такого содержания:

Digest username="service", realm="HTROM", nonce="527b004c29df30afd42c9dbf43dcb6d9", uri="/cgi/state", algorithm=MD5, response="3f4f49f5adefafdf19ce9148103486af", opaque="60DB81DD", qop=auth, nc=00000001, cnonce="669bcf2a9b1c9deb" User-Agent: test 

Из полученного дайджеста нужно взять несколько параметров: realm, nonce, algorithm, opaque, qop, скомпоновать новый дайджест с логином и паролем и уже используя его повторить запрос к серверу.

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

Function getAuthorizationHeader(optionsList)   
	
	user = "service";
	pass = "751426";
	nc = "00000001";
	cnonce = "669bcf2a9b1c9deb";
	
	uri = optionsList.Get("uri"); 
	realm = optionsList.Get("realm"); 
	nonce = optionsList.Get("nonce");
	algorithm = optionsList.Get("algorithm"); 
	opaque = optionsList.Get("opaque"); 
	qop = optionsList.Get("qop");
	method = optionsList.Get("httpMethod");
	
	A1 = ""+user+":"+realm+":"+pass;
	md5A1 = Lower(StrReplace(MD5(A1), " ", ""));
	A2 = ""+method+":"+uri;
	md5A2 = Lower(StrReplace(MD5(A2), " ", ""));
	A3 = ""+md5A1+":"+nonce+":"+nc+":"+cnonce+":"+qop+":"+md5A2;
	response = Lower(StrReplace(MD5(A3), " ", "")); 
	
	authorization = "Digest username="""+user+""", "+
		"realm="""+realm+""", "+ 
		"nonce="""+nonce+""", "+
		"uri="""+uri+""", "+
		"algorithm="""+algorithm+""", "+
		"response="""+response+""", "+ 
		"opaque="""+opaque+""", "+
		"qop="""+qop+""", "+
		"nc="""+nc+""", "+
		"cnonce="""+cnonce+"""";  
		
	Return authorization;
	
EndFunction

Для работы по Http протоколу можно использовать доступный во всех версиях Windows — WinHttp, вот полный пример функции, делающей Get запрос и возвращающей текст ответа сервера:

Переменная HOST содержит сетевой адрес кассового аппарата

Function GET(resourse)

	uri = "http://"+HOST+resourse;     
	
	winHttp = CreateObject("WinHttp.WinHttpRequest.5.1"); 
	winHttp.Open("GET", uri, 0);
	winHttp.SetRequestHeader("Host", HOST); 
	
	Try   
		
		winHttp.Send();
		                                
		httpStatus = winHttp.Status();
		If (httpStatus = 401) OR (httpStatus = 200) Then
			
			digest = winHttp.GetResponseHeader("WWW-Authenticate");  
			
			optionsList = parseString(digest);  
			optionsList.Set("uri", uri); 
			optionsList.Set("httpMethod", "GET");
			
			authorization = getAuthorizationHeader(optionsList);
				                     
			winHttp.Open("GET", uri, 0);
			winHttp.SetRequestHeader("Authorization", authorization);
			winHttp.SetRequestHeader("User-Agent", "programmer");
				
			winHttp.Send(); 
			
			Return winHttp.ResponseText();
			
		Else
			
			Message("GET resourse: "+resourse+" ----> "+httpStatus+" "+winHttp.StatusText(), "!");
			
		EndIf;
	Except
		Message("WinHttp GET error: "+GetErrorDescription(), "!"); 
		Message("    ---> uri: "+uri, "!");
	EndTry;  
	
	Return "";

EndFunction   

Ответы от сервера приходят в виде JSON объектов, по этому, для удобства разбора ответа, кодов ошибок, понадобится парсер в список значений, однако в простейшем варианте это необязательно. Сообщение об ошибке может быть примерно такого вида:

{"err":{"e":"xC2","line":1}}

Здесь указан код ошибки, список кодов есть в документации, и указана строка запроса, к которой относится сообщение. Видно, что содержание понятно и без дополнительной обработки.

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

{"F":[{"N":{"cm":"РН-011922"}},{"S":{"qty":1.000,"price":30.00,"name":"Кофе ЯКОБС МОНАРХ 60гр. раст.","code":1,"tax":1}},{"P":{"no":1}}]}

Аналогичным образом происходит формирование отчетов, служебные операции, тест состояния.