Advantage Notification Trigger - advantage-database-server

Has anyone used an Notification Trigger successfully?
Does anyone know where I can get a sample of the string returned?
Thanks,
Howard

I'm not completely sure what you are looking for, but you can create a trigger that will signal an event like this:
CREATE TRIGGER MyNotifier ON EventTest
AFTER UPDATE
BEGIN
execute procedure sp_SignalEvent( 'UpdateOccurred',
false, 0, 'some data' );
END;
Then the following two statements will create the event and then wait for it (30 seconds in this example). The data that would be returned is the last parameter in the sp_SignalEvent procedure ('some data' in the above example). More realistic would be to use data that was updated in the table.
execute procedure sp_CreateEvent( 'UpdateOccurred', 2 );
execute procedure sp_WaitForEvent( 'UpdateOccurred', 30000, 0, 0 );
The documentation for sp_CreateEvent also provides an example.

Related

Delphi 7, Windows 7, event handler, re-entrent code

I've got some very old code (15+yr) that used to run ok, on older slower machines with older software versions. It doesn't work so well now because if fails a race condition. This is a general question: tell me why I should have known and expected the failure in this code, so that I can recognise the pattern in other code:
procedure TMainform.portset(iComNumber:word);
begin
windows.outputdebugstring(pchar('portset ' + inttostr(icomnumber)));
with mainform.comport do
try
if open then open := False; // close port
comnumber:=iComNumber;
baud:=baudrate[baudbox.itemindex];
parity:=pNone;
databits:=8;
stopbits:=1;
open:=true;
flushinbuffer;
flushoutbuffer;
if open then mainform.statusb.Panels[5].text:=st[1,langnum] {Port open}
else mainform.statusb.Panels[5].text:=st[2,langnum]; {port set OK}
except
on E: exception do begin
windows.OutputDebugString('exception in portset');
mainform.statusb.Panels[5].text:=st[3,langnum];
beep;
beep;
end;
end;
windows.outputdebugstring('portset exit');
end;
Note that flushinbuffer is protected with EnterCriticalSection(); AFAIK Nothing else is protected, and AFAIK there are no message handling sections. BUT
When this code is called from a click event, it gets part way through, then is interupted by a paint event.
The only tracing I have done is with outputdebugstring. I can see the first string repeated on entry before the second string is shown on exit. Is that real, or is it an illusion?
The trace looks like this:
4.2595 [4680] graph form click event
4.2602 [4680] portset 1 'from click event handler'
4.2606 [4680] graph form paint event
4.2608 [4680] portset 1 'from paint event handler'
4.2609 [4680] portset exit
4.3373 [4680] portset exit
This is a race condition: The paint event handler of the form is called before the click event handler code finishes, which causes failures. Serial code is AsyncPro. No thread code. Yes, there is more code, no it doesn't do anything in particular before "portset 1" but it does write to a form before it gets there:
with graphform do begin
if not waitlab.Visible then begin
waitlab.visible:=true;
waitprogress.position:=0;
waitprogress.visible:=true;
waitprogress.max:=214;
end;
end;
mainform.Statusb.panels[5].text:=gcap[10,langnum];
Don't hold back: What is it doing wrong, what should I be looking for?
This is expected behaviour - opening or closing a TApdComPort will service the message queue, specifically by calling a function it names SafeYield:
function SafeYield : LongInt;
{-Allow other processes a chance to run}
var
Msg : TMsg;
begin
SafeYield := 0;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin
if Msg.Message = wm_Quit then
{Re-post quit message so main message loop will terminate}
PostQuitMessage(Msg.WParam)
else begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
{Return message so caller can act on message if necessary}
SafeYield := MAKELONG(Msg.Message, Msg.hwnd);
end;
end;
The TApdComPort is an async component - the com port is managed on background threads and opening or closing the port requires either starting or signaling those threads to stop. While waiting for them to free the component services the message queue in case it takes some time for things to synchronize (for example) :
if Assigned(ComThread) then
begin
{Force the comm thread to wake...}
FSerialEvent.SetEvent;
{... and wait for it to die}
ResetEvent(GeneralEvent);
while (ComThread <> nil) do
SafeYield;
end;
You haven't really show us enough of your own code to say why this is problematic in your case, however. I think David's point about com ports being manipulated in a paint handler is valid... we need to see the broader picture and what, exactly, the problem is that you are having.
A standard paint event cannot happen on its own, it can only be triggered by message retrieval. So the only way the code you showed could be interrupted the way you describe is if either the Serial component itself, or an event handler you have assigned to it, is doing something that pumps the calling thread's message queue for new messages.
Since you are closing the port in the beginning of your event handler, if there is any chance of triggering the event twice (i.e. by calling Application.ProcessMessages anywhere from your code, or calling TMainform.portset() directly from a worker thread), the new instance will close your port while the older one tries to communicate trough it, which will result in an error. AFAIS there are two solutions:
The faster but least bearable one is to protect your entire function with a Mutex (or event which is not a syncronisation object but can be used as one), but this only hides the coding error you have made.
The more pro solution is to find where the race condition gets raised, then fix your code. You can do it by searching all references to Application.ProcessMessages() and TMainform.portset(), and make sure that they won't get called paralelly. If no reference can be found on either mentioned function, the problem could still be caused by running multiple instances of your code ('cause it will not create multiple com ports :) ).
Remy Lebeau gets the credit for answering the question, because, as I asked for, it was a general reply to a general question. But it would have been inadequate without his comments in response to Uwe Raabe.
And what conclusively demonstrated that Remy Lebeau was correct was the exceptional answer from J, pointing out the specific point where the code failed.
Thanks also to David Heffernan for asking "why does code that responds to WM_PAINT call portset", which also makes a general point. And yes, the quick fix was just to block the path from the paint event handler to the comms code, but I'd done that without recognising the more general point.
I'll be having a look at the comms code, to see if there are more problems like this, and I'll be looking at the event handlers, to see if there are more problems like this, so thanks to everyone who read and considered the question.

