Monday, January 29, 2018

Side project: getting eyes far away

Last year I became obsessed with FPV. I don't mean games - that's kinda for kids. I'm talking here about getting an object, a plane in this case, equipped with a camera and other equipment that can be controlled from a distance. I was trying to get into the multi-rotor business but that seems not to be my thing. Planes, on the other hand, are. So I tried my luck there...

My first time was... well... exciting wouldn't even begin to cover it! It was a mysterious experience! I was actually in a plane that flies around, hundreds of meters away from me, seeing the real life flashing before my eyes. It was simply a blast! But as soon as I flew the first time and saw the land that I already covered something woke up in me: the nagging question what is out there? I guess I am kind of an explorer down deep inside :) And so I begun to cover more and more ground. It soon was pretty clear for me that the equipment I had at the time with a 200mW VTX was not going to quite cut it. All that was made crystal clear for me once I lost the plane after just several hundred meters flight in the neighborhood. It sucked big time and about 600 PLN was forever lost. Sometimes when I drive around I hope to see this plane hanging out in some tree but that didn't happen.

Someone could say: this is way too expensive and too dangerous - but not me. I came back home, late at night that day, and immediately ordered a stronger VTX (used to ride a 200mW one, now I ordered a 600mW ImmersionRC that people said was good for several kilometers!). It took a while before I was able to get back up in the sky but it eventually happened. With the new VTX, a changed site and a new plane I was rocking the sky again!

Over the course of last summer I didn't loose a plane (well, technically I did but more on that later or). I was flying first 1.3km to a landmark I knew and I was passionate to get to. That didn't work because of the antennas that I had. That day I learned that power is not everything - you need to have something to convey it with. And in the case of video transmission (and also the control transmission from your radio to the plane) antennas are the holy grail! So I went ahead and bought 3 of the most recommended (and most sexy) ones - the Aomway clover leave antennas. Alongside the 600mW VTX I also bought a 14dBi directional patch antenna. Using this setup I was finally able to easily break the 2km range!

But my appetite (and the addiction) grew...

So I started experimenting. First about going further than 2km. It turned out that the 100mW OpenLRS system wasn't all that good at ground leve. - so I built a 2m mast that gave me more range. A few weeks later I tested it up to 4.8km and let me tell you the wiggles that I have had that day were nothing compared to the first time with a woman! I was so fucking stressed out I almost fainted!

All things considered it was a pretty good flight. I used a 3s 3000mAh Li-on pack at that flight and it almost lasted to get the plane home. Just a few tents of meters over a field and the plane was safe and sound in my hands. That meant I needed more power. Lot's of it.

In a situation like that power comes from a combination of input voltage, amps, ESC, motor and propeller. So I started looking for the best combination. Just out of share luck I have had a few od 2212 810kv motors around (from my failed attempt to build a 450-sized quadcopter). I thought, since it had the required max thrust (around 1kg) so why I don't I give it a shot. And so I did!

The first attempt was very promising. At 4S and a 10x4.5 prop it flew great and I was able to finally reach the 5km mark. I turned back to see how the battery will behave on the way but it was plenty left. That encouraged me to give it another shot the next day and... that is how I achieved my up-to-date record of 8.2km out and safely back!

Oh boy was I excited! All my efforts were finally paying off! My dream was to take the plane from the place where I was usually flying back to the place I live - which was about 6.5km away. With the record flight of 8.2km I thought it was a no brainer. Man, was I wrong! It's not just the tx/rx/antennas combination but first and foremost the lay of the land that allows (or not) to get to longer distances. Luckily the day before that I have put my phone number and since the plane automatically landed on a busy street and a good man picked it up and called me the plane was recovered - and my ambition was greatly humbled.

This year I'm trying my luck in the same direction that my record flight of 8.2km was. I am hoping to break through the 20km barrier with some new equipment that I have bought:

  • 500mW Wolfbox TX (with potential to be modded to 1W)
  • 1W VTx and a biquad 11dBi antenna that I tested up to 12km with crystal clear picture on the ground
  • an all new Mini Talon with 4S2P Li-on battery pack

The plan is as follows:

  • First do a maiden of the plane with the minimum electronics to get it to fly
  • Add a flight controller for stabilization and get it to work as expected (F4 AIO FC with OSD)
  • Do a test flight over a 1km distance with the lest powerful antennas I have to check the viability of the video down and control up link
  • Do a endurance test with all equipment to see how long will the battery will last at what speeds - hope to get at least 40km range before the batteries are all dead)
  • Get back to the 5km mark and back
  • Get past the 10km mark and back
  • Get past the 15km mark and get back
  • Get past the 20km mark and get back

