Уведомления

Группа в Telegram: @pythonsu

#1 Ноя. 13, 2016 09:41:28

thunderamur
От:
Зарегистрирован: 2011-01-05
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

Имеется старый проект на Delphi, который, используя функции из DLL, читает по сети показания контроллера.

Подключение DLL в Delphi (RDTCP.PAS):

unit RDTCP;
interface

type
PDat=^TDat;
TDat=packed record
PVal:single; // Значение, для дискретных 1.0 - это true, 0.0 - это false
IsOK:byte;
end;

function TCP_Init(ip:PChar; port:word; tconnect:cardinal; tread:cardinal):pointer;stdcall;
procedure TCP_Close(H:pointer);stdcall;
function TCP_Connect(H:pointer):integer;stdcall; // 0-OK -1-error
procedure TCP_Disconnect(H:pointer);stdcall; // 0-OK -1-error

function TCP_GetAKolPar(H:pointer):integer;stdcall;
function TCP_RdAllAn(H:pointer):integer;stdcall;
function TCP_GetAnPar(H:pointer;s:PChar):TDat;stdcall;
function TCP_RdAnPar(H:pointer; s:PChar; var D:TDat):integer;stdcall; // 0-OK -1-error

function TCP_GetDKolPar(H:pointer):integer;stdcall;
function TCP_RdAllDi(H:pointer):integer;stdcall;
function TCP_GetDiPar(H:pointer;s:PChar):TDat;stdcall;
function TCP_RdDiPar(H:pointer; s:PChar; var D:TDat):integer;stdcall; // 0-OK -1-error


implementation

function TCP_Init; external 'RDTCP.DLL';
procedure TCP_Close; external 'RDTCP.DLL';
function TCP_Connect; external 'RDTCP.DLL';
procedure TCP_Disconnect; external 'RDTCP.DLL';

function TCP_GetAKolPar; external 'RDTCP.DLL';
function TCP_RdAllAn; external 'RDTCP.DLL';
function TCP_GetAnPar; external 'RDTCP.DLL';
function TCP_RdAnPar; external 'RDTCP.DLL';

function TCP_GetDKolPar; external 'RDTCP.DLL';
function TCP_RdAllDi; external 'RDTCP.DLL';
function TCP_GetDiPar; external 'RDTCP.DLL';
function TCP_RdDiPar; external 'RDTCP.DLL';

end.

Использование функций DLL в Delphi (MAIN.PAS):
procedure TForm1.FormShow(Sender: TObject);
begin
// TCP_Init вызывается при запуске проги
Hnd_Bl:=nil;
// В этом примере для хоста 172.28.72.2 используется порт 9123.
// А вообще по умолчанию везде порт 9000
Hnd_Bl:=TCP_Init('192.168.7.5',9000,5000,5000); // IP, port, timeout_connect, timeout_read
if Hnd_Bl=nil then begin
MessageDlg('Ошибка TCP_Init',mtError,[mbOK],0);
Close;
end;
ReadAndShow;
CNT:=0;
end;
//---

procedure TForm1.ReadAndShow;
const cOK=$0080FFFF;
cER=clGray;
var D:TDat;
begin
// TCP_RdAllAn читает значения всех параметров во внутренний буфер
// Если TCP_RdAllAn возвр. 0 - ОК, если <0 - error
if TCP_RdAllAn(Hnd_Bl)=0 then begin
StBar1.SimpleText:=FormatDateTime('dd-mm-yyyy hh:nn:ss',Now)+' - чтение данных - ОК';
end else StBar1.SimpleText:=FormatDateTime('dd-mm-yyyy hh:nn:ss',Now)+' - чтение данных - ERROR !!!';

// А дальше вытаскиваем значения параметров по их идентификаторам
D:=TCP_GetAnPar(Hnd_Bl,'BLT_TG1_PSUM');
if D.IsOK=1 then stP1.Color:=cOK else stP1.Color:=cER;
stP1.Caption:=FloatToStrF(D.PVal,ffFixed,5,1)+' ';

Попытка работать с DLL в Python:
 import ctypes
RDTCP = ctypes.WinDLL("RDTCP.dll")
IP = ctypes.c_char_p('192.168.7.5'.encode('cp1251'))
port = ctypes.c_ushort(9000)
timeout_connect = ctypes.c_ulong(5000)
timeout_read = ctypes.c_ulong(5000)
Hnd_Bl = RDTCP.TCP_Init(IP, port, timeout_connect, timeout_read)
print(RDTCP.TCP_RdAllAn(Hnd_Bl))
print(RDTCP.TCP_GetAKolPar(Hnd_Bl))
BLT_TG1_PSUM = ctypes.c_char_p('BLT_TG1_PSUM'.encode('cp1251'))
print(RDTCP.TCP_GetAnPar(Hnd_Bl, BLT_TG1_PSUM))

Подключение успешно, запрос количества параметров успешно, запрос конкретного параметра - access violation error.



Возможно это следствие того, что возвращается структура TDat.

Нашел функции для переноса структур
 def send(self):
    return buffer(self)[:]
	
def receiveSome(self, bytes):
    fit = min(len(bytes), ctypes.sizeof(self))
    ctypes.memmove(ctypes.addressof(self), bytes, fit)

Попробовал описать структура
 class TDat(object):
	def __init__(self, PVal=ctypes.c_float(0.0), IsOK=ctypes.c_byte(0)):
		self.PVal = PVal
		self.IsOK = IsOK

Но как это использовать?



Отредактировано thunderamur (Ноя. 13, 2016 09:42:07)

