From: albertleng on
Hi all. I'm writing a program which is connected to Fire Alarm System
via serial port. The Fire Alarm System will send me data like shown
below.

COMMON TRBL RST :: 22:18:50 16/06/2010 P:06 C:02 D:0137
Z6-4-9_BV_CLOSED 4S Corridor

COMMON TRBL ACT :: 22:18:50 16/06/2010 P:06 C:02 D:0137
Z6-4-9_BV_CLOSED 4S Corridor

My program needs to send "0" or "1" via DDE to another program based
on the FAS message. Referring to the above message,
"COMMON TRBL RST" is the message to reset an alarm, "COMMON TRBL ACT"
is the message to activate an alarm and "D:0137" is the locationID.
My program needs to send the status via DDE based on the "... RST" or
"...ACT" message and the locationID.

Upon startup, my program will read from a config file for the
available ACT message, RST message and a list of locationIDs.

I am encountering a few issues:
1) In my MSCOMM1_OnComm event, using breakpoint, i notice that my
program always receives just the portion of the FAS message, for e.g.,
I'll receive only "COM" at one time and then, I press F5 to carry on.
Next time, I'll receive another portion of the whole chunk like "MON
TRBL RST... ...". Because of this, i can't capture any alarm ACT or
alarm RST at all. How can i ensure i don't lose any data from FAS?

2) If I can solve the first issue, please suggest to me the most
efficient way to process the chunk of data received by my program.

Below are my code. Please help.
Private Sub MSComm1_OnComm()
Static strBuffer As String
Dim strData As String
Dim normalLocation1 As Integer
Dim alarmLocation1 As Integer
Dim normalLocation2 As Integer
Dim alarmLocation2 As Integer
Dim normalLocation3 As Integer
Dim alarmLocation3 As Integer
Dim normalLocation As Integer
Dim alarmLocation As Integer
Dim normalMsg As String
Dim alarmMsg As String
Dim finalNormalLocation As Integer
Dim finalAlarmLocation As Integer
Dim pointIndex As Integer
Dim i As Integer
Dim j As Integer



Select Case MSComm1.CommEvent
Case comEvReceive
strData = MSComm1.Input
strBuffer = strBuffer & strData

tTerminal.Text = tTerminal.Text & strBuffer

//This is the part where i put breakpoint and notice that my program
keeps receiving only part of the FAS message.
If Len(tTerminal.Text) > (Len(normalMsg1) + 1) Then

//normalMsg1, normalMsg2, normalMsg3, alarmMsg1, alarmMsg2 and
alarmMsg3 are the possible Alarm ACT and RST messages
//defined in the config files.
For i = 1 To Len(tTerminal.Text)
normalLocation1 = InStr(i, Mid$(tTerminal.Text, i,
Len(normalMsg1) + 1), normalMsg1)
alarmLocation1 = InStr(i, Mid$(tTerminal.Text, i,
Len(alarmMsg1) + 1), alarmMsg1)

If normalMsg2 <> "" Then
normalLocation2 = InStr(i, Mid$(tTerminal.Text, i,
Len(normalMsg2) + 1), normalMsg2)
Else
normalLocation2 = 0
End If

If alarmMsg2 <> "" Then
alarmLocation2 = InStr(i, Mid$(tTerminal.Text, i,
Len(alarmMsg2) + 1), alarmMsg2)
Else
alarmLocation2 = 0
End If

If normalMsg3 <> "" Then
normalLocation3 = InStr(i, Mid$(tTerminal.Text, i,
Len(normalMsg3) + 1), normalMsg3)
Else
normalLocation3 = 0
End If

If alarmMsg3 <> "" Then
alarmLocation3 = InStr(i, Mid$(tTerminal.Text, i,
Len(alarmMsg3) + 1), alarmMsg3)
Else
alarmLocation3 = 0
End If


//FindSmallest is a method i wrote to find the smallest number of
the 3 numbers
normalLocation = FindSmallest(normalLocation1,
normalLocation2, normalLocation3)

If normalLocation <> 0 Then
If normalLocation1 = normalLocation Then
normalMsg = normalMsg1
ElseIf normalLocation2 = normalLocation Then
normalMsg = normalMsg2
ElseIf normalLocation3 = normalLocation Then
normalMsg = normalMsg3
End If
End If

