关于多线程的问题(100分)

  • 主题发起人 主题发起人 wuchunhua
  • 开始时间 开始时间
W

wuchunhua

Unregistered / Unconfirmed
GUEST, unregistred user!
需要解决的问题:服务器下有多个串口,每个串口约2秒就产生一条数据。需要把这些数据记录到数据库中
初步设想:通过监听多个串口,一旦缓冲接收到数据,程序对接收到的数据进行处理并保存到数据库中。
本人想用spcomm控件进行监听,但是他们处理接收数据是同一个函数 ,采用临界的话,是不是会丢失一些数据,如果串口达到10~20个,是否一些串口接收数据事件始终不能进入临界区。
希望大家多多给点建议。
 
为什么要使用那么多的串口,建议如果可能最好使用控制器,然后通过控制器联机计算机,这样能好一些。
 
to :appfirst
控制器:是不是象MOXO模块,我就是用MOXO的NPort5210.它的Port口可以通过自带的软件,虚拟成服务器的串口。每个模块有独立的IP,这样通过TCP/IP实现他们的数据通信。对于服务器来说就和对一个自带的COM口操作一样。
现场在模块上面接有RS485线,连接到采集模块上,模块是采用CDT循环采集、发送。
 
差不多。
 
怎么没有人关注???
 
过几天可能我也要有这样的项目了,共同学习!
 
希望提供好的方法,我一直不会写这方面的程序
 
如果你一定要用spcomm控件的话,你可以一个串口用一个spcomm控件来进行监听,spcomm控件是线程安全的,但这种方法只是串口不多的情况下可行
 
谁有pcomm监听多个串口的原代码 ,高分求购
 
串口问题是老问题了
工作中经常遇到
如果你不相信别人的控件
就自己用API来实现吧
我在工作中都是使用API来做,基本都能完成要求的工作
 
今天用Moxa的pcomm.dll实现了对多个串口的监听,每个串口约3秒产生427个字节,包括了110节电池的电压。而且每个串口同时产生。如果不进行数据保存,没有问题,一旦将数据保存到sqlserver中 ,就出现以下问题:Project output/batteryMonitor.exe faulted with message:'access violation at 0X400022d9:write of address 0x00000001'.Process stopped.use Step or Run to continue

下面是我的主要代码:unit Monitor_Data;

interface
uses
uMyPublic,SysUtils,forms,adodb,DateUtils,Windows,Classes,ActiveX;

type
TMonitor_Data = class (Tobject)
private
Adoconn:TAdoconnection ;
adoqry:Tadoquery;
crcBuf:array[0..1000] of byte;
Mondate:TdateTime;
Rec_data:array of double;
cs:TRTLCriticalSection;
procedure SetFBatt_count(aValue:integer);
procedure SetFInsulate_Count(aValue:integer);
procedure SetFDcm_count(aValue:integer);
procedure Save_Batt_Vol_data();
public
FSuccessFul:boolean;//表示数据是否处于处理中...
FPortNo:integer;//表示那个串口的数据
constructor Create ;
destructor Destroy; virtual;
procedure opinion_style(Fbuf:array of byte) ;//判断是那个类型
procedure transact_Batt_vol_byte(iLength:integer);//处理电池数据
published
end;
var
MonitorData:array of TMonitor_Data;
implementation

uses uPublic;
const
abHead:array[0..11] of byte=($aa,$55,$aa,$55,$aa,$55,$eb,$90,$eb,$90,$eb,$90);

{ TMonitor }

constructor TMonitor_Data.Create;
begin
FSuccessFul:=false;
//InitializeCriticalSection(cs) ;
end;

destructor TMonitor_Data.Destroy;
begin
//DeleteCriticalSection(cs);
end;

//-------------------------
{判断数据类型}
//-------------------------
procedure TMonitor_Data.opinion_style(Fbuf: array of byte);
var