Прикреплённый файлы:
attachment rdtcp.py (991 байт)

Офлайн

#2 Ноя. 13, 2016 14:19:02

Iskatel
Зарегистрирован: 2015-07-29
Сообщения: 291
Репутация: +  3  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

Насколько я понял, тебе проще проснифить RAW комманды из длл, и отравлять запросы по TCP вручную из питона.

Ну и про ctypes, там вроде как нужен костыль при перегоне буфера из него в питон… я не помню, честно, но погугли. Я встречался с ним в теме про восстановление паролей из файерфокса, гдето на http://stackoverflow.com

Зы. и если ты лезешь в глубину… дельфофские библиотеки имеют другое “соглашение вызовов” по сравнению с сишными

Отредактировано Iskatel (Ноя. 13, 2016 14:36:08)

Офлайн

#3 Ноя. 13, 2016 14:55:28

thunderamur
От:
Зарегистрирован: 2011-01-05
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

Спасибо за ответ. Да, мне лучше делать запросы из питона. Действительно, можно попробовать посмотреть как идет опрос.



Офлайн

#4 Ноя. 13, 2016 16:42:19

Iskatel
Зарегистрирован: 2015-07-29
Сообщения: 291
Репутация: +  3  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

попробуй wireshark.

запускаешь всю безобразию на дельфе, и держишь включенным wireshark.

Дальше не особо сложно, хоть и кажется. Узнаешь в логах хоть один пакет (по айпишнику) , и правой кнопкой жмешь Folow -> TCP stream. там будет все общение твоей длл с устройством

Отредактировано Iskatel (Ноя. 13, 2016 16:42:36)

Офлайн

#5 Ноя. 13, 2016 17:02:58

Iskatel
Зарегистрирован: 2015-07-29
Сообщения: 291
Репутация: +  3  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

И кстати, я не очень понимаю накуяэтотебенадо, но осмелюсь предположить, что горазто проще написать некоторую прослойку на дельфях, которая будет юзать ту библиотеку, а данные отдавать хочешь по сети, хочешь через файл, хочешь через тунель, иль по простятски через stdin / stdout.

Отредактировано Iskatel (Ноя. 13, 2016 17:05:06)

Офлайн

#6 Ноя. 19, 2016 08:28:35

thunderamur
От:
Зарегистрирован: 2011-01-05
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

Дамп сделал, кадров немного, видна последовательность сообщений. Теперь вопрос как мне отправлять и получать сообщения в сети, а также в каком виде отправлять Data из Wireshark. Т.е. в Wireshark я могу взять данные из кадра, например “31:39:35:39:31:30:31:37:0a” как мне их отправить?

Iskatel
Да, можно поднять этот древний проект на Delphi, но лучшим решением будет работа только в Python.



Офлайн

#7 Ноя. 19, 2016 08:51:30

thunderamur
От:
Зарегистрирован: 2011-01-05
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

Такс, данные можно отправлять в таком виде скорее всего

 data = '31393539313031370a'
b = bytes.fromhex(data)

Осталось найти как отправить и получить данные по TCP.

Вроде пошла жара
 import socket
TCP_IP = '192.168.7.5'
TCP_PORT = 9000
BUFFER_SIZE = 1024
MESSAGE = bytes.fromhex('31393539313031370a')
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((TCP_IP, TCP_PORT))
s.send(MESSAGE)
data = s.recv(BUFFER_SIZE)
s.close()
print("received data:", data)

Отвечает.



Офлайн

#8 Ноя. 19, 2016 15:09:43

thunderamur
От:
Зарегистрирован: 2011-01-05
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

В общем опять затык на моменте чтения конкретных значений.

function TCP_GetAnPar(H:pointer;s:PChar):TDat;stdcall;

Возвращается TDat

  TDat=packed record
PVal:single; // Значение, для дискретных 1.0 - это true, 0.0 - это false
IsOK:byte;
end;

Возвращается 64 байта, что явно больш чем single (4 байта) + byte (1 байт). Видно также, что возращается название параметра, возможно под него и под какие-то другие данные выделены, не объявленные в TDat 59 байт. Судя по полученным значениям в HEX в начале идет single и сразу за ним byte. Если так, то думаю получится разобраться. Щас нужно придумать как расшифровать single. В справке по Delphi заявляено, что 1 бит - знак, 8 бит - экспонента, 23 бита - мантисса. Может есть готовые решения?



Офлайн

#9 Ноя. 19, 2016 15:30:04

thunderamur
От:
Зарегистрирован: 2011-01-05
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

0000   07 f6 2a 43 01 00 00 00 00 42 4c 54 5f 47 54 50  ..*C.....BLT_GTP
0010 31 5f 50 53 55 4d 00 00 00 00 00 00 00 00 00 00 1_PSUM..........
0020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

Решено!!! Помогла GHEX. Я вижу значение, теперь осталось написать код для вычисления значения на Python, главное, что я вижу какие биты участвуют в расчете.



Офлайн

#10 Ноя. 19, 2016 16:17:57

thunderamur
От:
Зарегистрирован: 2011-01-05
Сообщения: 7
Репутация: +  0  -
Профиль   Отправить e-mail  

Портирование кода с Delphi, использование DLL.

 import binascii
import struct
h = '07f62a430100000000424c545f475450315f5053554d000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
b = binascii.unhexlify(h[:8])
print(struct.unpack('<{0}f'.format(1), b)[0])
b = binascii.unhexlify(h[8]+h[9])
print(struct.unpack('<{0}B'.format(1), b)[0])

170.961044312
1



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version