Sunday, 7 August 2016

lg4j – Java library for controlling LG TVs

Inspired by lgcommander, lg4j is a Java API for controlling LG TVs via the webservice interface.

Example usage

First you need find the TV’s IP address and get an authentication key by executing:

Lg4j lg4j = new Lg4j();
String ip = lg4j.discoverIpAddress();
lg4j.displayAuthenticationKey(ip);

This will display a key on the TV in the bottom right corner:

Next, use the authentication key to authenticate with the TV:

lg4j.authenticate(ip, 674689);

After authentication you can send commands to the TV, for example to turn down the TV one level:

lg4j.sendKey(ip, KeyCodes.VOLUME_DOWN);

Putting it all together:

Lg4j lg4j = new Lg4j();
String ip = lg4j.discoverIpAddress();
lg4j.displayAuthenticationKey(ip);
int session = lg4j.authenticate(ip, 674689);
lg4j.sendKey(ip, KeyCodes.VOLUME_DOWN);

Source Code

  • Code available in GitHub - lg4j

Build and Test

The project is a standard Maven project which can be built with:

mvn clean install

For the JUnit tests to pass an LG TV must be available on the network and the correct authentication key set in the unit test code. Testing was performed against a LG 42LF580V, your mileage may vary with a different model.

Saturday, 9 April 2016

SQL Graph Database Using Continued Fractions

This post is a continuation of a previous post (Continued Fraction Database File System) which used a SQL RDBMS to implement a file system tree using continued fractions. If you want to understand the maths behind the project, read that previous post first and the paper by Dan Hazel which it is based on, Using rational numbers to key nested sets.

In the previous post, each node had one path, and so could only represent trees. In a graph, each node can have multiple paths. Each path can be represented by a list of integers, where each integer is the index of the child beneath it's parent node, for example:

The integer list paths can be used in a continued fraction to calculate a real number value to be used as the primary key for a node path relation. So our database schema looks like this:

This model could be extended with a properties relation to store multiple key-value pairs for each node and path.

The JPA entities for the node and path relations are:

Node.java

package org.adrianwalker.continuedfractions.graph.entity;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import javax.persistence.Basic;
import static javax.persistence.CascadeType.ALL;
import javax.persistence.Column;
import javax.persistence.Entity;
import static javax.persistence.FetchType.LAZY;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "node")
@NamedQueries({
  @NamedQuery(name = "tree",
      query = "SELECT DISTINCT n2 "
      + "FROM Node n1, Node n2, NodePath np1, NodePath np2 "
      + "WHERE n1.id = np1.node.id "
      + "AND (np2.id >= np1.id AND np2.id < np1.sid) "
      + "AND n2.id = np2.node.id "
      + "AND n1.id = :id "
      + "ORDER BY n2.id"
  ),
  @NamedQuery(name = "parents",
      query = "SELECT DISTINCT n2 "
      + "FROM Node n1, Node n2, NodePath np1, NodePath np2 "
      + "WHERE n1.id = np1.node.id "
      + "AND n2.id = np2.node.id "
      + "AND (np1.id > np2.id AND np1.id < np2.sid) "
      + "AND np2.hops = np1.hops - 1 "
      + "AND n1.id = :id "
      + "ORDER BY np2.id"
  ),
  @NamedQuery(name = "children",
      query = "SELECT DISTINCT n2 "
      + "FROM Node n1, Node n2, NodePath np1, NodePath np2 "
      + "WHERE n1.id = np1.node.id "
      + "AND n2.id = np2.node.id "
      + "AND (np2.id > np1.id AND np2.id < np1.sid) "
      + "AND np2.hops = np1.hops + 1 "
      + "AND n1.id = :id "
      + "ORDER BY np2.id"
  )})
public class Node implements Serializable {

  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = IDENTITY)
  @Basic(optional = false)
  @Column(name = "id", nullable = false)
  private Long id;

  @Basic(optional = false)
  @Column(name = "name", nullable = false)
  private String name;

  @OneToMany(fetch = LAZY, cascade = ALL, orphanRemoval = true, mappedBy = "node")
  private List<NodePath> nodePaths;

  public Node() {
  }

  public Long getId() {
    return id;
  }

  public void setId(final Long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(final String name) {
    this.name = name;
  }

  public List<NodePath> getNodePaths() {

    if (null == nodePaths) {
      nodePaths = new ArrayList<>();
    }

    return nodePaths;
  }

  public void setNodePaths(final List<NodePath> nodePaths) {
    this.nodePaths = nodePaths;
  }

  @Override
  public int hashCode() {

    int hash = 5;
    hash = 89 * hash + Objects.hashCode(this.id);

    return hash;
  }

  @Override
  public boolean equals(final Object obj) {

    if (this == obj) {
      return true;
    }

    if (obj == null) {
      return false;
    }

    if (getClass() != obj.getClass()) {
      return false;
    }

    return Objects.equals(this.id, ((Node) obj).id);
  }

  @Override
  public String toString() {
    return "Node{" + "id=" + id + ", name=" + name + '}';
  }
}