Log_buf:array[0..60] of byte ;
iLengTh:integer;
i:integer;
begin
Mondate:= Now ;
FSuccessFul:=true;
//判断头文件
Move(Fbuf[0],crcBuf[0],12);
if CompareMem(@crcbuf[0],@abhead[0],12) then
begin
FSuccessFul:=false;
exit;
end;
//遥测数据
if ( Fbuf[12] <> byte($71) ) and ( Fbuf[13] <> byte($61) ) then
begin
FSuccessFul:=false;
exit;
end;
iLengTh:=strToInt(IntToHex(FBuf[14],2));//遥测长度
for i:=low(crcBuf) to high(crcBuf) do
crcBuf := $00 ;
Move(Fbuf[24],crcBuf[0],(iLengTh-1)*6);

if FBuf[19] = $01 then
transact_Batt_vol_byte(iLengTh-1) ;
FSuccessFul:=false;
end;


//-------------------------
{处理蓄电池电压数据}
//-------------------------
procedure TMonitor_Data.transact_Batt_vol_byte(iLength:integer);
var
i:integer;
begin
setLength(Rec_data,ilength*2);
Rec_data[0]:=GetCommVol(crcBuf[2],crcBuf[1],1);//组压
Rec_data[1]:=GetCommVol(crcBuf[4],crcBuf[3],2);//温度
Rec_data[2]:=StrToFloat(intToHex(crcBuf[7],2));//市电
if StrToInt(intToHex(crcBuf[7],2)) < 0 then
Rec_data[3]:=GetCommVol(crcBuf[10],crcBuf[9],2)* -1 //电流
else
Rec_data[3]:=GetCommVol(crcBuf[10],crcBuf[9],2); //电流
for i:= 2 to iLength -1 do
begin
//if not CheckCrc8(Fbuf,i*6) then
//exit;
Rec_data[i*2] := GetCommVol(crcBuf[i*6+2],crcBuf[i*6+1],2);
Rec_data[i*2+1]:= GetCommVol(crcBuf[i*6+4],crcBuf[i*6+3],2);
application.ProcessMessages ;
end;
// EnterCriticalSection(CS);
Save_Batt_Vol_data;
// LeaveCriticalSection(CS);
end;


//-------------------------
{保存蓄电池电压}
//-------------------------
procedure TMonitor_Data.Save_Batt_Vol_data;
var
i:integer;
begin
try
//CoInitialize( nil );
adoconn:=TAdoconnection.create(Nil);
adoqry:= TAdoquery.create(nil);
adoqry.connection:=Adoconn;
adoqry.CursorLocation:=clUseServer;
adoconn.ConnectionString := 'Provider=SQLOLEDB.1;Password=sa;Persist Security Info=True;User ID=sa;Initial Catalog=test;Data Source=WCH';
adoconn.Connected :=true;
adoconn.BeginTrans ;
for i:= 4 to high(Rec_data)-1 do
begin
adoqry.Close;
adoqry.SQL.Clear ;
adoqry.SQL.Add('insert into Batt_History_detail_Log');
adoqry.SQL.Add('(zd_id,Group_Id,Batt_Id,Batt_Vol,Mon_Date,Mon_Time)');
adoqry.SQL.Add('values(:zd_id,:Group_Id,:Batt_Id,:Batt_Vol,:Mon_Date,:Mon_Time)');
adoqry.Parameters.ParamByName('zd_id').Value := format('%.3d',[FportNo]);
adoqry.Parameters.ParamByName('Group_Id').Value := '01' ;
adoqry.Parameters.ParamByName('Batt_Id').Value := format('%.3d',[i-3]);
adoqry.Parameters.ParamByName('Batt_Vol').Value :=Rec_data;
adoqry.Parameters.ParamByName('Mon_Date').Value :=dateOf(MonDate);
adoqry.Parameters.ParamByName('Mon_Time').Value :=timeof(MonDate);
try
adoqry.ExecSQL ;
except
adoconn.RollbackTrans ;
exit;
end;
end;
adoconn.CommitTrans ;
finally
adoqry.Free;
adoconn.Free;
//CoUninitialize;
end;
end;

end.

//----------------------------------------------
//以下是pcomm事件触发程序,iport表示那个串口,
//问题出现在数据保存到数据库是发生错误,
//当四个串口以3s为周期,同时上传数据,运行10分钟左右就有以下错误产生
//----------------------------------------------