alarmLocation = FindSmallest(alarmLocation1,
alarmLocation2, alarmLocation3)
If alarmLocation <> 0 Then
If alarmLocation1 = alarmLocation Then
alarmMsg = alarmMsg1
ElseIf alarmLocation2 = alarmLocation Then
alarmMsg = alarmMsg2
ElseIf alarmLocation3 = alarmLocation Then
alarmMsg = alarmMsg3
End If
End If

//totalPoint is the total venues/LocationIDs
For j = 1 To totalPoint
If alarmLocation <> 0 Then
//Send 1 via DDE to a program if point j is an alarm
If InStr(strBuffer, PointAddress(j)) > 0 Then
txtPoint(j).Text = "1"
txtPoint(j).LinkMode = vbLinkManual
txtPoint(j).LinkPoke
End If
ElseIf normalLocation <> 0 Then
//Send 0 via DDE to a program if point j is an
alarm
If InStr(strBuffer, PointAddress(j)) > 0 Then
txtPoint(j).Text = "0"
txtPoint(j).LinkMode = vbLinkManual
txtPoint(j).LinkPoke
End If
Else
Exit For
End If
Next j
Next i
tTerminal.Text = ""
strBuffer = ""
End If
End Select


End Sub
From: Steve on
I have always done this with a receive buffer. The idea is to simply
"pluck" data (characters) from the com port as they come in whilest all the
while looking for the character (or group of characters) that indicate end
of message. Once you have received the end of message marker your buffer
now should contain the entire message. Only then, with the complete
message, do you attempt to parse it and perform the appropriate logic.

Here is an air code example to illustrate

Private Sub MSCOMM1_OnComm()
Dim strData As String
Static strBuffer As String

'Change this to whatever is the marker for end of message
Const END_OF_MSG As String = vbCrLf

If MSComm1.CommEvent = comEvReceive Then
'Retrieve characters from com port
strData = MSComm1.Input

'Test if characters just retrieved contain end of message marker
If InStr(strData, END_OF_MSG) > 0 Then
'This has the end of message somewhere in so we have to find it
strBuffer = strBuffer & Left(strData, InStr(strData, END_OF_MSG)
+ Len(END_OF_MSG))

'Now that we have a complete message we send it off to be
processed
ProcessMsg strBuffer

'Now that the message has been processed we start over looking
for a new message
strBuffer = Mid(strData, InStr(strData, END_OF_MSG) +
Len(END_OF_MSG))
Else
'We have not recieved the end of the message yet so just add
this data to our buffer
strBuffer = strBuffer & strData
End If
End If

End Sub

Private Sub ProcessMsg()
'Do your message parsing and handling logic here
End Sub

Hope this helps,
Steve

From: Dee Earley on
On 16/06/2010 20:52, Steve wrote:
> I have always done this with a receive buffer. The idea is to simply
> "pluck" data (characters) from the com port as they come in whilest all
> the while looking for the character (or group of characters) that
> indicate end of message. Once you have received the end of message
> marker your buffer now should contain the entire message. Only then,
> with the complete message, do you attempt to parse it and perform the
> appropriate logic.
>
> Here is an air code example to illustrate
>
> Private Sub MSCOMM1_OnComm()
> Dim strData As String
> Static strBuffer As String
>
> 'Change this to whatever is the marker for end of message
> Const END_OF_MSG As String = vbCrLf
>
> If MSComm1.CommEvent = comEvReceive Then
> 'Retrieve characters from com port
> strData = MSComm1.Input
>
> 'Test if characters just retrieved contain end of message marker
> If InStr(strData, END_OF_MSG) > 0 Then
> 'This has the end of message somewhere in so we have to find it
> strBuffer = strBuffer & Left(strData, InStr(strData, END_OF_MSG) +
> Len(END_OF_MSG))
>
> 'Now that we have a complete message we send it off to be processed
> ProcessMsg strBuffer
>
> 'Now that the message has been processed we start over looking for a new
> message
> strBuffer = Mid(strData, InStr(strData, END_OF_MSG) + Len(END_OF_MSG))
> Else
> 'We have not recieved the end of the message yet so just add this data
> to our buffer
> strBuffer = strBuffer & strData
> End If
> End If
>
> End Sub
>
> Private Sub ProcessMsg()
> 'Do your message parsing and handling logic here
> End Sub

