diff --git a/pom.xml b/pom.xml
index 6803f45..b5e1631 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
weather
2008
- 2.7.3
+ 2.9.3
@@ -35,13 +35,13 @@
net.liftweb
- lift-util
- 1.0
+ lift-util_2.9.1
+ 2.5.1
net.liftweb
- lift-webkit
- 1.0
+ lift-webkit_2.9.1
+ 2.5.1
javax.servlet
@@ -69,9 +69,9 @@
test
- de.huxhorn.lilith
- de.huxhorn.lilith.3rdparty.rrd4j
- 2.0.5
+ org.rrd4j
+ rrd4j
+ 2.2
@@ -123,6 +123,11 @@
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ 2.1.1
+
diff --git a/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala b/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala
index 4ac86dc..fe3b3be 100644
--- a/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala
+++ b/src/main/scala/uk/org/floop/msc/comet/WeatherActor.scala
@@ -1,13 +1,12 @@
package uk.org.floop.msc.comet
import scala.collection.mutable.HashMap
-
import net.liftweb.http._
import net.liftweb.http.js.JsCmds._
import net.liftweb.util._
-
import uk.org.floop.msc.rrd._
import uk.org.floop.msc.wview.Forecast
+import net.liftweb.common.Full
class WeatherActor extends CometActor {
diff --git a/src/main/scala/uk/org/floop/msc/rest/Dump.scala b/src/main/scala/uk/org/floop/msc/rest/Dump.scala
index 50e5fe3..1923576 100644
--- a/src/main/scala/uk/org/floop/msc/rest/Dump.scala
+++ b/src/main/scala/uk/org/floop/msc/rest/Dump.scala
@@ -2,8 +2,9 @@
import net.liftweb.http._
import net.liftweb.util._
-
import uk.org.floop.msc.rrd._
+import net.liftweb.common.Box
+import net.liftweb.common.Empty
object Dump {
diff --git a/src/main/scala/uk/org/floop/msc/rest/Graph.scala b/src/main/scala/uk/org/floop/msc/rest/Graph.scala
index e4261a3..8566cd4 100644
--- a/src/main/scala/uk/org/floop/msc/rest/Graph.scala
+++ b/src/main/scala/uk/org/floop/msc/rest/Graph.scala
@@ -2,9 +2,10 @@
import net.liftweb.http._
import net.liftweb.util._
-
-
import uk.org.floop.msc.rrd._
+import net.liftweb.common.Box
+import net.liftweb.common.Full
+import net.liftweb.common.Empty
object Graph {
diff --git a/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala b/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala
index eaf485d..3ec545c 100644
--- a/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala
+++ b/src/main/scala/uk/org/floop/msc/rrd/DataStore.scala
@@ -3,7 +3,6 @@
import scala.actors.Actor
import scala.actors.Actor._
import scala.collection.mutable.ListBuffer
-
import java.io.{File, IOException, ByteArrayOutputStream, InputStream}
import java.util.{Date, Calendar}
import java.awt.Color
@@ -11,13 +10,14 @@
import javax.imageio.stream.MemoryCacheImageOutputStream
import javax.imageio.ImageIO
import _root_.org.xml.sax.InputSource
-
import _root_.org.rrd4j.core.{RrdDef, RrdDb}
import _root_.org.rrd4j.{DsType, ConsolFun}
import _root_.org.rrd4j.graph.{RrdGraphDef, RrdGraph, RrdGraphDefTemplate}
-
import net.liftweb.http.LiftRules
-import net.liftweb.util.{Full, Empty, Box}
+import net.liftweb.common.Full
+import net.liftweb.common.Empty
+import net.liftweb.http.CometActor
+
object TimePeriod extends Enumeration {
val HOUR, DAY, WEEK, MONTH, YEAR = Value
@@ -37,8 +37,8 @@
case class StorePacket(p: List[Pair[String, Any]])
case class GenerateGraph(template: String, p: TimePeriod.Value)
case class ImageGenerated(bytes: Array[Byte])
-case class AddWeatherListener(l: Actor)
-case class RemoveWeatherListener(l: Actor)
+case class AddWeatherListener(l: CometActor)
+case class RemoveWeatherListener(l: CometActor)
case class CurrentWeather(w: List[Pair[String, Any]])
case class DumpXml()
@@ -90,7 +90,7 @@
}
var currentWeather: List[Pair[String, Any]] = Nil
- val listeners = new ListBuffer[Actor]
+ val listeners = new ListBuffer[CometActor]
def act() {
loop {
@@ -130,30 +130,18 @@
}
updateListeners
case GenerateGraph(t, p) =>
- val gd = LiftRules.getResourceAsStream(t) match {
- case Full(inputStream) =>
- try {
- val templ = new RrdGraphDefTemplate(new InputSource(inputStream))
- templ.setVariable("rrdFile", STORE.getPath)
- Some(templ.getRrdGraphDef())
- } catch {
- case ex: IOException =>
- println(ex)
- None
- case ex: IllegalArgumentException =>
- println(ex)
- None
- }
- case x =>
- println(x)
- None
+ val gd = LiftRules.doWithResource(t) { inputStream =>
+ val templ = new RrdGraphDefTemplate(new InputSource(inputStream))
+ templ.setVariable("rrdFile", STORE.getPath)
+ templ.getRrdGraphDef()
}
gd match {
- case Some(gd) =>
+ case Full(gd) =>
gd.setTimeSpan(TimePeriod.last(p))
gd.setFilename("-") // in memory only
gd.setImageFormat("PNG")
gd.setAntiAliasing(true)
+ gd.setTextAntiAliasing(true)
reply(
try {
val g = new RrdGraph(gd)
@@ -169,7 +157,7 @@
None
}
)
- case None =>
+ case Empty =>
reply(None)
}
}
diff --git a/src/main/scala/uk/org/floop/msc/wview/DataCollector.scala b/src/main/scala/uk/org/floop/msc/wview/DataCollector.scala
index 0f8ec9a..ca297a3 100644
--- a/src/main/scala/uk/org/floop/msc/wview/DataCollector.scala
+++ b/src/main/scala/uk/org/floop/msc/wview/DataCollector.scala
@@ -7,9 +7,9 @@
import net.liftweb.http.LiftRules
import java.net.{InetSocketAddress, SocketTimeoutException, Socket}
-import java.io.{IOException, DataInputStream}
+import java.io.{IOException, DataInputStream, InputStream}
import java.nio.channels.{SocketChannel, ClosedByInterruptException}
-import java.nio.ByteBuffer
+import java.nio.{ByteBuffer, ByteOrder}
import java.util.Date
import uk.org.floop.msc.rrd._
@@ -18,6 +18,7 @@
def act() {
var holdoff = 1000
+ val buffer = new Array[Byte](LoopPacket.SIZE)
//LiftRules.unloadHooks.append{() => continue}
while(true) {
var sock: Socket = null
@@ -29,28 +30,48 @@
sock = new Socket("10.79.0.6", 11011)
sock.setSoTimeout(30000)
- var dis = new DataInputStream(sock.getInputStream)
+ var is = sock.getInputStream
while (true) {
while (startFramePos < 8) {
- if (dis.readByte() == LoopPacket.START_FRAME(startFramePos)) {
+ val res = is.read
+ if (res == -1)
+ throw new IOException("Unexpected end of stream.")
+ else if (res.toByte == LoopPacket.START_FRAME(startFramePos)) {
startFramePos = startFramePos + 1
} else {
startFramePos = 0
}
}
- packetPos = 0
- while (packetPos < LoopPacket.fields.length) {
- LoopPacket.fields(packetPos) match {
- case (VALUE_TYPE.FLOAT, field) => values += (field, dis.readFloat())
- case (VALUE_TYPE.USHORT, field) => values += (field, dis.readShort())
- case (VALUE_TYPE.TIME_T, field) => values += (field, dis.readInt() match {
+ var bytesRead = 0
+ while (bytesRead < LoopPacket.SIZE) {
+ val res = is.read(buffer, bytesRead, buffer.length - bytesRead)
+ if (res == -1)
+ throw new IOException("Unexpected end of stream.")
+ bytesRead = bytesRead + res
+ }
+ val bb = ByteBuffer.wrap(buffer)
+ bb.order(ByteOrder.BIG_ENDIAN)
+ for ((valueType, field) <- LoopPacket.fields) {
+ valueType match {
+ case VALUE_TYPE.FLOAT => values. += ((field, {
+ bb.order(ByteOrder.LITTLE_ENDIAN)
+ val l = bb.getFloat.toLong
+ bb.order(ByteOrder.BIG_ENDIAN)
+ val f = ((l >> 16) & 0x7fff) + ((l & 0xffff) / 63365.0f)
+ if (((l >> 31) & 0x1) == 0x1) {
+ -f
+ } else {
+ f
+ }
+ }))
+ case VALUE_TYPE.USHORT => values += ((field, bb.getShort))
+ case VALUE_TYPE.TIME_T => values += ((field, bb.getInt() match {
case 0 => None
case x => Some(new Date(x.toLong * 1000))
- })
- case (VALUE_TYPE.SHORT, field) => values += (field, dis.readShort())
- case (VALUE_TYPE.UCHAR, field) => values += (field, dis.readByte())
+ }))
+ case VALUE_TYPE.SHORT => values += ((field, bb.getShort()))
+ case VALUE_TYPE.UCHAR => values += ((field, bb.get()))
}
- packetPos = packetPos + 1
}
startFramePos = 0
DataStore ! StorePacket(values.toList)
diff --git a/src/main/scala/uk/org/floop/msc/wview/Forecast.scala b/src/main/scala/uk/org/floop/msc/wview/Forecast.scala
index cfa09f2..1aed516 100644
--- a/src/main/scala/uk/org/floop/msc/wview/Forecast.scala
+++ b/src/main/scala/uk/org/floop/msc/wview/Forecast.scala
@@ -1,11 +1,10 @@
package uk.org.floop.msc.wview
import java.io.{BufferedReader, InputStreamReader}
-
import net.liftweb.http.LiftRules
-import net.liftweb.util.{Full, Empty}
-
import scala.collection.mutable.ArrayBuffer
+import net.liftweb.common.Full
+import net.liftweb.common.Empty
object Forecast {
@@ -13,20 +12,18 @@
private val rules = new ArrayBuffer[String]()
private def initialize() {
- val confReader = LiftRules.getResourceAsStream("/forecast.conf") match {
- case Full(inputStream) =>
- new BufferedReader(new InputStreamReader(inputStream))
- case Empty => null
- }
- var line = confReader.readLine()
- var readingRules = false
- while (line != null) {
- if (readingRules) {
- rules += line
- } else if (line == "") {
- readingRules = true
+ LiftRules.doWithResource("/forecast.conf") { inputStream =>
+ val confReader = new BufferedReader(new InputStreamReader(inputStream))
+ var line = confReader.readLine()
+ var readingRules = false
+ while (line != null) {
+ if (readingRules) {
+ rules += line
+ } else if (line == "") {
+ readingRules = true
+ }
+ line = confReader.readLine()
}
- line = confReader.readLine()
}
}
@@ -34,7 +31,11 @@
if (rules.length == 0) {
initialize()
}
- rules(i)
+ if (rules.length > i) {
+ rules(i)
+ } else {
+ "Unknown"
+ }
}
}
diff --git a/src/main/scala/uk/org/floop/msc/wview/LoopPacket.scala b/src/main/scala/uk/org/floop/msc/wview/LoopPacket.scala
index 0721302..c7c6eab 100644
--- a/src/main/scala/uk/org/floop/msc/wview/LoopPacket.scala
+++ b/src/main/scala/uk/org/floop/msc/wview/LoopPacket.scala
@@ -28,6 +28,7 @@
(VALUE_TYPE.FLOAT, "sampleRain"), // inches
(VALUE_TYPE.FLOAT, "sampleET"), // ET
(VALUE_TYPE.USHORT, "radiation"), // watts/m^3
+ (VALUE_TYPE.USHORT, "padding1"), // ! 2 bytes of padding
(VALUE_TYPE.FLOAT, "UV"), // UV index * 10
(VALUE_TYPE.FLOAT, "dewpoint"), // degrees F
(VALUE_TYPE.FLOAT, "windchill"), // degrees F
@@ -43,7 +44,7 @@
(VALUE_TYPE.FLOAT, "monthET"), // inches
(VALUE_TYPE.FLOAT, "yearET"), // inches
(VALUE_TYPE.FLOAT, "intervalAvgWCHILL"), // degrees F
- (VALUE_TYPE.SHORT, "intervalAvgWSPEED"), // mph
+ (VALUE_TYPE.USHORT, "intervalAvgWSPEED"), // mph
(VALUE_TYPE.USHORT, "yearRainMonth"), // 1-12 Rain Start Month
// --- The following may or may not be supported for a given station ---
@@ -55,21 +56,22 @@
(VALUE_TYPE.USHORT, "forecastRule"), // VP only
(VALUE_TYPE.USHORT, "txBatteryStatus"), // VP only
(VALUE_TYPE.USHORT, "consBatteryVoltage"), // VP only
- (VALUE_TYPE.SHORT, "extraTemp1"), // degrees F + 90
- (VALUE_TYPE.SHORT, "extraTemp2"), // degrees F + 90
- (VALUE_TYPE.SHORT, "extraTemp3"), // degrees F + 90
- (VALUE_TYPE.SHORT, "soilTemp1"), // degrees F + 90
- (VALUE_TYPE.SHORT, "soilTemp2"), // degrees F + 90
- (VALUE_TYPE.SHORT, "soilTemp3"), // degrees F + 90
- (VALUE_TYPE.SHORT, "soilTemp4"), // degrees F + 90
- (VALUE_TYPE.SHORT, "leafTemp1"), // degrees F + 90
- (VALUE_TYPE.SHORT, "leafTemp2"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "extraTemp1"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "extraTemp2"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "extraTemp3"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "soilTemp1"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "soilTemp2"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "soilTemp3"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "soilTemp4"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "leafTemp1"), // degrees F + 90
+ (VALUE_TYPE.FLOAT, "leafTemp2"), // degrees F + 90
(VALUE_TYPE.UCHAR, "extraHumid1"), // percent
(VALUE_TYPE.UCHAR, "extraHumid2"), // percent
(VALUE_TYPE.UCHAR, "soilMoist1"),
(VALUE_TYPE.UCHAR, "soilMoist2"),
(VALUE_TYPE.UCHAR, "leafWet1"),
(VALUE_TYPE.UCHAR, "leafWet2"),
+ (VALUE_TYPE.USHORT, "padding2"), // !two bytes of padding
// Vaisala WXT-510
(VALUE_TYPE.FLOAT, "wxt510Hail"), // inches
@@ -85,8 +87,8 @@
(VALUE_TYPE.FLOAT, "wxt510Rain"), // inches
// WMR918/968
- (VALUE_TYPE.UCHAR, "wmr918Humid3"), // percent
(VALUE_TYPE.FLOAT, "wmr918Pool"), // degrees F
+ (VALUE_TYPE.UCHAR, "wmr918Humid3"), // percent
(VALUE_TYPE.UCHAR, "wmr918Tendency"), // WMR's Forecast
(VALUE_TYPE.UCHAR, "wmr918WindBatteryStatus"),
(VALUE_TYPE.UCHAR, "wmr918RainBatteryStatus"),
@@ -95,10 +97,69 @@
(VALUE_TYPE.UCHAR, "wmr918poolTempBatteryStatus"),
(VALUE_TYPE.UCHAR, "wmr918extra1BatteryStatus"),
(VALUE_TYPE.UCHAR, "wmr918extra2BatteryStatus"),
- (VALUE_TYPE.UCHAR, "wmr918extra3BatteryStatus")
-
+ (VALUE_TYPE.UCHAR, "wmr918extra3BatteryStatus"),
+ (VALUE_TYPE.USHORT, "padding3"), // ! 2 bytes of padding
+
+ // Generic extra sensor and status support:
+ (VALUE_TYPE.FLOAT, "extraTemp0"),
+ (VALUE_TYPE.FLOAT, "extraTemp1"),
+ (VALUE_TYPE.FLOAT, "extraTemp2"),
+ (VALUE_TYPE.FLOAT, "extraTemp3"),
+ (VALUE_TYPE.FLOAT, "extraTemp4"),
+ (VALUE_TYPE.FLOAT, "extraTemp5"),
+ (VALUE_TYPE.FLOAT, "extraTemp6"),
+ (VALUE_TYPE.FLOAT, "extraTemp7"),
+ (VALUE_TYPE.FLOAT, "extraTemp8"),
+ (VALUE_TYPE.FLOAT, "extraTemp9"),
+ (VALUE_TYPE.FLOAT, "extraTempA"),
+ (VALUE_TYPE.FLOAT, "extraTempB"),
+ (VALUE_TYPE.FLOAT, "extraTempC"),
+ (VALUE_TYPE.FLOAT, "extraTempD"),
+ (VALUE_TYPE.FLOAT, "extraTempE"),
+ (VALUE_TYPE.FLOAT, "extraTempF"),
+ (VALUE_TYPE.USHORT, "extraHumidity0"),
+ (VALUE_TYPE.USHORT, "extraHumidity1"),
+ (VALUE_TYPE.USHORT, "extraHumidity2"),
+ (VALUE_TYPE.USHORT, "extraHumidity3"),
+ (VALUE_TYPE.USHORT, "extraHumidity4"),
+ (VALUE_TYPE.USHORT, "extraHumidity5"),
+ (VALUE_TYPE.USHORT, "extraHumidity6"),
+ (VALUE_TYPE.USHORT, "extraHumidity7"),
+ (VALUE_TYPE.USHORT, "extraHumidity8"),
+ (VALUE_TYPE.USHORT, "extraHumidity9"),
+ (VALUE_TYPE.USHORT, "extraHumidityA"),
+ (VALUE_TYPE.USHORT, "extraHumidityB"),
+ (VALUE_TYPE.USHORT, "extraHumidityC"),
+ (VALUE_TYPE.USHORT, "extraHumidityD"),
+ (VALUE_TYPE.USHORT, "extraHumidityE"),
+ (VALUE_TYPE.USHORT, "extraHumidityF"),
+ (VALUE_TYPE.UCHAR, "windBatteryStatus"),
+ (VALUE_TYPE.UCHAR, "rainBatteryStatus"),
+ (VALUE_TYPE.UCHAR, "outTempBatteryStatus"),
+ (VALUE_TYPE.UCHAR, "consoleBatteryStatus"),
+ (VALUE_TYPE.UCHAR, "uvBatteryStatus"),
+ (VALUE_TYPE.UCHAR, "solarBatteryStatus"),
+ (VALUE_TYPE.USHORT, "padding4"), // ! 2 bytes of padding
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus0"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus1"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus2"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus3"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus4"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus5"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus6"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus7"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus8"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatus9"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatusA"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatusB"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatusC"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatusD"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatusE"),
+ (VALUE_TYPE.UCHAR, "extraTempBatteryStatusF")
)
- val START_FRAME = Array(0x88, 0xf3, 0xa2, 0xc6, 0xda, 0xda, 0xcf, 0xe7).map(_.toByte)
+ val START_FRAME = Array(0x88, 0xf3, 0xa2, 0xc6, 0xda, 0xda, 0x01, 0x00).map(_.toByte)
+
+ val SIZE = LoopPacket.fields.map(pair => VALUE_TYPE.sizeof(pair._1)).foldLeft(0)(_ + _)
}