procedure CntIrq(iPort:LongInt);stdcall;
var
Mon_array:array of byte;
rLen:LongInt;
i:integer;
begin
setLength(Mon_array ,1400);
rlen := sio_read(iPort,@Mon_array[0] ,1400); //读取数据
If rlen = 0 Then Exit; //若无数据则跳出
for i:=low(sio_read_port) to high(sio_read_port) do
begin
if sio_read_port.Port = iPort then
begin
frmMain.Grid_Port_Info.Cells[2,i+1]:=intToStr(rLen)+':'+dateTimeToStr(now) ;
if MonitorData = nil then
MonitorData:=TMonitor_Data.Create ;
if MonitorData.FSuccessFul then
begin
exit ;
end;
MonitorData.FPortNo := iPort ;
MonitorData.opinion_style(Mon_array);
break;
end;
end;
end;


//------------------------
//打开串口
//------------------------
procedure TFrom1.button1click(sender:Tobject)
begin
setLength(sio_read_port,7);
setLength(MonitorData,7);
sio_read_port[0]:= TSio_read_Port.Create ;
sio_read_port[0].Port := 3 ;
sio_read_port[1]:= TSio_read_Port.Create ;
sio_read_port[1].Port := 4 ;
sio_read_port[2]:= TSio_read_Port.Create ;
sio_read_port[2].Port := 5 ;
sio_read_port[3]:= TSio_read_Port.Create ;
sio_read_port[3].Port := 6 ;
sio_read_port[4]:= TSio_read_Port.Create ;
sio_read_port[4].Port := 7 ;
sio_read_port[5]:= TSio_read_Port.Create ;
sio_read_port[5].Port := 8 ;
sio_read_port[6]:= TSio_read_Port.Create ;
sio_read_port[6].Port := 9 ;
for i:=low(sio_read_port) to high(sio_read_port) do
begin
try
Grid_Port_Info.Cells[0,i+1]:='COM' + intToStr(sio_read_port.Port);
if sio_read_port.OpenPort then
Grid_Port_Info.Cells[1,i+1]:='COM口已经打开'
else
Grid_Port_Info.Cells[1,i+1]:='COM口打开失败' ;
except
sio_read_port.closePort ;
continue;
end;
end;
end;
 
没有认真看代码,但我想出现'access violation at 0X400022d9:write of address 0x00000001'异常信息的话可能不一定是线程的问题,一般这种情况都是访问非法内存而导致的,因此情况可能有二种:1、记录过程本身代码有问题导致了非法内存访问;2、记录过程耗时太长而导致pcomm.dll内部处理出现异常;
因为是3s钟出现一次而且大家都同时收到信号,所以可以考虑每个口分配自己的一个缓冲区对应起来,信号来的时候各自把数据存入自己的缓冲区,然后由一个总的调度线程去把缓冲区里的数据取出来放入另外一个缓冲区,最后由一个线程队列去把缓冲里的数据写入数据库。所有缓冲区里都需要加锁。
另外因为这个数据量还是比较大的,所以建议采用存储过程,效率相差不少。
你是做变电所的蓄电池项目吗?什么公司?
 
注册网址 http://3711.9soho.com/你想拥有自己的网站吗?
你想通过互联网上兼职创业.赚钱吗?
八十年代初,摆个地摊就能发财,可很多人不敢。
九十年代初,买支股票就能挣钱,可很多人不信。
二十一世纪,开个网站就能赚钱,可很多人不试。
现在是互联网时代,加入工作网就能月赚过万,你还想错过吗?
QQ:277341915
E-mail:zyjit2004@163.com
msn: zyjit2008@msn.com
網址:http://3711.9soho.com/
 
对于电力CDT规约,如果多串口,采用给每个串口产生一个线程,和收到字符事件,一旦串口有字符事件,启用线程读取。
 
to Wolfding:是做蓄电池项目的。
问题已经找到,代码需要修改,我会把修改后的代码放上来的。
你对蓄电池有兴趣?
 
多人接受答案了。
 
后退
顶部