跳轉到內容

XQuery/Pachube 饋送

來自華夏公益教科書,開放的書籍,用於開放的世界

您想為 Pachube 應用程式建立饋送。Pachube 應用程式允許您儲存、共享和發現來自世界各地物體、裝置和建築物的即時感測器、能源和環境資料。這為感測器資料整合提供了一個平臺。Pachube 收集的歷史記錄可以以各種格式呈現,並被其他應用程式用來混合饋送。

模組和概念

[編輯 | 編輯原始碼]
  • eXist httpclient 用於 GET、POST 和 PUT
  • eXist 排程程式用於作業排程
  • eXist 更新擴充套件
  • 伺服器端 XSLT

倫敦塔橋的開/關狀態饋送的想法借鑑了 @ni

一個 Twitter 流提供了一個簡單的狀態饋送的基礎資料。來自該流的 RSS 饋送 由 XQuery 指令碼讀取,狀態從文字中推斷出來,並更新了表示當前狀態的 XML 檔案。

此 XML 檔案附加了一個 XSLT 樣式表,因此當檔案按計劃從 eXist 資料庫中提取時,它首先在伺服器端轉換為 Pachube 饋送所需的 EEML 格式。在 UWE 伺服器上配置時,這將使用 Saxon XSLT-2。

XQuery 指令碼

[編輯 | 編輯原始碼]
let $rss := httpclient:get(xs:anyURI(
    "http://twitter.com/statuses/user_timeline/14012942.rss"
   ),false(),())/httpclient:body
let $lastChange:= $rss//item[1]
let $bridgeStatus :=  doc("/db/Wiki/Pachube/bridge.xml")/data/status
return 
  if (exists($lastChange) and exists($bridgeStatus))
  then
     let $open := if(contains($lastChange/description,"opening"))  then "1" else "0"
     return update replace $bridgeStatus with  
                element status {
                     attribute bridge {$open},
                     attribute lastChange {$lastChange/pubDate},
                     attribute lastUpdate {current-dateTime()}
                }
     else ()

1. 這裡使用 httpclient 是因為 doc() 會丟擲關於重複名稱空間宣告的錯誤 - 正在調查中

橋樑狀態

[編輯 | 編輯原始碼]
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="http://www.cems.uwe.ac.uk/xmlwiki/Pachube/bridge.xsl"?>
<data>
    <status bridge="0" lastChange="Mon, 14 Dec 2009 12:09:02 +0000" lastUpdate="2009-12-14T16:57:00.679Z"/>
</data>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output media-type="application/xml" method="xml" indent="yes"/>
    <xsl:template match="/data">
        <eeml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.eeml.org/xsd/005" xsi:schemaLocation="http://www.eeml.org/xsd/005 http://www.eeml.org/xsd/005/005.xsd" version="5">
            <environment updated="{current-dateTime()}">
                <title>Tower Bridge </title>
                <feed>http://www.cems.uwe.ac.uk/xmlwiki/Pachube/bridge.xml</feed>
                <description>The status of the lifting Tower Bridge: 1 is open , 0 is closed. </description>
                <email>kit.wallace@gmail.com</email>
                <location exposure="outdoor" domain="physical" disposition="fixed">
                    <name>Tower Bridge</name>
                    <lat>51.5064186</lat>
                    <lon>-0.074865818</lon>
                </location>
                <data id="0">
                    <tag>bridge open</tag>
                    <value minValue="0" maxValue="1">
                        <xsl:value-of select="status/@bridge"/>
                    </value>
                </data>
            </environment>
        </eeml>
    </xsl:template>
</xsl:stylesheet>

作業排程

[編輯 | 編輯原始碼]

eXist 作業排程程式每 1 分鐘呼叫一次 XQuery 更新指令碼

let $login := xmldb:login( "/db", "user", "password" ) 
let $del := scheduler:delete-scheduled-job("BRIDGE")
let $job := scheduler:schedule-xquery-cron-job("/db/Wiki/Pachube/pollbridgerss.xq" , "0 0/1 * * * ?","BRIDGE")
return $job

饋送檢視

[編輯 | 編輯原始碼]

有一個公開的饋送檢視,由 Pachube 處理:http://www.pachube.com/feeds/3922

Pachube 介面每 15 分鐘重新整理一次自動饋送(對於免費服務)。由於典型的橋樑升降持續 10 分鐘,因此有可能錯過升降。另一種方法是在檢測到更改時將更改推送到 Pachube。

