[求助]SPCOMM 开发多线程 上位机程序 ( 积分: 200 )

  • 主题发起人 主题发起人 citydawn
  • 开始时间 开始时间
C

citydawn

Unregistered / Unconfirmed
GUEST, unregistred user!
《通讯协议》
1.通讯模式:19200,N,8,1
2.命令格式:1字节命令字+1字节机号+.....+0x0d
3.命令含义:
1) 读读头的序列号:
上位机下发:'R'+机号+2字节校验校验 +0x0d
下位机返回:'R'+机号+8字节序列号+2字节校验校验 +0x0d
2) 读读头中的卡号
上位机下发:'o'+机号+o+0x0d
下位机返回:
若已读取卡号:'U'+机号+8字节卡号+2字节校验校验 +0x0d
若没有读取卡号:'o'+机号+‘1’+2字节校验+0x0d
8字节卡号指4字节16进制卡号转换为ASCII码
3)设置机号和读头序列号
上位机下发:'Z'+'X'+8字节序列号+2字节校验+0x0d
下位机返回:'Z'+'O'+'K'+0x0d
4)清除读卡标志命令1
上位机下发:'U'+机号+'o'+'k'+Oxod
下位机返回:无
5)清除读卡标志命令2
上位机下发:'Q'+机号+'O'+'P'+0x0d
下位机返回:无
说明:1 命令中的所有字节均为ASCII码
2 上位机下发时的校验算法为:前面所有字节异或后得到一个字节,然后将这个字节按高低位 转换为2个ASCII码
3 下位机上传时的校验算法:前面所有字节求和取反加1生成一个字节,然后将这个字节按高低 位转换为2个ASCII码

大家帮帮忙呀给点思路也好。
 
《通讯协议》
1.通讯模式:19200,N,8,1
2.命令格式:1字节命令字+1字节机号+.....+0x0d
3.命令含义:
1) 读读头的序列号:
上位机下发:'R'+机号+2字节校验校验 +0x0d
下位机返回:'R'+机号+8字节序列号+2字节校验校验 +0x0d
2) 读读头中的卡号
上位机下发:'o'+机号+o+0x0d
下位机返回:
若已读取卡号:'U'+机号+8字节卡号+2字节校验校验 +0x0d
若没有读取卡号:'o'+机号+‘1’+2字节校验+0x0d
8字节卡号指4字节16进制卡号转换为ASCII码
3)设置机号和读头序列号
上位机下发:'Z'+'X'+8字节序列号+2字节校验+0x0d
下位机返回:'Z'+'O'+'K'+0x0d
4)清除读卡标志命令1
上位机下发:'U'+机号+'o'+'k'+Oxod
下位机返回:无
5)清除读卡标志命令2
上位机下发:'Q'+机号+'O'+'P'+0x0d
下位机返回:无
说明:1 命令中的所有字节均为ASCII码
2 上位机下发时的校验算法为:前面所有字节异或后得到一个字节,然后将这个字节按高低位 转换为2个ASCII码
3 下位机上传时的校验算法:前面所有字节求和取反加1生成一个字节,然后将这个字节按高低 位转换为2个ASCII码

大家帮帮忙呀给点思路也好。
 
在SPCOMM的接收事件里把协议处理了就可以了
 
我现在还没有概念谁能给我写上一段代码,万分感谢了!
 
//串口接收
procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;
BufferLength: Word);
var i:integer;
rxbuf:array of byte;
s:string;
begin
setlength(rxbuf,bufferlength);
move(buffer^,rxbuf[0],bufferlength);
s:='';
for i:=0 to bufferlength-1 do s:=s+rxbuf;
//协议处理
if s='xxxx' then ........
end;
 
SPComm对于串口数据超过9的就不能使用拉, 建议采用PCOMM
 
还有, 如果你在接收到数据之后, 需要较长的处理时间的话, 那么这将会被后面的一个事件给冲掉, 因此楼主你还需要考虑这些问题
 
下位机没有存储,每次下位机的读头读到信息后如果不读取,有新的信息来时就会冲掉所以我要多线程,那么我就需要使用多个COM口,现在市场上有没有这样的硬件模块?
另外,和下位机通讯的时候,我发出一串命令他接受到就会自动返回我需要的信息,那么我发出的命令应该是一串16进制把
比如:我要发的命令格式为:'R'+机号+2字节校验校验 +0x0d
那么比如 r0001ts0x0d 其中0001是机号 ts 是2字节校验
我要把他转化为 16进制 "72H30H30H30H31H74H73H30H78H30H64H"
 
