I just discovered the amazing FileHelpers library. (on the past i'm already developed my own import/export library based on Attributes).
I wanna know if exists the possibility for the fixed record to specify the start character for each field.
Example:
INPUT FILE
3232432GIANLUIGI
4234234MARIO
I just wanna import the name (Gianluigi) from the 8th character.
I just want to read the CustomerName, without the filed i've to ignore (especially if we talk about performance).
My real case is about a record that have reserved record position for future implementation.
Example:
-From 1 to 100 real fields
-From 101 to 150 no field - Position reserved for future implementation
-From 151 to 160 real fields
-From 161 to 185 another time no field - position reserved for future implementation
-From 186 to ...N real fields.
I want avoid to create fake field for read nothing (Ex. from 101 to 150 and from 161 to 185).
For each FieldFixedLength attributes i imagine start position.
Yes, that is completely do-able. There are several ways to do this, manually as I would to produce:
[FixedLengthRecord(FixedMode.AllowVariableLength)]
public class Customer
{
[FieldFixedLength(8)]
[FieldValueDiscarded]
public int ignoredFieldOne;
[FieldTrim(TrimMode.Both)]
[FieldFixedLength(80)]
public String CustomerName;
// Skip fields 3 - 5
// field 3 (length 20)
// field 4 (length 5)
// field 5 (length 3)
[FieldFixedLength(28)]
[FieldValueDiscarded]
public string ignoredFieldsThreeThroughFive;
[FieldTrim(TrimMode.Both)]
[FieldFixedLength(20)]
public String AnotherField;
}
Alternatively, and probably easier for you is to use the Class Wizard that comes with FileHelpers where you can select the language you want the class built in.
Set the class name to Customer
Select Fixed Length Record
Set FixedMode as desired
Click next
Set Number of Fields you want, eg 2
If you have a header row, set Ignore the first to 1
Tick the Ignore Empty Lines
Click next
In the first box, enter Name as CustomerID, Type as int and Length as 8
In the second box, enter CustomerName, keep Type as string, set Length to your maximum (eg, 80) and Trim to Both (or Right if you want spaces at the start).
At this point, on the right hand side will be the class that you have just built. This is exactly what I would have typed manually since I already know how to build these classes.
Note 1: FixedMode set to ExactLength means ALL records have to be the same length
Note 2: The class builder will automatically make your class sealed so it won't be inheritable for performance purposes.
Note 3: For any position you are just not interested in, you will have to create a field but add the attribute as above and it will not populate it (see above where three fields are skipped as they are consecutive but only one field is created)
Related
In our current project we are using SAPNCO3 with RFC calls. The requirement is to create material with the function "BAPI_MATERIAL_SAVEDATA" and some custom fields (via EXTENSIONIN). The problem now is how to extend the needed structures "BAPI_TE_MARA/X" so that they can carry the custom fields? I cannot found any function for this.
Please have a look at the Code snippet at the bottom.
Thank you!
Tobias
var BAPI_TE_MARA = repo.GetStructureMetadata("BAPI_TE_MARA");
IRfcStructure structure = BAPI_TE_MARA.CreateStructure();
structure.SetValue("MATERIAL", material.Number);
//structure.SetValue("ZMM_JOB_REFERENCE", "f");
BAPI_MATERIAL_SAVEDATA has two table parameters EXTENSIONIN and EXTENSIONINX to which you pass lines with the values of your custom fields.
These table parameters have to indicate what extension structures you want to use and their values.
As these custom fields may extend different tables of the material, you have to indicate different extension structures depending on which table these fields belong to:
For the table MARA, the extension structures are BAPI_TE_MARA and BAPI_TE_MARAX.
For the table MARC, the extension structures are BAPI_TE_MARC and BAPI_TE_MARCX.
These extension structures should preferably have character-like fields to simplify the programming (and to support IDocs, as rule-of-thumb).
For instance, if you have the custom fields ZZCNAME (7 characters) and ZZCTEXT (50 characters) in the table MARA, they will also be defined in BAPI_TE_MARA and have the same names and types. In BAPI_TE_MARAX, you also have two fields with the same names, but always of length 1 character and their values must be 'X' to indicate that a value is passed in BAPI_TE_MARA (useful in case a blank value is passed that must not be ignored). The X extension structures are essential especially in "change" BAPIs.
If you want to pass values to the BAPI, you must first initialize these structures:
BAPI_TE_MARA:
MATERIAL ZZCNAME ZZCTEXT
------------ ------- -------
000000012661 NAME TEXT
BAPI_TE_MARAX:
MATERIAL ZZCNAME ZZCTEXT
------------ ------- -------
000000012661 X X
Then, you must initialize the two parameters of the BAPI:
EXTENSIONIN (notice that there are 3 spaces in NAME TEXT because the technical length of ZZCNAME is 7 characters and its value "NAME" occupies only 4 characters):
STRUCTURE VALUEPART1 (240 Char) VALUEPART2 (240) VALUEPART3 (240) VALUEPART4 (240)
------------ ----------------------- ---------------- ---------------- ----------------
BAPI_TE_MARA 000000012661NAME TEXT
EXTENSIONINX:
STRUCTURE VALUEPART1 (240 Char) VALUEPART2 (240) VALUEPART3 (240) VALUEPART4 (240)
------------- --------------------- ---------------- ---------------- ----------------
BAPI_TE_MARAX 000000012661XX
Consequently, your program must:
concatenate all BAPI_TE_MARA fields together and copy the resulting string into fields VALUEPART1 to VALUEPART4 of EXTENSIONIN as if it was a 960 characters field
concatenate all BAPI_TE_MARAX fields together and copy the resulting string into fields VALUEPART1 to VALUEPART4 of EXTENSIONINX
I guess you may use ToString() to get one concatenated string of characters of all fields of a structure, and to set the value of VALUEPART1, VALUEPART2, etc., you'll probably need to initialize them individually from the string of characters with Substring.
My comment was half by half correct and incorrect, I wasn't aware of the extension technique in this BAPI, so I wasn't aware of this structure is really used in this BAPI.
You asked
The problem now is how to extend the needed structures "BAPI_TE_MARA/X" so that they can carry the custom fields?`
and what I said is indeed stays valid: you cant extend the interface from NCo, only on backend.
You writes:
At this If I load BAPI_TE_MARA there aren't any custom fields but the material
and this get me to the idea that your ABAP developers made only half of the work. The things to be done on the SAP backend:
Extend MARA table with custom Z fields (in SAP it is called Append structure)
Extend interface structure BAPI_TE_MARA with the fields which should exactly correspond to the MARA fields
This is how it must look like on backend
If you don't see any custom fields in BAPI_TE_MARA except MATERIAL probably step 2 is missing on SAP side. As what I got from your comments, they created structure ZMM_S_MATMAS_ADDITION but appended it only to MARA, but not to BAPI_TE_MARA.
What is missing from Sandra excellent holistic answer is step 3: for all this construction to work some customizing need to be done.
T130F table must contain your custom fields. To maintain the entry for T130F go to transaction SPRO or directly to maintenance view V_130F.
SPRO way: go to SPRO -> Logistics-General -> Material Master -> Field Selection -> Assign fields to field Selection Groups and maintain the entry in the table
Sample ABAP code that does the thing:
DATA: ls_headdata TYPE bapimathead,
lt_extensionin TYPE STANDARD TABLE OF bapiparex,
ls_extensionin LIKE LINE OF lt_extensionin,
lt_extensioninx TYPE STANDARD TABLE OF bapiparexx,
ls_extensioninx LIKE LINE OF lt_extensioninx,
lt_messages TYPE bapiret2_t,
ls_bapi_te_mara TYPE bapi_te_mara,
ls_bapi_te_marax TYPE bapi_te_marax.
ls_headdata-material = |{ ls_headdata-material ALPHA = IN }|.
ls_headdata-basic_view = 'X'.
ls_bapi_te_mara-material = ls_headdata-material.
ls_bapi_te_mara-zztest1 = '322223'.
ls_bapi_te_marax-material = ls_headdata-material.
ls_bapi_te_marax-zztest1 = 'X'.
ls_extensionin-structure = 'BAPI_TE_MARA'.
ls_extensionin-valuepart1 = ls_bapi_te_mara
APPEND ls_extensionin TO lt_extensionin.
ls_extensioninx-structure = 'BAPI_TE_MARAX'.
ls_extensioninx-valuepart1 = ls_bapi_te_marax-zztest1.
APPEND ls_extensioninx TO lt_extensioninx.
CALL FUNCTION 'BAPI_MATERIAL_SAVEDATA'
EXPORTING
headdata = ls_headdata
TABLES
returnmessages = lt_messages
extensionin = lt_extensionin
extensioninx = lt_extensioninx.
CALL FUNCTION 'BAPI_TRANSACTION_COMMIT'
EXPORTING
wait = 'X'.
Based on this you can model your .Net code for BAPI calling.
P.S. Pay attention to the first line with ALPHA = IN. The input to the material number field must be in fully qualified 18-char format with leading zeroes, e.g. 000000000000323, otherwise the update will fail.
Always extend structure EMARA and not MARA, BAPI_TE_MARA, ... directly.
This may be something really simple, but I couldn't figure it out and been trying to find an example online to no avail. I'm basically trying to remove items found in one sequence from another sequence.
Example #1
Items added to the cart is in one sequence; items removed from cart is in another sequence:
<#assign Added_Items_to_Cart = "AAAA,BBBB,CCCC,DDDD,EEEE,FFFF">
<#assign Deleted_Items_from_Cart = "BBBB,DDDD">
The result I'm looking for is: AAAA,CCCC,EEEE,FFFF
Example #2
What if the all items added to and deleted from cart are in the same sequence?
<#assign Cart_Activity = "AAAA,BBBB,BBBB,CCCC,DDDD,EEEE,DDDD,FFFF,Add,Add,Delete,Add,Add,Add,Delete,Add">
The result I'm looking for is the same: AAAA,CCCC,EEEE,FFFF
First things first: You ask about sequence but the data you are dealing with are strings.
I know you are using the string to work as a sequence (and it works), but sequences are sequences and strings are strings, and they have diferente ways of dealing with. I just felt this was important to clarify if someone who is starting to learn how to program get to this answer.
Some assumptions since you're providing strings with data separated by comma:
You want a string with data separated by comma as a result.
You know how to properly create strings with data separated by comma.
You dont have commas in your items names.
Observations:
I'll give you the logic but not the code donne, as this can be a great chance for you to learn/practice freemarker (stackoverflow spirit, you know...)
You question is not about something specific of freemaker (it just happens to be the language you want to work with). Think about adding the logic tag to you question. :-)
Now to the answer on how to do what you want on a "string that is working as a sequence":
Example #1
Change your string to a real sequence :-)
1 - Use a built-in to split your string on commas. Do it for both Added_Items_to_Cart and Deleted_Items_from_Cart. Now you have two real sequences to work with.
2 - Create a new string tha twill be your result .
3 - Iterate over the sequence of added itens.
4 - For each item of the added list, you will check if the deleted list also contains this item.
4.1 - If the deleted list contains the item you do nothing.
4.2 - If the deleted list do not contains the item, you add that item to your string result
At the end of this nested iteration (thats another hint) you should get the result you're looking for.
Example #2
There are many ways of doing it and i'll just share the one that pops out of my mind right now.
I think it's noteworthy that in this approach you will always have an even sized list, as you always insert 2 infos each time: item and action.
So always the first half will be the 'item list' and the second half will be the 'action list'.
1 - Change that string to a sequence (yes, like on the other example).
2 - Get half of its size (in your example size = 16 so half of it is 8)
3 - Iterate over a range from 0 to half-1 (in your example 0 to 7)
4 - At each iteration you'll have a number. Lets call it num (yes I'm very creative):
4.1 - If at the position num + half you have the word "Add" you add the item of position num in your result string
4.2 - If at the position num + half you have the word "Delete" you remove the item of position num from your result string
And for the grand finale, some really usefull links that will help you in your freemarker life forever!!!
All built-ins from freemarker:
https://freemarker.apache.org/docs/ref_builtins.html
All directives from freemarker:
https://freemarker.apache.org/docs/ref_directive_alphaidx.html
Freemarekr cheatsheet :
https://freemarker.apache.org/docs/dgui_template_exp.html#exp_cheatsheet
I'm implementing a generic BACnet decoder and came across the following question, of which I can't seem to find the answer within the BACnet standard. The chapter "20.2.1.3.2 Constructed Data" does not answer my question, or I might not fully understand it.
Let's assume I have a List (SEQUENCE OF) with elements of type Record (SEQUENCE).
Said record has 4 fields, identified by context tag, where field 0 and 1 are optional.
I further assume that the order, in which those fields are serialized, can be arbitrary (because they're identified by their context tags).
The data could look like that (number indicates field / column):
[{ "3", "0", 2" }, {"1", "2", "3"}]
Over the wire, the only "structure information" I assume I get are the open / close tags for the list.
That means:
Open Tag List
ctxTagColumn3, valueColumn3,
ctxTagColumn0, valueColumn0,
ctxTagColumn2, valueColumn2,
ctxTagColumn1, valueColumn1,
ctxTagColumn2, valueColumn2,
ctxTagColumn3, valueColumn3
Close Tag List
How do I know, after I've read the last column data ("2") of my first list item, that I must begin decoding the second item, starting with a value for column "1"?
Which of my assumptions is wrong?
Thank you and kind regards
Pascal
The order of elements of a SEQUENCE is always known and shall not be arbitrarily by definition. Further, not all conceivable combinations are possible to encode. Regarding BACnet, all type definitions shall be decodable universally.
Assuming I understand you correctly; the "order" cannot be "arbitrary"; i.e.:
SEQUENCE = *ordered* collection of variables of **different** types
SEQUENCE OF = *ordered* collection of variables of **same** type
The tag-number for the item (SD) context-tag will be different (/possibly an incremented value/maybe +1) from the containing (PD) context-tag; so you could check for that, or better still if the tag-number value is <= 5 (/'length' value) then it's a SD context-tag for one of your items, rather than a (/the closing) PD context tag (/'type' value) delimiting the end of your items.
I want to do cross-references manually in microsoft word 2010 (similarly to latex \label - \ref or \cite - \bibliography). I found that the Field function does almost excactly what I want (the syntax is a bit weird). If I type the following to the document (wave brackets are field marks produced by ctrl+f9):
{set dischargeRate {seq Figure}}Figure {ref dischargeRate}: Discharge
rate vs. hole diameter. Figure is from Reference [{ref authorA}].
The results are shown in Figure {ref dischargeRate} and published
previously in [{ref authorB}] and [{ref authorA}].
References:
{set authorA {seq cites}}[{ref authorA}] author, title, journal, year
{set authorB {seq cites}}[{ref authorB}] author, title, journal, year
the above produces:
Figure 1: Discharge
rate vs. hole diameter. Figure is from Reference [1].
The results are shown in Figure 1 and published
previously in [2] and [1].
References:
[1] author, title, journal, year
[2] author, title, journal, year
Is there a way to define increment and reference with one command instead of those three commands: set, seq and ref? Or how do I create a macro that does this for me. I am looking something like {setOrRef sequencename labelname} that shows a number i+1 that can be later referenced by {setOrRef sequencename labelname}.
Also there should be a check that labels are not redefined. For example: If the label does not exist, the sequence (Figure or cites) is incremented by one and that number is assigned to the label. If the label exists the existing number for the label is used.
I consider this as a programming question as it so close to macros and automating Ms Word.
If you want to use field codes and nothing else, I don't think there is a practical way to avoid using SEQ, SET and REF.
There are a number of difficulties detecting the existence/non-existence of bookmarks. I think you can sidestep them using the following set of nested fields, but I cannot say I have tried this "for real". Personally, I would try to avoid this kind of complexity in field coding.
All you need to do is insert the bookmark name you want once in the QUOTE field, i.e. by substituting the "bookmark" name you want instead of "bm". Here, I use "s" as the name that provides sequential reference numbers. It isn't actually a bookmark name, by the way, but a SEQ name.
As usual, all the {} have to be the special field code braces that you can insert using ctrl-F9 on Windows Word. You will still need to be careful about your naming of references, e.g. don't use "AuthorA" and "AuthorA1". You will need to avoid using any other SEQ names such as AuthorA2, and avoid using SEQ names s and s1 elsewhere.
{QUOTE {SET b bm}{SEQ "{b}{SEQ {b}}" \r{SEQ "s{={SEQ {b}}-3 \#"1'';''"}}" \h}{SEQ "{b}1" \c}}
Since it's not obvious how this works, I'll step through. Suppose you name your reference "AuthorA". Then you would insert
{QUOTE {SET b AuthorA}{SEQ "{b}{SEQ {b}}" \r{SEQ "s{={SEQ {b}}-3 \#"1'';''"}} \h}{SEQ "{b}1" \c}}
Bookmark "b" is set to "AuthorA"
{SEQ "{b}{SEQ {b}}"}
expands to
{SEQ "AuthorA{SEQ AuthorA}"}
In the first such set of fields for AuthorA , this expands to
{SEQ "AuthorA1" }
In subsequent sets, it expands to
{SEQ "AuthorA2" }
and so on.
The final field (the one actually inserted by the QUOTE)
{SEQ "{b}1" \c}
expands to
{SEQ "AuthorA1" \c}
i.e. it is always the value returned by the first set of fields in the document for AuthorA.
The value for AuthorA1 is set by
\r{SEQ "s{={SEQ {b}}-3 \#"1'';''"}}
{SEQ {b}}
expands to
{SEQ AuthorA}
which will actually be 2 in the very first set of fields for AuthorA (this is one of Word's SEQ field evaluation quirks). So
{={SEQ AuthorA}-3}
will be -1 and the numeric switch
\#"1'';''"
will return ''. So AuthorA1 is set to
\r{SEQ "s"}
i.e. the next value of the sequence s.
Subsequently,
{={SEQ AuthorA}-3}
is greater than 0, the numeric switch returns '1' and so AuthorA2 will be set to
\r{SEQ "s1"}
But we don't care what AuthorA2 is set to. The purpose of this bit of numeric formatting is to ensure that { SEQ s } only increments the first time time we insert one of these sets of fields for a particular "reference name".
For inserting such stuff, you could create an autotext/building block that would insert the block. All you would need to do is change "bm". A piece of VBA to prompt for a bookmark name and insert the same block would not be hard, but I leave you to look around for that.
As always, even using the general technique suggested, there may be a way to simplify the fields. Personally, I think I would go for a design that let me insert some kind of placeholder and required me to run some "pre-publishing VBA" to set them up correctly.
I am creating a test application to calculate the area of a rectangle.This application uses NSTextField control to get the length and breadth of rectangle.
The user is expected to enter a string value which will include units of length as well (Sample Values 2.5 inches, 1500 mm).
I want NSTextField to convert the value in centimeters (cm) automatically so that it displays the converted value (in cm) as soon as the text field looses focus.
For Eg:
User Enters length as: 1500 mm
As soon as user goes to next text field to enter breadth, the length field displays
Displayed Value: 150 cm
How can we achieve this?
I think you want to use the delegate method, controlTextDidEndEditing:, which is sent to your text field's delegate when the editing is finished. Once you get the string from the field editor, you'll need to parse it somehow to find the units (how you parse it depends on what all you need to find). If you have a small number of things you're hunting for, like cm, mm, m, then you could probably just use rangeOfString: and test for 0 length to find out what units were entered. BTW, you should do your math correctly too -- 1500mm = 150 cm
I would consider a different approach, using a UIPicker to display the available units, and have them enter the number separately.
Otherwise, you'll have to do all this parsing. Don't see the point of parsing. Plus, if they make a mistake spelling the units, or use different ways of indicating it, then you would have to account for that as well. Depends on your application, but if the user has to type the units out anyway, it might be more convenient for them to use a UIPicker.