How to handle OnTimer event in Delphi XE2?

I need to show a message say "Hi" everyday at 9 AM. Do I require Timer for this? How can I check whether its 9 AM or not. What should be the interval of timer at which OnTimer event run?
procedure Form1.TimerTimer1(Sender: TObject);
begin
ShowMessage("Hi");
end;
If I run this event in after 24 hours, I fear it might pass 9 AM and will not fire.
Unless you have other valid reasons, it's far more easier to
write a simple application that shows the message and quits
schedule it to run using the task scheduler of Windows
You can use CRON like solutions for Delphi: http://www.cromis.net/blog/downloads/cron-scheduler/
As the answerers before me said, there are better and easier ways. Suppose you want to do this your way, in Delphi, then yes, you need a timer. The needed steps are:
Put the timer on your form;
Set the "interval" property to 1000 (one second) or less. For greater precision, you can set the interval property to 1, and the program will do the checking each milisecond
Write the handler for OnTimer:
procedure Form1.TimerTimer1(Sender: TObject);
var x:TDateTime
begin
x:=Now;
if {the hour read is 9 and minute is 0} then
ShowMessage("Hi");
end;
Hope it helps.

Procedure Send Mail Alert

I have a procedure which sends emails and this procedure is being called from other functions and procedures (primarily used for sending alerts and notifications).
One issue I face is if our mail server is down, then calling function or procedure stops execution, I mean they do not do any functionality which they are supposed to do. How can I make sure that calling function or procedure or for that matter any client which calls
MailProcedure should do its functionality even when mail server is down.
How can I achieve this?
Any help is highly appreciable.
Mail Procedure
CREATE OR REPLACE PROCEDURE MailProcedure(frm_id IN VARCHAR2, to_id IN VARCHAR2, subject IN VARCHAR2, body_text IN VARCHAR2)
AS
c utl_tcp.connection;
rc integer;
BEGIN
c := utl_tcp.open_connection('email_server', 25);
rc := utl_tcp.write_line(c, 'string');
rc := utl_tcp.write_line(c, 'from address');
rc := utl_tcp.write_line(c, 'to address');
rc := utl_tcp.write_line(c, 'Subject');
rc := utl_tcp.write_line(c, 'body');
utl_tcp.close_connection(c);
EXCEPTION
WHEN OTHERS
THEN
null;
END;
/
Since you want to queue the email for later delivery, the simplest option is to send all email messages asynchronously. Your other procedures would call a QueueMail procedure that inserts a row into the new mail_queue table
CREATE OR REPLACE PROCEDURE QueueMail(p_from IN VARCHAR2,
p_to IN VARCHAR2,
p_subject IN VARCHAR2,
p_body IN VARCHAR2)
AS
BEGIN
INSERT INTO mail_queue( mail_queue_id. from, to, subject, body )
VALUES( mail_queue_seq.nextval, p_from, p_to, p_subject, p_body );
END;
You would then have a separate procedure that runs in a separate thread that actually sense the emails and removes the messages from the queue. Something like
CREATE OR REPLACE PROCEDURE SendQueuedMessages
AS
BEGIN
FOR msg IN (SELECT * FROM mail_queue )
LOOP
sendMessage( msg.from, msg.to, msg.subject, msg.body );
DELETE FROM mail_queue
WHERE mail_queue_id = msg.mail_queue_id;
commit;
END LOOP;
END;
where sendMessage implements the actual logic for sending an email. I would think that you would want to use either the utl_mail or the utl_smtp package to send email rather than using utl_tcp but, of course, you can use utl_tcp. You would then schedule the SendQueuedMessages procedure using either the dbms_job or the dbms_scheduler package. Something like this
DECLARE
l_jobno PLS_INTEGER;
BEGIN
dbms_job.submit( l_jobno,
'BEGIN SendQueuedMessages; END;',
sysdate + interval '1' minute,
'sysdate + interval ''1'' minute' );
commit;
END;
will create a job that runs the SendQueuedMessages procedure every minute. If the mail server is down, the SendQueuedMessage procedure fails and the job is automatically rescheduled to run later. After the first failure, the job runs again 1 minute later. After the second failure, it runs 2 minutes later, then 4 minutes, 8 minutes, etc. until it fails 16 consecutive times. You can choose something other than the default behavior if you want to catch the exceptions in the SendQueuedMessages procedure. Since job failures cause the failure to be written to the alert log, your DBA may ask you to handle the exceptions and to handle rescheduling the job to avoid unnecessary data being written to the alert log.

