go to post Eduard Lebedyuk · May 27, 2019 What's the use case? I think it's better to keep independent tasks separate.Anyway:1. Create your own task class by extending %SYS.Task.Definition.2. Program your taskSpecify TaskName parameter to provide readable task nameAdd class properties if needed - they are task argumentsOverride OnTask method to program your logic3. Create a new task, choosing your task class (by TaskName).
go to post Eduard Lebedyuk · May 13, 2019 You need to specify where you want to send your request. Something like this should work. Method OnD03Alert(req As User.Alert, Output resp As Ens.StreamContainer) As %Status { #Dim sc As %Status = $$$OK #Dim test As %Integer = 0 Set httprequest = ##class(%Net.HttpRequest).%New() Set httprequest.Server = "www.usamobility.net" Set httprequest.Location = "cgi-bin/wwwpage.exe" Do httprequest.SetParam("PIN", ..PIN) Do httprequest.SetParam("MSSG", "motogplay") Set sc = httprequest.Post(, test, $$$NO) If $$$ISOK(sc) { Set stream = httprequest.HttpResponse.Data Set resp = ##class(Ens.StreamContainer).%New(stream) } Quit sc } Also Ensemble operations must return persistent objects so I replaced string with Ens.StreamResponse.
go to post Eduard Lebedyuk · May 6, 2019 1) Yes.2) Use zwrite command for debugging and key access to get individual values: zwrite tSettings set value = tSettings("MySettingName") Note, that if you want production host settings, you'll need to use GetHostSettings method from the same class.
go to post Eduard Lebedyuk · Apr 23, 2019 Why do you want to achieve that?You need custom datatype for that, defining your parameters: Class Testing.String Extends %String { Parameter myPropName; } And use that as a property type: Class Testing.PropertyParameters Extends %RegisteredObject { Property p1 As Testing.String(MAXLEN = 5, myPropName = "myPropValue"); } Still, please tell us your use case.
go to post Eduard Lebedyuk · Apr 19, 2019 You can store list properties same as array properties: Property Collection As list Of ICT.Experiments.B(SQLPROJECTION = "table/column", STORAGEDEFAULT = "list"); Note that you need first to delete class storage if any exists and either recreate or move the data to comply with the new storage.
go to post Eduard Lebedyuk · Apr 18, 2019 Working code: int GetGlobalOrder(char *global, int start, int end, CACHE_EXSTRP result) { if (isInitialized == false) { Initialize(NULL); } // narg Number of subscript expressions pushed onto the argument stack. int narg = 1; // Direction for the $Order is 1 for forward, -1 for reverse. int dir = 1; // Indicates whether the data value, if there is one, should be returned. int valueflag = 1; // Has argument flag int flag = 0; // key - current subscript int key = start - 1; // row count int c=0; while (key<end) { CACHEPUSHGLOBAL(strlen(global), global); CACHEPUSHINT(key); CACHEGLOBALORDER(narg, dir, valueflag); CACHEPOPINT(&flag); if (flag) { //use CACHETYPE() to get the value type, then the appropriate CACHEPOP for the subscript CACHEEXSTRKILL(result); CACHEPOPEXSTR(result); // do stuff with result } //use CACHETYPE() to get the subscript type, then the appropriate CACHEPOP for the subscript int type = CACHETYPE(); CACHEPOPINT(&key); if (key==NULL) { break; } c++; } return ZF_SUCCESS; } Thanks to @Chuck Sorenson for help!
go to post Eduard Lebedyuk · Apr 18, 2019 Here's a sample C code to iterate over $lb structure. #include <math.h> /// Convert unsigned integer bytes to integer int64_t makeint(const char *buff, size_t offset, size_t offsetinint, size_t len) { union { int64_t i64; uint8_t u8[8]; }d64; offsetinint = offsetinint & 7; memset(&d64, 0, sizeof(d64)); memcpy(&d64.u8[offsetinint], buff + offset, len > (8 - offsetinint) ? (8 - offsetinint) : len); return d64.i64; } /// get next power of 2 greater than v int64_t next2(int64_t v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v |= v >> 32; v++; return v; } /// List types. NONE and NONE2 are placeholders /// See %CACHE_HOME%\dev\Cache\callout\demo\czf.pdf (Section "Lists") enum ListTypes {NONE, STRING, USTRING, NONE2, INTP, INTN, DOUBLEP, DOUBLEN, FLOAT}; /// This function iterates over all elements in a list void ListToTuple(CACHE_EXSTRP result) { // $lb structure char* list = result->str.ch; int listLength = result->len; // current element int num = 0; // current byte position int i=0; // length of current element int l = 0; // datatype int type = 0; while (i<listLength) { // Calculate length of current element - START if (0 == (l = (list[i]&255))) { // First BYTE is 0, length is in following 2 BYTEs size_t t_n = ((list[i+1]&255)|((list[i+2]&255)<<8)); if (t_n != 0) { l = t_n + 3; } else { // 4 Byte length l = ((list[i+3]&255) | ((list[i+4]&255) << 8) | ((list[i+5]&255) << 16) | ((list[i+6]&255) << 24)) + 7; } } // Calculate length of current element - END // Calculate data position - START int dataStart = 0; int dataLength = 0; if (l < 255) { type = list[i+1]; dataStart = i + 2; dataLength = l - 2; } else if (l < 65536) { type = list[i+3]; dataStart = i + 4; dataLength = l - 4; } else { type = list[i+7]; dataStart = i + 8; dataLength = l - 8; } // Calculate data position - END if (type==STRING) { char* value; memcpy(value, list+dataStart, dataLength); } else if (type==USTRING) { char* value; memcpy(value, list+dataStart, dataLength/2); } else if (type == INTP) { int64_t value = makeint(list, dataStart, 0, dataLength); } else if (type==INTN) { int64_t value = 0; if (l==2) { value = -1; } else { memcpy(&value, list+dataStart, dataLength); if (value == 0) { value = - (1 << (dataLength * 8)); } else { int64_t pow2 = next2(value); value = value - pow2; } } } else if (type==DOUBLEP) { int64_t temp = makeint(list, dataStart+1, 0, dataLength-1); signed char exp = list[dataStart]; double value = temp*pow(10, exp); } else if (type==DOUBLEN) { int64_t temp = 0; memcpy(&temp, list+dataStart+1, dataLength-1); if (temp == 0) { temp = - (1 << (dataLength * 8)); } else { int64_t pow2 = next2(temp); temp = temp - pow2; } signed char exp = list[dataStart]; double value = temp*pow(10, exp); } else if (type==FLOAT) { double value; memcpy(&value, list+dataStart, 8); } i += l; num++; } }
go to post Eduard Lebedyuk · Apr 13, 2019 After you sent ASYNC call in BP add sync activity next. Process would wait for ASYNC call to complete before going further.
go to post Eduard Lebedyuk · Apr 11, 2019 Here's one idea (no idea if it would work though):Extract query into a new csp page.Make it auto-refresh with META TAGSEmbed it into a root page using INCLUDE.Long-term I recommend using REST API for that.
go to post Eduard Lebedyuk · Apr 11, 2019 Please provide a minimal example if possible.request and response objects should be available anywhere in the process job.
go to post Eduard Lebedyuk · Apr 10, 2019 What do you mean by "After that you need to create an instance of this class and add it to HeaderOut property of your WS client."I mean you create an object from the class using %New method.What is HeadersOut and where do I set it?HeadersOut is a property of your WebClient object.
go to post Eduard Lebedyuk · Apr 9, 2019 Do you want it for debugging or logging purposes?If for debugging, here's a series of articles Debugging Web (part 1, part 2) dealing with REST Debugging.Atelier can be used to debug REST.
go to post Eduard Lebedyuk · Apr 8, 2019 Comment out OnGetConnections method and restart the service.Do you still get errors?
go to post Eduard Lebedyuk · Apr 5, 2019 I would like to advise against this course of action, there are several reasons:Ensemble messages should be as small as possible. If you can pass id only, pass id only, etc. Minimizing message size keeps production efficient.ResultSet does not contain the data itself, it's more like a compiled instructions on how to get the data. Only when you're calling Next method the data is actually loaded into memoryResults of SQL query should be immediately acted upon, because the older the SQL results, the more they differ with reality. it is best to pass parameters and execute query in-place.If you want it for debugging purposes you can add a trace event which would store ids only for example, but if you actually need to pass a set of results between different business hosts it may be beneficial to you to rethink the production architecture.Here's some questions:What business hosts do you have?What data do you pass between different hosts?Can you pass parameters between hosts and execute a query on a target host?Can you pass not a set between hosts, but rather elements individually?
go to post Eduard Lebedyuk · Apr 2, 2019 Some options:Check this exampleUse ZEN reportsProduce CSV and use LibreOffice to convert CSV to XLSX. Article about this approach.Use Apache POI to generate XLSX. Here's Apache POI JGW wrapper and ObjectScript API but you'll need to extend it.> - Utilize another language or library that has methods to parse the data into an Excel file (https://stackoverflow.com/questions/17684610/python-convert-csv-to-xlsx).If you want to use Python from Cache, check PythonGateway.
go to post Eduard Lebedyuk · Apr 1, 2019 If you want to transfer data via REST check RESTForms project (part 2).
go to post Eduard Lebedyuk · Mar 29, 2019 Mimedata is subscripted by name and index.So in your case: set name = "BulkFileUpload" for i=1:1:%request.CountMimeData(name) set mimeData = %request.GetMimeData(name, , i) } On each iteration mimeData variable would hold the stream with one next mimedata. %request is simply an object of %CSP.Request class, check the docs or code to know how it works. Additionally you can use this snippet to see what's inside %request, %response and %session objects: set %response.ContentType = "html" do ##class(%CSP.Utils).DisplayAllObjects() quit $$$OK
go to post Eduard Lebedyuk · Mar 29, 2019 <FILEFULL> - Caché attempted to allocate a disk block for more global data or routine storage, but the attempt failed because the Caché database is full and could not be expanded.Affected db is /hs-connect-hom/db/BPINTEGRADEV-GLB.