Найти - Пользователи
Полная версия: Портирование кода с Delphi, использование DLL.
Начало » Python для новичков » Портирование кода с Delphi, использование DLL.
1
thunderamur
Имеется старый проект на 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

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

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

Зы. и если ты лезешь в глубину… дельфофские библиотеки имеют другое “соглашение вызовов” по сравнению с сишными
thunderamur
Спасибо за ответ. Да, мне лучше делать запросы из питона. Действительно, можно попробовать посмотреть как идет опрос.
Iskatel
попробуй wireshark.

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

Дальше не особо сложно, хоть и кажется. Узнаешь в логах хоть один пакет (по айпишнику) , и правой кнопкой жмешь Folow -> TCP stream. там будет все общение твоей длл с устройством
Iskatel
И кстати, я не очень понимаю накуяэтотебенадо, но осмелюсь предположить, что горазто проще написать некоторую прослойку на дельфях, которая будет юзать ту библиотеку, а данные отдавать хочешь по сети, хочешь через файл, хочешь через тунель, иль по простятски через stdin / stdout.
thunderamur
Дамп сделал, кадров немного, видна последовательность сообщений. Теперь вопрос как мне отправлять и получать сообщения в сети, а также в каком виде отправлять Data из Wireshark. Т.е. в Wireshark я могу взять данные из кадра, например “31:39:35:39:31:30:31:37:0a” как мне их отправить?

Iskatel
Да, можно поднять этот древний проект на Delphi, но лучшим решением будет работа только в Python.
thunderamur
Такс, данные можно отправлять в таком виде скорее всего
 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)

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

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 бита - мантисса. Может есть готовые решения?
thunderamur
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, главное, что я вижу какие биты участвуют в расчете.
thunderamur
 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
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB