- SYSTEM\CurrentControlSet\Enum\USB;
- HARDWARE\DEVICEMAP\SERIALCOMM;
C++ Demo
SerialPortFinder is the C++ projectC# Demo
USBInfo is a .NET/C# project.All the code can be found on my Github repo at the following link.
Enjoy
> npm install --save-dev electron-builder
After that we need to add some sections to our package.json confinguration file....
"build": {
"appId": "com.maurizioattanasi.electron-hmi",
"mac": {
"target": "dmg",
"icon": "src/assets/icons/mac/icon.png"
},
"win": {
"target": "nsis",
"icon": "src/assets/icons/win/icon.ico"
},
"linux": {
"target": [
"snap"
]
}
}
...
and a dist command in the script section to build and distribute the application itself...
"electron": "ng build && electron .",
"electron-aot": "ng build --aot && electron .",
"dist": "build"
...
As it can be seen in the build section we have three subsections win, mac and linux meaning that, depending on the platform on which we’re developing our app we can have an appropriate installer.Let’s tak a step ahead in our journey toward the target of this notes that is, how to build an HMI runnable on different platforms using some Web Technologies.
The first step is to rearrange our project in order to introduce TypeScript.
'../index.html'
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": false,
"sourceMap": true,
"outDir": "dist",
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*"
]
}
},
"include": [
"src/**/*"
]
}
"build": "tsc",
and then we need to modify a little our start script as follows:
"start": "npm run build && electron ./dist/main.js"
so that, in a few words, it will initially compile our TypeScript project, and then launch the application passing to the electron command the resulting main.js file.
The code written for this note is available for consulting and comments on my GitHub Repository at the following link.
For now that’s all folks.
Enjoy, 😉
As an engineer involved in the last two decades in design and development of complex systems in different fields such as Industrial Automation, Internet Of Things, and lately in Virtual/Augmented Reality aided Training Systems, one of the tasks to face with is the development of different HMI clients to allow the interaction between the end user and the above mentioned systems.
This note, and the attached project, freely available on my github repository, is aimed to identify a set of technologies that will allow the realization of those interfaces independently, as far as possible, from the hardware and operating system platform on which they are intended to run. Independency, in my humble opinion, is, and will be, a key concept, that will allow system engineers to scale all the components to the real needs and to the budget available to accomplish the requirements of a project.
Among all the different techonologies available on the market nowadays, the only ecosystem that is really cross platform is the one centered on the javascript programming language. Neither Java or .NET are capable of such a capillary diffusion natively. The language once used only for client applications running in a browser, thanks technologies like Node.js make its use allowed server side also. There’s a lot of tools and libraries borrowed from the Web Applications field that make the implementation of appealing user interfaces really simple. Boxing all this stuff in a desktop application runnable on Windows, Linux or even macOS, is really straightforward with frameworks such as electronjs.
The only caveat toward the use of plain javascript for large applications is in the nature of javascript itself or in the fact of being a loosely typed programming language. This fact made me, and many other old style programmers I suspect, suspicious respect the use of this idoma, but when I first read about TypeScript, a superset of Javascritpt introduced by Microsoft, that introduces types in JS, I felt very curious, and, a I always do with those kind of things, I began to study, and to search for some tools that hopefully will allow me to achieve the target that is to implement an HMI capable to connect and communicate over some channels and to run on my macbook, on a Windows pc/laptop and also on a tiny Raspbery Pi.
Let’s begin preparing to necessary to build do our application. To prove that the final product is really cross platform I will use a Macbook Pro with macOS High Sierra ver. 10.3.3 as my main development machine and a Windows 10 Pro laptop and an Ubuntu Server 16.04 LTS machine to repackage and test the resulting application.
To install all the necessary packages and frameworks I’ll make use of package managers, and, in detail:
First of all we need to have node.js, the javascript runtime, installed on development machines. I prefer using an LTS version, that, at the time of writing is 8.9.4 LTS. We can download the installer for the desired os from the node.js site, or we can use the above mentioned package managers.
On macOS, on a terminal we’ll issue the command
MacBook-Pro-di-Maurizio:/ maurizioattanasi$ brew install node@8
which will download and install the latest release of version 8. After the installation process terminates, on the same command line we’ll state the following command:
On a Windows system, with Chocolatey installed, on a command prompt, we’ll use the command:
C:\> choco install nodejs-lts
On Debian/Ubuntu machines, we’ll use:
m***@m***-server:~$ apt-get install -y nodejs
After the installation process, on a command promp (or Terminal), we’ll issue the command node -v to check the current version of node, and npm -v, to check the current version of npm.
MacBook-Pro-di-Maurizio:/ maurizioattanasi$ node -v
v8.9.4
that tells us that we have installed the desired version. As a side effect of installing node.js, we have also at our disposal the *Node Package Manager a.k.a. npm. To check the npm version, on the terminal, we’ll issue:
MacBook-Pro-di-Maurizio:/ maurizioattanasi$ npm -v
5.6.0
By installing TypeScript we mean the TypeScript compiler, named tsc that is the main component needed to compile TypeScript code into plain JavaScritpt. Having npm installed on our development machines, this step is quite straightforward. On a command prompt, or a terminal, simply type:
MacBook-Pro-di-Maurizio:/ maurizioattanasi$ npm install -g typescript
then hit enter. After completion we’ll have tsc, the TypeScript compiler, installed globally (-g switch applyed to the npm command). To check the version installed, as usal, type:
MacBook-Pro-di-Maurizio:~ maurizioattanasi$ tsc -v
Version 2.6.2
Perfect! We have node.js version 8.9.4, npm version 5.6.0 and TypeScript version 2.6.2 installed on our machine. From now on we only need a text editor, one with which we feel at ease, and we can start writing down some code.
To start a node project we’ll use npm again:
maurizioattanasi$ npm init -y
that will result in a package.json file which content will be similar to:
{
"name": "electron-hmi",
"version": "1.0.0",
"description": "Demonstration project to explore the possibilities offered by multiplatform technologies such as node.js, typescript and electron, in **H**uman **M**achine **I**nterfaces (HMI) development.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/alien70/electron-hmi.git"
},
"keywords": [],
"author": "",
"license": "MIT",
"bugs": {
"url": "https://github.com/alien70/electron-hmi/issues"
},
"homepage": "https://github.com/alien70/electron-hmi#readme"
}
Following the instructions on the npm page for electron, on the terminal:
maurizioattanasi$ npm install electron --save-dev --save-exact
or
maurizioattanasi$ npm i electron -D -E
at the end we’ll have the package.json file
"devDependencies": {
"electron": "1.8.2"
}
<tldr></tldr>
Following the electron tutorial Writing Your First Electron App
, we’ll create a main.js file and a index.html file and we’ll modify the package.json as follow:
...
"main": "main.js",
"scripts": {
"start": "electron ."
},
...
Now we can try our application running…
maurizioattanasi$ npm start
…and, here we have our first web based desktop application.
The next step is to replace the JavaScript code with TypeScript.
Enjoy
$ docker pull joomla$ docker pull mysql$ docker run -it --link some-mysql:mysql --rm mysql sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD"'
$ docker run --name some-joomla --link some-mysql:mysql -p 8080:80 -d joomlaLa seconda possibilità è quella di utilizzare Docker Compose e quindi:
###########################
# Joomla! CMS Container
joomla:
# nome univoco del container
container_name: my_web_site
# Definisce se il container deve essere riavviato
# (i pssibili valori sono no, always e on-failure)
restart: always
# Definisce l'immagine da cui avviare il container
image: joomla
# Collega il container ad un altro servizio
# (in questo caso a quello nel quale gira l'immagine di mysql)
links:
- joomladb:mysql
# Espone la porta, interna al container, all'esterno sulla porta 8080
ports:
- 8080:80
# Esegue il mount di una cartella del container (/var/www/html)
# su una dell'host (./html)
volumes:
- ./html/:/var/www/html
# Joomla! CMS Container
###########################
###########################
# MySql Container
joomladb:
container_name: my_web_site_db
restart: always
image: mysql
# Aggiunge alle variabili d'ambiente del container
# la password assegnata all'istanza di mysql (top of security XD)
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- 3306:3306
volumes:
- ~/usr/local/var/mysql/:/var/lib/mysql
# MySql Container
###########################
$ docker-compose up -d $ docker-compose downimport { Component, OnInit, ViewChild } from '@angular/core';
import { Subscription } from 'rxjs/Rx';
import { Router } from '@angular/router';
import { environment } from '../../environments/environment';
import { BookStoreService } from '../bookstore.service';
import { Book } from '../models/book';
import { ModalComponent } from '../modal/modal.component';
@Component({
selector: 'app-library',
templateUrl: './library.component.html',
styleUrls: ['./library.component.css'],
providers: [BookStoreService]
})
export class LibraryComponent implements OnInit {
title = 'Book Store';
books: Book[] = [];
errorMessage: string = '';
isLoading: boolean = true;
@ViewChild(ModalComponent) modal: ModalComponent;
private subscription: Subscription;
constructor(private _router: Router, private _bookStoreService: BookStoreService) { }
ngOnInit() {
this.reloadData();
}
reloadData() {
this._bookStoreService
.GetAll()
.subscribe(
b => this.books = b,
e => this.errorMessage = e,
() => this.isLoading = false);
}
onNew() {
console.log("onNew");
this._router.navigate(['/edit', 'new']);
}
onEdit(book: Book) {
console.log('edit ' + book.id);
this._router.navigate(['/edit', book.id]);
}
onDelete(book) {
var message: string = 'Delete \'' + book.title + '\'?: ';
this.modal.Title = 'Warning';
this.modal.show(message);
this.subscription = this.modal.observable.subscribe(x => {
if (x) {
this._bookStoreService.Delete(book.id).subscribe(
book => {
let b = this.books.find(item => item.id === book.id);
let id = this.books.indexOf(b);
this.books.splice(id, 1);
if (!environment.production)
console.log(JSON.stringify(book));
},
error => {
console.log(error);
}
);
}
this.subscription.unsubscribe();
});
}
}
e dal servizio BookStoreService, cuore del vero interfacciamento con il servizio REST:import { Injectable } from '@angular/core';
import { Http, Response, Headers } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { Book } from './models/book';
import { environment } from '../environments/environment';
@Injectable()
export class BookStoreService {
private baseUrl: string;
constructor(private _http: Http) {
this.baseUrl = environment.bookStoreApi.server + environment.bookStoreApi.apiUrl + '/books/';
}
GetAll(): Observable<Book[]> {
if (!environment.production)
console.log(this.baseUrl);
let books$ = this._http.get(this.baseUrl, { headers: this.GetHeaders() })
.map(mapBooks)
.catch(handleError);
return books$;
}
public GetById = (id: string): Observable<Book> => {
let books$ = this._http.get(this.baseUrl + id, { headers: this.GetHeaders() })
.map(response => response.json())
.catch(handleError);
return books$;
}
public Create = (book: Book): Observable<any> => {
let book$ = this._http.post(this.baseUrl, book, { headers: this.GetHeaders() })
.catch(handleError);
return book$;
}
public Update = (id: string, book: Book): Observable<any> => {
let book$ = this._http.put(this.baseUrl + id, book, { headers: this.GetHeaders() })
.catch(handleError);
return book$;
}
public Delete = (id: string): Observable<Book> => {
let book$ = this._http.delete(this.baseUrl + id)
.catch(handleError);
return book$;
}
private GetHeaders() {
let headers = new Headers();
headers.append('Accept', 'application/json');
return headers;
}
}
function mapBooks(response: Response): Book[] {
return response.json().map(toBook);
}
function toBook(r: any): Book {
if (!environment.production)
console.log('toBook: ' + JSON.stringify(r));
let book = <Book>({
id: r.id,
title: r.title,
authors: r.authors,
publicationYear: r.publicationYear,
isAvailable: r.isAvailable
});
if (!environment.production)
console.log('Parsed book: ', book);
return book;
}
function handleError(error: Response) {
return Observable.throw(error || 'Server error');
}
Per avere un’idea di come l’utilizzo della FRP abbia reso più agevole la gestione della programmazione asincrona focalizziamo la nostra attenzione sul metodo GetAll() che ritorna uno stream Observable GetAll(): Observable<Book[]> {
if (!environment.production)
console.log(this.baseUrl);
let books$ = this._http.get(this.baseUrl, { headers: this.GetHeaders() })
.map(mapBooks)
.catch(handleError);
return books$;
} this._bookStoreService
.GetAll()
.subscribe(
b => this.books = b,
e => this.errorMessage = e,
() => this.isLoading = false);
In modo analogo vengono implementate le interfacce verso gli altri metodi esposti dal servizio REST.