Nikita Savchenko · Jan 20, 2017 go to post

Mark,

Good point on the output issue. I was thinking to make non-freezing output during the development but postponed this idea in favor of synchronous output (which is freezing browser until all the content renders). I will take this into account in future versions of WebTerminal.

CTRL-C for now works as a default combination for copying text. Unfortunately, after reading documentation a lot, I am still having no idea how to implement interrupt in WebTerminal. Technically speaking, I need to send the interrupt signal to a jobbed process somehow, but continue to execute it from, for example, some label in the COS code. Any help here is appreciated!

To track the interrupt enhancement, I have created this issue.

Nikita Savchenko · Jan 19, 2017 go to post

Hello Mark, thank you!

I did a little fast research and found that it is possible to make the behavior of printing long output similar to the standard Caché terminal (just to make terminal scroll and scroll through the text until the output stops). But how do you think, does it makes sense to replace this "freezing" behavior with always-scrolling output? In both cases, the output is not readable (normally readable). Maybe it makes more sense to display a floating message like "Output in progress..." when WebTerminal freezes for more than 1 second?

Would be glad to hear your opinion, thanks!

Nikita Savchenko · Jan 3, 2017 go to post

Thanks for hint, Edward, but how can I start using it? open 3564 use 3564 write ##class(%Library.Device).GetType() close 3564 command throws <NOTOPEN> exception.

Nikita Savchenko · Jan 3, 2017 go to post

Hello Sebastian!

I am glad to hear you like WebTerminal. I do as much as possible to make it even better.

For now, WebTerminal may still have some escape sequences not implemented, and there is one issue that may also cause old "dinosaur" application to work in a little bit different way. If you find the reason causing the terminal application not to work as expected, please, feel free to describe this in the GitHub's issues section.

Nikita Savchenko · Jan 3, 2017 go to post

Kevin,

Please see my answer below and tell me if it helps.

Try using the browser's incognito mode or reinstalling WebTerminal if you need to log in from scratch.

Nikita Savchenko · Jan 3, 2017 go to post

Hello Kevin!

It's all about setting up the /terminal web application. Go to the Management Portal -> System Administration -> Security -> Applications -> Web Applications, then choose /terminal web application.

There you can specify the required resource for this web application:

For example, you can specify %Development resource, which means that any users that do not have the access to this resource will not be able to log in to WebTerminal.

As _SYSTEM user has this access, you can create a custom resource, for example, WebTerminal, and create a role WebTerminal which provides the access to this resource, and then set up this role to the web application and assign this role only to those users you want to have access to WebTerminal.

Please, feel free to ask if you need more details about setting up roles and resources.

Thanks for the good question!

And a question to community: I think creating WebTerminal role/resource programmatically during the WebTerminal installation makes sense, does it? As well as adding some documentation on setup/use/roles/etc.

Nikita Savchenko · Dec 10, 2016 go to post

Stefan,

I think it makes sense to edit this article a bit and replace all "dying" system methods with their working analogues, e.g. .$toJSON() -> .%ToJSON(), etc. Do we ready to do so in terms of transition back to no system methods?

This article has a good search engine ranking and I usually use information from here.

Thanks!

Nikita Savchenko · Nov 7, 2016 go to post

Thanks to Bernd, I finally found that this is not a Caché sessions unexpected behavior, the reason is in the browser's basic authentication cache.

To clear the browser's cache, here is one "dirty" solution for this: stackoverflow (and probably the only solution). The JavaScript function sends the wrong login/password authentication request to the server, and it results as 401 Unauthorized error. And this forces browser to clear its cache.

Nikita Savchenko · Nov 7, 2016 go to post

I did log in exactly as I described:

When I come to the /playground/index page at first, Caché meets me with an authentication window, asking to enter my username and a password.

The output of the log is the next:

You're logged in as _SYSTEM
SessionId = xH9mWezT2o, NewSession = 1

Thank you!

Nikita Savchenko · Nov 7, 2016 go to post

Hello Bernd,

Yes, I did:

ClassMethod Logout() As %Status
{
write "Bye, " _ $Username _ "!"
do %session.%SaveData()
set %session.EndSession = 1
do %session.Logout(1)
quit $$$OK
}

 

I tried %SaveData as documentation says as well, and this doesn't result as expected neither.

Nikita Savchenko · Nov 2, 2016 go to post

Wow, thanks, I thought that this routine will just be executed before the terminal session start and then the normal terminal session will happen.

Do you know is it possible to achieve this behavior? Thank you!

Nikita Savchenko · Jun 30, 2016 go to post

Thanks, Fabio!

Good to know about SPOOL device. Thank you! However, this method touches ^SPOOL global. Being set before the call, ^SPOOL global changes it's value, which is not secure for my needs.

P.S. Recently, Stuart gave me a complete answer. Thanks for your help!

Nikita Savchenko · Jun 30, 2016 go to post

Thank you,  Stuart!

I think this is a complete solution. I wrapped it to a class method and it works just as expected:

ClassMethod VarList() As %String
{
    IF $DATA(%) ; This places the existence of variable % into $TEST.
    NEW SET %=$SELECT($TEST:$LISTBUILD("%"),1:"")
    SET:$DATA(%0) %=%_$LISTBUILD("%0")
    NEW %0 SET %0="%0"
    FOR {
        SET %0=$ORDER(@%0)
        QUIT:%0=""
        SET %=%_$LISTBUILD(%0)
    }
    QUIT %
}