DBDateTimePicker Validation

i downloaded a DBDateTimePicker which i am using to edit dates in a table, a start date and completion date. When appending to a table in normal edit boxes i use the following code before the append and post in order to prevent a completion date being set before a start date.
if (DTPAddStartDate.Date) > (DTPAddCompletionDate.Date) then
begin
raise Exception.Create ('The Completion date of a Job can not be before a Start Date');
abort;
end;
The problem is that i want to be able to achieve the same thing when editing dates via the DBDateTimePickers, if i have similar code on the BeforePost event or something then i will get premature error messages as the user may not have had a chance to edit the other related DBDateTimePicker. I have no idea how i could relate the two fields so that i can validate them together somehow or come up with another solution to the problem, any suggestions?
(Im using an adotable connected to access and the component was downloaded from the following link, http://apollosoft.net/jms/).
I guess you are using the TDBDateTimePicker from this page but hard to say, it's missing in your question at this time.
However you may use the field's OnValidate event which is being fired whenever the value is changed, but it's the stage when the data are going to be written do the database, so it's IMHO the unnecessary wasting of time.
You can validate the values before they tries to update the record, what is in the case of (almost ?)every DB aware control when you try to exit the control, so you may pretty easily add some validation event, something like OnCanUpdate to your date time picker.
Because of missing link to the source where did you get your component I'll assume you are using this TDBDateTimePicker. If you add to the DBDateTimePicker.pas file the following lines and rebuild your package where is installed, the new OnCanUpdate event will appear in the component's Object Inspector. This event if you use it has one important parameter Allowed, if you set it to True you will allow the component to update the record, if you set it to False (what is default) no data will be updated in your dataset. So it's the place where you can do the validation and decide you want to update record or not.
type
TDBDateTimePicker = class;
TOnCanUpdate = procedure(Sender: TDBDateTimePicker;
var Allowed: Boolean) of object;
TDBDateTimePicker = class(TDateTimePicker)
private
FOnCanUpdate: TOnCanUpdate;
procedure CMExit(var Message: TCMExit); message CM_EXIT;
published
property OnCanUpdate: TOnCanUpdate read FOnCanUpdate write FOnCanUpdate;
end;
procedure TDBDateTimePicker.CMExit(var Message: TCMExit);
var
Allowed: Boolean;
begin
if Assigned(FOnCanUpdate) then
begin
Allowed := False;
FOnCanUpdate(Self, Allowed);
if not Allowed then
begin
SetFocused(True);
Exit;
end;
end;
try
FDataLink.UpdateRecord;
except
SetFocus;
raise;
end;
SetFocused(False);
inherited;
end;
So the code in the OnCanUpdate event may looks like this (note you can use this event as common for both of your date time pickers).
procedure TForm1.DTPAddStartDateCanUpdate(Sender: TDBDateTimePicker;
var Allowed: Boolean);
begin
if (DTPAddStartDate.Date) > (DTPAddCompletionDate.Date) then
begin
// the Allowed parameter is in current code initialized to False
// when it comes into this event, so the following line has no
// sense here
Allowed := False;
// here you can display the error message or raise exception or just
// whatever you want, the record won't be modified in any way
Application.MessageBox('The completion date of a job cannot be before a ' +
'start date. The record won''t be modified ;-)', 'Date Input Error...',
MB_YESNO + MB_ICONSTOP + MB_TOPMOST);
end
else
// only setting the Allowed to True will actually allows the record to be
// updated, so if your validation passes, set the Allowed to True
Allowed := True;
end;
Caviate:
A shot in the dark here since the component isn't familiar. If you have the components in a form then the objects are member variables of the form. Assuming they are created when the form is initialized (you're using them so they are) simple check an event when they lose focus.
//Pseudo Code--I'm sure it is using a standard event model
myForm.DTPAddCompletionDate.OnChange(...)
begin
//do your check if it is successful something like...
if not (DTPAddStartDate.Date > DTPAddCompletionDate.Date) then
return;
else
someSubmitButton.enabled = true;
end;
I don't have Delphi in front of me and it has been years so the code probably won't match up perfectly. However, the idea should work. Just check the references.

