go to post Dmitry Maslennikov · Feb 13, 2019 It would be better if you could provide some of your code.
go to post Dmitry Maslennikov · Feb 5, 2019 And how it looks like just with telnet $ telnet echo.websocket.org 80 Copy-Paste this data GET / HTTP/1.1 Accept: */* Host: echo.websocket.org Connection: Upgrade Upgrade: websocket Sec-WebSocket-Key: 7BOhi3I1WkBoazaXv+MfWA== Sec-WebSocket-Version: 13 After the empty line, it will show response HTTP/1.1 101 Web Socket Protocol Handshake Connection: Upgrade Date: Tue, 05 Feb 2019 10:37:17 GMT Sec-WebSocket-Accept: /gSfI5y+P3MMhONARUXNHG5vrHc= Server: Kaazing Gateway Upgrade: websocket
go to post Dmitry Maslennikov · Feb 5, 2019 You should not forget that WebSockets is still used the HTTP protocol, so, you should send some headers first.Look at this my code, it uses plain OPEN, and I'm not sure if my example 100% correct, but works. set securityKey = $SYSTEM.Util.CreateGUID() set securityKey = $SYSTEM.Encryption.MD5Hash(securityKey) set securityKey = $SYSTEM.Encryption.Base64Encode(securityKey) set host = "echo.websocket.org" set url = "/" set port = 80 set device = "|TCP|"_port Open device:(host:port:"SCWD"::8192:8192:/TCPNOXY) Use device Write "GET ",url," HTTP/1.1",! Write "Accept: */*",! Write "Host: ",host,! Write "Connection: Upgrade",! Write "Upgrade: websocket",! Write "Sec-WebSocket-Key: ",securityKey,! Write "Sec-WebSocket-Version: 13",! Write !,*-3 Use device:(::"A":$char(13)) Set fullResponse = "" Do { Set response = "" Read response:1 Quit:'$test Set fullResponse = fullResponse_response_$char(13) } While $test Use 0 Close device Write !!,fullResponse In this case, it only reads the first response, which actually should be with HTTP headers as well. Something like this. HTTP/1.1 101 Web Socket Protocol Handshake Connection: Upgrade Date: Tue, 05 Feb 2019 10:51:37 GMT Sec-WebSocket-Accept: qU2IAmlBvnSoEctnti8lcbc4bVA= Server: Kaazing Gateway Upgrade: websocket It does not contain the first portion of data, which some WebSocket servers may send after initial connect. But if your server sends it, you should see it at the and of response. If you have to send something before, you should do it after the first response, which says that connection established and you can send any data. But not any, it should be in binary format, more details you can find here. Any responses also decoded.
go to post Dmitry Maslennikov · Jan 31, 2019 first of all, you have to check docker logs iristest2 And while you trying to use durable %SYS, you look at IRIS log /nfs/IRIS/iconfig/mgr/message.log. And as Jim already noticed, you have double dash when defining the variable ICM_SENTINEL_DIR
go to post Dmitry Maslennikov · Jan 30, 2019 If you can edit this code, you can try change to this. <Data name="DESCRIP_2"> <RetrievalCode> S {DESCRIP_2}=$P($G(^PHPROP({L1},"DESC_CODES")),"\",2) S {DESCRIP_2}=$S($L({DESCRIP_2}):$Get(^SEDMIHP($P({DESCRIP_2},","),$P({DESCRIP_2},",",2))),1:{DESCRIP_2}) S {DESCRIP_2}=$E({DESCRIP_2},1,80) </RetrievalCode> </Data> But not sure, if this correct. What I did there, is, wrapped retrieving data from global ^SEDMIHP with the function $Get() Or this way, with the default value <Data name="DESCRIP_2"> <RetrievalCode> S {DESCRIP_2}=$P($G(^PHPROP({L1},"DESC_CODES")),"\",2) S {DESCRIP_2}=$S($L({DESCRIP_2}):$Get(^SEDMIHP($P({DESCRIP_2},","),$P({DESCRIP_2},",",2)),{DESCRIP_2}),1:{DESCRIP_2}) S {DESCRIP_2}=$E({DESCRIP_2},1,80) </RetrievalCode> </Data>
go to post Dmitry Maslennikov · Jan 27, 2019 Well, such an interesting topic, and also quite wide.Ok, TWAIN, is an API for image sources, it can be scanner or photo camera. You just asking about working with TWAIN, without any explanation of how you are going to use it and what sort of devices you going to utilize.So, I'll just share some of my experience. How I've used only scanners, different types and with different workflows.Server way. One or more stream document scanner (sorry don't know right term in English). Just any scanner which can work in network and configured to place all scans to some network folder or send my emails. Before sending some documents to this scanner, the operator should stick some barcode, on the title page, or on added empty first page. On the server side, we have used ABBYY Recognition Server, which just watches some folder, and can decode barcode and recognize text in the document, pack it in XML and place it in another folder. This folder was watched by Ensemble, where we searched for barcode in our system and placed this as an attachment to this document. With recognized text, we also are able to search documents in our system by the content of this document.Client way. The operator working with a personal scanner connected right to his machine. While our application web-based and the server is far from this scanner, we used only client resources to scan images. We used java-applet which worked directly with TWAIN sources, so operator just called some functionality right from our application after finishing the scan, it has appeared as an attachment in the document. But this case now has some issues due to limitations for JAVA plugins in modern browsers. But it is now possible to find some modern solutions which may help to get access to twain on modern browsers, you can just google it.
go to post Dmitry Maslennikov · Jan 23, 2019 SSO, can be achieved in some different ways. It can work over OAuth2, NTLM, Kerberos, SAML and so on. In different projects, I have used Kerberos/NTLM and OAuth2. But real SSO was only with Kerberos. And when you already have LDAP Auth in your application, it will be quite easy to add SSO. But also depends on which OS and which WEB server you have. On Windows much easier to start with IIS while so difficult to find a working module for Apache. On Linux there is also could be a problem to find the latest version of the module which will work with the latest version of apache. But when you will manage to get it worked on web-server side, on Caché side, almost nothing to do left. When you get first unauthorized request, you should return back with status 401, and say which method of authentication you need through header WWW-Authenticate: NTLM. Then if web server managed to get username, it will send it by header REMOTE_USER. Of course, you will not get password, you just use this username and authorize session.
go to post Dmitry Maslennikov · Jan 23, 2019 If your server on windows, you have two connection options. Terminal. Available only locally, and can use windows security. Enabled by default.Telnet. Used to connect from outside. Disabled by default (you can activate this service in SMP.). After enabling, you can connect using terminal or any other tool by default port 22.If your server on Linux. You have only one option is csession or irissession tool which works only locally to the server. For remote access you should use ssh or telnet. But you can't configure telnet from Cache. You should do it by yourself.
go to post Dmitry Maslennikov · Jan 22, 2019 How Caché works with licenses, actually very interesting and sometimes quite difficult to understand, but it is possible to find a balance for everybody. Fortunately, at the same time, it has actually some tricks on how to turn it on your side.The first important thing is every time when user login, you should log in the user not only in security but license as well. In this case, if the user uses the same IP address will be used the license unit.In your case, I'm not even sure about forcing log out, do you really need it? You mentioned that your application still web, and does not matter is at wrapped as an application or opened right from the browser. You should have the same behaviour. So, you can reduce timeout for the session, add some timer which will ping the server from time to time when app is active to extend session time. Sessions on the server side also have a grace period after a timeout which is 5 minutes long, in most cases enough to return back from the call.
go to post Dmitry Maslennikov · Jan 7, 2019 CACHE.DAT or even IRIS.DAT does not have any relation with operation system. So, you can easily move database files between instances on different OS and versions. But you should remember about data itself, like differences between 8-bit and Unicode. And code, which should be compiled for particular version but also not depends on OS.You can find some details about database format from my articles.
go to post Dmitry Maslennikov · Dec 20, 2018 Every debugging tied very much with an editor of code. So, the first question should be, which editor should be used. The studio already has some debugging options, Atelier, too. I'm working hard now on VisualStudio code, and they also have good ways for debugging and hope I will manage to add good debugger for VSCode as well, but not only me who can do it. George James already has their debugger and introduced their work on the same debugger in VSCode.
go to post Dmitry Maslennikov · Dec 16, 2018 when you get the error <DYNAMIC LIBRARY LOAD>, you should look at cconsole.log (or messages.log for IRIS), where you may find code of error.It is possible that you build it for 32bit, but uses in 64bit instance. In this case, you will get the error with code 139. if you got other code, you can google it.
go to post Dmitry Maslennikov · Nov 27, 2018 I'm sure it is quite difficult to write such a universal solution, every time it depends on your code. Years ago I wrote such a tool for an application which textual interface, and I had to convert parse code for pseudographics there and convert it too. So, I would say you need such specific too, just for your case.
go to post Dmitry Maslennikov · Nov 26, 2018 STORE error means, your reached limit of memory per process. Since 2012.2 we have 256Mb per process by default and you can increase it up to 2Tb. You can increase it, but I would recommend playing with different ways how to collect SQL Data, or on optimization in this SQL query. Or you can split such many rows by portions with less number of rows.
go to post Dmitry Maslennikov · Nov 20, 2018 batch file means, that you want to do it outside of Ensemble. In this case you should use ccontrol command or iris if you use IRIS. ccontrol stop <ensemble> quietly ccontrol start <ensemble> where you should replace <ensemble> with your instance name. if you work on windows, you can find ccontrol tool in bin directory of installed Ensemble. you can find more details about using ccontrol in the documentation
go to post Dmitry Maslennikov · Nov 20, 2018 Better to use command JOB INT^SHUTDOWN instead of DO. When you use DO, the task will not be finished and will be marked as suspended, and you will have to resume it manually.
go to post Dmitry Maslennikov · Nov 16, 2018 The example in the documentation to %XML.Reader, does not work for you? #include %occStatus // Create a new XML Reader class Set reader = ##class(%XML.Reader).%New() // Begin processing of the XML input Set sc=reader.OpenFile(filename) If $$$ISERR(sc) Do $system.OBJ.DisplayError(sc) Quit // Associate a class name with the XML element name Do reader.Correlate("Person","Sample.Person") // read Sample.Person objects from xml file Set Count=0 While reader.Next(.person,.sc) { Write person.Name_" imported.",! Set Count=Count+1 Set sc=person.%Save() If $$$ISERR(sc) Do $system.OBJ.DisplayError(sc) Quit } If $$$ISERR(sc) Do $system.OBJ.DisplayError(sc) Quit Write Count_" Sample.Person instances found."
go to post Dmitry Maslennikov · Nov 16, 2018 ROLLBACK should revert any changes in data which was done in a transaction, with some exceptions like $increment on Global.You can look at this example. Class User.Test Extends %Persistent { Property Name As %String; Property CalcName As %String [ Calculated, SqlComputeCode = { set {*} = {Name} }, SqlComputed, SqlComputeOnChange = Name ]; Index ByName On CalcName; Storage Default { <Data name="TestDefaultData"> <Value name="1"> <Value>%%CLASSNAME</Value> </Value> <Value name="2"> <Value>Name</Value> </Value> </Data> <DataLocation>^User.TestD</DataLocation> <DefaultData>TestDefaultData</DefaultData> <IdLocation>^User.TestD</IdLocation> <IndexLocation>^User.TestI</IndexLocation> <StreamLocation>^User.TestS</StreamLocation> <Type>%Storage.Persistent</Type> } } Let's create first object USER>s o=##class(Test).%New() USER>set o.Name="testname" USER>w o.%Save() 1 check saved in globals USER>zw ^User.TestD,^User.TestI ^User.TestD=1 ^User.TestD(1)=$lb("","testname") ^User.TestI("ByName"," TESTNAME",1)="" and now open transaction, and do some changes. USER>k USER>TSTART TL1:USER>set o=##class(Test).%OpenId(1) TL1:USER>s o.Name="testname2" TL1:USER>w o.%Save() 1 TL1:USER>zw ^User.TestD,^User.TestI ^User.TestD=1 ^User.TestD(1)=$lb("","testname2") ^User.TestI("ByName"," TESTNAME2",1)="" So, changes there, let's do rollback, and check data again. TL1:USER>TROLLBACK USER>zw ^User.TestD,^User.TestI ^User.TestD=1 ^User.TestD(1)=$lb("","testname") ^User.TestI("ByName"," TESTNAME",1)=""
go to post Dmitry Maslennikov · Nov 16, 2018 I think it will not be possible to restore backups from Caché/Ensemble to IRIS.I'm sure that in most cases, it is possible to just rename CACHE.DAT to IRIS.DAT, and configure it exactly as it was in Ensemble, should work. And maybe it will work for you as well.If you can easily repeat configuration on any just installed Ensemble server, do it in the same way for IRIS.
go to post Dmitry Maslennikov · Nov 16, 2018 I think we don't have any other possibilities how to catch changes in source code. Do you know how to catch if any object in any class was changed, outside of this class, if you don't have any triggers there? No easy task. But I think it is possible to find some tricky ways if you don't care about just in time notifications. You can monitor any changes in the data, and filter by changing particular globals and subscripts. If you will describe what are you going to achieve exactly, it will be easier to help you.