Retrieve "Variant" data type from Enterprise Architect using DXL - ole

How can I use the DXL OLE mechanism to fetch a diagram's modification time from Enterprise Architect 12?
Details:
I want to retrieve diagrams from EA and integrate them as OLE object into IBM Rational DOORS 9.5. This is already working. I intend to compare the modification dates of the EA diagram and the DOORS object before retrieving the diagram to decide if this operation is really needed.
Problem is, EA provides a diagram attribute EA.Diagram.ModifiedDate which returns the diagram's modification date as data type Variant. How can I handle this within DXL? The result parameter for oleGet() can be one of the types string|int|bool|char|OleAutoObj. No structured type (which would probably be DxlObject). Neither string nor int parameters contain any useful data after the call -- just null values.
Test code:
OleAutoObj eaRepository, eaProject, eaDiagram
OleAutoObj eaApp = oleGetAutoObject("EA.App")
OleAutoArgs autoArgs = create
string guid = "{729F140F-9DA4-4ff6-A9B2-75622AD1C22D}"
// connect to an existing EA instance
oleGet (eaApp, "Repository", eaRepository)
oleMethod (eaRepository, "GetProjectInterface", autoArgs, eaProject)
// call EA to a diagram which has this GUID
put(autoArgs, guid)
oleMethod(eaRepository, "GetDiagramByGuid", autoArgs, eaDiagram)
delete autoArgs
// access diagram attributes
string eaModifiedDate // DXL accepts [string|int|bool|char|OleAutoObj] only
oleGet(eaDiagram, "ModifiedDate", eaModifiedDate)
print "ModifiedDate = '" eaModifiedDate"'\n"
IBM's Support Team (commercial, available for paying customers) couldn't help and suggested to forward this question to the Service Team (for extra $s). Rather disappointing.

Try this one (just guessing :-)
OleAutoObj eaModifiedDate
oleGet(diagram, "ModifiedDate", eaModifiedDate)
if (null eaModifiedDate)
print "no eaModifiedDate\n"
else {
string diaDate
oleGet(eaModifiedDate, "Value", diaDate)
print "ModifiedDate = '" diaDate"'\n"
}
If that does not work then here comes the ultimate work around:
string err
string result
put(autoArgs, "SELECT ModifiedDate FROM t_diagram WHERE ea_guid = '" guid "'")
err = oleMethod (eaRepository, "SQLQuery", autoArgs, result)
if (!null err)
print "ERROR: " err "\n"
delete autoArgs
print "result= '" result"'\n"
This will return a XML formatted string (!) which contains the date in the format you like.
Edit 1: Turns out that EA's SQLQuery is buggy and returns just the date. Since Perl deals with OLE Variants in a similar way I found this code snippet:
my $dia = $rep->getdiagrambyguid("{63EFF3FA-0D5C-4986-AC0A-C723D2F755E3}");
my $value = $dia->ModifiedDate->Value;
print $value->Date( 'yyyy/MM/dd' );
print $value->Time( 'hh:mm:ss' );
So the ModifiedDate is an ole-object and has a Date and a Time method. This should work with DXL too.
Edit 2:Now here's the ulti-ultimate work around even shipping around the cliffs of EA's bug ocean:
my $dia = $rep->SQLQuery("SELECT Format (ModifiedDate, \"Short Time\") AS T FROM t_diagram");
This will format the date as time string. Works for EAP (aka Mickeysoft Access) according to this page. Other RDBMS likely have similar functions.