How to wait for COM port receive event before sending more data in a loop

I'm working on a small component for writing and reading AT Commands using an old Sony Ericsson phone.
Sending and writing to/from the phone is no problem at all, however I would like to be able to pause my SendATCmd function and wait for the COM Port component to notify me with a Notification Event, and then resume the SendATCmd function again.
Scenario: I want to get the count of SMS messages in the phone.
Normally I'd just tell the phone: Hey, how many SMS messages do you have?
and the phone would reply in the notification event.
Thats all good.
But what I really want to do is something like
if SendATCmd('CountSMS')>0 then
for 0 to SMSCount do
AddSMSToList;
The code for SendATCmd looks like this:
function TSE_Z1010.SendATCmd(Cmd: string): TATResult;
begin
fCOMPort.PutString(Cmd); //Sending AT command
//Here is where I would like to pause this function
//wait for the fCOMPort to notify me when data is available
//and then resume this function again.
result:=fTMPATResult;
end;
I've tried using a while-loop, pause, etc etc, but nothing's worked except for one thing, and that's when I put a ShowMessage where the pause should be.
I don't know how ShowMessage works internally but it seems that it doesn't halt the program like while-loop and pause do.
====================
Fixed it.
All I had to do was to add Forms in the uses clause, and then I added while fTMPATResult.Full=false do Application.ProcessMessages; in the part where I wanted to pause the procedure.
"fTMPATResult" is the variable where the incoming COM Port data is stored, globally within the component.
While AsyncPro does have some solutions for this (ontriggerdata), they are event based and make code difficult to read/understand.
here is SendAndWaitForResponse with AsyncPro (like Remy suggested):
TForm1 = class(TForm)
...
private
IOEvent : THandle; // used for IO events
IORx : string;
Comport : TapdComport;
...
procedure TForm1.ComportTriggerAvail(CP: TObject; Count: Word);
var i : integer;
begin
for i:=1 to Count do
IORx:=IORx+Comport.GetChar;
SetEvent(IOEvent);
end;
function TForm1.SerialSAWR(tx : string; TimeOut : integer) : boolean;
begin
Result := False;
try
IORx := ''; // your global var
ResetEvent(IOEvent);
Comport.PutString(tx);
Result := WaitForSingleObject(IOEvent, TimeOut) = WAIT_OBJECT_0;
except
on E : Exception do
// dosomething with exception
end;
end;
// constructor part
IOEvent := CreateEvent(nil, True, False, nil);
// destructor part
if IOEvent <> 0 then
CloseHandle(IOEvent);
Best solution is to create a thread with the comport so your GUI won't be blocked.
I have several applications in production with Asyncpro this way and it works like a charm...
Any time you need to call Application.ProcessMessages() manually, you need to rethink your code design. Doubly so when calling it in a loop.
I do not know how Asynch Pro works, but the Win32 API has a WaitCommEvent() function that does what you are asking for. You call that function to ask the serial port for notification of the desired event(s) and then you can use either WaitForOverlappedResult() or WaitForSingleObject() to wait for those events to actually occur, depending on whether the serial port is operating in overlapped mode or not. No message processing is needed. I would be surprised if Asynch Pro does not somehow expose that functionality.

Resources