VIM在文件夹中查找

在vim中提供2中方法来在其他文件或者文件夹中搜索字符串,第一种是vimgrep还有一种是grep。

如果只是在当前打开的文件中查找字符串的,使用

😕

后面加上想要搜索的字符串就可以。

这里要解决的是在别的文件中,也许这个文件还没有打开,或者需要在多个文件中操作,比如在当前文件夹下面所有.c的文件中查找,或者在莫个文件夹中查找。这些情况下,我们可以使用 vimgrep或者grep。这里只介绍vimgrep。

这是个vim的内部命令,因为可以在vim内部直接使用,

:vimgrep

这个命令还可简写为vim

:vim

用例1  在当前文件夹下面所有.c文件中查找字符串 classA

:vimgrep /classA/gj ./*.c

解释一下:

g: 如果一行中出现多处,只显示一次,没有g就会出现多次

j: 只是列出所有匹配的行,没有j那么就会跳去第一个匹配的行

用例2 在当前文件夹及其子文件夹下面查找字符串classA

:vimgrep /classA/gj **/*.*

搜索完毕后可以使用copen或者cw命令来打开搜索的结果。使用jk来移动选中行,回车可以跳到对应的文件。

copen后面可以指定窗口的高度。

注意当去到搜到到的文件后,当前的窗口会被该文件覆盖,那么怎么回到原来的文件,

ctrl+^

更多的信息可以使用

:help vimgrep

来查看。

发表在 Linux | 留下评论

命令行生成uuid

cat /proc/sys/kernel/random/uuid

发表在 Linux | 留下评论

ECLIPSE IOT CHALLENGE: MQTT COMMUNICATION FOR HOT DESKS FEATURING KURA, MOSQUITTO AND PAHO AND PROJECT WRAPUP

The last article focused on the sensor logic, today’s text is all about communication. As you might remember regarding former articles we would like to publish the status of the desks in the office so that employees can inform themselves right before they start their journey to their workplace. How can we achieve this? The solution is simple: MQTT over WebSockets in combination with a responsive web frontend. See it in action:

Mosquitto installation and setup

First of all we need a MQTT broker. A tiny but powerful broker is mosquitto of the eclipse IoT stack. The current version 1.4 offers WebSocket support out of the box. Unfortunately this version is not available via apt so we have to compile it ourselves. Here are the steps you need to perform for the installation on a raspberry pi with raspbian wheezy.

1. Install cmake for the compilation process

2. Install libwebsockets in order to use MQTT over WebSockets

3. install uuid-dev for the mosquitto compile

4. download and extract mosquitto

5. edit the config.mk file and enable WebSocket support by changing WITH_WEBSOCKETS:=no  to WITH_WEBSOCKETS:=yes

6. compile and install mosquitto

Before we start the broker we need a configuration. A good starting point is to copy the example configuration from /usr/share/doc/mosquitto/examples/mosquitto.conf.example to /etc/mosquitto/mosquitto.conf Now you have a very big configuration file with everything commented out. No need to worry, basically the  configuration should contain the following entries:

The password file must be generated to the /etc/mosquitto/ folder with the following command:

This is an interactive command and you will be prompted to enter and repeat a password for the user. The last parameter of the command is the username.

The password_file setting is needed for two reasons:

  1.  You should at least have some kind of technical user for the broker so that not everyone in your network can connect and publish data. Someone could easily mislead your projects with fake data.
  2. Eclipse kura requires it…

A better way is an additional configuration of the acl_file setting. There you can define who can read or write topics. For more information check the mosquitto configuration. It contains a detailed description of the feature.

The other parts of the configuration are needed to enable the WebSockets listener.

Now let’s start up the broker:

If you get an error like this one:

Probably your libwebsocket file is located in the /usr/local/lib folder but mosquitto searches for it in the /usr/lib folder. You can solve this problem with a symbolic link:

That’s it for mosquitto, let’s move on to the kura configuration for the MQTT data transport.

