diff --git a/recipes-app/iot2050-eio-manager/README.md b/recipes-app/iot2050-eio-manager/README.md new file mode 100644 index 000000000..0a5ef3759 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/README.md @@ -0,0 +1,24 @@ +# IOT2050 Extended IO Subsystem + +The IOT2050 Extended IO (EIO) subsystem is for managing the extended +IOs, currently the PLC1200 signal modules. + +This subsystem is only valid on IOT2050-SM variant at the moment. + +The core is a RPC service implemented with the help of gPRC. + +In addition: + - A FUSE service for loading the ext-io file system. + - A time syncing service for sync the system time to extended IO + controller. + - A cli tool for communicating with the gRPC server: + - Deploy/Retrieve extended IO configurations + - Update firmware for the extended IO controller or modules. + - A firmware updating monitoring service for notify updating while + firmware does not match. + +## Regenerate the gRPC python modules if proto file changes: + +```shell +python3 -m grpc_tools.protoc -I. --python_out=. --pyi_out=. --grpc_python_out=. gRPC/EIOManager/iot2050-eio.proto +``` diff --git a/recipes-app/iot2050-eio-manager/files/bin/firmware-version b/recipes-app/iot2050-eio-manager/files/bin/firmware-version new file mode 100644 index 000000000..47638f5a5 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/bin/firmware-version @@ -0,0 +1,4 @@ +{ + "version": "L00.00.00.12-0-ged53b19", + "sha1sum":"93dc2baadd6e74ffc5e74332625881ae6974879c" +} diff --git a/recipes-app/iot2050-eio-manager/files/bin/iot2050-ext-io b/recipes-app/iot2050-eio-manager/files/bin/iot2050-ext-io new file mode 100644 index 000000000..2181271b2 Binary files /dev/null and b/recipes-app/iot2050-eio-manager/files/bin/iot2050-ext-io differ diff --git a/recipes-app/iot2050-eio-manager/files/bin/map3-fw.bin b/recipes-app/iot2050-eio-manager/files/bin/map3-fw.bin new file mode 100644 index 000000000..3adc11432 Binary files /dev/null and b/recipes-app/iot2050-eio-manager/files/bin/map3-fw.bin differ diff --git a/recipes-app/iot2050-eio-manager/files/config-schema/schema-na.yaml b/recipes-app/iot2050-eio-manager/files/config-schema/schema-na.yaml new file mode 100644 index 000000000..fcdf7c17d --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-schema/schema-na.yaml @@ -0,0 +1,20 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +$schema: 'https://json-schema.org/draft/2020-12/schema' +$id: 'https://iot2050-sm/sm-na' + +type: object +properties: + description: + type: string + mlfb: + const: "NA" + +required: + - mlfb + +unevaluatedProperties: false diff --git a/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm-config.yaml b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm-config.yaml new file mode 100644 index 000000000..63c607782 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm-config.yaml @@ -0,0 +1,93 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +$schema: 'https://json-schema.org/draft/2020-12/schema' +$id: 'https://iot2050-sm/sm-config' + +type: object +properties: + slot1: + $ref: "#/$defs/slot" + slot2: + $ref: "#/$defs/slot" + slot3: + $ref: "#/$defs/slot" + slot4: + $ref: "#/$defs/slot" + slot5: + $ref: "#/$defs/slot" + slot6: + $ref: "#/$defs/slot" + +required: + - slot1 + - slot2 + - slot3 + - slot4 + - slot5 + - slot6 + +$defs: + slot: + type: object + properties: + mlfb: + enum: + - 6ES7647-0CM00-1AA2 + - 6ES7223-1QH32-0XB0 + - 6ES7231-4HF32-0XB0 + - 6ES7231-5PD32-0XB0 + - 6ES7231-5PF32-0XB0 + - 6ES7238-5XA32-0XB0 + - NA + + allOf: + + - if: + properties: + mlfb: + const: 6ES7647-0CM00-1AA2 + then: + $ref: https://iot2050-sm/sm-sens-di + + - if: + properties: + mlfb: + const: 6ES7223-1QH32-0XB0 + then: + $ref: https://iot2050-sm/sm1223-ac-rly + + - if: + properties: + mlfb: + const: 6ES7231-4HF32-0XB0 + then: + $ref: https://iot2050-sm/sm1231-ai + + - if: + properties: + mlfb: + enum: + - 6ES7231-5PD32-0XB0 + - 6ES7231-5PF32-0XB0 + then: + $ref: https://iot2050-sm/sm1231-rtd + + - if: + properties: + mlfb: + const: 6ES7238-5XA32-0XB0 + then: + $ref: https://iot2050-sm/sm1238-em-480vac + + - if: + properties: + mlfb: + const: NA + then: + $ref: https://iot2050-sm/sm-na + +unevaluatedProperties: false diff --git a/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm-sens-di.yaml b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm-sens-di.yaml new file mode 100644 index 000000000..b00665309 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm-sens-di.yaml @@ -0,0 +1,20 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +$schema: 'https://json-schema.org/draft/2020-12/schema' +$id: 'https://iot2050-sm/sm-sens-di' + +type: object +properties: + description: + type: string + mlfb: + const: "6ES7647-0CM00-1AA2" + +required: + - mlfb + +unevaluatedProperties: false diff --git a/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1223-ac-rly.yaml b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1223-ac-rly.yaml new file mode 100644 index 000000000..9a2a38c68 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1223-ac-rly.yaml @@ -0,0 +1,74 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +$schema: 'https://json-schema.org/draft/2020-12/schema' +$id: 'https://iot2050-sm/sm1223-ac-rly' + +type: object +properties: + description: + type: string + mlfb: + const: "6ES7223-1QH32-0XB0" + di: + type: object + properties: + ch0_3_delay_time: + enum: [2, 3, 4, 5, 6, 7, 9] + ch4_7_delay_time: + enum: [2, 3, 4, 5, 6, 7, 9] + required: + - ch0_3_delay_time + - ch4_7_delay_time + unevaluatedProperties: false + dq: + type: object + properties: + behavior_with_OD: + enum: [2, 3] + ch0: + $ref: "#/$defs/channel" + ch1: + $ref: "#/$defs/channel" + ch2: + $ref: "#/$defs/channel" + ch3: + $ref: "#/$defs/channel" + ch4: + $ref: "#/$defs/channel" + ch5: + $ref: "#/$defs/channel" + ch6: + $ref: "#/$defs/channel" + ch7: + $ref: "#/$defs/channel" + + if: + properties: + behavior_with_OD: + const: 3 + then: + required: [ch0, ch1, ch2, ch3, ch4, ch5, ch6, ch7] + + required: + - behavior_with_OD + unevaluatedProperties: false + +required: + - mlfb + - di + - dq + +$defs: + channel: + type: object + properties: + substitute: + enum: [0, 1] + required: + - substitute + +unevaluatedProperties: false diff --git a/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1231-ai.yaml b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1231-ai.yaml new file mode 100644 index 000000000..eebc22e31 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1231-ai.yaml @@ -0,0 +1,76 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +$schema: 'https://json-schema.org/draft/2020-12/schema' +$id: 'https://iot2050-sm/sm1231-ai' + +type: object +properties: + description: + type: string + mlfb: + const: "6ES7231-4HF32-0XB0" + power_alarm: + type: boolean + integ_time: + enum: [0, 1, 2, 3] + ch0: + $ref: "#/$defs/channel" + ch1: + $ref: "#/$defs/channel" + ch2: + $ref: "#/$defs/channel" + ch3: + $ref: "#/$defs/channel" + ch4: + $ref: "#/$defs/channel" + ch5: + $ref: "#/$defs/channel" + ch6: + $ref: "#/$defs/channel" + ch7: + $ref: "#/$defs/channel" + +required: [mlfb, power_alarm, integ_time, ch0, ch1, ch2, ch3, ch4, ch5, ch6, ch7] + +$defs: + channel: + type: object + properties: + type: + enum: [1, 3] + smooth: + enum: [0, 1, 2, 3] + open_wire_alarm: + type: boolean + overflow_alarm: + type: boolean + underflow_alarm: + type: boolean + + if: + properties: + type: + const: 1 + then: + properties: + range: + enum: [7, 8, 9] + else: + properties: + range: + enum: [2, 3] + + required: + - type + - range + - smooth + - open_wire_alarm + - overflow_alarm + - underflow_alarm + unevaluatedProperties: false + +unevaluatedProperties: false diff --git a/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1231-rtd.yaml b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1231-rtd.yaml new file mode 100644 index 000000000..8798c917f --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1231-rtd.yaml @@ -0,0 +1,220 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +$schema: 'https://json-schema.org/draft/2020-12/schema' +$id: 'https://iot2050-sm/sm1231-rtd' + +type: object +properties: + description: + type: string + mlfb: + enum: + - "6ES7231-5PD32-0XB0" + - "6ES7231-5PF32-0XB0" + power_alarm: + type: boolean + integ_time: + enum: [0, 1, 2 ,3] + ch0: + $ref: "#/$defs/channel" + ch1: + $ref: "#/$defs/channel" + ch2: + $ref: "#/$defs/channel" + ch3: + $ref: "#/$defs/channel" + +required: + - mlfb + - power_alarm + - integ_time + - ch0 + - ch1 + - ch2 + - ch3 + +if: + properties: + mlfb: + const: 6ES7231-5PF32-0XB0 +then: + properties: + ch4: + $ref: "#/$defs/channel" + ch5: + $ref: "#/$defs/channel" + ch6: + $ref: "#/$defs/channel" + ch7: + $ref: "#/$defs/channel" + required: + - ch4 + - ch5 + - ch6 + - ch7 + +$defs: + channel: + type: object + properties: + type: + enum: [0, 4, 5, 6, 7, 8, 9] + range: + enum: [0, 1, 2, 3, 4, 5, 6, 11, 12, 15, 16, 18, 20, 22, 24, 26, 28] + smooth: + enum: [0, 1, 2 ,3] + open_wire_alarm: + type: boolean + overflow_alarm: + type: boolean + temper_coeff: + enum: [0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13] + temper_unit: + enum: [0, 1] + underflow_alarm: + type: boolean + + allOf: + - if: + properties: + type: + const: 0 + then: + properties: + range: + type: integer + + - if: + properties: + type: + enum: [4, 5, 6] + then: + properties: + range: + enum: [1, 2, 3] + + - if: + properties: + type: + enum: [7, 8, 9] + then: + properties: + range: + enum: [0, 2, 3, 4, 5, 6, 11, 12, 15, 16, 18, 20, 22, 24, 26, 28] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + const: 0 + then: + properties: + temper_coeff: + const: 0 + + - if: + properties: + type: + enum: [7, 8, 9] + range: + enum: [2, 4] + then: + properties: + temper_coeff: + enum: [0, 1, 2, 3, 5] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + const: 3 + then: + properties: + temper_coeff: + enum: [7, 8, 9] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + enum: [5, 11] + then: + properties: + temper_coeff: + enum: [0, 1, 2, 3] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + enum: [6, 12, 16, 18] + then: + properties: + temper_coeff: + enum: [8, 9] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + const: 15 + then: + properties: + temper_coeff: + enum: [11, 12, 13] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + enum: [20, 22] + then: + properties: + temper_coeff: + enum: [0, 5] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + enum: [24, 26] + then: + properties: + temper_coeff: + enum: [11, 13] + + - if: + properties: + type: + enum: [7, 8, 9] + range: + const: 28 + then: + properties: + temper_coeff: + const: 10 + + required: + - type + - range + - temper_coeff + - temper_unit + - smooth + - open_wire_alarm + - overflow_alarm + - underflow_alarm + + unevaluatedProperties: false + +unevaluatedProperties: false diff --git a/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1238-em-480vac.yaml b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1238-em-480vac.yaml new file mode 100644 index 000000000..b6384fa38 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-schema/schema-sm1238-em-480vac.yaml @@ -0,0 +1,285 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +$schema: 'https://json-schema.org/draft/2020-12/schema' +$id: 'https://iot2050-sm/sm1238-em-480vac' + +type: object +properties: + + description: + type: string + + mlfb: + const: "6ES7238-5XA32-0XB0" + + module_version: + enum: [32, 112, 224, 225, 226, 227] + + con_type: + enum: [0x00, 0x0B, 0x0C, 0x0E, 0x0F, 0x10] + + range: + enum: [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C] + + line_freq: + enum: [1, 2] + + period_meters: + enum: [0, 1, 2, 3, 4, 5] + + meter_gate: + type: boolean + + line_vol_tol: + enum: [ + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, + 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, + 0x31, 0x32] + + min_max_cal: + type: boolean + + diag_line_vol: + type: boolean + + ch0: + $ref: "#/$defs/channel" + + ch1: + $ref: "#/$defs/channel" + + ch2: + $ref: "#/$defs/channel" + +allOf: + + # 3P mode for 112 bytes I / 12 bytes O + - if: + properties: + module_version: + const: 112 + con_type: + enum: [0x00, 0x0C, 0x0E, 0x0F] + then: + properties: + data_variant: + enum: [ + 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, + 0xF6, 0xF5, 0xF0, 0xEF, 0xE3, 0xE2, 0xE1, 0xE0, + 0x9F, 0x9E, 0x9D, 0x9C, 0x9B, 0x9A] + + # 1P mode for 112 bytes I / 12 bytes O + - if: + properties: + module_version: + const: 112 + con_type: + const: 0x0B + then: + properties: + data_variant: + enum: [ + 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF0, + 0xEF, 0xE3, 0xE2, 0xE1, 0xE0, 0x9F, 0x9E] + + # 2P mode for 112 bytes I / 12 bytes O + - if: + properties: + module_version: + const: 112 + con_type: + const: 0x10 + then: + properties: + data_variant: + enum: [ + 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, + 0xF5, 0xF0, 0xEF, 0xE3, 0xE2, 0xE1, 0xE0, 0x9F, + 0x9E, 0x9D, 0x9C] + + # 3P mode for 32 bytes I / 12 bytes O + - if: + properties: + module_version: + const: 32 + con_type: + enum: [0x00, 0x0C, 0x0E, 0x0F] + then: + properties: + data_variant: + enum: [ + 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, + 0xF6, 0xF5, 0xF0, 0xEF, 0xE2, 0xE1, 0xE0, 0x9F, + 0x9E, 0x9D, 0x9C, 0x9B, 0x9A] + + # 1P mode for 32 bytes I / 12 bytes O + - if: + properties: + module_version: + const: 32 + con_type: + const: 0x0B + then: + properties: + data_variant: + enum: [ + 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF0, + 0xEF, 0xE2, 0xE1, 0xE0, 0x9F, 0x9E] + + # 2P mode for 32 bytes I / 12 bytes O + - if: + properties: + module_version: + const: 32 + con_type: + const: 0x10 + then: + properties: + data_variant: + enum: [ + 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8, 0xF7, + 0xF5, 0xF0, 0xEF, 0xE2, 0xE1, 0xE0, 0x9F, 0x9E, + 0x9D, 0x9C] + + # EE@Industry measured data e0 + - if: + properties: + module_version: + const: 224 + then: + properties: + data_variant: + const: 0xE0 + + # EE@Industry measured data e1 + - if: + properties: + module_version: + const: 225 + then: + properties: + data_variant: + const: 0xE1 + + # EE@Industry measured data e2 + - if: + properties: + module_version: + const: 226 + then: + properties: + data_variant: + const: 0xE2 + + # EE@Industry measured data e3 + - if: + properties: + module_version: + const: 227 + then: + properties: + data_variant: + const: 0xE3 + +required: + - mlfb + - module_version + - con_type + - range + - line_freq + - period_meters + - meter_gate + - line_vol_tol + - min_max_cal + - diag_line_vol + - data_variant + - ch0 + - ch1 + - ch2 + +$defs: + channel: + type: object + properties: + diag_over_cur: + type: boolean + diag_over_cal: + type: boolean + diag_ll_vol: + type: boolean + diag_under_vol: + type: boolean + diag_over_vol: + type: boolean + over_cur_tol_val: + type: integer + minimum: 10 + maximum: 100 + over_cur_tol_time: + type: integer + minimum: 0 + maximum: 60000 + ct_primary_cur: + type: integer + minimum: 1 + maximum: 99999 + en_gate_cir_hour_meter: + type: boolean + ct_second_cur: + enum: [0, 2] + act_hour_meter: + type: boolean + re_cur_dir: + type: boolean + ll_cur_measure: + type: integer + minimum: 2 + maximum: 250 + vt_second_vol: + type: integer + minimum: 1 + maximum: 99999 + vt_prim_vol: + type: integer + minimum: 1 + maximum: 99999 + required: + - diag_over_cur + - diag_over_cal + - diag_ll_vol + - diag_under_vol + - diag_over_vol + - over_cur_tol_val + - over_cur_tol_time + - ct_primary_cur + - en_gate_cir_hour_meter + - ct_second_cur + - act_hour_meter + - re_cur_dir + - ll_cur_measure + - vt_second_vol + - vt_prim_vol + + allOf: + - if: + properties: + con_type: + const: 0x0F + then: + properties: + ct_second_cur: + const: 0 + + unevaluatedProperties: false + +unevaluatedProperties: false diff --git a/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7223-1QH32-0XB0.yaml b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7223-1QH32-0XB0.yaml new file mode 100644 index 000000000..4ee713277 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7223-1QH32-0XB0.yaml @@ -0,0 +1,77 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +description: | + SM1223-AC/RLY (8-channel digital input and 8-channel output module) + + Configuration Items: + + 0. MLFB + + Must be "6ES7223-1QH32-0XB0" + + 1. Digital inputs + + - di.ch0_3_delay_time: + Input delay time of di channel 0 ~ channel 3 + + value options: + - 2: 0.2 msec + - 3: 0.4 msec + - 4: 0.8 msec + - 5: 1.6 msec + - 6: 3.2 msec + - 7: 6.4 msec + - 9: 12.8 msec + + - di.ch4_7_delay_time: + input delay time of di channel 4 ~ channel 7 + + value options: same as di.ch0_3_delay_time + + 2. Digital outputs + + - dq.behavior_with_OD: + All dq channels behavior with OD + + value options: + - 2: hold last value + - 3: use substitute + + - dq.ch[0-7]: + + - substitute: + Substitute value for the channel + + value options: + - 0: use 0 as the substitute value for this channel + - 1: use 1 as the substitute value for this channel + +mlfb: "6ES7223-1QH32-0XB0" + +di: + ch0_3_delay_time: 9 + ch4_7_delay_time: 9 + +dq: + behavior_with_OD: 3 + ch0: + substitute: 0 + ch1: + substitute: 0 + ch2: + substitute: 0 + ch3: + substitute: 0 + ch4: + substitute: 0 + ch5: + substitute: 0 + ch6: + substitute: 0 + ch7: + substitute: 0 diff --git a/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-4HF32-0XB0.yaml b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-4HF32-0XB0.yaml new file mode 100644 index 000000000..8e5da6b14 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-4HF32-0XB0.yaml @@ -0,0 +1,153 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +description: | + SM1231-AI (8-channel analog measurement module for measuring current or voltage) + + + Configuration Items: + + 0. MLFB + + Must be "6ES7231-4HF32-0XB0" + + 1. Per module configuration + + - power_alarm: + user power alarm of all channels + + value options: + - true: enabled + - false: disabled + + - integ_time: + integration time + + value options: + - 0: 400 Hz + - 1: 60 Hz + - 2: 50 Hz + - 3: 10 Hz + + 2. Per channel configuration + + - ch[0-7].type: + measurement type + + value options: + - 1: voltage + - 3: 2 wire current + + - ch[0-7].range: + measurement range + + value options: + - 2: 0 - 20ma + - 3: 4 - 20ma + - 7: +/- 2.5v + - 8: +/- 5v + - 9: +/- 10v + + - ch[0-7].smooth: + smoothing + + value options: + - 0: 1X rejection period + - 1: 4X rejection period + - 2: 16X rejection period + - 3: 32X rejection period + + - ch[0-7].open_wire_alarm: + open wire alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].overflow_alarm: + overflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].underflow_alarm: + underflow alarm + + value options: + - true: enabled + - false: disabled + +mlfb: "6ES7231-4HF32-0XB0" + +power_alarm: true + +integ_time: 2 + +ch0: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +ch1: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +ch2: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +ch3: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +ch4: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +ch5: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +ch6: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +ch7: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true diff --git a/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-5PD32-0XB0.yaml b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-5PD32-0XB0.yaml new file mode 100644 index 000000000..59b4498ca --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-5PD32-0XB0.yaml @@ -0,0 +1,180 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +description: | + SM1231-RTD (4 or 8-channel analog measurement module for measuring thermistor) + + Configuration Items: + + 0. MLFB + + Must be one of "6ES7231-5PD32-0XB0" or "6ES7231-5PF32-0XB0" + + 1. Per module configuration + + - power_alarm: + user power alarm of all channels + + value options: + - true: enabled + - false: disabled + + - integ_time: + integration time + + value options: + - 0: 400 Hz + - 1: 60 Hz + - 2: 50 Hz + - 3: 10 Hz + + 2. Per channel configuration + + 4 channels (0-3) if MLFB == 6ES7231-5PD32-0XB0, 8 channels (0-7) if + MLFB == 6ES7231-5PF32-0XB0. + + - ch[0-3].type: + measurement type + + value options: + - 0: deactivated + - 4: Resistance (4 wire) + - 5: Resistance (3 wire) + - 6: Resistance (2 wire) + - 7: Thermal Resistance (linear, 4 wire) + - 8: Thermal Resistance (linear, 3 wire) + - 9: Thermal Resistance (linear, 2 wire) + + - ch[0-3].range: + measurement range + + value options: + if type == Resistance (one of 4, 5, 6): + - 0: 48 ohms + - 1: 150 ohms + - 2: 300 ohms + - 3: 600 ohms + - 4: 3000 ohms + + if type == Thermal Resistance (one of 7, 8, 9): + - 0: Pt_100_Climatic_Range + - 2: Pt_100_Standard_Range + - 3: Ni_100_Standard_Range + - 4: Pt_500_S_R + - 5: Pt_1000_S_R + - 6: Ni_1000_S_R + - 11: Pt_200_S_R + - 12: Ni_120_S_R + - 15: Cu_10_S_R + - 16: Ni_200_S_R + - 18: Ni_500_S_R + - 20: Pt_10_S_R + - 22: Pt_50_S_R + - 24: Cu_50_S_R + - 26: Cu_100_S_R + - 28: Lg_Ni_1000_S_R + + + - ch[0-3].temper_coeff: + temperature coefficient + + value options: + - 0: Pt 0.00385055 Ohm/ (DIN EN 60751) + - 1: Pt 0.003916 Ohm/ (USA, NBS) + - 2: Pt 0.003902 Ohm/ + - 3: Pt 0.003920 Ohm/ + - 5: Pt 0.003910 Ohm/ (GOST 6651, Russia 1994) + - 7: Ni 0.006170 Ohm/ (GOST 6651, Russia 1994) + - 8: Ni 0.006180 Ohm/ + - 9: Ni 0.006720 Ohm/ + - 10: LG-Ni 0.005000 Ohm/ + - 11: Cu 0.00426 Ohm/ (GOST 6651, Russia 1994) + - 12: Cu 0.00427 Ohm/ + - 13: Cu 0.00428 Ohm/ (GOST 6651, Russia 1994) + + - ch[0-3].smooth: + smoothing + + value options: + - 0: 1X rejection period + - 1: 4X rejection period + - 2: 16X rejection period + - 3: 32X rejection period + + - ch[0-3].temper_unit: + temperature units + + value options: + - 0: Celcius + - 1: Fahrenheit + + - ch[0-3].overflow_alarm: + overflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-3].underflow_alarm: + underflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-3].open_wire_alarm: + open wire alarm + + value options: + - true: enabled + - false: disabled + +mlfb: "6ES7231-5PD32-0XB0" + +power_alarm: true + +integ_time: 2 + +ch0: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch1: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch2: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch3: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true diff --git a/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-5PF32-0XB0.yaml b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-5PF32-0XB0.yaml new file mode 100644 index 000000000..57898700e --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7231-5PF32-0XB0.yaml @@ -0,0 +1,220 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +description: | + SM1231-RTD (4 or 8-channel analog measurement module for measuring thermistor) + + Configuration Items: + + 0. MLFB + + Must be one of "6ES7231-5PD32-0XB0" or "6ES7231-5PF32-0XB0" + + 1. Per module configuration + + - power_alarm: + user power alarm of all channels + + value options: + - true: enabled + - false: disabled + + - integ_time: + integration time + + value options: + - 0: 400 Hz + - 1: 60 Hz + - 2: 50 Hz + - 3: 10 Hz + + 2. Per channel configuration + + 4 channels (0-3) if MLFB == 6ES7231-5PD32-0XB0, 8 channels (0-7) if + MLFB == 6ES7231-5PF32-0XB0. + + - ch[0-7].type: + measurement type + + value options: + - 0: deactivated + - 4: Resistance (4 wire) + - 5: Resistance (3 wire) + - 6: Resistance (2 wire) + - 7: Thermal Resistance (linear, 4 wire) + - 8: Thermal Resistance (linear, 3 wire) + - 9: Thermal Resistance (linear, 2 wire) + + - ch[0-7].range: + measurement range + + value options: + if type == Resistance (one of 4, 5, 6): + - 0: 48 ohms + - 1: 150 ohms + - 2: 300 ohms + - 3: 600 ohms + - 4: 3000 ohms + + if type == Thermal Resistance (one of 7, 8, 9): + - 0: Pt_100_Climatic_Range + - 2: Pt_100_Standard_Range + - 3: Ni_100_Standard_Range + - 4: Pt_500_S_R + - 5: Pt_1000_S_R + - 6: Ni_1000_S_R + - 11: Pt_200_S_R + - 12: Ni_120_S_R + - 15: Cu_10_S_R + - 16: Ni_200_S_R + - 18: Ni_500_S_R + - 20: Pt_10_S_R + - 22: Pt_50_S_R + - 24: Cu_50_S_R + - 26: Cu_100_S_R + - 28: Lg_Ni_1000_S_R + + + - ch[0-7].temper_coeff: + temperature coefficient + + value options: + - 0: Pt 0.00385055 Ohm/ (DIN EN 60751) + - 1: Pt 0.003916 Ohm/ (USA, NBS) + - 2: Pt 0.003902 Ohm/ + - 3: Pt 0.003920 Ohm/ + - 5: Pt 0.003910 Ohm/ (GOST 6651, Russia 1994) + - 7: Ni 0.006170 Ohm/ (GOST 6651, Russia 1994) + - 8: Ni 0.006180 Ohm/ + - 9: Ni 0.006720 Ohm/ + - 10: LG-Ni 0.005000 Ohm/ + - 11: Cu 0.00426 Ohm/ (GOST 6651, Russia 1994) + - 12: Cu 0.00427 Ohm/ + - 13: Cu 0.00428 Ohm/ (GOST 6651, Russia 1994) + + - ch[0-7].smooth: + smoothing + + value options: + - 0: 1X rejection period + - 1: 4X rejection period + - 2: 16X rejection period + - 3: 32X rejection period + + - ch[0-7].temper_unit: + temperature units + + value options: + - 0: Celcius + - 1: Fahrenheit + + - ch[0-7].overflow_alarm: + overflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].underflow_alarm: + underflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].open_wire_alarm: + open wire alarm + + value options: + - true: enabled + - false: disabled + +mlfb: "6ES7231-5PF32-0XB0" + +power_alarm: true + +integ_time: 2 + +ch0: + type: 7 + range: 3 + temper_coeff: 7 + smooth: 1 + temper_unit: 1 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch1: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch2: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch3: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch4: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch5: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch6: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +ch7: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true diff --git a/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7238-5XA32-0XB0.yaml b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7238-5XA32-0XB0.yaml new file mode 100644 index 000000000..7439b1d0e --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7238-5XA32-0XB0.yaml @@ -0,0 +1,278 @@ + + + +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +description: | + SM1238 Energy Meter 480VAC + + Configuration Items: + + 0. MLFB + + Must be "6ES7238-5XA32-0XB0" + + 1. Per module configuration + + - module_version: + Module Version + + value options: + - 32: 32 bytes I/ 12 bytes O + - 112: 112 bytes I/ 12 bytes O + - 224: EE@Industry measurement data profiles e0 + - 225: EE@Industry measurement data profiles e1 + - 226: EE@Industry measurement data profiles e2 + - 227: EE@Industry measurement data profiles e3 + + - con_type: + Connection type of the phases + + value options: + - 0x00: Disabled + - 0x0B: 1P2W 1-phase measurement, two conductors, with or without + voltage transformers, with one current transformer + - 0x0C: 3P4W 3-phase, 4-wire, symmetrical and asymmetrical load, with or + without voltage transformers, with 3 current transformers + - 0x0E: 3P4W1 3-phase, 1-wire + - 0x0F: 3x1P2W 3x single phase, two conductors, with or without voltage + transformers, with three current transformers + - 0x10: 2P3W 2-phase, 3-wire, assymetrical load, without voltage + transformers, with two current transformers + + - range: + Voltage measuring range of the power supply + + value options: + - 0x01: 100 V + - 0x02: 110 V + - 0x03: 115 V + - 0x04: 120 V + - 0x05: 127 V + - 0x06: 190 V + - 0x07: 200 V + - 0x08: 208 V + - 0x09: 220 V + - 0x0A: 230 V + - 0x0B: 240 V + - 0x0C: 277 V + + - line_freq: + Line frequency of the power supply + + value options: + - 1: 60 Hz + - 2: 50 Hz + + - period_meters: + End value energy meter + + value options: + - 0x00: No end value (count indefinitely) + - 0x01: Count periodically up to 10^3 + - 0x02: Count periodically up to 10^6 + - 0x03: Count periodically up to 10^9 + - 0x04: Count periodically up to 10^12 + - 0x05: Count periodically up to 10^15 + + - meter_gate: + Energy meter gate + + value options: + - false: Energy counter always counting + - true: Energy counter gate + + - line_vol_tol: + Line voltage tolerance + + value options: + - 0x01: 1% + - 0x02: 2% + - 0x03: 3% + - ... + - 0x32: 50% + + - min_max_cal: + Minimum and maximum value calculation + + value options: bool + + - diag_line_vol: + Line voltage diagnostics + + value options: bool + + - data_variant: + User data variant + + value options: + - 0xFE: Total power L1L2L3 (default) + - 0xFD: Active power L1L2L3 + - 0xFC: Reactive power L1L2L3 + - 0xFB: Apparent power L1L2L3 + - 0xFA: Basic measurement values L1L2L3 + - 0xF9: Total energy L1L2L3 + - 0xF8: Energy L1 + - 0xF7: Energy L2 + - 0xF6: Energy L3 + - 0xF5: Basic values 3-phase measurement L1L2L3 + - 0xF0: Quality values 3-phase measurement + - 0xEF: Energy measurement (periodical) overrange meter + - 0xE3: EE@Industry Measurement Data Profile e3 + - 0xE2: EE@Industry Measurement Data Profile e2 + - 0xE1: EE@Industry Measurement Data Profile e1 + - 0xE0: EE@Industry measurement Data Profile e0 + - 0x9F: Basic values Single Phase Measurement L1 + - 0x9E: Basic values Single Phase Measurement L1a + - 0x9D: Basic values Single Phase Measurement L2 + - 0x9C: Basic values Single Phase Measurement L2a + - 0x9B: Basic values Single Phase Measurement L3 + - 0x9A: Basic values Single Phase Measurement L3a + + 2. Per channel configuration + + - ch[0-2].diag_over_cur: + Enable diagnostic overflow current + + value options: bool + + - ch[0-2].diag_over_cal: + Enable diagnostic overflow calculation values + + value options: bool + + - ch[0-2].diag_ll_vol: + Enable diagnostic of low limit voltage + + value options: bool + + - ch[0-2].diag_under_vol: + Enable diagnostic underflow voltage + + value options: bool + + - ch[0-2].diag_over_vol: + Enable diagnostic overflow voltage + + value options: bool + + - ch[0-2].over_cur_tol_val: + Tolerance value secondary over-current, 0.1A + + value options: [10 ~ 100] + + - ch[0-2].over_cur_tol_time: + Tolerance time over-current, ms + + value options: [0 ~ 60000] + + - ch[0-2].ct_primary_cur: + Current transformer primary current, A + + value options: [1 ~ 99999] + + - ch[0-2].en_gate_cir_hour_meter: + Enable gate circuit hours meter + + value options: bool + + - ch[0-2].ct_second_cur: + Current transformer secondary current: (0: 1A, 2: 5A) + + value options: + - 0: 1 A + - 2: 5 A + + - ch[0-2].act_hour_meter: + Activate hours meter + + value options: bool + + - ch[0-2].re_cur_dir: + Reverse current direction + + value options: bool + + - ch[0-2].ll_cur_measure: + Low limit current measurement, mA + + value options: [2 ~ 250] + + - ch[0-2].vt_second_vol: + Voltage transformer secondary voltage, V + + value options: [1 ~ 500] + + - ch[0-2].vt_prim_vol: + Voltage transformer primary voltage, V + + value options: [1 ~ 999999] + +mlfb: "6ES7238-5XA32-0XB0" + +module_version: 32 +con_type: 0x0C +range: 0x0A +line_freq: 2 +period_meters: 0 +meter_gate: false +line_vol_tol: 0x0A +min_max_cal: false +diag_line_vol: false +data_variant: 0xFE + +ch0: + diag_over_cur: false + diag_over_cal: false + diag_ll_vol: false + diag_under_vol: false + diag_over_vol: false + over_cur_tol_val: 100 + over_cur_tol_time: 40000 + ct_primary_cur: 1 + en_gate_cir_hour_meter: false + ct_second_cur: 0 + act_hour_meter: false + re_cur_dir: false + ll_cur_measure: 50 + vt_second_vol: 230 + vt_prim_vol: 230 + +ch1: + diag_over_cur: false + diag_over_cal: false + diag_ll_vol: false + diag_under_vol: false + diag_over_vol: false + over_cur_tol_val: 100 + over_cur_tol_time: 40000 + ct_primary_cur: 1 + en_gate_cir_hour_meter: false + ct_second_cur: 0 + act_hour_meter: false + re_cur_dir: false + ll_cur_measure: 50 + vt_second_vol: 230 + vt_prim_vol: 230 + +ch2: + diag_over_cur: false + diag_over_cal: false + diag_ll_vol: false + diag_under_vol: false + diag_over_vol: false + over_cur_tol_val: 100 + over_cur_tol_time: 40000 + ct_primary_cur: 1 + en_gate_cir_hour_meter: false + ct_second_cur: 0 + act_hour_meter: false + re_cur_dir: false + ll_cur_measure: 50 + vt_second_vol: 230 + vt_prim_vol: 230 diff --git a/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7647-0CM00-1AA2.yaml b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7647-0CM00-1AA2.yaml new file mode 100644 index 000000000..b9701d47e --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-6ES7647-0CM00-1AA2.yaml @@ -0,0 +1,19 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +description: | + SM SENS DI module + + Configuration Items: + + 0. MLFB + + Must be "6ES7647-0CM00-1AA2" + + There is no configuration entry for this module other than mlfb. + +mlfb: "6ES7647-0CM00-1AA2" diff --git a/recipes-app/iot2050-eio-manager/files/config-template/mlfb-NA.yaml b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-NA.yaml new file mode 100644 index 000000000..2d9a0a20a --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/mlfb-NA.yaml @@ -0,0 +1,11 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +description: | + No module for this slot + +mlfb: "NA" diff --git a/recipes-app/iot2050-eio-manager/files/config-template/sm-config-example.yaml b/recipes-app/iot2050-eio-manager/files/config-template/sm-config-example.yaml new file mode 100644 index 000000000..30e427567 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/config-template/sm-config-example.yaml @@ -0,0 +1,894 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT +# +# This is an example config file for IOT2050-SM signal modules to demonstrate +# how to config the external signal modules via YAML config file +# +slot1: + description: | + SM1223-AC/RLY (8-channel digital input and 8-channel output module) + + Configuration Items: + + 0. MLFB + + Must be "6ES7223-1QH32-0XB0" + + 1. Digital inputs + + - di.ch0_3_delay_time: + Input delay time of di channel 0 ~ channel 3 + + value options: + - 2: 0.2 msec + - 3: 0.4 msec + - 4: 0.8 msec + - 5: 1.6 msec + - 6: 3.2 msec + - 7: 6.4 msec + - 9: 12.8 msec + + - di.ch4_7_delay_time: + input delay time of di channel 4 ~ channel 7 + + value options: same as di.ch0_3_delay_time + + 2. Digital outputs + + - dq.behavior_with_OD: + All dq channels behavior with OD + + value options: + - 2: hold last value + - 3: use substitute + + - dq.ch[0-7]: + + - substitute: + Substitute value for the channel + + value options: + - 0: use 0 as the substitute value for this channel + - 1: use 1 as the substitute value for this channel + + mlfb: "6ES7223-1QH32-0XB0" + + di: + ch0_3_delay_time: 9 + ch4_7_delay_time: 9 + + dq: + behavior_with_OD: 3 + ch0: + substitute: 0 + ch1: + substitute: 0 + ch2: + substitute: 0 + ch3: + substitute: 0 + ch4: + substitute: 0 + ch5: + substitute: 0 + ch6: + substitute: 0 + ch7: + substitute: 0 + +slot2: + description: | + SM1231-AI (8-channel analog measurement module for measuring current or voltage) + + + Configuration Items: + + 0. MLFB + + Must be "6ES7231-4HF32-0XB0" + + 1. Per module configuration + + - power_alarm: + user power alarm of all channels + + value options: + - true: enabled + - false: disabled + + - integ_time: + integration time + + value options: + - 0: 400 Hz + - 1: 60 Hz + - 2: 50 Hz + - 3: 10 Hz + + 2. Per channel configuration + + - ch[0-7].type: + measurement type + + value options: + - 1: voltage + - 3: 2 wire current + + - ch[0-7].range: + measurement range + + value options: + - 2: 0 - 20ma + - 3: 4 - 20ma + - 7: +/- 2.5v + - 8: +/- 5v + - 9: +/- 10v + + - ch[0-7].smooth: + smoothing + + value options: + - 0: 1X rejection period + - 1: 4X rejection period + - 2: 16X rejection period + - 3: 32X rejection period + + - ch[0-7].open_wire_alarm: + open wire alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].overflow_alarm: + overflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].underflow_alarm: + underflow alarm + + value options: + - true: enabled + - false: disabled + + mlfb: "6ES7231-4HF32-0XB0" + + power_alarm: true + + integ_time: 2 + + ch0: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + + ch1: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + + ch2: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + + ch3: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + + ch4: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + + ch5: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + + ch6: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + + ch7: + type: 1 + range: 9 + smooth: 1 + open_wire_alarm: false + overflow_alarm: true + underflow_alarm: true + +slot3: + description: | + SM1231-4RTD (4-channel analog measurement module for measuring thermistor) + + Configuration Items: + + 0. MLFB + + Must be "6ES7231-5PD32-0XB0" + + 1. Per module configuration + + - power_alarm: + user power alarm of all channels + + value options: + - true: enabled + - false: disabled + + - integ_time: + integration time + + value options: + - 0: 400 Hz + - 1: 60 Hz + - 2: 50 Hz + - 3: 10 Hz + + 2. Per channel configuration + + - ch[0-3].type: + measurement type + + value options: + - 0: deactivated + - 4: Resistance (4 wire) + - 5: Resistance (3 wire) + - 6: Resistance (2 wire) + - 7: Thermal Resistance (linear, 4 wire) + - 8: Thermal Resistance (linear, 3 wire) + - 9: Thermal Resistance (linear, 2 wire) + + - ch[0-3].range: + measurement range + + value options: + if type == Resistance (one of 4, 5, 6): + - 0: 48 ohms + - 1: 150 ohms + - 2: 300 ohms + - 3: 600 ohms + - 4: 3000 ohms + + if type == Thermal Resistance (one of 7, 8, 9): + - 0: Pt_100_Climatic_Range + - 2: Pt_100_Standard_Range + - 3: Ni_100_Standard_Range + - 4: Pt_500_S_R + - 5: Pt_1000_S_R + - 6: Ni_1000_S_R + - 11: Pt_200_S_R + - 12: Ni_120_S_R + - 15: Cu_10_S_R + - 16: Ni_200_S_R + - 18: Ni_500_S_R + - 20: Pt_10_S_R + - 22: Pt_50_S_R + - 24: Cu_50_S_R + - 26: Cu_100_S_R + - 28: Lg_Ni_1000_S_R + + + - ch[0-3].temper_coeff: + temperature coefficient + + value options: + - 0: Pt 0.00385055 Ohm/ (DIN EN 60751) + - 1: Pt 0.003916 Ohm/ (USA, NBS) + - 2: Pt 0.003902 Ohm/ + - 3: Pt 0.003920 Ohm/ + - 5: Pt 0.003910 Ohm/ (GOST 6651, Russia 1994) + - 7: Ni 0.006170 Ohm/ (GOST 6651, Russia 1994) + - 8: Ni 0.006180 Ohm/ + - 9: Ni 0.006720 Ohm/ + - 10: LG-Ni 0.005000 Ohm/ + - 11: Cu 0.00426 Ohm/ (GOST 6651, Russia 1994) + - 12: Cu 0.00427 Ohm/ + - 13: Cu 0.00428 Ohm/ (GOST 6651, Russia 1994) + + - ch[0-3].smooth: + smoothing + + value options: + - 0: 1X rejection period + - 1: 4X rejection period + - 2: 16X rejection period + - 3: 32X rejection period + + - ch[0-3].temper_unit: + temperature units + + value options: + - 0: Celcius + - 1: Fahrenheit + + - ch[0-3].overflow_alarm: + overflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-3].underflow_alarm: + underflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-3].open_wire_alarm: + open wire alarm + + value options: + - true: enabled + - false: disabled + + mlfb: "6ES7231-5PD32-0XB0" + + power_alarm: true + + integ_time: 2 + + ch0: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch1: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch2: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch3: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +slot4: + description: | + SM1231-8RTD (8-channel analog measurement module for measuring thermistor) + + Configuration Items: + + 0. MLFB + + Must be "6ES7231-5PF32-0XB0" + + 1. Per module configuration + + - power_alarm: + user power alarm of all channels + + value options: + - true: enabled + - false: disabled + + - integ_time: + integration time + + value options: + - 0: 400 Hz + - 1: 60 Hz + - 2: 50 Hz + - 3: 10 Hz + + 2. Per channel configuration + + - ch[0-7].type: + measurement type + + value options: + - 0: deactivated + - 4: Resistance (4 wire) + - 5: Resistance (3 wire) + - 6: Resistance (2 wire) + - 7: Thermal Resistance (linear, 4 wire) + - 8: Thermal Resistance (linear, 3 wire) + - 9: Thermal Resistance (linear, 2 wire) + + - ch[0-7].range: + measurement range + + value options: + if type == Resistance (one of 4, 5, 6): + - 0: 48 ohms + - 1: 150 ohms + - 2: 300 ohms + - 3: 600 ohms + - 4: 3000 ohms + + if type == Thermal Resistance (one of 7, 8, 9): + - 0: Pt_100_Climatic_Range + - 2: Pt_100_Standard_Range + - 3: Ni_100_Standard_Range + - 4: Pt_500_S_R + - 5: Pt_1000_S_R + - 6: Ni_1000_S_R + - 11: Pt_200_S_R + - 12: Ni_120_S_R + - 15: Cu_10_S_R + - 16: Ni_200_S_R + - 18: Ni_500_S_R + - 20: Pt_10_S_R + - 22: Pt_50_S_R + - 24: Cu_50_S_R + - 26: Cu_100_S_R + - 28: Lg_Ni_1000_S_R + + + - ch[0-7].temper_coeff: + temperature coefficient + + value options: + - 0: Pt 0.00385055 Ohm/ (DIN EN 60751) + - 1: Pt 0.003916 Ohm/ (USA, NBS) + - 2: Pt 0.003902 Ohm/ + - 3: Pt 0.003920 Ohm/ + - 5: Pt 0.003910 Ohm/ (GOST 6651, Russia 1994) + - 7: Ni 0.006170 Ohm/ (GOST 6651, Russia 1994) + - 8: Ni 0.006180 Ohm/ + - 9: Ni 0.006720 Ohm/ + - 10: LG-Ni 0.005000 Ohm/ + - 11: Cu 0.00426 Ohm/ (GOST 6651, Russia 1994) + - 12: Cu 0.00427 Ohm/ + - 13: Cu 0.00428 Ohm/ (GOST 6651, Russia 1994) + + - ch[0-7].smooth: + smoothing + + value options: + - 0: 1X rejection period + - 1: 4X rejection period + - 2: 16X rejection period + - 3: 32X rejection period + + - ch[0-7].temper_unit: + temperature units + + value options: + - 0: Celcius + - 1: Fahrenheit + + - ch[0-7].overflow_alarm: + overflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].underflow_alarm: + underflow alarm + + value options: + - true: enabled + - false: disabled + + - ch[0-7].open_wire_alarm: + open wire alarm + + value options: + - true: enabled + - false: disabled + + mlfb: "6ES7231-5PF32-0XB0" + + power_alarm: true + + integ_time: 2 + + ch0: + type: 7 + range: 3 + temper_coeff: 7 + smooth: 1 + temper_unit: 1 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch1: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch2: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch3: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch4: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch5: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch6: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + + ch7: + type: 7 + range: 2 + temper_coeff: 0 + smooth: 1 + temper_unit: 0 + overflow_alarm: true + underflow_alarm: true + open_wire_alarm: true + +slot5: + description: | + SM SENS DI module + + Configuration Items: + + 0. MLFB + + Must be "6ES7647-0CM00-1AA2" + + There is no configuration entry for this module other than mlfb. + + mlfb: "6ES7647-0CM00-1AA2" + +slot6: + description: | + SM1238 Energy Meter 480VAC + + Configuration Items: + + 0. MLFB + + Must be "6ES7238-5XA32-0XB0" + + 1. Per module configuration + + - module_version: + Module Version + + value options: + - 32: 32 bytes I/ 12 bytes O + - 112: 112 bytes I/ 12 bytes O + - 224: EE@Industry measurement data profiles e0 + - 225: EE@Industry measurement data profiles e1 + - 226: EE@Industry measurement data profiles e2 + - 227: EE@Industry measurement data profiles e3 + + - con_type: + Connection type of the phases + + value options: + - 0x00: Disabled + - 0x0B: 1P2W 1-phase measurement, two conductors, with or without + voltage transformers, with one current transformer + - 0x0C: 3P4W 3-phase, 4-wire, symmetrical and asymmetrical load, with or + without voltage transformers, with 3 current transformers + - 0x0E: 3P4W1 3-phase, 1-wire + - 0x0F: 3x1P2W 3x single phase, two conductors, with or without voltage + transformers, with three current transformers + - 0x10: 2P3W 2-phase, 3-wire, assymetrical load, without voltage + transformers, with two current transformers + + - range: + Voltage measuring range of the power supply + + value options: + - 0x01: 100 V + - 0x02: 110 V + - 0x03: 115 V + - 0x04: 120 V + - 0x05: 127 V + - 0x06: 190 V + - 0x07: 200 V + - 0x08: 208 V + - 0x09: 220 V + - 0x0A: 230 V + - 0x0B: 240 V + - 0x0C: 277 V + + - line_freq: + Line frequency of the power supply + + value options: + - 1: 60 Hz + - 2: 50 Hz + + - period_meters: + End value energy meter + + value options: + - 0x00: No end value (count indefinitely) + - 0x01: Count periodically up to 10^3 + - 0x02: Count periodically up to 10^6 + - 0x03: Count periodically up to 10^9 + - 0x04: Count periodically up to 10^12 + - 0x05: Count periodically up to 10^15 + + - meter_gate: + Energy meter gate + + value options: + - false: Energy counter always counting + - true: Energy counter gate + + - line_vol_tol: + Line voltage tolerance + + value options: + - 0x01: 1% + - 0x02: 2% + - 0x03: 3% + - ... + - 0x32: 50% + + - min_max_cal: + Minimum and maximum value calculation + + value options: bool + + - diag_line_vol: + Line voltage diagnostics + + value options: bool + + - data_variant: + User data variant + + value options: + - 0xFE: Total power L1L2L3 (default) + - 0xFD: Active power L1L2L3 + - 0xFC: Reactive power L1L2L3 + - 0xFB: Apparent power L1L2L3 + - 0xFA: Basic measurement values L1L2L3 + - 0xF9: Total energy L1L2L3 + - 0xF8: Energy L1 + - 0xF7: Energy L2 + - 0xF6: Energy L3 + - 0xF5: Basic values 3-phase measurement L1L2L3 + - 0xF0: Quality values 3-phase measurement + - 0xEF: Energy measurement (periodical) overrange meter + - 0xE3: EE@Industry Measurement Data Profile e3 + - 0xE2: EE@Industry Measurement Data Profile e2 + - 0xE1: EE@Industry Measurement Data Profile e1 + - 0xE0: EE@Industry measurement Data Profile e0 + - 0x9F: Basic values Single Phase Measurement L1 + - 0x9E: Basic values Single Phase Measurement L1a + - 0x9D: Basic values Single Phase Measurement L2 + - 0x9C: Basic values Single Phase Measurement L2a + - 0x9B: Basic values Single Phase Measurement L3 + - 0x9A: Basic values Single Phase Measurement L3a + + 2. Per channel configuration + + - ch[0-2].diag_over_cur: + Enable diagnostic overflow current + + value options: bool + + - ch[0-2].diag_over_cal: + Enable diagnostic overflow calculation values + + value options: bool + + - ch[0-2].diag_ll_vol: + Enable diagnostic of low limit voltage + + value options: bool + + - ch[0-2].diag_under_vol: + Enable diagnostic underflow voltage + + value options: bool + + - ch[0-2].diag_over_vol: + Enable diagnostic overflow voltage + + value options: bool + + - ch[0-2].over_cur_tol_val: + Tolerance value secondary over-current, 0.1A + + value options: [10 ~ 100] + + - ch[0-2].over_cur_tol_time: + Tolerance time over-current, ms + + value options: [0 ~ 60000] + + - ch[0-2].ct_primary_cur: + Current transformer primary current, A + + value options: [1 ~ 99999] + + - ch[0-2].en_gate_cir_hour_meter: + Enable gate circuit hours meter + + value options: bool + + - ch[0-2].ct_second_cur: + Current transformer secondary current: (0: 1A, 2: 5A) + + value options: + - 0: 1 A + - 2: 5 A + + - ch[0-2].act_hour_meter: + Activate hours meter + + value options: bool + + - ch[0-2].re_cur_dir: + Reverse current direction + + value options: bool + + - ch[0-2].ll_cur_measure: + Low limit current measurement, mA + + value options: [2 ~ 250] + + - ch[0-2].vt_second_vol: + Voltage transformer secondary voltage, V + + value options: [1 ~ 500] + + - ch[0-2].vt_prim_vol: + Voltage transformer primary voltage, V + + value options: [1 ~ 999999] + + mlfb: "6ES7238-5XA32-0XB0" + + module_version: 32 + con_type: 0x0C + range: 0x0A + line_freq: 2 + period_meters: 0 + meter_gate: false + line_vol_tol: 0x0A + min_max_cal: false + diag_line_vol: false + data_variant: 0xFE + + ch0: + diag_over_cur: false + diag_over_cal: false + diag_ll_vol: false + diag_under_vol: false + diag_over_vol: false + over_cur_tol_val: 100 + over_cur_tol_time: 40000 + ct_primary_cur: 1 + en_gate_cir_hour_meter: false + ct_second_cur: 0 + act_hour_meter: false + re_cur_dir: false + ll_cur_measure: 50 + vt_second_vol: 230 + vt_prim_vol: 230 + + ch1: + diag_over_cur: false + diag_over_cal: false + diag_ll_vol: false + diag_under_vol: false + diag_over_vol: false + over_cur_tol_val: 100 + over_cur_tol_time: 40000 + ct_primary_cur: 1 + en_gate_cir_hour_meter: false + ct_second_cur: 0 + act_hour_meter: false + re_cur_dir: false + ll_cur_measure: 50 + vt_second_vol: 230 + vt_prim_vol: 230 + + ch2: + diag_over_cur: false + diag_over_cal: false + diag_ll_vol: false + diag_under_vol: false + diag_over_vol: false + over_cur_tol_val: 100 + over_cur_tol_time: 40000 + ct_primary_cur: 1 + en_gate_cir_hour_meter: false + ct_second_cur: 0 + act_hour_meter: false + re_cur_dir: false + ll_cur_measure: 50 + vt_second_vol: 230 + vt_prim_vol: 230 diff --git a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050-eio.proto b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050-eio.proto new file mode 100644 index 000000000..d79d5e4c8 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050-eio.proto @@ -0,0 +1,129 @@ +/* + * Copyright (c) Siemens AG, 2023 + * + * Authors: + * Su Bao Cheng + * + * SPDX-License-Identifier: MIT + */ +syntax = "proto3"; + +package eiomanager; + +service EIOManager { + rpc Deploy (DeployRequest) returns (DeployReply) {} + rpc Retrieve (RetrieveRequest) returns (RetrieveReply) {} + rpc SyncTime (SyncTimeRequest) returns (SyncTimeReply) {} + rpc UpdateFirmware (UpdateFirmwareRequest) returns (UpdateFirmwareReply) {} + rpc CheckFWU (CheckFWURequest) returns (CheckFWUReply) {} + rpc ReadEIOEvent (ReadEIOEventRequest) returns (ReadEIOEventReply) {} +} + +/* ----------------- Deploy EIO configuration ----------------- */ +/* DeployRequest + * - yaml_data: Configration data in yaml format. It could be exported from the + * webUI. + */ +message DeployRequest { + string yaml_data = 1; +} + +/* DeployReply + * - status: 0 means no error + * - message: the detail reply message + */ +message DeployReply { + int32 status = 1; + string message = 2; +} + +/* ----------------- Retrieve EIO configuration ----------------- */ +/* RetrieveRequest + * - name: Reserved for future. + */ +message RetrieveRequest { + string name = 1; +} + +/* RetrieveReply + * - status: 0 means no error + * - message: the detail reply message + * - yaml_data: Configration data in yaml format + */ +message RetrieveReply { + int32 status = 1; + string message = 2; + string yaml_data = 3; +} + +/* ----------------- Sync system time to EIO controller ----------------- */ +/* SyncTimeRequest + * - time: optional, if empty, then the current system time will be synced into + * the EIO controller + */ +message SyncTimeRequest { + optional string time = 1; +} + +/* SyncTimeReply + * - status: 0 means no error + * - message: the detail reply message + */ +message SyncTimeReply { + int32 status = 1; + string message = 2; +} + +/* ----------------- Update EIO firmware ----------------- */ +/* UpdateFirmwareRequest + * - entity: 0 means checking for EIO controller + */ +message UpdateFirmwareRequest { + int32 entity = 1; + string firmware = 2; +} + +/* UpdateFirmwareReply + * - status: 0 means no error + * - message: the detail reply message + */ +message UpdateFirmwareReply { + int32 status = 1; + string message = 2; +} + +/* ----------------- Check FWU status ----------------- */ +/* CheckFWURequest + * - entity: 0 means checking for EIO controller + */ +message CheckFWURequest { + int32 entity = 1; +} + +/* CheckFWUReply + * - status: 0 means no need to update + * 1 means need update + * 2 means need update, however binary data corrupted + * - message: the detail FWU status message + */ +message CheckFWUReply { + int32 status = 1; + string message = 2; +} + +/* ----------------- Read EIO event ----------------- */ +/* ReadEIOEventRequest + * - empty param + */ +message ReadEIOEventRequest { + +} + +/* ReadEIOEventReply + * - event: the string of eio event + */ +message ReadEIOEventReply { + int32 status = 1; + string message = 2; + string event = 3; +} diff --git a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.py b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.py new file mode 100644 index 000000000..399af47c2 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: gRPC/EIOManager/iot2050-eio.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!gRPC/EIOManager/iot2050-eio.proto\x12\neiomanager\"\"\n\rDeployRequest\x12\x11\n\tyaml_data\x18\x01 \x01(\t\".\n\x0b\x44\x65ployReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x1f\n\x0fRetrieveRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\"C\n\rRetrieveReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x11\n\tyaml_data\x18\x03 \x01(\t\"-\n\x0fSyncTimeRequest\x12\x11\n\x04time\x18\x01 \x01(\tH\x00\x88\x01\x01\x42\x07\n\x05_time\"0\n\rSyncTimeReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"9\n\x15UpdateFirmwareRequest\x12\x0e\n\x06\x65ntity\x18\x01 \x01(\x05\x12\x10\n\x08\x66irmware\x18\x02 \x01(\t\"6\n\x13UpdateFirmwareReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"!\n\x0f\x43heckFWURequest\x12\x0e\n\x06\x65ntity\x18\x01 \x01(\x05\"0\n\rCheckFWUReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\"\x15\n\x13ReadEIOEventRequest\"C\n\x11ReadEIOEventReply\x12\x0e\n\x06status\x18\x01 \x01(\x05\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\r\n\x05\x65vent\x18\x03 \x01(\t2\xc8\x03\n\nEIOManager\x12>\n\x06\x44\x65ploy\x12\x19.eiomanager.DeployRequest\x1a\x17.eiomanager.DeployReply\"\x00\x12\x44\n\x08Retrieve\x12\x1b.eiomanager.RetrieveRequest\x1a\x19.eiomanager.RetrieveReply\"\x00\x12\x44\n\x08SyncTime\x12\x1b.eiomanager.SyncTimeRequest\x1a\x19.eiomanager.SyncTimeReply\"\x00\x12V\n\x0eUpdateFirmware\x12!.eiomanager.UpdateFirmwareRequest\x1a\x1f.eiomanager.UpdateFirmwareReply\"\x00\x12\x44\n\x08\x43heckFWU\x12\x1b.eiomanager.CheckFWURequest\x1a\x19.eiomanager.CheckFWUReply\"\x00\x12P\n\x0cReadEIOEvent\x12\x1f.eiomanager.ReadEIOEventRequest\x1a\x1d.eiomanager.ReadEIOEventReply\"\x00\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'gRPC.EIOManager.iot2050_eio_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR._options = None + _globals['_DEPLOYREQUEST']._serialized_start=49 + _globals['_DEPLOYREQUEST']._serialized_end=83 + _globals['_DEPLOYREPLY']._serialized_start=85 + _globals['_DEPLOYREPLY']._serialized_end=131 + _globals['_RETRIEVEREQUEST']._serialized_start=133 + _globals['_RETRIEVEREQUEST']._serialized_end=164 + _globals['_RETRIEVEREPLY']._serialized_start=166 + _globals['_RETRIEVEREPLY']._serialized_end=233 + _globals['_SYNCTIMEREQUEST']._serialized_start=235 + _globals['_SYNCTIMEREQUEST']._serialized_end=280 + _globals['_SYNCTIMEREPLY']._serialized_start=282 + _globals['_SYNCTIMEREPLY']._serialized_end=330 + _globals['_UPDATEFIRMWAREREQUEST']._serialized_start=332 + _globals['_UPDATEFIRMWAREREQUEST']._serialized_end=389 + _globals['_UPDATEFIRMWAREREPLY']._serialized_start=391 + _globals['_UPDATEFIRMWAREREPLY']._serialized_end=445 + _globals['_CHECKFWUREQUEST']._serialized_start=447 + _globals['_CHECKFWUREQUEST']._serialized_end=480 + _globals['_CHECKFWUREPLY']._serialized_start=482 + _globals['_CHECKFWUREPLY']._serialized_end=530 + _globals['_READEIOEVENTREQUEST']._serialized_start=532 + _globals['_READEIOEVENTREQUEST']._serialized_end=553 + _globals['_READEIOEVENTREPLY']._serialized_start=555 + _globals['_READEIOEVENTREPLY']._serialized_end=622 + _globals['_EIOMANAGER']._serialized_start=625 + _globals['_EIOMANAGER']._serialized_end=1081 +# @@protoc_insertion_point(module_scope) diff --git a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.pyi b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.pyi new file mode 100644 index 000000000..08338e3a0 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2.pyi @@ -0,0 +1,93 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class DeployRequest(_message.Message): + __slots__ = ["yaml_data"] + YAML_DATA_FIELD_NUMBER: _ClassVar[int] + yaml_data: str + def __init__(self, yaml_data: _Optional[str] = ...) -> None: ... + +class DeployReply(_message.Message): + __slots__ = ["status", "message"] + STATUS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + status: int + message: str + def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ... + +class RetrieveRequest(_message.Message): + __slots__ = ["name"] + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + +class RetrieveReply(_message.Message): + __slots__ = ["status", "message", "yaml_data"] + STATUS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + YAML_DATA_FIELD_NUMBER: _ClassVar[int] + status: int + message: str + yaml_data: str + def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ..., yaml_data: _Optional[str] = ...) -> None: ... + +class SyncTimeRequest(_message.Message): + __slots__ = ["time"] + TIME_FIELD_NUMBER: _ClassVar[int] + time: str + def __init__(self, time: _Optional[str] = ...) -> None: ... + +class SyncTimeReply(_message.Message): + __slots__ = ["status", "message"] + STATUS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + status: int + message: str + def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ... + +class UpdateFirmwareRequest(_message.Message): + __slots__ = ["entity", "firmware"] + ENTITY_FIELD_NUMBER: _ClassVar[int] + FIRMWARE_FIELD_NUMBER: _ClassVar[int] + entity: int + firmware: str + def __init__(self, entity: _Optional[int] = ..., firmware: _Optional[str] = ...) -> None: ... + +class UpdateFirmwareReply(_message.Message): + __slots__ = ["status", "message"] + STATUS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + status: int + message: str + def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ... + +class CheckFWURequest(_message.Message): + __slots__ = ["entity"] + ENTITY_FIELD_NUMBER: _ClassVar[int] + entity: int + def __init__(self, entity: _Optional[int] = ...) -> None: ... + +class CheckFWUReply(_message.Message): + __slots__ = ["status", "message"] + STATUS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + status: int + message: str + def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ...) -> None: ... + +class ReadEIOEventRequest(_message.Message): + __slots__ = [] + def __init__(self) -> None: ... + +class ReadEIOEventReply(_message.Message): + __slots__ = ["status", "message", "event"] + STATUS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + EVENT_FIELD_NUMBER: _ClassVar[int] + status: int + message: str + event: str + def __init__(self, status: _Optional[int] = ..., message: _Optional[str] = ..., event: _Optional[str] = ...) -> None: ... diff --git a/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2_grpc.py b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2_grpc.py new file mode 100644 index 000000000..08cae142f --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/gRPC/EIOManager/iot2050_eio_pb2_grpc.py @@ -0,0 +1,231 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc + +from gRPC.EIOManager import iot2050_eio_pb2 as gRPC_dot_EIOManager_dot_iot2050__eio__pb2 + + +class EIOManagerStub(object): + """Missing associated documentation comment in .proto file.""" + + def __init__(self, channel): + """Constructor. + + Args: + channel: A grpc.Channel. + """ + self.Deploy = channel.unary_unary( + '/eiomanager.EIOManager/Deploy', + request_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.DeployRequest.SerializeToString, + response_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.DeployReply.FromString, + ) + self.Retrieve = channel.unary_unary( + '/eiomanager.EIOManager/Retrieve', + request_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.RetrieveRequest.SerializeToString, + response_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.RetrieveReply.FromString, + ) + self.SyncTime = channel.unary_unary( + '/eiomanager.EIOManager/SyncTime', + request_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.SyncTimeRequest.SerializeToString, + response_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.SyncTimeReply.FromString, + ) + self.UpdateFirmware = channel.unary_unary( + '/eiomanager.EIOManager/UpdateFirmware', + request_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.UpdateFirmwareRequest.SerializeToString, + response_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.UpdateFirmwareReply.FromString, + ) + self.CheckFWU = channel.unary_unary( + '/eiomanager.EIOManager/CheckFWU', + request_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.CheckFWURequest.SerializeToString, + response_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.CheckFWUReply.FromString, + ) + self.ReadEIOEvent = channel.unary_unary( + '/eiomanager.EIOManager/ReadEIOEvent', + request_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.ReadEIOEventRequest.SerializeToString, + response_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.ReadEIOEventReply.FromString, + ) + + +class EIOManagerServicer(object): + """Missing associated documentation comment in .proto file.""" + + def Deploy(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def Retrieve(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def SyncTime(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def UpdateFirmware(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def CheckFWU(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + def ReadEIOEvent(self, request, context): + """Missing associated documentation comment in .proto file.""" + context.set_code(grpc.StatusCode.UNIMPLEMENTED) + context.set_details('Method not implemented!') + raise NotImplementedError('Method not implemented!') + + +def add_EIOManagerServicer_to_server(servicer, server): + rpc_method_handlers = { + 'Deploy': grpc.unary_unary_rpc_method_handler( + servicer.Deploy, + request_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.DeployRequest.FromString, + response_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.DeployReply.SerializeToString, + ), + 'Retrieve': grpc.unary_unary_rpc_method_handler( + servicer.Retrieve, + request_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.RetrieveRequest.FromString, + response_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.RetrieveReply.SerializeToString, + ), + 'SyncTime': grpc.unary_unary_rpc_method_handler( + servicer.SyncTime, + request_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.SyncTimeRequest.FromString, + response_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.SyncTimeReply.SerializeToString, + ), + 'UpdateFirmware': grpc.unary_unary_rpc_method_handler( + servicer.UpdateFirmware, + request_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.UpdateFirmwareRequest.FromString, + response_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.UpdateFirmwareReply.SerializeToString, + ), + 'CheckFWU': grpc.unary_unary_rpc_method_handler( + servicer.CheckFWU, + request_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.CheckFWURequest.FromString, + response_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.CheckFWUReply.SerializeToString, + ), + 'ReadEIOEvent': grpc.unary_unary_rpc_method_handler( + servicer.ReadEIOEvent, + request_deserializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.ReadEIOEventRequest.FromString, + response_serializer=gRPC_dot_EIOManager_dot_iot2050__eio__pb2.ReadEIOEventReply.SerializeToString, + ), + } + generic_handler = grpc.method_handlers_generic_handler( + 'eiomanager.EIOManager', rpc_method_handlers) + server.add_generic_rpc_handlers((generic_handler,)) + + + # This class is part of an EXPERIMENTAL API. +class EIOManager(object): + """Missing associated documentation comment in .proto file.""" + + @staticmethod + def Deploy(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/eiomanager.EIOManager/Deploy', + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.DeployRequest.SerializeToString, + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.DeployReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def Retrieve(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/eiomanager.EIOManager/Retrieve', + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.RetrieveRequest.SerializeToString, + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.RetrieveReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def SyncTime(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/eiomanager.EIOManager/SyncTime', + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.SyncTimeRequest.SerializeToString, + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.SyncTimeReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def UpdateFirmware(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/eiomanager.EIOManager/UpdateFirmware', + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.UpdateFirmwareRequest.SerializeToString, + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.UpdateFirmwareReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def CheckFWU(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/eiomanager.EIOManager/CheckFWU', + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.CheckFWURequest.SerializeToString, + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.CheckFWUReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + + @staticmethod + def ReadEIOEvent(request, + target, + options=(), + channel_credentials=None, + call_credentials=None, + insecure=False, + compression=None, + wait_for_ready=None, + timeout=None, + metadata=None): + return grpc.experimental.unary_unary(request, target, '/eiomanager.EIOManager/ReadEIOEvent', + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.ReadEIOEventRequest.SerializeToString, + gRPC_dot_EIOManager_dot_iot2050__eio__pb2.ReadEIOEventReply.FromString, + options, channel_credentials, + insecure, call_credentials, compression, wait_for_ready, timeout, metadata) diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eio-cli.py b/recipes-app/iot2050-eio-manager/files/iot2050-eio-cli.py new file mode 100755 index 000000000..ba3326077 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eio-cli.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +import argparse +import sys +import textwrap +import time +from concurrent.futures import ThreadPoolExecutor +from progress.bar import Bar +import grpc +from gRPC.EIOManager.iot2050_eio_pb2 import ( + DeployRequest, + RetrieveRequest, + UpdateFirmwareRequest +) +from gRPC.EIOManager.iot2050_eio_pb2_grpc import EIOManagerStub +from iot2050_eio_global import ( + iot2050_eio_api_server, + EIO_FWU_MAP3_FW_BIN +) +from iot2050_eio_fwu_monitor import ( + do_fwu_check +) + + +def do_deploy(yaml_data): + with grpc.insecure_channel(iot2050_eio_api_server) as channel: + stub = EIOManagerStub(channel) + response = stub.Deploy(DeployRequest(yaml_data=yaml_data)) + print(f"Extended IO config deploy result: {response.status}") + print(f"Extended IO config deploy message: {response.message}") + + +def do_retrieve(): + with grpc.insecure_channel(iot2050_eio_api_server) as channel: + stub = EIOManagerStub(channel) + response = stub.Retrieve(RetrieveRequest(name="dummy")) + print(f"Extended IO config retrieve result: {response.status}") + print(f"Extended IO config retrieve message: {response.message}") + + return response.yaml_data + + +def show_progress_bar(task, interval): + with Bar('Updating'.ljust(15), fill='.', bar_prefix='', + bar_suffix='', suffix='') as bar: + while True: + bar.next() + if task.done(): + break + time.sleep(interval) + + +def do_update_firmware(firmware): + with grpc.insecure_channel(iot2050_eio_api_server) as channel: + stub = EIOManagerStub(channel) + print("===================================================") + print("EIO firmware update started - DO NOT INTERRUPT!") + print("===================================================") + with ThreadPoolExecutor(max_workers=1) as pool: + future = pool.submit( + stub.UpdateFirmware, + UpdateFirmwareRequest(firmware=firmware) + ) + show_progress_bar(future, 0.3) + print() + + response = future.result() + print(f"Extended IO config result: {response.status}") + print(f"Extended IO config message: {response.message}") + return response + + +if __name__ == "__main__": + description=textwrap.dedent('''\ + Extended IO command line tool + + Examples: + 1. %(prog)s config deploy config.yaml + Deploy the config.yaml to Extended IO Controller + 2. %(prog)s config retrieve config.yaml + Retrieve the config from Extended IO Controller and store into config.yaml + 3. %(prog)s fwu controller [firmware.bin] + update firmware for Extended IO Controller, using firmware.bin if provided, + otherwise using the stock firmware file. + + Example Configuration File: + + Please check /usr/lib/iot2050/eio/config-template/sm-config-example.yaml for full config + And check /usr/lib/iot2050/eio/config-template/mlfb-XXX.yaml for specific module + + + ''') + parser = argparse.ArgumentParser( + description=description, + formatter_class=argparse.RawDescriptionHelpFormatter + ) + subparsers = parser.add_subparsers(help='sub-command help', + title='sub-commands', + dest="command") + + config_parser = subparsers.add_parser("config", help='config help') + config_parser.add_argument('action', metavar='ACTION', + choices=['deploy', 'retrieve'], + help='Action, deploy or retrieve') + config_parser.add_argument('config', metavar='CONFIG_YAML', type=str, + help='Config file in yaml format') + + fwu_parser = subparsers.add_parser("fwu", help='firmware update help') + fwu_parser.add_argument('action', metavar='ACTION', + choices=['controller', 'module'], + help='Specify the firmware update type') + fwu_parser.add_argument('firmware', nargs='?', metavar='FIRMWARE', type=str, + help='Firmware file') + + args = parser.parse_args() + + if args.command == 'config': + status, message = do_fwu_check() + if status != 0: + print(message) + continue_op = input( + "\nWarning: Please fix above issue before continue. Continue? (y/N) " + ) + if continue_op != "y": + sys.exit(0) + + if args.action == "deploy": + with open(args.config, 'r', encoding='ascii') as f_config: + do_deploy(f_config.read()) + else: + config_returned = do_retrieve() + if config_returned == "": + print("Unable to retrieve config!") + sys.exit(1) + + with open(args.config, 'w', encoding='ascii') as f_config: + f_config.write(config_returned) + elif args.command == 'fwu': + if args.firmware: + firmware = args.firmware + else: + firmware = EIO_FWU_MAP3_FW_BIN + status, message = do_fwu_check() + if status == 0: + # no need to update! + print(message) + sys.exit(0) + + response = do_update_firmware(firmware) + if response.status: + print(f"ERROR: {response.message}") + print("EIO firmware update failed, please try again!") + sys.exit(1) + + print("EIO firmware update completed. Please reboot the device.") diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eio-fwu-monitor.service b/recipes-app/iot2050-eio-manager/files/iot2050-eio-fwu-monitor.service new file mode 100644 index 000000000..d22fdc88f --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eio-fwu-monitor.service @@ -0,0 +1,19 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT + +[Unit] +Description=IOT2050 Extended IO Firmware Version Monitoring +# Only run on IOT2050-SM variant +ConditionFirmware=device-tree-compatible(siemens,iot2050-advanced-sm) + +[Service] +Type=simple +ExecStart=/usr/bin/python3 -u /usr/bin/iot2050-eio-fwu-monitor +Restart=always +RestartSec=5 +RestartSteps=3 +RestartMaxDelaySec=30 + +[Install] +WantedBy=multi-user.target diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eio-service.py b/recipes-app/iot2050-eio-manager/files/iot2050-eio-service.py new file mode 100644 index 000000000..d0e4d52c2 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eio-service.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +from concurrent import futures +import datetime +import grpc +from gRPC.EIOManager.iot2050_eio_pb2 import ( + DeployRequest, DeployReply, + RetrieveRequest, RetrieveReply, + SyncTimeRequest, SyncTimeReply, + UpdateFirmwareRequest, UpdateFirmwareReply, + CheckFWURequest, CheckFWUReply, + ReadEIOEventRequest, ReadEIOEventReply +) +from gRPC.EIOManager.iot2050_eio_pb2_grpc import ( + EIOManagerServicer as BaseEIOManagerServicer, + add_EIOManagerServicer_to_server +) +from iot2050_eio_config import ( + deploy_config, + retrieve_config, + ConfigError, +) +from iot2050_eio_global import ( + iot2050_eio_api_server, + EIO_FS_TIMESTAMP, +) +from iot2050_eio_fwu import ( + update_firmware, + FirmwareUpdateChecker +) +from iot2050_eio_event import read_eio_event + + +class EIOManagerServicer(BaseEIOManagerServicer): + + def Deploy(self, request: DeployRequest, context): + try: + deploy_config(request.yaml_data) + except ConfigError as e: + return DeployReply(status=1, message=f'{e}') + + return DeployReply(status=0, message='OK') + + def Retrieve(self, request: RetrieveRequest, context): + try: + yaml_data = retrieve_config() + except ConfigError as e: + return RetrieveReply(status=1, + message=f'{e}', + yaml_data="") + + return RetrieveReply(status=0, + message='OK', + yaml_data=yaml_data) + + def SyncTime(self, request: SyncTimeRequest, context): + if request.HasField("time"): + time = request.time + else: + time = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + + with open(EIO_FS_TIMESTAMP, 'w', encoding='ascii') as f: + f.write(time) + + return SyncTimeReply(status=0, message=f'{time}') + + def UpdateFirmware(self, request: UpdateFirmwareRequest, context): + status, message = update_firmware(request.firmware) + return UpdateFirmwareReply(status=status, message=f'{message}') + + def CheckFWU(self, request: CheckFWURequest, context): + status, message = FirmwareUpdateChecker().collect_fwu_info() + return CheckFWUReply(status=status, message=message) + + def ReadEIOEvent(self, request: ReadEIOEventRequest, context): + try: + event = read_eio_event() + except Exception as e: + return ReadEIOEventReply(status=1, message=f'{e}', event=event) + + return ReadEIOEventReply(status=0, message='OK', event=event) + +def serve(): + server = grpc.server(futures.ThreadPoolExecutor(max_workers=1)) + add_EIOManagerServicer_to_server(EIOManagerServicer(), server) + server.add_insecure_port(iot2050_eio_api_server) + server.start() + server.wait_for_termination() + + +if __name__ == "__main__": + serve() diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eio-time-syncing.py b/recipes-app/iot2050-eio-manager/files/iot2050-eio-time-syncing.py new file mode 100644 index 000000000..7eafd88e5 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eio-time-syncing.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +import grpc +import time +from gRPC.EIOManager.iot2050_eio_pb2 import SyncTimeRequest +from gRPC.EIOManager.iot2050_eio_pb2_grpc import EIOManagerStub +from iot2050_eio_global import ( + iot2050_eio_api_server, + EIO_TIME_SYNC_INTERVAL +) + + +def run(): + print(f"Start syncing Extended IO timestamp every {EIO_TIME_SYNC_INTERVAL} seconds.") + while True: + with grpc.insecure_channel(iot2050_eio_api_server) as channel: + stub = EIOManagerStub(channel) + response = stub.SyncTime(SyncTimeRequest()) + print(f"Extended IO timestamp syncing result: {response.status}") + print(f"Extended IO timestamp synced to: {response.message}") + time.sleep(EIO_TIME_SYNC_INTERVAL) + + +if __name__ == "__main__": + run() diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eio-time-syncing.service b/recipes-app/iot2050-eio-manager/files/iot2050-eio-time-syncing.service new file mode 100644 index 000000000..1a698516c --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eio-time-syncing.service @@ -0,0 +1,19 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT + +[Unit] +Description=IOT2050 Extended IO Server daemon +# Only run on IOT2050-SM variant +ConditionFirmware=device-tree-compatible(siemens,iot2050-advanced-sm) + +[Service] +Type=simple +ExecStart=/usr/bin/python3 -u /usr/bin/iot2050-eio-time-syncing +Restart=always +RestartSec=5 +RestartSteps=3 +RestartMaxDelaySec=30 + +[Install] +WantedBy=multi-user.target diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eiod.service b/recipes-app/iot2050-eio-manager/files/iot2050-eiod.service new file mode 100644 index 000000000..f4e9810a6 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eiod.service @@ -0,0 +1,17 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT + +[Unit] +Description=IOT2050 Extended IO FUSE filesystem daemon +# Only run on IOT2050-SM variant +ConditionFirmware=device-tree-compatible(siemens,iot2050-advanced-sm) + +[Service] +Type=forking +ExecStart=/usr/bin/iot2050-ext-io /ext-io +# TBD: eiosd.service will notify the reboot to map3, hence need this service to be alive during reboot +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/recipes-app/iot2050-eio-manager/files/iot2050-eiosd.service b/recipes-app/iot2050-eio-manager/files/iot2050-eiosd.service new file mode 100644 index 000000000..4f6594d6a --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050-eiosd.service @@ -0,0 +1,18 @@ +# Copyright (c) Siemens AG, 2023 +# +# SPDX-License-Identifier: MIT + +[Unit] +Description=IOT2050 Extended IO Server daemon +# Only run on IOT2050-SM variant +ConditionFirmware=device-tree-compatible(siemens,iot2050-advanced-sm) + +[Service] +Type=exec +ExecStart=/usr/bin/python3 -u /usr/bin/iot2050-eio-service +# Need notify map3 during reboot +StandardOutput=journal+console +StandardError=journal+console + +[Install] +WantedBy=multi-user.target diff --git a/recipes-app/iot2050-eio-manager/files/iot2050_eio_config.py b/recipes-app/iot2050-eio-manager/files/iot2050_eio_config.py new file mode 100644 index 000000000..bf6623cd7 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050_eio_config.py @@ -0,0 +1,608 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +import struct +from abc import ABC, abstractmethod +from typing import List, Dict +import zlib +import yaml +try: + from yaml import CLoader as Loader, CDumper as Dumper +except ImportError: + from yaml import Loader, Dumper +import bitstruct +from jsonschema import RefResolver +from jsonschema.exceptions import ValidationError +from jsonschema import Draft202012Validator +from iot2050_eio_global import ( + EIO_FS_CONTROL, + EIO_FS_CONFIG, + eio_schema_top, + eio_schema_refs +) + + +class ModuleSerDes(ABC): + """Abstract class for module (de)/serializer""" + @abstractmethod + def __init__(self, mlfb): + self.mlfb = mlfb + + @abstractmethod + def serialize(self, config: Dict) -> bytes: + """Serialize the configuration to data blob + + Args: + config (Dict): configuration dict + + Returns: + bytes: blob + """ + + @abstractmethod + def deserialize(self, blob: bytes) -> Dict: + """Deserialize the configuration from data blob + + Args: + blob (bytes): data blob + + Returns: + Dict: configuration + """ + + +class SM1223SerDes(ModuleSerDes): + """SM1223 Configuration (de)/serializer""" + def __init__(self, mlfb): + super().__init__(mlfb) + header_fmt = 'p8 u8' + di_ch_fmt = 'p8 p4u4' + dq_ch_fmt = 'p8 u1p1u2p1p3' + self.serdesfmt = ''.join([header_fmt, di_ch_fmt * 8, header_fmt, dq_ch_fmt * 8]) + + def serialize(self, config: Dict) -> bytes: + parameters = [0x02] + for i in range(0, 8): + if i < 4: + parameters.append(config['di']['ch0_3_delay_time']) + else: + parameters.append(config['di']['ch4_7_delay_time']) + parameters.append(0x02) + for i in range(0, 8): + parameters.append(config['dq'][f'ch{i}']['substitute']) + parameters.append(config['dq']['behavior_with_OD']) + return bitstruct.pack(self.serdesfmt, *parameters) + + def deserialize(self, blob: bytes) -> Dict: + config = { + "description": "TBD", + "mlfb": self.mlfb, + "di": { + "ch0_3_delay_time": "", + "ch4_7_delay_time": "" + }, + "dq": { + "behavior_with_OD": "", + "ch0": {"substitute": ""}, + "ch1": {"substitute": ""}, + "ch2": {"substitute": ""}, + "ch3": {"substitute": ""}, + "ch4": {"substitute": ""}, + "ch5": {"substitute": ""}, + "ch6": {"substitute": ""}, + "ch7": {"substitute": ""} + } + } + + _, \ + config['di']['ch0_3_delay_time'], _, _, _, \ + config['di']['ch4_7_delay_time'], _, _, _, \ + _, \ + config['dq']['ch0']['substitute'], config['dq']['behavior_with_OD'], \ + config['dq']['ch1']['substitute'], _, \ + config['dq']['ch2']['substitute'], _, \ + config['dq']['ch3']['substitute'], _, \ + config['dq']['ch4']['substitute'], _, \ + config['dq']['ch5']['substitute'], _, \ + config['dq']['ch6']['substitute'], _, \ + config['dq']['ch7']['substitute'], _ = bitstruct.unpack(self.serdesfmt, blob) + return config + + +class SM1223AI8SerDes(ModuleSerDes): + """SM1223 8AI Configuration (de)/serializer""" + def __init__(self, mlfb): + super().__init__(mlfb) + header_fmt = 'p8 u8' + ch_fmt = 'u8 u8 p8 u4u4 p8p8 b1b1p1b1p3b1 p8 p144' + self.serdesfmt = ''.join([header_fmt, ch_fmt * 8]) + + def serialize(self, config: Dict) -> bytes: + parameters = [0x1A] + for i in range(0, 8): + parameters.extend([ + config[f'ch{i}']['type'], + config[f'ch{i}']['range'], + config['integ_time'], + config[f'ch{i}']['smooth'], + config[f'ch{i}']['open_wire_alarm'], + config[f'ch{i}']['overflow_alarm'], + config[f'ch{i}']['underflow_alarm'], + config['power_alarm'] + ]) + + return bitstruct.pack(self.serdesfmt, *parameters) + + def deserialize(self, blob: bytes) -> Dict: + config = { + "description": "TBD", + "mlfb": self.mlfb + } + + unpack_statement = '_' + for i in range(0, 8): + config[f'ch{i}'] = {} + integ_time_ele = "config['integ_time']" if i == 0 else "_" + power_alarm_ele = "config['power_alarm']" if i == 0 else "_" + unpack_statement = ','.join([ + unpack_statement, + f"config['ch{i}']['type']", + f"config['ch{i}']['range']", + integ_time_ele, + f"config['ch{i}']['smooth']", + f"config['ch{i}']['open_wire_alarm']", + f"config['ch{i}']['overflow_alarm']", + f"config['ch{i}']['underflow_alarm']", + power_alarm_ele + ]) + unpack_statement += ' = bitstruct.unpack(self.serdesfmt, blob)' + exec(unpack_statement) # pylint: disable=exec-used + return config + + +class SM1223RTDSerDes(ModuleSerDes): + """SM1223 RTD x4 x8 Configuration (de)/serializer""" + def __init__(self, mlfb): + super().__init__(mlfb) + header_fmt = 'p8 u8' + ch_fmt = 'u8 u8 u8 u4u4 u8 p8 b1b1p1b1p3b1 p8 p144' + if self.mlfb == '6ES7231-5PD32-0XB0': + self.ch_num = 4 + else: + self.ch_num = 8 + self.serdesfmt = ''.join([header_fmt, ch_fmt * self.ch_num]) + + def serialize(self, config: Dict) -> bytes: + parameters = [0x1A] + for i in range(0, self.ch_num): + parameters.extend([ + config[f'ch{i}']['type'], + config[f'ch{i}']['range'], + config[f'ch{i}']['temper_coeff'], + config['integ_time'], + config[f'ch{i}']['smooth'], + config[f'ch{i}']['temper_unit'], + config[f'ch{i}']['overflow_alarm'], + config[f'ch{i}']['underflow_alarm'], + config[f'ch{i}']['open_wire_alarm'], + config['power_alarm'], + ]) + + return bitstruct.pack(self.serdesfmt, *parameters) + + def deserialize(self, blob: bytes) -> Dict: + config = { + "description": "TBD", + "mlfb": self.mlfb + } + + unpack_statement = '_' + for i in range(0, self.ch_num): + config[f'ch{i}'] = {} + integ_time_ele = "config['integ_time']" if i == 0 else "_" + power_alarm_ele = "config['power_alarm']" if i == 0 else "_" + unpack_statement = ','.join([ + unpack_statement, + f"config['ch{i}']['type']", + f"config['ch{i}']['range']", + f"config['ch{i}']['temper_coeff']", + integ_time_ele, + f"config['ch{i}']['smooth']", + f"config['ch{i}']['temper_unit']", + f"config['ch{i}']['overflow_alarm']", + f"config['ch{i}']['underflow_alarm']", + f"config['ch{i}']['open_wire_alarm']", + power_alarm_ele + ]) + unpack_statement += ' = bitstruct.unpack(self.serdesfmt, blob)' + exec(unpack_statement) # pylint: disable=exec-used + return config + + +class SMSensDISerDes(ModuleSerDes): + """SM SENS DI Configuration (de)/serializer""" + def __init__(self, mlfb): + super().__init__(mlfb) + + def serialize(self, config: Dict) -> bytes: + blob = bytearray(14) + struct.pack_into('B', blob, 1, 0x02) + return blob + + def deserialize(self, blob: bytes) -> Dict: + return { + "description": "SM SENS DI module", + "mlfb": self.mlfb + } + + +class SM1238SerDes(ModuleSerDes): + """SM1238 EM 480VAC Configuration (de)/serializer""" + def __init__(self, mlfb): + super().__init__(mlfb) + # The first lonely u8 is for module_version, which is not defined in the + # data blob, but required by the extended IO controller. + header_fmt = 'u8' + 'u8 u8' + mod_head_fmt = 'u8 u8' + mod_fmt = 'u8 u8 b1u3u4 u8 b1p6b1 u8 p8 p8' + ch_head_fmt = 'u8u8' + ch_fmt = 'b1b1b1p2b1b1p1 u8 r16 r32 b1b1u2b1p3 u8 r16 r32 p8p8p8p8' + self.serdesfmt = ''.join([ + header_fmt, + mod_head_fmt, + mod_fmt, + ch_head_fmt, + ch_fmt * 3]) + + def serialize(self, config: Dict) -> bytes: + parameters = [config['module_version'], 0x51, 0x02] # Header + parameters.extend([0x01, 0x08]) # Module header + parameters.extend([ + config['con_type'], + config['range'], + config['meter_gate'], config['period_meters'], config['line_freq'], + config['line_vol_tol'], + config['min_max_cal'], config['diag_line_vol'], + config['data_variant'] + ]) + parameters.extend([0x03, 0x14]) # Channel header + for i in range(0, 3): + parameters.extend([ + config[f'ch{i}']['diag_over_vol'], + config[f'ch{i}']['diag_under_vol'], + config[f'ch{i}']['diag_ll_vol'], + config[f'ch{i}']['diag_over_cal'], + config[f'ch{i}']['diag_over_cur'], + config[f'ch{i}']['over_cur_tol_val'], + config[f'ch{i}']['over_cur_tol_time'].to_bytes(2, 'big'), + config[f'ch{i}']['ct_primary_cur'].to_bytes(4, 'big'), + config[f'ch{i}']['re_cur_dir'], + config[f'ch{i}']['act_hour_meter'], + config[f'ch{i}']['ct_second_cur'], + config[f'ch{i}']['en_gate_cir_hour_meter'], + config[f'ch{i}']['ll_cur_measure'], + config[f'ch{i}']['vt_second_vol'].to_bytes(2, 'big'), + config[f'ch{i}']['vt_prim_vol'].to_bytes(4, 'big'), + ]) + + return bitstruct.pack(self.serdesfmt, *parameters) + + def deserialize(self, blob: bytes) -> Dict: + config = { + "description": "TBD", + "mlfb": self.mlfb + } + + unpack_statement = ','.join([ + "config['module_version']", + '_', '_', + '_', '_', + "config['con_type']", + "config['range']", + "config['meter_gate']", "config['period_meters']", "config['line_freq']", + "config['line_vol_tol']", + "config['min_max_cal']", "config['diag_line_vol']", + "config['data_variant']", + '_', '_' + ]) + for i in range(0, 3): + config[f'ch{i}'] = {} + unpack_statement = ','.join([ + unpack_statement, + f"config['ch{i}']['diag_over_vol']", + f"config['ch{i}']['diag_under_vol']", + f"config['ch{i}']['diag_ll_vol']", + f"config['ch{i}']['diag_over_cal']", + f"config['ch{i}']['diag_over_cur']", + f"config['ch{i}']['over_cur_tol_val']", + f"config['ch{i}']['over_cur_tol_time']", + f"config['ch{i}']['ct_primary_cur']", + f"config['ch{i}']['re_cur_dir']", + f"config['ch{i}']['act_hour_meter']", + f"config['ch{i}']['ct_second_cur']", + f"config['ch{i}']['en_gate_cir_hour_meter']", + f"config['ch{i}']['ll_cur_measure']", + f"config['ch{i}']['vt_second_vol']", + f"config['ch{i}']['vt_prim_vol']" + ]) + unpack_statement += ' = bitstruct.unpack(self.serdesfmt, blob)' + exec(unpack_statement) # pylint: disable=exec-used + for i in range(0, 3): + config[f'ch{i}']['over_cur_tol_time'] = \ + int.from_bytes(config[f'ch{i}']['over_cur_tol_time'], byteorder='big') + config[f'ch{i}']['ct_primary_cur'] = \ + int.from_bytes(config[f'ch{i}']['ct_primary_cur'], byteorder='big') + config[f'ch{i}']['vt_second_vol'] = \ + int.from_bytes(config[f'ch{i}']['vt_second_vol'], byteorder='big') + config[f'ch{i}']['vt_prim_vol'] = \ + int.from_bytes(config[f'ch{i}']['vt_prim_vol'], byteorder='big') + return config + + +class NoModSerDes(ModuleSerDes): + """No module configuration (de)/serializer""" + def __init__(self, mlfb): + super().__init__(mlfb) + + def serialize(self, config: Dict) -> bytes: + return bytearray() + + def deserialize(self, blob: bytes) -> Dict: + return { + "description": "No module for this slot", + "mlfb": self.mlfb + } + + +class ModSerDesFactory(object): + @classmethod + def produce(cls, mlfb='NA') -> ModuleSerDes: + mlfb = mlfb.rstrip('\0') + if mlfb == '6ES7223-1QH32-0XB0': + return SM1223SerDes(mlfb) + elif mlfb == '6ES7231-4HF32-0XB0': + return SM1223AI8SerDes(mlfb) + elif mlfb == '6ES7231-5PD32-0XB0' or mlfb == '6ES7231-5PF32-0XB0': + return SM1223RTDSerDes(mlfb) + elif mlfb == '6ES7238-5XA32-0XB0': + return SM1238SerDes(mlfb) + elif mlfb == '6ES7647-0CM00-1AA2': + return SMSensDISerDes(mlfb) + else: + return NoModSerDes(mlfb) + + +class EIOConfigSerDes(): + """Extended IO configuration serializer and deserializer""" + + @staticmethod + def _calc_checksum(blob: bytes) -> int: + new_blob = bytearray(blob) + struct.pack_into('>I', new_blob, 12, 0) + checksum = zlib.crc32(new_blob) + return checksum + + @classmethod + def convert2blob(cls, config: Dict) -> bytes: + """Convert dict config data to blob format + + Then the blob data could be deployed into the external IO controller. + + Args: + config (Dict): Config data, which is normally read from yaml file or + passed in from webUI. + + Returns: + bytes: Config data blob + """ + pack_fmt_head = '> 4B 3I' + pack_fmt = pack_fmt_head + slot_pack_fmt_head = '> I 20s I' + slot_blobs = [] + for i in range(1, 7): + slot_pack_fmt = slot_pack_fmt_head + slot_config = config[f"slot{i}"] + serdes = ModSerDesFactory.produce(slot_config['mlfb']) + slot_blob = serdes.serialize(slot_config) + slot_pack_fmt += f' {len(slot_blob)}s' + + slot_blob = struct.pack(slot_pack_fmt, + len(slot_blob) + struct.calcsize(slot_pack_fmt_head), + bytes(slot_config['mlfb'], 'ascii'), + 0x01, + slot_blob) + slot_blobs.append(slot_blob) + pack_fmt += f' {len(slot_blob)}s' + + total_len = struct.calcsize(pack_fmt) - struct.calcsize(pack_fmt_head) + + blob = bytearray(struct.pack(pack_fmt, + 0x02, 0x00, 0x05, 0x00, + 0x01, + total_len, + 0, # Checksum placeholder + *slot_blobs)) + + # Updates the checksum + checksum = cls._calc_checksum(blob) + struct.pack_into('>I', blob, 12, checksum) + return bytes(blob) + + @staticmethod + def _get_next_slot_blob(blob: bytes) -> List: + """Expand slots blob. + + Extract metadata such as blob length, mlfb, version, and slot + blob data of all slots. + + Args: + blob (bytes): Blob data that contains zero or more slots. + + Raises: + ValueError: The blob data is in bad integrity + + Returns: + List: Each is a slot metadata tuple (length, mlfb, version, + slot_blob), the last element is an empty tuple () + """ + if len(blob) == 0: + return [()] + if len(blob) < (4 + 20 + 4): + raise ValueError('Blob size does not match, slot data corrupted!') + length, _ = struct.unpack(f'> I {len(blob) - 4}s', blob) + length, mlfb, version, slot_blob, blob_rest = struct.unpack( + f'> I 20s I {length - 20 - 8}s {len(blob) - length}s', blob) + + return [(length, mlfb, version, slot_blob), *EIOConfigSerDes._get_next_slot_blob(blob_rest)] + + @classmethod + def convert2config(cls, blob: bytes) -> Dict: + """Convert configration blob data to dict + + Args: + blob (bytes): Configuration data in blob format + + Raises: + ValueError: Blob size does not match, data corrupted + + Returns: + Dict: Configration data in dict format, which could be + easily dumpped into yaml or json. + """ + pack_fmt_head = '> 4B 3I' + slots_size = len(blob) - struct.calcsize(pack_fmt_head) + unpack_fmt = pack_fmt_head + f' {slots_size}s' + _, _, _, _, _, slots_size_unpacked, checksum, slots = struct.unpack(unpack_fmt, blob) + if slots_size_unpacked != slots_size: + raise ValueError('Blob size does not match, data corrupted!') + if checksum != cls._calc_checksum(blob): + raise ValueError('Checksum does not match, data corrupted!') + + slot_blobs = cls._get_next_slot_blob(slots) + slot_blobs.pop() # Remove the last empty tuple + + slot_configs = [ModSerDesFactory.produce(str(mlfb, 'ascii')).deserialize(blob) + for _, mlfb, _, blob in slot_blobs] + config = {} + for i in range(0, len(slot_configs)): + index = i + 1 + config[f'slot{index}'] = slot_configs[i] + + return config + + +class ConfigError(Exception): + def __init__(self, *args: object) -> None: + super().__init__(*args) + + +class EIOConfigValidator(object): + def __init__(self) -> None: + with open(eio_schema_top, 'r', encoding='ascii') as f: + schema_top = yaml.load(f.read(), Loader=Loader) + + ref_store = {} + for ref in eio_schema_refs: + with open(ref, 'r', encoding='ascii') as f: + ref_schema = yaml.load(f.read(), Loader=Loader) + ref_store[ref_schema["$id"]] = ref_schema + + self._schema_validator = Draft202012Validator( + schema = schema_top, + resolver = RefResolver(schema_top["$id"], schema_top, store=ref_store) + ) + + def validate_schema(self, instance): + try: + self._schema_validator.validate(instance) + except ValidationError as e: + msg = f"{e.message} @ {e.schema_path}" + print(msg) + raise ConfigError(f"Invalid config data: {msg}") from e + + def validate_semantic(self, config): + # Check holes in slot chain + last_mod = 0 + for i in range(1, 7): + if f"slot{i}" in config: + if last_mod > 0 and config[f"slot{i}"]["mlfb"] != "NA": + raise ConfigError(f"Invalid config: slot {i} is after an empty slot!") + elif config[f"slot{i}"]["mlfb"] != "NA": + continue + else: + last_mod = i + + # For 6ES7231-4HF32-0XB0 (or SM 1231-8AI), channels should be paired to + # be the same measurement type + for i in range(1, 7): + slot_conf = config[f"slot{i}"] + if slot_conf["mlfb"] != "6ES7231-4HF32-0XB0": + continue + for j in range(0, 8, 2): + ch_prev_type = slot_conf[f"ch{j}"]["type"] + ch_next_type = slot_conf[f"ch{j+1}"]["type"] + if ch_prev_type != ch_next_type: + raise ConfigError( + f"Invalid config: slot{i}: ch{j}.type != ch{j+1}.type") + + +class EIOConfigParser(object): + def __init__(self, config_yaml: str) -> None: + self._config: dict = yaml.load(config_yaml, Loader=Loader) + self._validator = EIOConfigValidator() + + def validate(self): + self._validator.validate_schema(self._config) + self._validator.validate_semantic(self._config) + + def transform(self) -> bytes: + return EIOConfigSerDes.convert2blob(self._config) + + +def deploy_config(config_yaml: str) -> bytes: + config_parser = EIOConfigParser(config_yaml) + config_parser.validate() + config_bin = config_parser.transform() + + try: + with open(EIO_FS_CONTROL, 'w', encoding='ascii') as f: + f.write("config") + + with open(EIO_FS_CONFIG, "wb") as f: + result = f.write(config_bin) + + if result != len(config_bin): + raise ConfigError("Failed to write config to Extended IO controller!") + + with open(EIO_FS_CONTROL, 'w', encoding='ascii') as f: + f.write("done") + except OSError as e: + print(e) + raise ConfigError(f"OSError: {e}") from e + + +class EIOConfigDumper(object): + def __init__(self, config_bin: bytearray) -> None: + self._blob = config_bin + + def transform(self) -> str: + config = EIOConfigSerDes.convert2config(self._blob) + config_yaml = yaml.dump(config, Dumper=Dumper, indent=2, sort_keys=False) + + return config_yaml + + +def retrieve_config() -> str: + try: + with open(EIO_FS_CONFIG, "rb") as f: + config_bin = f.read() + except OSError as e: + raise ConfigError(f"OSError: {e}") from e + + dumper = EIOConfigDumper(config_bin) + return dumper.transform() diff --git a/recipes-app/iot2050-eio-manager/files/iot2050_eio_event.py b/recipes-app/iot2050-eio-manager/files/iot2050_eio_event.py new file mode 100644 index 000000000..d7d4987d0 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050_eio_event.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Li Hua Qian +# +# SPDX-License-Identifier: MIT +# +from iot2050_eio_global import iot2050_eio_event_path + + +def read_eio_event(): + try: + with open(iot2050_eio_event_path, "r") as f: + event = f.read() + return event + except Exception as e: + raise Exception(f'{type(e).__name__}: {e}') + diff --git a/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu.py b/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu.py new file mode 100755 index 000000000..e614f0315 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu.py @@ -0,0 +1,216 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Li Hua Qian +# +# SPDX-License-Identifier: MIT +# +import hashlib +import json +import ctypes +from types import SimpleNamespace as Namespace +import gpiod +from iot2050_eio_global import ( + EIO_FS_FW_VER, + EIO_FWU_META, + EIO_FWU_MAP3_FW_BIN +) + + +class UpgradeError(Exception): + def __init__(self, ErrorInfo): + super().__init__(self) + self.err = ErrorInfo + + def __str__(self): + return self.err + + +class FlashRomProgrammer(): + def __init__(self, prog_name, prog_param, chip_names): + self.c_lib = ctypes.CDLL("/usr/lib/aarch64-linux-gnu/libflashrom.so") + self.flashprog = ctypes.c_void_p() + self.flashctx = ctypes.c_void_p() + self.prog_name = ctypes.create_string_buffer(prog_name) + self.prog_param = ctypes.create_string_buffer(prog_param) + self.chip_names = ctypes.create_string_buffer(chip_names) + + if self.c_lib.flashrom_programmer_init(ctypes.pointer(self.flashprog), + self.prog_name, + self.prog_param): + raise UpgradeError("Failed to init flashrom programmer!") + + if self.c_lib.flashrom_flash_probe(ctypes.pointer(self.flashctx), + self.flashprog, + self.chip_names): + raise UpgradeError("Failed to probe flashrom programmer!") + + def write(self, buffer, size): + if self.c_lib.flashrom_image_write(self.flashctx, + buffer, + size, + None): + raise UpgradeError("Failed to write from flash!") + + def read(self, firmware, size): + if self.c_lib.flashrom_image_read(self.flashctx, + firmware, + size): + raise UpgradeError("Failed to read from flash!") + + def release(self): + self.c_lib.flashrom_flash_release(self.flashctx) + + +class EIOFirmware(): + """The EIOFirmware class represents map3 flash operations""" + # 1 MB in total + MAP3_FLASH_SIZE_1_MB = 1024 * 1024 + # The first 784 KB + MAP3_FIRMWARE_SIZE = 784 * 1024 + # The last 32 KB + MAP3_CERTIFICATE_OFFSET = MAP3_FLASH_SIZE_1_MB - 32 * 1024 + + def __init__(self, firmware): + self.firmware = open(firmware, "rb") + + self.chip = gpiod.Chip("/dev/gpiochip2") + self.spi_mux_pin = self.chip.get_line(86) + self.spi_mux_pin.request(consumer='map3', type=gpiod.LINE_REQ_DIR_OUT) + + # Switch SPI Flash to AM65x + self.spi_mux_pin.set_value(1) + + try: + self.flash_prog = \ + FlashRomProgrammer(b"linux_spi", b"dev=/dev/spidev1.0", + b"MX25L8005/MX25L8006E/MX25L8008E/MX25V8005") + self.write_firmware = self.__get_write_firmware() + except UpgradeError as e: + raise UpgradeError("EIOFirmware: {}".format(e.err)) + + def __get_write_firmware(self): + read_buffer = ctypes.create_string_buffer(self.MAP3_FLASH_SIZE_1_MB) + self.flash_prog.read(read_buffer, self.MAP3_FLASH_SIZE_1_MB) + + self.firmware.seek(0) + write_firmware = self.firmware.read() + # Firmware and certificate partitions are required, other + # partitions are reserved. + write_firmware = write_firmware[:self.MAP3_FIRMWARE_SIZE] \ + + read_buffer[self.MAP3_FIRMWARE_SIZE:self.MAP3_CERTIFICATE_OFFSET] \ + + write_firmware[self.MAP3_CERTIFICATE_OFFSET:] + + return write_firmware + + def write(self): + """The EIOFirmware can write contents to map3 flash""" + # Need to readback and save the reserved partions + try: + self.flash_prog.write(self.write_firmware, + self.MAP3_FLASH_SIZE_1_MB) + except UpgradeError as e: + raise UpgradeError("EIOFirmware: {}".format(e.err)) + + def read(self): + """The EIOFirmware can read contents from map3 flash""" + read_buffer = ctypes.create_string_buffer(self.MAP3_FLASH_SIZE_1_MB) + try: + self.flash_prog.read(read_buffer, self.MAP3_FLASH_SIZE_1_MB) + except UpgradeError as e: + raise UpgradeError("EIOFirmware: {}".format(e.err)) + + return read_buffer + + def __del__(self): + self.spi_mux_pin.release() + if hasattr(self, "flash_prog") and self.flash_prog: + self.flash_prog.release() + self.firmware.close() + + +class FirmwareUpdate(): + """ + The FirmwareUpdate models the firmware updating behavior for all + firmware update. + """ + def __init__(self, firmware): + self.firmwares = {} + self.firmwares["map3"] = EIOFirmware(firmware) + + def update(self): + """Update the firmware to the specified flash""" + + for firmware_type in self.firmwares: + self.firmwares[firmware_type].write() + + content = self.firmwares[firmware_type].write_firmware + firmware_md5 = self.__get_md5_digest(content) + content = self.firmwares[firmware_type].read() + read_out_md5 = self.__get_md5_digest(content) + + if firmware_md5 != read_out_md5: + raise UpgradeError("Firmware digest verification failed") + + def __get_md5_digest(self, content): + """Verify the update integrity""" + md5 = hashlib.md5() + + md5.update(content) + + return md5.hexdigest() + + +class FirmwareUpdateChecker(): + def __init__(self): + self.fwu_meta = json.load(open(EIO_FWU_META, "r", encoding='ascii'), + object_hook=lambda d: Namespace(**d)) + + try: + with open(EIO_FS_FW_VER, "r", encoding='ascii') as f: + self.eio_controller_current_fw_ver = f.readline().split(' ')[0] + except OSError: + self.eio_controller_current_fw_ver = None + + def collect_fwu_info(self) -> tuple[int, str]: + """Collect EIO Firmware information for updating purpose + + Returns: + tuple[int, str]: + The 1st int element indicates the firmware status: + + - 0: means no need to update + - 1: means firmware need update. + - 2: means firmware need update, however, the firmware + checksum is not correct + + The second str elements explains details. + """ + status = 0 + message = 'EIO firmware is up-to-date, no need to update!' + + if self.eio_controller_current_fw_ver is None: + status = 1 + message = 'EIO FUSE does not exist! Suggest to update the firmware ' \ + 'via iot2050-eio, and check the iot2050-eiod.service' + elif self.fwu_meta.version != self.eio_controller_current_fw_ver: + with open(EIO_FWU_MAP3_FW_BIN, "rb") as f: + # Examine the checksum + if self.fwu_meta.sha1sum.lower() == hashlib.sha1(f.read()).hexdigest().lower(): + status = 1 + message = 'EIO firmware need update! Please update it via iot2050-eio!' + else: + status = 2 + message = 'EIO firmware need update, however, the firmware checksum does not match, Binary may be corrupted!' + + return status, message + + +def update_firmware(firmware): + try: + FirmwareUpdate(firmware).update() + return 0, "Firmware upgrade successfully!" + except UpgradeError as e: + return 1, e diff --git a/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu_monitor.py b/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu_monitor.py new file mode 100755 index 000000000..e1be71ff8 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050_eio_fwu_monitor.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Li Hua Qian +# +# SPDX-License-Identifier: MIT +# +import sys +import time +import os +import textwrap +import psutil +import grpc +from gRPC.EIOManager.iot2050_eio_pb2 import CheckFWURequest +from gRPC.EIOManager.iot2050_eio_pb2_grpc import EIOManagerStub +from iot2050_eio_global import ( + iot2050_eio_api_server +) + + +def stat_led_toggle(): + with open('/sys/class/leds/status-led-green/brightness', 'w') as green_f, \ + open('/sys/class/leds/status-led-green/trigger', 'w') as green_blink_f, \ + open('/sys/class/leds/status-led-red/brightness', 'w') as red_f: + green_blink_f.write('none') + green_blink_f.flush() + green_f.write('255') + red_f.write('255') + green_f.flush() + red_f.flush() + + +def broadcast_update_info(message): + width = 72 + frame = '=' * width + message = '\n '.join(textwrap.wrap(message, width)) + prompt = f''' + {frame} + + {message} + + {frame} + +Hit the Enter Key to Exit: + ''' + os.system(f'wall "{prompt}"') + + +def do_fwu_check(): + with grpc.insecure_channel(iot2050_eio_api_server) as channel: + stub = EIOManagerStub(channel) + response = stub.CheckFWU(CheckFWURequest(entity=0)) + return response.status, response.message + + +def firmware_check(): + status, message = do_fwu_check() + if status == 0: + return + + stat_led_toggle() + + # Check the firmware and reminded once on every new session if firmware + # is not the correct one + last_sessions = psutil.users() + while True: + time.sleep(5) + current_sessions = psutil.users() + new_sessions = [s for s in current_sessions if s not in last_sessions] + if len(new_sessions) > 0: + broadcast_update_info(message) + last_sessions = current_sessions + +if __name__ == "__main__": + firmware_check() + sys.exit(0) diff --git a/recipes-app/iot2050-eio-manager/files/iot2050_eio_global.py b/recipes-app/iot2050-eio-manager/files/iot2050_eio_global.py new file mode 100644 index 000000000..b4fba6f49 --- /dev/null +++ b/recipes-app/iot2050-eio-manager/files/iot2050_eio_global.py @@ -0,0 +1,86 @@ +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# SPDX-License-Identifier: MIT +from dotenv import dotenv_values + +default_conf = { + # IOT2050 Extended IO API server hostname + 'EIO_API_SERVER_HOSTNAME': 'localhost', + + # IOT2050 Extended IO API server port + 'EIO_API_SERVER_PORT': '5020', + + # Periodically syncing timestamp to Extended IO controller + 'EIO_TIME_SYNC_INTERVAL': 30, + + # Extended IO FUSE filesystem path for timestamp syncing + 'EIO_FS_TIMESTAMP': '/ext-io/proc/datetime', + + # Extended IO FUSE filesystem path for configuration + 'EIO_FS_CONTROL': '/ext-io/controller/control', + 'EIO_FS_CONFIG': '/ext-io/controller/config', + + # Extended IO FUSE filesystem path for firmware version + 'EIO_FS_FW_VER': '/ext-io/proc/version', + + # Extended IO FUSE filesystem path for eio events + 'EIO_FS_EVENT': '/ext-io/log/event', + + # EIO Firmware Update: Meta data + 'EIO_FWU_META': '/usr/lib/iot2050/eio/firmware-version', + + # EIO Firmware Update: Firmware binary file + 'EIO_FWU_MAP3_FW_BIN': '/usr/lib/iot2050/eio/map3-fw.bin', + + # JSON schema files for validating the config yaml + 'EIO_SCHEMA_ROOT': '/usr/lib/iot2050/eio/schema', + + # template file for yaml config + 'EIO_CONFIG_TEMP_ROOT': '/usr/lib/iot2050/eio/config-template' +} + +local_conf = dotenv_values(".env") + +effective_conf = { + **default_conf, + **local_conf +} + +EIO_API_SERVER_HOSTNAME = effective_conf['EIO_API_SERVER_HOSTNAME'] +EIO_API_SERVER_PORT = effective_conf['EIO_API_SERVER_PORT'] +EIO_TIME_SYNC_INTERVAL = effective_conf['EIO_TIME_SYNC_INTERVAL'] +EIO_FS_TIMESTAMP = effective_conf['EIO_FS_TIMESTAMP'] +EIO_FS_CONTROL = effective_conf['EIO_FS_CONTROL'] +EIO_FS_CONFIG = effective_conf['EIO_FS_CONFIG'] +EIO_FS_FW_VER = effective_conf['EIO_FS_FW_VER'] +EIO_FS_EVENT = effective_conf['EIO_FS_EVENT'] +EIO_FWU_META = effective_conf['EIO_FWU_META'] +EIO_FWU_MAP3_FW_BIN = effective_conf['EIO_FWU_MAP3_FW_BIN'] +EIO_SCHEMA_ROOT = effective_conf['EIO_SCHEMA_ROOT'] +EIO_CONFIG_TEMP_ROOT = effective_conf['EIO_CONFIG_TEMP_ROOT'] + +eio_schema_top = f"{EIO_SCHEMA_ROOT}/schema-sm-config.yaml" +eio_schema_refs = [ + f"{EIO_SCHEMA_ROOT}/schema-na.yaml", + f"{EIO_SCHEMA_ROOT}/schema-sm1223-ac-rly.yaml", + f"{EIO_SCHEMA_ROOT}/schema-sm1231-ai.yaml", + f"{EIO_SCHEMA_ROOT}/schema-sm1231-rtd.yaml", + f"{EIO_SCHEMA_ROOT}/schema-sm-sens-di.yaml", + f"{EIO_SCHEMA_ROOT}/schema-sm1238-em-480vac.yaml" +] + +eio_conf_templates = [ + f"{EIO_CONFIG_TEMP_ROOT}/mlfb-6ES7223-1QH32-0XB0.yaml", + f"{EIO_CONFIG_TEMP_ROOT}/mlfb-6ES7231-4HF32-0XB0.yaml", + f"{EIO_CONFIG_TEMP_ROOT}/mlfb-6ES7231-5PD32-0XB0.yaml", + f"{EIO_CONFIG_TEMP_ROOT}/mlfb-6ES7231-5PF32-0XB0.yaml", + f"{EIO_CONFIG_TEMP_ROOT}/mlfb-6ES7647-0CM00-1AA2.yaml", + f"{EIO_CONFIG_TEMP_ROOT}/mlfb-6ES7238-5XA32-0XB0.yaml", + f"{EIO_CONFIG_TEMP_ROOT}/mlfb-NA.yaml" +] + +iot2050_eio_api_server = f"{EIO_API_SERVER_HOSTNAME}:{EIO_API_SERVER_PORT}" +iot2050_eio_event_path = f"{EIO_FS_EVENT}" diff --git a/recipes-app/iot2050-eio-manager/iot2050-eio-manager_0.2.bb b/recipes-app/iot2050-eio-manager/iot2050-eio-manager_0.2.bb new file mode 100644 index 000000000..90eabad6b --- /dev/null +++ b/recipes-app/iot2050-eio-manager/iot2050-eio-manager_0.2.bb @@ -0,0 +1,118 @@ +# +# Copyright (c) Siemens AG, 2023 +# +# Authors: +# Su Bao Cheng +# +# This file is subject to the terms and conditions of the MIT License. See +# COPYING.MIT file in the top-level directory. +# +inherit dpkg-raw + +DESCRIPTION = "IOT2050 Extended IO Manager" +MAINTAINER = "baocheng.su@siemens.com" + +SRC_URI = "file://bin/iot2050-ext-io \ + file://gRPC/EIOManager/iot2050_eio_pb2_grpc.py \ + file://gRPC/EIOManager/iot2050_eio_pb2.py \ + file://gRPC/EIOManager/iot2050_eio_pb2.pyi \ + file://gRPC/EIOManager/iot2050-eio.proto \ + file://iot2050_eio_global.py \ + file://iot2050_eio_config.py \ + file://iot2050_eio_event.py \ + file://iot2050-eio-service.py \ + file://iot2050-eio-time-syncing.py \ + file://iot2050-eio-cli.py \ + file://iot2050-eio-time-syncing.service \ + file://iot2050-eiod.service \ + file://iot2050-eiosd.service \ + " + +SRC_URI += " \ + file://config-schema/schema-sm-config.yaml \ + file://config-schema/schema-na.yaml \ + file://config-schema/schema-sm1223-ac-rly.yaml \ + file://config-schema/schema-sm1231-ai.yaml \ + file://config-schema/schema-sm1231-rtd.yaml \ + file://config-schema/schema-sm-sens-di.yaml \ + file://config-schema/schema-sm1238-em-480vac.yaml \ + file://config-template/sm-config-example.yaml \ + file://config-template/mlfb-6ES7223-1QH32-0XB0.yaml \ + file://config-template/mlfb-6ES7231-4HF32-0XB0.yaml \ + file://config-template/mlfb-6ES7231-5PD32-0XB0.yaml \ + file://config-template/mlfb-6ES7231-5PF32-0XB0.yaml \ + file://config-template/mlfb-6ES7647-0CM00-1AA2.yaml \ + file://config-template/mlfb-6ES7238-5XA32-0XB0.yaml \ + file://config-template/mlfb-NA.yaml \ + " + +# Firmware update +SRC_URI += " \ + file://bin/map3-fw.bin \ + file://bin/firmware-version \ + file://iot2050_eio_fwu.py \ + file://iot2050_eio_fwu_monitor.py \ + file://iot2050-eio-fwu-monitor.service \ + " + +DEBIAN_DEPENDS = "python3, python3-grpcio, python3-jsonschema, python3-yaml, \ +python3-libgpiod, libflashrom1, libflashrom-dev, python3-bitstruct, python3-dotenv, \ +python3-progress, python3-psutil" + +do_install() { + install -v -d ${D}/usr/lib/ + install -v -d ${D}/usr/lib/iot2050/ + + install -v -d ${D}/usr/lib/iot2050/eio + install -v -d ${D}/usr/lib/iot2050/eio/gRPC + install -v -d ${D}/usr/lib/iot2050/eio/gRPC/EIOManager + install -v -m 755 ${WORKDIR}/gRPC/EIOManager/iot2050_eio_pb2_grpc.py ${D}/usr/lib/iot2050/eio/gRPC/EIOManager/ + install -v -m 755 ${WORKDIR}/gRPC/EIOManager/iot2050_eio_pb2.py ${D}/usr/lib/iot2050/eio/gRPC/EIOManager/ + install -v -m 755 ${WORKDIR}/gRPC/EIOManager/iot2050_eio_pb2.pyi ${D}/usr/lib/iot2050/eio/gRPC/EIOManager/ + install -v -m 755 ${WORKDIR}/gRPC/EIOManager/iot2050-eio.proto ${D}/usr/lib/iot2050/eio/gRPC/EIOManager/ + + install -v -m 755 ${WORKDIR}/iot2050_eio_global.py ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/iot2050_eio_config.py ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/iot2050_eio_event.py ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/iot2050-eio-service.py ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/iot2050-eio-time-syncing.py ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/iot2050_eio_fwu_monitor.py ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/iot2050-eio-cli.py ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/iot2050_eio_fwu.py ${D}/usr/lib/iot2050/eio/ + + install -v -m 755 ${WORKDIR}/bin/map3-fw.bin ${D}/usr/lib/iot2050/eio/ + install -v -m 755 ${WORKDIR}/bin/firmware-version ${D}/usr/lib/iot2050/eio/ + + install -v -d ${D}/usr/lib/iot2050/eio/schema + install -v -d ${D}/usr/lib/iot2050/eio/config-template + install -v -m 644 ${WORKDIR}/config-schema/schema-sm-config.yaml ${D}/usr/lib/iot2050/eio/schema/ + install -v -m 644 ${WORKDIR}/config-schema/schema-na.yaml ${D}/usr/lib/iot2050/eio/schema/ + install -v -m 644 ${WORKDIR}/config-schema/schema-sm1223-ac-rly.yaml ${D}/usr/lib/iot2050/eio/schema/ + install -v -m 644 ${WORKDIR}/config-schema/schema-sm1231-ai.yaml ${D}/usr/lib/iot2050/eio/schema/ + install -v -m 644 ${WORKDIR}/config-schema/schema-sm1231-rtd.yaml ${D}/usr/lib/iot2050/eio/schema/ + install -v -m 644 ${WORKDIR}/config-schema/schema-sm-sens-di.yaml ${D}/usr/lib/iot2050/eio/schema/ + install -v -m 644 ${WORKDIR}/config-schema/schema-sm1238-em-480vac.yaml ${D}/usr/lib/iot2050/eio/schema/ + install -v -m 644 ${WORKDIR}/config-template/sm-config-example.yaml ${D}/usr/lib/iot2050/eio/config-template/ + install -v -m 644 ${WORKDIR}/config-template/mlfb-6ES7223-1QH32-0XB0.yaml ${D}/usr/lib/iot2050/eio/config-template/ + install -v -m 644 ${WORKDIR}/config-template/mlfb-6ES7231-4HF32-0XB0.yaml ${D}/usr/lib/iot2050/eio/config-template/ + install -v -m 644 ${WORKDIR}/config-template/mlfb-6ES7231-5PD32-0XB0.yaml ${D}/usr/lib/iot2050/eio/config-template/ + install -v -m 644 ${WORKDIR}/config-template/mlfb-6ES7231-5PF32-0XB0.yaml ${D}/usr/lib/iot2050/eio/config-template/ + install -v -m 644 ${WORKDIR}/config-template/mlfb-6ES7647-0CM00-1AA2.yaml ${D}/usr/lib/iot2050/eio/config-template/ + install -v -m 644 ${WORKDIR}/config-template/mlfb-6ES7238-5XA32-0XB0.yaml ${D}/usr/lib/iot2050/eio/config-template/ + install -v -m 644 ${WORKDIR}/config-template/mlfb-NA.yaml ${D}/usr/lib/iot2050/eio/config-template/ + + install -v -d ${D}/lib/systemd/system/ + install -v -m 644 ${WORKDIR}/iot2050-eio-time-syncing.service ${D}/lib/systemd/system/ + install -v -m 644 ${WORKDIR}/iot2050-eiod.service ${D}/lib/systemd/system/ + install -v -m 644 ${WORKDIR}/iot2050-eiosd.service ${D}/lib/systemd/system/ + install -v -m 644 ${WORKDIR}/iot2050-eio-fwu-monitor.service ${D}/lib/systemd/system/ + + install -v -d ${D}/usr/bin/ + install -v -m 755 ${WORKDIR}/bin/iot2050-ext-io ${D}/usr/bin/ + ln -sf ../lib/iot2050/eio/iot2050-eio-time-syncing.py ${D}/usr/bin/iot2050-eio-time-syncing + ln -sf ../lib/iot2050/eio/iot2050-eio-service.py ${D}/usr/bin/iot2050-eio-service + ln -sf ../lib/iot2050/eio/iot2050-eio-cli.py ${D}/usr/bin/iot2050-eio + ln -sf ../lib/iot2050/eio/iot2050_eio_fwu_monitor.py ${D}/usr/bin/iot2050-eio-fwu-monitor + + install -v -d ${D}/ext-io +} diff --git a/recipes-core/images/iot2050-image-example.bb b/recipes-core/images/iot2050-image-example.bb index 7581d94c2..b1b49cbd4 100644 --- a/recipes-core/images/iot2050-image-example.bb +++ b/recipes-core/images/iot2050-image-example.bb @@ -41,6 +41,7 @@ IMAGE_INSTALL += " \ libteec1 \ optee-client-dev \ tee-supplicant \ + iot2050-eio-manager \ " IOT2050_NODE_RED_SUPPORT ?= "1"