NodePath.java

package org.adrianwalker.continuedfractions.graph.entity;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Objects;
import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Index;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.adrianwalker.continuedfractions.Fraction;

@Entity
@Table(name = "node_path",
    uniqueConstraints = {
      @UniqueConstraint(columnNames = {"id", "sid"})
    },
    indexes = {
      @Index(columnList = "hops", unique = false)
    })
public class NodePath implements Serializable {

  private static final long serialVersionUID = 1L;

  @Id
  @Basic(optional = false)
  @Column(name = "id", nullable = false, precision = (Fraction.SCALE * 2) - 1, scale = Fraction.SCALE)
  private BigDecimal id;

  @Basic(optional = false)
  @Column(name = "sid", nullable = false, precision = (Fraction.SCALE * 2) - 1, scale = Fraction.SCALE)
  private BigDecimal sid;

  @Basic(optional = false)
  @Column(name = "hops", nullable = false)
  private Integer hops;

  @ManyToOne(optional = false)
  @JoinColumn(name = "node_id", referencedColumnName = "id", nullable = false)
  private Node node;

  public NodePath() {
  }

  public BigDecimal getId() {
    return id;
  }

  public void setId(final BigDecimal id) {
    this.id = id;
  }

  public BigDecimal getSid() {
    return sid;
  }

  public void setSid(final BigDecimal sid) {
    this.sid = sid;
  }

  public Integer getHops() {
    return hops;
  }

  public void setHops(final Integer hops) {
    this.hops = hops;
  }

  public Node getNode() {
    return node;
  }

  public void setNode(final Node node) {
    this.node = node;
  }

  @Override
  public int hashCode() {

    int hash = 3;
    hash = 41 * hash + Objects.hashCode(this.id);

    return hash;
  }

  @Override
  public boolean equals(final Object obj) {

    if (this == obj) {
      return true;
    }

    if (obj == null) {
      return false;
    }

    if (getClass() != obj.getClass()) {
      return false;
    }

    return Objects.equals(this.id, ((NodePath) obj).id);
  }

  @Override
  public String toString() {
    return "NodePath{" + "id=" + id + ", sid=" + sid + ", hops=" + hops + '}';
  }
And a simple JPA controller:

Controller.java

package org.adrianwalker.continuedfractions.graph.controller;

import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Query;
import org.adrianwalker.continuedfractions.graph.entity.Node;
import org.adrianwalker.continuedfractions.graph.entity.NodePath;

public final class Controller {

  private final EntityManagerFactory emf;

  public Controller(final EntityManagerFactory emf) {
    this.emf = emf;
  }

  public EntityManager getEntityManager() {
    return emf.createEntityManager();
  }

  public Node create(final Node node) throws Exception {

    EntityManager em = getEntityManager();

    try {
      begin(em);
      em.persist(node);
      end(em);
    } finally {
      em.close();
    }

    return node;
  }

  public NodePath create(final NodePath nodePath) throws Exception {

    EntityManager em = getEntityManager();

    try {
      begin(em);
      em.persist(nodePath);
      end(em);
    } finally {
      em.close();
    }

    return nodePath;
  }

  public List<Node> tree(final Node node) {

    EntityManager em = getEntityManager();
    Query tree = em.createNamedQuery("tree");
    tree.setParameter("id", node.getId());

    try {
      return tree.getResultList();
    } finally {
      em.close();
    }
  }

  public List<Node> parents(final Node node) {

    EntityManager em = getEntityManager();
    Query children = em.createNamedQuery("parents");
    children.setParameter("id", node.getId());

    try {
      return children.getResultList();
    } finally {
      em.close();
    }
  }

  public List<Node> children(final Node node) {

    EntityManager em = getEntityManager();
    Query children = em.createNamedQuery("children");
    children.setParameter("id", node.getId());

    try {
      return children.getResultList();
    } finally {
      em.close();
    }
  }

