The goal of this exercise is to read and store an input file into a table then validate certain fields within the input and output any error records. I need to read and store each policy group so that there are just 5 records stored in the table at a time instead of the entire file.
So I need to read in a policy group which is 5 records, do the processing, then read the next 5 records, etc until the end of the file..
This is the input file.
10A 011111 2005062520060625
20A 011111000861038
32A 011111 79372
60A 0111112020 6 4
94A 011111 080 1
10A 02222 2005082520060825
20A 022221000187062
32A 022221 05038
60A 0222212003 6 4
94A 022221 090 1
....
I was able to load the first 5 records into a table by having my table OCCUR 5 TIMES but I don't know how I would continue that. My code is below. (I wrote it just to see if it was working correctly, but it prints the header line with the first 4 records, instead of just the first 5)
01 TABLES.
05 T1-RECORD-TABLE.
10 T1-ENTRY OCCURS 5 TIMES
INDEXED BY T1-INDEX.
15 RECORD-TYPE-10 PIC X(80).
15 RECORD-TYPE-20 PIC X(80).
15 RECORD-TYPE-32 PIC X(80).
15 RECORD-TYPE-60 PIC X(80).
15 RECORD-TYPE-94 PIC X(80).
copy trnrec10.
COPY TRNREC20.
COPY TRNREC32.
COPY TRNREC60.
COPY TRNREC94.
.....
Z200-READ-FILES.
READ DISK-IN INTO T1-ENTRY(T1-INDEX)
AT END MOVE 'YES' TO END-OF-FILE-SW.
WRITE PRINT-RECORD FROM T1-ENTRY(T1-INDEX).
I don't want a step by step for this (though that'd be nice :P) bc I know WHAT I need to do I just don't know HOW to do it bc my textbook and course note are useless to me. I've been stuck on this for a while and nothing I try works.
I'm assuming that every policy group has exactly 5 records with the 5 record types.
You can set up your working storage like this.
05 T1-RECORD.
10 T1-RECORD-TYPE PIC XX.
10 FILLER PIC X(78).
COPY TRNREC10.
COPY TRNREC20.
COPY TRNREC32.
COPY TRNREC60.
COPY TRNREC94.
Then your read paragraph would look like this. I assumed that TRNREC10-RECORD was the 01 level of the TRNREC10 copybook. if not, substitute the actual 01 levels in the following code.
2200-READ-FILE.
READ DISK-IN INTO T1-RECORD
AT END MOVE 'YES' TO END-OF-FILE-SW.
IF END-OF-FILE-SW = 'NO'
IF T1-RECORD-TYPE = '10'
MOVE T1-RECORD TO TRNREC10-RECORD
END-IF
IF T1-RECORD-TYPE = '20'
MOVE T1-RECORD TO TRNREC20-RECORD
END-IF
...
END-IF.
Your write paragraph would look like this
2400-WRITE-FILE.
WRITE PRINT-RECORD FROM TRNREC10-RECORD
WRITE PRINT-RECORD FROM TRNREC20-RECORD
...
Your processing paragraphs would access the data in the copybook records.
You have textbook, course notes, a manual, an editor, JCL and a computer.
All of those are going to be of use to you, but you've also got to get yourself thinking like your should program.
Your task is to read a file, load five records into a table, do something with them, then write them out.
You will have many tasks where you read a file, do something, and write a file.
So how about getting the file processing down pat first?
Define your files using FILE STATUS
PERFORM OPEN-INPUT-POLICY-MASTER
PERFORM OPEN-OUTPUT-NEW-POLICY-MASTER
In those paragraphs (or SECTIONs, depending on your site standards) OPEN the files, check the file status, abend if not "00".
You will need a READ paragraph. READ in there, check the file status, being aware that "10" is valid and that it indicates end-of-file (so you don't need AT END and END-READ). Count all records read (file status "00").
You will need a WRITE paragraph. Check the file status. Only "00" is valid. Count the records written.
PERFORM PRIMING-READ-OF-POLICY-MASTER
All that paragraph needs to do is PERFORM the READ paragraph. Putting it in a paragraph of its own is a way of documenting what it does. Telling the next person along.
What does it do? Reads, or attempts to read, the first record. If the file is empty, you will get file status "10". If the file should not be empty, abend. You've now dealt with an empty file without affecting your processing logic.
PERFORM PROCESS-POLICY-MASTER UNTIL END-OF-POLICY-MASTER
or
PERFORM UNTIL END-OF-POLICY-MASTER
....
END-PERFORM
I prefer the first, to avoid the main logic "spreading", but it's fine to start with the second if you prefer/it fits in with your course.
The last thing in the paragraph or before the END-PERFORM is a PERFORM of your READ.
You can then PERFORM CLOSE-INPUT-POLICY-MASTER, and similar for the output file.
Then check that the counts are equal. If not, abend. This is trivial in this example, but as your logic gets more complicated, things can go wrong.
Always provide counts to reconcile your input to output, count additions, deletions. Count updates separately for information. Get your program to check what it can. If you have more updates than input records, identify that and abend. Have your program do as much verification as possible.
You will now have a simple program which reads a file, writes a file, checks as much as it can, and is just lacking processing logic.
You can use that program as a base for all your tasks reading one file and writing another.
All that stuff will work in your new program, without you having to do anything.
The logic you need for now is to store data in a table.
OK, as Gilbert has rightly shown, storing in a table doesn't make sense in your actual case. But, it is the requirement. You need to get good at tables as well.
Your table is not defined correctly. Try this:
01 T1-RECORD-TABLE.
05 T1-ENTRY OCCURS 5 TIMES
INDEXED BY T1-INDEX.
10 POLICY-RECORD.
15 POLICY-RECORD-TYPE PIC XX.
15 POLICY-RECORD-DATA PIC X(78).
Put an 88 underneath POLICY-RECORD-TYPE for each of your record-types. Make the 88 descriptive of the business function, don't just say "RECORD-IS-TYPE-10".
You are using an index to reference items in the table. Before putting the first entry in the table you have to SET the index to 1. To access the next entry, you have to SET the index UP BY 1.
Once you have stored your items in the table, you need to get at them again. SET the index to 1 again and you can reference the first entry. SET the index UP BY 1 serially to access the other entries.
SET index TO 1 before you start the processing. MOVE zero to a count of table entries. Get into your file processing loop.
There, count what you store, every time that your count of table entries reaches five, PERFORM a paragraph to output your records and SET your index to 1. If the count is not five, SET your index UP BY 1.
In your paragraph to output the records, use PERFORM VARYING your index FROM 1 BY 1 UNTIL GREATER THAN 5. In the PERFORM, PERFORM your WRITE paragraph with the current table entry as the source for the record.
You will now have two programs, both of which read an input file and produce an identical output file.
Then you can do your verification logic.
If you break everything down, keep things separate, keep things simple, name them well, you'll start to write COBOL programs that are the same except for the specific business logic. All the standard stuff, all the boring stuff, if you like, all the basic structure stays the same. The new code you write is just the specifics of the next task.
Yes, you'll get to read more files, either as reference files, or as multiple inputs. You'll have multiple outputs. But you can build the basics of all those in exactly the same manner. Then you'll have more examples to base your future programs on.
Once you've got the basic stuff, you never need to code it again. You just copy and apply.
With good names, your programs will tell what they are doing.
The code that you actually write will be the "interesting" stuff, not the stuff you do "every time".
I've just "designed" this for you. It is not the only workable design. It is how I do it, and have done for some time. You should also design every part of your processing. You should know what it is doing before you write the code.
As an example, take a simple loop. Imagine how you will test it. With zero entries in the table, what happens? With one? With an intermediate number? One less than the maximum? The maximum? One more than the maximum? 10 more than the maximum? Then write the code knowing that you need to know how to deal with those cases.
In time, not too long, you'll think about the low-level design while you code. In more time, you'll do the high-level design that way as well. In enough time you'll only design things you've not had to deal with before, the rest you'll already know.
You have textbook, course notes, a manual, an editor, JCL and a computer. I've given you some ideas. How about seeing if taken all together they are useful to you. I think you have some frustrations now. Write some basic programs, then apply them to your tasks.
Related
Im trying to make a game on Scratch that will use a feature to generate a special code, and when that code is input into a certain area it will load the stats that were there when the code was generated. I've run into a problem however, I don't know how to make it and I couldn't find a clear cut answer for how to make it.
I would prefer that the solution be:
Able to save information for as long as needed (from 1 second to however long until it's input again.)
Doesn't take too many blocks to make, so that the project won't take forever to load it.
Of course i'm willing to take any solution in order to get my game up and running, those are just preferences.
You can put all of the programs in a custom block with "Run without screen refresh" on so that the program runs instantly.
If you save the stats using variables, you could combine those variable values into one string divided by /s. i.e. join([highscore]) (join("/") (join([kills]) (/))
NOTE: Don't add any "/" in your stats, you can probably guess why.
Now "bear" (pun) with me, this is going to take a while to read
Then you need the variables:
[read] for reading the inputted code
[input] for storing the numbers
Then you could make another function that reads the code like so: letter ([read]) of (code) and stores that information to the [input] variable like this: set [input] to (letter ([read]) of (code)). Then change [read] by (1) so the function can read the next character of the code. Once it letter ([read]) of (code) equals "/", this tells the program to set [*stat variable*] to (input) (in our example, this would be [highscore] since it was the first variable we saved) and set [input] to (0), and repeat again until all of the stats variables are filled (In this case, it repeats 2 times because we saved two variables: [highscore] and [kills]).
This is the least amount of code that it takes. Jumbling it up takes more code. I will later edit this answer with a screenshot showcasing whatever I just said before, hopefully clearing up the mess of words above.
The technique you mentioned is used in many scratch games but there is two option for you when making the save/load system. You can either do it the simpler way which makes the code SUPER long(not joking). The other way is most scratchers use, encoding the data into a string as short as possible so it's easy to transfer.
If you want to do the second way, you can have a look at griffpatch's video on the mario platformer remake where he used a encode system to save levels.https://www.youtube.com/watch?v=IRtlrBnX-dY The tips is to encode your data (maybe score/items name/progress) into numbers and letters for example converting repeated letters to a shorter string which the game can still decode and read without errors
If you are worried it took too long to load, I am pretty sure it won't be a problem unless you really save a big load of data. The common compress method used by everyone works pretty well. If you want more data stored you may have to think of some other method. There is not an actual way to do that as different data have different unique methods for things working the best. Good luck.
I have SPSSmodeler stream which is now used and updated every week constantly to generate a certain dataset. A raw data for this stream is also renewed on a weekly basis.
In part of this stream, there is a chunk of nodes that were necessary to modify and update manually every week, and the sequence of this part is below: Type Node => Restructure Node => Aggregate Node
To simplify the explanation of those nodes' role, I drew an image of them as bellow.
Because the original raw data is changed weekly basis, the range of Unit value above is always varied, sometimes more than 6 (maybe 100) others less than 6 (maybe 3). That is why somebody has to modify there and update those chunk of nodes on a weekly basis until now. *Unit value has a certain limitation (300 for now)
However, now we are aiming to run this stream automatically without touching any human operations on it that we need to customize there to work perfectly, automatically. Please help and will appreciate your efforts, thanks!
In order to automatize, I suggest to try to use global nodes combined with clem scripts inside the execution (default script). I have a stream that calculates the first date and the last date and those variables are used to rename files at the end of execution. I think you could use something similar as explained here:
1) Create derive nodes to bring the unit values used in the weekly stream
2) Save this information in a table named 'count_variable'
3) Use a Global node named Global with a query similar to this:
#GLOBAL_MAX(variable created in (2)) (only to record the number of variables. The step 2 created a table with only 1 values, so the GLOBAL_MAX will only bring the number of variables).
4) The query inside the execution tab will be similar to this:
execute count_variable
var tabledata
var fn
set tabledata = count_variable.output
set count_variable = value tabledata at 1 1
execute Global
5) You now can use the information of variables just using the already creatde "count_variable"
It's not easy to explain just by typing, but I hope to have been helpful.
Please mark as +1 in this answer if it was relevant one.
I think there is a better, simpler and more effective (yet risky, due to node's requirements to input data) solution to your problem. It is called Transpose node and does exactly that - pivot your table. But just from version 18.1 on. Here's an example:
https://developer.ibm.com/answers/questions/389161/how-does-new-feature-partial-transpose-work-in-sps/
If a table element (table without an index) is accessed using an index of another table it can give a Table overflow error on IBM Host. But the same program does not result in a crash or a message (even with debug options) when using GnuCOBOL (formerly OpenCOBOL).
e.g.
IDENTIFICATION DIVISION.
PROGRAM-ID. TSTPROGX.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 IX PIC 9(04) COMP VALUE ZERO.
01 VARS.
05 S-PART-C.
10 S-DETAIL OCCURS 100 TIMES
INDEXED BY S-SUB.
15 S-ACTUAL PIC 9(06) VALUE ZERO.
15 S-ACTUAL-A
REDEFINES S-ACTUAL
PIC X(06).
15 S-GRADE PIC X(02) VALUE LOW-VALUE.
05 POS-USED-ARRAY PIC X(999)
VALUE SPACE.
05 FILLER REDEFINES POS-USED-ARRAY
OCCURS 999.
10 FILLER-X PIC X .
88 POSITIONS-USED-X VALUE 'T'.
PROCEDURE DIVISION.
SET S-SUB TO 1
PERFORM VARYING IX FROM 1 BY 1 UNTIL IX > 999
SET S-SUB TO IX
SET POSITIONS-USED-X(S-SUB) TO TRUE
DISPLAY IX ":" FILLER-X(S-SUB)
END-PERFORM
GOBACK.
Is there a compiler option to issue warnings to avoid this kind of usage?
This error can be avoided by using the right usage.i.e, using the variable 'I-X', instead of using the index(S-SUB) of a different table.
SET POSITIONS-USED-X(I-X) TO TRUE
In general, exchanging index of independent tables(of different size) seems to be erroneous.
Presuming that by Host you mean Mainframe, using Enterprise COBOL, the link I originally included has the answer for you.
In Enterprise COBOL, you can use an index from one table to reference data on another table, even one which does not have an index (like your example), but unless the lengths of the data subordinate to the OCCURS is the same you will not get the results you expect.
With Enterprise COBOL and compiler option SSRANGE the code in your question will fail (as you are aware). Where will it fail? The length of the OCCURS associated with the index S-SUB is eight bytes. That length is effectively "inbuilt" into the index S-SUB. Dividing 999 (length of second table) by eight yields 124 (ignore the remainder), so an S-SUB being SET to 124 will be OK, and SET to 125 will not, because 125 will start at by 1,000, and you only have 999 bytes.
In GnuCOBOL the index does not have the length inbuilt, it is a simple integer relating directly to the occurence in the table. However, having got up to 100, you start overflowing the first table (where there is no compiler/run-time check) and 125 references later you go beyond the end of the second table, and with -g in your compile options will get a crash depending on how (and how much) the storage is allocated in the C program generated by the GnuCOBOL compiler. 225 would be the first point at which a crash could occur, but it may not do so at that point, and it may, or may not, later.
Basically, there's no way you can really expect the code to work, which you know, you just want to know how to set the compiler to check for it with GnuCOBOL. Currently, you can't.
Usually there is a field defined to hold the maximum entries in a table, and another defined to say how many are being used. When using entries, you first check to see the table is not full.
You could use LENGTH OF/FUNCTION LENGTH to protect against going over the end of a table as well.
You could combine the two approaches.
You just don't have a switch for GnuCOBOL that helps you here. The -g goes down to the C compiler. It gives you something, but not everything, and just depends.
Also, it is not, to my mind, a good idea to have a data-name which you use for subscripting named IX and an index named S-SUB. That will confuse a human reader of a program.
See this answer: https://stackoverflow.com/a/36254166/1927206 for some detail about using indexes from different tables.
IBM Enterprise COBOL can check that storage referenced using an index is within the table that the referenced-element is subordinate to. This is done with compiler option SSRANGE. If data outside the table is referenced (by any manner of subscripting, or by reference-modification) a run-time diagnostic message is produced. In all but the latest Enterprise COBOL compilers, that problem will cause an "abend" (an "abnormal end"). With the latest compilers, there are some sub-options to give more control.
This is not strictly checking the "bounds" of a table. For a one-dimensional table, the table-reference checking coincides with bounds-checking. For a multi-dimensional table it does not. The second and subsequent levels of subscript can be "wrong", but unless they cause a reference outside the table, this is unproblematic.
GnuCOBOL/OpenCOBOL does not have any checking for the use of subscripts or the reference of a table by subscripting/reference-modification. If you wanted to consider adding this yourself, you would be more than welcome. Or you could post it as a feature request. Visit https://sourceforge.net/p/open-cobol/discussion/?source=navbaring, but not everything.
See this answer: https://stackoverflow.com/a/36254166/1927206 for some detail about using indexes from different tables.
IBM Enterprise COBOL can check that storage referenced using an index is within the table that the referenced-element is subordinate to. This is done with compiler option SSRANGE. If data outside the table is referenced (by any manner of subscripting, or by reference-modification) a run-time diagnostic message is produced. In all but the latest Enterprise COBOL compilers, that problem will cause an "abend" (an "abnormal end"). With the latest compilers, there are some sub-options to give more control.
This is not strictly checking the "bounds" of a table. For a one-dimensional table, the table-reference checking coincides with bounds-checking. For a multi-dimensional table it does not. The second and subsequent levels of subscript can be "wrong", but unless they cause a reference outside the table, this is unproblematic.
GnuCOBOL/OpenCOBOL does not have any checking for the use of subscripts or the reference of a table by subscripting/reference-modification. If you wanted to consider adding this yourself, you would be more than welcome. Or you could post it as a feature request. Visit https://sourceforge.net/p/open-cobol/discussion/?source=navbar
In program a
EXEC CICS LINK
PROGRAM(PGMB)
COMMAREA(COMMA)
LENGTH(LENGTH OF COMMA)
RESP(CICS-RESP)
END-EXEC
In program b
EXEC CICS RETURN
END-EXEC
Does program b only return the commarea that program a passed? Or does it return the whole LINKAGE SECTION?
Program B returns neither the entire LINKAGE SECTION nor the commarea (COMMA in your example).
It returns nothing.
Why does it return nothing? Because nothing gets passed to it.
Or, rather, what gets passed to it is simply the address(es) of the parameter(s). Nothing else. That is all. Importantly, no length.
PROGA
01 some-stuff.
05 a-bit-of-stuff PIC X.
05 the-rest-of-the-stuff PIC X(99).
CALL .... USING a-bit-of-stuff
PROGB
LINKAGE SECTION.
01 stuff-that-is-somewhere-else PIC X(100).
PROCEDURE DIVISION USING stuff-that-is-somewhere-else.
a-bit-of-stuff is define as only one byte. This makes no difference. It is the definition, in the LINKAGE SECTION, of the item on the PROCEDURE DIVISION USING ... which matches, in order of reference, nothing else, to the CALL ... USING ...
PROGB will be "passed" the address of a-bit-of-stuff. If that address is then mapped to 100 bytes in the LINKAGE SECTION of the CALLed program, COBOL does not mind at all.
If we change that example CALL to use instead some-stuff, since some-stuff has the same starting address as a-bit-of-stuff, there would be absolutely no change in the generated code, and no change in the execution of the two programs.
Defining different sizes of data "between" CALLer and CALLed is usually not done, because it makes things less clear to us, humans. The compiler cares not one jot.
What you need to look at the 01s (or 77s if that silly idea takes your fancy) as is a REDEFINES. They are a REDEFINES, an implicit one, of data which is defined somewhere else. No data is defined for items in the LINKAGE SECTION (there is one exception to that on the Mainframe). The 01-levels in the LINKAGE SECTION are just redefining, or mapping, the address of the data that is passed to the program. The data does not "leave" the CALLing program, and the data is never "passed back".
Things can go wrong, of course, if you use different lengths for matching items on the USINGs. If the storage from the CALLer is "acquired" (like a GETMAIN in CICS) then attempting to referencing data outside of that storage, even one byte further on, can get you an abend due to an Addressing Exception (a S0C4, which CICS will kindly name something else for you, an AKEA).
Even without acquired storage, other fields after the one "passed" can be accidentally trashed, or the field itself may not get the expected amount of data MOVEd to it by the CALLed program, if the definition is short in the CALLed program.
There are actually two things which get "returned" from a CALLed program. They are the special-register RETURN-CODE, and the single item on the RETURNING of the PROCEDURE DIVISION (if used, likely not).
Even so, the mechanisms of how those are achieved are different from the normal misunderstanding of data "passed" between CALLing and CALLed programs.
I have not had the joy of programming CICS for sometime now, this answer is based on what knowledge I still remember.
The calling program gets at most an amount of data less than or equal to the size of the data area sent in the calling program (or as specified by the optional LENGTH parameter). Don't try to access data beyond what you have sent.
"So if program x LINKS to program y, any updates done to the COMMAREA in y will be visible in x."
Source: SOVF:How CICS Shared Memory Works.
"When a communication area is passed by way of an EXEC CICS LINK command, the invoked program is passed a pointer to the communication area itself. Any changes that are made to the contents of the data area in the invoked program are available to the invoking program, when control returns to it; to access any such changes, the program names the data area that is specified in the original COMMAREA option." Source: IBM-CICS-Ref.
So, does program b only return the commarea that program a passed?
I would answer the above as Yes.
Does it return the whole linkage section?
As for this one, it depends on the structure of the DFHCOMMAREA of the linked program. If it only contains 1 such area, then the answer is that it returns as many bytes as was sent on link command from that area (either implicitly or explicitly). Remember that this area is outside your caller. So, if the caller sends 100 bytes and the linkage section has an area of 500 bytes, you only get 100 back at the most.
If you want to allow your linked program to modify data in the commarea, there are some very serious limitations.
Exec CICS
Return
End-Exec
Will expose a changes in a commarea to the LINKing program, but only by accident and only if both tasks execute on the same CICS region. This is because the commarea is actually a pointer. On a distributed program link, the area is copied, but not copied back.
Hey, everyone. I'm having a bit of trouble in a coding project that I'm trying to tackle in the zOS environment using COBOL. I need to read a file in and put them into an indexed table (I know there will be less than 90 records).
The thing that is throwing me is that we are bound by the parameters of the project to use a variable called "Table-Size" (set to zero at declaration).
Given all that, I need to do something like "Occurs 1 to 90 times Depending on Table-Size", but I don't understand how that will work if table-size has to (as far as I can tell) because table-size is incremented along with each entry that is added to the table. Can anyone please clear this up for me?
Thanks!
It sounds like your primary concern is: how does the compiler know how much to allocate in the array if the size changes at run-time?
The answer is that it allocates the maximum amount of space (enough for 90 entries). Note that this is for space in working storage. When the record is written to a file, only the relevant portion is written.
An example:
01 TABLE-SIZE PIC 9
01 TABLE OCCURS 1 TO 9 TIMES DEPENDING ON TABLE-SIZE
03 FLD1 PIC X(4)
This will allocate 36 characters (9 multiplied by 4) for TABLE in working storage. If TABLE-SIZE is set to 2 when the record is written to a file, only 8 characters of TABLE will be written (over and above the characters written for TABLE-SIZE, of course).
So, for example, if the memory occupied by TABLE was AaaaBbbbCcccDdddEeeeFfffGgggHhhhIiii, the date written to the file may be the shortened (including size): 2AaaaBbbb.
Similarly, when the record is read back in, both TABLE-SIZE and the relevant bits of TABLE will be populated from the file (setting only the size and first two elements).
I don't believe that the unused TABLE entries are initialised to anything when that occurs. It's best to assume not anyway, and populate them explicitly if you need to add another item to the table.
For efficiency, you may want to consider setting the TABLE-SIZE to USAGE IS COMP.
We don't have quite enough information here, but the basic thing is that the variable named in the DEPENDING ON clause has to have a count of the variable number of groups. So you need something like
01 TABLE-SIZE PIC 99
01 TABLE OCCURS 1 TO 90 TIMES
DEPENDING ON TABLE-SIZE
03 FIELD-1
03 FIELD-2
and so on.
See this article or this article at Publib.