I have no idea if this is going to work or not. I truly don't! But I hope one day I'll have a video to show you how the flight went and what emotions it gave me!

Happy flying!

Sunday, January 21, 2018

Creating your own ESLint shareable configuration

This is going to be a quick one: How to share ESLint configuration between projects.

Step 1: create a separate project

By separate I mean really separate with its own package.json and own repository.

$ mkdir eslint-config-config-name
cd eslint-config-config-name
npm init
...
About to write to .../package.json:

{
  "name": "eslint-config-config-name",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes)

Step 2: create your configuration

Since the main in package.json points to index.js this will be the spot where we put our shareable configuration. Let's start small - you can always extend it further later on as you work your way through the available rues:

module.exports = {
  rules: {
    'no-var': 'error',
  }
}

To make sure you don't forget about installing ESLint in your target project add the following lines to the package.json of your configuration project:

  "peerDependencies": {
    "eslint": ">= 4"
  }

This will ensure that when the target project doesn't have ESLint as a dependency then an error will be thrown telling that it needs to be present.

Step 3: publish your repository

In order for the configuration to be available for everyone you need to have it somewhere everyone can access it. In that case it is recommended to add the following keywords to your package.json.

  "keywords": [
    "eslint",
    "eslintconfig"
  ]

If you choose to publish it to NPM then that's fine, but honestly those config files can get pretty specific for your company therefore I would suggest just tagging and pushing to your Git repository, like so:

$ git init
git add .
git commit -a -m "Initial import"
git tag 1.0.0
git remote add origin url-to-your-repo
git push --set-upstream origin master

Make sure your repository is accessible via HTTP or HTTPS - that's going to be needed for the next step, which is...

Step 4: use the config

In order to use that preset you need to add it as a dependency or devDependency to your project:

  "devDependencies": {
    ...
    "eslint-config-config-name": "git+https://url-to-your-repo#1.0.0"
  }

That way you are not specifying just the latest version but a specific version. This is done so that projects can adopt the changes to your common configuration as they get the time for it.

Next we need to make use of the configuration in our local project. We do that by creating either a local .eslintrc.js or by adding that configuration to package.json. Since we have already created an example .eslintrc.json in the previous step let's see it being used in package.json:

  "eslintConfig": {
    "extends": [
      "eslint-config-config-name"
    ]
  }

That's it! Now when you npm install your dependencies then the configuration and all its dependencies will be installed along with it.

If you would like to see a working example take a look at Vue.js + Electron example on Github that uses the common configuration I prepared for my company.

Happy linting!

Sunday, January 14, 2018

Electron - where Vue.js meets desktop

Today I was pushed by some unseen force to check out Electron - the desktop platform that embeds Chrome for everything. Let me tell you - it is amazing! Not only you have just one browser to care about but it automatically runs on all sorts of platforms! Well, I am sold on the idea - how about you? If you're like me then keep reading.

Getting started