  private void begin(final EntityManager em) {
    em.getTransaction().begin();
  }

  private void end(final EntityManager em) {
    em.getTransaction().commit();
  }
}

The Graph class calls the controller to persist Node and NodePath entities. It has methods to add nodes and node paths, to list all the nodes beneath a given node, and methods to get the immediate parents and children of a given node:

Graph.java

package org.adrianwalker.continuedfractions.graph;

import java.math.BigDecimal;
import java.util.List;
import static org.adrianwalker.continuedfractions.Fraction.decimal;
import org.adrianwalker.continuedfractions.graph.controller.Controller;
import org.adrianwalker.continuedfractions.graph.entity.Node;
import org.adrianwalker.continuedfractions.graph.entity.NodePath;
import static org.adrianwalker.continuedfractions.graph.Path.sibling;
import static org.adrianwalker.continuedfractions.Fraction.fraction;

public final class Graph {

  private final Controller controller;

  public Graph(final Controller controller) {

    this.controller = controller;
  }

  public Node addNode(final String name) throws Exception {

    Node node = new Node();
    node.setName(name);

    return controller.create(node);
  }

  public NodePath addPath(final Node node, final int... path) throws Exception {

    NodePath nodePath = new NodePath();
    int[] nvDv = fraction(path);
    BigDecimal id = decimal(nvDv);
    int[] snvSdv = fraction(sibling(path));
    BigDecimal sid = decimal(snvSdv);

    nodePath.setId(id);
    nodePath.setSid(sid);
    nodePath.setHops(path.length);
    nodePath.setNode(node);

    return controller.create(nodePath);
  }

  public List<Node> tree(final Node node) {

    return controller.tree(node);
  }

  public List<Node> parents(final Node node) {

    return controller.parents(node);
  }

  public List<Node> children(final Node node) {

    return controller.children(node);
  }
}

Below is a unit test example of how to use the Graph class to create the graph in the image above:

GraphTest.java

package org.adrianwalker.continuedfractions.graph;

import java.util.Arrays;
import static java.util.stream.Collectors.toList;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import org.adrianwalker.continuedfractions.graph.controller.Controller;
import org.adrianwalker.continuedfractions.graph.entity.Node;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

public class GraphTest {

  private static EntityManagerFactory emf;

  public GraphTest() {
  }

  @BeforeClass
  public static void setUpClass() {
    emf = Persistence.createEntityManagerFactory("graph");
  }

  @AfterClass
  public static void tearDownClass() {
    emf.close();
  }

  @Before
  public void setUp() {
  }

  @After
  public void tearDown() {
  }

  /*
      A
     /|\
    B | C
     \|/
      D
      |
      E
   */
  @Test
  public void testGraph() throws Exception {

    Controller nc = new Controller(emf);
    Graph hierarchy = new Graph(nc);

    Node a = hierarchy.addNode("A");
    Node b = hierarchy.addNode("B");
    Node c = hierarchy.addNode("C");
    Node d = hierarchy.addNode("D");
    Node e = hierarchy.addNode("E");

    hierarchy.addPath(a, 1);
    hierarchy.addPath(b, 1, 1);
    hierarchy.addPath(c, 1, 2);
    hierarchy.addPath(d, 1, 3);
    hierarchy.addPath(d, 1, 1, 1);
    hierarchy.addPath(d, 1, 2, 1);
    hierarchy.addPath(e, 1, 1, 1, 1);
    hierarchy.addPath(e, 1, 2, 1, 1);
    hierarchy.addPath(e, 1, 3, 1);

    // trees
    Assert.assertEquals(Arrays.asList(new String[]{"A", "B", "C", "D", "E"}),
        hierarchy.tree(a).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"B", "D", "E"}),
        hierarchy.tree(b).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"C", "D", "E"}),
        hierarchy.tree(c).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"D", "E"}),
        hierarchy.tree(d).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"E"}),
        hierarchy.tree(e).stream()
        .map(node -> node.getName())
        .collect(toList()));

    // children
    Assert.assertEquals(Arrays.asList(new String[]{"B", "C", "D"}),
        hierarchy.children(a).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"D"}),
        hierarchy.children(b).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"D"}),
        hierarchy.children(c).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"E"}),
        hierarchy.children(d).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{}),
        hierarchy.children(e).stream()
        .map(node -> node.getName())
        .collect(toList()));

    // parents
    Assert.assertEquals(Arrays.asList(new String[]{}),
        hierarchy.parents(a).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"A"}),
        hierarchy.parents(b).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"A"}),
        hierarchy.parents(c).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"A", "B", "C"}),
        hierarchy.parents(d).stream()
        .map(node -> node.getName())
        .collect(toList()));

    Assert.assertEquals(Arrays.asList(new String[]{"D"}),
        hierarchy.parents(e).stream()
        .map(node -> node.getName())
        .collect(toList()));
  }
}