MQTT communication with kura

Kura offers two services for data communication:

  • DataService: basic communication services, like connection handling
  • MqttDataTransport: a special service for the MQTT communication

At first we need to configure the MqttDataTransport. Navigate with your web browser to the kura web ui (the ip address of the pi) and select the element right at services. Enter your broker data: broker url, username and password and the client id. Your settings should look like this:

mqtt_service_settings

You can now connect manually or let kura do the job on startup .We decided us for the latter since a data connection is mandatory for our solution. Just hit the DataService configuration in the kura ui and check the “connect.auto-on-startup“ option. Save your settings and restart kura.

If everything runs fine your startup log under /var/log/kura.log should not contain any wild stacktraces. In our case we had a very special issue here: our raspberry pi was configured to another locale than an english one. We got the following error:

This is a simple bug in kura. It will only start up for english locales since it searches for the keyword „metric“ within the output of ifconfig <interface>. Unfortunately if you have another locale configured, for example german, ifconfig returns „metrik“ (german word for metric) and kura fails to parse the output with the above error. After changing the locale with raspi-config everything worked fine.

After the configuration let’s implement a publisher. Therefore we need a reference to the DataService. First we implement the field and methods for the injection:

Afterwards we need to configure the injection in the component.xml file:

Now that we have created the service we would like to know when it is time to subscribe. Basically we want to subscribe right after the connection is set up. Therefore we implement the DataServiceListener interface. The relevant method is onConnectionEstablished:

Notice that the DataServiceListener interface will only work if you provide a service in the component.xml file. We also provide another service here for the DeskStateChangedListener interface. We will discuss this in a minute. First lets take a look at the complete component.xml file:

We use the DeskStateChangedListener in order to get a update once the desk state changes, e.g. from occupied to free. The implementation of the interface looks like this:

We send every message with the retained flag. This way we make sure that clients which connect at a later point in time receive the last known state.

In order to notify the publisher of state changes we use the whiteboard pattern. The hot desking dilemma module contains a list of listeners. The list get injected by the container, you only need appropriate methods. Your component.xml file of the module should look like this:

The methods are very simple and look like this:

Here is a snippet of the activate method with the adapted code:

The notifyListeners method is pretty simple:

That’s it! Our publisher get now notified for every state change of the occupancy LED via MQTT.

The web ui

Screenshot - 24_03 002

The last part is about receiving the MQTT messages and displaying the status. Therefore we implement a simple responsive angularjs frontend and use eclipse paho as javascript MQTT client. Paho is very simple to use. Let’s have a quick look at the usage:

As soon as the connection is established we subscribe to the hot desk topic:

We subscribe to hot-desks/# to get updates of all tables. As soon as a message arrives we update our data model:

Conclusion

This is the final article of the eclipse open iot challenge but our project is not yet production ready. In the next months we need to test time frame settings and see how people react to it.

At the beginning we started with the sensor selection and the development of kura packages. We both knew OSGi and worked with it before but never heard of kura before the contest. The idea of kura to build a layer on top of platforms like a raspberry pi is very good. After spending some time with it we learned easily how to use it but we also think that one important thing is missing: a GPIO service. Almost every project with a sensor makes use of the GPIO header! We have already seen the draft here:https://wiki.eclipse.org/Kura_GPIO_Support, so keep it going! The DataService of kura is currently one of the best and useful parts. It is very easy to use and offers great functionality. The raspberry pi image is also very good and everything worked out flawless.

The implementation itself was easy and we think that everyone with a bit of knowledge of OSGi can be successful in a short amount of time.

In order to publish the state of our hot desks to the users we decided to use MQTT with the mosquitto broker. The installation of mosquitto was harder than expected since we wanted to use MQTT over WebSockets and Version 1.4 is not yet available in the apt repository. But once the installation was done everything worked quite well. It is fast and easy to configure. We also noticed the low memory consumption. This is definitely a big plus! About MQTT we liked the retain message option and the simplicity of the protocol.