Results with

USER > write
%=11
a=<OBJECT REFERENCE>[1@DevProject.Robot]
i=5
USER > zw ##class(DevProject.Robot).VarList()
$lb("%","a","i")
USER > kill %
USER > zw ##class(DevProject.Robot).VarList()
$lb("a","i")
Nikita Savchenko · Jun 15, 2016 go to post

Nice! Will wonder if you have some sort of more-liner, which also saves and restores objects (orefs). But I believe it is not too useful.

Nikita Savchenko · Jun 14, 2016 go to post

OK :) But how to use this variable name generator? :)

 set myNewVarName = ..GetNewVarName() // oops, no
 set @..GetNewVarName() = "" // now it's OK, but I need to call this function once more
 set @..GetNewVarName() = $ORDER(@@..GetNewVarName()) // oops, another variable names

Things get complicated here. I think we can stop on the solutions Michael and Timothy proposed.

Thanks!

Nikita Savchenko · Jun 14, 2016 go to post

Thank you, Timothy! This is good, but what if user specify ^||VariableNameList process-private variable before calling the method? The value will be lost:

USER>set ^||VariableNameList = "TempVal" zwrite ##class(Test.Class).GetVariablesByTimothy()
$lb("Einstein","Mozart")
USER>write ^||VariableNameList
...TempVal is now gone!...

Maybe there is no absolutely save method to get the variables list without touching/rewriting other variables or globals. I hope it is enough just to name temporary variables as no one suggests to name.

Nikita Savchenko · Jun 14, 2016 go to post

This is awesome. It works! Wondering how many things COS has.

But it also outputs variable x. I wrapped your code into the method, and that's what I got:

ClassMethod GetVariables() As %List
{
  set UndefinedSpecialList = $LB()
  set UndefinedSpecialInt = 0
  set Undefined = ""
  set Undefined = $ORDER(@Undefined)
  while (Undefined '= "") {
    quit:($ORDER(@Undefined) = "UndefinedSpecialList")
    set $LIST(UndefinedSpecialList, $INCREMENT(UndefinedSpecialInt)) = Undefined
    set Undefined = $ORDER(@Undefined)
  }
  return UndefinedSpecialList
}

It works awesome until user specify variables named UndefinedSpecialList, UndefinedSpecialInt and Undefined. I suppose user won't ever name variables this way, of course, but the method which will consider this is the winner.

Nikita Savchenko · Jun 14, 2016 go to post

Thank you Fabio! The problem with all of the methods in %ZEN.Auxiliary.jsonProvider is that it writes JSON string directly to the current device, not allowing to get the string itself. But nevertheless I did it by rewriting this method and creating a custom method, which returns a string instead of printing JSON to the device.

Nikita Savchenko · Jun 7, 2016 go to post

Thanks, Dmitry, this is very close to what can be true. But to use this I need to interrupt WaitMsg method and check if the global exists each... Second? The one idea I come up with is to set timeout for that: set st = $system.Event.WaitMsg(, 1) if $LG(st,1)=0 {/*check if the process is gone and wait again*/} else {/*handle message*/} but is this the best way to do it? Are there any sort of registering trigger or system "interrupt" for this?

Nikita Savchenko · Jun 6, 2016 go to post

Thanks.

Apparently I reached another question now. How to terminate child process if the parent process is gone? WaitMsg method in $system.Event class does not return when the parent process terminates.

Nikita Savchenko · Jun 6, 2016 go to post
Jobbed process runs independently of parent process.
Killing a  parent does not kill its children.

Oops. Thank you, it does not kill children process really.

In my application two processes already use interprocess communication. Child process rely on $ZPARENT variable. Is it possible to change it? Or the only way is to use custom variable to hold the parent's process ID?

Nikita Savchenko · Jun 3, 2016 go to post

Thanks for the tips! My use case is only to get the string, it doesn't matter printable or not.

I also tested that this works with nested lists, so everything is OK now:

USER>set l = $lb(1,2,$lb(3,4,5),6)
 
USER>w $LISTGET($LISTGET(l, 3), 2)
4
USER>w $LISTGET($LISTGET(l, 3), 1)
3
USER>w $LISTGET($LISTGET(l, 3), 3)
5
USER>w $LISTGET(l, 4)
Nikita Savchenko · Jun 3, 2016 go to post

Thanks John, the above terminal transcript is not correct showing what I wanted to show. I've fixed this.

Why can't you understand the purpose of this?

I can only transfer strings by some channel (f.e. interprocess communication). Now I need to send the array of strings (list, object, etc) from sender to receiver. To send this array, I must convert it to the string and then, after receiving, convert it back to array to read it's elements. How to do this in Caché 2013.*? The strings in array can contain any characters, so it is not possible to specify any delimiter character.

Finally I got why the question is pretty confusing for you. Just imagine, I didn't know that $lb() returns the simple string. I didn't know that I can write this string to file, read it from file, and then apply $LISTGET function to it. I didn't know $LISTGET function takes string type. I thought that $LIST is something more than just a string.

So the solution:

set string = $LB(1,2,3,",",5)
/* send this string anywhere */
set element = $LISTGET(string, 4) // = ","