Host Engineering Forum
General Category => Do-more CPUs and Do-more Designer Software => Topic started by: Mike Nash on September 20, 2017, 06:49:34 PM
-
I just finished reading almost every word of the update pdf on the v2.1 changes. That has certainly been a lot of work Host has accomplished. I am sure I will need to re-read it several times to benefit the most from it. I wasn't even aware of some of the issues that were fixed.
I'm really happy I hadn't yet tried to get all my menus rearranged after having to load/reload Windows on 3 different computers. This update came out just in time for me on that.
I have a question regarding the UDT Structure vs heap-item in the recipe example of the pdf. I see that the RecipeStruct becomes the structure for the CurrentRecipe heap item, but I don't think I fully get the difference. I can address the RecipeStruct structures directly via RecipeStruct1.itemname or indirectly via RecipeStruct[V100].itemname so the heap-item benefits me how? I'm not sure I am seeing the benefit other than no array designation.
And this is my first Windows 10 computer ever that I am typing this on. Oddly, I find it less annoying than Win7.
-
I have a question regarding the UDT Structure vs heap-item in the recipe example of the pdf. I see that the RecipeStruct becomes the structure for the CurrentRecipe heap item, but I don't think I fully get the difference. I can address the RecipeStruct structures directly via RecipeStruct1.itemname or indirectly via RecipeStruct[V100].itemname so the heap-item benefits me how? I'm not sure I am seeing the benefit other than no array designation.
Let's look at it using a built-in structure, PID. I can create a block of 16 PID Structs, called MyPID. I can address MyPID0..MyPID15 in a PID instruction. I can even use a FOR/NEXT loop with V0 0..15 with PID instruction with MyPID[V0] (this is how we tested 1000 PID loops without needing 1000 PID instructions - HA!)
I can also create PID heap-items. CookerABC. ChillerXYZ.
I can look at MyPID4.PV or MyPID[V100].PV or CookerABC.PV or ChillerXYZ.PV.
I did all this with the structure definition PID Struct. 1 data block of 16, and 2 heap-items. I could actually create another data-block of 100 PIDs called MyOtherPID, and now I have MyOtherPID0..MyOtherPID99, in addition to the other block of 16 and 2 heap-items.
Back to RecipeStruct. I defined a User Data Type structure definition called RecipeStruct. That just defines the data-type, not a data-block or heap-item. I can then create a data-block of 100 of them called Recipe, so I have Recipe0..Recipe99. But I then also create a heap-item of a RecipeStruct called CurrentRecipe. This heap-item is what my program actually uses for control. So, at the beginning of the batch, I use V42 as my recipe selector, where V42 equals 0..99. Say I picked recipe #11 (so my HMI stuck 11 in V42). I then MEMCOPY RecipeStruct[V42] into heap-item CurrentRecipe. After the MEMCOPY, CurrentRecipe contains the values that were in Recipe11. CurrentRecipe.CookTime is what I use in my TMR.
Heap-Items are just single instance (non-ID, non-array) structures. Program Structs, Task Structs, PID Structs, RampSoak Structs and many system structures are typically heap-items. But you could create a data-block of PID structs or RampSoak structs, or even Program Struct or Task Struct data-blocks.
Sometimes you want a block of 100. Sometimes just a bunch of single ones. But you can do both, or even multiples of both (e.g. have internal subroutine structure names called MySubRecipe, then pass in the CALL Recipe11 into MySubRecipe heap-item, then the Subroutine MySub uses MySubRecipe to generate a STRING describing that recipe into MySubString that is a STRING heap-item, and then move MySubString into SS100 in the CALL Output parameter list.
This is advanced stuff, but heap-items are good for well-named single instance data items. Data-blocks are good for having a block of something, like Timers or Counters, or especially where you want to do array indexing, like Recipe[V42].
-
Probably should also offer some background. The primary justification for creating the heap was to give the system a place to allocate the potentially large number of singleton structures that go along with devices. We could have used block memory, but architecturally we are limited to 256 blocks, less really. So we created a new space optimized for storing singletons, and we called it heap because that's what it's commonly called in high level languages. We could have just as easily called them tags...because they are effectively the same.
So now we have two areas for allocation...what goes where? Simple types always go to block memory (can't put simple types on the heap), and system allocated structures always go to heap (that's what we made it for). User allocated structures (using built in types or user created types) can go either place. If the structure is independent and you don't need array indexing, use the heap. If you have a collection of more than one, and you need to index them, use block memory.
-
Thanks gentlemen. After I posted I started thinking about the built-in structures and how we get to use those. I think what maybe threw me was the choice after filling in the info for the UDT of creating a memory block (array) or a structure (single) or neither. I picked memory block the first time and that of course stuck in my brain because it allowed me to have VFD1, VFD2, etc., which was one of the things I was wishing I had in prior versions.
Ironically, my current project isn't going to benefit from these great new features. :(
But I sure like being able to add a range in documentation such as V1:0 to V3:15. Maybe next release we will see MyUDT1:Bicycle to MyUDT3:Airplane. Yeah, I did try something similar and I wasn't even sure how I would enter something like that so I understand why you didn't add that just yet.
DEBOUNCE is a great new feature, as well as FLASHER. Very clean and intuitive.
-
Another project to do and I want to try one more time to see if I can have a single UDT that can have both status (inputs) and command (outputs) type fields. I wish I could wait until Do-more can handle this more elegantly for me, but I need this before December anyway.
A MEMCOPY requires ALL of the UDT DWORDS to be copied at once. But I have ten words (5 DWORDS worth) of data being read and 10 being written. I want to read the ten inputs into, lets say, N0-N9 and write N10-N19 in the MRX and MWX instructions.
SO:
MRX N0-N9
MEMCOPY N0-N19 to MyHeapItem (really want only N0-N9)
MEMCOPY MyHeapItem to N0-N19 (really want only N10-N19)
MWX N10-N19
isn't going to work as I'll be overwriting innies with outies and vice-versa. The alternative I see is writing every element individually, which would be hideous, especially for more than a single device.
Does anyone see a way to MASK, or AND or something to get the desired result? I can do it by having A MyUDTinnies and MyUDTouties and then MyHeapItemsInnies and MyHeapItemOuties (too long I know) but that means two heap items for each Modbus device.
Further, that single UDT is 80 fieldnames since the first two 16 bit words are mapped to 32 bits. The other eight 16 bit words are a mix of signed and unsigned data, then repeat for the outs.
"MBTCP" Structure Memory Layout (10 total DWORDs in length, DWORD Offset 0..9)
+------++----------------------------------------------------------------------------------------------------------------------------------++
| || Most Significant Least Significant ||
| ++----------------------------------------------------------------------------------------------------------------------------------++
| || DWORD ||
| ++----------------------------------------------------------------++----------------------------------------------------------------++
| || WORD 1 || WORD 0 ||
| ++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
| || BYTE 3 || BYTE 2 || BYTE 1 || BYTE 0 ||
|DWORD ++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|Offset||b31|b30|b29|b28|b27|b26|b25|b24||b23|b22|b21|b20|b19|b18|b17|b16||b15|b14|b13|b12|b11|b10| b9| b8|| b7| b6| b5| b4| b3| b2| b1| b0||
+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
Note: Every Bit Field layout only shows the Field # (no name). See the list of Bit Field Names listed by Field # at the end.
+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
| 0 || 32| 31| 30| 29| 28| 27| 26| 25|| 24| 23| 22| 21| 20| 19| 18| 17|| 16| 15| 14| 13| 12| 11| 10| 9|| 8| 7| 6| 5| 4| 3| 2| 1||
+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
| 1 || 34 .CurFbkPct SWORD || 33 .SpdFbkPct SWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++
| 2 || 36 .HealthStore UWORD || 35 .HealthWord UWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++
| 3 || 38 .UserValIn6 SWORD || 37 .LastAlarm UWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++
| 4 || 40 .ArmAmps SWORD || 39 .UserValIn7 SWORD ||
+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
| 5 || 72| 71| 70| 69| 68| 67| 66| 65|| 64| 63| 62| 61| 60| 59| 58| 57|| 56| 55| 54| 53| 52| 51| 50| 49|| 48| 47| 46| 45| 44| 43| 42| 41||
+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
| 6 || 74 .miniLinkVal2 SWORD || 73 .miniLinkVal1 SWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++
| 7 || 76 .miniLinkVal4 SWORD || 75 .miniLinkVal3 SWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++
| 8 || 78 .miniLinkVal6 SWORD || 77 .miniLinkVal5 SWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++
| 9 || 80 .miniLinkVal8 SWORD || 79 .miniLinkVal7 SWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++
List of Bit Fields by Field #
#1 (dw 0: 0) .CoastStop
#2 (dw 0: 1) .ProgramStop
#3 (dw 0: 2) .Disabled
#4 (dw 0: 3) .RunDemanded
#5 (dw 0: 4) .JogDemanded
#6 (dw 0: 5) .ReservedBit0I5
#7 (dw 0: 6) .Alarm
#8 (dw 0: 7) .ReservedBit0I7
#9 (dw 0: 8) .Running
#10 (dw 0: 9) .Enabled
#11 (dw 0:10) .ZeroSpeed
#12 (dw 0:11) .Healthy
#13 (dw 0:12) .Ready
#14 (dw 0:13) .ReservedBit0I13
#15 (dw 0:14) .ReservedBit0I14
#16 (dw 0:15) .ReservedBit0I15
#17 (dw 0:16) .Logic1Status
#18 (dw 0:17) .Logic2Status
#19 (dw 0:18) .Logic3Status
#20 (dw 0:19) .Logic4Status
#21 (dw 0:20) .Logic5Status
#22 (dw 0:21) .Logic6Status
#23 (dw 0:22) .FieldEnabled
#24 (dw 0:23) .WatchDogPulseIn
#25 (dw 0:24) .ReservedBit1I8
#26 (dw 0:25) .ReservedBit1I9
#27 (dw 0:26) .ReservedBit1I10
#28 (dw 0:27) .ReservedBit1I11
#29 (dw 0:28) .ReservedBit1I12
#30 (dw 0:29) .ReservedBit1I13
#31 (dw 0:30) .ReservedBit1I14
#32 (dw 0:31) .ReservedBit1I15
#41 (dw 5: 0) .RemEnable
#42 (dw 5: 1) .RemStart
#43 (dw 5: 2) .RemJog
#44 (dw 5: 3) .RemJogMode
#45 (dw 5: 4) .ReservedBit5o4
#46 (dw 5: 5) .ReservedBit5o5
#47 (dw 5: 6) .ReservedBit5o6
#48 (dw 5: 7) .ReservedBit5o7
#49 (dw 5: 8) .RemAlarmAck
#50 (dw 5: 9) .RemNotTrip
#51 (dw 5:10) .ReservedBit5o10
#52 (dw 5:11) .ReservedBit5o11
#53 (dw 5:12) .ReservedBit5o12
#54 (dw 5:13) .ReservedBit5o13
#55 (dw 5:14) .ReservedBit5o14
#56 (dw 5:15) .ReservedBit5o15
#57 (dw 5:16) .miniLINKLogOut1
#58 (dw 5:17) .miniLINKLogOut2
#59 (dw 5:18) .miniLINKLogOut3
#60 (dw 5:19) .miniLINKLogOut4
#61 (dw 5:20) .miniLINKLogOut5
#62 (dw 5:21) .miniLINKLogOut6
#63 (dw 5:22) .TripResetCmd
#64 (dw 5:23) .WatchDogPulseOut
#65 (dw 5:24) .ReservedBit6o8
#66 (dw 5:25) .ReservedBit6o9
#67 (dw 5:26) .ReservedBit6o10
#68 (dw 5:27) .ReservedBit6o11
#69 (dw 5:28) .ReservedBit6o12
#70 (dw 5:29) .ReservedBit6o13
#71 (dw 5:30) .ReservedBit6o14
#72 (dw 5:31) .ReservedBit6o15
-
Create union fields for each DWORD of the struct, then it's just 5 assignments for each direction. If you need to do this more than once, put it in a subroutine.
-
I did not realize I could have multiple fields in the structure referencing the same memory location.
I added back the 16 bit fields using Manual Memory Layout and was able to use a COPY instruction with 10 entries to get N0-N9 only into the structure.
I added 10 DWORD fields using manual and was able to use a COPY with 5 entries to get N0:D to N8:D into the structure. I do see all three structure field types showing the same data (bit, SWORD and DWORD).
Then I tried COPY N0:D to the SWORD and it worked, so I don't need the DWORD fields after all.
Then, duh, I tried COPY N0:D to the first bit type field and that works too. So I don't need the status SWORDs either for the bit fields.
(Edit on 10/26/2017 - This is not true. See my post dated 10/26/2017.
But to be fair to myself, Help pretty much found nothing on Unions. I did find something about it on C compilers on the web. I think my takeaway on this is that I can COPY to or from any DWORD in the structure using any field that lays on the 32 bit boundary starting bit, but also that it is going to be 32 bits copied either way.
It also means I can have a plethora of field names that all work exactly the same if manually setting the memory location. "Machine.Start" or "Machine.GoBabyGo", my choice, either or both. Kinda neat.
And most importantly for me, this looks doable without too much clutter.
But I don't yet see how a subroutine would help here with multiple devices. Is there some way to pass a pointer to the structure if they are individual heap items and not memory block arrays?
-
For now, the subroutine would require a memory block. We will eventually get data types passed as value or reference.
-
I am just not seeing how the subroutine would help here with heap items rather than arrayed memory block.
On previous jobs, arrays would be ideal (VFD1.Start, VFD2.Start, VFD[V0].Start), but I think for this job I might prefer section names. There isn't currently some way to pass a value that would point to a heap item is there? I mean like decoding where MyHeapItem is in memory and then using that in the subroutine to point to MyHeapItem. Just wondering here.
-
Yes, correct. It would require a memory block.
You are describing passing a structure by reference. The controller supports it, but the work to support it at DmD is considerable.
-
Then, duh, I tried COPY N0:D to the first bit type field and that works too.
All that did was copy the 0/non-zero state of N0:D into the first bit type field.
It's basically doing a MOVE N0:D MyStruct.BitA
If N0:D is 0, then the .BitA is FALSE
otherwise, .BitA is TRUE
You will have to do that field by field (using union DWORDs). If you do that in a subroutine (do not pass any parameters, just do it in a subroutine), then the code at the high level looks cleaner
MRX
CALL MySub
MWX
-
Then, duh, I tried COPY N0:D to the first bit type field and that works too.
All that did was copy the 0/non-zero state of N0:D into the first bit type field.
It's basically doing a MOVE N0:D MyStruct.BitA
If N0:D is 0, then the .BitA is FALSE
otherwise, .BitA is TRUE
Right, I think. I did this 5 times in the one COPY instruction to get all 32 BITs and 8 SWORDs copied (as 5 DWORDs). Another COPY does the other direction for the outgoing 32 BITS and 8 SWORDs. It's amazingly hard to keep all this straight when I have to actually write it out as text.
You will have to do that field by field (using union DWORDs). If you do that in a subroutine (do not pass any parameters, just do it in a subroutine), then the code at the high level looks cleaner
MRX
CALL MySub
MWX
Since it is a single COPY instruction after MRX and another for MWX, it's not really bad. The Comms are in their own Program anyway.
But I would like an opinion on using the MRX success bit to trigger the COPY after the MRX. I don't think I can do that for MWX though since I am loading the data in to be sent.
I will have multiple Modbus TCP Client devices configured.
I have this incredible desire to say something about misspelling DWARF, but I won't.
-
But I would like an opinion on using the MRX success bit to trigger the COPY after the MRX. I don't think I can do that for MWX though since I am loading the data in to be sent.
That is a good way to do it, assuming that you also trigger the MRX based on the same MRX OnSuccess bit (or at least that is one of the enabling conditions for the MWX instruction).
MRX
If MRX.OnSuccss then {COPY; MWX;}
-
Thank you both for the help! I am hoping this means I am not doing anything here that might be challenged by future software/firmware updates.
-
Thank you both for the help! I am hoping this means I am not doing anything here that might be challenged by future software/firmware updates.
Not at all.
-
Two steps forward, one step back.
I seem to have misread my test results. COPY N2:D MyStruct.Word2 does not copy N3 into Mystruct.Word3.
So I am stuck with either a qty 2, COPY with 40 items (32 bits and 8 words) or I have to also have fields in the UDT that are DWORDS, for qty 2, COPY with 5 items as I previously thought. My bad.
I'll edit the previous post if I can to point this out.
-
Can we not MEMCOPY in and out of UDT instances to make it type/register-agnostic, just X bits or bytes from location A to location B and let me take care of the mapping?
-
Can we not MEMCOPY in and out of UDT instances to make it type/register-agnostic, just X bits or bytes from location A to location B and let me take care of the mapping?
Yes...and no. He needed half of his struct to be input and half output. There isn't currently a mechanism to do a partial struct or start anywhere but the beginning. The controller can do it now, but we need to do some work on DmD to generate the proper code.
-
Ah, OK. Thanks for the explanation.
-
Two steps forward, one step back.
I seem to have misread my test results. COPY N2:D MyStruct.Word2 does not copy N3 into Mystruct.Word3.
So I am stuck with either a qty 2, COPY with 40 items (32 bits and 8 words) or I have to also have fields in the UDT that are DWORDS, for qty 2, COPY with 5 items as I previously thought. My bad.
I'll edit the previous post if I can to point this out.
You need to use a union structure member that occupies the same DWORD as .Word2 and .Word3, (say .Dword23), then use COPY N2:D to .Dword23
-
franji1,
That is what I was trying to say. I need to add the extra 10 DWORDs to my structure to allow fewer entries in the COPY instructions. The DWORDS simply point to the start of each 32 bit section (and overlap them). Unfortunately, this adds those 10 words to the available elements when I am trying to "build" the element in ladder or data view from the (whatever you call that selection list that opens as you start typing) thingy.
I am conflicted about trying to use the User Structures until they can be slightly more transparent, simply because going back and trying to understand what is happening later might be a chore. Obviously I am wanting to get to the point they function more like built-in structures and the hanging point is the need to move data in and out of the structure for the MRX and MWX or EIPMSG needs. Now if I could simply put structure elements as a pointer into those instructions and how many of what size elements (ignoring the 32 bit field issue by the instruction handling it for me) it would be simply wonderful.
I like it and it does work and it is a work in progress still, but it isn't quite graceful enough for my particular needs just yet. Also, I don't get to pick the form of the structure I need as that is defined by the device data being transferred and the manufacturer chose that already.
-
They can't function any more like built-in structures than they do, because there is literally zero difference. Maybe this is semantics.
You can do everything you are wanting right now (MEMCOPY from array data to/from structure), except split a single structure into input and output sections. Maybe you could just use two different structures?
We will definitely look at what it takes to copy to/from a subset of structure, and we can also look at what it would take to make structures a source and destination for comm instructions.
-
They can't function any more like built-in structures than they do, because there is literally zero difference. Maybe this is semantics.
Quite possibly. Terminology isn't my strong suit.
You can do everything you are wanting right now (MEMCOPY from array data to/from structure), except split a single structure into input and output sections. Maybe you could just use two different structures?
I considered it, and might again, but then there are those elements that ought to be included but would break the model again. For example, some type of value associated with the structure, but not part of either the in or out world, for a Modbus device, such as a status word, or bit. So now there are three structures, which is probably OK anyway, but feels kinda loose. :-\
We will definitely look at what it takes to copy to/from a subset of structure, and we can also look at what it would take to make structures a source and destination for comm instructions.
Now these would really make me smile! :) Bigger! I mean smile bigger! ;D
-
I'm trying to use some User Data Types for some TCP communications.
I would like to have my structure somewhat as follows:
.Command - unsigned word
.Response - unsigned word
.ACKResp - unsigned word
.NAKResp - unsigned word
.Success - bit
.Fail - bit
.SuccDate1...9 - signed d words
.FailDate1...9 - signed d words
I can set it all up like I think it should work, but cannot use the .Command and .Response as strings for my STREAMOUT/IN.
Also, is there an easy way to block copy the .SuccDate1-8 to .SuccDate2-9 and then $Now to .SuccDate1
Am I missing something or are both of these the nature of the beast?
-
I can set it all up like I think it should work, but cannot use the .Command and .Response as strings for my STREAMOUT/IN.
It has to be from a data-block of a numeric type. Sorry. You will have to do a MOVE or COPY to/from the data-block element to the structure members.
Also, is there an easy way to block copy the .SuccDate1-8 to .SuccDate2-9 and then $Now to .SuccDate1
No. But you can do it in the new COPY instruction pretty easily (see attached - I only did the first 3 and the last two, using DST22, the local time 1970 epoch value in a DST)
-
It has to be from a data-block of a numeric type. Sorry. You will have to do a MOVE or COPY to/from the data-block element to the structure members.
I have tried that, but it tells me they don't match, one is numeric, the other structure. Is there a way to setup my .Command, etc as a string structure type?
-
At the bottom is the clip I stubbed out, but it's basically a PROGRAM code-block called DoTransaction that moves the .Command and .Response members into/out of an OutBuff and InBuff data blocks before it does the STREAMOUT and STREAMIN.
Look at the attachment for the .PDF of the Ladder Logic where the STREAMOUT and STREAMIN are done. Note how in Rung #2 I move the structure's .Command WORD to OutBuff0:UW, then I use 2 BYTES of OutBuff0 in my STREAMOUT. Similarly, in the corresponding STREAMIN instruction I use 2 BYTEs of InBuff0, then in rung #44 I move those 2 BYTEs of OutBuff0:UW to the structure's .Response WORD. InBuff and OutBuff are both BYTE blocks, each 8 bytes long, and that is what I use in the STREAMIN and STREAMOUT instructions (since I can't use the structure members).
From $Main, just RUN DoTransaction, then look for DoTransaction.Done then check MyTCP.Success vs. MyTCP.Fail.
// Ver(103) PCU(4) Do-more Designer Clip Source Project: C:\Do-more\Designer2_1\Projects\TCPProtoStruct1 Format: Do-more Designer 2.00+ DO-MORE Ladder
PLC BX-DM1E-18ED13
$PRGRM DoTransaction
SG DoTransaction.S0
RST MyTCP.Success
RST MyTCP.Fail
MOVE MyTCP.Command OutBuff0:W
OPENTCP @MyTCPClient -1062666239 12345 0x101 DoTransaction.S1 DoTransaction.S99
SG DoTransaction.S1
STREAMOUT @MyTCPClient 0x1 SS0 OutBuff0 2 0x100 0x101 DoTransaction.S2 DoTransaction.S99
SG DoTransaction.S2
STREAMIN @MyTCPClient 2 0x0 1000 0x1 SS0 InBuff0 2 D100 0x0 0x101 DoTransaction.S3 DoTransaction.S99
SG DoTransaction.S3
MOVE InBuff0:W MyTCP.Response
SET MyTCP.Success
EXIT
SG DoTransaction.S99
SET MyTCP.Fail
EXIT
$PGMEND DoTransaction
#BEGIN DEVICE
@MyTCPClient, 32769, 8
#END
#BEGIN UDT_CONFIG
TCPProtoStruct
// Field Name, Data Type, Memory Layout, Read/Write, Display Format, Data View Detail
Command, UWORD, 0:0, Read-Write, Native, Short
Response, UWORD, 0:1, Read-Write, Native, Short
ACKResp, UWORD, 1:0, Read-Write, Native, Short
NAKResp, UWORD, 1:1, Read-Write, Native, Short
Success, BIT, 2:0, Read-Write, Native, Short
Fail, BIT, 2:1, Read-Write, Native, Short
#END
#BEGIN MEM_CONFIG
InBuff UBYTE decimal 8
OutBuff UBYTE decimal 8
MyTCP TCPProtoStruct 0
DoTransaction PROGRAM 0 -1
#END
-
Thank you, franji1. I haven't had much time to play with that, but it seems like something I can make work with this newfound knowledge. I can really clean up multiple programs that do similar stream out and in commands, while still maintaining some structure to all the element involved. Thanks again for pointing me in the right direction.