An update with a few corrections of my statements in comments.
I was wrong. It is indeed possible to receive structured data types using the OLE automation interface. As Thomas mentioned, the OleAutoObject is the proper DXL return type, oleGet() is the proper function. To access the returned object's attributes, it is required to be aware of the "real" data type and to have a look at the SDK documentation in order to know which class attributes are available since DXL is not aware of the data type.
However, in DXL, retrieving the attribute of a structured data type is a bit cumbersone. Example (intended to be appended to the inital post's code):
OleAutoObj diaLinksCollection
int count = -1
string buffer = ""
// access simple diagram attributes
err = oleGet(eaDiagram, "Version", buffer)
if (!null err) print "ERROR in Diagram.Version: " err "\n"
print "diagram version = " buffer "\n"
// access structured diagram attribute
err = oleGet(eaDiagram, "DiagramLinks", diaLinksCollection)
if (!null err) print "ERROR in Diagram.DiagramLinks: " err "\n"
err = oleGet(diaLinksCollection, "Count", count)
if (!null err) print "ERROR in Collection.Count: " err "\n"
print "count = " count "\n"
Thus, it is indeed possible to execute an SQL query, even if the repository is residing in a plain *.eap file, see Thomas' workaround.
As mentioned, the Variant data type can not be retrieved using oleGet() since this approach returns a NULL result.

Related

DoGet with multiple parameters not being recognized

I'm currently trying to connect a Lua Script with a GS WebApp. The connection is working but due to my lack of knowledge in GScripting I'm not sure why it isn't saving my data correctly.
In the Lua side I'm just passing in a hard-code a random name and simple numerical userid.
local HttpService = game:GetService("HttpService")
local scriptID = scriptlink
local WebApp
local function updateSpreadSheet ()
local playerData = (scriptID .. "?userid=123&name:Jhon Smith")
WebApp = HttpService:GetAsync(playerData)
end
do
updateSpreadSheet()
end
On the Google Script side i'm only saving the data on the last row and then add the value of the userid and the name.
function doGet(e) {
console.log(e)
// console.log(f)
callName(e.parameter.userid,e.parameter.name);
}
function callName(userid,name) {
// Get the last Row and add the name provided
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(sheet.getLastRow() + 1,1).setValues([userid],[name]);
}
However, the only data the script is saving is the name, bypassing the the userid for reasons I have yet to discover.
setValues() requires a 2D array and range dimensions should correspond to that array. The script is only getting 1 x 1 range and setValues argument is not a 2D array. Fix the syntax or use appendRow
sheet.getRange(sheet.getLastRow() + 1,1,1,2).setValues([[userid,name]]);
//or
sheet.appendRow([userid,name])
References:
appendRow

Application Insights and Azure Stream Analytics Query export the whole custom dimensions as string

I have setup a continuous export from Application Insights into Blog. With a data stream I'm able to get out the JSON files into SQL DB. So far so good.
Also with help from Phani Rahul Sivalenka I'm able to query the individual properties of custom dimensions as described here: Application Insights and Azure Stream Analytics Query a custom JSON property
My custom dimensions looks like this when exporting manually into CSV file:
"{""OperatingSystemVersion"":""10.0.18362.418"",""OperatingSystem"":""WINDOWS"",""RuntimePlatform"":""UWP"",""Manufacturer"":""LENOVO"",""ScreenHeight"":""696"",""IsSimulator"":""False"",""ScreenWidth"":""1366"",""Language"":""it"",""IsTablet"":""False"",""Model"":""LENOVO_BI_IDEAPAD4Q_BU_idea_FM_""}"
Additionally to the single columns I like to have the whole custom dimensions as a string in a SQL Table column (varchar(max)).
In the "Test results" of my Data Stream Output Query I see the column as formated above - but when really exporting / wrinting into SQL DB all my tests ended having only the value "Array" or "Record" as value in my SQL Table column.
What do I have to do in the Data Stream Query to get the whole custom dimensions value as a string and I'm able to write this into SQL Table as a whole string?
What do I have to do in the Data Stream Query to get the whole custom
dimensions value as a string and I'm able to write this into SQL Table
as a whole string?
You could use UDF to merge all key-values of single raw into one single json format string.
UDF:
function main(raw) {
let str = "{";
for(let key in raw) {
str = str + "\""+ key+"\":\""+raw[key]+"\",";
}
str += "}";
return str;
}
SQL:
SELECT udf.jsonstring(INPUT1) FROM INPUT1
Output:
The answer brought me on the right track.
The above script don't include the values as expected. So I modified the script to get it work as needed:
function main(dimensions) {
let str = "{";
for (let i in dimensions)
{
let dim = dimensions[i];
for (let key in dim)
{
str = str + "\"" + key+ "\":\""+dim[key]+"\",";
}
}
str += "}";
return str;
}
Selecting:
WITH pageViews as (
SELECT
V.ArrayValue.name as pageName
, *
, customDimensions = UDF.flattenCustomDimensions(A.context.custom.dimensions)
, customDimensionsString = UDF.createCustomDimesionsString(A.context.custom.dimensions)
FROM [AIInput] as A
CROSS APPLY GetElements(A.[view]) as V
)
With this I'm getting the custom dimensions string as follow in my SQL table:
{"Language":"tr","IsSimulator":"False","ScreenWidth":"1366","Manufacturer":"Hewlett-Packard","OperatingSystem":"WINDOWS","IsTablet":"False","Model":"K8K51ES#AB8","OperatingSystemVersion":"10.0.17763.805","ScreenHeight":"696","RuntimePlatform":"UWP",}

Node red multiple msg.payloads

i got 2 nodes fetching some mqtt data, some temperature and humidity readings. Im trying to pass that info to a sqlite node. On that node i have the following code:
var newMsg = {
"topic": "INSERT INTO ambiente VALUES (null, #thisshouldbetemperature, #thisshouldbehumidity, date('now'), time('now') )"
}
return newMsg;
I try to use a join node but didnt make the work. So, what is the correct way to pass both msg.payload to that function? Thanks!
The join node is the correct way to combine the 2 incoming messages. You should use the manual mode and configure it to create a key value object something like this.
The problem is that your function node is ignoring the incoming data and creating a new message with just a topic set.
The fix for the function node is:
msg.topic = "INSERT INTO ambiente VALUES (null, " + msg.payload.temperature + ", " + msg.payload.humidity + " , date('now'), time('now') )";
return msg;
This will just update the msg.topic and leave the incoming msg.payload intact. This assumes that the MQTT messages arrive on topics temperature and humidity.

How to use SQLitePCL execute content of sql file

I'm trying to use SQLitePCL package to develop a simple UWP app that executes database commands (create-select-update-delete). I created a database sql file that contains some sqlite commands and I'm trying to execute them in my code:
Uri appUri = new Uri("ms-appx:///Assets/db.sql");
StorageFile sFile = StorageFile.GetFileFromApplicationUriAsync(appUri).AsTask().ConfigureAwait(false).GetAwaiter().GetResult();
string sSQL = FileIO.ReadTextAsync(sFile).AsTask().ConfigureAwait(false).GetAwaiter().GetResult();
ISQLiteStatement cnStatement = dbConnection.Prepare(sSQL);
cnStatement.Step();
But when I run the program, it only executes the first statement in the sql file which is CREATE command and exit without executing the rest of the commands. Here is the sample content of the sql file:
CREATE TABLE Superhero (
Type TEXT PRIMARY KEY,
Picture TEXT
);
INSERT INTO Superhero (
Type,
Picture
)
VALUES (
'batman',
'batman.ico'
);
Anyone knows if there is a way in SQLitePCL to execute a sql file?
Any help would be very much appreciated!
Thanks!
According to the description of sqlite3_prepare interface:
These routines only compile the first statement in zSql, so *pzTail is left pointing to what remains uncompiled.
So that it seems like only the first statement in the commands is actually executed. The remainder is silently ignored. Since every command is ended up with symbol ";", for a quick and simple solution, you may just split the sql commands into single statements and then execute one by one. For example:
string sSQL = FileIO.ReadTextAsync(sFile).AsTask().ConfigureAwait(false).GetAwaiter().GetResult();
var dbConnection = new SQLiteConnection("sun.db", SQLiteOpen.READWRITE);
//using (ISQLiteStatement cnStatement = dbConnection.Prepare(sSQL))
//{
// var result = cnStatement.Step();
//}
var statements = sSQL.Split(new[] { ';' });
foreach (string onestate in statements)
{
using (ISQLiteStatement cnStatement = dbConnection.Prepare(onestate))
{
var result = cnStatement.Step();
}
}
Otherwise, you may need to update the SQLitePCL Nuget package.

Concat 2 strings erlang and send with http

I'm trying to concat 2 variables Address and Payload. After that I want to send them with http to a server but I have 2 problems. When i try to concat the 2 variables with a delimiter ';' it doesn't work. Also sending the data of Payload or Address doesn't work. This is my code:
handle_rx(Gateway, #link{devaddr=DevAddr}=Link, #rxdata{port=Port, data= RxData }, RxQ)->
Data = base64:encode(RxData),
Devaddr = base64:encode(DevAddr),
TextAddr="Device address: ",
TextPayload="Payload: ",
Address = string:concat(TextAddr, Devaddr),
Payload = string:concat(TextPayload, Data),
Json=string:join([Address,Payload], "; "),
file:write_file("/tmp/foo.txt", io_lib:fwrite("~s.\n", [Json] )),
inets:start(),
ssl:start(),
httpc:request(post, {"http://192.168.0.121/apiv1/lorapacket/rx", [], "application/x-www-form-urlencoded", Address },[],[]),
ok;
handle_rx(_Gateway, _Link, RxData, _RxQ) ->
{error, {unexpected_data, RxData}}.
I have no errors that I can show you. When I write Address or Payload individually to the file it works but sending doesn't work...
Thank you for your help!
When i try to concat the 2 variables with a delimiter ';' it doesn't work.
5> string:join(["hello", <<"world">>], ";").
[104,101,108,108,111,59|<<"world">>]
6> string:join(["hello", "world"], ";").
"hello;world"
base64:encode() returns a binary, yet string:join() requires string arguments. You can do this:
7> string:join(["hello", binary_to_list(<<"world">>)], ";").
"hello;world"
Response to comment:
In erlang the string "abc" is equivalent to the list [97,98,99]. However, the binary syntax <<"abc">> is not equivalent to <<[97,98,99]>>, rather the binary syntax <<"abc">> is special short hand notation for the binary <<97, 98, 99>>.
Therefore, if you write:
Address = [97,98,99].
then the code:
Bin = <<Address>>.
after variable substitution becomes:
Bin = <<[97,98,99]>>.
and that isn't legal binary syntax.
If you need to convert a string/list contained in a variable, like Address, to a binary, you use list_to_binary(Address)--not <<Address>>.
In your code here:
Json = string:join([binary_to_list(<<Address>>),
binary_to_list(<<Pa‌​yload>>)],
";").
Address and Payload were previously assigned the return value of string:concat(), which returns a string, so there is no reason to (attempt) to convert Address to a binary with <<Address>>, then immediately convert the binary back to a string with binary_to_list(). Instead, you would just write:
Json = string:join(Address, Payload, ";")
The problem with your original code is that you called string:concat() with a string as the first argument and a binary as the second argument--yet string:concat() takes two string arguments. You can use binary_to_list() to convert a binary to the string that you need for the second argument.
Sorry I'm new to Erlang
As with any language, you have to study the basics and write numerous toy examples before you can start writing code that actually does something.
You don't have to concatenate strings. It is called iolist and is one of best things in Erlang:
1> RxData = "Hello World!", DevAddr = "Earth",
1> Data = base64:encode(RxData), Devaddr = base64:encode(DevAddr),
1> TextAddr="Device address", TextPayload="Payload",
1> Json=["{'", TextAddr, "': '", Devaddr, "', '", TextPayload, "': '", Data, "'}"].
["{'","Device address","': '",<<"RWFydGg=">>,"', '",
"Payload","': '",<<"SGVsbG8gV29ybGQh">>,"'}"]
2> file:write_file("/tmp/foo.txt", Json).
ok
3> file:read_file("/tmp/foo.txt").
{ok,<<"{'Device address': 'RWFydGg=', 'Payload': 'SGVsbG8gV29ybGQh'}">>}

Resources