So in trying to investigate a PI issue, I discovered that the client was running pure Java stack. I've not spent loads of time there, but was sure I could pick it up once I was in the correct transactions : Going to
NWA/Configuration just left me with the spinny-circle-of-boringness... PI was hanging.
An OSS Note (I forget which) said that JAVA PI systems hanging can be fixed by making a setting in Configuration... WTF? I can't even reach that, because the system is hanging! Maybe I got the wrong note.
In the end, this was fixed by:
Installing the newer version of Java
https://java.com/en/download/
Adding the URL of the application to the exceptions list:
http://neo.jpl.nasa.gov/orbits/java_exception.html
Also, in the place where you can set the exceptions, ramping the security down to minimum.
And now I can do all my PI config!
I'm looking forward to the next time I run eclipse, and seeing what the problems with that are!
This is one of the usual complaints about Java, there's a whole bunch of incompatibilities that mean you can only really purpose Java for one thing at a time! That and that SAP/Eclipse/IE never seem to keep up with what Java is doing...
Thursday, 8 December 2016
Thursday, 10 November 2016
Can't see my Service in IWFND/MAINT_SERVICE
When trying to add my service, from my ED2(300) server, I can't see it in the Gateway IWFND/MAINT_SERVICE
It's definitely there in the backend, I've activated SEGW...
The way round this (for reasons unknown...) was to create a SEGW Project in the Gateway with the same name as the project in the backend.
You don't need to populate it with the data entries, mappings to fms etc, just needs to be a faux shell.
Once that's done, go back into the IWFND/MAINT_SERVICE transaction, and try and add the Service again.
This time round, the Service was there, and the transaction even seems to have picked up that it needs to be looking at ED2(300). My guess as to why the latter should be the case is that this was a previously used service that was deleted then re-inserted.
Thanks a lot to Manu for this one... :D
It's definitely there in the backend, I've activated SEGW...
The way round this (for reasons unknown...) was to create a SEGW Project in the Gateway with the same name as the project in the backend.
You don't need to populate it with the data entries, mappings to fms etc, just needs to be a faux shell.
Once that's done, go back into the IWFND/MAINT_SERVICE transaction, and try and add the Service again.
This time round, the Service was there, and the transaction even seems to have picked up that it needs to be looking at ED2(300). My guess as to why the latter should be the case is that this was a previously used service that was deleted then re-inserted.
Thanks a lot to Manu for this one... :D
Wednesday, 2 November 2016
oData Cacheing
Hi guys,
Caching eh? Supposed to make things a lot faster, prevent unnecessary trips to the server to get data the browser thinks it's got already.
Except it doesn't...
Here was our scenario : We had a UI5 application that dragged data from SAP into the browser. The user could change this, and save the changes back to SAP. So far so good...
except
when the user went out of the document, then back in again, they got the OLD version of the data. This took a bit of analysis to get to the bottom of.
Of course the first thing I tried was to put a breakpoint in the backend function, to see if that was even getting hit. It wasn't.
The next thing I tried was switching on debug mode (F12 in the browser - IE in my case) and using "Network"->"Monitor" to see what requests were going to the backend. Now, since my browser console is set to "Always refresh data from server" this then called SAP, and my breakpoint was hit...
The inconsistency here is really frustrating, as you can be debugging and see one set of results, tell your user that the problem is fixed, then have them see a different set of results.
So the issue here is that the oData results had been cached.
There were 2 ways round this... one "hackier" than the other...
1 : Spoof a filter option, that the backend then ignores...
This was a colleagues idea, and I liked it as it solved the problem without significant additional investigation. We included a dummy timestamped filter, which made the URL unique, and so the browser HAD to go to the backend because it couldn't find a cached result for that URL.
2: Switch off Caching :
https://blogs.sap.com/2014/05/28/disabling-cache-for-crudfi-odata-scenarios-for-a-ui5-application-using-ie/
Really liked this guys style!
Caching eh? Supposed to make things a lot faster, prevent unnecessary trips to the server to get data the browser thinks it's got already.
Except it doesn't...
Here was our scenario : We had a UI5 application that dragged data from SAP into the browser. The user could change this, and save the changes back to SAP. So far so good...
except
when the user went out of the document, then back in again, they got the OLD version of the data. This took a bit of analysis to get to the bottom of.
Of course the first thing I tried was to put a breakpoint in the backend function, to see if that was even getting hit. It wasn't.
The next thing I tried was switching on debug mode (F12 in the browser - IE in my case) and using "Network"->"Monitor" to see what requests were going to the backend. Now, since my browser console is set to "Always refresh data from server" this then called SAP, and my breakpoint was hit...
The inconsistency here is really frustrating, as you can be debugging and see one set of results, tell your user that the problem is fixed, then have them see a different set of results.
So the issue here is that the oData results had been cached.
There were 2 ways round this... one "hackier" than the other...
1 : Spoof a filter option, that the backend then ignores...
This was a colleagues idea, and I liked it as it solved the problem without significant additional investigation. We included a dummy timestamped filter, which made the URL unique, and so the browser HAD to go to the backend because it couldn't find a cached result for that URL.
2: Switch off Caching :
https://blogs.sap.com/2014/05/28/disabling-cache-for-crudfi-odata-scenarios-for-a-ui5-application-using-ie/
Really liked this guys style!
Thursday, 25 August 2016
Naming your Models in UI5
Once again, I'm indebted to the ABAP community for their generosity of spirit and sharing. This post explains really succinctly how you can mix and mash up your models by naming them as you set them.
http://scn.sap.com/thread/3948723
The biggest learning point for me was that the setModel method, which I'd thought indicated "Set the Model for the View" (i.e. you can only have one...)
this.getView().setModel(oSTModel,"TableModel")
Actually you can do this multiple times, setting several Models against the view. This is great, as we're now at the point where we need to draw data from several different places, and have them consolidated on the front end.
The named models can then be referred to by the XML view :
<core:ListItem text="{TableModel>Description}" additionalText="{TableModel>Key}" />
Tuesday, 23 August 2016
view.byID
So I'd seen code examples all over the place referring to "view.byID"
When I coded them in, they never worked.
It's because the view variable needs to be declared.
Once that's been done, you can re-use the variable over and over.
Really simple, but if no-one's told you, it's not apparent!
When I coded them in, they never worked.
It's because the view variable needs to be declared.
Once that's been done, you can re-use the variable over and over.
Really simple, but if no-one's told you, it's not apparent!
How to loop around a JSON result...
Okay, I spent ages looking for how to do this. The tricky part I found was marrying up the data in the response from the server, and the labels that people were giving different parts of the request... Soooooo I'm going to add to the noise, and hopefully help a few people out.
First up - the address of my service that's providing the JSON data:
/sap/opu/odata/sap/ZPD_RR_SRV/RepairActivitySet
Although clearly, that's the oData, so with the JSON tag on the end:
/sap/opu/odata/sap/ZPD_RR_SRV/RepairActivitySet?$format=json
This returns a list of Repair Activities (viewed from Gateway):
Note that it has the structure d/results/{Rakey/Radesc}
This is what I wanted to loop around in my UI5 javascript application, in one of my ViewControllers.
Here's the complete code that reads the lot:
var NRAModel = new sap.ui.model.json.JSONModel();
NRAModel.loadData("/sap/opu/odata/sap/ZPD_RR_SRV/RepairActivitySet", null, false);
var NRAModelArray = NRAModel.getData();
var myResults = [];
myResults = NRAModelArray.d.results;
console.log(myResults.length);
for( i = 0; i < myResults.length; i++) {
var obj = myResults[i];
console.log(myResults[i]);
console.log(myResults[i].Rakey );
console.log(myResults[i].Radesc);
}
The thing to note here, is that myResults is declared as an Array, and is populated by passing the d.results content from the original NRAModel.getData
d.results, you'll notice, is the same path as that provided by my Service, in the first screenshot.
Using console.log as you go along really helps with understanding what each of the variables are doing.
First up - the address of my service that's providing the JSON data:
/sap/opu/odata/sap/ZPD_RR_SRV/RepairActivitySet
Although clearly, that's the oData, so with the JSON tag on the end:
/sap/opu/odata/sap/ZPD_RR_SRV/RepairActivitySet?$format=json
This returns a list of Repair Activities (viewed from Gateway):
Note that it has the structure d/results/{Rakey/Radesc}
This is what I wanted to loop around in my UI5 javascript application, in one of my ViewControllers.
Here's the complete code that reads the lot:
var NRAModel = new sap.ui.model.json.JSONModel();
NRAModel.loadData("/sap/opu/odata/sap/ZPD_RR_SRV/RepairActivitySet", null, false);
var NRAModelArray = NRAModel.getData();
var myResults = [];
myResults = NRAModelArray.d.results;
console.log(myResults.length);
for( i = 0; i < myResults.length; i++) {
var obj = myResults[i];
console.log(myResults[i]);
console.log(myResults[i].Rakey );
console.log(myResults[i].Radesc);
}
The thing to note here, is that myResults is declared as an Array, and is populated by passing the d.results content from the original NRAModel.getData
d.results, you'll notice, is the same path as that provided by my Service, in the first screenshot.
Using console.log as you go along really helps with understanding what each of the variables are doing.
Wednesday, 17 August 2016
UI5 Deep Entities
Surprisingly, it's not possible to just pass a table of data (or an Array, if we're talking JavaScript) into an ABAP FM.
This was a little disappointing, given my oData experience so far went from
I think you can guess which one I preferred...
So when you need to populate lots of stuff into a function module (e.g. you're creating a Sales Order, and you need to pass header data and several lines of line Item data)
you need to use a "Deep Entity". This immediately makes me think that the entity is sitting around contemplating the meaning of life, questioning it's own existence, etc etc.
The setting up of a deep entity call is described in detail here:
http://scn.sap.com/community/gateway/blog/2014/04/27/step-by-step-development-guide-for-createdeepentity-operation
and the corresponding front-end call is described here:
https://scn.sap.com/thread/3557655
I'm about to go through these; if there's anything notable about them, I'll blog in some additions below. Peace.
This was a little disappointing, given my oData experience so far went from
- messing around with the DPC_EXT methods (where you had to do a bit of work to read the contents of the message from the frontend)
- just cloning the FM interface out to an oData structure (where you didn't have to do any work...
I think you can guess which one I preferred...
So when you need to populate lots of stuff into a function module (e.g. you're creating a Sales Order, and you need to pass header data and several lines of line Item data)
you need to use a "Deep Entity". This immediately makes me think that the entity is sitting around contemplating the meaning of life, questioning it's own existence, etc etc.
The setting up of a deep entity call is described in detail here:
http://scn.sap.com/community/gateway/blog/2014/04/27/step-by-step-development-guide-for-createdeepentity-operation
and the corresponding front-end call is described here:
https://scn.sap.com/thread/3557655
I'm about to go through these; if there's anything notable about them, I'll blog in some additions below. Peace.
Monday, 25 July 2016
UI5 SplitApp Container
Excellent jsbin here that explains how to have multiple views in a splitapp...
http://jsbin.com/aku_switch_views/1/edit?html,output
The bit I'd been missing for a day or two was:
app.toDetail which includes the natty UI5 swishy animation for no extra development effort from myself! :D
Whilst we're here, we should probably shout out jsbin for their excellent crowd-sourced examples library, which also include an online editor (like the w3schools example). Super tool!
http://jsbin.com/aku_switch_views/1/edit?html,output
The bit I'd been missing for a day or two was:
app.toDetail which includes the natty UI5 swishy animation for no extra development effort from myself! :D
Whilst we're here, we should probably shout out jsbin for their excellent crowd-sourced examples library, which also include an online editor (like the w3schools example). Super tool!
Monday, 11 July 2016
Test data table randomiser
Okay, so I need to populate some interesting test data in a Z-table I'm supposed to be reading. I could be really boring and set up a bunch of sequential data, which all drops out of the table in a boring organised way. This is right out, it'll just look really contrived.
I could set up a number of itabs, each with lists of possible data, then randomly attack each of them, getting me an interesting set of data. I've done this in the past, and it looked fine, just took a while.
I could just populate the table whilly-nilly. This works fine, again, takes a while, and doesn't get the computer to do the work...
The method I've just employed, I'm happy with; populate a small table, whilly-nilly, then randomly attack that table to make the data you send to the database. This has the required combination of randomness, interestingness, and capacity to create lots of data all at the once. (I'm only doing 20 here, but could just as easily be 200!).
define getRand .
CALL FUNCTION 'QF05_RANDOM_INTEGER'
EXPORTING
RAN_INT_MAX = 5
RAN_INT_MIN = 1
IMPORTING
RAN_INT = l_rndInt.
READ TABLE t_zpd_rh2 INDEX l_rndINt into &1.
end-OF-DEFINITION.
REPORT ZPD_POP_RH2.
"Dataset to randomise out of...
data: t_ZPD_RH2 type TABLE OF ZPD_RH2.
data: l_ZPD_RH2 type ZPD_RH2,
l_ZPD_RH2_feeder type ZPD_RH2,
l_rndInt type QF00-RAN_INT.
*(operation/workcentre/plant/description)
l_zpd_rh2-property = 1.
l_zpd_rh2-work_centre = 'WC1'.
l_zpd_rh2-plant = '1000'.
l_zpd_rh2-description = 'Replace boiler'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 2.
l_zpd_rh2-work_centre = 'WC2'.
l_zpd_rh2-plant = '2000'.
l_zpd_rh2-description = 'Renovate Bathroom'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 3.
l_zpd_rh2-work_centre = 'WC3'.
l_zpd_rh2-plant = '3000'.
l_zpd_rh2-description = 'Replace kitchen sink'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 4.
l_zpd_rh2-work_centre = 'WC4'.
l_zpd_rh2-plant = '4000'.
l_zpd_rh2-description = 'Fix leaky tap'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 5.
l_zpd_rh2-work_centre = 'WC5'.
l_zpd_rh2-plant = '5000'.
l_zpd_rh2-description = 'Diagnose bad smell'.
append l_zpd_rh2 to t_zpd_rh2.
data: l_operation type int4.
do 20 times.
clear l_zpd_rh2.
l_operation = l_operation + 1.
l_zpd_rh2-operation = l_operation.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-work_centre = l_ZPD_RH2_feeder-work_centre.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-plant = l_ZPD_RH2_feeder-plant.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-description = l_ZPD_RH2_feeder-description.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-property = l_ZPD_RH2_feeder-property.
append l_zpd_rh2 to t_zpd_rh2.
enddo.
MODIFY zpd_rh2 from TABLE t_zpd_rh2.
commit work and wait.
I could set up a number of itabs, each with lists of possible data, then randomly attack each of them, getting me an interesting set of data. I've done this in the past, and it looked fine, just took a while.
I could just populate the table whilly-nilly. This works fine, again, takes a while, and doesn't get the computer to do the work...
The method I've just employed, I'm happy with; populate a small table, whilly-nilly, then randomly attack that table to make the data you send to the database. This has the required combination of randomness, interestingness, and capacity to create lots of data all at the once. (I'm only doing 20 here, but could just as easily be 200!).
define getRand .
CALL FUNCTION 'QF05_RANDOM_INTEGER'
EXPORTING
RAN_INT_MAX = 5
RAN_INT_MIN = 1
IMPORTING
RAN_INT = l_rndInt.
READ TABLE t_zpd_rh2 INDEX l_rndINt into &1.
end-OF-DEFINITION.
REPORT ZPD_POP_RH2.
"Dataset to randomise out of...
data: t_ZPD_RH2 type TABLE OF ZPD_RH2.
data: l_ZPD_RH2 type ZPD_RH2,
l_ZPD_RH2_feeder type ZPD_RH2,
l_rndInt type QF00-RAN_INT.
*(operation/workcentre/plant/description)
l_zpd_rh2-property = 1.
l_zpd_rh2-work_centre = 'WC1'.
l_zpd_rh2-plant = '1000'.
l_zpd_rh2-description = 'Replace boiler'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 2.
l_zpd_rh2-work_centre = 'WC2'.
l_zpd_rh2-plant = '2000'.
l_zpd_rh2-description = 'Renovate Bathroom'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 3.
l_zpd_rh2-work_centre = 'WC3'.
l_zpd_rh2-plant = '3000'.
l_zpd_rh2-description = 'Replace kitchen sink'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 4.
l_zpd_rh2-work_centre = 'WC4'.
l_zpd_rh2-plant = '4000'.
l_zpd_rh2-description = 'Fix leaky tap'.
append l_zpd_rh2 to t_zpd_rh2.
l_zpd_rh2-property = 5.
l_zpd_rh2-work_centre = 'WC5'.
l_zpd_rh2-plant = '5000'.
l_zpd_rh2-description = 'Diagnose bad smell'.
append l_zpd_rh2 to t_zpd_rh2.
data: l_operation type int4.
do 20 times.
clear l_zpd_rh2.
l_operation = l_operation + 1.
l_zpd_rh2-operation = l_operation.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-work_centre = l_ZPD_RH2_feeder-work_centre.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-plant = l_ZPD_RH2_feeder-plant.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-description = l_ZPD_RH2_feeder-description.
getRand l_ZPD_RH2_feeder.
l_zpd_rh2-property = l_ZPD_RH2_feeder-property.
append l_zpd_rh2 to t_zpd_rh2.
enddo.
MODIFY zpd_rh2 from TABLE t_zpd_rh2.
commit work and wait.
Friday, 3 June 2016
PI Search Nuisance
PI Searching is case sensitive... thus
yields different results to:
I need all the help I can get with PI, and this is just irritating!
yields different results to:
I need all the help I can get with PI, and this is just irritating!
Monday, 18 April 2016
Memory ID tip..
Came across this neat trick today :
So, normally, we use the "export to memory id" and "import from memory id" commands when we need to pick up data from one place and read it in another, without the hassle of passing it from place to place as the code gets processed.
In the above case, there were many shell layers between where the variable was set, and where it was read. However, Simon has done us all a favour, and assigned the variable to Class-attribute. The beauty of this is that it makes the SET really easy to find if you've got the READ, and vice versa... It's a neat simple trick, but it's a good way of organising your code!
Wednesday, 6 January 2016
If Line Exists...
New to ABAP 740 (okay, that's well old, but new to me) this blog describes how to use the itab function "if line_exists"
Looks really useful!
Subscribe to:
Posts (Atom)