我每秒钟都有一条外部信息
消息有效负载保存在ClientDataSet中并显示在dbGrid中
不涉及数据库。仅限RAM存储。
这很好,
但
当数据集为空并且第一次填充时,我有间歇性问题。
代码如下:
procedure TCtlCfg_DM_WarningsFaults_frm.DecodeRxFrame(Protocol: TProtocolSelection;
// PROVA UTAN VAR VAR Frame : CAN_Driver.TCAN_Frame);
Frame : CAN_Driver.TCAN_Frame);
var
OldRecNo : integer;
// OldIxname : string;
// bMark : TBookMark;
WasFiltered : Boolean;
IdBitFields : TCanId_IdBitFields;
Msg : TCan_Msg;
MsgType : integer;
GlobalNode : TCanId_GlobalNode;
LocalNode : TCanId_LocalNode;
SubNode : TCanId_SubNode;
EntryType : integer;
SubSystemType : integer;
SubSystemDeviceId : integer;
IsActive : Boolean;
IsAcked : Boolean;
begin
with cdsWarningsFaults do
begin
if not Active then Exit;
Msg := Frame.Msg;
IdBitFields := DecodeCanId(Protocol, Frame.ID);
if IdBitFields.SubNode <> cSubNode_Self then Exit; // Ignore non controller/slave messages
if IdBitFields.AddressMode <> cCanId_AddrMode_CA then Exit;
MsgType := IdBitFields.MessageType;
if MsgType <> cMsg_CTL_CA_Broadcast_WarningAndFaultList then Exit;
if Frame.MsgLength < 5 then Exit;
GlobalNode := IdBitFields.GlobalNode;
LocalNode := IdBitFields.LocalNode;
SubNode := IdBitFields.SubNode;
// Silent exit if wrong node
if GlobalNode <> fNodeFilter.GlobalNode then Exit;
if LocalNode <> fNodeFilter.LocalNode then Exit;
if SubNode <> fNodeFilter.SubNode then Exit;
EntryType := Msg[1];
SubSystemType := Msg[2];
IsActive := (Msg[3] = 1);
SubSystemDeviceId := Msg[4];
IsAcked := (Msg[8] = 1);
DisableControls; // 2007-12-03/AJ Flytta inte scrollbars under uppdatering
OldRecNo := RecNo;
// OldIxName := IndexName; // Save current index
// IndexName := IndexDefs.Items[0].Name;
WasFiltered := Filtered; // Save filter status
Filtered := False;
try
try
if Findkey([GlobalNode, LocalNode, SubNode, EntryType, SubSystemType, SubSystemDeviceId]) then
begin // Update record
Edit;
FieldByName('fIsActive').AsBoolean := IsActive;
FieldByName('fIsAcked').AsBoolean := IsAcked;
FieldByName('fTimeout').AsDateTime := GetDatabaseTimeoutAt;
Post;
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Efter Edit. N=' + IntToStr(GlobalNode) + ' ' +
IntToStr(LocalNode) + ' ' +
IntToStr(SubNode) +
' RecCnt=' + IntToStr(RecordCount) + ' ET=' + IntToStr(EntryType) + ' SST=' + IntToStr(subSystemType) + ' SSD=' + IntToStr(SubSystemDeviceId), False);
end
else
begin // Create new record
Append;
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Efter Append. N=' + IntToStr(GlobalNode) + ' ' +
IntToStr(LocalNode) + ' ' +
IntToStr(SubNode) +
' RecCnt=' + IntToStr(RecordCount) + ' ET=' + IntToStr(EntryType) + ' SST=' + IntToStr(subSystemType) + ' SSD=' + IntToStr(SubSystemDeviceId), False);
try
FieldByName('fGlobalNode').AsInteger := GlobalNode;
FieldByName('fLocalNode').AsInteger := LocalNode;
FieldByName('fSubNode').AsInteger := SubNode;
FieldByName('fEntryType').AsInteger := EntryType;
FieldByName('fSubSystemType').AsInteger := SubSystemType;
FieldByName('fSubSystemDeviceId').AsInteger := SubSystemDeviceId;
FieldByName('fIsActive').AsBoolean := IsActive;
FieldByName('fIsAcked').AsBoolean := IsAcked;
FieldByName('fTimeout').AsDateTime := GetDatabaseTimeoutAt;
finally
try
Post; // VArför biter inte denna post så att det blir edit nästa gång
except
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Exception efter Post.', True);
end;
MainForm.AddToActivityLog('CtlCfg_DM_WF: DecodeRxFrame: Efter Post. N=' + IntToStr(GlobalNode) + ' ' +
IntToStr(LocalNode) + ' ' +
IntToStr(SubNode) +
' RecCnt=' + IntToStr(RecordCount) + ' ET=' + IntToStr(EntryType) + ' SST=' + IntToStr(subSystemType) + ' SSD=' + IntToStr(SubSystemDeviceId), False);
end;
end;
except
on E: Exception do
begin
MainForm.AddToActivityLog('Post exception message: [' + E.Message + ']', False);
MainForm.AddToActivityLog('Post exception class: [' + E.ClassName + ']', False);
MainForm.AddToActivityLog('Post exception Error code: [' + IntToStr(EDBCLIENT (E).ErrorCode) + ']', False);
MainForm.AddToActivityLog('Post exception ReadOnly is: [' + BoolToStr(ReadOnly) + ']', False);
MainForm.AddToActivityLog('Post exception CanModify is: [' + BoolToStr(CanModify) + ']', False);
MainForm.AddToActivityLog('DecodeRxFrame: Exception inside FindKey block', False);
Cancel;
end;
end;
finally
// IndexName := OldIxName; // Restore previous index
Filtered := WasFiltered; // Restore filter state
if (OldRecNo >= 1) and (OldRecNo <= RecordCount) then RecNo := OldRecNo;
EnableControls;
end;
end;
//MainForm.AddToActivityLog('DecodeRxFrame: Exit ur proceduren', False);
end;
问题是当记录尚不存在时,
我需要添加一条新记录
它通常工作正常,但很多时候似乎POST不起作用,
当新数据出现时,附加内容会重复几次或多次。
突然,附加工作正常,后续更新使用编辑完成 而据我所知,之后它会永远有效。
问题是间歇性的,成功所需的尝试次数各不相同 这感觉像是一个时间问题,但我无法弄清楚。
任何想法都非常感激。
谢谢,
Anders J
答案 0 :(得分:3)
正如我的评论中所提到的,可以通过使用日志提取来了解代码如何流动。 (另外作为附注,有时您需要注意日志记录系统的可靠性。您的日志记录至少部分是家酿,所以当您随意通过True
/我不知道它意味着什么False
方法的AddToActivityLog
值。)
但是,我仍然可以提供一些指导来帮助您确定问题。我还有一些一般性意见可以帮助您改进代码。
使用日志来缩小问题范围你并不害羞:这是件好事!
但是你的技术可以使用一点改进。您正试图确定Post
方法周围出了什么问题。鉴于这样一个明确的目标,您的日志记录似乎令人惊讶地随意。
您应该执行以下操作:
//Log the line before Post is called. It confirms when it is called.
//Log important state information to check you're ready to post "correctly"
//In this it's especially useful to know the Sate of the dataset (dsEdit/dsInsert).
Post;
//Log the line after Post to confirm it completed.
//Note that "completed" and "succeeded" aren't always the same, so...
//Again, you want to log important state information (in this case dsBrowse).
如果你有这个记录,你可以(例如)告诉我们:
Post
数据集之前,dsInsert
状态。Post
后,数据集仍处于dsInsert
状态。所以现在:发布“完成”没有记录被“发布”将提供更多的东西:
TClientDataSet
,因此您需要查看OnPostError这一重要事件。 DBClient使用此回调机制在发布时通知客户端错误。如果您记录OnPostError
,我相信您会更好地了解问题。
最后我提到你的代码还有很多其他问题;特别是错误处理。
在您知道如何正确使用之前,请勿使用 。当你知道如何正确使用它时,你也会知道使用它是没有充分理由的。就目前而言,你的代码实际上是2个字符,而不是一个微妙的bug,甚至可能是一个噩梦甚至意识到它甚至存在;但是完整的非问题 没有 。 (你声明并使用了一个名为IsActive
的属性,与TDataSet的Active
只有两个字符不同。我相信你没有意识到这一点;他们的区别仅仅是一个意外。但是,如果他们有同样,与会非常悄悄地使用错误的。)
你需要编写更小的方法 - 更小!像你这样的代码是调试的噩梦,并且非常善于隐藏错误。
您的异常处理从根本上说是错误的:
您对日志记录和异常处理的评论表明,您只是简单地添加了绝望的内容。我认为理解是什么让你的日志记录变得有用并避免混乱是值得的。让我们仔细看看问题最多的部分。
/_ try
/_ FieldByName('fGlobalNode').AsInteger := GlobalNode;
/_E FieldByName('fLocalNode').AsInteger := LocalNode;
| FieldByName('fSubNode').AsInteger := SubNode;
| FieldByName('fEntryType').AsInteger := EntryType;
| FieldByName('fSubSystemType').AsInteger := SubSystemType;
| FieldByName('fSubSystemDeviceId').AsInteger := SubSystemDeviceId;
| FieldByName('fIsActive').AsBoolean := IsActive;
| FieldByName('fIsAcked').AsBoolean := IsAcked;
| FieldByName('fTimeout').AsDateTime := GetDatabaseTimeoutAt;
|_ finally
/_ try
/_ Post;
/ except
| MainForm.AddToActivityLog(..., True);
| end;
|_ MainForm.AddToActivityLog(..., False);
/ end;
|
...
所以,在上面的代码中:
Post
。当你最终得到的数据只有他们应该只有一小部分的记录时,这会令你感到头痛 - 除非你 幸运 并且Post
因为关键而失败数据丢失。提示:(适用于99%的异常处理案例。
<Get Resource>try .... finally<Cleanup>end
(唯一可以在....
内进行另一项资源保护的地方。)