天氣推送饋送

[編輯 | 編輯原始碼]

許多業餘氣象站使用 Weather Display 軟體。該軟體將當前觀測結果寫入一個空格分隔的文字檔案,以支援與檢視軟體的介面,例如 Flash Weather Display Live。文字檔案通常可供 Web 訪問,因此任何客戶端都可以訪問此原始資料,儘管禮貌地要求訪問是可取的。

其中一個氣象站由 Martyn Hicks 運營,位於 http://www.martynhicks.co.uk/weather/data.php,位於布里斯托爾的霍菲爾德。原始資料檔案是 http://www.martynhicks.co.uk/weather/clientraw.txt

在此推送實現中,一個手動饋送透過 API 在 Pachube 中定義,方法是 POST 一個完整的 EEML 文件。一個 XML 描述符檔案定義了資料檔案中的值與饋送中的資料流之間的對映。一個計劃的 XQuery 指令碼讀取資料檔案,並透過對映檔案將其轉換為 EEML 格式,然後再 PUT 到 Pachube API。


XQuery 饋送建立

[編輯 | 編輯原始碼]

饋送是在 EEML 文件中定義的,該文件 POST 到 Pachube API。返回程式碼 201 表示饋送已建立。

let $url := xs:anyURI("http://www.pachube.com/api/feeds/")
let $headers := 
        <headers>
            <header name="X-PachubeApiKey" value="...api key ...."/>
        </headers>
let $feed :=
          <eeml  xmlns="http://www.eeml.org/xsd/005">
            <environment updated="{current-dateTime()}">
                <title>Horfield Weather</title>
                <description>The weather observed by a weather station run by Martyn Hicks.  Public interface is http://www.martynhicks.co.uk/weather/data.php   </description>
                <email>kit.wallace@gmail.com</email>
                <location exposure="outdoor" domain="physical" disposition="fixed">
                    <name>Horfield</name>
                    <lat>51.4900</lat>
                    <lon>-2.5805</lon>
                </location>
            </environment>
        </eeml>
return
     httpclient:post($url,$feed,false(),$headers)

對映檔案

[編輯 | 編輯原始碼]

一個 XML 文件定義了原始資料的來源、Pachube appid 以及從資料值(從 1 開始)到資料流(從文件順序中的 1 開始編號)的對映。

<weatherfeed xmlns = "http://www.cems.uwe.ac.uk/xmlwiki/wdl">
    <data>http://www.martynhicks.co.uk/weather/clientraw.txt</data>
    <appid>4013</appid>
    <format>
        <field n="2" unit="kts">Average Wind Speed</field>
        <field n="4" unit="degrees">Wind Direction</field>
        <field n="5" unit="Celcius">Temperature</field>
        <field n="7" unit="hPa">Barometer</field>
    </format>
</weatherfeed>

更新指令碼

[編輯 | 編輯原始碼]

此指令碼計劃每分鐘執行一次(如上所述)。

對映檔案名稱空間需要宣告

declare namespace wdl = "http://www.cems.uwe.ac.uk/xmlwiki/wdl";

首先是一個函式來讀取原始資料檔案並將其標記化為一系列值

declare function local:client-data ($rawuri) {
 let $headers :=
    element headers{
       element header {
         attribute name {"Cache-Control"},
         attribute value {"no-cache"}
      }
    }
 let $raw := httpclient:get(xs:anyURI($rawuri),false(),$headers )/httpclient:body
 return tokenize($raw,"\+")
};

然後是一個函式來將一系列值轉換為 Pachube 資料通道

declare function local:data-to-eeml ($data,$format) {
         for $field at $id in $format/wdl:field
         let $name := string($field)
         let $index := xs:integer($field/@n)
         return 
            element data {
                attribute id {$id},
                element tag { string($field)},
                element value {$data[$index] },
                element unit {string($field/@unit)}
            }
};

主線獲取饋送定義檔案(這裡硬編碼,但可以作為引數傳入)。獲取資料值,生成 EEML 並 PUT 到 Pachube API。

let $feed := doc("/db/Wiki/Pachube/horfieldweather.xml")/wdl:weatherfeed
let $data := local:client-data($feed/wdl:data)
let $appid := $feed/wdl:appid
let $APIKey := "eeda7c27ff8b7c49e8529e4eb4b3f57724c5b609db0d22904df11edd4742e92c"
let $url := xs:anyURI(concat( "http://www.pachube.com/api/",$appid))
let $headers := 
        <headers>
            <header name="X-PachubeApiKey" value="{$APIKey}"/>
        </headers>