Source Code

ANTLR Dynamic Runtime Tokens and Rules

ANTLR lexer tokens and parser rules are normally coded into the grammar and not modifiable during the codes execution, but I need to add lexer rule tokens and enable or disable parser rules at runtime. So here's an example of how you might want to do that.

First, two classes to hold lexer tokens and the enabled/disabled status of parser rules:

LexerLookup.java

package org.adrianwalker.antlr.dynamicrules;

import static java.lang.String.format;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.logging.Logger;
import static org.adrianwalker.antlr.dynamicrules.DynamicRulesParser.ruleNames;
import org.antlr.v4.runtime.CharStream;

public enum LexerLookup {

  INSTANCE;

  private static final Logger LOGGER = Logger.getLogger(LexerLookup.class.getName());
  private static final Comparator<String> LONGEST_FIRST = (s1, s2) -> s2.length() - s1.length();

  private final Map<Integer, Set<String>> tokenIdTermsMap;

  private LexerLookup() {
    tokenIdTermsMap = new HashMap<>();
  }

  public void put(final int tokenId, final List<String> tokens) {

    if (null == tokens) {
      throw new IllegalArgumentException("Illegal argument, tokens must be not null");
    }

    tokens.removeIf(Objects::isNull);

    Collections.sort(tokens, LONGEST_FIRST);

    LinkedHashSet tokenSet = new LinkedHashSet(tokens);

    LOGGER.info(format("tokens '%s' %s\n", ruleNames[tokenId - 1], tokenSet));

    this.tokenIdTermsMap.put(tokenId, tokenSet);
  }

  public boolean contains(final int tokenId, final CharStream input) {

    boolean contains = false;

    if (!tokenIdTermsMap.containsKey(tokenId)) {
      return contains;
    }

    Set<String> terms = tokenIdTermsMap.get(tokenId);

    for (String term : terms) {

      contains = ahead(term, input);

      if (contains) {
        LOGGER.info(format("contains '%s' ('%s')\n", term, ruleNames[tokenId - 1]));
        break;
      }
    }

    return contains;
  }

  private boolean ahead(final String word, final CharStream input) {

    for (int i = 0; i < word.length(); i++) {

      char wordChar = word.charAt(i);
      int inputChar = input.LA(i + 1);

      if (inputChar != wordChar) {
        return false;
      }
    }

    input.seek(input.index() + word.length() - 1);

    return true;
  }
}

ParserLookup.java

package org.adrianwalker.antlr.dynamicrules;

import static java.lang.String.format;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

public enum ParserLookup {

  INSTANCE;

  private static final Logger LOGGER = Logger.getLogger(ParserLookup.class.getName());
  private final Map<Integer, Boolean> ruleIdEnabledMap;

  private ParserLookup() {
    ruleIdEnabledMap = new HashMap<>();
  }

  public void put(final int ruleId, final boolean enabled) {

    LOGGER.info(format("ruleId = %s, enabled = %s\n", ruleId, enabled));

    this.ruleIdEnabledMap.put(ruleId, enabled);
  }

  public boolean enabled(final int ruleId) {

    return ruleIdEnabledMap.getOrDefault(ruleId, true);
  }
}
These two classes are used by the grammar to assign values to lexer rules and to enable or disable parser rules like this:

DynamicRules.g4

grammar DynamicRules;

@lexer::header {
  import org.adrianwalker.antlr.dynamicrules.LexerLookup;
}

@lexer::members {
  public static final LexerLookup LOOKUP = LexerLookup.INSTANCE;
}

@parser::header {
  import org.adrianwalker.antlr.dynamicrules.ParserLookup;
}

@parser::members {
  public static final ParserLookup LOOKUP = ParserLookup.INSTANCE;
}

// Parser Rules

sentence : ({LOOKUP.enabled(RULE_words)}? words) FULL_STOP ;
words : WORD (WS WORD)+ ;

// Lexer Rules