多串口卡市面上很多
发送‘123’和发送$31+$32+$33是等价的,不用特意去转
你最后要发送的的命令字串可以写成'R0001'+Char(ts1)+Char(ts2)+#13
 
字符串r0001ts0x0d发送时最后的0x0d是结束标志吧?
这个'R0001'+Char(ts1)+Char(ts2)+#13 是不是应该写成
'R0001'+Char(ts1)+Char(ts2)+0x0d
 
'R0001'+Char(ts1)+Char(ts2)+char($0d)
 
var
tmpstr:string;
plcaddress:string;
begin
plcaddress:='0001';//先只和一台下位机通讯
tmpstr:= 'r'+pcaddress+'ts'+'0x0d';//其中 ts 是校验字符
comm1.writecommdata(pchar(tmpstr),length(tmpstr));

end;
还有就是这里的校验算法
2 上位机下发时的校验算法为:前面所有字节异或后得到一个字节,然后将这个字节按高 低位转换为2个ASCII码
3 下位机上传时的校验算法:前面所有字节求和取反加1生成一个字节,然后将这个字节按高低位转换为2个ASCII码

是怎么处理 ts 这两个校验字符的??
 
#13和char($0d)是一样的,都看做是字符,0x0d不是字符类型编译会报错。
校验算法说的不够明确,再说详细一些“将这个字节按高 低位转换为2个ASCII码”,是高4位低4位吗?
校验做个累加和还不够吗,搞这么麻烦。
 
“将这个字节按高 低位转换为2个ASCII码”,我也是这么理解的。

这里的校验是指将字符 ‘ts’按照“上位机下发时的校验算法”处理后发给下位机
然后下位机用“下位机上传时的校验算法”处理后在发给上位机 。
当上位机收到这个校验信息后与自己的结果对比(上位机自己先用“下位机上传时的校验算法”计算出结果)如果正确则通讯正常,是么?
 
还是不明白。
比如校验后的字节是16进制的AB,是要转换成字符'A'和'B'吗
 
unit PTCom232;
{
设计:张世平
时间:2005-6-24
注意:需要Config232.ini文件配合
全双工-内存数据库通讯

}
interface
uses
StrUtils,
Classes,
windows,
Dialogs,
WinSock,
ScktComp,
IniFiles,
SysUtils,
ExtCtrls,
SPComm,
GlobalDefine;
resourcestring
StatusDateTimeFormat = 'yyyy/mm/dd" - "hh:nn:ss:zzz AM/PM';

type
TCFG = record

CommName: pchar;
BaudRate: integer;
ByteSize: pchar;
StopBits: pchar;
Parity: pchar;
NeedWriteLog: integer;
end;

PMyPobject = ^MyPobject;
MyPobject = record
Msg: PByte;
Len: Integer
end;

TCom232 = class
private
AComm: TComm;
TimerSend: TTimer; //如果com232 通,并且发送队列有数据,则发送

procedure TimerSendTimer(Sender: tobject);
procedure CommReceiveData(Sender: TObject; Buffer: Pointer; BufferLength: Word);
procedure CommReceiveError(Sender: TObject; EventMask: Cardinal);
function LoadUDPParameter(): BOOLEAN;
function SetUDPParameter(): BOOLEAN;

public
constructor Create();
destructor DESTROY; override;
function Open(): boolean;
function Execute(): BOOLEAN;
procedure WriteLog(ErrStr: string);
end;

function FreeBuf(Buffer: PTThreadList): Boolean;
function ReadFromBuf(var MyObject: PMyPobject; var
Buffer: PTThreadList): Boolean;
function HaveData(Buffer: PTThreadList): Boolean;
function WriteToBuf(var MyObject: PMyPobject; var
Buffer: PTThreadList; Capacity: Integer): Boolean;

procedure Log(LogFilename: string; ErrStr: string);
procedure SendLog(ErrStr: string);
procedure RecLog(ErrStr: string);

procedure HexStrToBytes(hHexStr: string; pbyteArray: Pointer);
procedure BytesToHexStr(var hHexStr: string; pbyteArray: PByte; InputLength: WORD);


const
iCapacity = 1024;
CONFIG = 'CONFIG232.INI';
SelfLog = 'log_232.Log';
SendDataQueue_log = 'log_232_send.log';
ReceiveDataQueue_Log = 'log_232_Rec.log';