let $eeml:=
     <eeml xmlns="http://www.eeml.org/xsd/005">
          <environment updated="{current-dateTime()}">          
           {local:data-to-eeml($data,$feed/wdl:format)}
          </environment>
   </eeml>
return
       httpclient:put($url,$eeml,false(),$headers)

饋送檢視

[編輯 | 編輯原始碼]

Pachube 饋送的公開檢視位於 http://www.pachube.com/feeds/4013


天氣拉取饋送

[編輯 | 編輯原始碼]

另一種方法更簡單,依賴於 Pachube 按其計劃拉取資料。

在這個例子中,天氣站資料由 WeatherUnderground 整合,並以 XML 格式重新發布,被轉換為 EEML。

Weatherundgerground 資料來源

[編輯 | 編輯原始碼]

Weatherunderground 上一個典型的天氣站的 XML 資料來源是 http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=IBAYOFPL1

XSLT 轉換

[編輯 | 編輯原始碼]

可以使用 XSLT 將此 XML 轉換為 EEML。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output media-type="application/xml" method="xml" indent="yes"/>
    <xsl:template match="/current_observation">
        <eeml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://www.eeml.org/xsd/005"
            xsi:schemaLocation="http://www.eeml.org/xsd/005 http://www.eeml.org/xsd/005/005.xsd"
            version="5">
            <environment updated="{current-dateTime()}">
                <title>Weather Report</title>
                <location exposure="outdoor" domain="physical" disposition="fixed">
                    <name>
                        <xsl:value-of select="location/full"/>
                    </name>
                    <lat>
                        <xsl:value-of select="location/latitude"/>
                    </lat>
                    <lon><xsl:value-of select="location/longitude"/>
                    </lon>
                </location>
                <data id="1">
                    <tag>Average Wind speed</tag>
                    <value>
                        <xsl:value-of select="round-half-to-even(wind_mph * 1.15077945,1)"/>
                    </value>
                    <unit>kts</unit>
                </data>
                <data id="2">
                    <tag>Wind Direction</tag>
                    <value>
                        <xsl:value-of select="wind_degrees"/>
                    </value>
                    <unit>degrees</unit>
                </data>
                <data id="3">
                    <tag>Temperature</tag>
                    <value>
                        <xsl:value-of select="temp_c"/>
                    </value>
                    <unit>Celcius</unit>
                </data>
                <data id="4">
                    <tag>Barometric Pressure</tag>
                    <value>
                        <xsl:value-of select="pressure_mb"/>
                    </value>
                    <unit>hPA</unit>
                </data>
            </environment>
        </eeml>
    </xsl:template>
</xsl:stylesheet>

連線 XML 到 XSLT

[編輯 | 編輯原始碼]

一個簡單的 XQuery 指令碼接受一個引數,即站點 ID,獲取 XML 資料來源,並使用 XSLT 轉換為 EEML。

let $id := request:get-parameter("id",())
let $ss := doc("/db/Wiki/Pachube/weatherunderground.xsl")
let $data := doc(concat("http://api.wunderground.com/weatherstation/WXCurrentObXML.asp?ID=",$id))
return
  transform:transform($data,$ss,())

該指令碼可以被呼叫:http://www.cems.uwe.ac.uk/xmlwiki/Pachube/weatherunderground.xq?id=IBAYOFPL1

由於此指令碼是引數化的,因此可以與任何 WeatherUnderground 站點一起使用。

Pachube 資料來源

[編輯 | 編輯原始碼]

可以建立一個自動資料來源 - http://www.pachube.com/feeds/4037,它使用此資料來源。


NOAA 資料來源

[編輯 | 編輯原始碼]

