Subscribe Bookmark



Mar 21, 2013

Can JSL talk to Linux?

A year ago a 4K TV came home, and finally, last week, a video card that can drive it at that resolution. The card has a massive heat sink, but no fan. No fan means not too much heat (I hope), which means less power required from the ancient 250W power supply. The computer runs Linux, and after poking around on the internet, I found these commands to check the temperature sensors inside the case. The green Netflix is the Linux computer's name.

nvidia-settings and sensors commands to show temperaturenvidia-settings and sensors commands to show temperature

The two commands show the Nvidia (graphics card) temperature at 53C and the CPU temperature(s) between 46C and 53C. The sensors command also seems to suggest 80C is the beginning of the start-to-be-worried neighborhood. Currently the video card is idling while this post is written, and the temperature is pretty low. It works a lot harder to display full screen 4K video, and the temperature will rise. How much, and how fast?

To find out, JMP will run the Linux commands in a loop, recording the results and making a graph that updates as the data is collected. The magic is in RunProgram (a JSL function) and plink (an external program, part of the putty package, that runs on windows.) Before this will work, there are a few things to do.

First, the Linux host computer needs the SSH server running and needs to be secured appropriately. I used sites like this and this this to puzzle it out.

Next you'll need plink installed on your windows computer. I picked the msi installer here and then read this to figure out how plink works.

Finally, configure puttygen/putty/plink to use the ssh connection with the keys (same this from above) so you don't put your password in plain sight.

Now run a really simple test to see if everything is in place. Plink is the program your windows computer is going to run (part of the putty install). Netflix is also the name of my Linux computer's configuration in the putty program. Sensors is the name of the program the Linux computer will run. And text tells RunProgram to just return all of the text output from sensors after sensors is finished.

Output of sensors command showing B0 for degree symbolOutput of sensors command showing B0 for degree symbol

The high-lighted B0 should be a degree symbol. RunProgram assumes the output of a executable is 7-bit ASCII, not Unicode. To fix this, use the blob option and convert the blob to text. This example will run two commands. The JSL string holding the commands uses the \[ and ]\ characters inside the quotation marks to make it easier to use special characters later, if they turn out to be needed. The semicolon separates the two Linux commands. You can see the yellow high lighted values of interest, followed by degree symbols (not B0).

RunProgram with the blob option returns binary data which might convert to UnicodeRunProgram with the blob option returns binary data which might convert to Unicode

Creating the connection to the Linux computer over and over is OK, but not ideal. The overhead can be avoided by creating it once. I'm going to do this by writing a loop for the Linux computer to run, with a short delay. The ReadFunction will still get a blob, but it will become an actual JSL function that gets chunks of text back from the Linux computer. The chunks get joined together, parsed for values of interest, and the values added to a data table.

// open a data table to record results
dt = New Table( "temperature",
    New Column( "time", Format( "hr:m:s", 13, 0 ) ),
    New Column( "gpu" ),
    New Column( "PhysicalID0" ),
    New Column( "Core0" ),
    New Column( "Core1" ),
    New Column( "Core2" ),
    New Column( "Core3" )

start = Tick Seconds(); // capture initial time to subtract out later
buffer = Char To Blob( "" ); // buffer several reads together; init to empty

rp = RunProgram( // launch the long-running external program; note the shell command "while [ 1 ]" and the "sleep 2"
    executable( "plink" ),
    options( {"netflix", "\[while [ 1 ]; do nvidia-settings -c :0 -q gpucoretemp; sensors; sleep 2; done]\"} ), // inxi -Fxz
        Function( {this}, {},
            While( this << canread,
                buffer = buffer || (this << read( "blob" ));
                Wait( 0.1 ); // try to grab up all of the output for each 2-second report
            If( Length( buffer ) > 0, // not always 
                text = Blob To Char( buffer ); // there is a remote possibility a unicode sequence gets split here
                buffer = Char To Blob( "" ); // reset empty
                dt << addrow( 1 ); // build the table
                dt:time = Tick Seconds() - start; // apply offset to get 0 time for start
                dt:gpu = Num( Regex( text, "\[Attribute 'GPUCoreTemp' \(Netflix.local:0.0\): (\d+)]\", "\1" ) );
                dt:PhysicalID0 = Num( Regex( text, "\[Physical id 0:  \+(\d+)]\", "\1" ) );
                dt:Core0 = Num( Regex( text, "\[Core 0:         \+(\d+)]\", "\1" ) );
                dt:Core1 = Num( Regex( text, "\[Core 1:         \+(\d+)]\", "\1" ) );
                dt:Core2 = Num( Regex( text, "\[Core 2:         \+(\d+)]\", "\1" ) );
                dt:Core3 = Num( Regex( text, "\[Core 3:         \+(\d+)]\", "\1" ) );
                //write("\!n", gpu," ",PhysicalID0," ",Core0," ",Core1," ",Core2," ",Core3 );

Wait( 10 ); // wait for a few rows before launching
dt << Graph Builder(
    Size( 679, 444 ),
    Show Control Panel( 0 ),
        X( :time ),
        Y( :gpu ),
        Y( :PhysicalID0, Position( 1 ) ),
        Y( :Core0, Position( 1 ) ),
        Y( :Core1, Position( 1 ) ),
        Y( :Core2, Position( 1 ) ),
        Y( :Core3, Position( 1 ) )
    Elements( Smoother( X, Y( 1 ), Y( 2 ), Y( 3 ), Y( 4 ), Y( 5 ), Y( 6 ), Legend( 4 ), Lambda( 4.32912374502826 ) ) ),
        Dispatch( {}, "graph title", TextEditBox, {Set Text( "Temperatures" )} ),
        Dispatch( {}, "X title", TextEditBox, {Set Text( "H:M:S" )} ),
        Dispatch( {}, "Y title", TextEditBox, {Set Text( "Centigrade" )} )
Wait( 10000 ); // wait 10,000 seconds, then...
rp = 0; // kill it. it is in a loop.

The 5 non-GPU temperatures track togetherThe 5 non-GPU temperatures track together

Two episodes of Firefly. The initial 15 minutes was scrolling through selections looking for something to watch. The final few minutes, after the video, was a hard CPU loop. I accidentally ended the program before 10,000 seconds.




Article Tags