After configuring kura to connect to the mosquitto broker we implemented a publisher module and used the whiteboard pattern.

In the last stage we connected our frontend to display the latest hot desk status. We chose the paho javascript client and it was the right decision. It is very easy to use and worked out great!

You can check out the source code of the project here:https://github.com/fisch3r/hot-desking-dilemma.

We hope that our project can be useful to others in order to learn more about the eclipse iot stack.Therefore we took great care of the traceability of our steps.

原文链接:

http://icanseedeadcats.com/tag/mosquitto/

发表在 Linux | 留下评论

c – Long Long to char conversion function

#include <stdio.h>
#include <limits.h>


char* lltoa(long long val, int base){

    static char buf[64] = {0};

    int i = 62;
    int sign = (val < 0);
    if(sign) val = -val;

    if(val == 0) return "0";

    for(; val && i ; --i, val /= base) {
        buf[i] = "0123456789abcdef"[val % base];
    }

    if(sign) {
        buf[i--] = '-';
    }
    return &buf[i+1];

}

int main() {
    long long a = LLONG_MAX;
    long long b = LLONG_MIN + 1;
    long long c = 23;

    printf("%ld\n", sizeof(a));
    printf("max '%s'\n", lltoa(a, 10));
    printf("min '%s'\n", lltoa(b, 10));
    printf("-1  '%s'\n", lltoa((long long)-1, 10));
    printf("23  '%s'\n", lltoa(c, 10));
}

 

发表在 Linux | 留下评论

Android Platform Guide

This guide shows how to set up your SDK environment to deploy Cordova apps for Android devices, and how to optionally use Android-centered command-line tools in your development workflow. You need to install the Android SDK regardless of whether you want to use these platform-centered shell tools or cross-platform Cordova CLI for development. For a comparison of the two development paths, see the Overview. For details on the CLI, see The Command-Line Interface.

Requirements and Support

Cordova for Android requires the Android SDK. See the Android SDK’s System Requirements.

Cordova supports Android 2.3.x (Gingerbread, starting with Android API level 10) and 4.x. As a general rule, Android versions become unsupported by Cordova as they dip below 5% on Google’s distribution dashboard. Android versions earlier than API level 10, and the 3.x versions (Honeycomb, API levels 11-13) fall significantly below that 5% threshold.

Install Cordova Shell Tools

If you want to use Cordova’s Android-centered shell tools in conjunction with the SDK, download Cordova fromcordova.apache.org. Otherwise ignore this section if you plan to use the cross-platform CLI tool described in The Command-Line Interface.

The Cordova download contains separate archives for each platform. Be sure to expand the appropriate archive, android in this case, within an empty directory. The relevant executible utilities are available in the top-level bin directory. (Consult theREADME file if necessary for more detailed directions.)

These shell tools allow you to create, build, and run Android apps. For information on the additional command-line interface that enables plugin features across all platforms, see Using Plugman to Manage Plugins. See Application Plugins for details on how to develop plugins.

Install the Android SDK from developer.android.com/sdk. The android sdk is distributed as an ‘adt-bundle-<os>-<arch>-<ver>’ file. On windows, the adt-bundle is packaged with an installer. On OSX and Linux, simply unpack the ‘adt-bundle’ in the location you store development tools. More detailed information on Android SDK setup can be found here

For Cordova command-line tools to work, or the CLI that is based upon them, you need to include the SDK’s tools and platform-tools directories in your PATH. On a Mac, you can use a text editor to create or modify the ~/.bash_profilefile, adding a line such as the following, depending on where the SDK installs:

    export PATH=${PATH}:/Development/adt-bundle/sdk/platform-tools:/Development/adt-bundle/sdk/tools

Add the paths for java and ant if needed. This line in ~/.bash_profile exposes these tools in newly opened terminal windows. If your terminal window is already open in OSX, or to avoid a logout/login on Linux, run this to make them available in the current terminal window:

    $ source ~/.bash_profile