var SendDataQueue: PTThreadList; //待发送数据队列
ReceiveDataQueue: PTThreadList; //接收数据
CurPath: string;
CFG: TCFG;


implementation

procedure HexStrToBytes(hHexStr: string; pbyteArray: Pointer);
{pbyteArray must point to enough memory to hold the output}
var
i, j: WORD;
tempPtr: PChar;
twoDigits: string[2];
begin
tempPtr := pbyteArray;
j := 1;
for i := 1 to (Length(hHexStr) div 2) do begin
twoDigits := Copy(hHexStr, j, 2);
Inc(j, 2);
PByte(tempPtr)^ := StrToInt('$' + twoDigits);
Inc(tempPtr);
end; {for}
if ((Length(hHexStr) mod 2) = 1) then
begin
twoDigits := '0' + RightStr(hHexStr, 1);
Inc(j, 2);
PByte(tempPtr)^ := StrToInt('$' + twoDigits);
end;

end;

procedure BytesToHexStr(var hHexStr: string; pbyteArray: PByte; InputLength: WORD);
const
HexChars: array[0..15] of Char = '0123456789ABCDEF';
var
i, j: WORD;
begin
hHexStr := '';
j := 1;
for i := 1 to InputLength do begin
hHexStr := hHexStr + Char(HexChars[pbyteArray^ shr 4]);
inc(j);
hHexStr := hHexStr + Char(HexChars[pbyteArray^ and 15]);
inc(j);
inc(pbyteArray);
end;

end;


procedure Log(LogFilename: string; ErrStr: string);
var
LogFile: TextFile;
begin
if CFG.NeedWriteLog <> 1 then exit;
AssignFile(LogFile, LogFilename);
if FileExists(LogFilename) then Append(LogFile)
else Rewrite(LogFile);
Writeln(LogFile, ErrStr);
CloseFile(LogFile);
end;


procedure TCom232.WriteLog(ErrStr: string);
begin
LOG(CurPath + selflog, DateTimeToStr(now) + ': ' + ErrStr);
end;


procedure SendLog(ErrStr: string);
begin
LOG(CurPath + SendDataQueue_log, ErrStr);
end;

procedure RecLog(ErrStr: string);
begin
LOG(CurPath + ReceiveDataQueue_Log, ErrStr);
end;


function GETINIkey(IniFile: pchar; MAINKEY: pchar; SUBKEY: pchar): pchar; stdcall; //export;
var
myini: Tinifile;
s: string;
begin
// curpath:=extractfilepath(Application.exename);
myini := Tinifile.Create(IniFile);
// result := PCHAR(Myini.readString(mainkey, subkey, ''));
s := myini.readString(MAINKEY, SUBKEY, '');
getmem(result, length(s) + 1);
strcopy(result, pchar(s));
myini.Free;
end;

constructor TCom232.Create();
begin
if not LoadUDPParameter() then EXIT;

New(SendDataQueue);
SendDataQueue^ := TThreadList.Create;

New(ReceiveDataQueue);
ReceiveDataQueue^ := TThreadList.create;


TimerSend := TTimer.Create(nil); //
TimerSend.Enabled := false;
TimerSend.Interval := 1; //时间可以自己定义
TimerSend.OnTimer := TimerSendtimer;

ACOMM := TCOMM.Create(nil);
ACOMM.OnReceiveData := CommReceiveData;
Acomm.OnReceiveError := CommReceiveError;

if not SetUDPParameter() then EXIT;

end;

function TCom232.Open(): boolean;
begin
result := false;
try
AComm.StartComm();
except
writelog(AComm.CommName + ' 打开失败,请确信电缆正确...');
exit;
end;
//是否判断已经打开,重复打开
// writelog(AComm.CommName + ' 打开失败...');
result := true;
end;

destructor TCom232.DESTROY;
begin
if TimerSend <> nil then
begin
TimerSend.Enabled := FALSE;
TimerSend.Free;
end;
if ACOMM <> nil then
begin
Acomm.StopComm;
AComm.Free;
end;
WRITELOG('COM232 服务正常关闭!');
end;

function TCom232.Execute: BOOLEAN;
begin
timerSend.Enabled := true;
RESULT := TRUE;
end;

