dilettante
02-21-2008, 07:41 PM
I have a situation where I'll have a set of programs that will receive, manipulate, and send messages via TCP that are comprised of a series of "blocks" of characters. Each block is of a fixed size, each message has a varying number of blocks, and the blocks might be in any position:
00 01 05 05 05 32 41 79
00 01 05 32 41 79
00 01 05 05 32 41 79
Each "type" of block has a block type number that defines its layout. There is always a 00 block that contains the "block count" of a particular message, so I have no problem knowing when I have a complete message. The 00 block also defines the message's "application" in a field. This is important because each "application" requires unique processing, and also blocks beyond 00 and 01 have a different set of fields (layout) for each application.
Each block type has its own set of fields defined (varying by application), and in most cases I need to manipulate these fields' contents. I receive these messages and I create new ones to send back.
My question is:
What is a good (best?) way to handle processing this data?
One obvious way might be to define a VB Type (UDT) for each possible layout. The drawbacks seem to be:
Lots of shuffling data into and out of instances of these Types, via LSet or CopyMemory.
The general problem with visibility of VB Types: You basically end up needing to define them as Public in a separate DLL to make them Public and passable to Classes, etc.
What I'm playing with:
Define a BlockedMessage Class. Store the message into this as a String value. Operate on the fields using Mid$() on the left and right sides of the assignment operator (=). Define the fields via a Public Enum where each value is a composite value containing:
Field offset within its block.
Field length in characters.
Numeric flag for the values that are numeric instead of alphanumeric.
Then use a default property "Field" that accepts:
A "field descriptor" value from that Enum.
A "block offset" from the start of the message String value.
An "occurrence" value for fields that are really "arrays" (yes, fun hmm?).
The result yields a syntax that isn't too clumsy:
Option Explicit
Private Sub Command1_Click()
Dim Msg As New BlockedMessage
Dim intBlock As Integer
Msg.Clear Blocks:=2
'Set up MC block.
Msg(MC_TRAN) = "XXXX"
Msg(MC_SOURCEID) = "ABC"
Msg(MC_DESTID) = "WXYZ"
Msg(MC_BCOUNT_9) = 2
Msg(MC_DATESENT) = Format$(Date, "YYYYMMDD")
Msg(MC_TIMESENT) = Format$(Time, "HHNNSS")
Msg(MC_ECHO) = "HELLO"
'Set up MV block.
For intBlock = BN_min To BN_max
Msg(MV_LOCATION, 1, intBlock) = 0
Msg(MV_COUNT, 1, intBlock) = 0
Next
Msg(MV_LOCATION, 1, BN_MC) = 0
Msg(MV_COUNT, 1, BN_MC) = 1
Msg(MV_LOCATION, 1, BN_MV) = 1
Msg(MV_COUNT, 1, BN_MV) = 1
MsgBox Replace$(Msg.Content, " ", "+")
End Sub
It seems to be working well, and I doubt it is horribly inefficient compared to alternatives. It is certainly better to work with than hand-coding all of the Mid$() and field-padding logic inline everywhere.
I have included my prototype program as well as a text file containing extremely abbreviated specs. The specs I provided may help illustrate why I need such a convoluted process.
In real programs there will be several hundred block and block field definitions. I'm wondering if I'll bump into an Enum member list limit. I'm also wondering if there isn't a better way to approach it before I take it much further.
00 01 05 05 05 32 41 79
00 01 05 32 41 79
00 01 05 05 32 41 79
Each "type" of block has a block type number that defines its layout. There is always a 00 block that contains the "block count" of a particular message, so I have no problem knowing when I have a complete message. The 00 block also defines the message's "application" in a field. This is important because each "application" requires unique processing, and also blocks beyond 00 and 01 have a different set of fields (layout) for each application.
Each block type has its own set of fields defined (varying by application), and in most cases I need to manipulate these fields' contents. I receive these messages and I create new ones to send back.
My question is:
What is a good (best?) way to handle processing this data?
One obvious way might be to define a VB Type (UDT) for each possible layout. The drawbacks seem to be:
Lots of shuffling data into and out of instances of these Types, via LSet or CopyMemory.
The general problem with visibility of VB Types: You basically end up needing to define them as Public in a separate DLL to make them Public and passable to Classes, etc.
What I'm playing with:
Define a BlockedMessage Class. Store the message into this as a String value. Operate on the fields using Mid$() on the left and right sides of the assignment operator (=). Define the fields via a Public Enum where each value is a composite value containing:
Field offset within its block.
Field length in characters.
Numeric flag for the values that are numeric instead of alphanumeric.
Then use a default property "Field" that accepts:
A "field descriptor" value from that Enum.
A "block offset" from the start of the message String value.
An "occurrence" value for fields that are really "arrays" (yes, fun hmm?).
The result yields a syntax that isn't too clumsy:
Option Explicit
Private Sub Command1_Click()
Dim Msg As New BlockedMessage
Dim intBlock As Integer
Msg.Clear Blocks:=2
'Set up MC block.
Msg(MC_TRAN) = "XXXX"
Msg(MC_SOURCEID) = "ABC"
Msg(MC_DESTID) = "WXYZ"
Msg(MC_BCOUNT_9) = 2
Msg(MC_DATESENT) = Format$(Date, "YYYYMMDD")
Msg(MC_TIMESENT) = Format$(Time, "HHNNSS")
Msg(MC_ECHO) = "HELLO"
'Set up MV block.
For intBlock = BN_min To BN_max
Msg(MV_LOCATION, 1, intBlock) = 0
Msg(MV_COUNT, 1, intBlock) = 0
Next
Msg(MV_LOCATION, 1, BN_MC) = 0
Msg(MV_COUNT, 1, BN_MC) = 1
Msg(MV_LOCATION, 1, BN_MV) = 1
Msg(MV_COUNT, 1, BN_MV) = 1
MsgBox Replace$(Msg.Content, " ", "+")
End Sub
It seems to be working well, and I doubt it is horribly inefficient compared to alternatives. It is certainly better to work with than hand-coding all of the Mid$() and field-padding logic inline everywhere.
I have included my prototype program as well as a text file containing extremely abbreviated specs. The specs I provided may help illustrate why I need such a convoluted process.
In real programs there will be several hundred block and block field definitions. I'm wondering if I'll bump into an Enum member list limit. I'm also wondering if there isn't a better way to approach it before I take it much further.