我們可以對 美國 ICAO 站點 的資料來源採用類似的方法。NOAA 提供 XML 資料來源,例如 http://www.weather.gov/xml/current_obs/KEWR.xml。格式與 Weatherunderground 資料來源幾乎相同,並有文件:http://www.weather.gov/view/current_observation.xsd。更新頻率為每小時,但目前無法配置 Pachube 以該頻率更新。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output media-type="application/xml" method="xml" indent="yes"/>
    <xsl:template match="/current_observation">
        <eeml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://www.eeml.org/xsd/005"
            xsi:schemaLocation="http://www.eeml.org/xsd/005 http://www.eeml.org/xsd/005/005.xsd"
            version="5">
            <environment updated="{current-dateTime()}">
                <title>NOAA Weather Report</title>
                <location exposure="outdoor" domain="physical" disposition="fixed">
                    <name>
                        <xsl:value-of select="location"/>
                    </name>
                    <lat>
                        <xsl:value-of select="latitude"/>
                    </lat>
                    <lon><xsl:value-of select="longitude"/>
                    </lon>
                </location>
                <data id="1">
                    <tag>Average Wind speed</tag>
                    <value>
                       <xsl:value-of select="wind_kt"/>
      
                    </value>
                    <unit>kts</unit>
                </data>
                <data id="2">
                    <tag>Wind Direction</tag>
                    <value>
                        <xsl:value-of select="wind_degrees"/>
                    </value>
                    <unit>degrees</unit>
                </data>
                <data id="3">
                    <tag>Temperature</tag>
                    <value>
                        <xsl:value-of select="temp_c"/>
                    </value>
                    <unit>Celcius</unit>
                </data>
                <data id="4">
                    <tag>Barometric Pressure</tag>
                    <value>
                        <xsl:value-of select="pressure_mb"/>
                    </value>
                    <unit>hPA</unit>
                </data>
            </environment>
        </eeml>
    </xsl:template>
</xsl:stylesheet>

XQuery 指令碼

[編輯 | 編輯原始碼]
let $id := request:get-parameter("id",())
let $ss := doc("/db/Wiki/Pachube/NOAA.xsl")
let $data := doc(concat("http://www.weather.gov/xml/current_obs/",$id,".xml"))
return
  transform:transform($data,$ss,())

資料來源

[編輯 | 編輯原始碼]

轉換後的 XML http://www.cems.uwe.ac.uk/xmlwiki/Pachube/NOAA.xq?id=KEWR 是手動資料來源 http://www.pachube.com/feeds/4047 的基礎。

僅使用 XSLT

[編輯 | 編輯原始碼]

如果 Pachube 在伺服器端支援 XSLT,則整個任務都可以由單個 XSLT 指令碼處理。為了通用性,提供一個介面,允許將引數傳遞給指令碼,但這不是必需的。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output media-type="application/xml" method="xml" indent="yes"/>
    <xsl:param name="station" select="'KEWR'"/>
    <xsl:template match="/">
        <xsl:variable name="url" select='concat("http://www.weather.gov/xml/current_obs/",$station,".xml")'></xsl:variable>
         <xsl:apply-templates select="doc($url)/current_observation"/>
     </xsl:template>
    <xsl:template match="current_observation">
         <eeml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xmlns="http://www.eeml.org/xsd/005"
            xsi:schemaLocation="http://www.eeml.org/xsd/005 http://www.eeml.org/xsd/005/005.xsd"
            version="5">
            <environment updated="{current-dateTime()}">
                <title>NOAA Current weather for {station_id}</title>
                <location exposure="outdoor" domain="physical" disposition="fixed">
                    <name>
                        <xsl:value-of select="location"/>
                    </name>
                    <lat>
                        <xsl:value-of select="latitude"/>
                    </lat>
                    <lon>
                        <xsl:value-of select="longitude"/>
                    </lon>
                </location>
                <data id="1">
                    <tag>Average Wind speed</tag>
                    <value>
                        <xsl:value-of select="wind_kt"/>
                    </value>
                    <unit>kts</unit>
                </data>
                <data id="2">
                    <tag>Wind Direction</tag>
                    <value>
                        <xsl:value-of select="wind_degrees"/>
                    </value>
                    <unit>degrees</unit>
                </data>
                <data id="3">
                    <tag>Temperature</tag>
                    <value>
                        <xsl:value-of select="temp_c"/>
                    </value>
                    <unit>Celcius</unit>
                </data>
                <data id="4">
                    <tag>Barometric Pressure</tag>
                    <value>
                        <xsl:value-of select="pressure_mb"/>
                    </value>
                    <unit>hPA</unit>
                </data>
            </environment>
        </eeml>        
    </xsl:template>
</xsl:stylesheet>

伺服器可以簡單地執行此獨立指令碼以生成 EEML 資料來源。此小型 XQuery 指令碼使用 eXist 平臺上的 SAXON 處理器。

  transform:transform((),doc("/db/Wiki/Pachube/NOAA3.xsl"),())