To modify the PATH environment on Windows 7:

  1. Click on the Start menu in the lower-left corner of the desktop, right-click on Computer, then select Properties.
  2. Select Advanced System Settings in the column on the left.
  3. In the resulting dialog box, press Environment Variables.
  4. Select the PATH variable and press Edit.
  5. Append the following to the PATH based on where you installed the SDK, for example:
    ;C:\Development\adt-bundle\sdk\platform-tools;C:\Development\adt-bundle\sdk\tools
    
  6. Save the value and close both dialog boxes.

You may also need to enable Java and Ant. Open a command prompt and type java, and also type ant. Append to the PATH whichever of these fails to run:

    ;%JAVA_HOME%\bin;%ANT_HOME%\bin

Open a New Project in the SDK

At this point, to create a new project you can choose between the cross-platform CLI tool described in The Command-Line Interface, or the set of Android-specific shell tools. From within a source-code directory, here’s the CLI approach:

    $ cordova create hello com.example.hello HelloWorld
    $ cd hello
    $ cordova platform add android
    $ cordova build

Here’s the corresponding lower-level shell-tool approach for both Unix and Windows:

    $ /path/to/cordova-android/bin/create /path/to/new/hello com.example.hello HelloWorld
    C:\path\to\cordova-android\bin\create.bat C:\path\to\new\hello com.example.hello HelloWorld

Here’s how to use the SDK to modify it:

  1. Launch the Eclipse application.
  2. Select the New Project menu item.
  3. Choose Android Project from Existing Code from the resulting dialog box, and press Next:

  4. If you’re using the CLI, navigate to the hello directory you created for the project, then to the platforms/androidsubdirectory. Alternately, if you use the create shell utility, simply navigate to the hello directory.
  5. Press Finish.

Once the Eclipse window opens, a red X may appear to indicate unresolved problems. If so, follow these additional steps:

  1. Right-click on the project directory.
  2. In the resulting Properties dialog, select Android from the navigation pane.
  3. For the project build target, select the highest Android API level you have installed.
  4. Click OK.
  5. Select Clean from the Project menu. This should correct all the errors in the project.

Build the Project

If you are using the CLI in development, the project directory’s top-level www directory contains the source files. Run any of these within the project directory to rebuild the app:

    $ cordova build                   # build all platforms that were added
    $ cordova build android           # build debug for only Android
    $ cordova build android --debug   # build debug for only Android
    $ cordova build android --release # build release for only Android

If you are using the Android-specific shell tools in development, there is a different approach. Once you generate the project, the default app’s source is available in the assets/www subdirectory. Subsequent commands are available in its cordovasubdirectory.

The build command cleans project files and rebuilds the app. Here is the syntax for both Mac and Windows. The first pair of examples generate debugging information, and the second builds the apps for release:

    $ /path/to/project/cordova/build --debug
    C:\path\to\project\cordova\build.bat --debug

    $ /path/to/project/cordova/build --release
    C:\path\to\project\cordova\build.bat --release

When building for release, if you add the following definitions to your local.properties file, then your APK will get signed and aligned such that it will be ready for upload to the Google Play store:

    key.store=/Users/me/Developer/mykeystore.jks
    key.alias=mykeyalias

If the keystore and/or the aliased key have a password, the build script will prompt you for the password. You do not need to define the passwords in a properties file. If you do want to avoid the prompt, you can define them in local.properties as key.store.password and key.alias.password. Be aware of security concerns with those passwords if you do so.

Configure an Emulator

You can use either the cordova CLI utility or Cordova’s Android-centered shell tools to run an app in an emulator. Either way, the SDK must first be configured to display at least one device. To do so, use the Android SDK Manager, a Java application that runs separately from Eclipse. There are two ways to open it:

  1. Run android on the command line.
  2. From within Eclipse, press this toolbar icon:

