Generate CSV docs, tag non-room PLCs with "plc" instead of "room"
This commit is contained in:
parent
1aac927559
commit
0126b02caf
3 changed files with 272 additions and 67 deletions
|
@ -4,12 +4,14 @@ Generate a **Telegraf** config file to ingest **OPC-UA** data based on a **KEPSe
|
|||
|
||||
The generated config includes two inputs, one based on `opcua_listener` (event-based, instant updates, no initial values or periodic polling) and one based on `opcua` (polling-based, slow, but provides initial values and outputs a value at least every `interval`).
|
||||
|
||||
It also generates a CSV with a list of all the different data points and their descriptions: [./nodes.csv](./nodes.csv)
|
||||
|
||||
## Inputs
|
||||
|
||||
- **kepware_export.xml** - list of OPC nodes from KEPServerEX (see below)
|
||||
- **base_telegraf.conf.toml** - base Telegraf conf (should contain at least one of `inputs.opcua` or `inputs.opcua_listener`)
|
||||
- **generated_telegraf.conf.toml** - path where to save the generated Telegraf conf
|
||||
- **nodes.csv** - path where to save a CSV with a list of nodes and descriptions
|
||||
|
||||
## Getting the project file
|
||||
|
||||
|
@ -32,5 +34,5 @@ go build
|
|||
Once you have the executable:
|
||||
|
||||
```sh
|
||||
./kepware-opc-telegraf kepware_export.xml base_telegraf.conf.toml generated_telegraf.conf.toml
|
||||
./kepware-opc-telegraf -csv_out nodes.csv -telegraf_template ./base_telegraf.conf.toml -telegraf_out ./generated_telegraf.conf.toml ./kepware_export.xml
|
||||
```
|
||||
|
|
91
main.go
91
main.go
|
@ -1,11 +1,13 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/xml"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
@ -28,6 +30,7 @@ type Device struct {
|
|||
type Tag struct {
|
||||
Name string `xml:"Name"`
|
||||
Description string `xml:"Description"`
|
||||
DataType string `xml:"DataType"`
|
||||
}
|
||||
|
||||
type TelegrafConfig struct {
|
||||
|
@ -38,7 +41,7 @@ type TelegrafConfig struct {
|
|||
} `toml:"inputs"`
|
||||
}
|
||||
|
||||
func printStatistics(project Project, nodes []map[string]interface{}) {
|
||||
func printStatistics(project Project) {
|
||||
groupSet := make(map[string]bool)
|
||||
roomSet := make(map[string]bool)
|
||||
|
||||
|
@ -52,22 +55,29 @@ func printStatistics(project Project, nodes []map[string]interface{}) {
|
|||
fmt.Printf("Statistics:\n")
|
||||
fmt.Printf(" Number of groups: %d\n", len(groupSet))
|
||||
fmt.Printf(" Number of rooms: %d\n", len(roomSet))
|
||||
fmt.Printf(" Total number of nodes: %d\n", len(nodes))
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Parse CLI args
|
||||
inputPath := flag.String("telegraf_template", "", "Input telegraf.conf file")
|
||||
outputPath := flag.String("telegraf_out", "", "Output telegraf.conf file")
|
||||
csvPath := flag.String("csv_out", "", "Write documentation to CSV file")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 3 {
|
||||
fmt.Println("Usage: program <kepware_export.xml> <input_file.toml> <output_file.conf>")
|
||||
if flag.NArg() != 1 {
|
||||
fmt.Println("Usage: program <kepware_export.xml>")
|
||||
flag.PrintDefaults()
|
||||
return
|
||||
}
|
||||
|
||||
// Need either both or neither of telegraf input/output
|
||||
if *inputPath != "" && *outputPath == "" || *inputPath == "" && *outputPath != "" {
|
||||
fmt.Println("Error: Either both or neither of -telegraf_template and -output_file must be provided.")
|
||||
return
|
||||
}
|
||||
|
||||
kepwareXML := flag.Arg(0)
|
||||
inputFile := flag.Arg(1)
|
||||
outputFile := flag.Arg(2)
|
||||
|
||||
// Read the XML file
|
||||
xmlFile, err := os.Open(kepwareXML)
|
||||
|
@ -82,18 +92,34 @@ func main() {
|
|||
var project Project
|
||||
xml.Unmarshal(byteValue, &project)
|
||||
|
||||
/*
|
||||
Telegraf config generation
|
||||
*/
|
||||
|
||||
if *inputPath != "" {
|
||||
|
||||
// Read the base Telegraf config
|
||||
var config map[string]interface{}
|
||||
if _, err := toml.DecodeFile(inputFile, &config); err != nil {
|
||||
if _, err := toml.DecodeFile(*inputPath, &config); err != nil {
|
||||
fmt.Println("Error reading base config:", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Print statistics
|
||||
printStatistics(project)
|
||||
|
||||
// Generate node definitions
|
||||
var nodes []map[string]interface{}
|
||||
for _, channel := range project.ChannelList {
|
||||
for _, device := range channel.DeviceList {
|
||||
for _, tag := range device.TagList {
|
||||
|
||||
// Group "Razno" are general PLCs, not bound to specific rooms, so we use "plc" instead of "room"
|
||||
plc_tag := "room"
|
||||
if channel.Name == "Razno" {
|
||||
plc_tag = "plc"
|
||||
}
|
||||
|
||||
node := map[string]interface{}{
|
||||
"name": tag.Name,
|
||||
"identifier_type": "s",
|
||||
|
@ -101,7 +127,7 @@ func main() {
|
|||
"identifier": fmt.Sprintf("%s.%s.%s", channel.Name, device.Name, tag.Name),
|
||||
"tags": [][]string{
|
||||
{"group", channel.Name},
|
||||
{"room", device.Name},
|
||||
{plc_tag, device.Name},
|
||||
},
|
||||
}
|
||||
nodes = append(nodes, node)
|
||||
|
@ -109,8 +135,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
// Print statistics
|
||||
printStatistics(project, nodes)
|
||||
fmt.Printf(" Total number of nodes: %d\n", len(nodes))
|
||||
|
||||
// Update the config
|
||||
inputs, ok := config["inputs"].(map[string]interface{})
|
||||
|
@ -136,7 +161,7 @@ func main() {
|
|||
}
|
||||
|
||||
// Write the updated config to a new file
|
||||
f, err := os.Create(outputFile)
|
||||
f, err := os.Create(*outputPath)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating output file:", err)
|
||||
return
|
||||
|
@ -150,5 +175,47 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
fmt.Println("Config generated successfully: generated_telegraf.conf.toml")
|
||||
fmt.Printf("Config written to %s\n", *outputPath)
|
||||
}
|
||||
|
||||
/*
|
||||
Generate CSV documentation (optional)
|
||||
*/
|
||||
if *csvPath != "" {
|
||||
uniqueTags := make(map[string]Tag)
|
||||
for _, channel := range project.ChannelList {
|
||||
for _, device := range channel.DeviceList {
|
||||
for _, tag := range device.TagList {
|
||||
uniqueTags[fmt.Sprintf("%s.%s.%s", tag.Name, tag.Description, tag.DataType)] = tag
|
||||
}
|
||||
}
|
||||
}
|
||||
fmt.Printf(" Total number of unique tags: %d\n", len(uniqueTags))
|
||||
|
||||
// Sort the tags alphabetically by name
|
||||
keys := make([]string, 0, len(uniqueTags))
|
||||
for k := range uniqueTags {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
// Write the CSV file
|
||||
f, err := os.Create(*csvPath)
|
||||
if err != nil {
|
||||
fmt.Println("Error creating CSV file:", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
writer := csv.NewWriter(f)
|
||||
defer writer.Flush()
|
||||
|
||||
writer.Write([]string{"DataType", "Name", "Description"})
|
||||
for _, k := range keys {
|
||||
writer.Write([]string{uniqueTags[k].DataType, uniqueTags[k].Name, uniqueTags[k].Description})
|
||||
}
|
||||
|
||||
fmt.Printf("CSV documentation written to %s\n", *csvPath)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
136
nodes.csv
Normal file
136
nodes.csv
Normal file
|
@ -0,0 +1,136 @@
|
|||
DataType,Name,Description
|
||||
Float,AI_AI1_Temp,Zunanja temperatura
|
||||
Float,AI_AI1_Temp_VTZ,Temperatura vtočni zrak
|
||||
Float,AI_AI1_Temp_prostora,Temperatura prostora
|
||||
Float,AI_AI1_Vlaga,Zunanja vlaga
|
||||
Float,AI_AI2_Temp_VTZ,Temperatura vtočni zrak 2
|
||||
Float,AI_AI2_Temp_prostora,Temperatura prostora - K.KL.03.11
|
||||
Float,AI_AI2_Temp_prostora,Temperatura prostora - R.KL.03.06
|
||||
Float,AI_AI2_Temp_prostora,Temperatura prostora - X.KL.03.02
|
||||
Float,AI_AI2_Tlak_prostor,Tlak v prostoru
|
||||
Float,AI_AI3_Kvalita_zr_pr,Kvaliteta zraka v prostoru
|
||||
Float,AI_AI3_Temp_VTZ,Temperatura vtočni zrak 3
|
||||
Float,AI_AI3_Tlak_prostor_2,Tlak v prostoru
|
||||
Float,AI_AI3_Veter,Hitrost vetra
|
||||
Float,AI_AI4_Twilight,Osvetlejnost max 250 lx
|
||||
Float,AI_AI4_VRP_Dovod,Volumski regulator pretoka dov. - pretok
|
||||
Float,AI_AI5_Osvetljenost_Jug,Osvetlejnost - JUG
|
||||
Float,AI_AI5_Osvetljenost_Vzh,Osvetlejnost - VZHOD
|
||||
Float,AI_AI5_Temp_VTZ_2,Temperatura vtočni zrak
|
||||
Float,AI_AI6_Osvetljenost_Zah,Osvetlejnost - ZAHOD
|
||||
Float,AI_AI6_Padavine,"Padavine: 0V-DA, 10V-NE"
|
||||
Float,AI_AI6_VRP_Dovod_2,Volumski regulator pretoka dov. - pretok
|
||||
Float,AO_AO1_Ventil_Predgr,Ventil predgrelnika
|
||||
Float,AO_AO2_VRP_Dovod,Volumski regulator pretoka - dovod
|
||||
Float,AO_AO2_Ventil_Predgr2,Ventil predgrelnika 2
|
||||
Float,AO_AO3_LOPUTA_Odvod,Loputa - odvod
|
||||
Float,AO_AO3_Ventil_Predgr3,Ventil predgrelnika 3
|
||||
Boolean,AO_DO4_Ventil_FC_HL,Ventil konvektor hlajenje
|
||||
Float,AO_DO4_Ventil_FC_HL,Ventil konvektor hlajenje
|
||||
Boolean,AO_DO5_Ventil_RAD_GR,Ventil radiator gretje
|
||||
Float,AO_DO5_Ventil_RAD_GR,Ventil radiator gretje
|
||||
Float,AV_AVG_Pretok,Pretok - Povprečna vrednost
|
||||
Float,AV_AVG_Temperatura,Temperatura - Povprečna vrednost
|
||||
Float,AV_AVG_Tlak,Tlak - Povprečna vrednost
|
||||
Float,AV_Dnevni_rezim,Dnevni režim - Izkl komf/stby/varčni
|
||||
Boolean,AV_FKKT_KRM1_Pozar_1,Požar cona TR13/1
|
||||
Boolean,AV_FKKT_KRM1_Pozar_2,Požar cona TR12/4
|
||||
Boolean,AV_FKKT_KRM1_Pozar_3,Požar cona TR12/2
|
||||
Float,AV_FRI_KRM1_Padavine,Padavine - Vremenska postaja
|
||||
Boolean,AV_FRI_KRM1_Pozar,Požar
|
||||
Boolean,AV_FRI_KRM1_Pozar_R,Tipka požar - Ročno
|
||||
Float,AV_HIST_Occ,Histereza ogrevanja - dnevni režim
|
||||
Float,AV_HIST_Stby,Histereza ogrevanja - režim pripravljenost
|
||||
Float,AV_HIST_UnOcc,Histereza ogrevanja - nočni režim
|
||||
Float,AV_Hitrost_ventilat_G,Dejanska hitrost ventilatorja - GR
|
||||
Float,AV_Hitrost_ventilator,Dejanska hitrost ventilatorja - HL
|
||||
Float,AV_Hitrost_ventilator,Dejanska hitrost ventilatorja
|
||||
Float,AV_Nagib_zaluzij,Dejanski kot zaluzij
|
||||
Float,AV_Odpiranje_CNS_1,Odpiranje kupol preko CNS- Stikalo
|
||||
Float,AV_Odpiranje_CNS_1,Odpiranje oken preko CNS- Stikalo
|
||||
Float,AV_Odpiranje_CNS_2,Odpiranje kupol preko CNS- Stikalo
|
||||
Float,AV_Odpiranje_CNS_2,Odpiranje oken preko CNS- Stikalo
|
||||
Float,AV_Odpiranje_CNS_3,Odpiranje kupol preko CNS- Stikalo
|
||||
Float,AV_Odpiranje_CNS_3,Odpiranje oken preko CNS- Stikalo
|
||||
Float,AV_Odpiranje_CNS_4,Odpiranje kupol preko CNS- Stikalo
|
||||
Float,AV_Odpiranje_CNS_4,Odpiranje oken preko CNS- Stikalo
|
||||
Float,AV_PID_P_I,Regulator tlaka: I-člen
|
||||
Float,AV_PID_P_OPT_Y,Regulator tlaka - Optimalna vr. izhoda
|
||||
Float,AV_PID_P_P,Regulator tlaka: P-člen
|
||||
Float,AV_Polozaj_kupola_1,"Trenutni položaj kupole: 0-zaprta, 1-odprta"
|
||||
Float,AV_Polozaj_kupola_2,"Trenutni položaj kupole: 0-zaprta, 1-odprta"
|
||||
Float,AV_Polozaj_kupola_3,"Trenutni položaj kupole: 0-zaprta, 1-odprta"
|
||||
Float,AV_Polozaj_kupola_4,"Trenutni položaj kupole: 0-zaprta, 1-odprta"
|
||||
Float,AV_Polozaj_okna_1,"Trenutni položaj okna: 0-zaprto, 1-odprto"
|
||||
Float,AV_Polozaj_okna_2,"Trenutni položaj okna: 0-zaprto, 1-odprto"
|
||||
Float,AV_Polozaj_okna_3,"Trenutni položaj okna: 0-zaprto, 1-odprto"
|
||||
Float,AV_Polozaj_okna_4,"Trenutni položaj okna: 0-zaprto, 1-odprto"
|
||||
Float,AV_Preklop_Lab_Uc,"Tip prostora: 0-Laboratorij, 1-Učilnica"
|
||||
Float,AV_Prisotnost,Prisotnost preko kartice
|
||||
Float,AV_SEQ_VRP_Y1,Nastavitev rampe - VRP Y1
|
||||
Float,AV_SEQ_VRP_Y2,Nastavitev rampe - VRP Y2
|
||||
Float,AV_SP_Hist_Izklopa,Histereza izklopa GR in HL
|
||||
Float,AV_SP_Hist_Izklopa_2,Histereza izklopa GR in HL - K.KL.03.11
|
||||
Float,AV_SP_Hist_Izklopa_2,Histereza izklopa GR in HL - R.KL.03.06
|
||||
Float,AV_SP_Hist_Izklopa_2,Histereza izklopa GR in HL - X.KL.03.02
|
||||
Float,AV_SP_Ppr,Želen tlak v prostoru
|
||||
Float,AV_SP_Prezrac_vkl,Nastavitev pretoka VRP - Prezrac. vkl
|
||||
Float,AV_SP_Qpr,Želena kvaliteta zraka v prostoru
|
||||
Float,AV_SP_Rezim_Urnik,Nastavitev dnevnega režima - Urnik
|
||||
Float,AV_SP_Tpr,Želena temperatura prostora
|
||||
Float,AV_SP_Tpr_Max,Maksimalna želena temp. prostora
|
||||
Float,AV_SP_Tpr_Max_1,Maksimalna želena temp. prostora-1
|
||||
Float,AV_SP_Tpr_Max_2,Maksimalna želena temp. prostora - K.KL.03.11
|
||||
Float,AV_SP_Tpr_Max_2,Maksimalna želena temp. prostora - R.KL.03.06
|
||||
Float,AV_SP_Tpr_Max_2,Maksimalna želena temp. prostora - X.KL.03.02
|
||||
Float,AV_SP_Tpr_Max_2,Maksimalna želena temp. prostora-2
|
||||
Float,AV_SP_Tpr_Min,Minimalna želena temp. prostora
|
||||
Float,AV_SP_Tpr_Min_2,Minimalna želena temp. prostora - K.KL.03.11
|
||||
Float,AV_SP_Tpr_Min_2,Minimalna želena temp. prostora - R.KL.03.06
|
||||
Float,AV_SP_Tpr_Min_2,Minimalna želena temp. prostora - X.KL.03.02
|
||||
Float,AV_SP_Tpr_Odmik,Želena temperatura prostora - Odmik
|
||||
Float,AV_SP_Tvp,Želena temperatura vtočni zrak
|
||||
Float,AV_SP_Tvp_2,Želena temperatura vtočni zrak 2
|
||||
Float,AV_SP_Tvp_3,Želena temperatura vtočni zrak 3
|
||||
Float,AV_SP_Zaluzije_AVTO,Nastavitev kota žaluzij Avtomatsko
|
||||
Float,AV_Sp_Tpr_Aktivna,Aktivna želena temp. prostora
|
||||
Float,AV_Sp_Tpr_Dejanska,Dejanska želena temp. prostora
|
||||
Float,AV_Stikalo_ventilator,Položaj stikala ventialtorja A/0/1/2/3
|
||||
Float,AV_Temp_Rezim,Temperaturni režim - GR/HL/IZKL
|
||||
Float,AV_Temp_prostora,Temperatura prostora
|
||||
Float,AV_Temp_prostora_1,Temperatura prostora 1
|
||||
Float,AV_Temp_prostora_2,Temperatura prostora 2
|
||||
Float,AV_Temp_prostora_povpr,Temperatura prostora - povprečna
|
||||
Float,AV_VRP_Prekl_Min_Max,Vklop povečanega prezračevanja
|
||||
Float,AV_VRP_SP_Max,Nastavitev povečanega prezračevanja
|
||||
Float,AV_VRP_SP_Min,Nastavitev zmanjšanega prezračevanja
|
||||
Float,AV_Vlaga_prostora_1,Vlaga prostora 1
|
||||
Float,AV_Vlaga_prostora_2,Vlaga prostora 2
|
||||
Boolean,BI_DI1_Ogr_zlebov_nap,Ogrevanje žlebov - napaka
|
||||
Boolean,BI_DI1_Pozar,Požar
|
||||
Boolean,BI_DI1_Pozar_1,Požar cona TR13/1
|
||||
Boolean,BI_DI2_Ogr_zlebov_del,Ogrevanje žlebov - delovanje
|
||||
Boolean,BI_DI2_Pozar_2,Požar cona TR12/4
|
||||
Boolean,BI_DI2_Tipka_Pozar_R,Tipka požar - Ročno
|
||||
Float,BI_DI3_Okno,Okensko stikalo
|
||||
Boolean,BI_DI3_Pozar,Požar
|
||||
Boolean,BI_DI3_Pozar_3,Požar cona TR12/2
|
||||
Boolean,BI_DI4_Tehnicni_Plini,Signal koncentracije tehničnih plinov
|
||||
Boolean,BI_DI4_Tipka_Pozar_R,Tipka požar - Ročno
|
||||
Boolean,BO_DO1_Vkl_Prezrac,Vklop hlajenja v prostoru
|
||||
Boolean,BO_DO1_Vkl_Prezrac_1,Vklop hlajenja v prostoru-1
|
||||
Boolean,BO_DO2_Vkl_Kalorifer,Vklop gretja v prostoru
|
||||
Boolean,BO_DO2_Vkl_Prezrac_2,Vklop hlajenja v prostoru-2
|
||||
Boolean,BO_DO3_Vkl_Prezrac,Vklop hlajenja v prostoru - K.KL.03.11
|
||||
Boolean,BO_DO3_Vkl_Prezrac,Vklop hlajenja v prostoru - R.KL.03.06
|
||||
Boolean,BO_DO3_Vkl_Prezrac,Vklop hlajenja v prostoru - X.KL.03.02
|
||||
Boolean,BO_DO4_Vkl_Kalorifer,Vklop gretja v prostoru - K.KL.03.11
|
||||
Boolean,BO_DO4_Vkl_Kalorifer,Vklop gretja v prostoru - R.KL.03.06
|
||||
Boolean,BO_DO4_Vkl_Kalorifer,Vklop gretja v prostoru - X.KL.03.02
|
||||
Boolean,BO_DO6_Prezrac_vkl,Signal - Prezračevanje vključeno
|
||||
Boolean,BO_DO7_Prezrac_vkl,Signal - Prezračevanje vključeno
|
||||
Boolean,BO_DO7_Prezrac_vkl,Vklop prezračevanja
|
||||
Boolean,BO_DO8_Prezrac_vkl,Signal - Prezračevanje vključeno
|
||||
Boolean,BV_Izklop_FC_iz_CNS,Izklop hitrosti FC iz CNS
|
||||
Boolean,BV_Ni_medija_v_HP,Ni hladilnega medija v HP
|
||||
Boolean,BV_Ni_medija_v_TP,Ni grelnega medija v TP
|
|
Loading…
Reference in a new issue