bash to resolve dependencies

#!/bin/bash

declare -A USE_MAP
declare -a ENABLES

# export_name dependency dependecy dependency...
USE_MAP['HBASE']='USE_HBASE HCATALOG HUE'
USE_MAP['PIG']='USE_PIG'
USE_MAP['HUE']='USE_HUE'
USE_MAP['HCATALOG']='USE_HCATALOG'
USE_MAP['PRESTO']='USE_PRESTO HCATALOG'
ENABLES=()

function use() {
  local dep=(${USE_MAP["$1"]})
  ENABLES+=(${dep[0]})
  dep=("${dep[@]:1}")
  if [ ! -z "$dep" ]; then
    for item in ${dep[@]}; do
      use $item
    done
  fi
}

function export_enabled() {
  sorted_use=($(echo "${ENABLES[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))
  for item in ${sorted_use[@]}; do
    echo "export $item";
  done
}

use HBASE
export_enabled

------

$ bash resolve.sh
export USE_HBASE
export USE_HCATALOG
export USE_HUE

Ambari – user can log in but has no privileges

We ran into some interesting issue – user has all the privileges, was in correct ldap group, can log in to the ambari, but privileges weren’t effective.

I did multiple discoveries – checked log ambari-server.log and found out following

com.google.common.cache.CacheLoader$InvalidCacheLoadException: CacheLoader returned null for key myuser.

So, tried to restart ambari-server, didn’t work. Did further investigation, using ambari API

$ curl -XGET -H"X-Requested-By: ambari" -u admin:... https://ambari.mycluster.com/api/v1/users/myuser
{
  "href" : "http://ambari.mycluster.com/api/v1/users/myuser",
  "Users" : {
    "active" : true,
    "admin" : false,
    "groups" : [
      "ldap-users",
      "cluster-admins"
    ],
    "ldap_user" : false,
    "user_name" : "MYUSER",
    "user_type" : "PAM"
  },

so, this worked. Notice uppercase in user_name. So I tried to fetch user info with privileges

$ curl -XGET -H"X-Requested-By: ambari" -u admin:... https://ambari.mycluster.com/api/v1/users/myuser?fields=privileges/*
{
  "href" : "http://ambari.mycluster.com/api/v1/users/myuser?fields=privileges/*",
  "Users" : {
    "user_name" : "MYUSER"
  },
  "privileges" : [
    {
      "href" : "http://ambari.mycluster.com/api/v1/users/myuser/privileges/153",
      "PrivilegeInfo" : {
        "instance_name" : "INSTANCE",
        "permission_label" : "View User",
        "permission_name" : "VIEW.USER",
        "principal_name" : "cluster-admins",
        "principal_type" : "GROUP",
        "privilege_id" : 153,
        "type" : "VIEW",
        "user_name" : "MYUSER",
        "version" : "2.4.3.0",
        "view_name" : "ADMIN_VIEW"
      }
    },

Nice. Works too. So another check – load only certain privileges – 153.

$ curl -XGET -H"X-Requested-By: ambari" -u admin:... https://ambari.mycluster.com/api/v1/users/myuser/privileges/153
{
  "status": 500,
  "message": "Server Error"
}

and even upcase

$ curl -XGET -H"X-Requested-By: ambari" -u admin:... https://ambari.mycluster.com/api/v1/users/MYUSER/privileges/153
{
  "status": 500,
  "message": "Server Error"
}

Aha! Looks like there’s some inconsitency in the postgres database. So, check it!

ambari=> select * from users where user_name='myuser';
 user_id | principal_id | ldap_user | user_name | create_time | user_password | active | active_widget_layouts | user_type
---------+--------------+-----------+-----------+-------------+---------------+--------+-----------------------+-----------
(0 rows)

ambari=> select * from users where user_name='MYUSER';
 user_id | principal_id | ldap_user | user_name |      create_time       | user_password | active | active_widget_layouts | user_type
---------+--------------+-----------+-----------+------------------------+---------------+--------+-----------------------+-----------
   16005 |        20005 |         0 | MYUSER  | 2019-11-22 04:37:44.18 |               |      1 | [{"id":"29405"}]      | PAM
(1 row)

Gotcha. There’s no such user named myuser in the database – and pg is case sensitive. So, rename the user

ambari=> update users set user_name='myuser' where user_id=16005;
UPDATE 1
ambari=> \q

and restart ambari-server. Now we can try again the api call

$ curl -XGET -H"X-Requested-By: ambari" -u admin:... https://ambari.mycluster.com/api/v1/users/myuser/privileges/153
{
  "href" : "http://ambari.mycluster.com/api/v1/users/myuser/privileges/153",
  "PrivilegeInfo" : {
    "instance_name" : "INSTANCE",
    "permission_label" : "View User",
    "permission_name" : "VIEW.USER",
    "principal_name" : "cluster-admins",
    "principal_type" : "GROUP",
    "privilege_id" : 153,
    "type" : "VIEW",
    "user_name" : "myuser",
    "version" : "2.4.3.0",
    "view_name" : "ADMIN_VIEW"
  }
}

Voila!

Foxtrot: Programy pro ČOV

Program pro řídící jednotku Foxtrot a čistírnu odpadních vod Aquatec AT-6. Nabízí 10 programů, dle zatížení domácnosti. Dá se později napojit na vodoměr (počitadlo) a podle toho měnit program.

TYPE
  T_COVProgram :STRUCT
    runs: ARRAY[0..23] OF usint;
  END_STRUCT
END_TYPE
VAR_GLOBAL
  COV_Programs: ARRAY[1..10] OF T_COVProgram := [
  // cas - hodina rozdelena na 12 useku po 5min.
  // hodnoty: 1 -> 1 min bezi, 5-1=4 stoji
//            0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
// 1 - 5.6h celkem, 4 min stoji, 1 min bezi
    (runs := [5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]),
// 2 - 8.8h celkem, 4 min stoji, 1 min bezi
    (runs := [5, 5, 5, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 5]),
// 3 - 10.4h celkem, 4 min stoji, 1 min bezi
    (runs := [5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 1, 1]),
// 4 - 12h celkem, 4 min stoji, 1 min bezi
    (runs := [5, 5, 5, 5, 5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 5, 1, 1, 1, 1, 1, 5, 5]),
// 5 - 15h celkem, 3 min stoji, 2 min bezi
    (runs := [5, 5, 5, 5, 5, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 5, 2, 2, 2, 2, 2, 5, 5]),
// 6 - 18h celkem, 2 min stoji, 3 min bezi
    (runs := [5, 5, 5, 5, 5, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 5, 3, 3, 3, 3, 3, 5, 5]),
// 7 - 20.4h celkem, 2 min stoji, 3 min bezi
    (runs := [5, 5, 5, 5, 5, 5, 3, 3, 3, 5, 5, 5, 3, 3, 3, 5, 5, 5, 3, 3, 3, 5, 5, 5]),
// 8 - 21.6h celkem, 2 min stoji, 3 min bezi
    (runs := [5, 5, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5]),
// 9 - 22.8h celkem, 2 min stoji, 3 min bezi
    (runs := [5, 5, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5, 5, 3, 5, 5, 5, 5]),
// 10 - 24h celkem, 24h bezi
    (runs := [5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5])
   ];
END_VAR
VAR_GLOBAL RETAIN
  COV_PROGRAM : usint := 0;  
END_VAR

FUNCTION_BLOCK fbCistickaProgram
  VAR_INPUT
    prog_num: usint;
  END_VAR
  VAR_OUTPUT
    output: BOOL;
  END_VAR
  VAR_IN_OUT
  END_VAR
  VAR
    ton_stop: ton;
    ton_run: ton;
    light: fb_iLight;
    curr_time: time;
    hour: usint;
    covprg : T_COVProgram;
    mins : usint;
  END_VAR

curr_time := GetTime();
hour := hour_of_time(in := curr_time);
covprg := COV_Programs[prog_num];
mins := covprg.runs[hour];

ton_stop( IN := not ton_run.q, pt := encodeTime(M := 5-mins));
ton_run( IN := ton_stop.q, pt := encodeTime(M:= mins));

light( lightOn := ton_stop.q, lightOff := ton_run.q, name:= 'Cisticka', out => output );
END_FUNCTION_BLOCK
PROGRAM prgJson
  VAR
    cisticka : fbCistickaProgram;
  END_VAR

if COV_PROGRAM = 0 then
  COV_PROGRAM := 1;
end_if;

cisticka( prog_num := COV_PROGRAM, output => RO04_COV );
END_PROGRAM

Jsou tam ještě nějaké záseky, např. že pokud je cyklus plný (5 minut) a následuje ten samý (bez pauzy), bylo by fajn vynechat úplně vypínání výstupu, popř. zkrátit cyklus o párset ms. Dále pak přidat reset v případě změny programu atd. Každopádně to ale zatím funguje k mé spokojenosti.

R – Četnost podruhé

Mějme následující data:

> head(dotaznik.csv[,c(2,3)])
  type gender
1    H      F
2    H      M
3    A      F
4    A      M
5    A      F
6    H      M

A potřebujeme zjistit absolutní a relativní četnost výskytu žen a mužů, rozdělenou ještě podle hodnoty v type .

> tbl <- table(dotaznik.csv$gender, dotaznik.csv$type)
> tbl
   
     A  H
  M 18 16
  Ž 17 19

Opět převedeme na data frame

> tbl.frame <- as.data.frame(tbl)
> tbl.frame
  Var1 Var2 Freq
1    M    A   18
2    Ž    A   17
3    M    H   16
4    Ž    H   19

A máme absolutní četnost. Teď vypočítáme relativní četnost.

> library(plyr) 
> ddply(tbl.frame, .(Var2), transform, prop=Freq/sum(Freq))
  Var1 Var2 Freq      prop
1    M    A   18 0.5142857
2    Ž    A   17 0.4857143
3    M    H   16 0.4571429
4    Ž    H   19 0.5428571

Což znamená: pro každý subset z framu tbl.frame , rozdělený podle proměnné Var2 , proveď transform , a vypočti relativní četnost podle frekvence (abs. četnosti) a součtu hodnot frekvence.

R – četnost a graf

Dostal jsem pověření z nejvyšších míst vyrobit statistické zhodnocení dotazníků. A protože bytostně nemám rád Excel, našel jsem R a zkouším.

Udělal jsem data do CSV souboru a nahrál do R (RStudio)

> head(dotaznik.csv)
  num type gender age height weight           edu
1   1    H      M  60    182    100 Vysokoškolské
2   2    H      M  49    188    102      Vyučen/a
3   3    H      M  61    176     75      Vyučen/a
4   4    H      M  56    180    110       Střední
5   5    H      M  47    180     95 Vysokoškolské
6   6    H      M  48    178     95       Střední

Zkusíme vyrobit četnost vzdělání (edu) a potom ji ještě rozdělit podle pohlaví (gender).

> edu <- table(dotaznik.csv$edu)
> edu

      Střední Vysokoškolské Vyšší odborné      Vyučen/a 
           30             7             2            25 
     Základní 
            6

Máme tabulku, ale potřebujeme z ní frame

> fedu <- as.data.frame(edu)
> fedu
           Var1 Freq
1       Střední   30
2 Vysokoškolské    7
3 Vyšší odborné    2
4      Vyučen/a   25
5      Základní    6

Přidáme relativní četnost (mean)

> fedu$mean <- fedu$Freq / sum(fedu$Freq)
> fedu
           Var1 Freq       mean
1       Střední   30 0.42857143
2 Vysokoškolské    7 0.10000000
3 Vyšší odborné    2 0.02857143
4      Vyučen/a   25 0.35714286
5      Základní    6 0.08571429

A teď na graf. Používáme ggplot2.

ggplot(fedu, aes(x=Var1, y=Freq)) + geom_bar(stat='identity')

Rplot1

Fajn. Ale chceme graf otočit

ggplot(fedu, aes(x=Var1, y=Freq)) + geom_bar(stat='identity') + coord_flip()

Rplot2

Teď ještě smazat popisek osy x a přepsat osu y.

ggplot(fedu, aes(x=Var1, y=Freq)) + geom_bar(stat='identity') +
coord_flip() +
xlab('Vzdělání') + 
theme(axis.title.x = element_blank()) + 
ggtitle('Dosažené vzdělání')

Rplot3

A finálně přidáme hodnoty frekvence do jednotlivých sloupců.

ggplot(fedu, aes(x=Var1, y=Freq)) + 
geom_bar(stat='identity', position=position_dodge()) + 
geom_text(aes(label=Freq), hjust=1.3, color="white",position = position_dodge(0.9), size=3.5) + 
coord_flip() + 
xlab('Vzdělání') + 
theme(axis.title.x = element_blank()) + 
ggtitle('Dosažené vzdělání')

Rplot4

A teď trochu komplexněji. Ještě to rozdělíme na muže a ženy..

> edu <- table(dotaznik.csv$gender, dotaznik.csv$edu)
> edu
   
    Střední Vysokoškolské Vyšší odborné Vyučen/a Základní
  M      10             4             2       17        1
  Ž      20             3             0        8        5

> fedu <- as.data.frame(edu)
> fedu
   Var1          Var2 Freq
1     M       Střední   10
2     Ž       Střední   20
3     M Vysokoškolské    4
4     Ž Vysokoškolské    3
5     M Vyšší odborné    2
6     Ž Vyšší odborné    0
7     M      Vyučen/a   17
8     Ž      Vyučen/a    8
9     M      Základní    1
10    Ž      Základní    5

a přidáme graf

> ggplot(fedu, aes(x=Var2, y=Freq, fill=Var1)) + 
geom_bar(stat='identity', position=position_dodge()) + 
geom_text(aes(label=Freq), hjust=1.6, color="white",position = position_dodge(0.9), size=3.5) + 
coord_flip() + 
xlab('Vzdělání') + 
theme(axis.title.x = element_blank()) + 
ggtitle('Dosažené vzdělání') + 
labs(fill = 'Pohlaví')

Rplot6

A co když budeme chtít jeden bar, ale rozdělený podle hodnot?

# seridime podle Var2 a Freq
> library(plyr)
> fedu_s <- arrange(fedu, Var2, Freq)
> fedu_s
   Var1          Var2 Freq
1     M       Střední   10
2     Ž       Střední   20
3     Ž Vysokoškolské    3
4     M Vysokoškolské    4
5     Ž Vyšší odborné    0
6     M Vyšší odborné    2
7     Ž      Vyučen/a    8
8     M      Vyučen/a   17
9     M      Základní    1
10    Ž      Základní    5

# a pridame souhrnny soucet, rozdeleny s kazdym jinym Var2
> fedu_s_sum <- ddply(fedu_s, 'Var2', transform, label_ypos=cumsum(Freq))
> fedu_s_sum
   Var1          Var2 Freq label_ypos
1     M       Střední   10         10
2     Ž       Střední   20         30
3     Ž Vysokoškolské    3          3
4     M Vysokoškolské    4          7
5     Ž Vyšší odborné    0          0
6     M Vyšší odborné    2          2
7     Ž      Vyučen/a    8          8
8     M      Vyučen/a   17         25
9     M      Základní    1          1
10    Ž      Základní    5          6

A graf

ggplot(fedu_s_sum, aes(x=Var2, y=Freq, fill=Var1)) + 
geom_bar(stat='identity') + 
geom_text(aes(label=Freq, y=label_ypos), hjust=1.6, color="white", size=3.5) + 
coord_flip() + 
xlab('Vzdělání') + 
theme(axis.title.x = element_blank()) + 
ggtitle('Dosažené vzdělání') + 
labs(fill = 'Pohlaví')

Rplot7

 

STM32F4Discovery + eLua + OSX

I fixed few bugs when compiling elua from git on osx – clone my branch clone official repo here.

$ ./build_elua.lua board=stm32f4discovery

create .bin file

$ arm-none-eabi-objcopy -O binary elua_lua_stm32f4discovery.elf elua_lua_stm32f4discovery.bin

create openocd config file for stm32f4 board:

$ cat ~/stm32f4discovery.cfg
# stm32f4discover board
source [find interface/stlink-v2-1.cfg]
transport select hla_swd
source [find target/stm32f4x.cfg]
reset_config srst_only

and upload to the board:

$ openocd -f ~/stm32f4discovery.cfg \
   -c "init" \   
   -c "reset halt" \
   -c "sleep 100" \
   -c "wait_halt 2" 
   -c "echo \"--- Writing elua_lua_stm32f4discovery.bin\"" \
   -c "flash write_image erase elua_lua_stm32f4discovery.bin 0x08000000" \
   -c "sleep 100" \
   -c "echo \"--- Verifying\"" \
   -c "verify_image elua_lua_stm32f4discovery.bin 0x08000000" \
   -c "sleep 100" \
   -c "echo \"--- Done\"" \
   -c "resume" \
   -c "shutdown"

Open On-Chip Debugger 0.9.0 (2015-11-16-01:48)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
adapter speed: 2000 kHz
adapter_nsrst_delay: 100
none separate
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : Unable to match requested speed 2000 kHz, using 1800 kHz
Info : clock speed 1800 kHz
Info : STLINK v2 JTAG v25 API v2 SWIM v14 VID 0x0483 PID 0x374B
Info : using stlink api v2
Info : Target voltage: 2.884520
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080063f0 msp: 0x20000c78
--- Writing elua_lua_stm32f4discovery.bin
auto erase enabled
Info : device id = 0x10076413
Info : flash size = 1024kbytes
wrote 262144 bytes from file elua_lua_stm32f4discovery.bin in 7.783807s (32.889 KiB/s)
--- Verifying
target state: halted
target halted due to breakpoint, current mode: Thread
xPSR: 0x61000000 pc: 0x2000002e msp: 0x20000c78
verified 220776 bytes in 2.229936s (96.685 KiB/s)
--- Done
shutdown command invoked

Now you can connect to the board using /dev/tty.usbmodem621, 115200/8/n/1. You should get this prompt:

eLua#
eLua# help
Shell commands:
  help   - shell help
  lua    - start a Lua session
  ls     - lists files and directories
  dir    - lists files and directories
  cat    - list the contents of a file
  type   - list the contents of a file
  recv   - receive files via XMODEM
  cp     - copy files
  mv     - move/rename files
  rm     - remove files
  ver    - show version information
  mkdir  - create directories
  exit   - exit the shell
For more information use 'help <command>'.
eLua# lua
Press CTRL+Z to exit Lua
Lua 5.1.4  Copyright (C) 1994-2011 Lua.org, PUC-Rio
> print(pd.board() .. "/" .. pd.platform() .. "/" .. pd.cpu())
STM32F4DISCOVERY/STM32F4/STM32F407VG

enjoy :)

nodemcu – simple webserver

I’ve modified and updated this source:

local SSID = "my_wifi_ssid"
local SSID_PASSWORD = "my_wifi_password"

local function http_header(conn)
  conn:send('HTTP/1.1 200 OK\n\n')
  conn:send('<!DOCTYPE HTML>\n')
  conn:send('<html>\n')
  conn:send('<head><meta  content="text/html; charset=utf-8">\n')
  conn:send('<title>ESP8266 znouza test</title></head>\n')
end

local function connect (conn, data)
   local query_data

   conn:on ("receive",
      function (cn, req_data)
         query_data = get_http_req (req_data)
         print (query_data["METHOD"] .. " " .. " " .. query_data["User-Agent"])
         http_header(cn)
         cn:send ("<body>")
         cn:send ("<h1>Hello World from ESP8266 and NodeMCU!!</h1>")
         cn:send ("</body></html>")
         -- Close the connection for the request
         cn:close ( )
      end)
end

function wait_for_wifi_conn ( )
   tmr.alarm (1, 1000, 1, function ( )
      if wifi.sta.getip ( ) == nil then
         print ("Waiting for Wifi connection")
      else
         tmr.stop (1)
         print ("ESP8266 mode is: " .. wifi.getmode ( ))
         print ("The module MAC address is: " .. wifi.sta.getmac ( ))
         print ("Config done, IP is " .. wifi.sta.getip ( ))
      end
   end)
end

-- Build and return a table of the http request data
function get_http_req (instr)
   local t = {}
   local first = nil
   local key, v, strt_ndx, end_ndx

   for str in string.gmatch (instr, "([^\n]+)") do
      -- First line in the method and path
      if (first == nil) then
         first = 1
         strt_ndx, end_ndx = string.find (str, "([^ ]+)")
         v = trim (string.sub (str, end_ndx + 2))
         key = trim (string.sub (str, strt_ndx, end_ndx))
         t["METHOD"] = key
         t["REQUEST"] = v
      else -- Process and remaining ":" fields
         strt_ndx, end_ndx = string.find (str, "([^:]+)")
         if (end_ndx ~= nil) then
            v = trim (string.sub (str, end_ndx + 2))
            key = trim (string.sub (str, strt_ndx, end_ndx))
            t[key] = v
         end
      end
   end

   return t
end

-- String trim left and right
function trim (s)
  return (s:gsub ("^%s*(.-)%s*$", "%1"))
end

-- Configure the ESP as a station (client)
wifi.setmode (wifi.STATION)
wifi.sta.config (SSID, SSID_PASSWORD,1)

-- Hang out until we get a wifi connection before the httpd server is started.
wait_for_wifi_conn ( )

-- Create the httpd server
svr = net.createServer (net.TCP, 30)

-- Server listening on port 80, call connect function if a request is received
svr:listen (80, connect)

nodemcu + el capitan

To flash

git clone https://github.com/themadinventor/esptool.git 
cd esptool
sudo python ./setup.py install
  • upgrade firmware
$ python ./esptool.py --port=/dev/cu.wchusbserial620  write_flash  -fm=dio -fs=32m 0x00000 /tmp/nodemcu_integer_0.9.6-dev_20150704.bin

Connecting...
Erasing flash...
Took 3.92s to erase flash block
Wrote 450560 bytes at 0x00000000 in 54.1 seconds (66.6 kbit/s)...

Leaving...