Once open, the Android SDK Manager displays various runtime libraries:

Choose Tools → Manage AVDs (Android Virtual Devices), then choose any item from Device Definitions in the resulting dialog box:

Press Create AVD, optionally modifying the name, then press OK to accept the changes:

The AVD then appears in the Android Virtual Devices list:

To open the emulator as a separate application, select the AVD and press Start. It launches much as it would on the device, with additional controls available for hardware buttons:

Deploy to Emulator

At this point you can use the cordova CLI utility to deploy the application to the emulator from the command line:

    $ cordova emulate android

Otherwise use the alternate shell interface:

    $ /path/to/project/cordova/run --emulator

Instead of relying on whichever emulator is currently enabled within the SDK, you can refer to each by the names you supply:

    $ /path/to/project/cordova/run --target=NAME

This pushes the app to the home screen and launches it:

When you run the app, you also build it. You can append additional --debug, --release, and --nobuild flags to control how it is built, or even whether a rebuild is necessary:

    $ /path/to/project/cordova/run --emulator --nobuild

If instead you are working within Eclipse, right-click the project and choose Run As → Android Application. You may be asked to specify an AVD if none are already open.

For a faster experience, you can use the Virtual Machine Acceleration to improve the execution speed. Many modern CPUs provide extensions to execute Virtual Machines more efficiently. Before attempting to use this type of acceleration, you need to determine if your current development system’s CPU, supports one the following virtualization technologies:

  • Intel Virtualization Technology (VT-x, vmx) → Intel VT-x supported processor list
  • AMD Virtualization (AMD-V, SVM), only supported for Linux (Since May 2006, all CPUs AMD include AMD-V, except Sempron).

Another way to find out if your Intel processor supports VT-x Technology, it’s by executing the Intel Processor Identification Utility, for Windowsyou can download it from the Intel Download Center, or you can use the booteable utility, which is OS Independent.

After install and execute the Intel Processor Identification Utility over Windows, you will get the following window, in order to check if your CPU supports the Virtualization Technologies:

In order to speed up the emulator, you need to download and install one or more Intel x86 Atom System Images, as well as the Intel Hardware Accelerated Execution Manager (HAXM).

Open your Android SDK Manager, and select the Intel x86 Atom System Image, for whichever version that you want to test. Then go to Extras and select Intel x86 Emulator Accelerator (HAXM), and install those packages:

After download, run the Intel installer, which is available within your Android SDK at extras/intel/Hardware_Accelerated_Execution_Manager. Note:If you have any problems installing the package, you can find more information and step by step guidance check this Intel Article.

  1. Install one or more Intel x86 Atom System Images as well as the Intel Hardware Accelerated Execution Manager, available under Extras.
  2. Run the Intel installer, which is available within your Android SDK at extras/intel/Hardware_Accelerated_Execution_Manager.
  3. Create a new AVD with the target set to an Intel image.
  4. When starting the emulator, ensure there are no error messages indicating a failure to load HAX modules.

Deploy to Device

To push an app directly to the device, make sure USB debugging is enabled on your device as described on the Android Developer Site, and use a mini USB cable to plug it into your system.

You can use this CLI command to push the app to the device:

    $ cordova run android

…or use this Android-centered shell interface:

    $ /path/to/project/cordova/run --device

With no flags specified, the run command detects a connected device, or a currently running emulator if no device is found, otherwise it prompts to specify an emulator.

To run the app from within Eclipse, right-click the project and choose Run As → Android Application.

Other Commands

The following generates a detailed log of the app as it runs:

    $ /path/to/project/cordova/log
    C:\path\to\project\cordova\log.bat

The following cleans the project files:

    $ /path/to/project/cordova/clean
    C:\path\to\project\cordova\clean.bat
发表在 android | 留下评论

android webview alert无法弹出 只能弹出一次

package com.mtjst.demo;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Toast;

