Use docker ARGs for conditionals on build

Problem

Run some script on docker build but only on production and not on development.

Solution

Using ARG in Dockerfile and docker-compose.yml. The "main" is an example for production and "main_dev" for development.

Dockerfile:

FROM python:3

ARG DEV
RUN if [ ! "$DEV" = "yes" ]; then echo "DEV is NOT set"; else echo "DEV is set"; fi

CMD [ "echo", "OK" ]

docker-compose.yml:

version: "3.8"
services:
  main:
    build:
      context: .
  main_dev:
    build:
      context: .
      args:
         DEV: "yes"

Now lets run the examples:

docker-compose build --no-cache main results in

Building main
Step 1/4 : FROM python:3
 ---> 1f88553e8143
Step 2/4 : ARG DEV
 ---> Running in a369f15bdbba
Removing intermediate container a369f15bdbba
 ---> d33834a028c4
Step 3/4 : RUN if [ ! "$DEV" = "yes" ]; then echo "DEV is NOT set"; else echo "DEV is set"; fi
 ---> Running in c47c9d3ae979
DEV is NOT set
Removing intermediate container c47c9d3ae979
 ---> d93839918c95
Step 4/4 : CMD [ "echo", "OK" ]
 ---> Running in 1280176dc942
Removing intermediate container 1280176dc942
 ---> ac023ae1b6fa

Successfully built ac023ae1b6fa
Successfully tagged dockerarg_main:latest

and

docker-compose build --no-cache main_dev results in

Building main_dev
Step 1/4 : FROM python:3
 ---> 1f88553e8143
Step 2/4 : ARG DEV
 ---> Running in a9bbe1cbf494
Removing intermediate container a9bbe1cbf494
 ---> f28af462f46a
Step 3/4 : RUN if [ ! "$DEV" = "yes" ]; then echo "DEV is NOT set"; else echo "DEV is set"; fi
 ---> Running in 908227c7de9c
DEV is set
Removing intermediate container 908227c7de9c
 ---> 4f4caad6140a
Step 4/4 : CMD [ "echo", "OK" ]
 ---> Running in 0f83a6b15abf
Removing intermediate container 0f83a6b15abf
 ---> 19bf8758a2ae

Successfully built 19bf8758a2ae
Successfully tagged dockerarg_main_dev:latest

This seems to work as seen with DEV is NOT set vs DEV is set.

Count Rows on an old rowing machine

Idea

At the end of 2019 a mechanical rowing machine came into my possession. This machine (a Hanseatic Rowing Machine) has no electronics at all but I want to measure the rows I am doing.

Recording

So I bought an I2C based Accelerometer: MMA7455 and soldered a raspberry-pi zero shield for it:

/images/rowing_sensor.png

The recording is done with a Python script that is started on boot via systemd.

This code shows howto read the current value from the sensor:

import smbus

bus = smbus.SMBus(1)
# MMA7455L address is 0x1D
bus.write_byte_data(0x1D, 0x16, 0x01)

# read values
data = bus.read_i2c_block_data(0x1D, 0x00, 6)

# Convert the data to 10-bits
xAcc = (data[1] & 0x03) * 256 + data [0]
if xAcc > 511 :
    xAcc -= 1024
yAcc = (data[3] & 0x03) * 256 + data [2]
if yAcc > 511 :
    yAcc -= 1024
zAcc = (data[5] & 0x03) * 256 + data [4]
if zAcc > 511 :
    zAcc -= 1024

print(f"Acceleration {xAcc:5d} {yAcc:5d} {zAcc:5d}")

The full record script used on the raspberry pi is additionally logging to a csv file: https://github.com/mfa/rowing-count/blob/master/record.py.

Evaluation

The latest version of the evaluation as a jupyter notebook: https://github.com/mfa/rowing-count/blob/master/experiments.ipynb

First, we need to find the best curve for the problem. Here we see 1 row, 2 rows and 5 rows:

/images/rowing_all_axes.png

And only the 5 rows zoomed in:

/images/rowing_all_axes_5.png

Only in the x axis curve the rows are clearly distinguishable.

So the isolated x axis looks like this

/images/rowing_5x.png

Because it is easier to detect peaks on top we negate the curve:

/images/rowing_5x_neg.png

And now smoothen the curve using a savgol filter:

/images/rowing_5x_neg_savgol.png

We got the parameters for the savgol by trial and error.

The next step is the peak finding. Scipy has a find_peaks method that works after some tweaking quite good:

/images/rowing_5.png

The (orange) line below the found peak is the prominence. This "height" helps filtering too small peaks. The complete filtering methods looks like this

def get_peaks(x):
    _ = np.negative(x)
    _ = scipy.signal.savgol_filter(_, 51, 3)
    peaks, properties = scipy.signal.find_peaks(_, prominence=5, width=40)
    return sum(map(lambda i: i>12, properties["prominences"]))

The sum in the last line filters all peaks with a prominence higher than 12 and only sums them.

Another example with 100 rows:

/images/rowing_100.png

The full code: https://github.com/mfa/rowing-count/