Bear in mind that you MAY receive multiple end of lines per call.
This caught me out for a while and I couldn't figure out why it was
backlogged by a number of commands.

Here's a sample that does essentially the same thing with a socket control:
http://hashvb.earlsoft.co.uk/Designing_network_protocols#Receiving_data

--
Dee Earley (dee.earley(a)icode.co.uk)
i-Catcher Development Team

iCode Systems

(Replies direct to my email address will be ignored.
Please reply to the group.)
From: Larry Serflaten on

"albertleng" <albertleng(a)gmail.com> wrote

> 2) If I can solve the first issue, please suggest to me the most
> efficient way to process the chunk of data received by my program.

As you have read, others have suggested that you need to add what you
get from the connection to what has already arrived, and then parse that
for an end of line sequence. eg: (From Mr. Grier's reply)

Buffer = Buffer & MSComm1.Input
CrLfPosition = InStr(Buffer, vbCrLf)
If CrLfPostion > 0 Then
Dim Temp As String
Temp = Mid$(Buffer, 1, CrLfPosition -1)
Process(Temp) 'you write this code<<< - Temp contains a full line
Buffer = Mid$(Buffer, CrLfPosition +1) 'this is important! clean-up required
End If


I wanted to suggest that you avoid processing the input from the OnComm event.
Instead of processing it then and there, add the recieved line to a collection (a queue)
and enable a timer:

...
Temp = Mid$(Buffer, 1, CrLfPosition -1)
RcvQue.Add Temp
ProcessTimer.Enable
Buffer = Mid$(Buffer, CrLfPosition +1) 'this is important! clean-up required
...

When the timer fires, then call your process routine:

Private Sub ProcessTimer_Timer()
If RcvQue.Count > 0 Then
Process RcvQue(1)
RcvQue.Remove 1
Else
ProcessTimer.Enabled = False
End If
End Sub

That way, you get in and get out of the OnComm event quickly, always
a good idea when talking with the outside world.

As far as actually doing the string manipulations and other work, That would
best be handled in another thread, once you get the communication up and
working. It seemed to me you were checking Terminal.Text quite often and
should have really assigned the needed portion to a string for processing.
But first, see if you can get your complete messages working, then post again
to clean up the processing code....

LFS


From: albertleng on
Hi all.

Thank you so much. I think your help has helped me to have a big leap
forward in solving my problem.
I have changed my program accordingly as below:


Private Sub MSComm1_OnComm()
Static Buffer As String
Dim CrLfPosition As Integer

If MSComm1.CommEvent = comEvReceive Then
Buffer = Buffer & MSComm1.Input
Print #LogFNo, Format(Now, "YYYYYYYY-MM-DD hh:mm:ss") & " " &
"Received = " & MSComm1.Input
logFLineNo = logFLineNo + 1

CrLfPosition = InStr(Buffer, vbCrLf)
If CrLfPosition > 0 Then
tTerminal.Text = Buffer
ListMessage.AddItem Format(Now, "YYYYYYYY-MM-DD hh:mm:ss") & "
" & " Received " & Buffer, ListMessage.ListCount
Print #LogFNo, Format(Now, "YYYYYYYY-MM-DD hh:mm:ss") & " " &
"Received = " & MSComm1.Input
logFLineNo = logFLineNo + 1


Dim Temp As String
Temp = Mid$(Buffer, 1, CrLfPosition - 1)
Call ProcessFASMessage(Temp)
Buffer = Mid$(Buffer, CrLfPosition + 1)
End If
End If

End Sub

Private Sub ProcessFASMessage(Temp As String)
Dim j As Integer

'There are 3 alarmMsgs and 3 normalMsgs in this system.
'PointAddress is the device name
If (InStr(Temp, alarmMsg1) > 0) Or (InStr(Temp, alarmMsg2) > 0) Or _
(InStr(Temp, alarmMsg3) > 0) Then
For j = 1 To totalPoint
If InStr(Temp, PointAddress(j)) > 0 Then
txtPoint(j).Text = "1"
txtPoint(j).LinkMode = vbLinkManual
txtPoint(j).LinkPoke
ListMessage.AddItem Format(Now, "YYYYYYYY-MM-DD hh:mm:ss")
& " " & " Send " & txtPoint(j).Text _
& " to " & txtPoint(j).LinkItem, ListMessage.ListCount
Print #LogFNo, Format(Now, "YYYYYYYY-MM-DD hh:mm:ss") & "
" & " Send " & txtPoint(j).Text _
& " to " & txtPoint(j).LinkItem
logFLineNo = logFLineNo + 1
End If
Next j
ElseIf (InStr(Temp, normalMsg1) > 0) Or (InStr(Temp, normalMsg2) > 0)
Or _
(InStr(Temp, normalMsg3) > 0) Then
For j = 1 To totalPoint
If InStr(Temp, PointAddress(j)) > 0 Then
txtPoint(j).Text = "0"
txtPoint(j).LinkMode = vbLinkManual
txtPoint(j).LinkPoke
ListMessage.AddItem Format(Now, "YYYYYYYY-MM-DD hh:mm:ss")
& " " & " Send " & txtPoint(j).Text _
& " to " & txtPoint(j).LinkItem, ListMessage.ListCount
Print #LogFNo, Format(Now, "YYYYYYYY-MM-DD hh:mm:ss") & "
" & " Send " & txtPoint(j).Text _
& " to " & txtPoint(j).LinkItem
logFLineNo = logFLineNo + 1
End If
Next j
End If

End Sub

I don't have access to site at the moment and my testing is done using
virtual serial port downloaded from http://www.eterlogic.com/downloads/SetupVSPE.zip
I'll need my people on site to test it out and let me know the
outcome.

Anyway, in my system, both the normal/alarm message and the device
name are on the same line. How about if normal/alarm message and the
device name are in separate line? How can i effectively process the
message then?

Thanks again, everyone.



On Jun 18, 6:49 am, "Larry Serflaten" <serfla...(a)gmail.com> wrote:
> "albertleng" <albertl...(a)gmail.com> wrote
>
> > 2) If I can solve the first issue, please suggest to me the most
> > efficient way to process the chunk of data received by my program.
>
> As you have read, others have suggested that you need to add what you
> get from the connection to what has already arrived, and then parse that
> for an end of line sequence.  eg: (From Mr. Grier's reply)
>
>     Buffer = Buffer & MSComm1.Input
>     CrLfPosition = InStr(Buffer, vbCrLf)
>     If CrLfPostion > 0 Then
>          Dim Temp As String
>          Temp = Mid$(Buffer, 1, CrLfPosition -1)
>           Process(Temp)   'you write this code<<< - Temp contains a full line
>           Buffer = Mid$(Buffer, CrLfPosition +1)    'this is important! clean-up required
>     End If
>
> I wanted to suggest that you avoid processing the input from the OnComm event.
> Instead of processing it then and there, add the recieved line to a collection (a queue)
> and enable a timer:
>
>           ...
>          Temp = Mid$(Buffer, 1, CrLfPosition -1)
>           RcvQue.Add Temp
>           ProcessTimer.Enable
>           Buffer = Mid$(Buffer, CrLfPosition +1)    'this is important! clean-up required
>           ...
>
> When the timer fires, then call your process routine:
>
> Private Sub ProcessTimer_Timer()
>    If RcvQue.Count > 0 Then
>      Process RcvQue(1)
>      RcvQue.Remove 1
>    Else
>      ProcessTimer.Enabled = False
>    End If
> End Sub
>
> That way, you get in and get out of the OnComm event quickly, always
> a good idea when talking with the outside world.
>
> As far as actually doing the string manipulations and other work, That would
> best be handled in another thread, once you get the communication up and
> working.  It seemed to me you were checking Terminal.Text quite often and
> should have really assigned the needed portion to a string for processing..
> But first, see if you can get your complete messages working, then post again
> to clean up the processing code....
>
> LFS