public class MainActivity extends Activity {
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        webView = (WebView) findViewById(R.id.webView);

        if (Build.VERSION.SDK_INT >= 19) {
            webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        }

        webView.getSettings().setJavaScriptEnabled(true);

        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
                Toast.makeText(getBaseContext(), "hello world", Toast.LENGTH_SHORT).show();
                result.cancel();
                return true;
            }
        });

        webView.loadUrl("file:///android_asset/index.html");
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (webView.canGoBack()) {
                webView.goBack();
                return true;
            } else {
                finish();
            }
        }
        return super.onKeyDown(keyCode, event);
    }
}

 

发表在 android | 留下评论

android webview

package com.mtjst.demo;

import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;

public class MainActivity extends Activity {
    private WebView webView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        webView = (WebView) findViewById(R.id.webView);

        if (Build.VERSION.SDK_INT >= 19) {
            webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
        }

        webView.getSettings().setJavaScriptEnabled(true);

        webView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });

        webView.loadUrl("file:///android_asset/index.html");
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (webView.canGoBack()) {
                webView.goBack();
                return true;
            } else {
                finish();
            }
        }
        return super.onKeyDown(keyCode, event);
    }
}

 

发表在 android | 留下评论

Where to place Assets folder in Android Studio

Since Android Studio uses the new Gradle-based build system, you should be putting assets/inside of the source sets (e.g., src/main/assets/), if I understand correctly.

Let Android Studio do it for you.

  1. In Android Studio (1.0 & above), right-click on the enter image description here folder and navigate to the Assets Folder.

enter image description here

  1. On the next screen just click Finish.

enter image description here

And voila! It will create the assets folder in the main target source set.

enter image description here

发表在 android | 留下评论

Poor Man’s Firebase: LevelDB, REST, and WebSockets

Firebase

I wanted to build a web app that would allow data to easily be transmitted to other connected web clients. I had heard of Firebase before. So I started reading the Firebase documentation and playing around with the examples.There is a nice library that they created called AngularFire which provides some slick integration between Firebase andAngularJS. But for some reason, the provided chat example would sporadically not work. (As an aside, it seems that it would work most of the time in most of environments that I’d try, but for some reason, it rarely worked in one). So I needed to find a new solution.

LevelDB

According to the project page: “LevelDB is a fast key-value storage library written at Google that provides an ordered mapping from string keys to string values.” What’s great about LevelDB is that it’s fast and it doesn’t have any external dependencies that users need to install before they install your app.

LevelUP is the Node.js bindings built on LevelDOWN which is the low-level Node.js bindings for LevelDB.

example:

var levelup = require('levelup')

var db = levelup('./mydb.db', {valueEncoding: 'json'}) //default value encoding is 'utf8'
db.put('somekey', {name: 'some data'}, function(err) {
  db.get('somekey', function(err, data) {
    console.dir(data) //{name: 'some data'}
  })
})

As you can see, it has pretty intuitive API.

That’s not the exciting part though. What’s exciting is the ecosystem of modules and plugins built on LevelDB and LevelUP.

REST

So let’s assume that you want your database to be accessible by others. You can create a simple REST API over your LevelDB database by using multilevel-http.multilevel-http just wraps Express and adds REST routes.

npm install --save multilevel-http

example (server.js):

var levelup = require('levelup')
  , multilevelHttp = require('multilevel-http')
  , http = require('http')

var db = levelup('./mydb.db', {valueEncoding: 'json'})
var app = multilevelHttp.server(db)

var server = http.createServer(app)

server.listen(3000, function(){
  console.log('listening on port %d...', 3000)
})

you can now run:

node server.js

and access the REST interface via your browser or curl:

curl -X POST -d '{"name":"data from curl"}' -H "Content-Type:application/json" http://localhost:3000/data/somekey

retrieve the key somekey:

curl -x GET http://localhost:3000/data/somekey

serving HTML

create the following file (index.html):