WORD : {LOOKUP.contains(WORD, _input)}? . ;
FULL_STOP : '.' ;
WS : [ \t\r\n]+ ;
OTHER : . ;

The parser and lexer generated from the ANLTR grammer can be used with the lexer and parser lookup classes to set token values and enable and disable rules:

SentenceParser.java

package org.adrianwalker.antlr.dynamicrules;

import java.util.List;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.RecognitionException;

public final class SentenceParser {

  public SentenceParser() {
  }

  public void setWords(final List<String> words) {
    LexerLookup.INSTANCE.put(DynamicRulesLexer.WORD, words);
  }

  public void enableWords(final boolean enabled) {
    ParserLookup.INSTANCE.put(DynamicRulesParser.RULE_words, enabled);
  }

  public Result parse(final String term) throws RecognitionException {

    DynamicRulesLexer lexer = new DynamicRulesLexer(new ANTLRInputStream(term));
    CommonTokenStream tokens = new CommonTokenStream(lexer);
    DynamicRulesParser parser = new DynamicRulesParser(tokens);

    return new Result(parser.sentence().getText(), parser.getNumberOfSyntaxErrors());
  }

  public static final class Result {

    private String text;
    private int numberOfSyntaxErrors;

    public Result(final String text, final int numberOfSyntaxErrors) {
      this.text = text;
      this.numberOfSyntaxErrors = numberOfSyntaxErrors;
    }

    public String getText() {
      return text;
    }

    public void setText(final String text) {
      this.text = text;
    }

    public int getNumberOfSyntaxErrors() {
      return numberOfSyntaxErrors;
    }

    public void setNumberOfSyntaxErrors(final int numberOfSyntaxErrors) {
      this.numberOfSyntaxErrors = numberOfSyntaxErrors;
    }
  }
}

Some unit tests for example usage:

SentenceParserTest.java

package org.adrianwalker.antlr.dynamicrules;

import static java.util.Arrays.asList;
import org.adrianwalker.antlr.dynamicrules.SentenceParser.Result;
import org.junit.Assert;
import org.junit.Test;

public class SentenceParserTest {

  @Test
  public void testValid() {

    SentenceParser parser = new SentenceParser();
    parser.enableWords(true);
    parser.setWords(asList(new String[]{
      "on", "cat", "mat", "sat", "the"
    }));

    Result result = parser.parse("the cat sat on the mat.");

    Assert.assertEquals("the cat sat on the mat.", result.getText());
    Assert.assertEquals(0, result.getNumberOfSyntaxErrors());
  }

  @Test
  public void testDisabledRule() {

    SentenceParser parser = new SentenceParser();
    parser.enableWords(false);
    parser.setWords(asList(new String[]{
      "on", "cat", "mat", "sat", "the"
    }));

    Result result = parser.parse("the cat sat on the mat.");

    Assert.assertEquals(1, result.getNumberOfSyntaxErrors());
  }

  @Test
  public void testInvalidWords() {

    SentenceParser parser = new SentenceParser();
    parser.enableWords(false);
    parser.setWords(asList(new String[]{
      "on", "cat", "mat", "sat", "the"
    }));

    Result result = parser.parse("INVALID");

    Assert.assertEquals(1, result.getNumberOfSyntaxErrors());
  }

  @Test
  public void testUpdateWordsAndDisableRule() {

    SentenceParser parser = new SentenceParser();
    parser.enableWords(true);
    parser.setWords(asList(new String[]{
      "the"
    }));

    Result result = parser.parse("the cat sat on the mat.");

    Assert.assertEquals(1, result.getNumberOfSyntaxErrors());

    parser.setWords(asList(new String[]{
      "on", "cat", "mat", "sat", "the"
    }));

    result = parser.parse("the cat sat on the mat.");

    Assert.assertEquals(0, result.getNumberOfSyntaxErrors());

    parser.enableWords(false);

    result = parser.parse("the cat sat on the mat.");

    Assert.assertEquals(1, result.getNumberOfSyntaxErrors());
  }
}

Source Code

Getting Started With AngularJS

I've only done a couple of projects with AngularJS, but the hardest part of using Angular each time has been just getting up and running. After that it's been pretty plain sailing. So here is a starting point to get me (and you?) hitting the ground running next time.