XSLT 執行(目前失敗 - 正在調查中)

XSLT 資料來源服務

[編輯 | 編輯原始碼]

自動資料來源

[編輯 | 編輯原始碼]

XSLT 轉換可以由 eXist 資料庫作為服務提供。它需要

  • 一個連線定義的 XML 資料庫,例如:
<PachubeFeeds>
    <PachubeFeed id="2001">
        <data>http://www.weather.gov/xml/current_obs/KORS.xml</data>
        <xslt>http://www.cems.uwe.ac.uk/xmlwiki/Pachube/NOAA7.xsl</xslt>
        <params/>
    </PachubeFeed>
    <PachubeFeed id="2002">
        <data>http://www.weather.gov/xml/current_obs/KEWR.xml</data>
        <xslt>http://www.cems.uwe.ac.uk/xmlwiki/Pachube/NOAA7.xsl</xslt>
        <params/>
    </PachubeFeed>
</PachubeFeeds>
  • 一個用於定位和執行資料來源的指令碼
let $id := request:get-parameter("id",())
let $feed := doc("/db/Wiki/Pachube/feeds.xml")//PachubeFeed[@id=$id]
return 
 transform:transform(
     doc($feed/data),
     doc($feed/xslt),
     $feed/params
     )

  • Pachube 自動資料來源現在可以建立,URL 類似於
  http://www.cems.uwe.ac.uk/xmlwiki/Pachube/getFeed.xq?id=2001   

例如 http://www.pachube.com/feeds/4661

  • 使用者介面和資料庫,允許使用者註冊、建立和編輯資料來源

這裡存在載入問題以及儲存在 XSLT 中的不安全程式碼問題。

類似地,可以透過一些程式碼和 XSLT 來提供當前 EEML 或特定資料流的 CSV 歷史記錄的輸出處理。由於這可能需要身份驗證,因此 API 金鑰也必須儲存在此資料庫中。可以生成和計劃作業來實現觸發器,但這需要定時拉取所需的資料。

需要程式碼將 Pachube 提供的歷史記錄資料來源轉換為 XML,因為這些資料來源僅以 CSV 格式提供。轉換為 XML 後,XSLT 可以轉換為所需格式。當然,如果 Pachube 除了 CSV 資料來源外還能提供 XML 資料來源,那將是最好的。

完整存檔以 CSV 檔案形式提供。我們可以使用以下指令碼將其轉換為 XML

import module namespace csv = "http://www.cems.uwe.ac.uk/xmlwiki/csv" at "../lib/csv.xqm";

let $feed := request:get-parameter("feed","")
let $stream := request:get-parameter("stream","")
let $archiveurl := concat("http://www.pachube.com/feeds/",$feed,"/datastreams/",$stream,"/archive.csv")
let $data:= csv:get-data($archiveurl)
let $rows :=  tokenize($data,$csv:newline)
let $now := current-dateTime()
return
<history feed="{$feed}" stream="{$stream}" dateTime="{$now}"  count="{count($rows)}">
{for $row in $rows
let $point := tokenize($row,",")
return
  <value dateTime="{$point[1]}">{$point[2]}</value>
}
</history>

http://www.cems.uwe.ac.uk/xmlwiki/Pachube/getArchive.xq?feed=4037&stream=2

24 小時歷史記錄

[編輯 | 編輯原始碼]

在 CSV 資料流中,這些資料是無時間戳的。時間必須使用 xs:dateTimeDuration 估算和計算

import module namespace csv = "http://www.cems.uwe.ac.uk/xmlwiki/csv" at "../lib/csv.xqm";

let $feed := request:get-parameter("feed","")
let $stream := request:get-parameter("stream","")
let $historyurl := concat("http://www.pachube.com/feeds/",$feed,"/datastreams/",$stream,"/history.csv")
let $data:= csv:get-data($historyurl)
let $values :=tokenize($data,",")
let $now := current-dateTime()
let $then := $now - xs:dayTimeDuration("P1D")
return
<history feed="{$feed}" stream="{$stream}" dateTime="{$now}"  count="{count($values)}">
{for $value at $i in $values
let $dt := $then + xs:dayTimeDuration(concat("PT",15*$i,"M"))
return
  <value dateTime="{$dt}">{$value}</value>
}
</history>

http://www.cems.uwe.ac.uk/xmlwiki/Pachube/getHistory.xq?feed=4037&stream=2

華夏公益教科書