function TCom232.LoadUDPParameter(): BOOLEAN;
var inifile: pchar;
begin
RESULT := FALSE;
inifile := pchar(CurPath + CONFIG);
try
CFG.CommName := GETINIkey(inifile, 'com232', 'CommName');
CFG.BaudRate := strtoint(GETINIkey(inifile, 'com232', 'BaudRate'));
CFG.ByteSize := GETINIkey(inifile, 'com232', 'ByteSize');
CFG.StopBits := GETINIkey(inifile, 'COM232', 'StopBits');
CFG.Parity := GETINIkey(inifile, 'COM232', 'Parity');
CFG.NeedWriteLog := STRTOINT(GETINIkey(inifile, 'COM232', 'NeedWriteLog'));
RESULT := TRUE;
except
RESULT := FALSE;
EXIT;
end;

if (CFG.ByteSize <> '_5') and (CFG.ByteSize <> '_6') and (CFG.ByteSize <> '_7') and (CFG.ByteSize <> '_8') then
begin
Writelog(config + ' is error:bytesize must in [_5, _6, _7, _8] . . ');
result := false;
EXIT;
end;
if (CFG.StopBits <> '_1') and (CFG.StopBits <> '_1_5') and (CFG.StopBits <> '_2') then
begin
Writelog(config + ' is error:StopBits must in [_1, _1_5, _2] . . ');
result := false;
EXIT;
end;
//None, Odd, Even, Mark, Space
if (CFG.Parity <> 'None') and (CFG.Parity <> 'Odd') and (CFG.Parity <> 'Even') and (CFG.Parity <> 'Mark') and (CFG.Parity <> 'Space') then
begin
Writelog(config + ' is error:Parity must in [None, Odd, Even, Mark, Space] . . ');
result := false;
EXIT;
end;
end;

function TCom232.SetUDPParameter(): BOOLEAN;
begin
RESULT := FALSE;
try
WriteLog('COM232 全双工通信服务正在加载...');
WriteLog('commname:' + cfg.CommName);
WriteLog('BaudRate:' + inttostr(cfg.BaudRate));
WriteLog('ByteSize:' + cfg.ByteSize);
WriteLog('StopBits:' + cfg.StopBits);
WriteLog('Parity:' + cfg.Parity);
AComm.CommName := '//./'+cfg.CommName;
AComm.BaudRate := cfg.BaudRate;

if cfg.ByteSize = '_5' then AComm.ByteSize := tbytesize(0);
if cfg.ByteSize = '_6' then AComm.ByteSize := tbytesize(1);
if cfg.ByteSize = '_7' then AComm.ByteSize := tbytesize(2);
if cfg.ByteSize = '_8' then AComm.ByteSize := tbytesize(3);

if cfg.StopBits = ' _1' then AComm.StopBits := TStopBits(0);
if cfg.StopBits = ' _1_5' then AComm.StopBits := TStopBits(1);
if cfg.StopBits = ' _2' then AComm.StopBits := TStopBits(2);

if cfg.Parity = 'None' then AComm.Parity := TParity(0);
if cfg.Parity = 'Odd' then AComm.Parity := TParity(1);
if cfg.Parity = 'Even' then AComm.Parity := TParity(2);
if cfg.Parity = 'Mark' then AComm.Parity := TParity(3);
if cfg.Parity = 'Space' then AComm.Parity := TParity(4);

writelog('Set Com232 Parameter success...');
RESULT := TRUE;
except
writelog('Set Com232 Parameter fail...');
RESULT := FALSE;
end;
end;



procedure TCom232.TimerSendTimer(Sender: tobject);
var
wmsg: PMyPobject;
TmpStr: string;
buf: array of byte;
begin
if not HaveData(SendDataQueue) then exit;
//现在如何确信com依然打开呢?否则readfrombuf 读了就被删除了
try
wmsg := nil;
if not ReadFromBuf(wmsg, SendDataQueue) then exit;
if wmsg = nil then exit; //如果成功会自动从队列删除
// buf[0]:=byte($A1);
// buf[1]:=byte($A2);
// buf[2]:=byte($A3);
// buf[3]:=byte($f1);
//AComm.WriteCommData(@buf[0],4);
//winsock.SendBuf(wmsg.msg^, wmsg.Len);
setlength(buf, wmsg.Len);
copymemory(buf, wmsg.Msg, wmsg.Len);
AComm.WriteCommData(@buf[0], wmsg.Len);
BytesToHexStr(TmpStr,wmsg.Msg, wmsg.Len);
//也可以BytesToHexStr(TmpStr, @buf[0], wmsg.Len);完全正确
SendLog(TmpStr);
finally
if wmsg <> nil then
begin
if wmsg^.Msg <> nil then
FreeMem(wmsg.Msg, wmsg.Len);
Dispose(wmsg);
end;
end;
end;