First we need a sort of web application. For obvious reasons I am choosing Vue.js (don't even get me started on Angular X or React - I could bitch about them forever). So Vue.js it is. Obviously we need a build system for it. And what better can we get than POI! So let's do a Vue.js app.

$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install ` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
package name: (vue-electron) 
version: (1.0.0) 
description: 
entry point: (index.js) main.js
test command: poi test
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to .../vue-electron/package.json:

{
  "name": "vue-electron",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "poi test"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes) 

$ npm install --save-dev poi poi-class-component

Since this will be an Electron app we need the electron package too:

$ npm install --save-dev electron

Now we're all set to create our app. In the src folder let's create 2 files: index.js and App.vue

src/index.js

import App from './App.vue'

new App({ el: '#app' })

src/App.vue

<template>
  <h1>{{ message }}</h1>
</template>

<script>
import Vue from 'vue'
import Component from 'vue-class-component'

@Component
export default class App extends Vue {
  message = "Hello, world!"
}
</script>

<style>
h1 {
  background-color: green;
}
</style>

Easy, right? Now here comes the hard part: embedding the application in Electron. I mean, it's not really hard if you know how :). There is a good documentation for both Electron and POI on how do to it but nowhere is there one place that shows the whole solution. So there you have it.

poi.config.js

module.exports = {
  entry: './src/index',
  webpack(config) {
    config.target = 'electron-renderer'
    return config
  },
  homepage: './',
}

main.js

const electron = require('electron')
const isDev = require('electron-is-dev')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const path = require('path')
const url = require('url')

let mainWindow

function createWindow () {
  mainWindow = new BrowserWindow({width: 800, height: 600})

  const entryDev = 'http://localhost:4000'
  const entryProd = url.format({
    pathname: path.join(__dirname, 'dist/index.html'),
    protocol: 'file:',
    slashes: true
  })
  mainWindow.loadURL(isDev ? entryDev : entryProd)

  if (isDev) mainWindow.webContents.openDevTools()

  mainWindow.on('closed', function () {
    mainWindow = null
  })
}

app.on('ready', createWindow)

app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

app.on('activate', function () {
  if (mainWindow === null) {
    createWindow()
  }
})

Now we need a few things to be able to run it. First there needs to be a way of starting the web application and then the Electron container hosting our app. We'll do that by using the concurrently module and by adding a start script to our package.json. While we're at it let's also install electron-is-dev package used in the main.js script:

$ npm install --save-dev concurrently
$ npm install --save electron-is-dev

Since the electron-is-dev package will be part of our Electron app and will not be bundled with the rest of the app then it needs to be added as a regular, transitive, dependency.

Now the promised start script. Remember, that goes into the "scripts" section in your package.json

"start": "concurrently \"poi\" \"sleep 10 && electron .\""

Please note the sleep 10 before starting electron. That is to give the initial bundler run enough time to start serving our app.

And that is it! Your Vue.js app runs in Electron! And yes, it does hot reloading! Isn't that cool? Well... How about giving your app to other people? You wouldn't do that this way as you do in development, would you? We need some bundling techniques. Luckily for us there is a package called electron-packager that has our backs covered.

Bundling

Let's start by installing the electron-packager module:

$ npm install --save-dev electron-packager

With that covered let's add a few more scripts:

"build": "npm run clean && npm run compile && npm run package",
"clean": "rm -rf dist $npm_package_name-*",
"compile": "poi build --no-clear",
"package": "electron-packager . --ignore=\"src|poi.config.js|.gitignore\""

I think they are pretty self explanatory. Now from the console when you start the npm run build command it will first remove all leftovers, then build the client application using POI and then package the application for your platform. If you would like to additionally package your app for a different operating system then you can always start the package target with additional parameters, like so:

$ npm run package -- --platform=win32 --arch=all

And that is it! Electron is super cool - go check it out! And as a reward for getting to those last words here's a link to a GitHub repository that has a ready-made working example: https://github.com/padcom/vue-electron-example. Besides the mentioned settings it also contains ESLint integration that I described in my previous post.

Happy coding!

Saturday, January 13, 2018

POI, Vue and ESLint

Writing code is hard. It is however much harder to write code that conforms to standards that are already in the project. Fortunately for us there is ESLint with all the bells and whistles that help in that tedious task. And believe it or not there are ready-made presets and plugins that make the configuration really simple.

Installation

First, as usual, we need some packages added to our project:

$ npm install --save-dev eslint poi-preset-eslint eslint-plugin-vue babel-eslint
Then create or update your existing poi.config.js with as follows:
module.exports = {
  presets: [
    require('poi-preset-eslint')({ mode: '*' }),
  ]
}

Configuration

All that is left is to configure ESLint. I prefer to have a set of rules that don't get in the way while developing but hold the forth when building final version. That is why some of my settings are environment-dependant:

// This defines the mode of operation for checks depending on the environment
const mode = process.env.NODE_ENV === 'production' ? 'error' : 'warn'

module.exports = {
  parserOptions: {
    'parser': 'babel-eslint',
    'sourceType': 'module'
  },

  extends: [ 'eslint:recommended', 'plugin:vue/recommended' ],

  rules: {
    'camelcase': [ 'error', { 'properties': 'always' } ],
    'func-name-matching': 'error',
    'func-names': [ 'error', 'never' ],
    'object-shorthand': [ 'error', 'always' ],
    'prefer-const': 'error',
    'prefer-template': 'error',
    'template-curly-spacing': [ 'error', 'never' ],
    'no-useless-rename': 'error',
    'no-useless-constructor': 'error',
    'arrow-spacing': [ 'error', {
      'before': true,
      'after': true,
    } ],
    'arrow-parens': [ 'error', 'as-needed' ],
    'arrow-body-style': [ 'error', 'as-needed' ],
    'no-const-assign': 'error',
    'prefer-numeric-literals': 'error',
    'indent': [ 'error', 2, {
      'SwitchCase': 1,
    } ],
    'semi': [ 'error', 'never' ],
    'quotes': [ 'error', 'single' ],
    'comma-dangle': [ 'error', 'always-multiline' ],
    'no-console': mode,
    'no-debugger': mode,
    'no-alert': mode,
    'no-var': 'error',
    'one-var': [ 'error', 'never' ],
    'space-before-function-paren': [ 'error', {
      'anonymous': 'never',
      'named': 'always',
      'asyncArrow': 'always',
    }],
    'object-curly-spacing': [ 'error', 'always' ],
    'array-bracket-spacing': [ 'error', 'always' ],
    'computed-property-spacing': [ 'error', 'never' ],
    'key-spacing': [ 'error', {
      'beforeColon': false,
      'afterColon': true,
    } ],
    'keyword-spacing': 'error',
    'space-infix-ops': 'error',
    'space-unary-ops': 'error',
    'space-in-parens': [ 'error', 'never' ],
    'comma-spacing': [ 'error', { 'before': false, 'after': true } ],
    'no-whitespace-before-property': 'error',
    'no-multi-spaces': 'error',
    'no-multiple-empty-lines': [ 'error', { 'max': 1 } ],
    'dot-location': [ 'error', 'property' ],
    'getter-return': 'error',
    'consistent-return': [ 'error', { 'treatUndefinedAsUnspecified': true } ],
    'valid-jsdoc': 'error',
    'eqeqeq': 'error',
    'no-return-assign': [ 'error', 'always' ],
    'vue/max-attributes-per-line': [ 'error', {
      singleline: 5,
      multiline: { max: 5, 'allowFirstLine': true },
    } ],
    'vue/html-indent': [ 'none' ],
    'vue/attribute-hyphenation': [ 'error', 'never' ],
  },
}

As for the rules (and remember this is a personal preference), I don't like the fact of being forced to write every single attribute for a component in a separate line. I think this looks awful. Sure, if there are more than X attributes then it should be in a separate line. That's why the number is so high (5). Also, I found that the hyphenation of attributes that are bound, like :selection-mode causes compilation errors so this one is (at least currently) broken.

Practice

A hint for transitioning projects from non-eslint to eslint: when you'll start with the project there will be a massive number of violations. I mean like hundrets of them. Before you commit to fixing them all consider selectively disabling ESLint in all files by means of the following directive:

/* eslint-disable */

Then, when you will get the time remove that directive from 1 file and fix warnings. Then move to the next until ESLint is enabled in all the files. That will make the job much more approachable!

And that is it! Of course you can add or change more rules as you see fit. Whatever works for you.

Happy coding!

Sunday, December 31, 2017

Creating libraries with Vue components

Vue.js is great. There is no question about it. It's small, versatile, conscious, easy to learn - you name it. With the addition of POI it is probably also one of the most friendly frameworks to work with. Let's see how we can share single-file components between projects!

Method 1: npm link

npm link is probably one of the least known features if your primary occupation is creation of web application and you have never created an NPM package yourself. It has been there in NPM for ages and is the primary tool for library creators.

To use it when you have created your library and want to test it then what you do is you run

npm link

in the library folder and you run

npm link <library-name>

in the project that you want to use the library in. That gives you practically a hard binding between the library and the project using a symlink which means that if you introduce a change in the library then the project sees those changes immediately. It's a fantastic tool when you're in the process of working on the library. It can be further streamlined when we specify a dependency using the file: protocol like so:

"dependencies": {
  "my-library": "file:path-to-library"
}

This is also a great option if the library itself is part of your project and is being used to simply tear the application code from the reusable components. This is a great idea because if the application code changed often and the components change seldom then the size of your client.js gets smaller as all code from node_modules (this includes libraries referenced with the file: protocol) get pushed into the vendor.js block.

"dependencies": {
  "project-components": "file:../project-components"
}

This will automatically setup a link named project-components inside node_modules. What's even more interesting, when you do the npm install to install dependencies then the project-component's dependencies will be installed in ../project-components/node_modules automatically thus each project that uses that library won't need a copy of the shared dependencies.

Method 2: npm publish

When you want to go for a reusable library that you'd like to share between multiple projects then the right way to go is to use a repository. There is a multitude of options but the one I find really useful in an organization are npmjs.com for all things opensource and verdaccio for all the packages that you would like to keep available only for in-house components. The nice thing about verdaccio is that it automatically acts as a proxy to npmjs.com so all you do in your project is you tell npm that the repository is located where verdaccio is

npm set registry http://localhost:4873/

and you're all set.

Example

To show you how it is done I have created a sample project on GitHub. It consists of 3 parts: the library containing 2 components, a host application using those components and a little bit of automation to start the repository server.

Patterns

When creating a component library there are 2 options that one can use:

  • include the modules that build the library
  • include the built version of the library

Which one to use depends on the library itself. If you create a library that is to be used in all sorts of projects but you wrote it in some non-standard way (let's say you used CoffeeScript) then to make it useful for the rest of us you need to point the main to the compiled version. However, if you created a library specifically for use within a specific environment it makes sense to create an index.js and point your main entry in package.json to it and to put all your code in the lib folder:

export { default as componentA } from './lib/componentA.vue'
export { default as componentB } from './lib/componentB.vue'
That way, if someone wants to include all of your library they just include it and all is great:

import myLib from 'myLib'

Or using requirejs:

const myLib = require('myLib')

If for whatever reason they want to include only a portion of it, let's say just one or two components and not the whole shebang, then they are free to do so as follows:

import componentA from 'myLib/lib/componentA.vue'

Or using requirejs:

const componentA = require('myLib/lib/componentA.vue')

The latter form allows for the treeshaking algorithm to exclude everything that is not used by the application in the resulting file thus making the final bundle smaller (sometimes a lot smaller - see the use of lodash).

To create a compiled version of your library with POI all you need is to run the following command:

poi build --library index.js

and in the dist folder you'll get a library.js that you point the main in package.json to and you're done!

That's it! It is really simple to create libraries shared between parts of your project and not all that much more complex to create universal applications shared with everyone else in the NPM ecosystem.

Happy coding in 2018!

Thursday, December 14, 2017

Using webfonts-generator with POI

The problem

This might come as a surprise to you (it did to me at least) that POI is not handling generation of webfonts by default. It does so many wonderful things but webfonts generation is not one of them, unfortunately. You might ask what are webfonts and why do we need to generate one in a frontend project? Aren't there enough fonts to choose from already? To answer that we need to understand how browsers work when downloading multiple resources. First of all there is a limit to how many connections can be made at the same time to one server. Secondly there is a limit to how many connections can there be in total at any given time. So if, for example, you'd like to load 30 small black and white images (for use as icons) then the browser would have a lot of work to do and the page would load very, very slowly. To mitigate that problem you could use a custom font that will contain all the images, compressed, and it will take a fraction of the time to load. This is exactly what webfonts-generator does.

The solution

So how do we use it in a POI project if it is not available by default? It's actually not that simple. We need to extend the already impressive webpack configuration to teach it what to do with the font descriptors. Font descriptors are just JavaScript modules ending with .font.js exporting definition of the font, like the one below:

module.exports = {
  fontName: 'icons',
  files: [
    'phone.svg',
  ]
}

Having that in place we can start extending our configuration. As you already know POI contains webpack rules for all the common types of files. Those rules differ from production to test to development configuration. The rule we need to add for it to work properly needs to be just like the one for CSSes, just with the webfonts-loader added at the end of the use list. So first we need to find the css rule:

const isCssRule = rule => rule.test.toString().indexOf('\.css$') >= 0
const findCssRule = rules => rules.find(isCssRule)

Having that rule in hand let's create another rule for webfonts that contains the definition of a loader:

const constructWebfontRuleFromCssRule = rule => ({
  test: /\.font\.js$/,
  use: [
    ...rule.use, 
    {
      loader: 'webfonts-loader', options: {
        fileName: 'assets/fonts/[fontname].[hash:8].[ext]'
      }
    },
  ]
})

Then we can add the rule to webpack configuration in poi.config.js:

module.exports = {
  webpack (config) {
    const rule = findCssRule(config.module.rules)
    config.module.rules.push(constructWebfontRuleFromCssRule(rule))

    return config
  }
}

And finally we need to install the additional loader like so:

npm install --save-dev webfonts-loader

webfonts-generator is a transitive dependency so it will be installed automatically too.

Final thoughts

I know that doesn't seem like much but it took me way too long to figure it out so I figured I'll post it for posterity. In the meanwhile I have created a ticket in the POI project for it to be implemented as part of the default configuration so that we won't have to deal with it ourselves. Will see if that will catch on or not.

Happy building!

Thursday, December 7, 2017

POI and naming classes in CSS Modules

This will be a quick one. If you're trying to use CSS modules with POI in a Vue.js app and you want to exercise some control over what the names of the generated classes are then there is a configuration option in POI for that. It's a little bit hidden so it took mi like 5 minutes to figure it out - hence the post.

module.exports = {
  vue: {
    cssModules: {
      localIdentName: '[hash:base64:5]'
    }
  }
}

That weirdness stems from the fact that POI configures the css-loader indirectly via the vue when setting up the vue-loader. We can take advantage of the fact that it uses the Object.assign method and sneak in our configuration option.

Happy coding!