<!-- 
  watch this: http://www.youtube.com/watch?v=WxmcDoAxdoY 
-->
<!doctype html>
<meta charset="utf-8">
<title>LevelDB Rules the World</title>

<h1>hi</h1>

As a quick aside, you don’t need the html, body, and head tags in HTML5. Watch this talk by Paul Irish explaining why.

let’s modify (server.js):

/* ... */

var app = multilevelHttp.server(db)

app.get('/', function(req, res) {
  res.sendfile('./index.html')
})

var server = http.createServer(app)

/* ... */

now, rerun:

node server.js

notice now that you’re redirected to /meta? This is because multilevel-http has setup this redirect. Here’s how you can fix it:

/* ... */

function removeRoute(app, method, routeMatcher) {
  var routes = app.routes[method]

  for (var i = 0; i < routes.length; ++i) {
    var route = routes[i]
    if (route.path === routeMatcher)
      break;
  }

  routes.splice(i, 1)
}

var app = multilevelHttp.server(db)

removeRoute(app, 'get', '/')
app.get('/', function(req, res) {
  res.sendfile('./index.html')
})

var server = http.createServer(app)

/* ... */

now run:

node server.js

notice now your index.html page is being served up correctly.

WebSockets

A REST API is nice, but there is still more that you’d need to do to get it working with your client-side JavaScript. Yes, you can easily interface with a REST API via AJAX calls, but let’s make things even easier and use RPC over WebSockets.

install multilevel and shoe:

npm install --save multilevel shoe

shoe requires browserify for it to run client-side:

npm install -g browserify

browserify is an awesome solution for client-side package management. Probably the best at the moment.

multilevel isn’t the same as the package above multilevel-http. This is its sexier sister. shoe is a wrapper for sockjs. It makes dealing with WebSockets more like Node.js streams.

server.js:

/*** 
  other requires
***/
var multilevel = require('multilevel')
var shoe = require('shoe')

/*** other code ***/

var wsdb = shoe(function(stream) {
  stream.pipe(multilevel.db(db)).pipe(stream)
})
wsdb.install(server, '/wsdb')

client.js:

var multilevel = require('multilevel')
var shoe = require('shoe')

var db = multilevel.client()
var stream = shoe('/wsdb')

stream.pipe(db.createRpcStream()).pipe(stream)

/****

later in the script, you use the leveldb api

e.g.: db.get, db.put, etc

*****/

(reference app.js in index.html)

browserify:

browserify client.js > app.js

run it:

node server.js

That’s it. Now client-side/browser scripts can use the levelup API.

Live Changes

Part of the utility of Firebase is that changes propagate to other connected clients. Fortunately, you can do the same with LevelDB. We’ll use another WebSocket to broadcast the changes.

install deps:

install --save event-stream level-live-stream

server.js:

/*****
  other requires
******/
var leveLiveStream = require('level-live-stream')
var es = require('event-stream')

/* ... */

var liveDBStream = levelLiveStream(db)
var changesSocket = shoe(function(stream) {
  es.pipeline(
    liveDbStream,
    es.map(function(data,next) { next(null, JSON.stringify(data)) }),
    stream
  )
})
changesSocket.install(server, '/wschanges')

/* ... */

client.js:

var changesSocket = shoe('/wschanges')
changesSocket.on('data', function(data) {
  console.dir(JSON.parse(data))
})

Chat Example

Let’s put together what we learned to create a chat example. Similar to the one found on http://angularfire.com.

Install deps:

npm init
npm install --save levelup leveldown multilevel event-stream shoe level-live-stream browserify

create server.js:

var levelup = require('levelup')
  , multilevel = require('multilevel')
  , levelLiveStream = require('level-live-stream')
  , http = require('http')
  , shoe = require('shoe')
  , fs = require('fs')
  , browserify = require('browserify')
  , es = require('event-stream')

var db = levelup('./chat.db', {valueEncoding: 'json'})
var liveDbStream = levelLiveStream(db)

