From caeca4940c3286784928b64d2e1a7a32b29d40f4 Mon Sep 17 00:00:00 2001 From: Hongyuan Jia Date: Mon, 8 Jul 2019 11:58:17 +0800 Subject: [PATCH 1/6] Fix data.frame conversion. --- DESCRIPTION | 2 + NAMESPACE | 5 +++ R/dataApi.r | 120 +++++++--------------------------------------------- 3 files changed, 23 insertions(+), 104 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3d9ff1b..b9faae4 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -11,5 +11,7 @@ LazyData: true Depends: R6, httr, rjson +Imports: + data.table RoxygenNote: 6.0.1 diff --git a/NAMESPACE b/NAMESPACE index 40ea8e2..79ed3a3 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1 +1,6 @@ +importFrom(data.table,rbindlist) +importFrom(data.table,set) +importFrom(data.table,setattr) +importFrom(data.table,setcolorder) +importFrom(data.table,setnames) exportPattern("^[[:alpha:]]+") diff --git a/R/dataApi.r b/R/dataApi.r index 96d88d0..c367813 100644 --- a/R/dataApi.r +++ b/R/dataApi.r @@ -33,49 +33,12 @@ dataApi <- R6Class("dataApi", return(as.vector(webIds)) }, convertToDataFrame = function(items) { - itemsLength <- length(items) - value <- array(1:itemsLength) - timestamp <- array(1:itemsLength) - unitsAbbreviation <- array(1:itemsLength) - good <- array(1:itemsLength) - questionable <- array(1:itemsLength) - substituted <- array(1:itemsLength) - for (i in 1:itemsLength) { - if (is.null(items[[i]]$Value) == FALSE) - { - if (is.numeric(items[[i]]$Value) == TRUE) - { - value[i] <- items[[i]]$Value - } - else - { - value[i] <- items[[i]]$Value$Name - } - } - if (is.null(items[[i]]$Timestamp) == FALSE) - { - timestamp[i] <- items[[i]]$Timestamp - } - if (is.null(items[[i]]$UnitsAbbreviation) == FALSE) - { - unitsAbbreviation[i] <- items[[i]]$UnitsAbbreviation - } - if (is.null(items[[i]]$Good) == FALSE) - { - good[i] <- items[[i]]$Good - } - if (is.null(items[[i]]$Questionable) == FALSE) - { - questionable[i] <- items[[i]]$Questionable - } - if (is.null(items[[i]]$Substituted) == FALSE) - { - substituted[i] <- items[[i]]$Substituted - } - } - - - resDataFrame <- data.frame(value, timestamp, unitsAbbreviation, good, questionable, substituted) + resDataFrame <- data.table::rbindlist(items, fill = TRUE) + data.table::setnames(resDataFrame, + c("timestamp", "value", "unitsAbbreviation", "good", "questionable", "substituted", "annotated") + ) + data.table::set(resDataFrame, NULL, "annotated", NULL) + data.table::setcolorder(resDataFrame, "value") return(resDataFrame) }, calculateItemsIndex = function(webId, items, originalIndex){ @@ -90,72 +53,21 @@ dataApi <- R6Class("dataApi", return(originalIndex) }, convertMultipleStreamsToDataFrame = function(items, gatherInOneDataFrame, webIds, paths = NULL) { - streamsLength <- length(items) + resDataFrame <- lapply(items, function (item) self$convertToDataFrame(item$Items)) + data.table::setattr(resDataFrame, "names", paths) if (gatherInOneDataFrame == TRUE) { - itemsLength <- length(items[[1]]$Items) - order <- 1:itemsLength - resDataFrame <- data.frame(order) - for (i in 1:streamsLength) { - k = self$calculateItemsIndex (webIds[i], items, i); - value <- array(1:itemsLength) - timestamp <- array(1:itemsLength) - unitsAbbreviation <- array(1:itemsLength) - good <- array(1:itemsLength) - questionable <- array(1:itemsLength) - substituted <- array(1:itemsLength) - for (j in 1:itemsLength) { - if (is.null(items[[k]]$Items[[j]]$Value) == FALSE) - { - if (is.numeric(items[[k]]$Items[[j]]$Value) == TRUE) - { - value[j] <- items[[k]]$Items[[j]]$Value - } - else - { - value[j] <- items[[k]]$Items[[j]]$Value$Name - } - } - if (is.null(items[[k]]$Items[[j]]$Timestamp) == FALSE) - { - timestamp[j] <- items[[k]]$Items[[j]]$Timestamp - } - if (is.null(items[[k]]$Items[[j]]$UnitsAbbreviation) == FALSE) - { - unitsAbbreviation[j] <- items[[k]]$Items[[j]]$UnitsAbbreviation - } - if (is.null(items[[k]]$Items[[k]]$Good) == FALSE) - { - good[j] <- items[[k]]$Items[[k]]$Good - } - if (is.null(items[[k]]$Items[[j]]$Questionable) == FALSE) - { - questionable[j] <- items[[k]]$Items[[j]]$Questionable - } - if (is.null(items[[k]]$Items[[j]]$Substituted) == FALSE) - { - substituted[j] <- items[[k]]$Items[[j]]$Substituted - } - } - if (i == 1) { - resDataFrame <- data.frame(timestamp) - } + timestamp <- resDataFrame[[1]][, .SD, .SDcols = "timestamp"] - resDataFrame[[paste0("value", i)]] = as.vector(value) - resDataFrame[[paste0("unitsAbbreviation", i)]] = as.vector(unitsAbbreviation) - resDataFrame[[paste0("good", i)]] = as.vector(good) - resDataFrame[[paste0("questionable", i)]] = as.vector(questionable) - resDataFrame[[paste0("substituted", i)]] = as.vector(substituted) - } - } - else { - resDataFrame <- list() - for (i in 1:streamsLength) { - key <- paste0(paths[i]) - df <- self$convertToDataFrame(items[[i]]$Items) - resDataFrame[[key]] = df + for (i in seq_along(resDataFrame)) { + data.table::setnames(resDataFrame[[i]], NULL, "timestamp", NULL) + data.table::setnames(resDataFrame[[i]], paste0(names(resDataFrame[[1]]), "i")) } + + resDataFrame <- Reduce(cbind, c(list(timestamp), resDataFrame)) + } + return(resDataFrame) }, From c29db8a35806c67a7d4e6e467be4935bcacbdd8b Mon Sep 17 00:00:00 2001 From: Hongyuan Jia Date: Mon, 8 Jul 2019 22:34:30 +0800 Subject: [PATCH 2/6] Make sure return column are unlisted. --- R/dataApi.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/dataApi.r b/R/dataApi.r index c367813..eb456bd 100644 --- a/R/dataApi.r +++ b/R/dataApi.r @@ -33,7 +33,7 @@ dataApi <- R6Class("dataApi", return(as.vector(webIds)) }, convertToDataFrame = function(items) { - resDataFrame <- data.table::rbindlist(items, fill = TRUE) + resDataFrame <- data.table::rbindlist(items, fill = TRUE)[, lapply(.SD, unlist, use.names = FALSE)] data.table::setnames(resDataFrame, c("timestamp", "value", "unitsAbbreviation", "good", "questionable", "substituted", "annotated") ) From 7e2f78cb2816a5ebb2d470f29900bcb0327715df Mon Sep 17 00:00:00 2001 From: Hongyuan Jia Date: Mon, 8 Jul 2019 22:39:53 +0800 Subject: [PATCH 3/6] Update README. --- README.md | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 36467b9..17bc12b 100644 --- a/README.md +++ b/README.md @@ -3,26 +3,32 @@ PI Web API client R package (2018) ## Introduction -This is an R package that integrates the PI System with R through PI Web API. With this package, you can retrieve PI data without having to generate the URL for each request. This version was developed on top of the PI Web API 2018 swagger specification. +This is an R package that integrates the PI System with R through PI Web API. +With this package, you can retrieve PI data without having to generate the URL +for each request. This version was developed on top of the PI Web API 2018 +swagger specification. ## Requirements. - - PI Web API 2018 installed within your domain using Kerberos or Basic Authentication. If you are using an older version, some methods might not work. + - PI Web API 2018 installed within your domain using Kerberos or Basic + Authentication. If you are using an older version, some methods might not + work. - R 3.4.3+ ## Installation -This R package is not available on CRAN. You should download it directly from this GitHub repository by using the devtools R package. If you don't have it installed, please use the command below: +This R package is not available on CRAN. You should download it directly from +this GitHub repository by using the `remotes` R package. If you don't have it +installed, please use the command below: ```r -install.packages("devtools") +install.packages("remotes") ``` -Then, load the library and install the PI Web API R package with the install_github method: +Then, load the library and install the PI Web API R package with the `install_github` method: ```r -library(devtools) -install_github("osimloeff/PI-Web-API-Client-R") +remotes::install_github("hongyuanjia/piwebapi") ``` If the installation is successful, the command below will load the package: @@ -42,17 +48,20 @@ remove.packages("piwebapi") All the methods and classes from this R package are described on its documentation, which can be opened by typing on the R console: ```r -help(package="piwebapi") +help(package="piwebapi") ``` ## Notes - - Is is highly recommended to turn debug mode on in case you are using PI Web API 2017 R2+ in order to receive more detailed exception errors. This can be achieved by creating or editing the DebugMode attribute's value to TRUE from the System Configuration element. + - Is is highly recommended to turn debug mode on in case you are using PI Web + API 2017 R2+ in order to receive more detailed exception errors. This can be + achieved by creating or editing the DebugMode attribute's value to TRUE from + the System Configuration element. - The X-Requested-With header is added to work with CSRF defences. ## Examples -Please refer to the following examples to understand how to use this library: +Please refer to the following examples to understand how to use this library: ### Create an intance of the piwebapi top level object. @@ -77,9 +86,9 @@ debug <- TRUE piWebApiService <- piwebapi$new("https://webserver/piwebapi", useKerberos, username, password, validateSSL, debug) ``` -If you want to use basic authentication instead of Kerberos, set useKerberos to FALSE. -If you are having issues with your SSL certificate and you want to ignore this error, set validateSSL to FALSE. -If you want to receive a log about each HTTP request, set debug to TRUE. +* If you want to use basic authentication instead of Kerberos, set useKerberos to FALSE. +* If you are having issues with your SSL certificate and you want to ignore this error, set validateSSL to FALSE. +* If you want to receive a log about each HTTP request, set debug to TRUE. ### Retrieve data from the main PI Web API endpoint @@ -247,6 +256,7 @@ response17 <- piWebApiService$streamSet$retrieveStreamSetUpdates(markers); ## Licensing + Copyright 2018 OSIsoft, LLC. Licensed under the Apache License, Version 2.0 (the "License"); @@ -260,5 +270,5 @@ Copyright 2018 OSIsoft, LLC. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + Please see the file named [LICENSE.md](LICENSE.md). From 6af5c0e2ad4e921092545af3c89f7ff208469b83 Mon Sep 17 00:00:00 2001 From: Hongyuan Jia Date: Tue, 9 Jul 2019 09:39:50 +0800 Subject: [PATCH 4/6] Handle tagged values. --- R/dataApi.r | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/R/dataApi.r b/R/dataApi.r index eb456bd..f27f9e6 100644 --- a/R/dataApi.r +++ b/R/dataApi.r @@ -33,6 +33,13 @@ dataApi <- R6Class("dataApi", return(as.vector(webIds)) }, convertToDataFrame = function(items) { + # check if value is tagged + for (i in seq_along(items)) { + if (!is.numeric(items[[i]]$Value)) { + items[[i]]$Value <- items[[i]]$Value$Name + } + } + resDataFrame <- data.table::rbindlist(items, fill = TRUE)[, lapply(.SD, unlist, use.names = FALSE)] data.table::setnames(resDataFrame, c("timestamp", "value", "unitsAbbreviation", "good", "questionable", "substituted", "annotated") From b7f739dc6417789145d76d3615d6ec013eef60fc Mon Sep 17 00:00:00 2001 From: Hongyuan Jia Date: Mon, 9 Dec 2019 14:20:19 +0800 Subject: [PATCH 5/6] Make package work when attached. --- NAMESPACE | 1 + R/piwebapi.r | 11 ++++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 79ed3a3..243fe43 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,4 +3,5 @@ importFrom(data.table,set) importFrom(data.table,setattr) importFrom(data.table,setcolorder) importFrom(data.table,setnames) +importFrom(R6, R6Class) exportPattern("^[[:alpha:]]+") diff --git a/R/piwebapi.r b/R/piwebapi.r index fb5b370..f1c987f 100644 --- a/R/piwebapi.r +++ b/R/piwebapi.r @@ -1,7 +1,12 @@ -library(R6) -library(httr) -library(rjson) +.onLoad <- function(libname, pkgname) { + require("httr") + require("rjson") +} +.onAttach <- function(libname, pkgname) { + require("httr") + require("rjson") +} piwebapi <- R6Class("piwebapi", private = list(), From 1a7e42f5cb13037caf78ebd08afe8c9cdb30b985 Mon Sep 17 00:00:00 2001 From: Hongyuan Jia Date: Mon, 9 Dec 2019 15:24:04 +0800 Subject: [PATCH 6/6] Only reassign value when returned item is a list. --- R/dataApi.r | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/dataApi.r b/R/dataApi.r index f27f9e6..b02a422 100644 --- a/R/dataApi.r +++ b/R/dataApi.r @@ -35,7 +35,7 @@ dataApi <- R6Class("dataApi", convertToDataFrame = function(items) { # check if value is tagged for (i in seq_along(items)) { - if (!is.numeric(items[[i]]$Value)) { + if (!is.numeric(items[[i]]$Value) && is.list(items[[i]]$Value)) { items[[i]]$Value <- items[[i]]$Value$Name } }