procedure TCom232.CommReceiveData(Sender: TObject; Buffer: Pointer;
BufferLength: Word);
var
TmpStr: string;
wmsg: PMyPobject;
begin
new(wmsg);
getmem(wmsg.Msg, BufferLength);
CopyMemory(wmsg.Msg, Buffer, BufferLength); // wmsg.Msg^ := MsgOfWrite^;
WriteToBuf(wmsg, ReceiveDataQueue, BufferLength);
BytesToHexStr(TmpStr, wmsg.Msg, BufferLength);
RecLog(Tmpstr);
end;

procedure TCom232.CommReceiveError(Sender: TObject; EventMask: Cardinal);
begin
writelog(ACOMM.CommName + ' ReceiveError...');
end;

function FreeBuf(Buffer: PTThreadList): Boolean;
var
tmpList: TList;
wmsg: pMyPobject;
begin
result := true;
tmpList := Buffer^.LockList;
while tmpList.Count > 0 do
begin
wmsg := PMyPobject(tmpList.Last);
tmpList.Delete(tmplist.Count - 1);
tmplist.Pack;
if wmsg <> nil then
begin
if wmsg^.Msg <> nil then
FreeMem(wmsg.Msg, wmsg.Len);
Dispose(wmsg);
end;
end
end;

function ReadFromBuf(var MyObject: PMyPobject; var
Buffer: PTThreadList): Boolean;
var
tmpList: TList;
begin
result := true;
tmpList := Buffer^.LockList;
try
if tmpList.Count > 0 then
begin
MyObject := tmpList.First;
tmpList.Delete(0);
tmpList.Pack;
end
else
begin
MyObject := nil;
result := false;
end;
finally
begin
Buffer^.UnlockList;
end;
end;

end;

function HaveData(Buffer: PTThreadList): Boolean;
var
tmpList: TList;
begin
tmpList := Buffer^.LockList;
if tmpList.Count > 0 then
result := true
else
result := false;
Buffer^.UnlockList;
end;

function WriteToBuf(var MyObject: PMyPobject; var
Buffer: PTThreadList; Capacity: Integer): Boolean;
var
tmpList: TList;
wmsg: PMyPobject;
begin

tmpList := Buffer^.LockList;
try
while tmpList.Count >= Capacity do
begin
wmsg := tmpList.First;
tmpList.Delete(0);
tmpList.Pack;
if wmsg <> nil then
begin
if wmsg^.Msg <> nil then
FreeMem(wmsg.Msg, wmsg.Len);
Dispose(wmsg);
end;
end;
tmpList.Add(MyObject);
tmpList.Pack;
finally
begin
Buffer^.UnlockList;
end;
end;
result := true;
end;



end.
 
//发送字符
procedure TForm1.Button3Click(Sender: TObject);
var
p:pchar;
x:integer;
begin
x:=Length(Memo1.Lines.Text);//Memo1.Lines.Text是发送的内容
p:=Pchar(Memo1.Lines.Text);
Comm1.WriteCommData(p,x);
end;
//接受字符
procedure TForm1.Comm1ReceiveData(Sender: TObject; Buffer: Pointer;
BufferLength: Word);
var
pStr:pchar;
begin
pStr:= Buffer;
memo2.Lines.Add(pStr);
end;

我这样写对么?
 
spcomm本身就是多线程处理的
 
那么就是说我要用8线程的话
我就放8个SPCOMM控件和8个TIMER控件他们各自控制收发数据就可以了是么?
 
哈哈,大家真热心,也说一下我的经验,如果接收数据后的处理较耗时,建议使用队列的方式处理,即在接收事件中接收数据后不处理,而是放在队列中,然后启动通知事件,让另一个线程处理.另外定义一个类似于TBYTES=ARRAY OF BYTE的类型,方便操作.对接收的缓冲区进行强制转换后使用可以简化操作.
 

Similar threads

D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
2K
DelphiTeacher的专栏
D
D
回复
0
查看
752
DelphiTeacher的专栏
D
D
回复
0
查看
1K
DelphiTeacher的专栏
D
D
回复
0
查看
658
DelphiTeacher的专栏
D
后退
顶部