var messages = {}

//load initial messages
db.get('messages', function(err, data) {
  if (err) return
  messages = data
})

liveDbStream.on('data', function(data) {
  if (data.type === 'del' && data.key === 'messages') { 
    //'clear' pressed, doesn't actually remove all of the keys, although you easily could
    messages = {}
  }

  if (data.key.indexOf('message:') >= 0) {
    var idx = data.key.split(':')[1]
    messages[idx] = '' //not sophisticated enough to handle messages generated at exact same time
    db.put('messages', messages)
  }
})

var server = http.createServer(function(req, res) {
  switch (req.url) {
    case '/': 
      fs.createReadStream('./index.html').pipe(res)
      break;
    case '/client.js':
      res.writeHead(200, {'Content-Type': 'application/javascript'})
      browserify('./client.js').bundle({debug:true}).pipe(res)
      break;
    default: 
      res.writeHead(200, {'Content-Type': 'text/plain'})
      res.end(res.url + ' not found')
  }
})

var dbSocket = shoe(function(stream) {
  stream.pipe(multilevel.server(db)).pipe(stream)
})
dbSocket.install(server, '/wsdb')

var changesSocket = shoe(function(stream) {
  es.pipeline(
    liveDbStream,
    es.map(function(data, next) { next(null, JSON.stringify(data)) }),
    stream
  )
})
changesSocket.install(server, '/wschanges')

server.listen(8000, function() {
  console.log('listening...')
})

create index.html:

<!DOCTYPE html>
<meta charset=utf-8>
<title>chat example</title>
<script src="client.js"></script>
<form>
  <input type="text" id="name" value="guest" style="width: 75px;">
  <input type="text" id="message" placeholder="type message here..." style="width: 300px;">
  <input type="submit" onclick="send(); return false;" value="send">
  <button onclick="clearMessages(); return false;">clear</button>
</form>
<hr>
<div id="messages"></div>

create client.js:

var multilevel = require('multilevel')
  , shoe = require('shoe')

var db = multilevel.client()
var dbSocket = shoe('/wsdb')
var changesSocket = shoe('/wschanges')

dbSocket.pipe(db.createRpcStream()).pipe(dbSocket)

changesSocket.on('data', function(updateData) {
  var updateData = JSON.parse(updateData)

  if (updateData.type === 'del' && updateData.key === 'messages') {
    document.getElementById('messages').innerHTML = ''
    return
  }

  if (updateData.key.indexOf('message:') >= 0) {
    appendMessage(updateData.value)
  }
})

function appendMessage(msg) {
  var p = document.createElement('p')
  var text = document.createTextNode(msg.name + ': ' + msg.message)
  p.appendChild(text)
  document.getElementById('messages').appendChild(p)
}

window.send = function() {
  var nameEl = document.getElementById('name')
  var msgEl = document.getElementById('message')
  var obj = {name: nameEl.value, message: msgEl.value}
  msgEl.value = ''
  db.put('message:' + Date.now(), obj)
}

window.onload = function() {
  var nameEl = document.getElementById('name')
  var id = Math.random().toString().substr(2,3)
  nameEl.value += id

  //get initial chat state
  db.get('messages', function(err, messages) {
    if (messages == null) return

    var ids = Object.keys(messages).slice(-15) //take last 15
    ids.forEach(function(id) {
      db.get('message:' + id, function(err, data) {
        appendMessage(data)
      })
    })
  })
}

window.clearMessages = function() {
  db.del('messages', function(err) {
    if (err) alert(err.message)
  })
}

now run:

node server.js

Boom! Now you have a hacky chat server ready to rock!

参考链接:

http://procbits.com/2014/01/06/poor-mans-firebase-leveldb-rest-and-websockets

发表在 Linux | 留下评论

/usr/bin/env: escript: No such file or directory make: *** [all] Error 127

把erlang的bin目录加入系统PATH即可

发表在 Linux | 留下评论