The project uses ngRoute and ngResource to create a single page app to call a REST web service (http://jsonplaceholder.typicode.com).

All the JavaScript dependencies are hosted on CloudFlair so there is nothing extra to download.

To turn this into a real project, you're going to ant to refactor the code a little, pull controllers and and services into their own files etc, but like I said, the code is just a starting point that works.

angularjs-demo.html

<!DOCTYPE html>
<html>
  <head>
    <title>Angular JS Demo</title>

    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.css" rel="stylesheet">

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.3/jquery.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.3/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.3/angular-route.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-resource/1.5.3/angular-resource.js"></script>

    <script src="angularjs-demo.js"></script>
  </head>

  <body ng-app="angularjs-demo">
    <div class="collapse navbar-collapse" role="navigation">
      <ul class="nav navbar-nav">
        <li><a href="#/posts">Posts</a></li>
      </ul>
    </div>

    <div class="container">
      <div class="panel panel-default">
        <div class="panel-body">
          <div ng-view></div>
        </div>
      </div>
    </div>
  </body>
</html>

posts.html

<!DOCTYPE html>
<html>
  <head>
    <title>Angular JS Demo - Posts</title>
  </head>
  <body>
    <div>
      <h4>Get Post by ID</h4>
      Post ID: <input type="text" ng-model="id" />
      <button ng-click="query(id)" type="button">Get Post</button>
    </div>
    <div>
      <h4>Post JSON</h4>
      <pre>{{postsResponse| json}}</pre>
    </div>
  </body>
</html>

angularjs-demo.js

angular
  .module('angularjs-demo', [
    'ngRoute',
    'ngResource'
  ])
  .config(function ($routeProvider) {
    $routeProvider
      .when('/posts', {
        templateUrl: 'posts.html',
        controller: 'PostsCtrl'
      })
      .otherwise({
        redirectTo: '/posts'
      });
  })
  .factory('Posts', function ($resource) {

    var POSTS_URL = 'http://jsonplaceholder.typicode.com/posts/:id';

    return $resource(POSTS_URL, {},
      {get: {method: 'GET'}}
    );
  })
  .controller('PostsCtrl', function ($scope, $log, Posts) {

    function query(id) {
      return Posts.get({id: id},
      function (data) {
        return data;
      }, function (err) {
        $log.error(JSON.stringify(err));
      });
    }

    $scope.query = function (id) {
      $log.info("id = " + id);
      $scope.postsResponse = query(id);
    };
  });

Source Code

Monday, 17 August 2015

The short life and fast times of a beekeeper

A couple of years ago I decided I wanted to try beekeeping - nothing big - just a little hive in the back garden. I wasn't bothered about harvesting honey, or making stuff from wax, it was just that I read so much about bee populations in decline and I thought I could build a hive and give some bee's a place to stay, you know, do my bit to help them along and in return they could pollinate my strawberries.

Also I find bees pretty fascinating – they are the ultimate in natural decentralized decision making. They are like a distributed computing system made up of thousands of small multi-purpose nodes which on their own can't achieve much, but when they communicate and work together amazing things can be made possible.

So I downloaded these top-bar hive plans and got to work:  http://www.biobees.com/build-a-beehive-free-plans.php

Newly built hive, not quite finished

After the hive was done I read everything I could get my hands on about bees, and specifically how to attract them. And then waited. And read. And waited. And waited and read.

Hive today, completed with felt roof, stained wood, bottom board and entrance holes.

For some reason I thought it would be like 'Field Of Bee Dreams' – build it and they will come. They fucking didn't.

This year I did the same as the previous two spring/summers to try and attract a swarm. Starting in May, every couple of weeks:

  • rubbed the inside of the hive and entrance to the hive with pure bee's wax
  • scattered drops of lemon grass inside the hive and on the tops of the top bars
  • blobbed some honey inside the hive, on the bottom board, to try and drum up some interest

When it got to the start of July I thought that was it for attracting a swarm for this year, and thought I should probably save up and just buy a nuc next year. Then in the second week of July a swarm moved in.

I was over the moon!

I thought they might be hungry – but didn't want to disturb them just yet, so I made a jar feeder with 1:1 sugar water and hung it on the end of the hive.

After a week the bees had shown absolutely no interest in the sugar water, but they seemed busy going back and forth – in and out of the hive. I decided to open up the hive and see what I had.

I had attracted the smallest swarm of bees ever. They were in a cluster on the front inside wall of the hive, above the entrance holes, in a ball slightly larger than a tennis ball. Aren't swarms meat to be an amazing sight to behold, comprised of thousands of bees all moving into a new home?

They had made no attempt to start building comb either and seemed to be happy just sat there in a cluster. I guess with the swarm being attracted so late in the season and it being so small that it must be a cast. Since my new colony was so tiny and they didn't want sugar water, I ordered some Ambrosia Fondant from a near by bee keeping supplier: http://www.abelo.co.uk/shop/apifonda-2-5kg/

Ambrosia feed paste

When the fondant arrived, I did another hive inspection. I was expecting to see some comb drawn as it had been two weeks since they had moved in and swarms are meant to be notorious for being fast builders I thought?

They had built a couple of inches square of comb on one bar. This was turning out to be nothing like what I had read about swarms.

I figure because of the bad weather and lack of comb for honey stores, they really must be hungry now. I cut some holes in the bag of fondant, hung it from one of the top bars, and placed it right next the cluster so the bees didn't have to go too far and get too cold to reach it.

Because I'd opened the hive twice in two weeks, I decided I should probably leave the bees alone for a while now that they have started building comb and have a massive bag of food if they need it.

Fast-forward two weeks – For two days now there has been been a massive drop off in hive activity. I might see a bee going in or out of the hive every ten minutes. But then again, why would they need to be out and about? Maybe they are keeping warm, eating the fondant, building comb and just popping out now and again to get water and pollen, surely they have everything they need to get on with comb building and brood rearing right?

I thought I'd best check.

I opened the hive today to find about a dozen worker bees in there - that is all.

They had eaten some fondant, and built a tiny bit more comb, and even tried to rear some brood - half of which is black and dead in the cells.

First comb with dead brood, and what looks like some attempts at queen cells.

Second and final comb, barely begun.

Oddly there are no dead bees on the floor of the hive, it just looks abandoned, apart from the dozen or so which were left behind.

I don't know what went wrong really, I know the swarm was weak to start with, but I thought with some attention and care I would be able to help them become stronger and even get them through the winter.

It's all so disappointing. I only got to spend one month as a beekeeper, I guess I'll clean out the hive when the last bee leaves and try to do a better job next year.

Tuesday, 10 March 2015

Desktop Upgrade – Part 3

With my hardware tested and running well at stock speeds in part 1 and part 2, it's time to try for an overclock.

The Asrock Z97 motherboard detects the G3258 on boot up and prompts you to press the P key to enable 'Pentium Anniversary Boot'. Really this is just some pre-configured settings to get you started overclocking. Pressing P will gives you the following screen with some clock speeds to choose from:

I like big boosts and I can not lie

Obviously, when presented with this screen, you're just going to nail it straight on to the 4.2GHz option and see what happens.

Testing

I fired Prime95 up again with the same 'Small FFTs' torture as in part 2 and monitored temperatures using the Linux command 'sensors'. The temperatures reported were about 20°C hotter than before, but held absolutely solidy at the 60°C mark for two hours:

coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +60.0°C  (high = +80.0°C, crit = +100.0°C)
Core 0:         +60.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:         +52.0°C  (high = +80.0°C, crit = +100.0°C)

+1 hour

coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +60.0°C  (high = +80.0°C, crit = +100.0°C)
Core 0:         +60.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:         +51.0°C  (high = +80.0°C, crit = +100.0°C)

+2 hours

coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +60.0°C  (high = +80.0°C, crit = +100.0°C)
Core 0:         +60.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:         +51.0°C  (high = +80.0°C, crit = +100.0°C)

No errors or warning were reported during the tests:

[Worker #2 Mar 8 22:03] Worker stopped.
[Worker #1 Mar 8 22:03] Torture Test completed 177 tests in 1 hour, 43 minutes - 0 errors, 0 warnings.
[Worker #1 Mar 8 22:03] Worker stopped.
[Main thread Mar 8 22:03] Execution halted.

Overclocked Benchmark

I benchmarked the new setup at 4.2GHz using the same tools and tests as used in part 1 and part 2.

I ran GeekBench three times to make sure the results were consistent:

RunSingle-Core ScoreMulti-Core ScoreFull Results
1st35956562http://browser.primatelabs.com/geekbench3/2054643
2nd36006567http://browser.primatelabs.com/geekbench3/2054658
3rd35916558http://browser.primatelabs.com/geekbench3/2048367

Disappointingly the GPU benchmarks with the 4.2GHz 'Pentium Anniversary Boot' configuration came out exactly the same as at stock speeds in part 2, so I wont include them here. I suspect some custom settings tuning may yield better results.

Compared to the benchmark from part 1, my overclocked system is almost three times as fast on single core performance and twice as fast using multiple cores:

TestNumber of times faster
Single-Core Score2.8x
Multi-Core Score2.0x

Tweaking

The BIOS comes with loads of options for OC tweaking:

Tweak my MIPS harder

The core voltage looks a little high on the pre-configured overclock settings, so I've reduced that slightly, and I need to optimize settings for GPU and RAM, but for now I'm happy with my 4.2GHz clock.

There are some good tutorials out there for OCing this kind of rig. In the future I'll give a this guide a go and post the results here.

Desktop Upgrade – Part 2

My new hardware came and it looks a bit like this:

Looking sexy

First things first - time to rag out my old kit and clean out seven years of accumulated dust.

Before: I wish my girlfriend was this dirty (that's not really my PC).

After: Loads cleaner than your mucky mother.

Brilliant, time to put it all together, that heat sink is an absolute beast and it was a bit of a sod to bolt to the motherboard. The Asrock Z97 Anniversary motherboard is only about 2/3 the size of my old Asus so there is a bit more room for messing about with the drives.

Assembled: The heatsink fits in the Cooler Master Centurion C5 case no problem if you remove the pipe attached to the case side.

After a fresh install of Linux Mint 17.1 with the Xfce desktop, we're ready to rock. The Linux 'sensors' command show that things are running pretty cool at stock speeds:

coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +27.0°C  (high = +80.0°C, crit = +100.0°C)
Core 0:         +27.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:         +26.0°C  (high = +80.0°C, crit = +100.0°C)

Testing

Before attempting any overclocking I wanted to make sure the new hardware was stable enough and cool enough with the out of the box configuration.

First I used Memtest86 to make sure that there were no faults with the memory, downloadable here:
http://www.memtest86.com/download.htm

Faultless

Memtest86 took about half an hour to run on 8GB RAM, and reported no errors after one pass. I couldn't really be bothered running it for more time - I was going to overclock it whatever the result.

Booting back into Linux, I installed Prime95 and ran the 'Small FTTs' torture test to bring the CPU up to full utilization. Prime95 is downloadable from here:
http://www.mersenne.org/download/

The 'sensors' command showed the temperature immediately go up to the low 40°C range, still pretty cool, and increase slow by about 1°C per hour:

coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +42.0°C  (high = +80.0°C, crit = +100.0°C)
Core 0:         +42.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:         +38.0°C  (high = +80.0°C, crit = +100.0°C)

+1 hour

coretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +43.0°C  (high = +80.0°C, crit = +100.0°C)
Core 0:         +43.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:         +39.0°C  (high = +80.0°C, crit = +100.0°C)

+2 hours

ccoretemp-isa-0000
Adapter: ISA adapter
Physical id 0:  +44.0°C  (high = +80.0°C, crit = +100.0°C)
Core 0:         +44.0°C  (high = +80.0°C, crit = +100.0°C)
Core 1:         +40.0°C  (high = +80.0°C, crit = +100.0°C)

No errors or warning were reported during the tests:

[Worker #2 Mar 7 21:21] Torture Test completed 182 tests in 2 hours, 23 minutes - 0 errors, 0 warnings.
[Worker #2 Mar 7 21:21] Worker stopped.
[Worker #1 Mar 7 21:21] Torture Test completed 181 tests in 2 hours, 23 minutes - 0 errors, 0 warnings.
[Worker #1 Mar 7 21:21] Worker stopped.
[Main thread Mar 7 21:21] Execution halted.

Stock Benchmark

I benchmarked the new setup at stock speeds using the same tools and tests as used in part 1.

I ran GeekBench three times to make sure the results were consistent:

RunSingle-Core ScoreMulti-Core ScoreFull Results
1st28715180http://browser.primatelabs.com/geekbench3/2048335
2nd28695178http://browser.primatelabs.com/geekbench3/2048351
3rd28695178http://browser.primatelabs.com/geekbench3/2048367

I ran each of the GpuTest fullscreen benchmark scripts, as before:

ModulePointsFPS
Triangle39818663
Plot3D371861
FurMark3946
PixMark Volplosion1762

Compared to the benchmark from part 1, my new system is roughly twice as fast:

TestNumber of times faster
Single-Core Score2.2x
Multi-Core Score1.6x
Triangle2.0x
Plot3D1.7x
FurMark2.8x
PixMark Volplosion3.2x

Part 3

Desktop Upgrade – Part 3